Skip to main content

fxfs_platform/fuchsia/
volume.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
60// LINT.IfChange
61// TODO:(b/299919008) Fix this number to something reasonable, or maybe just for fxblob.
62const DIRENT_CACHE_LIMIT: usize = 2000;
63// LINT.ThenChange(//src/storage/stressor/src/aggressive.rs)
64
65/// The read ahead/around size to target. Increase reads to be this size within the restrictions of
66/// the format for the target object.
67pub 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    /// The period to wait between flushes, as well as perform other background maintenance tasks
76    /// (e.g. purging caches).
77    pub background_task_period: Duration,
78
79    /// The limit of cached nodes.
80    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    /// The configuration to use at [`MemoryPressureLevel::Normal`].
95    pub mem_normal: MemoryPressureLevelConfig,
96
97    /// The configuration to use at [`MemoryPressureLevel::Warning`].
98    pub mem_warning: MemoryPressureLevelConfig,
99
100    /// The configuration to use at [`MemoryPressureLevel::Critical`].
101    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        // TODO(https://fxbug.dev/42061389): investigate a smarter strategy for determining flush
117        // frequency.
118        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
135/// FxVolume represents an opened volume. It is also a (weak) cache for all opened Nodes within the
136/// volume.
137pub 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    // A tuple of the actual task and a channel to signal to terminate the task.
146    background_task: Mutex<Option<(fasync::Task<()>, oneshot::Sender<()>)>>,
147
148    // Unique identifier of the filesystem that owns this volume.
149    fs_id: u64,
150
151    // The execution scope for this volume.
152    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    /// The number of dirty bytes in pager backed VMOs that belong to this volume. VolumesDirectory
164    /// holds a count for all volumes. This count is only used for tracing.
165    pager_dirty_byte_count: AtomicU64,
166
167    /// A trusted buffer allocator used for reading and decompressing blobs. Unlike the underlying
168    /// block device's buffer allocator (whose VMOs are shared with drivers and thus untrusted),
169    /// this allocator uses VMOs without the TRANSFER right. This prevents foreign processes or
170    /// drivers from mutating memory concurrently, allowing us to safely extract Rust slices
171    /// (`try_as_slice`/`try_as_mut_slice`) and perform zero-copy Merkle tree verification without
172    /// heap copies.
173    blob_allocator: OnceLock<Arc<BufferAllocator>>,
174
175    /// True if blob page requests for this volume are handled in the block driver.
176    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    /// Returns the trusted buffer allocator for this volume, initializing it on first access.
224    /// This is primarily used by `BlobDirectory` and `FxBlob` to perform zero-copy decompression
225    /// and Merkle verification safely.
226    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    /// Reports the filesystem info, but if the volume has a space limit applied then the space
264    /// available and space used are reported based on the volume instead.
265    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    /// Stop profiling, recover resources from it and finalize recordings.
281    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    /// Reads the named profile from the volume's internal profile directory into a VMO.
291    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    /// Writes `recorded_vmo` as the named profile in the volume's internal profile directory.
323    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    /// Opens or creates the profile directory in the volume's internal directory.
360    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        // Have to do separate calls to create the profile dir if necessary.
366        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    /// Starts recording a profile for the volume under the name given, and if a profile exists
407    /// under that same name it is replayed and will be replaced after by the new recording if it
408    /// is cleanly shutdown and finalized.
409    pub async fn record_and_replay_profile(
410        self: &Arc<Self>,
411        mut state: Box<dyn ProfileState>,
412        name: &str,
413    ) -> Result<(), Error> {
414        // We don't meddle in FxDirectory or FxFile here because we don't want a paged object.
415        // Normally we ensure that there's only one copy by using the Node cache on the volume, but
416        // that would create FxFile, so in this case we just assume that only one profile operation
417        // should be ongoing at a time, as that is ensured in `VolumesDirectory`.
418
419        // If there is a recording already, prepare to replay it.
420        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        // Begin recording first to ensure that we capture any activity from the replay.
424        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    /// Replays a profile if one exists, and only records if one does not exist.
442    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        // `NodeCache::terminate` will break any strong reference cycles contained within nodes
514        // (pager registration). The only remaining nodes should be those with open FIDL
515        // connections or vmo references in the process of handling the VMO_ZERO_CHILDREN signal.
516        // `ExecutionScope::shutdown` + `ExecutionScope::wait` will close the open FIDL connections
517        // and synchonrize the signal handling which should result in all nodes flushing and then
518        // dropping. Any async tasks required to flush a node should take an active guard on the
519        // `ExecutionScope` which will prevent `ExecutionScope::wait` from completing until all
520        // nodes are flushed.
521        self.scope.shutdown();
522        self.cache.terminate();
523        self.scope.wait().await;
524
525        // Make sure there are no deferred operations still pending for this volume.
526        Epoch::global().barrier().await;
527
528        // The dirent_cache must be cleared *after* shutting down the scope because there can be
529        // tasks that insert entries into the cache.
530        self.dirent_cache.clear();
531
532        if self.store.filesystem().options().read_only {
533            // If the filesystem is read only, we don't need to flush/sync anything.
534            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                // The store will be left in a safe state and there won't be data-loss unless
545                // there's an issue flushing the journal later.
546                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    /// Attempts to get a node from the node cache. If the node wasn't present in the cache, loads
560    /// the object from the object store, installing the returned node into the cache and returns
561    /// the newly created FxNode backed by the loaded object.  |parent| is only set on the node if
562    /// the node was not present in the cache.  Otherwise, it is ignored.
563    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                        // Can't use open_unchecked because we don't know if the dir is casefolded
579                        // or encrypted.
580                        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    /// Opens a linked regular file by object ID, returning a node with an open count held.
593    ///
594    /// Opening by ID skips the directory traversal that normally prevents open races, so this
595    /// performs the equivalent checks itself while holding a read lock on the object.
596    ///
597    /// The graveyard is checked before the node is loaded so that we never publish an `FxFile` in
598    /// the node cache for an object that is about to be destroyed.
599    ///
600    /// Returns [`FxfsError::NotFound`] if the object does not exist or is awaiting purge, and
601    /// [`FxfsError::NotSupported`] if it is not a regular file.
602    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        // A read lock blocks the commit of any transaction that would modify the object,
609        // including deletion. So the object cannot be unlinked or purged while the checks below
610        // run.
611        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        // An object with no remaining links is kept alive solely by its graveyard entry, and is
626        // either awaiting purge or is an unnamed temporary file. Its object record is
627        // indistinguishable from that of a linked file, because `adjust_refs` leaves `refs` at one
628        // for objects in the graveyard, so the graveyard entry is the only way to tell them apart.
629        // TODO(https:///fxbug.dev/570648396): Consider adding a permanent in-memory copy of the
630        // graveyard to make this faster.
631        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        // The open count must be taken before `_guard` is released. Once the open count is held,
652        // any later unlink defers the tombstone until the returned node is dropped.
653        file.into_opened_node().ok_or_else(|| FxfsError::NotFound.into())
654    }
655
656    /// Marks the given directory deleted.
657    pub fn mark_directory_deleted(&self, object_id: u64) {
658        if let Some(node) = self.cache.get(object_id) {
659            // It's possible that node is a placeholder, in which case we don't need to wait for it
660            // to be resolved because it should be blocked behind the locks that are held by the
661            // caller, and once they're dropped, it'll be found to be deleted via the tree.
662            if let Ok(dir) = node.into_any().downcast::<FxDirectory>() {
663                dir.set_deleted();
664            }
665        }
666    }
667
668    /// Removes resources associated with |object_id| (which ought to be a file), if there are no
669    /// open connections to that file.
670    ///
671    /// This must be called *after committing* a transaction which deletes the last reference to
672    /// |object_id|, since before that point, new connections could be established.
673    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        // If this fails, the graveyard should clean it up on next mount.
683        self.store.purge(object_id, truncate_guard).await?;
684        Ok(())
685    }
686
687    /// Starts the background work task.  This task will periodically:
688    ///   - scan all files and flush them to disk, and
689    ///   - purge unused cached data.
690    /// The task will hold a strong reference to the FxVolume while it is running, so the task must
691    /// be closed later with Self::terminate, or the FxVolume will never be dropped.
692    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                // With no memory pressure monitoring, just stub the stream out as always pending.
709                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        // Default to the normal period until updates come from the `level_stream`.
726        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                    // Because `level_stream` will never terminate, this is safe to unwrap.
740                    let new_level = new_level.unwrap();
741                    // At critical levels, it's okay to undertake expensive work immediately
742                    // to reclaim memory.
743                    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                    // Only purge layer file caches once we have elevated memory pressure.
765                    should_purge_layer_files = !matches!(level, MemoryPressureLevel::Normal);
766                }
767            };
768            if should_terminate {
769                break;
770            }
771            // Maybe close extra files *before* iterating them for flush/low mem.
772            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                // This is a softer version of flushing files, so don't bother if we're flushing.
780                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    /// Reports that a certain number of bytes will be dirtied in a pager-backed VMO.
792    ///
793    /// Note that this function may await flush tasks.
794    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    /// Reports that a certain number of bytes were cleaned in a pager-backed VMO.
813    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    /// Flushes only files with dirty pages.
846    ///
847    /// `PagedObjectHandle` tracks the number dirty pages locally (except for overwrite files) which
848    /// makes determining whether flushing a file will reduce the number of dirty pages efficient.
849    /// `flush_all_files` checks if any metadata needs to be flushed which involves a syscall making
850    /// it significantly slower when there are lots of open files without dirty pages.
851    #[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    /// Spawns a short term task for the volume that includes a guard that will prevent termination.
868    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    /// Tries to unwrap this volume.  If it fails, it will poison the volume so that when it is
875    /// dropped, you get a backtrace.
876    #[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                // Log details about all the places where there might be a reference cycle.
886                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                    // Try and get an active guard before upgrading.
921                    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                        // NB: For now, we only expect these calls in a regular (non-blob) volume.
929                        // Hard-code the type for simplicity; attempts to call on a blob volume will
930                        // get an error.
931                        .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            // Try and get an active guard before upgrading.
964            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    /// Clears the directory entry cache and internal object store caches for this volume.
1048    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            // Try and get an active guard before upgrading.
1060            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    // Maximum entries to fit based on 64KiB message size minus 16 bytes of header, 16 bytes
1094    // of vector header, 16 bytes for the optional token header, and 8 bytes of token value.
1095    // https://fuchsia.dev/fuchsia-src/development/languages/fidl/guides/max-out-pagination
1096    const MAX_PROJECT_ENTRIES: usize = 8184;
1097
1098    // Calls out to the inner volume to list available projects, removing and re-adding the fidl
1099    // wrapper types for the pagination token.
1100    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        // At least one of them needs to be around to return anything.
1121        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        // Don't try to return data if the volume is shutting down.
1160        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    // This is used for service connections and anything that isn't the actual volume.
1196    admin_scope: ExecutionScope,
1197
1198    // The outgoing directory that the volume might be served on.
1199    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    // The same as root but downcasted to FxDirectory.
1254    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
1263// The correct number here is arguably u64::MAX - 1 (because node 0 is reserved). There's a bug
1264// where inspect test cases fail if we try and use that, possibly because of a signed/unsigned bug.
1265// See https://fxbug.dev/42168242.  Until that's fixed, we'll have to use i64::MAX.
1266const TOTAL_NODES: u64 = i64::MAX as u64;
1267
1268// An array used to initialize the FilesystemInfo |name| field. This just spells "fxfs" 0-padded to
1269// 32 bytes.
1270const 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        // TODO(https://fxbug.dev/42175592): Support free_shared_pool_bytes.
1287        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        // The src file is non-empty.
1480        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        // The dst file is empty (so we can distinguish it).
1495        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        // The src dir is non-empty.
1565        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            // Write some data to the file, which will only go to the cache for now.
1638            file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1639
1640            // Initialized to the default size.
1641            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                // We have to reopen the object each time since this is a distinct handle from the
1646                // one managed by the FxFile.
1647                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            // Write some data to the file, which will only go to the cache for now.
1711            file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1712
1713            // Initialized to the default size.
1714            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                // We have to reopen the object each time since this is a distinct handle from the
1719                // one managed by the FxFile.
1720                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            // Configure the flush task to only flush quickly on warning.
1730            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            // Send the memory pressure update.
1753            fixture
1754                .memory_pressure_proxy()
1755                .on_level_changed(MemoryPressureLevel::Warning)
1756                .await
1757                .expect("Failed to send memory pressure level change");
1758
1759            // Wait a bit of time for the flush to occur (but less than the normal and critical
1760            // periods).
1761            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            // Write some data to the file, which will only go to the cache for now.
1805            file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1806
1807            // Initialized to the default size.
1808            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                // We have to reopen the object each time since this is a distinct handle from the
1813                // one managed by the FxFile.
1814                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            // Send the memory pressure update.
1843            fixture
1844                .memory_pressure_proxy()
1845                .on_level_changed(MemoryPressureLevel::Critical)
1846                .await
1847                .expect("Failed to send memory pressure level change");
1848
1849            // Critical memory should trigger a flush immediately so expect a flush very quickly.
1850            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    // This test verifies that it is safe to query a volume after it is unmounted in case of a race
1871    // during unmount/shutdown.
1872    #[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            // Unmount it but keep a reference.
1893            volumes_directory
1894                .lock()
1895                .await
1896                .unmount(volume.volume().store().store_object_id())
1897                .await
1898                .expect("unmount failed");
1899
1900            // This returns None, but doesn't crash.
1901            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            // Should be unable to clear the project limit, due to being in use.
2091            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            // Grow the file by a block.
2275            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                // Empty file should have less space than the non-empty file from above.
2396                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            // Trim to zero. Bytes should decrease.
2420            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            // Dropping node from project. Usage should go to zero.
2430            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            // An unnamed temporary file is created slightly differently to a regular file object.
2594            // Just in case, check that it inherits project ID as well.
2595            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            // This is just to ensure that the small numbers below can be used for this test.
2673            assert!(FxVolume::MAX_PROJECT_ENTRIES >= 4);
2674            // Create a bunch of proxies. 3 more than the limit to ensure pagination.
2675            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            // Add one usage entry to be interspersed with the limit entries. Verifies that the
2681            // iterator will progress passed it with no effect.
2682            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            // If this `unwrap()` fails, it is likely the MAX_PROJECT_ENTRIES is too large for fidl.
2721            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            // Page two should have a small set at the end.
2729            (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            // Delete a couple and list all again, but one has usage still.
2740            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            // Delete two more to hit the edge case.
2758            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            // Page in the zero offsets only to avoid readahead strangeness.
2803            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        // Do this multiple times to ensure that the re-recording doesn't drop anything.
2813        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                // Ensure that nothing is paged in right now.
2819                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                // Move the file in flight to ensure a new version lands to be used next time.
2832                {
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                // Await all data being played back by checking that things have paged in.
2858                async {
2859                    for hash in &hashes {
2860                        // Fetch vmo this way as well to ensure that the open is counting the file
2861                        // as used in the current recording.
2862                        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                // Complete the recording.
2875                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            // Include a crypt file to access during the recording. It should not be added.
2888            {
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            // Page in the zero offsets only to avoid readahead strangeness.
2987            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        // Do this multiple times to ensure that the re-recording doesn't drop anything.
3002        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                // Need to get the root vmo to check committed bytes.
3012                let volume = fixture.volume().volume().clone();
3013                // Ensure that nothing is paged in right now.
3014                {
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                // Move the file in flight to ensure a new version lands to be used next time.
3043                {
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                // Await all data being played back by checking that things have paged in.
3069                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                // The crypt file access should not have been recorded or replayed.
3088                {
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                // Open all the files to show that they have been used.
3100                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                // Complete the recording.
3111                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                // Page in the zero offsets only to avoid readahead strangeness.
3145                {
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                // The recording happens asynchronously, so we must wait.  This is crude, but it's
3153                // only for testing and it's simple.
3154                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            // Need to get the root vmo to check committed bytes.
3168            // Ensure that nothing is paged in right now.
3169            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            // Await all data being played back by checking that things have paged in.
3182            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            // Record the new profile that will overwrite it.
3197            {
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            // The recording happens asynchronously, so we must wait.  This is crude, but it's only
3205            // for testing and it's simple.
3206            while RECORDED.load(Ordering::Relaxed) == original_recorded {
3207                fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3208            }
3209
3210            // Complete the recording.
3211            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            // Need to get the root vmo to check committed bytes.
3220            // Ensure that nothing is paged in right now.
3221            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            // Await all data being played back by checking that things have paged in.
3234            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            // Complete the recording.
3247            fixture.volume().volume().stop_profile_tasks().await;
3248
3249            // Verify that first blob was not paged in as the it should be dropped from the profile.
3250            {
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        // Make a new Fxfs filesystem with an unencrypted volume named "vol".
3279        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            // Re-open the device as read-only and mount the filesystem as read-only.
3288            device.reopen(true);
3289            FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3290        };
3291        // Ensure we can access the volume and gracefully terminate any tasks.
3292        {
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        // Close the filesystem, and make sure we don't have any dangling references.
3310        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        // Make a new Fxfs filesystem with an encrypted volume named "vol".
3319        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            // Re-open the device as read-only and mount the filesystem as read-only.
3328            device.reopen(true);
3329            FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3330        };
3331        // Ensure we can access the volume and gracefully terminate any tasks.
3332        {
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        // Close the filesystem, and make sure we don't have any dangling references.
3353        fs.close().await.unwrap();
3354        let device = fs.take_device().await;
3355        device.ensure_unique();
3356    }
3357}