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};
11use crate::fuchsia::pager::Pager;
12use crate::fuchsia::profile::ProfileState;
13use crate::fuchsia::symlink::FxSymlink;
14use crate::fuchsia::volumes_directory::VolumesDirectory;
15use anyhow::{Error, bail, ensure};
16use async_trait::async_trait;
17use fidl::endpoints::ServerEnd;
18use fidl_fuchsia_fxfs::{
19    BytesAndNodes, FileBackedVolumeProviderRequest, FileBackedVolumeProviderRequestStream,
20    ProjectIdRequest, ProjectIdRequestStream, ProjectIterToken,
21};
22use fidl_fuchsia_io as fio;
23use fs_inspect::{FsInspectVolume, VolumeData};
24use fuchsia_async as fasync;
25use fuchsia_async::epoch::Epoch;
26use fuchsia_sync::Mutex;
27use futures::channel::oneshot;
28use futures::stream::{self, FusedStream, Stream};
29use futures::{FutureExt, StreamExt, TryStreamExt};
30use fxfs::errors::FxfsError;
31use fxfs::filesystem::{self, SyncOptions};
32use fxfs::future_with_guard::FutureWithGuard;
33use fxfs::log::*;
34use fxfs::object_store::directory::Directory;
35use fxfs::object_store::project_id::ProjectIdExt;
36use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
37use fxfs::object_store::{
38    DirType, HandleOptions, HandleOwner, ObjectDescriptor, ObjectStore, ProjectId,
39};
40use refaults_vmo::PageRefaultCounter;
41use std::future::Future;
42use std::pin::pin;
43#[cfg(any(test, feature = "testing"))]
44use std::sync::atomic::AtomicBool;
45use std::sync::atomic::{AtomicU64, Ordering};
46use std::sync::{Arc, OnceLock, Weak};
47use std::time::Duration;
48use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
49use vfs::directory::entry::DirectoryEntry;
50use vfs::directory::simple::Simple;
51use vfs::execution_scope::ExecutionScope;
52
53// LINT.IfChange
54// TODO:(b/299919008) Fix this number to something reasonable, or maybe just for fxblob.
55const DIRENT_CACHE_LIMIT: usize = 2000;
56// LINT.ThenChange(//src/storage/stressor/src/aggressive.rs)
57
58/// The read ahead/around size to target. Increase reads to be this size within the restrictions of
59/// the format for the target object.
60pub const READ_AHEAD_SIZE: u64 = 128 * 1024;
61
62const BLOB_TRANSFER_VMO_SIZE: usize = 32 * 1024 * 1024;
63
64const PROFILE_DIRECTORY: &str = "profiles";
65
66#[derive(Clone, Copy, Debug)]
67pub struct MemoryPressureLevelConfig {
68    /// The period to wait between flushes, as well as perform other background maintenance tasks
69    /// (e.g. purging caches).
70    pub background_task_period: Duration,
71
72    /// The limit of cached nodes.
73    pub cache_size_limit: usize,
74
75    /// The initial delay before the background task runs. The background task has a longer initial
76    /// delay to avoid running the task during boot.
77    pub background_task_initial_delay: Duration,
78}
79
80impl Default for MemoryPressureLevelConfig {
81    fn default() -> Self {
82        Self {
83            background_task_period: Duration::from_secs(20),
84            cache_size_limit: DIRENT_CACHE_LIMIT,
85            background_task_initial_delay: Duration::from_secs(70),
86        }
87    }
88}
89
90#[derive(Clone, Copy, Debug)]
91pub struct MemoryPressureConfig {
92    /// The configuration to use at [`MemoryPressureLevel::Normal`].
93    pub mem_normal: MemoryPressureLevelConfig,
94
95    /// The configuration to use at [`MemoryPressureLevel::Warning`].
96    pub mem_warning: MemoryPressureLevelConfig,
97
98    /// The configuration to use at [`MemoryPressureLevel::Critical`].
99    pub mem_critical: MemoryPressureLevelConfig,
100}
101
102impl MemoryPressureConfig {
103    pub fn for_level(&self, level: &MemoryPressureLevel) -> &MemoryPressureLevelConfig {
104        match level {
105            MemoryPressureLevel::Normal => &self.mem_normal,
106            MemoryPressureLevel::Warning => &self.mem_warning,
107            MemoryPressureLevel::Critical => &self.mem_critical,
108        }
109    }
110}
111
112impl Default for MemoryPressureConfig {
113    fn default() -> Self {
114        // TODO(https://fxbug.dev/42061389): investigate a smarter strategy for determining flush
115        // frequency.
116        Self {
117            mem_normal: MemoryPressureLevelConfig {
118                background_task_period: Duration::from_secs(20),
119                cache_size_limit: DIRENT_CACHE_LIMIT,
120                background_task_initial_delay: Duration::from_secs(70),
121            },
122            mem_warning: MemoryPressureLevelConfig {
123                background_task_period: Duration::from_secs(5),
124                cache_size_limit: 100,
125                background_task_initial_delay: Duration::from_secs(5),
126            },
127            mem_critical: MemoryPressureLevelConfig {
128                background_task_period: Duration::from_millis(1500),
129                cache_size_limit: 20,
130                background_task_initial_delay: Duration::from_millis(1500),
131            },
132        }
133    }
134}
135
136/// FxVolume represents an opened volume. It is also a (weak) cache for all opened Nodes within the
137/// volume.
138pub struct FxVolume {
139    parent: Weak<VolumesDirectory>,
140    cache: NodeCache,
141    store: Arc<ObjectStore>,
142    pager: Pager,
143    executor: fasync::EHandle,
144    name: String,
145
146    // A tuple of the actual task and a channel to signal to terminate the task.
147    background_task: Mutex<Option<(fasync::Task<()>, oneshot::Sender<()>)>>,
148
149    // Unique identifier of the filesystem that owns this volume.
150    fs_id: u64,
151
152    // The execution scope for this volume.
153    scope: ExecutionScope,
154
155    dirent_cache: DirentCache,
156
157    profile_state: Mutex<Option<Box<dyn ProfileState>>>,
158
159    #[cfg(any(test, feature = "testing"))]
160    poisoned: AtomicBool,
161
162    blob_resupplied_count: Arc<PageRefaultCounter>,
163
164    /// The number of dirty bytes in pager backed VMOs that belong to this volume. VolumesDirectory
165    /// holds a count for all volumes. This count is only used for tracing.
166    pager_dirty_byte_count: AtomicU64,
167
168    /// A trusted buffer allocator used for reading and decompressing blobs. Unlike the underlying
169    /// block device's buffer allocator (whose VMOs are shared with drivers and thus untrusted),
170    /// this allocator uses VMOs without the TRANSFER right. This prevents foreign processes or
171    /// drivers from mutating memory concurrently, allowing us to safely extract Rust slices
172    /// (`try_as_slice`/`try_as_mut_slice`) and perform zero-copy Merkle tree verification without
173    /// heap copies.
174    blob_allocator: OnceLock<Arc<BufferAllocator>>,
175}
176
177#[fxfs_trace::trace]
178impl FxVolume {
179    pub fn new(
180        parent: Weak<VolumesDirectory>,
181        store: Arc<ObjectStore>,
182        fs_id: u64,
183        name: String,
184        blob_resupplied_count: Arc<PageRefaultCounter>,
185        memory_pressure_config: MemoryPressureConfig,
186    ) -> Result<Self, Error> {
187        let scope = ExecutionScope::new();
188        Ok(Self {
189            parent,
190            cache: NodeCache::new(),
191            store,
192            name,
193            pager: Pager::new(scope.clone())?,
194            executor: fasync::EHandle::local(),
195            background_task: Mutex::new(None),
196            fs_id,
197            scope,
198            dirent_cache: DirentCache::new(memory_pressure_config.mem_normal.cache_size_limit),
199            profile_state: Mutex::new(None),
200            #[cfg(any(test, feature = "testing"))]
201            poisoned: AtomicBool::new(false),
202            blob_resupplied_count,
203            pager_dirty_byte_count: AtomicU64::new(0),
204            blob_allocator: OnceLock::new(),
205        })
206    }
207
208    pub fn store(&self) -> &Arc<ObjectStore> {
209        &self.store
210    }
211
212    /// Returns the trusted buffer allocator for this volume, initializing it on first access.
213    /// This is primarily used by `BlobDirectory` and `FxBlob` to perform zero-copy decompression
214    /// and Merkle verification safely.
215    pub fn blob_allocator(&self) -> &Arc<BufferAllocator> {
216        self.blob_allocator.get_or_init(|| {
217            Arc::new(BufferAllocator::new(
218                self.store().block_size() as usize,
219                BufferSource::new_trusted(BLOB_TRANSFER_VMO_SIZE),
220            ))
221        })
222    }
223
224    pub fn cache(&self) -> &NodeCache {
225        &self.cache
226    }
227
228    pub fn dirent_cache(&self) -> &DirentCache {
229        &self.dirent_cache
230    }
231
232    pub fn pager(&self) -> &Pager {
233        &self.pager
234    }
235
236    pub fn id(&self) -> u64 {
237        self.fs_id
238    }
239
240    pub fn scope(&self) -> &ExecutionScope {
241        &self.scope
242    }
243
244    pub fn blob_resupplied_count(&self) -> &PageRefaultCounter {
245        &self.blob_resupplied_count
246    }
247
248    pub fn name(&self) -> &str {
249        &self.name
250    }
251
252    /// Reports the filesystem info, but if the volume has a space limit applied then the space
253    /// available and space used are reported based on the volume instead.
254    pub fn filesystem_info_for_volume(&self) -> fio::FilesystemInfo {
255        let allocator = self.store.filesystem().allocator();
256        let info =
257            if let Some(limit) = allocator.get_owner_bytes_limit(self.store.store_object_id()) {
258                filesystem::Info {
259                    used_bytes: allocator.get_owner_bytes_used(self.store.store_object_id()),
260                    total_bytes: limit,
261                }
262            } else {
263                self.store.filesystem().get_info()
264            };
265
266        info_to_filesystem_info(info, self.store.block_size(), self.store.object_count(), self.id())
267    }
268
269    /// Stop profiling, recover resources from it and finalize recordings.
270    pub async fn stop_profile_tasks(self: &Arc<Self>) {
271        let Some(mut state) = self.profile_state.lock().take() else { return };
272        state.wait_for_replay_to_finish().await;
273        self.pager.set_recorder(None);
274        state.wait_for_recording_to_finish().await;
275    }
276
277    /// Opens or creates the profile directory in the volume's internal directory.
278    pub async fn get_profile_directory(self: &Arc<Self>) -> Result<Directory<FxVolume>, Error> {
279        let internal_dir = self
280            .get_or_create_internal_dir()
281            .await
282            .map_err(|e| e.context("Opening internal directory"))?;
283        // Have to do separate calls to create the profile dir if necessary.
284        let mut transaction = self
285            .store()
286            .new_transaction(
287                lock_keys![LockKey::object(
288                    self.store().store_object_id(),
289                    internal_dir.object_id(),
290                )],
291                Options::default(),
292            )
293            .await?;
294        Ok(match internal_dir.directory().lookup(PROFILE_DIRECTORY).await? {
295            Some((object_id, _, _)) => {
296                Directory::open_unchecked(self.clone(), object_id, DirType::Normal)
297            }
298            None => {
299                let new_dir = internal_dir
300                    .directory()
301                    .create_child_dir(&mut transaction, PROFILE_DIRECTORY)
302                    .await?;
303                transaction.commit().await?;
304                new_dir
305            }
306        })
307    }
308
309    /// Starts recording a profile for the volume under the name given, and if a profile exists
310    /// under that same name it is replayed and will be replaced after by the new recording if it
311    /// is cleanly shutdown and finalized.
312    pub async fn record_and_replay_profile(
313        self: &Arc<Self>,
314        mut state: Box<dyn ProfileState>,
315        name: &str,
316    ) -> Result<(), Error> {
317        // We don't meddle in FxDirectory or FxFile here because we don't want a paged object.
318        // Normally we ensure that there's only one copy by using the Node cache on the volume, but
319        // that would create FxFile, so in this case we just assume that only one profile operation
320        // should be ongoing at a time, as that is ensured in `VolumesDirectory`.
321
322        // If there is a recording already, prepare to replay it.
323        let profile_dir = self.get_profile_directory().await?;
324        let replay_handle = if let Some((id, descriptor, _)) = profile_dir.lookup(name).await? {
325            ensure!(matches!(descriptor, ObjectDescriptor::File), FxfsError::Inconsistent);
326            Some(Box::new(
327                ObjectStore::open_object(self, id, HandleOptions::default(), None).await?,
328            ))
329        } else {
330            None
331        };
332
333        info!("Recording new profile '{name}' for volume object {}", self.store.store_object_id());
334        // Begin recording first to ensure that we capture any activity from the replay.
335        let recording_handle =
336            crate::fuchsia::profile::FileRecordingHandle::new(name, self.clone()).await?;
337
338        let mut profile_state = self.profile_state.lock();
339        self.pager.set_recorder(Some(state.record_new(self, Box::new(recording_handle))));
340        if let Some(handle) = replay_handle {
341            if let Some(guard) = self.scope().try_active_guard() {
342                state.replay_profile(handle, self.clone(), guard);
343                info!(
344                    "Replaying existing profile '{name}' for volume object {}",
345                    self.store.store_object_id()
346                );
347            }
348        }
349        *profile_state = Some(state);
350        Ok(())
351    }
352
353    /// Replays a profile if one exists, and only records if one does not exist.
354    pub async fn replay_xor_record_profile(
355        self: &Arc<Self>,
356        mut state: Box<dyn ProfileState>,
357        name: &str,
358    ) -> Result<(), Error> {
359        let profile_dir = self.get_profile_directory().await?;
360        let replay_handle = if let Some((id, descriptor, _)) = profile_dir.lookup(name).await? {
361            ensure!(matches!(descriptor, ObjectDescriptor::File), FxfsError::Inconsistent);
362            Some(Box::new(
363                ObjectStore::open_object(self, id, HandleOptions::default(), None).await?,
364            ))
365        } else {
366            None
367        };
368
369        if let Some(handle) = replay_handle {
370            let mut profile_state = self.profile_state.lock();
371            if let Some(guard) = self.scope().try_active_guard() {
372                state.replay_profile(handle, self.clone(), guard);
373                info!(
374                    "Replaying existing profile '{name}' for volume object {}",
375                    self.store.store_object_id()
376                );
377            }
378            *profile_state = Some(state);
379        } else {
380            info!(
381                "Recording new profile '{name}' for volume object {}",
382                self.store.store_object_id()
383            );
384            let recording_handle =
385                crate::fuchsia::profile::FileRecordingHandle::new(name, self.clone()).await?;
386            let mut profile_state = self.profile_state.lock();
387            self.pager.set_recorder(Some(state.record_new(self, Box::new(recording_handle))));
388            *profile_state = Some(state);
389        }
390        Ok(())
391    }
392
393    async fn get_or_create_internal_dir(self: &Arc<Self>) -> Result<Arc<FxDirectory>, Error> {
394        let internal_data_id = self.store().get_or_create_internal_directory_id().await?;
395        let internal_dir = self
396            .get_or_load_node(internal_data_id, ObjectDescriptor::Directory, None)
397            .await?
398            .into_any()
399            .downcast::<FxDirectory>()
400            .unwrap();
401        Ok(internal_dir)
402    }
403
404    pub async fn terminate(&self) {
405        let task = std::mem::replace(&mut *self.background_task.lock(), None);
406        if let Some((task, terminate)) = task {
407            let _ = terminate.send(());
408            task.await;
409        }
410
411        // `NodeCache::terminate` will break any strong reference cycles contained within nodes
412        // (pager registration). The only remaining nodes should be those with open FIDL
413        // connections or vmo references in the process of handling the VMO_ZERO_CHILDREN signal.
414        // `ExecutionScope::shutdown` + `ExecutionScope::wait` will close the open FIDL connections
415        // and synchonrize the signal handling which should result in all nodes flushing and then
416        // dropping. Any async tasks required to flush a node should take an active guard on the
417        // `ExecutionScope` which will prevent `ExecutionScope::wait` from completing until all
418        // nodes are flushed.
419        self.scope.shutdown();
420        self.cache.terminate();
421        self.scope.wait().await;
422
423        // Make sure there are no deferred operations still pending for this volume.
424        Epoch::global().barrier().await;
425
426        // The dirent_cache must be cleared *after* shutting down the scope because there can be
427        // tasks that insert entries into the cache.
428        self.dirent_cache.clear();
429
430        if self.store.filesystem().options().read_only {
431            // If the filesystem is read only, we don't need to flush/sync anything.
432            if self.store.is_unlocked() {
433                self.store.lock_read_only();
434            }
435            return;
436        }
437
438        self.flush_all_files(FlushType::LastChance).await;
439        self.store.filesystem().graveyard().flush().await;
440        if self.store.crypt().is_some() {
441            if let Err(e) = self.store.lock().await {
442                // The store will be left in a safe state and there won't be data-loss unless
443                // there's an issue flushing the journal later.
444                warn!(error:? = e; "Locking store error");
445            }
446        }
447        let sync_status = self
448            .store
449            .filesystem()
450            .sync(SyncOptions { flush_device: true, ..Default::default() })
451            .await;
452        if let Err(e) = sync_status {
453            error!(error:? = e; "Failed to sync filesystem; data may be lost");
454        }
455    }
456
457    /// Attempts to get a node from the node cache. If the node wasn't present in the cache, loads
458    /// the object from the object store, installing the returned node into the cache and returns
459    /// the newly created FxNode backed by the loaded object.  |parent| is only set on the node if
460    /// the node was not present in the cache.  Otherwise, it is ignored.
461    pub async fn get_or_load_node(
462        self: &Arc<Self>,
463        object_id: u64,
464        object_descriptor: ObjectDescriptor,
465        parent: Option<Arc<FxDirectory>>,
466    ) -> Result<Arc<dyn FxNode>, Error> {
467        match self.cache.get_or_reserve(object_id).await {
468            GetResult::Node(node) => Ok(node),
469            GetResult::Placeholder(placeholder) => {
470                let node = match object_descriptor {
471                    ObjectDescriptor::File => FxFile::new(
472                        ObjectStore::open_object(self, object_id, HandleOptions::default(), None)
473                            .await?,
474                    ) as Arc<dyn FxNode>,
475                    ObjectDescriptor::Directory => {
476                        // Can't use open_unchecked because we don't know if the dir is casefolded
477                        // or encrypted.
478                        Arc::new(FxDirectory::new(parent, Directory::open(self, object_id).await?))
479                            as Arc<dyn FxNode>
480                    }
481                    ObjectDescriptor::Symlink => Arc::new(FxSymlink::new(self.clone(), object_id)),
482                    _ => bail!(FxfsError::Inconsistent),
483                };
484                placeholder.commit(&node);
485                Ok(node)
486            }
487        }
488    }
489
490    /// Marks the given directory deleted.
491    pub fn mark_directory_deleted(&self, object_id: u64) {
492        if let Some(node) = self.cache.get(object_id) {
493            // It's possible that node is a placeholder, in which case we don't need to wait for it
494            // to be resolved because it should be blocked behind the locks that are held by the
495            // caller, and once they're dropped, it'll be found to be deleted via the tree.
496            if let Ok(dir) = node.into_any().downcast::<FxDirectory>() {
497                dir.set_deleted();
498            }
499        }
500    }
501
502    /// Removes resources associated with |object_id| (which ought to be a file), if there are no
503    /// open connections to that file.
504    ///
505    /// This must be called *after committing* a transaction which deletes the last reference to
506    /// |object_id|, since before that point, new connections could be established.
507    pub(super) async fn maybe_purge_file(&self, object_id: u64) -> Result<(), Error> {
508        if let Some(node) = self.cache.get(object_id) {
509            node.clone().mark_to_be_purged();
510            return Ok(());
511        }
512        // If this fails, the graveyard should clean it up on next mount.
513        self.store
514            .tombstone_object(
515                object_id,
516                Options { borrow_metadata_space: true, ..Default::default() },
517            )
518            .await?;
519        Ok(())
520    }
521
522    /// Starts the background work task.  This task will periodically:
523    ///   - scan all files and flush them to disk, and
524    ///   - purge unused cached data.
525    /// The task will hold a strong reference to the FxVolume while it is running, so the task must
526    /// be closed later with Self::terminate, or the FxVolume will never be dropped.
527    pub fn start_background_task(
528        self: &Arc<Self>,
529        config: MemoryPressureConfig,
530        mem_monitor: Option<&MemoryPressureMonitor>,
531    ) {
532        let mut background_task = self.background_task.lock();
533        if background_task.is_none() {
534            let (tx, rx) = oneshot::channel();
535
536            let task = if let Some(mem_monitor) = mem_monitor {
537                fasync::Task::spawn(self.clone().background_task(
538                    config,
539                    mem_monitor.get_level_stream(),
540                    rx,
541                ))
542            } else {
543                // With no memory pressure monitoring, just stub the stream out as always pending.
544                fasync::Task::spawn(self.clone().background_task(config, stream::pending(), rx))
545            };
546
547            *background_task = Some((task, tx));
548        }
549    }
550
551    #[trace]
552    async fn background_task(
553        self: Arc<Self>,
554        config: MemoryPressureConfig,
555        mut level_stream: impl Stream<Item = MemoryPressureLevel> + FusedStream + Unpin,
556        terminate: oneshot::Receiver<()>,
557    ) {
558        debug!(store_id = self.store.store_object_id(); "FxVolume::background_task start");
559        let mut terminate = terminate.fuse();
560        // Default to the normal period until updates come from the `level_stream`.
561        let mut level = MemoryPressureLevel::Normal;
562        let mut timer =
563            pin!(fasync::Timer::new(config.for_level(&level).background_task_initial_delay));
564
565        loop {
566            let mut should_terminate = false;
567            let mut should_flush = false;
568            let mut low_mem = false;
569            let mut should_purge_layer_files = false;
570            let mut should_update_cache_limit = false;
571
572            futures::select_biased! {
573                _ = terminate => should_terminate = true,
574                new_level = level_stream.next() => {
575                    // Because `level_stream` will never terminate, this is safe to unwrap.
576                    let new_level = new_level.unwrap();
577                    // At critical levels, it's okay to undertake expensive work immediately
578                    // to reclaim memory.
579                    low_mem = matches!(new_level, MemoryPressureLevel::Critical);
580                    should_purge_layer_files = true;
581                    if new_level != level {
582                        level = new_level;
583                        should_update_cache_limit = true;
584                        let level_config = config.for_level(&level);
585                        timer.as_mut().reset(fasync::MonotonicInstant::after(
586                            level_config.background_task_period.into())
587                        );
588                        debug!(
589                            "Background task period changed to {:?} due to new memory pressure \
590                            level ({:?}).",
591                            config.for_level(&level).background_task_period, level
592                        );
593                    }
594                }
595                _ = timer => {
596                    timer.as_mut().reset(fasync::MonotonicInstant::after(
597                        config.for_level(&level).background_task_period.into())
598                    );
599                    should_flush = true;
600                    // Only purge layer file caches once we have elevated memory pressure.
601                    should_purge_layer_files = !matches!(level, MemoryPressureLevel::Normal);
602                }
603            };
604            if should_terminate {
605                break;
606            }
607            // Maybe close extra files *before* iterating them for flush/low mem.
608            if should_update_cache_limit {
609                self.dirent_cache.set_limit(config.for_level(&level).cache_size_limit);
610            }
611            if should_flush {
612                self.flush_all_files(FlushType::Sync).await;
613                self.dirent_cache.recycle_stale_files();
614            } else if low_mem {
615                // This is a softer version of flushing files, so don't bother if we're flushing.
616                self.minimize_memory().await;
617            }
618            if should_purge_layer_files {
619                for layer in self.store.tree().immutable_layer_set().layers {
620                    layer.purge_cached_data();
621                }
622            }
623        }
624        debug!(store_id = self.store.store_object_id(); "FxVolume::background_task end");
625    }
626
627    /// Reports that a certain number of bytes will be dirtied in a pager-backed VMO.
628    ///
629    /// Note that this function may await flush tasks.
630    pub fn report_pager_dirty(
631        self: Arc<Self>,
632        byte_count: u64,
633        mark_dirty: impl FnOnce() + Send + 'static,
634    ) {
635        let this = self.clone();
636        let callback = move || {
637            let prev = this.pager_dirty_byte_count.fetch_add(byte_count, Ordering::Relaxed);
638            mark_dirty();
639            fxfs_trace::counter!("dirty-bytes", 0, this.name => prev.saturating_add(byte_count));
640        };
641        if let Some(parent) = self.parent.upgrade() {
642            parent.report_pager_dirty(byte_count, self, callback);
643        } else {
644            callback();
645        }
646    }
647
648    /// Reports that a certain number of bytes were cleaned in a pager-backed VMO.
649    pub fn report_pager_clean(&self, byte_count: u64) {
650        if let Some(parent) = self.parent.upgrade() {
651            parent.report_pager_clean(byte_count);
652        }
653        let prev = self.pager_dirty_byte_count.fetch_sub(byte_count, Ordering::Relaxed);
654        fxfs_trace::counter!("dirty-bytes", 0, self.name => prev.saturating_sub(byte_count));
655    }
656
657    #[trace]
658    pub async fn flush_all_files(&self, flush_type: FlushType) {
659        let mut flushed = 0;
660        for file in self.cache.files() {
661            if let Some(node) = file.into_opened_node() {
662                if let Err(e) = FxFile::flush(&node, flush_type).await {
663                    warn!(
664                        store_id = self.store.store_object_id(),
665                        oid = node.object_id(),
666                        error:? = e;
667                        "Failed to flush",
668                    )
669                }
670                if flush_type == FlushType::LastChance {
671                    let file = node.clone();
672                    std::mem::drop(node);
673                    file.force_clean();
674                }
675            }
676            flushed += 1;
677        }
678        debug!(store_id = self.store.store_object_id(), file_count = flushed; "FxVolume flushed");
679    }
680
681    /// Flushes only files with dirty pages.
682    ///
683    /// `PagedObjectHandle` tracks the number dirty pages locally (except for overwrite files) which
684    /// makes determining whether flushing a file will reduce the number of dirty pages efficient.
685    /// `flush_all_files` checks if any metadata needs to be flushed which involves a syscall making
686    /// it significantly slower when there are lots of open files without dirty pages.
687    #[trace]
688    pub async fn minimize_memory(&self) {
689        for file in self.cache.files() {
690            if let Some(node) = file.into_opened_node() {
691                if let Err(e) = node.handle().minimize_memory().await {
692                    warn!(
693                        store_id = self.store.store_object_id(),
694                        oid = node.object_id(),
695                        error:? = e;
696                        "Failed to flush",
697                    )
698                }
699            }
700        }
701    }
702
703    /// Spawns a short term task for the volume that includes a guard that will prevent termination.
704    pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
705        if let Some(guard) = self.scope.try_active_guard() {
706            self.executor.spawn_detached(FutureWithGuard::new(guard, task));
707        }
708    }
709
710    /// Tries to unwrap this volume.  If it fails, it will poison the volume so that when it is
711    /// dropped, you get a backtrace.
712    #[cfg(any(test, feature = "testing"))]
713    pub fn try_unwrap(self: Arc<Self>) -> Option<FxVolume> {
714        self.poisoned.store(true, Ordering::Relaxed);
715        match Arc::try_unwrap(self) {
716            Ok(volume) => {
717                volume.poisoned.store(false, Ordering::Relaxed);
718                Some(volume)
719            }
720            Err(this) => {
721                // Log details about all the places where there might be a reference cycle.
722                info!(
723                    "background_task: {}, profile_state: {}, dirent_cache count: {}, \
724                     pager strong file refs={}, no tasks={}",
725                    this.background_task.lock().is_some(),
726                    this.profile_state.lock().is_some(),
727                    this.dirent_cache.len(),
728                    crate::pager::STRONG_FILE_REFS.load(Ordering::Relaxed),
729                    {
730                        let mut no_tasks = pin!(this.scope.wait());
731                        no_tasks
732                            .poll_unpin(&mut std::task::Context::from_waker(
733                                std::task::Waker::noop(),
734                            ))
735                            .is_ready()
736                    },
737                );
738                None
739            }
740        }
741    }
742
743    pub async fn handle_file_backed_volume_provider_requests(
744        this: Weak<Self>,
745        scope: ExecutionScope,
746        mut requests: FileBackedVolumeProviderRequestStream,
747    ) -> Result<(), Error> {
748        while let Some(request) = requests.try_next().await? {
749            match request {
750                FileBackedVolumeProviderRequest::Open {
751                    parent_directory_token,
752                    name,
753                    server_end,
754                    control_handle: _,
755                } => {
756                    // Try and get an active guard before upgrading.
757                    let Some(_guard) = scope.try_active_guard() else {
758                        bail!("Volume shutting down")
759                    };
760                    let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
761                    match this
762                        .scope
763                        .token_registry()
764                        // NB: For now, we only expect these calls in a regular (non-blob) volume.
765                        // Hard-code the type for simplicity; attempts to call on a blob volume will
766                        // get an error.
767                        .get_owner_and_rights(parent_directory_token)
768                        .and_then(|dir| {
769                            dir.ok_or(zx::Status::BAD_HANDLE).and_then(|(dir, rights)| {
770                                if !rights.contains(fio::Rights::MODIFY_DIRECTORY) {
771                                    return Err(zx::Status::BAD_HANDLE);
772                                }
773                                dir.into_any()
774                                    .downcast::<FxDirectory>()
775                                    .map_err(|_| zx::Status::BAD_HANDLE)
776                            })
777                        }) {
778                        Ok(dir) => {
779                            dir.open_block_file(&name, server_end).await;
780                        }
781                        Err(status) => {
782                            let _ = server_end.close_with_epitaph(status).unwrap_or_else(|e| {
783                                error!(error:? = e; "open failed to send epitaph");
784                            });
785                        }
786                    }
787                }
788            }
789        }
790        Ok(())
791    }
792
793    pub async fn handle_project_id_requests(
794        this: Weak<Self>,
795        scope: ExecutionScope,
796        mut requests: ProjectIdRequestStream,
797    ) -> Result<(), Error> {
798        while let Some(request) = requests.try_next().await? {
799            // Try and get an active guard before upgrading.
800            let Some(_guard) = scope.try_active_guard() else { bail!("Volume shutting down") };
801            let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
802            let store_id = this.store.store_object_id();
803
804            match request {
805                ProjectIdRequest::SetLimit { responder, project_id, bytes, nodes } => {
806                    let result = if let Some(project_id) = ProjectId::new(project_id) {
807                        this.store()
808                        .set_project_limit(project_id, bytes, nodes)
809                        .await
810                        .map_err(|error| {
811                            error!(error:?, store_id, project_id; "Failed to set project limit");
812                            map_to_raw_status(error)
813                        })
814                    } else {
815                        Err(zx::Status::OUT_OF_RANGE.into_raw())
816                    };
817                    responder.send(result)?
818                }
819                ProjectIdRequest::Clear { responder, project_id } => {
820                    let result = if let Some(project_id) = ProjectId::new(project_id) {
821                        this.store().clear_project_limit(project_id).await.map_err(|error| {
822                            error!(error:?, store_id, project_id; "Failed to clear project limit");
823                            map_to_raw_status(error)
824                        })
825                    } else {
826                        Err(zx::Status::OUT_OF_RANGE.into_raw())
827                    };
828                    responder.send(result)?
829                }
830                ProjectIdRequest::SetForNode { responder, node_id, project_id } => {
831                    let result = if let Some(project_id) = ProjectId::new(project_id) {
832                        this.store()
833                        .set_project_for_node(node_id, project_id)
834                        .await
835                        .map_err(|error| {
836                            error!(error:?, store_id, node_id, project_id; "Failed to apply node.");
837                            map_to_raw_status(error)
838                        })
839                    } else {
840                        Err(zx::Status::OUT_OF_RANGE.into_raw())
841                    };
842                    responder.send(result)?
843                }
844                ProjectIdRequest::GetForNode { responder, node_id } => responder.send(
845                    this.store()
846                        .get_project_for_node(node_id)
847                        .await
848                        .map(ProjectIdExt::raw)
849                        .map_err(|error| {
850                            error!(error:?, store_id, node_id; "Failed to get node.");
851                            map_to_raw_status(error)
852                        }),
853                )?,
854                ProjectIdRequest::ClearForNode { responder, node_id } => responder.send(
855                    this.store().clear_project_for_node(node_id).await.map_err(|error| {
856                        error!(error:?, store_id, node_id; "Failed to clear for node.");
857                        map_to_raw_status(error)
858                    }),
859                )?,
860                ProjectIdRequest::List { responder, token } => {
861                    responder.send(match this.list_projects(&token).await {
862                        Ok((ref entries, ref next_token)) => Ok((entries, next_token.as_ref())),
863                        Err(error) => {
864                            error!(error:?, store_id, token:?; "Failed to list projects.");
865                            Err(map_to_raw_status(error))
866                        }
867                    })?
868                }
869                ProjectIdRequest::Info { responder, project_id } => {
870                    responder.send(match this.project_info(project_id).await {
871                        Ok((ref limit, ref usage)) => Ok((limit, usage)),
872                        Err(error) => {
873                            error!(error:?, store_id, project_id; "Failed to get project info.");
874                            Err(map_to_raw_status(error))
875                        }
876                    })?
877                }
878            }
879        }
880        Ok(())
881    }
882
883    // Maximum entries to fit based on 64KiB message size minus 16 bytes of header, 16 bytes
884    // of vector header, 16 bytes for the optional token header, and 8 bytes of token value.
885    // https://fuchsia.dev/fuchsia-src/development/languages/fidl/guides/max-out-pagination
886    const MAX_PROJECT_ENTRIES: usize = 8184;
887
888    // Calls out to the inner volume to list available projects, removing and re-adding the fidl
889    // wrapper types for the pagination token.
890    async fn list_projects(
891        &self,
892        last_token: &Option<Box<ProjectIterToken>>,
893    ) -> Result<(Vec<u64>, Option<ProjectIterToken>), Error> {
894        let (entries, token) = self
895            .store()
896            .list_projects(
897                last_token.as_ref().and_then(|v| ProjectId::new(v.value)),
898                Self::MAX_PROJECT_ENTRIES,
899            )
900            .await?;
901        Ok((
902            entries.into_iter().map(ProjectId::raw).collect(),
903            token.map(|value| ProjectIterToken { value: value.raw() }),
904        ))
905    }
906
907    async fn project_info(&self, project_id: u64) -> Result<(BytesAndNodes, BytesAndNodes), Error> {
908        let project_id = ProjectId::new(project_id).ok_or(FxfsError::OutOfRange)?;
909        let (limit, usage) = self.store().project_info(project_id).await?;
910        // At least one of them needs to be around to return anything.
911        ensure!(limit.is_some() || usage.is_some(), FxfsError::NotFound);
912        Ok((
913            limit.map_or_else(
914                || BytesAndNodes { bytes: u64::MAX, nodes: u64::MAX },
915                |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
916            ),
917            usage.map_or_else(
918                || BytesAndNodes { bytes: 0, nodes: 0 },
919                |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
920            ),
921        ))
922    }
923}
924
925#[cfg(any(test, feature = "testing"))]
926impl Drop for FxVolume {
927    fn drop(&mut self) {
928        assert!(!*self.poisoned.get_mut());
929    }
930}
931
932impl HandleOwner for FxVolume {}
933
934impl AsRef<ObjectStore> for FxVolume {
935    fn as_ref(&self) -> &ObjectStore {
936        &self.store
937    }
938}
939
940#[async_trait]
941impl FsInspectVolume for FxVolume {
942    async fn get_volume_data(&self) -> Option<VolumeData> {
943        // Don't try to return data if the volume is shutting down.
944        let _guard = self.scope.try_active_guard()?;
945
946        let object_count = self.store().object_count();
947        let (used_bytes, bytes_limit) =
948            self.store.filesystem().allocator().owner_allocation_info(self.store.store_object_id());
949        let encrypted = self.store().crypt().is_some();
950        let port_koid = fasync::EHandle::local().port().as_handle_ref().koid().unwrap().raw_koid();
951        Some(VolumeData { bytes_limit, used_bytes, used_nodes: object_count, encrypted, port_koid })
952    }
953}
954
955pub trait RootDir: FxNode + DirectoryEntry {
956    fn as_directory_entry(self: Arc<Self>) -> Arc<dyn DirectoryEntry>;
957
958    fn serve(self: Arc<Self>, flags: fio::Flags, server_end: ServerEnd<fio::DirectoryMarker>);
959
960    fn as_node(self: Arc<Self>) -> Arc<dyn FxNode>;
961
962    fn register_additional_volume_services(
963        self: Arc<Self>,
964        _svc_dir: &Simple,
965    ) -> Result<(), Error> {
966        Ok(())
967    }
968}
969
970#[derive(Clone)]
971pub struct FxVolumeAndRoot {
972    volume: Arc<FxVolume>,
973    root: Arc<dyn RootDir>,
974
975    // This is used for service connections and anything that isn't the actual volume.
976    admin_scope: ExecutionScope,
977
978    // The outgoing directory that the volume might be served on.
979    outgoing_dir: Arc<Simple>,
980}
981
982impl FxVolumeAndRoot {
983    pub async fn new<T: From<Directory<FxVolume>> + RootDir>(
984        parent: Weak<VolumesDirectory>,
985        store: Arc<ObjectStore>,
986        unique_id: u64,
987        volume_name: String,
988        blob_resupplied_count: Arc<PageRefaultCounter>,
989        memory_pressure_config: MemoryPressureConfig,
990    ) -> Result<Self, Error> {
991        let volume = Arc::new(FxVolume::new(
992            parent,
993            store,
994            unique_id,
995            volume_name,
996            blob_resupplied_count.clone(),
997            memory_pressure_config,
998        )?);
999        let root_object_id = volume.store().root_directory_object_id();
1000        let root_dir = Directory::open(&volume, root_object_id).await?;
1001        let root = Arc::<T>::new(root_dir.into()) as Arc<dyn RootDir>;
1002        volume
1003            .cache
1004            .get_or_reserve(root_object_id)
1005            .await
1006            .placeholder()
1007            .unwrap()
1008            .commit(&root.clone().as_node());
1009        Ok(Self {
1010            volume,
1011            root,
1012            admin_scope: ExecutionScope::new(),
1013            outgoing_dir: vfs::directory::immutable::simple(),
1014        })
1015    }
1016
1017    pub fn volume(&self) -> &Arc<FxVolume> {
1018        &self.volume
1019    }
1020
1021    pub fn root(&self) -> &Arc<dyn RootDir> {
1022        &self.root
1023    }
1024
1025    pub fn admin_scope(&self) -> &ExecutionScope {
1026        &self.admin_scope
1027    }
1028
1029    pub fn outgoing_dir(&self) -> &Arc<Simple> {
1030        &self.outgoing_dir
1031    }
1032
1033    // The same as root but downcasted to FxDirectory.
1034    pub fn root_dir(&self) -> Arc<FxDirectory> {
1035        self.root().clone().into_any().downcast::<FxDirectory>().expect("Invalid type for root")
1036    }
1037
1038    pub fn into_volume(self) -> Arc<FxVolume> {
1039        self.volume
1040    }
1041}
1042
1043// The correct number here is arguably u64::MAX - 1 (because node 0 is reserved). There's a bug
1044// where inspect test cases fail if we try and use that, possibly because of a signed/unsigned bug.
1045// See https://fxbug.dev/42168242.  Until that's fixed, we'll have to use i64::MAX.
1046const TOTAL_NODES: u64 = i64::MAX as u64;
1047
1048// An array used to initialize the FilesystemInfo |name| field. This just spells "fxfs" 0-padded to
1049// 32 bytes.
1050const FXFS_INFO_NAME_FIDL: [i8; 32] = [
1051    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,
1052    0, 0, 0, 0,
1053];
1054
1055fn info_to_filesystem_info(
1056    info: filesystem::Info,
1057    block_size: u64,
1058    object_count: u64,
1059    fs_id: u64,
1060) -> fio::FilesystemInfo {
1061    fio::FilesystemInfo {
1062        total_bytes: info.total_bytes,
1063        used_bytes: info.used_bytes,
1064        total_nodes: TOTAL_NODES,
1065        used_nodes: object_count,
1066        // TODO(https://fxbug.dev/42175592): Support free_shared_pool_bytes.
1067        free_shared_pool_bytes: 0,
1068        fs_id,
1069        block_size: block_size as u32,
1070        max_filename_size: fio::MAX_NAME_LENGTH as u32,
1071        fs_type: fidl_fuchsia_fs::VfsType::Fxfs.into_primitive(),
1072        padding: 0,
1073        name: FXFS_INFO_NAME_FIDL,
1074    }
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079    use super::DIRENT_CACHE_LIMIT;
1080    use crate::fuchsia::file::FxFile;
1081    use crate::fuchsia::fxblob::testing::{self as blob_testing, BlobFixture};
1082    use crate::fuchsia::memory_pressure::MemoryPressureLevel;
1083    use crate::fuchsia::pager::PagerBacked;
1084    use crate::fuchsia::profile::{RECORDED, new_profile_state};
1085    use crate::fuchsia::testing::{
1086        TestFixture, TestFixtureOptions, close_dir_checked, close_file_checked, open_dir,
1087        open_dir_checked, open_file, open_file_checked,
1088    };
1089    use crate::fuchsia::volume::{FxVolume, MemoryPressureConfig, MemoryPressureLevelConfig};
1090    use crate::fuchsia::volumes_directory::VolumesDirectory;
1091    use delivery_blob::CompressionMode;
1092    use fidl_fuchsia_fxfs::{BytesAndNodes, ProjectIdMarker};
1093    use fidl_fuchsia_io as fio;
1094    use fs_inspect::FsInspectVolume;
1095    use fuchsia_async::{self as fasync, TimeoutExt as _};
1096    use fuchsia_component_client::connect_to_protocol_at_dir_svc;
1097    use fuchsia_fs::file;
1098    use fxfs::filesystem::{FxFilesystem, FxFilesystemBuilder};
1099    use fxfs::fsck::{fsck, fsck_volume};
1100    use fxfs::object_store::directory::replace_child;
1101    use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
1102    use fxfs::object_store::volume::root_volume;
1103    use fxfs::object_store::{HandleOptions, ObjectDescriptor, ObjectStore, StoreOptions};
1104    use fxfs_crypt_common::CryptBase;
1105    use fxfs_crypto::{Crypt, WrappingKeyId};
1106    use fxfs_insecure_crypto::new_insecure_crypt;
1107    use refaults_vmo::PageRefaultCounter;
1108    use std::sync::atomic::Ordering;
1109    use std::sync::{Arc, Weak};
1110    use std::time::Duration;
1111    use storage_device::DeviceHolder;
1112    use storage_device::fake_device::FakeDevice;
1113    use zx::Status;
1114
1115    const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
1116
1117    #[fuchsia::test(threads = 10)]
1118    async fn test_rename_different_dirs() {
1119        use zx::Event;
1120
1121        let fixture = TestFixture::new().await;
1122        let root = fixture.root();
1123
1124        let src = open_dir_checked(
1125            &root,
1126            "foo",
1127            fio::Flags::FLAG_MAYBE_CREATE
1128                | fio::PERM_READABLE
1129                | fio::PERM_WRITABLE
1130                | fio::Flags::PROTOCOL_DIRECTORY,
1131            Default::default(),
1132        )
1133        .await;
1134
1135        let dst = open_dir_checked(
1136            &root,
1137            "bar",
1138            fio::Flags::FLAG_MAYBE_CREATE
1139                | fio::PERM_READABLE
1140                | fio::PERM_WRITABLE
1141                | fio::Flags::PROTOCOL_DIRECTORY,
1142            Default::default(),
1143        )
1144        .await;
1145
1146        let f = open_file_checked(
1147            &root,
1148            "foo/a",
1149            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1150            &Default::default(),
1151        )
1152        .await;
1153        close_file_checked(f).await;
1154
1155        let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1156        Status::ok(status).expect("get_token failed");
1157        src.rename("a", Event::from(dst_token.unwrap()), "b")
1158            .await
1159            .expect("FIDL call failed")
1160            .expect("rename failed");
1161
1162        assert_eq!(
1163            open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1164                .await
1165                .expect_err("Open succeeded")
1166                .root_cause()
1167                .downcast_ref::<Status>()
1168                .expect("No status"),
1169            &Status::NOT_FOUND,
1170        );
1171        let f =
1172            open_file_checked(&root, "bar/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1173        close_file_checked(f).await;
1174
1175        close_dir_checked(dst).await;
1176        close_dir_checked(src).await;
1177        fixture.close().await;
1178    }
1179
1180    #[fuchsia::test(threads = 10)]
1181    async fn test_rename_same_dir() {
1182        use zx::Event;
1183        let fixture = TestFixture::new().await;
1184        let root = fixture.root();
1185
1186        let src = open_dir_checked(
1187            &root,
1188            "foo",
1189            fio::Flags::FLAG_MAYBE_CREATE
1190                | fio::PERM_READABLE
1191                | fio::PERM_WRITABLE
1192                | fio::Flags::PROTOCOL_DIRECTORY,
1193            Default::default(),
1194        )
1195        .await;
1196
1197        let f = open_file_checked(
1198            &root,
1199            "foo/a",
1200            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1201            &Default::default(),
1202        )
1203        .await;
1204        close_file_checked(f).await;
1205
1206        let (status, src_token) = src.get_token().await.expect("FIDL call failed");
1207        Status::ok(status).expect("get_token failed");
1208        src.rename("a", Event::from(src_token.unwrap()), "b")
1209            .await
1210            .expect("FIDL call failed")
1211            .expect("rename failed");
1212
1213        assert_eq!(
1214            open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1215                .await
1216                .expect_err("Open succeeded")
1217                .root_cause()
1218                .downcast_ref::<Status>()
1219                .expect("No status"),
1220            &Status::NOT_FOUND,
1221        );
1222        let f =
1223            open_file_checked(&root, "foo/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1224        close_file_checked(f).await;
1225
1226        close_dir_checked(src).await;
1227        fixture.close().await;
1228    }
1229
1230    #[fuchsia::test(threads = 10)]
1231    async fn test_rename_overwrites_file() {
1232        use zx::Event;
1233        let fixture = TestFixture::new().await;
1234        let root = fixture.root();
1235
1236        let src = open_dir_checked(
1237            &root,
1238            "foo",
1239            fio::Flags::FLAG_MAYBE_CREATE
1240                | fio::PERM_READABLE
1241                | fio::PERM_WRITABLE
1242                | fio::Flags::PROTOCOL_DIRECTORY,
1243            Default::default(),
1244        )
1245        .await;
1246
1247        let dst = open_dir_checked(
1248            &root,
1249            "bar",
1250            fio::Flags::FLAG_MAYBE_CREATE
1251                | fio::PERM_READABLE
1252                | fio::PERM_WRITABLE
1253                | fio::Flags::PROTOCOL_DIRECTORY,
1254            Default::default(),
1255        )
1256        .await;
1257
1258        // The src file is non-empty.
1259        let src_file = open_file_checked(
1260            &root,
1261            "foo/a",
1262            fio::Flags::FLAG_MAYBE_CREATE
1263                | fio::PERM_READABLE
1264                | fio::PERM_WRITABLE
1265                | fio::Flags::PROTOCOL_FILE,
1266            &Default::default(),
1267        )
1268        .await;
1269        let buf = vec![0xaa as u8; 8192];
1270        file::write(&src_file, buf.as_slice()).await.expect("Failed to write to file");
1271        close_file_checked(src_file).await;
1272
1273        // The dst file is empty (so we can distinguish it).
1274        let f = open_file_checked(
1275            &root,
1276            "bar/b",
1277            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1278            &Default::default(),
1279        )
1280        .await;
1281        close_file_checked(f).await;
1282
1283        let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1284        Status::ok(status).expect("get_token failed");
1285        src.rename("a", Event::from(dst_token.unwrap()), "b")
1286            .await
1287            .expect("FIDL call failed")
1288            .expect("rename failed");
1289
1290        assert_eq!(
1291            open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1292                .await
1293                .expect_err("Open succeeded")
1294                .root_cause()
1295                .downcast_ref::<Status>()
1296                .expect("No status"),
1297            &Status::NOT_FOUND,
1298        );
1299        let file = open_file_checked(
1300            &root,
1301            "bar/b",
1302            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1303            &Default::default(),
1304        )
1305        .await;
1306        let buf = file::read(&file).await.expect("read file failed");
1307        assert_eq!(buf, vec![0xaa as u8; 8192]);
1308        close_file_checked(file).await;
1309
1310        close_dir_checked(dst).await;
1311        close_dir_checked(src).await;
1312        fixture.close().await;
1313    }
1314
1315    #[fuchsia::test(threads = 10)]
1316    async fn test_rename_overwrites_dir() {
1317        use zx::Event;
1318        let fixture = TestFixture::new().await;
1319        let root = fixture.root();
1320
1321        let src = open_dir_checked(
1322            &root,
1323            "foo",
1324            fio::Flags::FLAG_MAYBE_CREATE
1325                | fio::PERM_READABLE
1326                | fio::PERM_WRITABLE
1327                | fio::Flags::PROTOCOL_DIRECTORY,
1328            Default::default(),
1329        )
1330        .await;
1331
1332        let dst = open_dir_checked(
1333            &root,
1334            "bar",
1335            fio::Flags::FLAG_MAYBE_CREATE
1336                | fio::PERM_READABLE
1337                | fio::PERM_WRITABLE
1338                | fio::Flags::PROTOCOL_DIRECTORY,
1339            Default::default(),
1340        )
1341        .await;
1342
1343        // The src dir is non-empty.
1344        open_dir_checked(
1345            &root,
1346            "foo/a",
1347            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1348            Default::default(),
1349        )
1350        .await;
1351        open_file_checked(
1352            &root,
1353            "foo/a/file",
1354            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1355            &Default::default(),
1356        )
1357        .await;
1358        open_dir_checked(
1359            &root,
1360            "bar/b",
1361            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1362            Default::default(),
1363        )
1364        .await;
1365
1366        let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1367        Status::ok(status).expect("get_token failed");
1368        src.rename("a", Event::from(dst_token.unwrap()), "b")
1369            .await
1370            .expect("FIDL call failed")
1371            .expect("rename failed");
1372
1373        assert_eq!(
1374            open_dir(&root, "foo/a", fio::Flags::PROTOCOL_DIRECTORY, &Default::default())
1375                .await
1376                .expect_err("Open succeeded")
1377                .root_cause()
1378                .downcast_ref::<Status>()
1379                .expect("No status"),
1380            &Status::NOT_FOUND,
1381        );
1382        let f =
1383            open_file_checked(&root, "bar/b/file", fio::Flags::PROTOCOL_FILE, &Default::default())
1384                .await;
1385        close_file_checked(f).await;
1386
1387        close_dir_checked(dst).await;
1388        close_dir_checked(src).await;
1389
1390        fixture.close().await;
1391    }
1392
1393    #[fuchsia::test]
1394    async fn test_background_flush() {
1395        let fixture = TestFixture::new().await;
1396        {
1397            let file = open_file_checked(
1398                fixture.root(),
1399                "file",
1400                fio::Flags::FLAG_MAYBE_CREATE
1401                    | fio::PERM_READABLE
1402                    | fio::PERM_WRITABLE
1403                    | fio::Flags::PROTOCOL_FILE,
1404                &Default::default(),
1405            )
1406            .await;
1407            let object_id = file
1408                .get_attributes(fio::NodeAttributesQuery::ID)
1409                .await
1410                .expect("Fidl get attr")
1411                .expect("get attr")
1412                .1
1413                .id
1414                .unwrap();
1415
1416            // Write some data to the file, which will only go to the cache for now.
1417            file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1418
1419            // Initialized to the default size.
1420            assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1421            let volume = fixture.volume().volume().clone();
1422
1423            let data_has_persisted = || async {
1424                // We have to reopen the object each time since this is a distinct handle from the
1425                // one managed by the FxFile.
1426                let object =
1427                    ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1428                        .await
1429                        .expect("open_object failed");
1430                let data = object.contents(8192).await.expect("read failed");
1431                data.len() == 1 && data[..] == [123u8]
1432            };
1433            assert!(!data_has_persisted().await);
1434
1435            fixture.volume().volume().start_background_task(
1436                MemoryPressureConfig {
1437                    mem_normal: MemoryPressureLevelConfig {
1438                        background_task_period: Duration::from_millis(100),
1439                        background_task_initial_delay: Duration::from_millis(100),
1440                        ..Default::default()
1441                    },
1442                    mem_warning: Default::default(),
1443                    mem_critical: Default::default(),
1444                },
1445                None,
1446            );
1447
1448            const MAX_WAIT: Duration = Duration::from_secs(20);
1449            let wait_increments = Duration::from_millis(400);
1450            let mut total_waited = Duration::ZERO;
1451
1452            while total_waited < MAX_WAIT {
1453                fasync::Timer::new(wait_increments).await;
1454                total_waited += wait_increments;
1455
1456                if data_has_persisted().await {
1457                    break;
1458                }
1459            }
1460
1461            assert!(data_has_persisted().await);
1462        }
1463
1464        fixture.close().await;
1465    }
1466
1467    #[fuchsia::test(threads = 2)]
1468    async fn test_background_flush_with_warning_memory_pressure() {
1469        let fixture = TestFixture::new().await;
1470        {
1471            let file = open_file_checked(
1472                fixture.root(),
1473                "file",
1474                fio::Flags::FLAG_MAYBE_CREATE
1475                    | fio::PERM_READABLE
1476                    | fio::PERM_WRITABLE
1477                    | fio::Flags::PROTOCOL_FILE,
1478                &Default::default(),
1479            )
1480            .await;
1481            let object_id = file
1482                .get_attributes(fio::NodeAttributesQuery::ID)
1483                .await
1484                .expect("Fidl get attr")
1485                .expect("get attr")
1486                .1
1487                .id
1488                .unwrap();
1489
1490            // Write some data to the file, which will only go to the cache for now.
1491            file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1492
1493            // Initialized to the default size.
1494            assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1495            let volume = fixture.volume().volume().clone();
1496
1497            let data_has_persisted = || async {
1498                // We have to reopen the object each time since this is a distinct handle from the
1499                // one managed by the FxFile.
1500                let object =
1501                    ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1502                        .await
1503                        .expect("open_object failed");
1504                let data = object.contents(8192).await.expect("read failed");
1505                data.len() == 1 && data[..] == [123u8]
1506            };
1507            assert!(!data_has_persisted().await);
1508
1509            // Configure the flush task to only flush quickly on warning.
1510            let flush_config = MemoryPressureConfig {
1511                mem_normal: MemoryPressureLevelConfig {
1512                    background_task_period: Duration::from_secs(20),
1513                    cache_size_limit: DIRENT_CACHE_LIMIT,
1514                    ..Default::default()
1515                },
1516                mem_warning: MemoryPressureLevelConfig {
1517                    background_task_period: Duration::from_millis(100),
1518                    cache_size_limit: 100,
1519                    background_task_initial_delay: Duration::from_millis(100),
1520                    ..Default::default()
1521                },
1522                mem_critical: MemoryPressureLevelConfig {
1523                    background_task_period: Duration::from_secs(20),
1524                    cache_size_limit: 50,
1525                    ..Default::default()
1526                },
1527            };
1528            fixture.volume().volume().start_background_task(
1529                flush_config,
1530                fixture.volumes_directory().memory_pressure_monitor(),
1531            );
1532
1533            // Send the memory pressure update.
1534            fixture
1535                .memory_pressure_proxy()
1536                .on_level_changed(MemoryPressureLevel::Warning)
1537                .await
1538                .expect("Failed to send memory pressure level change");
1539
1540            // Wait a bit of time for the flush to occur (but less than the normal and critical
1541            // periods).
1542            const MAX_WAIT: Duration = Duration::from_secs(3);
1543            let wait_increments = Duration::from_millis(400);
1544            let mut total_waited = Duration::ZERO;
1545
1546            while total_waited < MAX_WAIT {
1547                fasync::Timer::new(wait_increments).await;
1548                total_waited += wait_increments;
1549
1550                if data_has_persisted().await {
1551                    break;
1552                }
1553            }
1554
1555            assert!(data_has_persisted().await);
1556            assert_eq!(fixture.volume().volume().dirent_cache().limit(), 100);
1557        }
1558
1559        fixture.close().await;
1560    }
1561
1562    #[fuchsia::test(threads = 2)]
1563    async fn test_background_flush_with_critical_memory_pressure() {
1564        let fixture = TestFixture::new().await;
1565        {
1566            let file = open_file_checked(
1567                fixture.root(),
1568                "file",
1569                fio::Flags::FLAG_MAYBE_CREATE
1570                    | fio::PERM_READABLE
1571                    | fio::PERM_WRITABLE
1572                    | fio::Flags::PROTOCOL_FILE,
1573                &Default::default(),
1574            )
1575            .await;
1576            let object_id = file
1577                .get_attributes(fio::NodeAttributesQuery::ID)
1578                .await
1579                .expect("Fidl get attr")
1580                .expect("get attr")
1581                .1
1582                .id
1583                .unwrap();
1584
1585            // Write some data to the file, which will only go to the cache for now.
1586            file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1587
1588            // Initialized to the default size.
1589            assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1590            let volume = fixture.volume().volume().clone();
1591
1592            let data_has_persisted = || async {
1593                // We have to reopen the object each time since this is a distinct handle from the
1594                // one managed by the FxFile.
1595                let object =
1596                    ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1597                        .await
1598                        .expect("open_object failed");
1599                let data = object.contents(8192).await.expect("read failed");
1600                data.len() == 1 && data[..] == [123u8]
1601            };
1602            assert!(!data_has_persisted().await);
1603
1604            let flush_config = MemoryPressureConfig {
1605                mem_normal: MemoryPressureLevelConfig {
1606                    cache_size_limit: DIRENT_CACHE_LIMIT,
1607                    ..Default::default()
1608                },
1609                mem_warning: MemoryPressureLevelConfig {
1610                    cache_size_limit: 100,
1611                    ..Default::default()
1612                },
1613                mem_critical: MemoryPressureLevelConfig {
1614                    cache_size_limit: 50,
1615                    ..Default::default()
1616                },
1617            };
1618            fixture.volume().volume().start_background_task(
1619                flush_config,
1620                fixture.volumes_directory().memory_pressure_monitor(),
1621            );
1622
1623            // Send the memory pressure update.
1624            fixture
1625                .memory_pressure_proxy()
1626                .on_level_changed(MemoryPressureLevel::Critical)
1627                .await
1628                .expect("Failed to send memory pressure level change");
1629
1630            // Critical memory should trigger a flush immediately so expect a flush very quickly.
1631            const MAX_WAIT: Duration = Duration::from_secs(2);
1632            let wait_increments = Duration::from_millis(400);
1633            let mut total_waited = Duration::ZERO;
1634
1635            while total_waited < MAX_WAIT {
1636                fasync::Timer::new(wait_increments).await;
1637                total_waited += wait_increments;
1638
1639                if data_has_persisted().await {
1640                    break;
1641                }
1642            }
1643
1644            assert!(data_has_persisted().await);
1645            assert_eq!(fixture.volume().volume().dirent_cache().limit(), 50);
1646        }
1647
1648        fixture.close().await;
1649    }
1650
1651    // This test verifies that it is safe to query a volume after it is unmounted in case of a race
1652    // during unmount/shutdown.
1653    #[fuchsia::test(threads = 2)]
1654    async fn test_query_info_unmounted_volume() {
1655        const TEST_VOLUME: &str = "test_1234";
1656        let crypt = Arc::new(new_insecure_crypt()) as Arc<dyn Crypt>;
1657
1658        let fixture = TestFixture::new().await;
1659        {
1660            let volumes_directory = fixture.volumes_directory();
1661            let volume = volumes_directory
1662                .create_and_mount_volume(TEST_VOLUME, Some(crypt.clone()), false, None)
1663                .await
1664                .unwrap();
1665
1666            assert!(volume.volume().get_volume_data().await.is_some());
1667
1668            // Unmount it but keep a reference.
1669            volumes_directory
1670                .lock()
1671                .await
1672                .unmount(volume.volume().store().store_object_id())
1673                .await
1674                .expect("unmount failed");
1675
1676            // This returns None, but doesn't crash.
1677            assert!(volume.volume().get_volume_data().await.is_none());
1678        }
1679        fixture.close().await;
1680    }
1681
1682    #[fuchsia::test]
1683    async fn test_project_limit_persistence() {
1684        const BYTES_LIMIT_1: u64 = 123456;
1685        const NODES_LIMIT_1: u64 = 4321;
1686        const BYTES_LIMIT_2: u64 = 456789;
1687        const NODES_LIMIT_2: u64 = 9876;
1688        const VOLUME_NAME: &str = "A";
1689        const FILE_NAME: &str = "B";
1690        const PROJECT_ID: u64 = 42;
1691        const PROJECT_ID2: u64 = 343;
1692        let volume_store_id;
1693        let node_id;
1694        let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1695        let filesystem = FxFilesystem::new_empty(device).await.unwrap();
1696        {
1697            let blob_resupplied_count =
1698                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1699            let volumes_directory = VolumesDirectory::new(
1700                root_volume(filesystem.clone()).await.unwrap(),
1701                Weak::new(),
1702                None,
1703                blob_resupplied_count,
1704                MemoryPressureConfig::default(),
1705            )
1706            .await
1707            .unwrap();
1708
1709            let volume_and_root = volumes_directory
1710                .create_and_mount_volume(VOLUME_NAME, None, false, None)
1711                .await
1712                .expect("create unencrypted volume failed");
1713            volume_store_id = volume_and_root.volume().store().store_object_id();
1714
1715            let (volume_dir_proxy, dir_server_end) =
1716                fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1717            volumes_directory
1718                .serve_volume(&volume_and_root, dir_server_end, false)
1719                .expect("serve_volume failed");
1720
1721            let project_proxy =
1722                connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1723                    .expect("Unable to connect to project id service");
1724
1725            project_proxy
1726                .set_limit(0, BYTES_LIMIT_1, NODES_LIMIT_1)
1727                .await
1728                .unwrap()
1729                .expect_err("Should not set limits for project id 0");
1730
1731            assert_eq!(
1732                project_proxy.clear(0).await.unwrap().expect_err("Should not clear project id 0"),
1733                Status::OUT_OF_RANGE.into_raw()
1734            );
1735
1736            assert_eq!(
1737                project_proxy
1738                    .info(0)
1739                    .await
1740                    .unwrap()
1741                    .expect_err("Should not get info for project id 0"),
1742                Status::OUT_OF_RANGE.into_raw()
1743            );
1744
1745            project_proxy
1746                .set_limit(PROJECT_ID, BYTES_LIMIT_1, NODES_LIMIT_1)
1747                .await
1748                .unwrap()
1749                .expect("To set limits");
1750            {
1751                let BytesAndNodes { bytes, nodes } =
1752                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1753                assert_eq!(bytes, BYTES_LIMIT_1);
1754                assert_eq!(nodes, NODES_LIMIT_1);
1755            }
1756
1757            let file_proxy = {
1758                let (root_proxy, root_server_end) =
1759                    fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1760                volume_dir_proxy
1761                    .open(
1762                        "root",
1763                        fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1764                        &Default::default(),
1765                        root_server_end.into_channel(),
1766                    )
1767                    .expect("Failed to open volume root");
1768
1769                open_file_checked(
1770                    &root_proxy,
1771                    FILE_NAME,
1772                    fio::Flags::FLAG_MAYBE_CREATE
1773                        | fio::PERM_READABLE
1774                        | fio::PERM_WRITABLE
1775                        | fio::Flags::PROTOCOL_FILE,
1776                    &Default::default(),
1777                )
1778                .await
1779            };
1780
1781            let (_, immutable_attributes) =
1782                file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
1783            node_id = immutable_attributes.id.unwrap();
1784
1785            project_proxy
1786                .set_for_node(node_id, 0)
1787                .await
1788                .unwrap()
1789                .expect_err("Should not set 0 project id");
1790
1791            project_proxy
1792                .set_for_node(node_id, PROJECT_ID)
1793                .await
1794                .unwrap()
1795                .expect("Setting project on node");
1796
1797            project_proxy
1798                .set_limit(PROJECT_ID, BYTES_LIMIT_2, NODES_LIMIT_2)
1799                .await
1800                .unwrap()
1801                .expect("To set limits");
1802            {
1803                let BytesAndNodes { bytes, nodes } =
1804                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1805                assert_eq!(bytes, BYTES_LIMIT_2);
1806                assert_eq!(nodes, NODES_LIMIT_2);
1807            }
1808
1809            assert_eq!(
1810                project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1811                PROJECT_ID
1812            );
1813
1814            volumes_directory.terminate().await;
1815            filesystem.close().await.expect("close filesystem failed");
1816        }
1817        device = filesystem.take_device().await;
1818        device.ensure_unique();
1819        device.reopen(false);
1820        let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
1821        {
1822            fsck(filesystem.clone()).await.expect("Fsck");
1823            fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
1824            let blob_resupplied_count =
1825                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1826            let volumes_directory = VolumesDirectory::new(
1827                root_volume(filesystem.clone()).await.unwrap(),
1828                Weak::new(),
1829                None,
1830                blob_resupplied_count,
1831                MemoryPressureConfig::default(),
1832            )
1833            .await
1834            .unwrap();
1835            let volume_and_root = volumes_directory
1836                .mount_volume(VOLUME_NAME, None, false)
1837                .await
1838                .expect("mount unencrypted volume failed");
1839
1840            let (volume_proxy, _scope) = crate::volumes_directory::serve_startup_volume_proxy(
1841                &volumes_directory,
1842                VOLUME_NAME,
1843            );
1844
1845            let project_proxy = {
1846                let (volume_dir_proxy, dir_server_end) =
1847                    fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1848                volumes_directory
1849                    .serve_volume(&volume_and_root, dir_server_end, false)
1850                    .expect("serve_volume failed");
1851
1852                connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1853                    .expect("Unable to connect to project id service")
1854            };
1855
1856            let usage_bytes_and_nodes = {
1857                let (
1858                    BytesAndNodes { bytes: limit_bytes, nodes: limit_nodes },
1859                    usage_bytes_and_nodes,
1860                ) = project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info");
1861                assert_eq!(limit_bytes, BYTES_LIMIT_2);
1862                assert_eq!(limit_nodes, NODES_LIMIT_2);
1863                usage_bytes_and_nodes
1864            };
1865
1866            // Should be unable to clear the project limit, due to being in use.
1867            project_proxy.clear(PROJECT_ID).await.unwrap().expect("To clear limits");
1868
1869            assert_eq!(
1870                project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1871                PROJECT_ID
1872            );
1873            project_proxy
1874                .set_for_node(node_id, PROJECT_ID2)
1875                .await
1876                .unwrap()
1877                .expect("Changing project");
1878            assert_eq!(
1879                project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1880                PROJECT_ID2
1881            );
1882
1883            assert_eq!(
1884                project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
1885                Status::NOT_FOUND.into_raw()
1886            );
1887            assert_eq!(
1888                project_proxy.info(PROJECT_ID2).await.unwrap().expect("Fetching project info").1,
1889                usage_bytes_and_nodes
1890            );
1891
1892            std::mem::drop(volume_proxy);
1893            volumes_directory.terminate().await;
1894            std::mem::drop(volumes_directory);
1895            filesystem.close().await.expect("close filesystem failed");
1896        }
1897        device = filesystem.take_device().await;
1898        device.ensure_unique();
1899        device.reopen(false);
1900        let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
1901        fsck(filesystem.clone()).await.expect("Fsck");
1902        fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
1903        let blob_resupplied_count =
1904            Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1905        let volumes_directory = VolumesDirectory::new(
1906            root_volume(filesystem.clone()).await.unwrap(),
1907            Weak::new(),
1908            None,
1909            blob_resupplied_count,
1910            MemoryPressureConfig::default(),
1911        )
1912        .await
1913        .unwrap();
1914        let volume_and_root = volumes_directory
1915            .mount_volume(VOLUME_NAME, None, false)
1916            .await
1917            .expect("mount unencrypted volume failed");
1918        let (volume_dir_proxy, dir_server_end) =
1919            fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1920        volumes_directory
1921            .serve_volume(&volume_and_root, dir_server_end, false)
1922            .expect("serve_volume failed");
1923        let project_proxy = connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1924            .expect("Unable to connect to project id service");
1925        assert_eq!(
1926            project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
1927            Status::NOT_FOUND.into_raw()
1928        );
1929        volumes_directory.terminate().await;
1930        std::mem::drop(volumes_directory);
1931        filesystem.close().await.expect("close filesystem failed");
1932    }
1933
1934    #[fuchsia::test]
1935    async fn test_project_limit_accounting() {
1936        const BYTES_LIMIT: u64 = 123456;
1937        const NODES_LIMIT: u64 = 4321;
1938        const VOLUME_NAME: &str = "A";
1939        const FILE_NAME: &str = "B";
1940        const PROJECT_ID: u64 = 42;
1941        let volume_store_id;
1942        let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1943        let first_object_id;
1944        let mut bytes_usage;
1945        let filesystem = FxFilesystem::new_empty(device).await.unwrap();
1946        {
1947            let blob_resupplied_count =
1948                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1949            let volumes_directory = VolumesDirectory::new(
1950                root_volume(filesystem.clone()).await.unwrap(),
1951                Weak::new(),
1952                None,
1953                blob_resupplied_count,
1954                MemoryPressureConfig::default(),
1955            )
1956            .await
1957            .unwrap();
1958
1959            let volume_and_root = volumes_directory
1960                .create_and_mount_volume(
1961                    VOLUME_NAME,
1962                    Some(Arc::new(new_insecure_crypt())),
1963                    false,
1964                    None,
1965                )
1966                .await
1967                .expect("create unencrypted volume failed");
1968            volume_store_id = volume_and_root.volume().store().store_object_id();
1969
1970            let (volume_dir_proxy, dir_server_end) =
1971                fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1972            volumes_directory
1973                .serve_volume(&volume_and_root, dir_server_end, false)
1974                .expect("serve_volume failed");
1975
1976            let project_proxy =
1977                connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1978                    .expect("Unable to connect to project id service");
1979
1980            project_proxy
1981                .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
1982                .await
1983                .unwrap()
1984                .expect("To set limits");
1985            {
1986                let BytesAndNodes { bytes, nodes } =
1987                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1988                assert_eq!(bytes, BYTES_LIMIT);
1989                assert_eq!(nodes, NODES_LIMIT);
1990            }
1991
1992            let file_proxy = {
1993                let (root_proxy, root_server_end) =
1994                    fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1995                volume_dir_proxy
1996                    .open(
1997                        "root",
1998                        fio::PERM_READABLE | fio::PERM_WRITABLE,
1999                        &Default::default(),
2000                        root_server_end.into_channel(),
2001                    )
2002                    .expect("Failed to open volume root");
2003
2004                open_file_checked(
2005                    &root_proxy,
2006                    FILE_NAME,
2007                    fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2008                    &Default::default(),
2009                )
2010                .await
2011            };
2012
2013            assert_eq!(
2014                8192,
2015                file_proxy
2016                    .write(&vec![0xff as u8; 8192])
2017                    .await
2018                    .expect("FIDL call failed")
2019                    .map_err(Status::from_raw)
2020                    .expect("File write was successful")
2021            );
2022            file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2023
2024            {
2025                let BytesAndNodes { bytes, nodes } =
2026                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2027                assert_eq!(bytes, 0);
2028                assert_eq!(nodes, 0);
2029            }
2030
2031            let (_, immutable_attributes) =
2032                file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2033            let node_id = immutable_attributes.id.unwrap();
2034
2035            first_object_id = node_id;
2036            project_proxy
2037                .set_for_node(node_id, PROJECT_ID)
2038                .await
2039                .unwrap()
2040                .expect("Setting project on node");
2041
2042            bytes_usage = {
2043                let BytesAndNodes { bytes, nodes } =
2044                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2045                assert!(bytes > 0);
2046                assert_eq!(nodes, 1);
2047                bytes
2048            };
2049
2050            // Grow the file by a block.
2051            assert_eq!(
2052                8192,
2053                file_proxy
2054                    .write(&vec![0xff as u8; 8192])
2055                    .await
2056                    .expect("FIDL call failed")
2057                    .map_err(Status::from_raw)
2058                    .expect("File write was successful")
2059            );
2060            file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2061            bytes_usage = {
2062                let BytesAndNodes { bytes, nodes } =
2063                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2064                assert!(bytes > bytes_usage);
2065                assert_eq!(nodes, 1);
2066                bytes
2067            };
2068
2069            volumes_directory.terminate().await;
2070            filesystem.close().await.expect("close filesystem failed");
2071        }
2072        device = filesystem.take_device().await;
2073        device.ensure_unique();
2074        device.reopen(false);
2075        let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2076        {
2077            fsck(filesystem.clone()).await.expect("Fsck");
2078            fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2079                .await
2080                .expect("Fsck volume");
2081            let blob_resupplied_count =
2082                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2083            let volumes_directory = VolumesDirectory::new(
2084                root_volume(filesystem.clone()).await.unwrap(),
2085                Weak::new(),
2086                None,
2087                blob_resupplied_count,
2088                MemoryPressureConfig::default(),
2089            )
2090            .await
2091            .unwrap();
2092            let volume_and_root = volumes_directory
2093                .mount_volume(VOLUME_NAME, Some(Arc::new(new_insecure_crypt())), false)
2094                .await
2095                .expect("mount unencrypted volume failed");
2096
2097            let (root_proxy, project_proxy) = {
2098                let (volume_dir_proxy, dir_server_end) =
2099                    fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2100                volumes_directory
2101                    .serve_volume(&volume_and_root, dir_server_end, false)
2102                    .expect("serve_volume failed");
2103
2104                let (root_proxy, root_server_end) =
2105                    fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2106                volume_dir_proxy
2107                    .open(
2108                        "root",
2109                        fio::PERM_READABLE | fio::PERM_WRITABLE,
2110                        &Default::default(),
2111                        root_server_end.into_channel(),
2112                    )
2113                    .expect("Failed to open volume root");
2114                let project_proxy = {
2115                    connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2116                        .expect("Unable to connect to project id service")
2117                };
2118                (root_proxy, project_proxy)
2119            };
2120
2121            {
2122                let BytesAndNodes { bytes, nodes } =
2123                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2124                assert_eq!(bytes, bytes_usage);
2125                assert_eq!(nodes, 1);
2126            }
2127
2128            assert_eq!(
2129                project_proxy
2130                    .get_for_node(first_object_id)
2131                    .await
2132                    .unwrap()
2133                    .expect("Checking project"),
2134                PROJECT_ID
2135            );
2136            root_proxy
2137                .unlink(FILE_NAME, &fio::UnlinkOptions::default())
2138                .await
2139                .expect("FIDL call failed")
2140                .expect("unlink failed");
2141            filesystem.graveyard().flush().await;
2142
2143            {
2144                let BytesAndNodes { bytes, nodes } =
2145                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2146                assert_eq!(bytes, 0);
2147                assert_eq!(nodes, 0);
2148            }
2149
2150            let file_proxy = open_file_checked(
2151                &root_proxy,
2152                FILE_NAME,
2153                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2154                &Default::default(),
2155            )
2156            .await;
2157
2158            let (_, immutable_attributes) =
2159                file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2160            let node_id = immutable_attributes.id.unwrap();
2161
2162            project_proxy
2163                .set_for_node(node_id, PROJECT_ID)
2164                .await
2165                .unwrap()
2166                .expect("Applying project");
2167
2168            bytes_usage = {
2169                let BytesAndNodes { bytes, nodes } =
2170                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2171                // Empty file should have less space than the non-empty file from above.
2172                assert!(bytes < bytes_usage);
2173                assert_eq!(nodes, 1);
2174                bytes
2175            };
2176
2177            assert_eq!(
2178                8192,
2179                file_proxy
2180                    .write(&vec![0xff as u8; 8192])
2181                    .await
2182                    .expect("FIDL call failed")
2183                    .map_err(Status::from_raw)
2184                    .expect("File write was successful")
2185            );
2186            file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2187            bytes_usage = {
2188                let BytesAndNodes { bytes, nodes } =
2189                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2190                assert!(bytes > bytes_usage);
2191                assert_eq!(nodes, 1);
2192                bytes
2193            };
2194
2195            // Trim to zero. Bytes should decrease.
2196            file_proxy.resize(0).await.expect("FIDL call failed").expect("Resize file");
2197            file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2198            {
2199                let BytesAndNodes { bytes, nodes } =
2200                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2201                assert!(bytes < bytes_usage);
2202                assert_eq!(nodes, 1);
2203            };
2204
2205            // Dropping node from project. Usage should go to zero.
2206            project_proxy
2207                .clear_for_node(node_id)
2208                .await
2209                .expect("FIDL call failed")
2210                .expect("Clear failed.");
2211            {
2212                let BytesAndNodes { bytes, nodes } =
2213                    project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2214                assert_eq!(bytes, 0);
2215                assert_eq!(nodes, 0);
2216            };
2217
2218            volumes_directory.terminate().await;
2219            filesystem.close().await.expect("close filesystem failed");
2220        }
2221        device = filesystem.take_device().await;
2222        device.ensure_unique();
2223        device.reopen(false);
2224        let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2225        fsck(filesystem.clone()).await.expect("Fsck");
2226        fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2227            .await
2228            .expect("Fsck volume");
2229        filesystem.close().await.expect("close filesystem failed");
2230    }
2231
2232    #[fuchsia::test]
2233    async fn test_project_node_inheritance() {
2234        const BYTES_LIMIT: u64 = 123456;
2235        const NODES_LIMIT: u64 = 4321;
2236        const VOLUME_NAME: &str = "A";
2237        const DIR_NAME: &str = "B";
2238        const SUBDIR_NAME: &str = "C";
2239        const FILE_NAME: &str = "D";
2240        const PROJECT_ID: u64 = 42;
2241        let volume_store_id;
2242        let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2243        let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2244        {
2245            let blob_resupplied_count =
2246                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2247            let volumes_directory = VolumesDirectory::new(
2248                root_volume(filesystem.clone()).await.unwrap(),
2249                Weak::new(),
2250                None,
2251                blob_resupplied_count,
2252                MemoryPressureConfig::default(),
2253            )
2254            .await
2255            .unwrap();
2256
2257            let volume_and_root = volumes_directory
2258                .create_and_mount_volume(
2259                    VOLUME_NAME,
2260                    Some(Arc::new(new_insecure_crypt())),
2261                    false,
2262                    None,
2263                )
2264                .await
2265                .expect("create unencrypted volume failed");
2266            volume_store_id = volume_and_root.volume().store().store_object_id();
2267
2268            let (volume_dir_proxy, dir_server_end) =
2269                fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2270            volumes_directory
2271                .serve_volume(&volume_and_root, dir_server_end, false)
2272                .expect("serve_volume failed");
2273
2274            let project_proxy =
2275                connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2276                    .expect("Unable to connect to project id service");
2277
2278            project_proxy
2279                .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
2280                .await
2281                .unwrap()
2282                .expect("To set limits");
2283
2284            let dir_proxy = {
2285                let (root_proxy, root_server_end) =
2286                    fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2287                volume_dir_proxy
2288                    .open(
2289                        "root",
2290                        fio::PERM_READABLE | fio::PERM_WRITABLE,
2291                        &Default::default(),
2292                        root_server_end.into_channel(),
2293                    )
2294                    .expect("Failed to open volume root");
2295
2296                open_dir_checked(
2297                    &root_proxy,
2298                    DIR_NAME,
2299                    fio::Flags::FLAG_MAYBE_CREATE
2300                        | fio::PERM_READABLE
2301                        | fio::PERM_WRITABLE
2302                        | fio::Flags::PROTOCOL_DIRECTORY,
2303                    Default::default(),
2304                )
2305                .await
2306            };
2307            {
2308                let (_, immutable_attributes) =
2309                    dir_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2310                let node_id = immutable_attributes.id.unwrap();
2311
2312                project_proxy
2313                    .set_for_node(node_id, PROJECT_ID)
2314                    .await
2315                    .unwrap()
2316                    .expect("Setting project on node");
2317            }
2318
2319            let subdir_proxy = open_dir_checked(
2320                &dir_proxy,
2321                SUBDIR_NAME,
2322                fio::Flags::FLAG_MAYBE_CREATE
2323                    | fio::PERM_READABLE
2324                    | fio::PERM_WRITABLE
2325                    | fio::Flags::PROTOCOL_DIRECTORY,
2326                Default::default(),
2327            )
2328            .await;
2329            {
2330                let (_, immutable_attributes) = subdir_proxy
2331                    .get_attributes(fio::NodeAttributesQuery::ID)
2332                    .await
2333                    .unwrap()
2334                    .unwrap();
2335                let node_id = immutable_attributes.id.unwrap();
2336
2337                assert_eq!(
2338                    project_proxy
2339                        .get_for_node(node_id)
2340                        .await
2341                        .unwrap()
2342                        .expect("Setting project on node"),
2343                    PROJECT_ID
2344                );
2345            }
2346
2347            let file_proxy = open_file_checked(
2348                &subdir_proxy,
2349                FILE_NAME,
2350                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2351                &Default::default(),
2352            )
2353            .await;
2354            {
2355                let (_, immutable_attributes) =
2356                    file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2357                let node_id = immutable_attributes.id.unwrap();
2358
2359                assert_eq!(
2360                    project_proxy
2361                        .get_for_node(node_id)
2362                        .await
2363                        .unwrap()
2364                        .expect("Setting project on node"),
2365                    PROJECT_ID
2366                );
2367            }
2368
2369            // An unnamed temporary file is created slightly differently to a regular file object.
2370            // Just in case, check that it inherits project ID as well.
2371            let tmpfile_proxy = open_file_checked(
2372                &subdir_proxy,
2373                ".",
2374                fio::Flags::PROTOCOL_FILE
2375                    | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2376                    | fio::PERM_READABLE,
2377                &fio::Options::default(),
2378            )
2379            .await;
2380            {
2381                let (_, immutable_attributes) = tmpfile_proxy
2382                    .get_attributes(fio::NodeAttributesQuery::ID)
2383                    .await
2384                    .unwrap()
2385                    .unwrap();
2386                let node_id: u64 = immutable_attributes.id.unwrap();
2387                assert_eq!(
2388                    project_proxy
2389                        .get_for_node(node_id)
2390                        .await
2391                        .unwrap()
2392                        .expect("Setting project on node"),
2393                    PROJECT_ID
2394                );
2395            }
2396
2397            let BytesAndNodes { nodes, .. } =
2398                project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2399            assert_eq!(nodes, 3);
2400            volumes_directory.terminate().await;
2401            filesystem.close().await.expect("close filesystem failed");
2402        }
2403        device = filesystem.take_device().await;
2404        device.ensure_unique();
2405        device.reopen(false);
2406        let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2407        fsck(filesystem.clone()).await.expect("Fsck");
2408        fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2409            .await
2410            .expect("Fsck volume");
2411        filesystem.close().await.expect("close filesystem failed");
2412    }
2413
2414    #[fuchsia::test]
2415    async fn test_project_listing() {
2416        const VOLUME_NAME: &str = "A";
2417        const FILE_NAME: &str = "B";
2418        const NON_ZERO_PROJECT_ID: u64 = 3;
2419        let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2420        let volume_store_id;
2421        let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2422        {
2423            let blob_resupplied_count =
2424                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2425            let volumes_directory = VolumesDirectory::new(
2426                root_volume(filesystem.clone()).await.unwrap(),
2427                Weak::new(),
2428                None,
2429                blob_resupplied_count,
2430                MemoryPressureConfig::default(),
2431            )
2432            .await
2433            .unwrap();
2434            let volume_and_root = volumes_directory
2435                .create_and_mount_volume(VOLUME_NAME, None, false, None)
2436                .await
2437                .expect("create unencrypted volume failed");
2438            volume_store_id = volume_and_root.volume().store().store_object_id();
2439
2440            let (volume_dir_proxy, dir_server_end) =
2441                fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2442            volumes_directory
2443                .serve_volume(&volume_and_root, dir_server_end, false)
2444                .expect("serve_volume failed");
2445            let project_proxy =
2446                connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2447                    .expect("Unable to connect to project id service");
2448            // This is just to ensure that the small numbers below can be used for this test.
2449            assert!(FxVolume::MAX_PROJECT_ENTRIES >= 4);
2450            // Create a bunch of proxies. 3 more than the limit to ensure pagination.
2451            let num_entries = u64::try_from(FxVolume::MAX_PROJECT_ENTRIES + 3).unwrap();
2452            for project_id in 1..=num_entries {
2453                project_proxy.set_limit(project_id, 1, 1).await.unwrap().expect("To set limits");
2454            }
2455
2456            // Add one usage entry to be interspersed with the limit entries. Verifies that the
2457            // iterator will progress passed it with no effect.
2458            let file_proxy = {
2459                let (root_proxy, root_server_end) =
2460                    fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2461                volume_dir_proxy
2462                    .open(
2463                        "root",
2464                        fio::PERM_READABLE | fio::PERM_WRITABLE,
2465                        &Default::default(),
2466                        root_server_end.into_channel(),
2467                    )
2468                    .expect("Failed to open volume root");
2469
2470                open_file_checked(
2471                    &root_proxy,
2472                    FILE_NAME,
2473                    fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2474                    &Default::default(),
2475                )
2476                .await
2477            };
2478            let (_, immutable_attributes) =
2479                file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2480            let node_id = immutable_attributes.id.unwrap();
2481            project_proxy
2482                .set_for_node(node_id, NON_ZERO_PROJECT_ID)
2483                .await
2484                .unwrap()
2485                .expect("Setting project on node");
2486            {
2487                let BytesAndNodes { nodes, .. } = project_proxy
2488                    .info(NON_ZERO_PROJECT_ID)
2489                    .await
2490                    .unwrap()
2491                    .expect("Fetching project info")
2492                    .1;
2493                assert_eq!(nodes, 1);
2494            }
2495
2496            // If this `unwrap()` fails, it is likely the MAX_PROJECT_ENTRIES is too large for fidl.
2497            let (mut entries, mut next_token) =
2498                project_proxy.list(None).await.unwrap().expect("To get project listing");
2499            assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2500            assert!(next_token.is_some());
2501            assert!(entries.contains(&1));
2502            assert!(entries.contains(&3));
2503            assert!(!entries.contains(&num_entries));
2504            // Page two should have a small set at the end.
2505            (entries, next_token) = project_proxy
2506                .list(next_token.as_deref())
2507                .await
2508                .unwrap()
2509                .expect("To get project listing");
2510            assert_eq!(entries.len(), 3);
2511            assert!(next_token.is_none());
2512            assert!(entries.contains(&num_entries));
2513            assert!(!entries.contains(&1));
2514            assert!(!entries.contains(&3));
2515            // Delete a couple and list all again, but one has usage still.
2516            project_proxy.clear(1).await.unwrap().expect("Clear project");
2517            project_proxy.clear(3).await.unwrap().expect("Clear project");
2518            (entries, next_token) =
2519                project_proxy.list(None).await.unwrap().expect("To get project listing");
2520            assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2521            assert!(next_token.is_some());
2522            assert!(!entries.contains(&num_entries));
2523            assert!(!entries.contains(&1));
2524            assert!(entries.contains(&3));
2525            (entries, next_token) = project_proxy
2526                .list(next_token.as_deref())
2527                .await
2528                .unwrap()
2529                .expect("To get project listing");
2530            assert_eq!(entries.len(), 2);
2531            assert!(next_token.is_none());
2532            assert!(entries.contains(&num_entries));
2533            // Delete two more to hit the edge case.
2534            project_proxy.clear(2).await.unwrap().expect("Clear project");
2535            project_proxy.clear(4).await.unwrap().expect("Clear project");
2536            (entries, next_token) =
2537                project_proxy.list(None).await.unwrap().expect("To get project listing");
2538            assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2539            assert!(next_token.is_none());
2540            assert!(entries.contains(&num_entries));
2541            volumes_directory.terminate().await;
2542            filesystem.close().await.expect("close filesystem failed");
2543        }
2544        device = filesystem.take_device().await;
2545        device.ensure_unique();
2546        device.reopen(false);
2547        let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2548        fsck(filesystem.clone()).await.expect("Fsck");
2549        fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
2550        filesystem.close().await.expect("close filesystem failed");
2551    }
2552
2553    #[fuchsia::test(threads = 10)]
2554    async fn test_profile_blob() {
2555        let mut hashes = Vec::new();
2556        let device = {
2557            let fixture = blob_testing::new_blob_fixture().await;
2558
2559            for i in 0..3u64 {
2560                let hash =
2561                    fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
2562                hashes.push(hash);
2563            }
2564            fixture.close().await
2565        };
2566        device.ensure_unique();
2567
2568        device.reopen(false);
2569        let mut device = {
2570            let fixture = blob_testing::open_blob_fixture(device).await;
2571            fixture
2572                .volume()
2573                .volume()
2574                .record_and_replay_profile(new_profile_state(true), "foo")
2575                .await
2576                .expect("Recording");
2577
2578            // Page in the zero offsets only to avoid readahead strangeness.
2579            let mut writable = [0u8];
2580            for hash in &hashes {
2581                let vmo = fixture.get_blob_vmo(*hash).await;
2582                vmo.read(&mut writable, 0).expect("Vmo read");
2583            }
2584            fixture.volume().volume().stop_profile_tasks().await;
2585            fixture.close().await
2586        };
2587
2588        // Do this multiple times to ensure that the re-recording doesn't drop anything.
2589        for i in 0..3 {
2590            device.ensure_unique();
2591            device.reopen(false);
2592            let fixture = blob_testing::open_blob_fixture(device).await;
2593            {
2594                // Ensure that nothing is paged in right now.
2595                for hash in &hashes {
2596                    let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2597                    assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2598                }
2599
2600                fixture
2601                    .volume()
2602                    .volume()
2603                    .record_and_replay_profile(new_profile_state(true), "foo")
2604                    .await
2605                    .expect("Replaying");
2606
2607                // Move the file in flight to ensure a new version lands to be used next time.
2608                {
2609                    let store = fixture.volume().volume().store();
2610                    let store_id = store.store_object_id();
2611                    let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
2612                    let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
2613                    let mut transaction = store
2614                        .new_transaction(
2615                            lock_keys!(
2616                                LockKey::object(store_id, dir.object_id()),
2617                                LockKey::object(store_id, old_file),
2618                            ),
2619                            Options::default(),
2620                        )
2621                        .await
2622                        .unwrap();
2623                    replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
2624                        .await
2625                        .expect("Replace old profile.");
2626                    transaction.commit().await.unwrap();
2627                    assert!(
2628                        dir.lookup("foo").await.unwrap().is_none(),
2629                        "Old profile should be moved"
2630                    );
2631                }
2632
2633                // Await all data being played back by checking that things have paged in.
2634                async {
2635                    for hash in &hashes {
2636                        // Fetch vmo this way as well to ensure that the open is counting the file
2637                        // as used in the current recording.
2638                        let _vmo = fixture.get_blob_vmo(*hash).await;
2639                        let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2640                        while blob.vmo().info().unwrap().populated_bytes == 0 {
2641                            fasync::Timer::new(Duration::from_millis(25)).await;
2642                        }
2643                    }
2644                }
2645                .on_timeout(std::time::Duration::from_secs(120), || {
2646                    panic!("Replay did not page in for all VMOs")
2647                })
2648                .await;
2649
2650                // Complete the recording.
2651                fixture.volume().volume().stop_profile_tasks().await;
2652            }
2653            device = fixture.close().await;
2654        }
2655    }
2656
2657    #[fuchsia::test(threads = 10)]
2658    async fn test_profile_file() {
2659        let mut hashes = Vec::new();
2660        let crypt_file_id;
2661        let device = {
2662            let fixture = TestFixture::new().await;
2663            // Include a crypt file to access during the recording. It should not be added.
2664            {
2665                let crypt_dir = open_dir_checked(
2666                    fixture.root(),
2667                    "crypt_dir",
2668                    fio::Flags::FLAG_MUST_CREATE
2669                        | fio::PERM_WRITABLE
2670                        | fio::Flags::PERM_GET_ATTRIBUTES,
2671                    Default::default(),
2672                )
2673                .await;
2674                let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2675                crypt
2676                    .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2677                    .expect("add_wrapping_key failed");
2678                crypt_dir
2679                    .update_attributes(&fio::MutableNodeAttributes {
2680                        wrapping_key_id: Some(WRAPPING_KEY_ID),
2681                        ..Default::default()
2682                    })
2683                    .await
2684                    .expect("update_attributes wire call failed")
2685                    .expect("update_attributes failed");
2686                let crypt_file = open_file_checked(
2687                    &crypt_dir,
2688                    "crypt_file",
2689                    fio::Flags::FLAG_MUST_CREATE
2690                        | fio::PERM_WRITABLE
2691                        | fio::Flags::PERM_GET_ATTRIBUTES,
2692                    &Default::default(),
2693                )
2694                .await;
2695                crypt_file.write("asdf".as_bytes()).await.unwrap().expect("Writing crypt file");
2696                crypt_file_id = crypt_file
2697                    .get_attributes(fio::NodeAttributesQuery::ID)
2698                    .await
2699                    .unwrap()
2700                    .expect("Get id")
2701                    .1
2702                    .id
2703                    .expect("Reading id in response");
2704            }
2705
2706            for i in 0..3u64 {
2707                let file_proxy = open_file_checked(
2708                    fixture.root(),
2709                    &i.to_string(),
2710                    fio::Flags::FLAG_MUST_CREATE
2711                        | fio::PERM_WRITABLE
2712                        | fio::Flags::PERM_GET_ATTRIBUTES,
2713                    &Default::default(),
2714                )
2715                .await;
2716                file_proxy.write(i.to_string().as_bytes()).await.unwrap().expect("Writing file");
2717                let id = file_proxy
2718                    .get_attributes(fio::NodeAttributesQuery::ID)
2719                    .await
2720                    .unwrap()
2721                    .expect("Get id")
2722                    .1
2723                    .id
2724                    .expect("Reading id in response");
2725                hashes.push((i, id));
2726            }
2727            fixture.close().await
2728        };
2729        device.ensure_unique();
2730
2731        device.reopen(false);
2732        let mut device = {
2733            let fixture = TestFixture::open(
2734                device,
2735                TestFixtureOptions { format: false, ..Default::default() },
2736            )
2737            .await;
2738            fixture
2739                .volume()
2740                .volume()
2741                .record_and_replay_profile(new_profile_state(false), "foo")
2742                .await
2743                .expect("Recording");
2744
2745            {
2746                let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2747                crypt
2748                    .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2749                    .expect("add wrapping key failed");
2750                let crypt_file = open_file_checked(
2751                    fixture.root(),
2752                    "crypt_dir/crypt_file",
2753                    fio::PERM_READABLE,
2754                    &Default::default(),
2755                )
2756                .await;
2757                crypt_file.read(1).await.unwrap().expect("Reading crypt file");
2758            }
2759            // Page in the zero offsets only to avoid readahead strangeness.
2760            for (i, _) in &hashes {
2761                let file_proxy = open_file_checked(
2762                    fixture.root(),
2763                    &i.to_string(),
2764                    fio::PERM_READABLE,
2765                    &Default::default(),
2766                )
2767                .await;
2768                file_proxy.read(1).await.unwrap().expect("Reading file");
2769            }
2770            fixture.volume().volume().stop_profile_tasks().await;
2771            fixture.close().await
2772        };
2773
2774        // Do this multiple times to ensure that the re-recording doesn't drop anything.
2775        for i in 0..3 {
2776            device.ensure_unique();
2777            device.reopen(false);
2778            let fixture = TestFixture::open(
2779                device,
2780                TestFixtureOptions { format: false, ..Default::default() },
2781            )
2782            .await;
2783            {
2784                // Need to get the root vmo to check committed bytes.
2785                let volume = fixture.volume().volume().clone();
2786                // Ensure that nothing is paged in right now.
2787                {
2788                    let crypt_file = volume
2789                        .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
2790                        .await
2791                        .expect("Opening file internally")
2792                        .into_any()
2793                        .downcast::<FxFile>()
2794                        .expect("Should be file");
2795                    assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
2796                }
2797                for (_, id) in &hashes {
2798                    let file = volume
2799                        .get_or_load_node(*id, ObjectDescriptor::File, None)
2800                        .await
2801                        .expect("Opening file internally")
2802                        .into_any()
2803                        .downcast::<FxFile>()
2804                        .expect("Should be file");
2805                    assert_eq!(file.vmo().info().unwrap().populated_bytes, 0);
2806                }
2807
2808                fixture
2809                    .volume()
2810                    .volume()
2811                    .record_and_replay_profile(new_profile_state(false), "foo")
2812                    .await
2813                    .expect("Replaying");
2814
2815                // Move the file in flight to ensure a new version lands to be used next time.
2816                {
2817                    let store = fixture.volume().volume().store();
2818                    let store_id = store.store_object_id();
2819                    let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
2820                    let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
2821                    let mut transaction = store
2822                        .new_transaction(
2823                            lock_keys!(
2824                                LockKey::object(store_id, dir.object_id()),
2825                                LockKey::object(store_id, old_file),
2826                            ),
2827                            Options::default(),
2828                        )
2829                        .await
2830                        .unwrap();
2831                    replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
2832                        .await
2833                        .expect("Replace old profile.");
2834                    transaction.commit().await.unwrap();
2835                    assert!(
2836                        dir.lookup("foo").await.unwrap().is_none(),
2837                        "Old profile should be moved"
2838                    );
2839                }
2840
2841                // Await all data being played back by checking that things have paged in.
2842                async {
2843                    for (_, id) in &hashes {
2844                        let file = volume
2845                            .get_or_load_node(*id, ObjectDescriptor::File, None)
2846                            .await
2847                            .expect("Opening file internally")
2848                            .into_any()
2849                            .downcast::<FxFile>()
2850                            .expect("Should be file");
2851                        while file.vmo().info().unwrap().populated_bytes == 0 {
2852                            fasync::Timer::new(Duration::from_millis(25)).await;
2853                        }
2854                    }
2855                }
2856                .on_timeout(std::time::Duration::from_secs(120), || {
2857                    panic!("Replay did not page in for all VMOs")
2858                })
2859                .await;
2860                // The crypt file access should not have been recorded or replayed.
2861                {
2862                    let crypt_file = volume
2863                        .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
2864                        .await
2865                        .expect("Opening file internally")
2866                        .into_any()
2867                        .downcast::<FxFile>()
2868                        .expect("Should be file");
2869                    assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
2870                }
2871
2872                // Open all the files to show that they have been used.
2873                for (i, _) in &hashes {
2874                    let _file_proxy = open_file_checked(
2875                        fixture.root(),
2876                        &i.to_string(),
2877                        fio::PERM_READABLE,
2878                        &Default::default(),
2879                    )
2880                    .await;
2881                }
2882
2883                // Complete the recording.
2884                fixture.volume().volume().stop_profile_tasks().await;
2885            }
2886            device = fixture.close().await;
2887        }
2888    }
2889
2890    #[fuchsia::test(threads = 10)]
2891    async fn test_profile_update() {
2892        let mut hashes = Vec::new();
2893        let device = {
2894            let fixture = blob_testing::new_blob_fixture().await;
2895            for i in 0..2u64 {
2896                let hash =
2897                    fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
2898                hashes.push(hash);
2899            }
2900            fixture.close().await
2901        };
2902        device.ensure_unique();
2903
2904        device.reopen(false);
2905        let device = {
2906            let fixture = blob_testing::open_blob_fixture(device).await;
2907
2908            {
2909                let volume = fixture.volume().volume();
2910                volume
2911                    .record_and_replay_profile(new_profile_state(true), "foo")
2912                    .await
2913                    .expect("Recording");
2914
2915                let original_recorded = RECORDED.load(Ordering::Relaxed);
2916
2917                // Page in the zero offsets only to avoid readahead strangeness.
2918                {
2919                    let mut writable = [0u8];
2920                    let hash = &hashes[0];
2921                    let vmo = fixture.get_blob_vmo(*hash).await;
2922                    vmo.read(&mut writable, 0).expect("Vmo read");
2923                }
2924
2925                // The recording happens asynchronously, so we must wait.  This is crude, but it's
2926                // only for testing and it's simple.
2927                while RECORDED.load(Ordering::Relaxed) == original_recorded {
2928                    fasync::Timer::new(std::time::Duration::from_millis(10)).await;
2929                }
2930
2931                volume.stop_profile_tasks().await;
2932            }
2933            fixture.close().await
2934        };
2935
2936        device.ensure_unique();
2937        device.reopen(false);
2938        let fixture = blob_testing::open_blob_fixture(device).await;
2939        {
2940            // Need to get the root vmo to check committed bytes.
2941            // Ensure that nothing is paged in right now.
2942            for hash in &hashes {
2943                let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2944                assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2945            }
2946
2947            let volume = fixture.volume().volume();
2948
2949            volume
2950                .record_and_replay_profile(new_profile_state(true), "foo")
2951                .await
2952                .expect("Replaying");
2953
2954            // Await all data being played back by checking that things have paged in.
2955            async {
2956                let hash = &hashes[0];
2957                let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2958                while blob.vmo().info().unwrap().populated_bytes == 0 {
2959                    fasync::Timer::new(Duration::from_millis(25)).await;
2960                }
2961            }
2962            .on_timeout(std::time::Duration::from_secs(120), || {
2963                panic!("Replay did not page in for all VMOs")
2964            })
2965            .await;
2966
2967            let original_recorded = RECORDED.load(Ordering::Relaxed);
2968
2969            // Record the new profile that will overwrite it.
2970            {
2971                let mut writable = [0u8];
2972                let hash = &hashes[1];
2973                let vmo = fixture.get_blob_vmo(*hash).await;
2974                vmo.read(&mut writable, 0).expect("Vmo read");
2975            }
2976
2977            // The recording happens asynchronously, so we must wait.  This is crude, but it's only
2978            // for testing and it's simple.
2979            while RECORDED.load(Ordering::Relaxed) == original_recorded {
2980                fasync::Timer::new(std::time::Duration::from_millis(10)).await;
2981            }
2982
2983            // Complete the recording.
2984            volume.stop_profile_tasks().await;
2985        }
2986        let device = fixture.close().await;
2987
2988        device.ensure_unique();
2989        device.reopen(false);
2990        let fixture = blob_testing::open_blob_fixture(device).await;
2991        {
2992            // Need to get the root vmo to check committed bytes.
2993            // Ensure that nothing is paged in right now.
2994            for hash in &hashes {
2995                let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2996                assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2997            }
2998
2999            fixture
3000                .volume()
3001                .volume()
3002                .record_and_replay_profile(new_profile_state(true), "foo")
3003                .await
3004                .expect("Replaying");
3005
3006            // Await all data being played back by checking that things have paged in.
3007            async {
3008                let hash = &hashes[1];
3009                let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3010                while blob.vmo().info().unwrap().populated_bytes == 0 {
3011                    fasync::Timer::new(Duration::from_millis(25)).await;
3012                }
3013            }
3014            .on_timeout(std::time::Duration::from_secs(30), || {
3015                panic!("Replay did not page in for all VMOs")
3016            })
3017            .await;
3018
3019            // Complete the recording.
3020            fixture.volume().volume().stop_profile_tasks().await;
3021
3022            // Verify that first blob was not paged in as the it should be dropped from the profile.
3023            {
3024                let hash = &hashes[0];
3025                let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3026                assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
3027            }
3028        }
3029        fixture.close().await;
3030    }
3031
3032    #[fuchsia::test(threads = 10)]
3033    async fn test_unencrypted_volume() {
3034        let fixture = TestFixture::new_unencrypted().await;
3035        let root = fixture.root();
3036
3037        let f = open_file_checked(
3038            &root,
3039            "foo",
3040            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3041            &Default::default(),
3042        )
3043        .await;
3044        close_file_checked(f).await;
3045
3046        fixture.close().await;
3047    }
3048
3049    #[fuchsia::test]
3050    async fn test_read_only_unencrypted_volume() {
3051        // Make a new Fxfs filesystem with an unencrypted volume named "vol".
3052        let fs = {
3053            let device = fxfs::filesystem::mkfs_with_volume(
3054                DeviceHolder::new(FakeDevice::new(8192, 512)),
3055                "vol",
3056                None,
3057            )
3058            .await
3059            .unwrap();
3060            // Re-open the device as read-only and mount the filesystem as read-only.
3061            device.reopen(true);
3062            FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3063        };
3064        // Ensure we can access the volume and gracefully terminate any tasks.
3065        {
3066            let root_volume = root_volume(fs.clone()).await.unwrap();
3067            let store = root_volume.volume("vol", StoreOptions::default()).await.unwrap();
3068            let unique_id = store.store_object_id();
3069            let blob_resupplied_count =
3070                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
3071            let volume = FxVolume::new(
3072                Weak::new(),
3073                store,
3074                unique_id,
3075                "vol".to_owned(),
3076                blob_resupplied_count,
3077                MemoryPressureConfig::default(),
3078            )
3079            .unwrap();
3080            volume.terminate().await;
3081        }
3082        // Close the filesystem, and make sure we don't have any dangling references.
3083        fs.close().await.unwrap();
3084        let device = fs.take_device().await;
3085        device.ensure_unique();
3086    }
3087
3088    #[fuchsia::test]
3089    async fn test_read_only_encrypted_volume() {
3090        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
3091        // Make a new Fxfs filesystem with an encrypted volume named "vol".
3092        let fs = {
3093            let device = fxfs::filesystem::mkfs_with_volume(
3094                DeviceHolder::new(FakeDevice::new(8192, 512)),
3095                "vol",
3096                Some(crypt.clone()),
3097            )
3098            .await
3099            .unwrap();
3100            // Re-open the device as read-only and mount the filesystem as read-only.
3101            device.reopen(true);
3102            FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3103        };
3104        // Ensure we can access the volume and gracefully terminate any tasks.
3105        {
3106            let root_volume = root_volume(fs.clone()).await.unwrap();
3107            let store = root_volume
3108                .volume("vol", StoreOptions { crypt: Some(crypt), ..StoreOptions::default() })
3109                .await
3110                .unwrap();
3111            let unique_id = store.store_object_id();
3112            let blob_resupplied_count =
3113                Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
3114            let volume = FxVolume::new(
3115                Weak::new(),
3116                store,
3117                unique_id,
3118                "vol".to_owned(),
3119                blob_resupplied_count,
3120                MemoryPressureConfig::default(),
3121            )
3122            .unwrap();
3123            volume.terminate().await;
3124        }
3125        // Close the filesystem, and make sure we don't have any dangling references.
3126        fs.close().await.unwrap();
3127        let device = fs.take_device().await;
3128        device.ensure_unique();
3129    }
3130}