Skip to main content

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