1use crate::fuchsia::component::map_to_raw_status;
6use crate::fuchsia::directory::FxDirectory;
7use crate::fuchsia::dirent_cache::DirentCache;
8use crate::fuchsia::file::{FlushType, FxFile};
9use crate::fuchsia::memory_pressure::{MemoryPressureLevel, MemoryPressureMonitor};
10use crate::fuchsia::node::{FxNode, GetResult, NodeCache, OpenedNode};
11use crate::fuchsia::pager::Pager;
12use crate::fuchsia::profile::{
13 AttributeRecordingHandle, FileRecordingHandle, IO_SIZE, ProfileState, RecordingHandle,
14};
15use crate::fuchsia::symlink::FxSymlink;
16use crate::fuchsia::volumes_directory::VolumesDirectory;
17use anyhow::{Context as _, Error, bail, ensure};
18use async_trait::async_trait;
19use fidl::endpoints::ServerEnd;
20use fidl_fuchsia_fxfs::{
21 BytesAndNodes, DebugRequest, DebugRequestStream, FileBackedVolumeProviderRequest,
22 FileBackedVolumeProviderRequestStream, ProjectIdRequest, ProjectIdRequestStream,
23 ProjectIterToken,
24};
25use fidl_fuchsia_io as fio;
26use fs_inspect::{FsInspectVolume, VolumeData};
27use fuchsia_async as fasync;
28use fuchsia_async::epoch::Epoch;
29use fuchsia_sync::Mutex;
30use futures::channel::oneshot;
31use futures::stream::{self, FusedStream, Stream};
32use futures::{FutureExt, StreamExt, TryStreamExt};
33use fxfs::errors::FxfsError;
34use fxfs::filesystem::{self, SyncOptions, TruncateGuard};
35use fxfs::future_with_guard::FutureWithGuard;
36use fxfs::log::*;
37use fxfs::object_handle::{ObjectHandle as _, ReadObjectHandle};
38use fxfs::object_store::directory::Directory;
39use fxfs::object_store::object_record::ObjectItem;
40use fxfs::object_store::project_id::ProjectIdExt;
41use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
42use fxfs::object_store::{
43 AttributeId, AttributeKey, DataObjectHandle, DirType, HandleOptions, HandleOwner,
44 ObjectDescriptor, ObjectKey, ObjectKind, ObjectStore, ObjectStoreTrait, ObjectValue, ProjectId,
45};
46use refaults_vmo::PageFaultCounter;
47use scopeguard::ScopeGuard;
48use std::future::Future;
49use std::pin::pin;
50use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
51use std::sync::{Arc, OnceLock, Weak};
52use std::time::Duration;
53use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
54use storage_profile::MAX_PROFILE_VMO_SIZE;
55use storage_units::BlockSize;
56use vfs::directory::entry::DirectoryEntry;
57use vfs::directory::simple::Simple;
58use vfs::execution_scope::ExecutionScope;
59
60const DIRENT_CACHE_LIMIT: usize = 2000;
63pub const READ_AHEAD_SIZE: u64 = 128 * 1024;
68
69const BLOB_TRANSFER_VMO_SIZE: usize = 32 * 1024 * 1024;
70
71const PROFILE_DIRECTORY: &str = "profiles";
72
73#[derive(Clone, Copy, Debug)]
74pub struct MemoryPressureLevelConfig {
75 pub background_task_period: Duration,
78
79 pub cache_size_limit: usize,
81}
82
83impl Default for MemoryPressureLevelConfig {
84 fn default() -> Self {
85 Self {
86 background_task_period: Duration::from_secs(20),
87 cache_size_limit: DIRENT_CACHE_LIMIT,
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug)]
93pub struct MemoryPressureConfig {
94 pub mem_normal: MemoryPressureLevelConfig,
96
97 pub mem_warning: MemoryPressureLevelConfig,
99
100 pub mem_critical: MemoryPressureLevelConfig,
102}
103
104impl MemoryPressureConfig {
105 pub fn for_level(&self, level: &MemoryPressureLevel) -> &MemoryPressureLevelConfig {
106 match level {
107 MemoryPressureLevel::Normal => &self.mem_normal,
108 MemoryPressureLevel::Warning => &self.mem_warning,
109 MemoryPressureLevel::Critical => &self.mem_critical,
110 }
111 }
112}
113
114impl Default for MemoryPressureConfig {
115 fn default() -> Self {
116 Self {
119 mem_normal: MemoryPressureLevelConfig {
120 background_task_period: Duration::from_secs(20),
121 cache_size_limit: DIRENT_CACHE_LIMIT,
122 },
123 mem_warning: MemoryPressureLevelConfig {
124 background_task_period: Duration::from_secs(5),
125 cache_size_limit: 100,
126 },
127 mem_critical: MemoryPressureLevelConfig {
128 background_task_period: Duration::from_millis(1500),
129 cache_size_limit: 20,
130 },
131 }
132 }
133}
134
135pub struct FxVolume {
138 parent: Weak<VolumesDirectory>,
139 cache: NodeCache,
140 store: Arc<ObjectStore>,
141 pager: Pager,
142 executor: fasync::EHandle,
143 name: String,
144
145 background_task: Mutex<Option<(fasync::Task<()>, oneshot::Sender<()>)>>,
147
148 fs_id: u64,
150
151 scope: ExecutionScope,
153
154 dirent_cache: DirentCache,
155
156 profile_state: Mutex<Option<Box<dyn ProfileState>>>,
157
158 #[cfg(any(test, feature = "testing"))]
159 poisoned: AtomicBool,
160
161 page_fault_counter: Arc<PageFaultCounter>,
162
163 pager_dirty_byte_count: AtomicU64,
166
167 blob_allocator: OnceLock<Arc<BufferAllocator>>,
174
175 use_driver_blob_paging: AtomicBool,
177}
178
179#[fxfs_trace::trace]
180impl FxVolume {
181 pub fn new(
182 parent: Weak<VolumesDirectory>,
183 store: Arc<ObjectStore>,
184 fs_id: u64,
185 name: String,
186 page_fault_counter: Arc<PageFaultCounter>,
187 memory_pressure_config: MemoryPressureConfig,
188 ) -> Result<Self, Error> {
189 let scope = ExecutionScope::new();
190 Ok(Self {
191 parent,
192 cache: NodeCache::new(),
193 store,
194 name,
195 pager: Pager::new(scope.clone())?,
196 executor: fasync::EHandle::local(),
197 background_task: Mutex::new(None),
198 fs_id,
199 scope,
200 dirent_cache: DirentCache::new(memory_pressure_config.mem_normal.cache_size_limit),
201 profile_state: Mutex::new(None),
202 #[cfg(any(test, feature = "testing"))]
203 poisoned: AtomicBool::new(false),
204 page_fault_counter,
205 pager_dirty_byte_count: AtomicU64::new(0),
206 blob_allocator: OnceLock::new(),
207 use_driver_blob_paging: AtomicBool::new(false),
208 })
209 }
210
211 pub fn use_driver_blob_paging(&self) -> bool {
212 self.use_driver_blob_paging.load(Ordering::Relaxed)
213 }
214
215 pub fn set_use_driver_blob_paging(&self, enabled: bool) {
216 self.use_driver_blob_paging.store(enabled, Ordering::Relaxed);
217 }
218
219 pub fn store(&self) -> &Arc<ObjectStore> {
220 &self.store
221 }
222
223 pub fn blob_allocator(&self) -> &Arc<BufferAllocator> {
227 self.blob_allocator.get_or_init(|| {
228 Arc::new(BufferAllocator::new(
229 self.store().block_size().get() as usize,
230 BufferSource::new_trusted(BLOB_TRANSFER_VMO_SIZE),
231 ))
232 })
233 }
234
235 pub fn cache(&self) -> &NodeCache {
236 &self.cache
237 }
238
239 pub fn dirent_cache(&self) -> &DirentCache {
240 &self.dirent_cache
241 }
242
243 pub fn pager(&self) -> &Pager {
244 &self.pager
245 }
246
247 pub fn id(&self) -> u64 {
248 self.fs_id
249 }
250
251 pub fn scope(&self) -> &ExecutionScope {
252 &self.scope
253 }
254
255 pub fn page_fault_counter(&self) -> &PageFaultCounter {
256 &self.page_fault_counter
257 }
258
259 pub fn name(&self) -> &str {
260 &self.name
261 }
262
263 pub fn filesystem_info_for_volume(&self) -> fio::FilesystemInfo {
266 let allocator = self.store.filesystem().ensure_local().allocator();
267 let info =
268 if let Some(limit) = allocator.get_owner_bytes_limit(self.store.store_object_id()) {
269 filesystem::Info {
270 used_bytes: allocator.get_owner_bytes_used(self.store.store_object_id()),
271 total_bytes: limit,
272 }
273 } else {
274 self.store.filesystem().get_info()
275 };
276
277 info_to_filesystem_info(info, self.store.block_size(), self.store.object_count(), self.id())
278 }
279
280 pub async fn stop_profile_tasks(&self) {
282 let state = self.profile_state.lock().take();
283 if let Some(mut state) = state {
284 state.wait_for_replay_to_finish().await;
285 self.pager.set_recorder(None);
286 state.wait_for_recording_to_finish().await;
287 }
288 }
289
290 pub async fn get_profile_vmo(self: &Arc<Self>, name: &str) -> Result<zx::Vmo, Error> {
292 let handle = self.open_replay_handle(name).await?.ok_or(FxfsError::NotFound)?;
293 let raw_size = handle.get_size();
294 if raw_size == 0 {
295 return Ok(zx::Vmo::create(0)?);
296 }
297 ensure!(raw_size <= MAX_PROFILE_VMO_SIZE, FxfsError::OutOfRange);
298 let file_size = raw_size as usize;
299 let block_size = handle.block_size().get() as usize;
300 let aligned_size = file_size.next_multiple_of(block_size);
301 ensure!(aligned_size <= MAX_PROFILE_VMO_SIZE as usize, FxfsError::OutOfRange);
302 let vmo = zx::Vmo::create(aligned_size as u64)?;
303 let buf_size = std::cmp::min(aligned_size, IO_SIZE);
304 let mut io_buf = handle.allocate_buffer(buf_size).await;
305 let mut temp_buf = vec![0u8; buf_size];
306 let mut offset = 0;
307 while offset < file_size {
308 let actual = handle
309 .read_aligned(offset as u64, io_buf.as_mut())
310 .await
311 .with_context(|| format!("Failed to read profile at offset: {offset}"))?;
312 if actual == 0 {
313 break;
314 }
315 io_buf.subslice(0..actual).copy_to_slice(&mut temp_buf[..actual]);
316 vmo.write(&temp_buf[..actual], offset as u64)?;
317 offset += actual;
318 }
319 Ok(vmo)
320 }
321
322 pub async fn write_profile_vmo(
324 self: &Arc<Self>,
325 name: &str,
326 recorded_vmo: zx::Vmo,
327 ) -> Result<(), Error> {
328 let vmo_size = recorded_vmo.get_size()? as usize;
329 let block_size = self.store().block_size().get() as usize;
330 ensure!(vmo_size % block_size == 0, FxfsError::InvalidArgs);
331 ensure!(vmo_size <= MAX_PROFILE_VMO_SIZE as usize, FxfsError::OutOfRange);
332
333 let recording_handle = Box::new(FileRecordingHandle::new(name, self.clone()).await?)
334 as Box<dyn RecordingHandle>;
335 let recording_handle = scopeguard::guard(recording_handle, |recording_handle| {
336 recording_handle.abort_cleanup();
337 });
338 if vmo_size > 0 {
339 let buf_size = std::cmp::min(vmo_size, IO_SIZE);
340 let mut io_buf = recording_handle.allocate_buffer(buf_size).await;
341 let mut temp_buf = vec![0u8; buf_size];
342 let mut offset = 0;
343 while offset < vmo_size {
344 let chunk_len = std::cmp::min(vmo_size - offset, io_buf.len());
345 recorded_vmo.read(&mut temp_buf[..chunk_len], offset as u64)?;
346 io_buf.as_mut().subslice_mut(0..chunk_len).copy_from_slice(&temp_buf[..chunk_len]);
347 recording_handle
348 .append(io_buf.subslice(0..chunk_len))
349 .await
350 .context("Failed to write profile block")?;
351 offset += chunk_len;
352 }
353 }
354 let recording_handle = ScopeGuard::into_inner(recording_handle);
355 recording_handle.commit().await?;
356 Ok(())
357 }
358
359 pub async fn get_profile_directory(self: &Arc<Self>) -> Result<Directory<FxVolume>, Error> {
361 let internal_dir = self
362 .get_or_create_internal_dir()
363 .await
364 .map_err(|e| e.context("Opening internal directory"))?;
365 let mut transaction = self
367 .store()
368 .new_transaction(
369 lock_keys![LockKey::object(
370 self.store().store_object_id(),
371 internal_dir.object_id(),
372 )],
373 Options::default(),
374 )
375 .await?;
376 Ok(match internal_dir.directory().lookup(PROFILE_DIRECTORY).await? {
377 Some((object_id, _, _)) => {
378 Directory::open_unchecked(self.clone(), object_id, DirType::Normal)
379 }
380 None => {
381 let new_dir = internal_dir
382 .directory()
383 .create_child_dir(&mut transaction, PROFILE_DIRECTORY)
384 .await?;
385 transaction.commit().await?;
386 new_dir
387 }
388 })
389 }
390
391 async fn open_replay_handle(
392 self: &Arc<Self>,
393 name: &str,
394 ) -> Result<Option<Box<dyn ReadObjectHandle>>, Error> {
395 let profile_dir = self.get_profile_directory().await?;
396 if let Some((id, descriptor, _)) = profile_dir.lookup(name).await? {
397 ensure!(matches!(descriptor, ObjectDescriptor::File), FxfsError::Inconsistent);
398 Ok(Some(Box::new(
399 ObjectStore::open_object(self, id, HandleOptions::default(), None).await?,
400 )))
401 } else {
402 Ok(None)
403 }
404 }
405
406 pub async fn record_and_replay_profile(
410 self: &Arc<Self>,
411 mut state: Box<dyn ProfileState>,
412 name: &str,
413 ) -> Result<(), Error> {
414 let replay_handle = self.open_replay_handle(name).await?;
421
422 info!("Recording new profile '{name}' for volume object {}", self.store.store_object_id());
423 let recording_handle = FileRecordingHandle::new(name, self.clone()).await?;
425
426 let mut profile_state = self.profile_state.lock();
427 self.pager.set_recorder(Some(state.record_new(self, Box::new(recording_handle))));
428 if let Some(handle) = replay_handle {
429 if let Some(guard) = self.scope().try_active_guard() {
430 state.replay_profile(handle, self.clone(), guard);
431 info!(
432 "Replaying existing profile '{name}' for volume object {}",
433 self.store.store_object_id()
434 );
435 }
436 }
437 *profile_state = Some(state);
438 Ok(())
439 }
440
441 pub async fn replay_xor_record_profile(
443 self: &Arc<Self>,
444 mut state: Box<dyn ProfileState>,
445 node: OpenedNode<dyn FxNode>,
446 ) -> Result<(), Error> {
447 let object_id = node.object_id();
448 let key = ObjectKey::attribute(
449 object_id,
450 AttributeId::PROFILE_RECORDING,
451 AttributeKey::Attribute,
452 );
453
454 let replay_handle =
455 if let Some(ObjectItem { value: ObjectValue::Attribute { size, .. }, .. }) =
456 self.store().tree().find(&key).await?
457 {
458 Some(Box::new(DataObjectHandle::new(
459 self.clone(),
460 object_id,
461 false,
462 AttributeId::PROFILE_RECORDING,
463 size,
464 HandleOptions::default(),
465 false,
466 &[],
467 )) as Box<dyn ReadObjectHandle>)
468 } else {
469 None
470 };
471
472 if let Some(handle) = replay_handle {
473 let mut profile_state = self.profile_state.lock();
474 if let Some(guard) = self.scope().try_active_guard() {
475 state.replay_profile(handle, self.clone(), guard);
476 info!(
477 "Replaying existing profile for object {object_id} in volume object {}",
478 self.store.store_object_id()
479 );
480 }
481 *profile_state = Some(state);
482 } else {
483 info!(
484 "Recording new profile for object {object_id} in volume object {}",
485 self.store.store_object_id()
486 );
487 let recording_handle = AttributeRecordingHandle::new(node, self.clone()).await?;
488 let mut profile_state = self.profile_state.lock();
489 self.pager.set_recorder(Some(state.record_new(self, Box::new(recording_handle))));
490 *profile_state = Some(state);
491 }
492 Ok(())
493 }
494
495 async fn get_or_create_internal_dir(self: &Arc<Self>) -> Result<Arc<FxDirectory>, Error> {
496 let internal_data_id = self.store().get_or_create_internal_directory_id().await?;
497 let internal_dir = self
498 .get_or_load_node(internal_data_id, ObjectDescriptor::Directory, None)
499 .await?
500 .into_any()
501 .downcast::<FxDirectory>()
502 .unwrap();
503 Ok(internal_dir)
504 }
505
506 pub async fn terminate(&self) {
507 let task = std::mem::replace(&mut *self.background_task.lock(), None);
508 if let Some((task, terminate)) = task {
509 let _ = terminate.send(());
510 task.await;
511 }
512
513 self.scope.shutdown();
522 self.cache.terminate();
523 self.scope.wait().await;
524
525 Epoch::global().barrier().await;
527
528 self.dirent_cache.clear();
531
532 if self.store.filesystem().options().read_only {
533 if self.store.is_unlocked() {
535 self.store.lock_read_only();
536 }
537 return;
538 }
539
540 self.flush_all_files(FlushType::LastChance).await;
541 self.store.filesystem().ensure_local().graveyard().flush().await;
542 if self.store.crypt().is_some() {
543 if let Err(e) = self.store.lock().await {
544 warn!(error:? = e; "Locking store error");
547 }
548 }
549 let sync_status = self
550 .store
551 .filesystem()
552 .sync(SyncOptions { flush_device: true, ..Default::default() })
553 .await;
554 if let Err(e) = sync_status {
555 error!(error:? = e; "Failed to sync filesystem; data may be lost");
556 }
557 }
558
559 pub async fn get_or_load_node(
564 self: &Arc<Self>,
565 object_id: u64,
566 object_descriptor: ObjectDescriptor,
567 parent: Option<Arc<FxDirectory>>,
568 ) -> Result<Arc<dyn FxNode>, Error> {
569 match self.cache.get_or_reserve(object_id).await {
570 GetResult::Node(node) => Ok(node),
571 GetResult::Placeholder(placeholder) => {
572 let node = match object_descriptor {
573 ObjectDescriptor::File => FxFile::new(
574 ObjectStore::open_object(self, object_id, HandleOptions::default(), None)
575 .await?,
576 ) as Arc<dyn FxNode>,
577 ObjectDescriptor::Directory => {
578 Arc::new(FxDirectory::new(parent, Directory::open(self, object_id).await?))
581 as Arc<dyn FxNode>
582 }
583 ObjectDescriptor::Symlink => Arc::new(FxSymlink::new(self.clone(), object_id)),
584 _ => bail!(FxfsError::Inconsistent),
585 };
586 placeholder.commit(&node);
587 Ok(node)
588 }
589 }
590 }
591
592 pub async fn open_file_by_id(
603 self: &Arc<Self>,
604 object_id: u64,
605 ) -> Result<OpenedNode<FxFile>, Error> {
606 let store = self.store();
607 let fs = store.filesystem();
608 let _guard = fs
612 .lock_manager()
613 .read_lock(lock_keys![LockKey::object(store.store_object_id(), object_id)])
614 .await;
615
616 match store.tree().find(&ObjectKey::object(object_id)).await? {
617 Some(ObjectItem {
618 value: ObjectValue::Object { kind: ObjectKind::File { .. }, .. },
619 ..
620 }) => {}
621 Some(_) => bail!(FxfsError::NotSupported),
622 None => bail!(FxfsError::NotFound),
623 }
624
625 if matches!(
632 store
633 .tree()
634 .find_value(&ObjectKey::graveyard_entry(
635 store.graveyard_directory_object_id(),
636 object_id,
637 ))
638 .await?,
639 Some(ObjectValue::Some)
640 ) {
641 bail!(FxfsError::NotFound);
642 }
643
644 let node = self
645 .get_or_load_node(object_id, ObjectDescriptor::File, None)
646 .await
647 .map_err(|_| FxfsError::NotFound)?;
648 let Ok(file) = node.into_any().downcast::<FxFile>() else {
649 bail!(FxfsError::NotSupported);
650 };
651 file.into_opened_node().ok_or_else(|| FxfsError::NotFound.into())
654 }
655
656 pub fn mark_directory_deleted(&self, object_id: u64) {
658 if let Some(node) = self.cache.get(object_id) {
659 if let Ok(dir) = node.into_any().downcast::<FxDirectory>() {
663 dir.set_deleted();
664 }
665 }
666 }
667
668 pub(super) async fn maybe_purge_file(
674 &self,
675 object_id: u64,
676 truncate_guard: Option<&TruncateGuard<'_>>,
677 ) -> Result<(), Error> {
678 if let Some(node) = self.cache.get(object_id) {
679 node.clone().mark_to_be_purged();
680 return Ok(());
681 }
682 self.store.purge(object_id, truncate_guard).await?;
684 Ok(())
685 }
686
687 pub fn start_background_task(
693 self: &Arc<Self>,
694 config: MemoryPressureConfig,
695 mem_monitor: Option<&MemoryPressureMonitor>,
696 ) {
697 let mut background_task = self.background_task.lock();
698 if background_task.is_none() {
699 let (tx, rx) = oneshot::channel();
700
701 let task = if let Some(mem_monitor) = mem_monitor {
702 fasync::Task::spawn(self.clone().background_task(
703 config,
704 mem_monitor.get_level_stream(),
705 rx,
706 ))
707 } else {
708 fasync::Task::spawn(self.clone().background_task(config, stream::pending(), rx))
710 };
711
712 *background_task = Some((task, tx));
713 }
714 }
715
716 #[trace]
717 async fn background_task(
718 self: Arc<Self>,
719 config: MemoryPressureConfig,
720 mut level_stream: impl Stream<Item = MemoryPressureLevel> + FusedStream + Unpin,
721 terminate: oneshot::Receiver<()>,
722 ) {
723 debug!(store_id = self.store.store_object_id(); "FxVolume::background_task start");
724 let mut terminate = terminate.fuse();
725 let mut level = MemoryPressureLevel::Normal;
727 let mut timer = pin!(fasync::Timer::new(config.for_level(&level).background_task_period));
728
729 loop {
730 let mut should_terminate = false;
731 let mut should_flush = false;
732 let mut low_mem = false;
733 let mut should_purge_layer_files = false;
734 let mut should_update_cache_limit = false;
735
736 futures::select_biased! {
737 _ = terminate => should_terminate = true,
738 new_level = level_stream.next() => {
739 let new_level = new_level.unwrap();
741 low_mem = matches!(new_level, MemoryPressureLevel::Critical);
744 should_purge_layer_files = true;
745 if new_level != level {
746 level = new_level;
747 should_update_cache_limit = true;
748 let level_config = config.for_level(&level);
749 timer.as_mut().reset(fasync::MonotonicInstant::after(
750 level_config.background_task_period.into())
751 );
752 debug!(
753 "Background task period changed to {:?} due to new memory pressure \
754 level ({:?}).",
755 config.for_level(&level).background_task_period, level
756 );
757 }
758 }
759 _ = timer => {
760 timer.as_mut().reset(fasync::MonotonicInstant::after(
761 config.for_level(&level).background_task_period.into())
762 );
763 should_flush = true;
764 should_purge_layer_files = !matches!(level, MemoryPressureLevel::Normal);
766 }
767 };
768 if should_terminate {
769 break;
770 }
771 if should_update_cache_limit {
773 self.dirent_cache.set_limit(config.for_level(&level).cache_size_limit);
774 }
775 if should_flush {
776 self.flush_all_files(FlushType::Sync).await;
777 self.dirent_cache.recycle_stale_files();
778 } else if low_mem {
779 self.minimize_memory().await;
781 }
782 if should_purge_layer_files {
783 for layer in self.store.tree().immutable_layer_set().layers {
784 layer.purge_cached_data();
785 }
786 }
787 }
788 debug!(store_id = self.store.store_object_id(); "FxVolume::background_task end");
789 }
790
791 pub fn report_pager_dirty(
795 self: Arc<Self>,
796 byte_count: u64,
797 mark_dirty: impl FnOnce() + Send + 'static,
798 ) {
799 let this = self.clone();
800 let callback = move || {
801 let prev = this.pager_dirty_byte_count.fetch_add(byte_count, Ordering::Relaxed);
802 mark_dirty();
803 fxfs_trace::counter!("dirty-bytes", 0, this.name => prev.saturating_add(byte_count));
804 };
805 if let Some(parent) = self.parent.upgrade() {
806 parent.report_pager_dirty(byte_count, self, callback);
807 } else {
808 callback();
809 }
810 }
811
812 pub fn report_pager_clean(&self, byte_count: u64) {
814 if let Some(parent) = self.parent.upgrade() {
815 parent.report_pager_clean(byte_count);
816 }
817 let prev = self.pager_dirty_byte_count.fetch_sub(byte_count, Ordering::Relaxed);
818 fxfs_trace::counter!("dirty-bytes", 0, self.name => prev.saturating_sub(byte_count));
819 }
820
821 #[trace]
822 pub async fn flush_all_files(&self, flush_type: FlushType) {
823 let mut flushed = 0;
824 for file in self.cache.files() {
825 if let Some(node) = file.into_opened_node() {
826 if let Err(e) = FxFile::flush(&node, flush_type).await {
827 warn!(
828 store_id = self.store.store_object_id(),
829 oid = node.object_id(),
830 error:? = e;
831 "Failed to flush",
832 )
833 }
834 if flush_type == FlushType::LastChance {
835 let file = node.clone();
836 std::mem::drop(node);
837 file.force_clean();
838 }
839 }
840 flushed += 1;
841 }
842 debug!(store_id = self.store.store_object_id(), file_count = flushed; "FxVolume flushed");
843 }
844
845 #[trace]
852 pub async fn minimize_memory(&self) {
853 for file in self.cache.files() {
854 if let Some(node) = file.into_opened_node() {
855 if let Err(e) = node.handle().minimize_memory().await {
856 warn!(
857 store_id = self.store.store_object_id(),
858 oid = node.object_id(),
859 error:? = e;
860 "Failed to flush",
861 )
862 }
863 }
864 }
865 }
866
867 pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
869 if let Some(guard) = self.scope.try_active_guard() {
870 self.executor.spawn_detached(FutureWithGuard::new(guard, task));
871 }
872 }
873
874 #[cfg(any(test, feature = "testing"))]
877 pub fn try_unwrap(self: Arc<Self>) -> Option<FxVolume> {
878 self.poisoned.store(true, Ordering::Relaxed);
879 match Arc::try_unwrap(self) {
880 Ok(volume) => {
881 volume.poisoned.store(false, Ordering::Relaxed);
882 Some(volume)
883 }
884 Err(this) => {
885 info!(
887 "background_task: {}, profile_state: {}, dirent_cache count: {}, \
888 pager strong file refs={}, no tasks={}",
889 this.background_task.lock().is_some(),
890 this.profile_state.lock().is_some(),
891 this.dirent_cache.len(),
892 crate::pager::STRONG_FILE_REFS.load(Ordering::Relaxed),
893 {
894 let mut no_tasks = pin!(this.scope.wait());
895 no_tasks
896 .poll_unpin(&mut std::task::Context::from_waker(
897 std::task::Waker::noop(),
898 ))
899 .is_ready()
900 },
901 );
902 None
903 }
904 }
905 }
906
907 pub async fn handle_file_backed_volume_provider_requests(
908 this: Weak<Self>,
909 scope: ExecutionScope,
910 mut requests: FileBackedVolumeProviderRequestStream,
911 ) -> Result<(), Error> {
912 while let Some(request) = requests.try_next().await? {
913 match request {
914 FileBackedVolumeProviderRequest::Open {
915 parent_directory_token,
916 name,
917 server_end,
918 control_handle: _,
919 } => {
920 let Some(_guard) = scope.try_active_guard() else {
922 bail!("Volume shutting down")
923 };
924 let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
925 match this
926 .scope
927 .token_registry()
928 .get_owner_and_rights(parent_directory_token)
932 .and_then(|dir| {
933 dir.ok_or(zx::Status::BAD_HANDLE).and_then(|(dir, rights)| {
934 if !rights.contains(fio::Rights::MODIFY_DIRECTORY) {
935 return Err(zx::Status::BAD_HANDLE);
936 }
937 dir.into_any()
938 .downcast::<FxDirectory>()
939 .map_err(|_| zx::Status::BAD_HANDLE)
940 })
941 }) {
942 Ok(dir) => {
943 dir.open_block_file(&name, server_end).await;
944 }
945 Err(status) => {
946 let _ = server_end.close_with_epitaph(status).unwrap_or_else(|e| {
947 error!(error:? = e; "open failed to send epitaph");
948 });
949 }
950 }
951 }
952 }
953 }
954 Ok(())
955 }
956
957 pub async fn handle_project_id_requests(
958 this: Weak<Self>,
959 scope: ExecutionScope,
960 mut requests: ProjectIdRequestStream,
961 ) -> Result<(), Error> {
962 while let Some(request) = requests.try_next().await? {
963 let Some(_guard) = scope.try_active_guard() else { bail!("Volume shutting down") };
965 let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
966 let store_id = this.store.store_object_id();
967
968 match request {
969 ProjectIdRequest::SetLimit { responder, project_id, bytes, nodes } => {
970 let result = if let Some(project_id) = ProjectId::new(project_id) {
971 this.store()
972 .set_project_limit(project_id, bytes, nodes)
973 .await
974 .map_err(|error| {
975 error!(error:?, store_id, project_id; "Failed to set project limit");
976 map_to_raw_status(error)
977 })
978 } else {
979 Err(zx::Status::OUT_OF_RANGE.into_raw())
980 };
981 responder.send(result)?
982 }
983 ProjectIdRequest::Clear { responder, project_id } => {
984 let result = if let Some(project_id) = ProjectId::new(project_id) {
985 this.store().clear_project_limit(project_id).await.map_err(|error| {
986 error!(error:?, store_id, project_id; "Failed to clear project limit");
987 map_to_raw_status(error)
988 })
989 } else {
990 Err(zx::Status::OUT_OF_RANGE.into_raw())
991 };
992 responder.send(result)?
993 }
994 ProjectIdRequest::SetForNode { responder, node_id, project_id } => {
995 let result = if let Some(project_id) = ProjectId::new(project_id) {
996 this.store()
997 .set_project_for_node(node_id, project_id)
998 .await
999 .map_err(|error| {
1000 error!(error:?, store_id, node_id, project_id; "Failed to apply node.");
1001 map_to_raw_status(error)
1002 })
1003 } else {
1004 Err(zx::Status::OUT_OF_RANGE.into_raw())
1005 };
1006 responder.send(result)?
1007 }
1008 ProjectIdRequest::GetForNode { responder, node_id } => responder.send(
1009 this.store()
1010 .get_project_for_node(node_id)
1011 .await
1012 .map(ProjectIdExt::raw)
1013 .map_err(|error| {
1014 error!(error:?, store_id, node_id; "Failed to get node.");
1015 map_to_raw_status(error)
1016 }),
1017 )?,
1018 ProjectIdRequest::ClearForNode { responder, node_id } => responder.send(
1019 this.store().clear_project_for_node(node_id).await.map_err(|error| {
1020 error!(error:?, store_id, node_id; "Failed to clear for node.");
1021 map_to_raw_status(error)
1022 }),
1023 )?,
1024 ProjectIdRequest::List { responder, token } => {
1025 responder.send(match this.list_projects(&token).await {
1026 Ok((ref entries, ref next_token)) => Ok((entries, next_token.as_ref())),
1027 Err(error) => {
1028 error!(error:?, store_id, token:?; "Failed to list projects.");
1029 Err(map_to_raw_status(error))
1030 }
1031 })?
1032 }
1033 ProjectIdRequest::Info { responder, project_id } => {
1034 responder.send(match this.project_info(project_id).await {
1035 Ok((ref limit, ref usage)) => Ok((limit, usage)),
1036 Err(error) => {
1037 error!(error:?, store_id, project_id; "Failed to get project info.");
1038 Err(map_to_raw_status(error))
1039 }
1040 })?
1041 }
1042 }
1043 }
1044 Ok(())
1045 }
1046
1047 pub fn clear_caches(&self) {
1049 self.dirent_cache.clear();
1050 self.store.clear_caches();
1051 }
1052
1053 pub async fn handle_debug_requests(
1054 this: Weak<Self>,
1055 scope: ExecutionScope,
1056 mut requests: DebugRequestStream,
1057 ) -> Result<(), Error> {
1058 while let Some(request) = requests.try_next().await? {
1059 let Some(_guard) = scope.try_active_guard() else { bail!("Volume shutting down") };
1061 let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
1062
1063 match request {
1064 DebugRequest::ClearCaches { responder } => {
1065 this.clear_caches();
1066 let fs = this.store.filesystem();
1067 let fs = fs.ensure_local();
1068 fs.root_store().clear_caches();
1069 fs.root_parent_store().clear_caches();
1070 fs.allocator().tree().clear_cache();
1071 responder.send(Ok(()))?;
1072 }
1073 DebugRequest::Compact { responder } => {
1074 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
1075 }
1076 DebugRequest::DeleteProfile { responder, .. } => {
1077 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
1078 }
1079 DebugRequest::RecordAndReplayProfile { responder, .. } => {
1080 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
1081 }
1082 DebugRequest::ReplayXorRecordProfile { responder, .. } => {
1083 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
1084 }
1085 DebugRequest::StopProfileTasks { responder } => {
1086 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
1087 }
1088 }
1089 }
1090 Ok(())
1091 }
1092
1093 const MAX_PROJECT_ENTRIES: usize = 8184;
1097
1098 async fn list_projects(
1101 &self,
1102 last_token: &Option<Box<ProjectIterToken>>,
1103 ) -> Result<(Vec<u64>, Option<ProjectIterToken>), Error> {
1104 let (entries, token) = self
1105 .store()
1106 .list_projects(
1107 last_token.as_ref().and_then(|v| ProjectId::new(v.value)),
1108 Self::MAX_PROJECT_ENTRIES,
1109 )
1110 .await?;
1111 Ok((
1112 entries.into_iter().map(ProjectId::raw).collect(),
1113 token.map(|value| ProjectIterToken { value: value.raw() }),
1114 ))
1115 }
1116
1117 async fn project_info(&self, project_id: u64) -> Result<(BytesAndNodes, BytesAndNodes), Error> {
1118 let project_id = ProjectId::new(project_id).ok_or(FxfsError::OutOfRange)?;
1119 let (limit, usage) = self.store().project_info(project_id).await?;
1120 ensure!(limit.is_some() || usage.is_some(), FxfsError::NotFound);
1122 Ok((
1123 limit.map_or_else(
1124 || BytesAndNodes { bytes: u64::MAX, nodes: u64::MAX },
1125 |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
1126 ),
1127 usage.map_or_else(
1128 || BytesAndNodes { bytes: 0, nodes: 0 },
1129 |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
1130 ),
1131 ))
1132 }
1133}
1134
1135#[cfg(any(test, feature = "testing"))]
1136impl Drop for FxVolume {
1137 fn drop(&mut self) {
1138 assert!(!*self.poisoned.get_mut());
1139 }
1140}
1141
1142impl HandleOwner for FxVolume {}
1143
1144impl AsRef<ObjectStore> for FxVolume {
1145 fn as_ref(&self) -> &ObjectStore {
1146 &self.store
1147 }
1148}
1149
1150impl AsRef<dyn ObjectStoreTrait> for FxVolume {
1151 fn as_ref(&self) -> &dyn ObjectStoreTrait {
1152 &*self.store
1153 }
1154}
1155
1156#[async_trait]
1157impl FsInspectVolume for FxVolume {
1158 async fn get_volume_data(&self) -> Option<VolumeData> {
1159 let _guard = self.scope.try_active_guard()?;
1161
1162 let object_count = self.store().object_count();
1163 let (used_bytes, bytes_limit) = self
1164 .store
1165 .filesystem()
1166 .ensure_local()
1167 .allocator()
1168 .owner_allocation_info(self.store.store_object_id());
1169 let encrypted = self.store().crypt().is_some();
1170 let port_koid = fasync::EHandle::local().port().as_handle_ref().koid().unwrap().raw_koid();
1171 Some(VolumeData { bytes_limit, used_bytes, used_nodes: object_count, encrypted, port_koid })
1172 }
1173}
1174
1175pub trait RootDir: FxNode + DirectoryEntry {
1176 fn as_directory_entry(self: Arc<Self>) -> Arc<dyn DirectoryEntry>;
1177
1178 fn serve(self: Arc<Self>, flags: fio::Flags, server_end: ServerEnd<fio::DirectoryMarker>);
1179
1180 fn as_node(self: Arc<Self>) -> Arc<dyn FxNode>;
1181
1182 fn register_additional_volume_services(
1183 self: Arc<Self>,
1184 _svc_dir: &Simple,
1185 ) -> Result<(), Error> {
1186 Ok(())
1187 }
1188}
1189
1190#[derive(Clone)]
1191pub struct FxVolumeAndRoot {
1192 volume: Arc<FxVolume>,
1193 root: Arc<dyn RootDir>,
1194
1195 admin_scope: ExecutionScope,
1197
1198 outgoing_dir: Arc<Simple>,
1200}
1201
1202impl FxVolumeAndRoot {
1203 pub async fn new<T: From<Directory<FxVolume>> + RootDir>(
1204 parent: Weak<VolumesDirectory>,
1205 store: Arc<ObjectStore>,
1206 unique_id: u64,
1207 volume_name: String,
1208 page_fault_counter: Arc<PageFaultCounter>,
1209 memory_pressure_config: MemoryPressureConfig,
1210 ) -> Result<Self, Error> {
1211 let volume = Arc::new(FxVolume::new(
1212 parent,
1213 store,
1214 unique_id,
1215 volume_name,
1216 page_fault_counter.clone(),
1217 memory_pressure_config,
1218 )?);
1219 let root_object_id = volume.store().root_directory_object_id();
1220 let root_dir = Directory::open(&volume, root_object_id).await?;
1221 let root = Arc::<T>::new(root_dir.into()) as Arc<dyn RootDir>;
1222 volume
1223 .cache
1224 .get_or_reserve(root_object_id)
1225 .await
1226 .placeholder()
1227 .unwrap()
1228 .commit(&root.clone().as_node());
1229 Ok(Self {
1230 volume,
1231 root,
1232 admin_scope: ExecutionScope::new(),
1233 outgoing_dir: vfs::directory::immutable::simple(),
1234 })
1235 }
1236
1237 pub fn volume(&self) -> &Arc<FxVolume> {
1238 &self.volume
1239 }
1240
1241 pub fn root(&self) -> &Arc<dyn RootDir> {
1242 &self.root
1243 }
1244
1245 pub fn admin_scope(&self) -> &ExecutionScope {
1246 &self.admin_scope
1247 }
1248
1249 pub fn outgoing_dir(&self) -> &Arc<Simple> {
1250 &self.outgoing_dir
1251 }
1252
1253 pub fn root_dir(&self) -> Arc<FxDirectory> {
1255 self.root().clone().into_any().downcast::<FxDirectory>().expect("Invalid type for root")
1256 }
1257
1258 pub fn into_volume(self) -> Arc<FxVolume> {
1259 self.volume
1260 }
1261}
1262
1263const TOTAL_NODES: u64 = i64::MAX as u64;
1267
1268const FXFS_INFO_NAME_FIDL: [i8; 32] = [
1271 0x66, 0x78, 0x66, 0x73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1272 0, 0, 0, 0,
1273];
1274
1275fn info_to_filesystem_info(
1276 info: filesystem::Info,
1277 block_size: BlockSize,
1278 object_count: u64,
1279 fs_id: u64,
1280) -> fio::FilesystemInfo {
1281 fio::FilesystemInfo {
1282 total_bytes: info.total_bytes,
1283 used_bytes: info.used_bytes,
1284 total_nodes: TOTAL_NODES,
1285 used_nodes: object_count,
1286 free_shared_pool_bytes: 0,
1288 fs_id,
1289 block_size: block_size.get() as u32,
1290 max_filename_size: fio::MAX_NAME_LENGTH as u32,
1291 fs_type: fidl_fuchsia_fs::VfsType::Fxfs.into_primitive(),
1292 padding: 0,
1293 name: FXFS_INFO_NAME_FIDL,
1294 }
1295}
1296
1297#[cfg(test)]
1298mod tests {
1299 use super::DIRENT_CACHE_LIMIT;
1300 use crate::fuchsia::file::FxFile;
1301 use crate::fuchsia::fxblob::testing::{self as blob_testing, BlobFixture};
1302 use crate::fuchsia::memory_pressure::MemoryPressureLevel;
1303 use crate::fuchsia::pager::PagerBacked;
1304 use crate::fuchsia::profile::{RECORDED, new_profile_state};
1305 use crate::fuchsia::testing::{
1306 TestFixture, TestFixtureOptions, close_dir_checked, close_file_checked, open_dir,
1307 open_dir_checked, open_file, open_file_checked,
1308 };
1309 use crate::fuchsia::volume::{FxVolume, MemoryPressureConfig, MemoryPressureLevelConfig};
1310 use crate::fuchsia::volumes_directory::VolumesDirectory;
1311 use delivery_blob::CompressionMode;
1312 use fidl_fuchsia_fs_startup::CreateOptions;
1313 use fidl_fuchsia_fxfs::{BytesAndNodes, ProjectIdMarker};
1314 use fidl_fuchsia_io as fio;
1315 use fs_inspect::FsInspectVolume;
1316 use fuchsia_async::{self as fasync, TimeoutExt as _};
1317 use fuchsia_component_client::connect_to_protocol_at_dir_svc;
1318 use fuchsia_fs::file;
1319 use fxfs::filesystem::{FxFilesystem, FxFilesystemBuilder};
1320 use fxfs::fsck::{fsck, fsck_volume};
1321 use fxfs::object_store::directory::replace_child;
1322 use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
1323 use fxfs::object_store::volume::root_volume;
1324 use fxfs::object_store::{HandleOptions, ObjectDescriptor, ObjectStore, StoreOptions};
1325 use fxfs_crypt_common::CryptBase;
1326 use fxfs_crypto::{Crypt, WrappingKeyId};
1327 use fxfs_insecure_crypto::new_insecure_crypt;
1328 use refaults_vmo::PageFaultCounter;
1329 use std::sync::atomic::Ordering;
1330 use std::sync::{Arc, Weak};
1331 use std::time::Duration;
1332 use storage_device::DeviceHolder;
1333 use storage_device::fake_device::FakeDevice;
1334 use zx::Status;
1335
1336 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
1337
1338 #[fuchsia::test(threads = 10)]
1339 async fn test_rename_different_dirs() {
1340 use zx::Event;
1341
1342 let fixture = TestFixture::new().await;
1343 let root = fixture.root();
1344
1345 let src = open_dir_checked(
1346 &root,
1347 "foo",
1348 fio::Flags::FLAG_MAYBE_CREATE
1349 | fio::PERM_READABLE
1350 | fio::PERM_WRITABLE
1351 | fio::Flags::PROTOCOL_DIRECTORY,
1352 Default::default(),
1353 )
1354 .await;
1355
1356 let dst = open_dir_checked(
1357 &root,
1358 "bar",
1359 fio::Flags::FLAG_MAYBE_CREATE
1360 | fio::PERM_READABLE
1361 | fio::PERM_WRITABLE
1362 | fio::Flags::PROTOCOL_DIRECTORY,
1363 Default::default(),
1364 )
1365 .await;
1366
1367 let f = open_file_checked(
1368 &root,
1369 "foo/a",
1370 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1371 &Default::default(),
1372 )
1373 .await;
1374 close_file_checked(f).await;
1375
1376 let (status, dst_token) = dst.deprecated_get_token().await.expect("FIDL call failed");
1377 Status::ok(status).expect("get_token failed");
1378 src.rename("a", Event::from(dst_token.unwrap()), "b")
1379 .await
1380 .expect("FIDL call failed")
1381 .expect("rename failed");
1382
1383 assert_eq!(
1384 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1385 .await
1386 .expect_err("Open succeeded")
1387 .root_cause()
1388 .downcast_ref::<Status>()
1389 .expect("No status"),
1390 &Status::NOT_FOUND,
1391 );
1392 let f =
1393 open_file_checked(&root, "bar/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1394 close_file_checked(f).await;
1395
1396 close_dir_checked(dst).await;
1397 close_dir_checked(src).await;
1398 fixture.close().await;
1399 }
1400
1401 #[fuchsia::test(threads = 10)]
1402 async fn test_rename_same_dir() {
1403 use zx::Event;
1404 let fixture = TestFixture::new().await;
1405 let root = fixture.root();
1406
1407 let src = open_dir_checked(
1408 &root,
1409 "foo",
1410 fio::Flags::FLAG_MAYBE_CREATE
1411 | fio::PERM_READABLE
1412 | fio::PERM_WRITABLE
1413 | fio::Flags::PROTOCOL_DIRECTORY,
1414 Default::default(),
1415 )
1416 .await;
1417
1418 let f = open_file_checked(
1419 &root,
1420 "foo/a",
1421 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1422 &Default::default(),
1423 )
1424 .await;
1425 close_file_checked(f).await;
1426
1427 let (status, src_token) = src.deprecated_get_token().await.expect("FIDL call failed");
1428 Status::ok(status).expect("get_token failed");
1429 src.rename("a", Event::from(src_token.unwrap()), "b")
1430 .await
1431 .expect("FIDL call failed")
1432 .expect("rename failed");
1433
1434 assert_eq!(
1435 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1436 .await
1437 .expect_err("Open succeeded")
1438 .root_cause()
1439 .downcast_ref::<Status>()
1440 .expect("No status"),
1441 &Status::NOT_FOUND,
1442 );
1443 let f =
1444 open_file_checked(&root, "foo/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1445 close_file_checked(f).await;
1446
1447 close_dir_checked(src).await;
1448 fixture.close().await;
1449 }
1450
1451 #[fuchsia::test(threads = 10)]
1452 async fn test_rename_overwrites_file() {
1453 use zx::Event;
1454 let fixture = TestFixture::new().await;
1455 let root = fixture.root();
1456
1457 let src = open_dir_checked(
1458 &root,
1459 "foo",
1460 fio::Flags::FLAG_MAYBE_CREATE
1461 | fio::PERM_READABLE
1462 | fio::PERM_WRITABLE
1463 | fio::Flags::PROTOCOL_DIRECTORY,
1464 Default::default(),
1465 )
1466 .await;
1467
1468 let dst = open_dir_checked(
1469 &root,
1470 "bar",
1471 fio::Flags::FLAG_MAYBE_CREATE
1472 | fio::PERM_READABLE
1473 | fio::PERM_WRITABLE
1474 | fio::Flags::PROTOCOL_DIRECTORY,
1475 Default::default(),
1476 )
1477 .await;
1478
1479 let src_file = open_file_checked(
1481 &root,
1482 "foo/a",
1483 fio::Flags::FLAG_MAYBE_CREATE
1484 | fio::PERM_READABLE
1485 | fio::PERM_WRITABLE
1486 | fio::Flags::PROTOCOL_FILE,
1487 &Default::default(),
1488 )
1489 .await;
1490 let buf = vec![0xaa as u8; 8192];
1491 file::write(&src_file, buf.as_slice()).await.expect("Failed to write to file");
1492 close_file_checked(src_file).await;
1493
1494 let f = open_file_checked(
1496 &root,
1497 "bar/b",
1498 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1499 &Default::default(),
1500 )
1501 .await;
1502 close_file_checked(f).await;
1503
1504 let (status, dst_token) = dst.deprecated_get_token().await.expect("FIDL call failed");
1505 Status::ok(status).expect("get_token failed");
1506 src.rename("a", Event::from(dst_token.unwrap()), "b")
1507 .await
1508 .expect("FIDL call failed")
1509 .expect("rename failed");
1510
1511 assert_eq!(
1512 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1513 .await
1514 .expect_err("Open succeeded")
1515 .root_cause()
1516 .downcast_ref::<Status>()
1517 .expect("No status"),
1518 &Status::NOT_FOUND,
1519 );
1520 let file = open_file_checked(
1521 &root,
1522 "bar/b",
1523 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1524 &Default::default(),
1525 )
1526 .await;
1527 let buf = file::read(&file).await.expect("read file failed");
1528 assert_eq!(buf, vec![0xaa as u8; 8192]);
1529 close_file_checked(file).await;
1530
1531 close_dir_checked(dst).await;
1532 close_dir_checked(src).await;
1533 fixture.close().await;
1534 }
1535
1536 #[fuchsia::test(threads = 10)]
1537 async fn test_rename_overwrites_dir() {
1538 use zx::Event;
1539 let fixture = TestFixture::new().await;
1540 let root = fixture.root();
1541
1542 let src = open_dir_checked(
1543 &root,
1544 "foo",
1545 fio::Flags::FLAG_MAYBE_CREATE
1546 | fio::PERM_READABLE
1547 | fio::PERM_WRITABLE
1548 | fio::Flags::PROTOCOL_DIRECTORY,
1549 Default::default(),
1550 )
1551 .await;
1552
1553 let dst = open_dir_checked(
1554 &root,
1555 "bar",
1556 fio::Flags::FLAG_MAYBE_CREATE
1557 | fio::PERM_READABLE
1558 | fio::PERM_WRITABLE
1559 | fio::Flags::PROTOCOL_DIRECTORY,
1560 Default::default(),
1561 )
1562 .await;
1563
1564 open_dir_checked(
1566 &root,
1567 "foo/a",
1568 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1569 Default::default(),
1570 )
1571 .await;
1572 open_file_checked(
1573 &root,
1574 "foo/a/file",
1575 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1576 &Default::default(),
1577 )
1578 .await;
1579 open_dir_checked(
1580 &root,
1581 "bar/b",
1582 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1583 Default::default(),
1584 )
1585 .await;
1586
1587 let (status, dst_token) = dst.deprecated_get_token().await.expect("FIDL call failed");
1588 Status::ok(status).expect("get_token failed");
1589 src.rename("a", Event::from(dst_token.unwrap()), "b")
1590 .await
1591 .expect("FIDL call failed")
1592 .expect("rename failed");
1593
1594 assert_eq!(
1595 open_dir(&root, "foo/a", fio::Flags::PROTOCOL_DIRECTORY, &Default::default())
1596 .await
1597 .expect_err("Open succeeded")
1598 .root_cause()
1599 .downcast_ref::<Status>()
1600 .expect("No status"),
1601 &Status::NOT_FOUND,
1602 );
1603 let f =
1604 open_file_checked(&root, "bar/b/file", fio::Flags::PROTOCOL_FILE, &Default::default())
1605 .await;
1606 close_file_checked(f).await;
1607
1608 close_dir_checked(dst).await;
1609 close_dir_checked(src).await;
1610
1611 fixture.close().await;
1612 }
1613
1614 #[fuchsia::test]
1615 async fn test_background_flush() {
1616 let fixture = TestFixture::new().await;
1617 {
1618 let file = open_file_checked(
1619 fixture.root(),
1620 "file",
1621 fio::Flags::FLAG_MAYBE_CREATE
1622 | fio::PERM_READABLE
1623 | fio::PERM_WRITABLE
1624 | fio::Flags::PROTOCOL_FILE,
1625 &Default::default(),
1626 )
1627 .await;
1628 let object_id = file
1629 .get_attributes(fio::NodeAttributesQuery::ID)
1630 .await
1631 .expect("Fidl get attr")
1632 .expect("get attr")
1633 .1
1634 .id
1635 .unwrap();
1636
1637 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1639
1640 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1642 let volume = fixture.volume().volume().clone();
1643
1644 let data_has_persisted = || async {
1645 let object =
1648 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1649 .await
1650 .expect("open_object failed");
1651 let data = object.contents(8192).await.expect("read failed");
1652 data.len() == 1 && data[..] == [123u8]
1653 };
1654 assert!(!data_has_persisted().await);
1655
1656 fixture.volume().volume().start_background_task(
1657 MemoryPressureConfig {
1658 mem_normal: MemoryPressureLevelConfig {
1659 background_task_period: Duration::from_millis(100),
1660 ..Default::default()
1661 },
1662 mem_warning: Default::default(),
1663 mem_critical: Default::default(),
1664 },
1665 None,
1666 );
1667
1668 const MAX_WAIT: Duration = Duration::from_secs(20);
1669 let wait_increments = Duration::from_millis(400);
1670 let mut total_waited = Duration::ZERO;
1671
1672 while total_waited < MAX_WAIT {
1673 fasync::Timer::new(wait_increments).await;
1674 total_waited += wait_increments;
1675
1676 if data_has_persisted().await {
1677 break;
1678 }
1679 }
1680
1681 assert!(data_has_persisted().await);
1682 }
1683
1684 fixture.close().await;
1685 }
1686
1687 #[fuchsia::test(threads = 2)]
1688 async fn test_background_flush_with_warning_memory_pressure() {
1689 let fixture = TestFixture::new().await;
1690 {
1691 let file = open_file_checked(
1692 fixture.root(),
1693 "file",
1694 fio::Flags::FLAG_MAYBE_CREATE
1695 | fio::PERM_READABLE
1696 | fio::PERM_WRITABLE
1697 | fio::Flags::PROTOCOL_FILE,
1698 &Default::default(),
1699 )
1700 .await;
1701 let object_id = file
1702 .get_attributes(fio::NodeAttributesQuery::ID)
1703 .await
1704 .expect("Fidl get attr")
1705 .expect("get attr")
1706 .1
1707 .id
1708 .unwrap();
1709
1710 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1712
1713 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1715 let volume = fixture.volume().volume().clone();
1716
1717 let data_has_persisted = || async {
1718 let object =
1721 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1722 .await
1723 .expect("open_object failed");
1724 let data = object.contents(8192).await.expect("read failed");
1725 data.len() == 1 && data[..] == [123u8]
1726 };
1727 assert!(!data_has_persisted().await);
1728
1729 let flush_config = MemoryPressureConfig {
1731 mem_normal: MemoryPressureLevelConfig {
1732 background_task_period: Duration::from_secs(20),
1733 cache_size_limit: DIRENT_CACHE_LIMIT,
1734 ..Default::default()
1735 },
1736 mem_warning: MemoryPressureLevelConfig {
1737 background_task_period: Duration::from_millis(100),
1738 cache_size_limit: 100,
1739 ..Default::default()
1740 },
1741 mem_critical: MemoryPressureLevelConfig {
1742 background_task_period: Duration::from_secs(20),
1743 cache_size_limit: 50,
1744 ..Default::default()
1745 },
1746 };
1747 fixture.volume().volume().start_background_task(
1748 flush_config,
1749 fixture.volumes_directory().memory_pressure_monitor(),
1750 );
1751
1752 fixture
1754 .memory_pressure_proxy()
1755 .on_level_changed(MemoryPressureLevel::Warning)
1756 .await
1757 .expect("Failed to send memory pressure level change");
1758
1759 const MAX_WAIT: Duration = Duration::from_secs(3);
1762 let wait_increments = Duration::from_millis(400);
1763 let mut total_waited = Duration::ZERO;
1764
1765 while total_waited < MAX_WAIT {
1766 fasync::Timer::new(wait_increments).await;
1767 total_waited += wait_increments;
1768
1769 if data_has_persisted().await {
1770 break;
1771 }
1772 }
1773
1774 assert!(data_has_persisted().await);
1775 assert_eq!(fixture.volume().volume().dirent_cache().limit(), 100);
1776 }
1777
1778 fixture.close().await;
1779 }
1780
1781 #[fuchsia::test(threads = 2)]
1782 async fn test_background_flush_with_critical_memory_pressure() {
1783 let fixture = TestFixture::new().await;
1784 {
1785 let file = open_file_checked(
1786 fixture.root(),
1787 "file",
1788 fio::Flags::FLAG_MAYBE_CREATE
1789 | fio::PERM_READABLE
1790 | fio::PERM_WRITABLE
1791 | fio::Flags::PROTOCOL_FILE,
1792 &Default::default(),
1793 )
1794 .await;
1795 let object_id = file
1796 .get_attributes(fio::NodeAttributesQuery::ID)
1797 .await
1798 .expect("Fidl get attr")
1799 .expect("get attr")
1800 .1
1801 .id
1802 .unwrap();
1803
1804 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1806
1807 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1809 let volume = fixture.volume().volume().clone();
1810
1811 let data_has_persisted = || async {
1812 let object =
1815 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1816 .await
1817 .expect("open_object failed");
1818 let data = object.contents(8192).await.expect("read failed");
1819 data.len() == 1 && data[..] == [123u8]
1820 };
1821 assert!(!data_has_persisted().await);
1822
1823 let flush_config = MemoryPressureConfig {
1824 mem_normal: MemoryPressureLevelConfig {
1825 cache_size_limit: DIRENT_CACHE_LIMIT,
1826 ..Default::default()
1827 },
1828 mem_warning: MemoryPressureLevelConfig {
1829 cache_size_limit: 100,
1830 ..Default::default()
1831 },
1832 mem_critical: MemoryPressureLevelConfig {
1833 cache_size_limit: 50,
1834 ..Default::default()
1835 },
1836 };
1837 fixture.volume().volume().start_background_task(
1838 flush_config,
1839 fixture.volumes_directory().memory_pressure_monitor(),
1840 );
1841
1842 fixture
1844 .memory_pressure_proxy()
1845 .on_level_changed(MemoryPressureLevel::Critical)
1846 .await
1847 .expect("Failed to send memory pressure level change");
1848
1849 const MAX_WAIT: Duration = Duration::from_secs(2);
1851 let wait_increments = Duration::from_millis(400);
1852 let mut total_waited = Duration::ZERO;
1853
1854 while total_waited < MAX_WAIT {
1855 fasync::Timer::new(wait_increments).await;
1856 total_waited += wait_increments;
1857
1858 if data_has_persisted().await {
1859 break;
1860 }
1861 }
1862
1863 assert!(data_has_persisted().await);
1864 assert_eq!(fixture.volume().volume().dirent_cache().limit(), 50);
1865 }
1866
1867 fixture.close().await;
1868 }
1869
1870 #[fuchsia::test(threads = 2)]
1873 async fn test_query_info_unmounted_volume() {
1874 const TEST_VOLUME: &str = "test_1234";
1875 let crypt = Arc::new(new_insecure_crypt()) as Arc<dyn Crypt>;
1876
1877 let fixture = TestFixture::new().await;
1878 {
1879 let volumes_directory = fixture.volumes_directory();
1880 let volume = volumes_directory
1881 .create_and_mount_volume(
1882 TEST_VOLUME,
1883 Some(crypt.clone()),
1884 false,
1885 CreateOptions::default(),
1886 )
1887 .await
1888 .unwrap();
1889
1890 assert!(volume.volume().get_volume_data().await.is_some());
1891
1892 volumes_directory
1894 .lock()
1895 .await
1896 .unmount(volume.volume().store().store_object_id())
1897 .await
1898 .expect("unmount failed");
1899
1900 assert!(volume.volume().get_volume_data().await.is_none());
1902 }
1903 fixture.close().await;
1904 }
1905
1906 #[fuchsia::test]
1907 async fn test_project_limit_persistence() {
1908 const BYTES_LIMIT_1: u64 = 123456;
1909 const NODES_LIMIT_1: u64 = 4321;
1910 const BYTES_LIMIT_2: u64 = 456789;
1911 const NODES_LIMIT_2: u64 = 9876;
1912 const VOLUME_NAME: &str = "A";
1913 const FILE_NAME: &str = "B";
1914 const PROJECT_ID: u64 = 42;
1915 const PROJECT_ID2: u64 = 343;
1916 let volume_store_id;
1917 let node_id;
1918 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1919 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
1920 {
1921 let page_fault_counter =
1922 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
1923 let volumes_directory = VolumesDirectory::new(
1924 root_volume(filesystem.clone()).await.unwrap(),
1925 Weak::new(),
1926 None,
1927 page_fault_counter,
1928 MemoryPressureConfig::default(),
1929 )
1930 .await
1931 .unwrap();
1932
1933 let volume_and_root = volumes_directory
1934 .create_and_mount_volume(VOLUME_NAME, None, false, CreateOptions::default())
1935 .await
1936 .expect("create unencrypted volume failed");
1937 volume_store_id = volume_and_root.volume().store().store_object_id();
1938
1939 let (volume_dir_proxy, dir_server_end) =
1940 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1941 volumes_directory
1942 .serve_volume(&volume_and_root, dir_server_end, false)
1943 .expect("serve_volume failed");
1944
1945 let project_proxy =
1946 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1947 .expect("Unable to connect to project id service");
1948
1949 project_proxy
1950 .set_limit(0, BYTES_LIMIT_1, NODES_LIMIT_1)
1951 .await
1952 .unwrap()
1953 .expect_err("Should not set limits for project id 0");
1954
1955 assert_eq!(
1956 project_proxy.clear(0).await.unwrap().expect_err("Should not clear project id 0"),
1957 Status::OUT_OF_RANGE.into_raw()
1958 );
1959
1960 assert_eq!(
1961 project_proxy
1962 .info(0)
1963 .await
1964 .unwrap()
1965 .expect_err("Should not get info for project id 0"),
1966 Status::OUT_OF_RANGE.into_raw()
1967 );
1968
1969 project_proxy
1970 .set_limit(PROJECT_ID, BYTES_LIMIT_1, NODES_LIMIT_1)
1971 .await
1972 .unwrap()
1973 .expect("To set limits");
1974 {
1975 let BytesAndNodes { bytes, nodes } =
1976 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1977 assert_eq!(bytes, BYTES_LIMIT_1);
1978 assert_eq!(nodes, NODES_LIMIT_1);
1979 }
1980
1981 let file_proxy = {
1982 let (root_proxy, root_server_end) =
1983 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1984 volume_dir_proxy
1985 .open(
1986 "root",
1987 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1988 &Default::default(),
1989 root_server_end.into_channel(),
1990 )
1991 .expect("Failed to open volume root");
1992
1993 open_file_checked(
1994 &root_proxy,
1995 FILE_NAME,
1996 fio::Flags::FLAG_MAYBE_CREATE
1997 | fio::PERM_READABLE
1998 | fio::PERM_WRITABLE
1999 | fio::Flags::PROTOCOL_FILE,
2000 &Default::default(),
2001 )
2002 .await
2003 };
2004
2005 let (_, immutable_attributes) =
2006 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2007 node_id = immutable_attributes.id.unwrap();
2008
2009 project_proxy
2010 .set_for_node(node_id, 0)
2011 .await
2012 .unwrap()
2013 .expect_err("Should not set 0 project id");
2014
2015 project_proxy
2016 .set_for_node(node_id, PROJECT_ID)
2017 .await
2018 .unwrap()
2019 .expect("Setting project on node");
2020
2021 project_proxy
2022 .set_limit(PROJECT_ID, BYTES_LIMIT_2, NODES_LIMIT_2)
2023 .await
2024 .unwrap()
2025 .expect("To set limits");
2026 {
2027 let BytesAndNodes { bytes, nodes } =
2028 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
2029 assert_eq!(bytes, BYTES_LIMIT_2);
2030 assert_eq!(nodes, NODES_LIMIT_2);
2031 }
2032
2033 assert_eq!(
2034 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
2035 PROJECT_ID
2036 );
2037
2038 volumes_directory.terminate().await;
2039 filesystem.close().await.expect("close filesystem failed");
2040 }
2041 device = filesystem.take_device().await;
2042 device.ensure_unique();
2043 device.reopen(false);
2044 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2045 {
2046 fsck(filesystem.clone()).await.expect("Fsck");
2047 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
2048 let page_fault_counter =
2049 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
2050 let volumes_directory = VolumesDirectory::new(
2051 root_volume(filesystem.clone()).await.unwrap(),
2052 Weak::new(),
2053 None,
2054 page_fault_counter,
2055 MemoryPressureConfig::default(),
2056 )
2057 .await
2058 .unwrap();
2059 let volume_and_root = volumes_directory
2060 .mount_volume(VOLUME_NAME, None, false)
2061 .await
2062 .expect("mount unencrypted volume failed");
2063
2064 let (volume_proxy, _scope) = crate::volumes_directory::serve_startup_volume_proxy(
2065 &volumes_directory,
2066 VOLUME_NAME,
2067 );
2068
2069 let project_proxy = {
2070 let (volume_dir_proxy, dir_server_end) =
2071 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2072 volumes_directory
2073 .serve_volume(&volume_and_root, dir_server_end, false)
2074 .expect("serve_volume failed");
2075
2076 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2077 .expect("Unable to connect to project id service")
2078 };
2079
2080 let usage_bytes_and_nodes = {
2081 let (
2082 BytesAndNodes { bytes: limit_bytes, nodes: limit_nodes },
2083 usage_bytes_and_nodes,
2084 ) = project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info");
2085 assert_eq!(limit_bytes, BYTES_LIMIT_2);
2086 assert_eq!(limit_nodes, NODES_LIMIT_2);
2087 usage_bytes_and_nodes
2088 };
2089
2090 project_proxy.clear(PROJECT_ID).await.unwrap().expect("To clear limits");
2092
2093 assert_eq!(
2094 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
2095 PROJECT_ID
2096 );
2097 project_proxy
2098 .set_for_node(node_id, PROJECT_ID2)
2099 .await
2100 .unwrap()
2101 .expect("Changing project");
2102 assert_eq!(
2103 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
2104 PROJECT_ID2
2105 );
2106
2107 assert_eq!(
2108 project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
2109 Status::NOT_FOUND.into_raw()
2110 );
2111 assert_eq!(
2112 project_proxy.info(PROJECT_ID2).await.unwrap().expect("Fetching project info").1,
2113 usage_bytes_and_nodes
2114 );
2115
2116 std::mem::drop(volume_proxy);
2117 volumes_directory.terminate().await;
2118 std::mem::drop(volumes_directory);
2119 filesystem.close().await.expect("close filesystem failed");
2120 }
2121 device = filesystem.take_device().await;
2122 device.ensure_unique();
2123 device.reopen(false);
2124 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2125 fsck(filesystem.clone()).await.expect("Fsck");
2126 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
2127 let page_fault_counter =
2128 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
2129 let volumes_directory = VolumesDirectory::new(
2130 root_volume(filesystem.clone()).await.unwrap(),
2131 Weak::new(),
2132 None,
2133 page_fault_counter,
2134 MemoryPressureConfig::default(),
2135 )
2136 .await
2137 .unwrap();
2138 let volume_and_root = volumes_directory
2139 .mount_volume(VOLUME_NAME, None, false)
2140 .await
2141 .expect("mount unencrypted volume failed");
2142 let (volume_dir_proxy, dir_server_end) =
2143 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2144 volumes_directory
2145 .serve_volume(&volume_and_root, dir_server_end, false)
2146 .expect("serve_volume failed");
2147 let project_proxy = connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2148 .expect("Unable to connect to project id service");
2149 assert_eq!(
2150 project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
2151 Status::NOT_FOUND.into_raw()
2152 );
2153 volumes_directory.terminate().await;
2154 std::mem::drop(volumes_directory);
2155 filesystem.close().await.expect("close filesystem failed");
2156 }
2157
2158 #[fuchsia::test]
2159 async fn test_project_limit_accounting() {
2160 const BYTES_LIMIT: u64 = 123456;
2161 const NODES_LIMIT: u64 = 4321;
2162 const VOLUME_NAME: &str = "A";
2163 const FILE_NAME: &str = "B";
2164 const PROJECT_ID: u64 = 42;
2165 let volume_store_id;
2166 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2167 let first_object_id;
2168 let mut bytes_usage;
2169 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2170 {
2171 let page_fault_counter =
2172 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
2173 let volumes_directory = VolumesDirectory::new(
2174 root_volume(filesystem.clone()).await.unwrap(),
2175 Weak::new(),
2176 None,
2177 page_fault_counter,
2178 MemoryPressureConfig::default(),
2179 )
2180 .await
2181 .unwrap();
2182
2183 let volume_and_root = volumes_directory
2184 .create_and_mount_volume(
2185 VOLUME_NAME,
2186 Some(Arc::new(new_insecure_crypt())),
2187 false,
2188 CreateOptions::default(),
2189 )
2190 .await
2191 .expect("create unencrypted volume failed");
2192 volume_store_id = volume_and_root.volume().store().store_object_id();
2193
2194 let (volume_dir_proxy, dir_server_end) =
2195 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2196 volumes_directory
2197 .serve_volume(&volume_and_root, dir_server_end, false)
2198 .expect("serve_volume failed");
2199
2200 let project_proxy =
2201 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2202 .expect("Unable to connect to project id service");
2203
2204 project_proxy
2205 .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
2206 .await
2207 .unwrap()
2208 .expect("To set limits");
2209 {
2210 let BytesAndNodes { bytes, nodes } =
2211 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
2212 assert_eq!(bytes, BYTES_LIMIT);
2213 assert_eq!(nodes, NODES_LIMIT);
2214 }
2215
2216 let file_proxy = {
2217 let (root_proxy, root_server_end) =
2218 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2219 volume_dir_proxy
2220 .open(
2221 "root",
2222 fio::PERM_READABLE | fio::PERM_WRITABLE,
2223 &Default::default(),
2224 root_server_end.into_channel(),
2225 )
2226 .expect("Failed to open volume root");
2227
2228 open_file_checked(
2229 &root_proxy,
2230 FILE_NAME,
2231 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2232 &Default::default(),
2233 )
2234 .await
2235 };
2236
2237 assert_eq!(
2238 8192,
2239 file_proxy
2240 .write(&vec![0xff as u8; 8192])
2241 .await
2242 .expect("FIDL call failed")
2243 .map_err(Status::err_from_raw)
2244 .expect("File write was successful")
2245 );
2246 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2247
2248 {
2249 let BytesAndNodes { bytes, nodes } =
2250 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2251 assert_eq!(bytes, 0);
2252 assert_eq!(nodes, 0);
2253 }
2254
2255 let (_, immutable_attributes) =
2256 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2257 let node_id = immutable_attributes.id.unwrap();
2258
2259 first_object_id = node_id;
2260 project_proxy
2261 .set_for_node(node_id, PROJECT_ID)
2262 .await
2263 .unwrap()
2264 .expect("Setting project on node");
2265
2266 bytes_usage = {
2267 let BytesAndNodes { bytes, nodes } =
2268 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2269 assert!(bytes > 0);
2270 assert_eq!(nodes, 1);
2271 bytes
2272 };
2273
2274 assert_eq!(
2276 8192,
2277 file_proxy
2278 .write(&vec![0xff as u8; 8192])
2279 .await
2280 .expect("FIDL call failed")
2281 .map_err(Status::err_from_raw)
2282 .expect("File write was successful")
2283 );
2284 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2285 bytes_usage = {
2286 let BytesAndNodes { bytes, nodes } =
2287 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2288 assert!(bytes > bytes_usage);
2289 assert_eq!(nodes, 1);
2290 bytes
2291 };
2292
2293 volumes_directory.terminate().await;
2294 filesystem.close().await.expect("close filesystem failed");
2295 }
2296 device = filesystem.take_device().await;
2297 device.ensure_unique();
2298 device.reopen(false);
2299 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2300 {
2301 fsck(filesystem.clone()).await.expect("Fsck");
2302 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2303 .await
2304 .expect("Fsck volume");
2305 let page_fault_counter =
2306 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
2307 let volumes_directory = VolumesDirectory::new(
2308 root_volume(filesystem.clone()).await.unwrap(),
2309 Weak::new(),
2310 None,
2311 page_fault_counter,
2312 MemoryPressureConfig::default(),
2313 )
2314 .await
2315 .unwrap();
2316 let volume_and_root = volumes_directory
2317 .mount_volume(VOLUME_NAME, Some(Arc::new(new_insecure_crypt())), false)
2318 .await
2319 .expect("mount unencrypted volume failed");
2320
2321 let (root_proxy, project_proxy) = {
2322 let (volume_dir_proxy, dir_server_end) =
2323 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2324 volumes_directory
2325 .serve_volume(&volume_and_root, dir_server_end, false)
2326 .expect("serve_volume failed");
2327
2328 let (root_proxy, root_server_end) =
2329 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2330 volume_dir_proxy
2331 .open(
2332 "root",
2333 fio::PERM_READABLE | fio::PERM_WRITABLE,
2334 &Default::default(),
2335 root_server_end.into_channel(),
2336 )
2337 .expect("Failed to open volume root");
2338 let project_proxy = {
2339 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2340 .expect("Unable to connect to project id service")
2341 };
2342 (root_proxy, project_proxy)
2343 };
2344
2345 {
2346 let BytesAndNodes { bytes, nodes } =
2347 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2348 assert_eq!(bytes, bytes_usage);
2349 assert_eq!(nodes, 1);
2350 }
2351
2352 assert_eq!(
2353 project_proxy
2354 .get_for_node(first_object_id)
2355 .await
2356 .unwrap()
2357 .expect("Checking project"),
2358 PROJECT_ID
2359 );
2360 root_proxy
2361 .unlink(FILE_NAME, &fio::UnlinkOptions::default())
2362 .await
2363 .expect("FIDL call failed")
2364 .expect("unlink failed");
2365 filesystem.graveyard().flush().await;
2366
2367 {
2368 let BytesAndNodes { bytes, nodes } =
2369 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2370 assert_eq!(bytes, 0);
2371 assert_eq!(nodes, 0);
2372 }
2373
2374 let file_proxy = open_file_checked(
2375 &root_proxy,
2376 FILE_NAME,
2377 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2378 &Default::default(),
2379 )
2380 .await;
2381
2382 let (_, immutable_attributes) =
2383 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2384 let node_id = immutable_attributes.id.unwrap();
2385
2386 project_proxy
2387 .set_for_node(node_id, PROJECT_ID)
2388 .await
2389 .unwrap()
2390 .expect("Applying project");
2391
2392 bytes_usage = {
2393 let BytesAndNodes { bytes, nodes } =
2394 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2395 assert!(bytes < bytes_usage);
2397 assert_eq!(nodes, 1);
2398 bytes
2399 };
2400
2401 assert_eq!(
2402 8192,
2403 file_proxy
2404 .write(&vec![0xff as u8; 8192])
2405 .await
2406 .expect("FIDL call failed")
2407 .map_err(Status::err_from_raw)
2408 .expect("File write was successful")
2409 );
2410 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2411 bytes_usage = {
2412 let BytesAndNodes { bytes, nodes } =
2413 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2414 assert!(bytes > bytes_usage);
2415 assert_eq!(nodes, 1);
2416 bytes
2417 };
2418
2419 file_proxy.resize(0).await.expect("FIDL call failed").expect("Resize file");
2421 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2422 {
2423 let BytesAndNodes { bytes, nodes } =
2424 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2425 assert!(bytes < bytes_usage);
2426 assert_eq!(nodes, 1);
2427 };
2428
2429 project_proxy
2431 .clear_for_node(node_id)
2432 .await
2433 .expect("FIDL call failed")
2434 .expect("Clear failed.");
2435 {
2436 let BytesAndNodes { bytes, nodes } =
2437 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2438 assert_eq!(bytes, 0);
2439 assert_eq!(nodes, 0);
2440 };
2441
2442 volumes_directory.terminate().await;
2443 filesystem.close().await.expect("close filesystem failed");
2444 }
2445 device = filesystem.take_device().await;
2446 device.ensure_unique();
2447 device.reopen(false);
2448 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2449 fsck(filesystem.clone()).await.expect("Fsck");
2450 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2451 .await
2452 .expect("Fsck volume");
2453 filesystem.close().await.expect("close filesystem failed");
2454 }
2455
2456 #[fuchsia::test]
2457 async fn test_project_node_inheritance() {
2458 const BYTES_LIMIT: u64 = 123456;
2459 const NODES_LIMIT: u64 = 4321;
2460 const VOLUME_NAME: &str = "A";
2461 const DIR_NAME: &str = "B";
2462 const SUBDIR_NAME: &str = "C";
2463 const FILE_NAME: &str = "D";
2464 const PROJECT_ID: u64 = 42;
2465 let volume_store_id;
2466 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2467 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2468 {
2469 let page_fault_counter =
2470 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
2471 let volumes_directory = VolumesDirectory::new(
2472 root_volume(filesystem.clone()).await.unwrap(),
2473 Weak::new(),
2474 None,
2475 page_fault_counter,
2476 MemoryPressureConfig::default(),
2477 )
2478 .await
2479 .unwrap();
2480
2481 let volume_and_root = volumes_directory
2482 .create_and_mount_volume(
2483 VOLUME_NAME,
2484 Some(Arc::new(new_insecure_crypt())),
2485 false,
2486 CreateOptions::default(),
2487 )
2488 .await
2489 .expect("create unencrypted volume failed");
2490 volume_store_id = volume_and_root.volume().store().store_object_id();
2491
2492 let (volume_dir_proxy, dir_server_end) =
2493 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2494 volumes_directory
2495 .serve_volume(&volume_and_root, dir_server_end, false)
2496 .expect("serve_volume failed");
2497
2498 let project_proxy =
2499 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2500 .expect("Unable to connect to project id service");
2501
2502 project_proxy
2503 .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
2504 .await
2505 .unwrap()
2506 .expect("To set limits");
2507
2508 let dir_proxy = {
2509 let (root_proxy, root_server_end) =
2510 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2511 volume_dir_proxy
2512 .open(
2513 "root",
2514 fio::PERM_READABLE | fio::PERM_WRITABLE,
2515 &Default::default(),
2516 root_server_end.into_channel(),
2517 )
2518 .expect("Failed to open volume root");
2519
2520 open_dir_checked(
2521 &root_proxy,
2522 DIR_NAME,
2523 fio::Flags::FLAG_MAYBE_CREATE
2524 | fio::PERM_READABLE
2525 | fio::PERM_WRITABLE
2526 | fio::Flags::PROTOCOL_DIRECTORY,
2527 Default::default(),
2528 )
2529 .await
2530 };
2531 {
2532 let (_, immutable_attributes) =
2533 dir_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2534 let node_id = immutable_attributes.id.unwrap();
2535
2536 project_proxy
2537 .set_for_node(node_id, PROJECT_ID)
2538 .await
2539 .unwrap()
2540 .expect("Setting project on node");
2541 }
2542
2543 let subdir_proxy = open_dir_checked(
2544 &dir_proxy,
2545 SUBDIR_NAME,
2546 fio::Flags::FLAG_MAYBE_CREATE
2547 | fio::PERM_READABLE
2548 | fio::PERM_WRITABLE
2549 | fio::Flags::PROTOCOL_DIRECTORY,
2550 Default::default(),
2551 )
2552 .await;
2553 {
2554 let (_, immutable_attributes) = subdir_proxy
2555 .get_attributes(fio::NodeAttributesQuery::ID)
2556 .await
2557 .unwrap()
2558 .unwrap();
2559 let node_id = immutable_attributes.id.unwrap();
2560
2561 assert_eq!(
2562 project_proxy
2563 .get_for_node(node_id)
2564 .await
2565 .unwrap()
2566 .expect("Setting project on node"),
2567 PROJECT_ID
2568 );
2569 }
2570
2571 let file_proxy = open_file_checked(
2572 &subdir_proxy,
2573 FILE_NAME,
2574 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2575 &Default::default(),
2576 )
2577 .await;
2578 {
2579 let (_, immutable_attributes) =
2580 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2581 let node_id = immutable_attributes.id.unwrap();
2582
2583 assert_eq!(
2584 project_proxy
2585 .get_for_node(node_id)
2586 .await
2587 .unwrap()
2588 .expect("Setting project on node"),
2589 PROJECT_ID
2590 );
2591 }
2592
2593 let tmpfile_proxy = open_file_checked(
2596 &subdir_proxy,
2597 ".",
2598 fio::Flags::PROTOCOL_FILE
2599 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2600 | fio::PERM_READABLE,
2601 &fio::Options::default(),
2602 )
2603 .await;
2604 {
2605 let (_, immutable_attributes) = tmpfile_proxy
2606 .get_attributes(fio::NodeAttributesQuery::ID)
2607 .await
2608 .unwrap()
2609 .unwrap();
2610 let node_id: u64 = immutable_attributes.id.unwrap();
2611 assert_eq!(
2612 project_proxy
2613 .get_for_node(node_id)
2614 .await
2615 .unwrap()
2616 .expect("Setting project on node"),
2617 PROJECT_ID
2618 );
2619 }
2620
2621 let BytesAndNodes { nodes, .. } =
2622 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2623 assert_eq!(nodes, 3);
2624 volumes_directory.terminate().await;
2625 filesystem.close().await.expect("close filesystem failed");
2626 }
2627 device = filesystem.take_device().await;
2628 device.ensure_unique();
2629 device.reopen(false);
2630 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2631 fsck(filesystem.clone()).await.expect("Fsck");
2632 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2633 .await
2634 .expect("Fsck volume");
2635 filesystem.close().await.expect("close filesystem failed");
2636 }
2637
2638 #[fuchsia::test]
2639 async fn test_project_listing() {
2640 const VOLUME_NAME: &str = "A";
2641 const FILE_NAME: &str = "B";
2642 const NON_ZERO_PROJECT_ID: u64 = 3;
2643 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2644 let volume_store_id;
2645 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2646 {
2647 let page_fault_counter =
2648 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
2649 let volumes_directory = VolumesDirectory::new(
2650 root_volume(filesystem.clone()).await.unwrap(),
2651 Weak::new(),
2652 None,
2653 page_fault_counter,
2654 MemoryPressureConfig::default(),
2655 )
2656 .await
2657 .unwrap();
2658 let volume_and_root = volumes_directory
2659 .create_and_mount_volume(VOLUME_NAME, None, false, CreateOptions::default())
2660 .await
2661 .expect("create unencrypted volume failed");
2662 volume_store_id = volume_and_root.volume().store().store_object_id();
2663
2664 let (volume_dir_proxy, dir_server_end) =
2665 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2666 volumes_directory
2667 .serve_volume(&volume_and_root, dir_server_end, false)
2668 .expect("serve_volume failed");
2669 let project_proxy =
2670 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2671 .expect("Unable to connect to project id service");
2672 assert!(FxVolume::MAX_PROJECT_ENTRIES >= 4);
2674 let num_entries = u64::try_from(FxVolume::MAX_PROJECT_ENTRIES + 3).unwrap();
2676 for project_id in 1..=num_entries {
2677 project_proxy.set_limit(project_id, 1, 1).await.unwrap().expect("To set limits");
2678 }
2679
2680 let file_proxy = {
2683 let (root_proxy, root_server_end) =
2684 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2685 volume_dir_proxy
2686 .open(
2687 "root",
2688 fio::PERM_READABLE | fio::PERM_WRITABLE,
2689 &Default::default(),
2690 root_server_end.into_channel(),
2691 )
2692 .expect("Failed to open volume root");
2693
2694 open_file_checked(
2695 &root_proxy,
2696 FILE_NAME,
2697 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2698 &Default::default(),
2699 )
2700 .await
2701 };
2702 let (_, immutable_attributes) =
2703 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2704 let node_id = immutable_attributes.id.unwrap();
2705 project_proxy
2706 .set_for_node(node_id, NON_ZERO_PROJECT_ID)
2707 .await
2708 .unwrap()
2709 .expect("Setting project on node");
2710 {
2711 let BytesAndNodes { nodes, .. } = project_proxy
2712 .info(NON_ZERO_PROJECT_ID)
2713 .await
2714 .unwrap()
2715 .expect("Fetching project info")
2716 .1;
2717 assert_eq!(nodes, 1);
2718 }
2719
2720 let (mut entries, mut next_token) =
2722 project_proxy.list(None).await.unwrap().expect("To get project listing");
2723 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2724 assert!(next_token.is_some());
2725 assert!(entries.contains(&1));
2726 assert!(entries.contains(&3));
2727 assert!(!entries.contains(&num_entries));
2728 (entries, next_token) = project_proxy
2730 .list(next_token.as_deref())
2731 .await
2732 .unwrap()
2733 .expect("To get project listing");
2734 assert_eq!(entries.len(), 3);
2735 assert!(next_token.is_none());
2736 assert!(entries.contains(&num_entries));
2737 assert!(!entries.contains(&1));
2738 assert!(!entries.contains(&3));
2739 project_proxy.clear(1).await.unwrap().expect("Clear project");
2741 project_proxy.clear(3).await.unwrap().expect("Clear project");
2742 (entries, next_token) =
2743 project_proxy.list(None).await.unwrap().expect("To get project listing");
2744 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2745 assert!(next_token.is_some());
2746 assert!(!entries.contains(&num_entries));
2747 assert!(!entries.contains(&1));
2748 assert!(entries.contains(&3));
2749 (entries, next_token) = project_proxy
2750 .list(next_token.as_deref())
2751 .await
2752 .unwrap()
2753 .expect("To get project listing");
2754 assert_eq!(entries.len(), 2);
2755 assert!(next_token.is_none());
2756 assert!(entries.contains(&num_entries));
2757 project_proxy.clear(2).await.unwrap().expect("Clear project");
2759 project_proxy.clear(4).await.unwrap().expect("Clear project");
2760 (entries, next_token) =
2761 project_proxy.list(None).await.unwrap().expect("To get project listing");
2762 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2763 assert!(next_token.is_none());
2764 assert!(entries.contains(&num_entries));
2765 volumes_directory.terminate().await;
2766 filesystem.close().await.expect("close filesystem failed");
2767 }
2768 device = filesystem.take_device().await;
2769 device.ensure_unique();
2770 device.reopen(false);
2771 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2772 fsck(filesystem.clone()).await.expect("Fsck");
2773 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
2774 filesystem.close().await.expect("close filesystem failed");
2775 }
2776
2777 #[fuchsia::test(threads = 10)]
2778 async fn test_profile_blob() {
2779 let mut hashes = Vec::new();
2780 let device = {
2781 let fixture = blob_testing::new_blob_fixture().await;
2782
2783 for i in 0..3u64 {
2784 let hash =
2785 fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
2786 hashes.push(hash);
2787 }
2788 fixture.close().await
2789 };
2790 device.ensure_unique();
2791
2792 device.reopen(false);
2793 let mut device = {
2794 let fixture = blob_testing::open_blob_fixture(device).await;
2795 fixture
2796 .volume()
2797 .volume()
2798 .record_and_replay_profile(new_profile_state(true), "foo")
2799 .await
2800 .expect("Recording");
2801
2802 let mut writable = [0u8];
2804 for hash in &hashes {
2805 let vmo = fixture.get_blob_vmo(*hash).await;
2806 vmo.read(&mut writable, 0).expect("Vmo read");
2807 }
2808 fixture.volume().volume().stop_profile_tasks().await;
2809 fixture.close().await
2810 };
2811
2812 for i in 0..3 {
2814 device.ensure_unique();
2815 device.reopen(false);
2816 let fixture = blob_testing::open_blob_fixture(device).await;
2817 {
2818 for hash in &hashes {
2820 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2821 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2822 }
2823
2824 fixture
2825 .volume()
2826 .volume()
2827 .record_and_replay_profile(new_profile_state(true), "foo")
2828 .await
2829 .expect("Replaying");
2830
2831 {
2833 let store = fixture.volume().volume().store();
2834 let store_id = store.store_object_id();
2835 let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
2836 let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
2837 let mut transaction = store
2838 .new_transaction(
2839 lock_keys!(
2840 LockKey::object(store_id, dir.object_id()),
2841 LockKey::object(store_id, old_file),
2842 ),
2843 Options::default(),
2844 )
2845 .await
2846 .unwrap();
2847 replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
2848 .await
2849 .expect("Replace old profile.");
2850 transaction.commit().await.unwrap();
2851 assert!(
2852 dir.lookup("foo").await.unwrap().is_none(),
2853 "Old profile should be moved"
2854 );
2855 }
2856
2857 async {
2859 for hash in &hashes {
2860 let _vmo = fixture.get_blob_vmo(*hash).await;
2863 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2864 while blob.vmo().info().unwrap().populated_bytes == 0 {
2865 fasync::Timer::new(Duration::from_millis(25)).await;
2866 }
2867 }
2868 }
2869 .on_timeout(std::time::Duration::from_secs(120), || {
2870 panic!("Replay did not page in for all VMOs")
2871 })
2872 .await;
2873
2874 fixture.volume().volume().stop_profile_tasks().await;
2876 }
2877 device = fixture.close().await;
2878 }
2879 }
2880
2881 #[fuchsia::test(threads = 10)]
2882 async fn test_profile_file() {
2883 let mut hashes = Vec::new();
2884 let crypt_file_id;
2885 let device = {
2886 let fixture = TestFixture::new().await;
2887 {
2889 let crypt_dir = open_dir_checked(
2890 fixture.root(),
2891 "crypt_dir",
2892 fio::Flags::FLAG_MUST_CREATE
2893 | fio::PERM_WRITABLE
2894 | fio::Flags::PERM_GET_ATTRIBUTES,
2895 Default::default(),
2896 )
2897 .await;
2898 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2899 crypt
2900 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2901 .expect("add_wrapping_key failed");
2902 crypt_dir
2903 .update_attributes(&fio::MutableNodeAttributes {
2904 encryption_policy: Some(fio::FscryptPolicy {
2905 key_identifier: WRAPPING_KEY_ID,
2906 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2907 }),
2908 ..Default::default()
2909 })
2910 .await
2911 .expect("update_attributes wire call failed")
2912 .expect("update_attributes failed");
2913 let crypt_file = open_file_checked(
2914 &crypt_dir,
2915 "crypt_file",
2916 fio::Flags::FLAG_MUST_CREATE
2917 | fio::PERM_WRITABLE
2918 | fio::Flags::PERM_GET_ATTRIBUTES,
2919 &Default::default(),
2920 )
2921 .await;
2922 crypt_file.write("asdf".as_bytes()).await.unwrap().expect("Writing crypt file");
2923 crypt_file_id = crypt_file
2924 .get_attributes(fio::NodeAttributesQuery::ID)
2925 .await
2926 .unwrap()
2927 .expect("Get id")
2928 .1
2929 .id
2930 .expect("Reading id in response");
2931 }
2932
2933 for i in 0..3u64 {
2934 let file_proxy = open_file_checked(
2935 fixture.root(),
2936 &i.to_string(),
2937 fio::Flags::FLAG_MUST_CREATE
2938 | fio::PERM_WRITABLE
2939 | fio::Flags::PERM_GET_ATTRIBUTES,
2940 &Default::default(),
2941 )
2942 .await;
2943 file_proxy.write(i.to_string().as_bytes()).await.unwrap().expect("Writing file");
2944 let id = file_proxy
2945 .get_attributes(fio::NodeAttributesQuery::ID)
2946 .await
2947 .unwrap()
2948 .expect("Get id")
2949 .1
2950 .id
2951 .expect("Reading id in response");
2952 hashes.push((i, id));
2953 }
2954 fixture.close().await
2955 };
2956 device.ensure_unique();
2957
2958 device.reopen(false);
2959 let mut device = {
2960 let fixture = TestFixture::open(
2961 device,
2962 TestFixtureOptions { format: false, ..Default::default() },
2963 )
2964 .await;
2965 fixture
2966 .volume()
2967 .volume()
2968 .record_and_replay_profile(new_profile_state(false), "foo")
2969 .await
2970 .expect("Recording");
2971
2972 {
2973 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2974 crypt
2975 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2976 .expect("add wrapping key failed");
2977 let crypt_file = open_file_checked(
2978 fixture.root(),
2979 "crypt_dir/crypt_file",
2980 fio::PERM_READABLE,
2981 &Default::default(),
2982 )
2983 .await;
2984 crypt_file.read(1).await.unwrap().expect("Reading crypt file");
2985 }
2986 for (i, _) in &hashes {
2988 let file_proxy = open_file_checked(
2989 fixture.root(),
2990 &i.to_string(),
2991 fio::PERM_READABLE,
2992 &Default::default(),
2993 )
2994 .await;
2995 file_proxy.read(1).await.unwrap().expect("Reading file");
2996 }
2997 fixture.volume().volume().stop_profile_tasks().await;
2998 fixture.close().await
2999 };
3000
3001 for i in 0..3 {
3003 device.ensure_unique();
3004 device.reopen(false);
3005 let fixture = TestFixture::open(
3006 device,
3007 TestFixtureOptions { format: false, ..Default::default() },
3008 )
3009 .await;
3010 {
3011 let volume = fixture.volume().volume().clone();
3013 {
3015 let crypt_file = volume
3016 .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
3017 .await
3018 .expect("Opening file internally")
3019 .into_any()
3020 .downcast::<FxFile>()
3021 .expect("Should be file");
3022 assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
3023 }
3024 for (_, id) in &hashes {
3025 let file = volume
3026 .get_or_load_node(*id, ObjectDescriptor::File, None)
3027 .await
3028 .expect("Opening file internally")
3029 .into_any()
3030 .downcast::<FxFile>()
3031 .expect("Should be file");
3032 assert_eq!(file.vmo().info().unwrap().populated_bytes, 0);
3033 }
3034
3035 fixture
3036 .volume()
3037 .volume()
3038 .record_and_replay_profile(new_profile_state(false), "foo")
3039 .await
3040 .expect("Replaying");
3041
3042 {
3044 let store = fixture.volume().volume().store();
3045 let store_id = store.store_object_id();
3046 let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
3047 let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
3048 let mut transaction = store
3049 .new_transaction(
3050 lock_keys!(
3051 LockKey::object(store_id, dir.object_id()),
3052 LockKey::object(store_id, old_file),
3053 ),
3054 Options::default(),
3055 )
3056 .await
3057 .unwrap();
3058 replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
3059 .await
3060 .expect("Replace old profile.");
3061 transaction.commit().await.unwrap();
3062 assert!(
3063 dir.lookup("foo").await.unwrap().is_none(),
3064 "Old profile should be moved"
3065 );
3066 }
3067
3068 async {
3070 for (_, id) in &hashes {
3071 let file = volume
3072 .get_or_load_node(*id, ObjectDescriptor::File, None)
3073 .await
3074 .expect("Opening file internally")
3075 .into_any()
3076 .downcast::<FxFile>()
3077 .expect("Should be file");
3078 while file.vmo().info().unwrap().populated_bytes == 0 {
3079 fasync::Timer::new(Duration::from_millis(25)).await;
3080 }
3081 }
3082 }
3083 .on_timeout(std::time::Duration::from_secs(120), || {
3084 panic!("Replay did not page in for all VMOs")
3085 })
3086 .await;
3087 {
3089 let crypt_file = volume
3090 .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
3091 .await
3092 .expect("Opening file internally")
3093 .into_any()
3094 .downcast::<FxFile>()
3095 .expect("Should be file");
3096 assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
3097 }
3098
3099 for (i, _) in &hashes {
3101 let _file_proxy = open_file_checked(
3102 fixture.root(),
3103 &i.to_string(),
3104 fio::PERM_READABLE,
3105 &Default::default(),
3106 )
3107 .await;
3108 }
3109
3110 fixture.volume().volume().stop_profile_tasks().await;
3112 }
3113 device = fixture.close().await;
3114 }
3115 }
3116
3117 #[fuchsia::test(threads = 10)]
3118 async fn test_profile_update() {
3119 let mut hashes = Vec::new();
3120 let device = {
3121 let fixture = blob_testing::new_blob_fixture().await;
3122 for i in 0..2u64 {
3123 let hash =
3124 fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
3125 hashes.push(hash);
3126 }
3127 fixture.close().await
3128 };
3129 device.ensure_unique();
3130
3131 device.reopen(false);
3132 let device = {
3133 let fixture = blob_testing::open_blob_fixture(device).await;
3134
3135 {
3136 let volume = fixture.volume().volume();
3137 volume
3138 .record_and_replay_profile(new_profile_state(true), "foo")
3139 .await
3140 .expect("Recording");
3141
3142 let original_recorded = RECORDED.load(Ordering::Relaxed);
3143
3144 {
3146 let mut writable = [0u8];
3147 let hash = &hashes[0];
3148 let vmo = fixture.get_blob_vmo(*hash).await;
3149 vmo.read(&mut writable, 0).expect("Vmo read");
3150 }
3151
3152 while RECORDED.load(Ordering::Relaxed) == original_recorded {
3155 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3156 }
3157
3158 volume.stop_profile_tasks().await;
3159 }
3160 fixture.close().await
3161 };
3162
3163 device.ensure_unique();
3164 device.reopen(false);
3165 let fixture = blob_testing::open_blob_fixture(device).await;
3166 {
3167 for hash in &hashes {
3170 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3171 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
3172 }
3173
3174 let volume = fixture.volume().volume();
3175
3176 volume
3177 .record_and_replay_profile(new_profile_state(true), "foo")
3178 .await
3179 .expect("Replaying");
3180
3181 async {
3183 let hash = &hashes[0];
3184 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3185 while blob.vmo().info().unwrap().populated_bytes == 0 {
3186 fasync::Timer::new(Duration::from_millis(25)).await;
3187 }
3188 }
3189 .on_timeout(std::time::Duration::from_secs(120), || {
3190 panic!("Replay did not page in for all VMOs")
3191 })
3192 .await;
3193
3194 let original_recorded = RECORDED.load(Ordering::Relaxed);
3195
3196 {
3198 let mut writable = [0u8];
3199 let hash = &hashes[1];
3200 let vmo = fixture.get_blob_vmo(*hash).await;
3201 vmo.read(&mut writable, 0).expect("Vmo read");
3202 }
3203
3204 while RECORDED.load(Ordering::Relaxed) == original_recorded {
3207 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3208 }
3209
3210 volume.stop_profile_tasks().await;
3212 }
3213 let device = fixture.close().await;
3214
3215 device.ensure_unique();
3216 device.reopen(false);
3217 let fixture = blob_testing::open_blob_fixture(device).await;
3218 {
3219 for hash in &hashes {
3222 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3223 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
3224 }
3225
3226 fixture
3227 .volume()
3228 .volume()
3229 .record_and_replay_profile(new_profile_state(true), "foo")
3230 .await
3231 .expect("Replaying");
3232
3233 async {
3235 let hash = &hashes[1];
3236 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3237 while blob.vmo().info().unwrap().populated_bytes == 0 {
3238 fasync::Timer::new(Duration::from_millis(25)).await;
3239 }
3240 }
3241 .on_timeout(std::time::Duration::from_secs(30), || {
3242 panic!("Replay did not page in for all VMOs")
3243 })
3244 .await;
3245
3246 fixture.volume().volume().stop_profile_tasks().await;
3248
3249 {
3251 let hash = &hashes[0];
3252 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3253 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
3254 }
3255 }
3256 fixture.close().await;
3257 }
3258
3259 #[fuchsia::test(threads = 10)]
3260 async fn test_unencrypted_volume() {
3261 let fixture = TestFixture::new_unencrypted().await;
3262 let root = fixture.root();
3263
3264 let f = open_file_checked(
3265 &root,
3266 "foo",
3267 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3268 &Default::default(),
3269 )
3270 .await;
3271 close_file_checked(f).await;
3272
3273 fixture.close().await;
3274 }
3275
3276 #[fuchsia::test]
3277 async fn test_read_only_unencrypted_volume() {
3278 let fs = {
3280 let device = fxfs::filesystem::mkfs_with_volume(
3281 DeviceHolder::new(FakeDevice::new(8192, 512)),
3282 "vol",
3283 None,
3284 )
3285 .await
3286 .unwrap();
3287 device.reopen(true);
3289 FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3290 };
3291 {
3293 let root_volume = root_volume(fs.clone()).await.unwrap();
3294 let store = root_volume.volume("vol", StoreOptions::default()).await.unwrap();
3295 let unique_id = store.store_object_id();
3296 let page_fault_counter =
3297 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
3298 let volume = FxVolume::new(
3299 Weak::new(),
3300 store,
3301 unique_id,
3302 "vol".to_owned(),
3303 page_fault_counter,
3304 MemoryPressureConfig::default(),
3305 )
3306 .unwrap();
3307 volume.terminate().await;
3308 }
3309 fs.close().await.unwrap();
3311 let device = fs.take_device().await;
3312 device.ensure_unique();
3313 }
3314
3315 #[fuchsia::test]
3316 async fn test_read_only_encrypted_volume() {
3317 let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
3318 let fs = {
3320 let device = fxfs::filesystem::mkfs_with_volume(
3321 DeviceHolder::new(FakeDevice::new(8192, 512)),
3322 "vol",
3323 Some(crypt.clone()),
3324 )
3325 .await
3326 .unwrap();
3327 device.reopen(true);
3329 FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3330 };
3331 {
3333 let root_volume = root_volume(fs.clone()).await.unwrap();
3334 let store = root_volume
3335 .volume("vol", StoreOptions { crypt: Some(crypt), ..StoreOptions::default() })
3336 .await
3337 .unwrap();
3338 let unique_id = store.store_object_id();
3339 let page_fault_counter =
3340 Arc::new(PageFaultCounter::new().expect("Failed to create PageFaultCounter"));
3341 let volume = FxVolume::new(
3342 Weak::new(),
3343 store,
3344 unique_id,
3345 "vol".to_owned(),
3346 page_fault_counter,
3347 MemoryPressureConfig::default(),
3348 )
3349 .unwrap();
3350 volume.terminate().await;
3351 }
3352 fs.close().await.unwrap();
3354 let device = fs.take_device().await;
3355 device.ensure_unique();
3356 }
3357}