Skip to main content

fxfs/object_store/
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::errors::FxfsError;
6use crate::filesystem::{FlushReason, FxFilesystem};
7use crate::object_store::directory::Directory;
8use crate::object_store::transaction::{LockKeys, Mutation, Options, Transaction, lock_keys};
9use crate::object_store::tree_cache::TreeCache;
10use crate::object_store::{
11    ChildValue, DirType, INVALID_OBJECT_ID, LockKey, NewChildStoreOptions, ObjectDescriptor,
12    ObjectKey, ObjectStore, ObjectValue, StoreOptions, load_store_info,
13};
14use crate::serialized_types::LATEST_VERSION;
15use anyhow::{Context, Error, anyhow, bail, ensure};
16use log::error;
17use std::sync::Arc;
18
19// Volumes are a grouping of an object store and a root directory within this object store. They
20// model a hierarchical tree of objects within a single store.
21//
22// Typically there will be one root volume which is referenced directly by the superblock. This root
23// volume stores references to all other volumes on the system (as volumes/foo, volumes/bar, ...).
24// For now, this hierarchy is only one deep.
25
26pub const VOLUMES_DIRECTORY: &str = "volumes";
27
28/// RootVolume is the top-level volume which stores references to all of the other Volumes.
29pub struct RootVolume {
30    _root_directory: Directory<ObjectStore>,
31    filesystem: Arc<FxFilesystem>,
32}
33
34impl RootVolume {
35    pub fn filesystem(&self) -> &Arc<FxFilesystem> {
36        &self.filesystem
37    }
38
39    pub fn volume_directory(&self) -> &Directory<ObjectStore> {
40        self.filesystem.object_manager().volume_directory()
41    }
42
43    /// Creates a new volume under a transaction lock.
44    pub async fn new_volume(
45        &self,
46        volume_name: &str,
47        options: NewChildStoreOptions,
48    ) -> Result<Arc<ObjectStore>, Error> {
49        let root_store = self.filesystem.root_store();
50        let store;
51        let mut transaction = root_store
52            .new_transaction(
53                lock_keys![LockKey::object(
54                    root_store.store_object_id(),
55                    self.volume_directory().object_id(),
56                )],
57                Options::default(),
58            )
59            .await?;
60
61        ensure!(
62            matches!(self.volume_directory().lookup(volume_name).await?, None),
63            FxfsError::AlreadyExists
64        );
65        store = root_store
66            .new_child_store(&mut transaction, options, Some(Box::new(TreeCache::new())))
67            .await?;
68        store.set_trace(self.filesystem.trace());
69
70        // We must register the store here because create will add mutations for the store.
71        self.filesystem.object_manager().add_store(store.clone());
72
73        // If the transaction fails, we must unregister the store.
74        struct CleanUp<'a>(&'a ObjectStore);
75        impl Drop for CleanUp<'_> {
76            fn drop(&mut self) {
77                self.0
78                    .filesystem()
79                    .ensure_local()
80                    .object_manager()
81                    .forget_store(self.0.store_object_id());
82            }
83        }
84        let clean_up = CleanUp(&store);
85
86        // Actually create the store in the transaction.
87        store.create(&mut transaction).await?;
88
89        self.volume_directory()
90            .add_child_volume(&mut transaction, volume_name, store.store_object_id())
91            .await?;
92        transaction.commit().await?;
93
94        std::mem::forget(clean_up);
95
96        Ok(store)
97    }
98
99    /// Returns the volume with the given name.  This is not thread-safe.
100    pub async fn volume(
101        &self,
102        volume_name: &str,
103        options: StoreOptions,
104    ) -> Result<Arc<ObjectStore>, Error> {
105        // Lookup the volume object in the volume directory.
106        let (store_object_id, descriptor, _) = self
107            .volume_directory()
108            .lookup(volume_name)
109            .await
110            .context("Volume lookup failed")?
111            .ok_or(FxfsError::NotFound)
112            .context("Volume missing in volume directory")?;
113        match descriptor {
114            ObjectDescriptor::Volume => (),
115            _ => bail!(anyhow!(FxfsError::Inconsistent).context("Expected volume")),
116        }
117        // Lookup the object store corresponding to the volume.
118        let store = self
119            .filesystem
120            .object_manager()
121            .store(store_object_id)
122            .ok_or(FxfsError::NotFound)
123            .context("Missing volume store")?;
124        store.set_trace(self.filesystem.trace());
125        // Unlock the volume if required.
126        if let Some(crypt) = options.crypt {
127            let read_only = self.filesystem.options().read_only;
128            store.unlock_inner(crypt, read_only).await.context("Failed to unlock volume")?;
129        } else if store.is_locked() {
130            bail!(FxfsError::AccessDenied);
131        }
132        // If the layer files are out of date, kick off a full compaction in the background.
133        if let Some(scope_guard) = self.filesystem.scope().active_guard()
134            && !self.filesystem.options().read_only
135            && store.tree().get_earliest_version() != LATEST_VERSION
136        {
137            let keys = lock_keys![LockKey::flush(store_object_id)];
138            let guard = self
139                .filesystem
140                .lock_manager()
141                .write_lock(keys)
142                .await
143                .into_owned(self.filesystem.clone());
144            let store = store.clone();
145            self.filesystem.scope().spawn(async move {
146                // This needs to be taken outside the task and passed in. The volume cannot be
147                // re-locked until the guard is dropped so this prevents a race where the flush
148                // starts after locking by taking it synchronously.
149                let _guard = guard;
150                // Takes a scope guard so that unencrypted stores will finish their flush before
151                // allowing the filesystem to close as well. Journal flushes are normally run on
152                // the filesystem's scope, and just like that case it is safe since encrypted
153                // volumes can't be locked without taking the flush lock so it will be forced to
154                // wait for this completion.
155                let _scope_guard = scope_guard;
156                if let Err(error) =
157                    store.flush_guarded_with_reason(FlushReason::UpgradeVersion).await
158                {
159                    error!(error:?; "Failed background flush");
160                }
161            });
162        }
163        Ok(store)
164    }
165
166    /// Deletes the given volume.  Consumes `transaction` and runs `callback` during commit. The
167    /// caller must have the correct locks for the volumes directory.
168    pub async fn delete_volume(
169        &self,
170        volume_name: &str,
171        mut transaction: Transaction<'_>,
172        callback: impl FnOnce() + Send,
173    ) -> Result<(), Error> {
174        let objects_to_delete = self.delete_volume_impl(volume_name, &mut transaction).await?;
175        transaction.commit_with_callback(|_| callback()).await.context("commit")?;
176        // Tombstone the deleted objects.
177        let root_store_id = self.filesystem.root_store().store_object_id();
178        for object_id in &objects_to_delete {
179            self.filesystem.tombstone_object(root_store_id, *object_id, None).await?;
180        }
181        Ok(())
182    }
183
184    async fn delete_volume_impl(
185        &self,
186        volume_name: &str,
187        transaction: &mut Transaction<'_>,
188    ) -> Result<Vec<u64>, Error> {
189        let object_id =
190            match self.volume_directory().lookup(volume_name).await?.ok_or(FxfsError::NotFound)? {
191                (object_id, ObjectDescriptor::Volume, _) => object_id,
192                _ => bail!(anyhow!(FxfsError::Inconsistent).context("Expected volume")),
193            };
194        let root_store = self.filesystem.root_store();
195
196        // Delete all the layers and encrypted mutations stored in root_store for this volume.
197        // This includes the StoreInfo itself.
198        let mut objects_to_delete = load_store_info(&root_store, object_id).await?.parent_objects();
199        objects_to_delete.push(object_id);
200
201        for object_id in &objects_to_delete {
202            root_store.adjust_refs(transaction, *object_id, -1).await?;
203        }
204        // Mark all volume data as deleted.
205        self.filesystem.allocator().mark_for_deletion(transaction, object_id);
206        // Remove the volume entry from the VolumeDirectory.
207        self.volume_directory().delete_child_volume(transaction, volume_name, object_id)?;
208        Ok(objects_to_delete)
209    }
210
211    /// Adds the required mutations to atomically replace a volume, returning a list of object IDs
212    /// of objects which can be deleted. If `dst` does not exist, this is equivalent to renaming the
213    /// volume from `src` to `dst`. The caller must have the correct locks on the volumes directory.
214    pub(crate) async fn replace_volume(
215        &self,
216        transaction: &mut Transaction<'_>,
217        src: &str,
218        dst: &str,
219    ) -> Result<Option<Vec<u64>>, Error> {
220        let src_object_id = match self.volume_directory().lookup(src).await? {
221            Some((object_id, ObjectDescriptor::Volume, _)) => Ok(object_id),
222            Some(_) => Err(FxfsError::Inconsistent),
223            None => Err(FxfsError::NotFound),
224        }?;
225
226        let replaced_objects = if let Some((_, ObjectDescriptor::Volume, _)) =
227            self.volume_directory().lookup(dst).await?
228        {
229            Some(self.delete_volume_impl(dst, transaction).await?)
230        } else {
231            None
232        };
233
234        transaction.add(
235            self.volume_directory().store().store_object_id(),
236            Mutation::replace_or_insert_object(
237                ObjectKey::child(self.volume_directory().object_id(), src, DirType::Normal),
238                ObjectValue::None,
239            ),
240        );
241
242        transaction.add(
243            self.volume_directory().store().store_object_id(),
244            Mutation::replace_or_insert_object(
245                ObjectKey::child(self.volume_directory().object_id(), dst, DirType::Normal),
246                ObjectValue::Child(ChildValue {
247                    object_id: src_object_id,
248                    object_descriptor: ObjectDescriptor::Volume,
249                }),
250            ),
251        );
252
253        Ok(replaced_objects)
254    }
255
256    /// Attempts to install the image `image_file` in the volume `src` as the volume `dst`. The
257    /// image file should be an fxfs partition image containing a volume matching the name `dst`.
258    /// The contents of the `dst` volume in the image will be installed in-place into this
259    /// filesystem, replacing an existing `dst` volume if one exists.
260    ///
261    /// There can be no other objects in `src` with extent records, and neither `src` nor `dst` can
262    /// be encrypted.
263    pub async fn install_volume(
264        &self,
265        src: &str,
266        image_file: &str,
267        dst: &str,
268    ) -> Result<(), Error> {
269        ObjectStore::install_volume(self, src, image_file, dst).await
270    }
271
272    /// Acquires a transaction with appropriate locks to remove volume |name|.
273    /// Also returns the object ID of the store which will be deleted.
274    pub async fn acquire_transaction_for_remove_volume<'a>(
275        &self,
276        name: &str,
277        extra_keys: impl IntoIterator<Item = LockKey>,
278        allow_not_found: bool,
279        options: Options<'a>,
280    ) -> Result<(u64, Transaction<'a>), Error> {
281        // Since we don't know the store object ID until we've looked it up in the volumes
282        // directory, we need to loop until we have acquired a lock on a store whose ID is the same
283        // as it was in the last iteration.
284        let volume_dir = self.volume_directory();
285        let store = volume_dir.store();
286        let extra_keys = extra_keys.into_iter();
287        let mut lock_keys = Vec::with_capacity(extra_keys.size_hint().1.unwrap_or(2) + 2);
288        lock_keys.extend(extra_keys);
289        lock_keys.push(LockKey::object(store.store_object_id(), volume_dir.object_id()));
290        let orig_len = lock_keys.len();
291        let mut transaction = None;
292        loop {
293            lock_keys.truncate(orig_len);
294            let object_id = match volume_dir.lookup(name).await? {
295                Some((object_id, ObjectDescriptor::Volume, _)) => {
296                    // We have to ensure that the store isn't flushed while we delete it, because
297                    // deleting the store will remove references to it from ObjectManager which are
298                    // then updated by flushing.
299                    lock_keys.push(LockKey::flush(object_id));
300                    object_id
301                }
302                None => {
303                    if allow_not_found {
304                        INVALID_OBJECT_ID
305                    } else {
306                        bail!(FxfsError::NotFound);
307                    }
308                }
309                _ => bail!(anyhow!(FxfsError::Inconsistent).context("Expected volume")),
310            };
311
312            // If the IDs match, return the transaction now.
313            match transaction {
314                Some(result @ (id, _)) if id == object_id => return Ok(result),
315                _ => {}
316            }
317
318            transaction = Some((
319                object_id,
320                store.new_transaction(LockKeys::Vec(lock_keys.clone()), options).await?,
321            ));
322        }
323    }
324}
325
326/// Returns the root volume for the filesystem.
327pub async fn root_volume(filesystem: Arc<FxFilesystem>) -> Result<RootVolume, Error> {
328    let root_store = filesystem.root_store();
329    let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
330        .await
331        .context("Unable to open root volume directory")?;
332    Ok(RootVolume { _root_directory: root_directory, filesystem })
333}
334
335/// Returns the object IDs for all volumes.
336pub async fn list_volumes(volume_directory: &Directory<ObjectStore>) -> Result<Vec<u64>, Error> {
337    let layer_set = volume_directory.store().tree().layer_set();
338    let mut merger = layer_set.merger();
339    let mut iter = volume_directory.iter(&mut merger).await?;
340    let mut object_ids = vec![];
341    while let Some((_, id, _)) = iter.get() {
342        object_ids.push(id);
343        iter.advance().await?;
344    }
345    Ok(object_ids)
346}
347
348#[cfg(test)]
349mod tests {
350    use super::root_volume;
351    use crate::filesystem::{FxFilesystem, SyncOptions};
352    use crate::fsck::{FsckOptions, fsck_volume_with_options, fsck_with_options};
353    use crate::lsm_tree::persistent_layer::PersistentLayerWriter;
354    use crate::lsm_tree::types::LayerWriter as _;
355    use crate::object_handle::{ObjectHandle, WriteObjectHandle};
356    use crate::object_store::directory::Directory;
357    use crate::object_store::transaction::{Options, ReservationOptions, lock_keys};
358    use crate::object_store::{
359        DirectWriter, HandleOptions, LockKey, NewChildStoreOptions, ObjectKey, ObjectStore,
360        ObjectValue, StoreOptions,
361    };
362    use crate::serialized_types::{EARLIEST_SUPPORTED_VERSION, LATEST_VERSION};
363    use fxfs_crypto::{Crypt, EncryptionKey, KeyPurpose};
364    use fxfs_insecure_crypto::new_insecure_crypt;
365    use std::sync::Arc;
366    use storage_device::DeviceHolder;
367    use storage_device::fake_device::FakeDevice;
368    use test_case::test_case;
369
370    async fn do_fsck(
371        fs: &Arc<FxFilesystem>,
372        volume_name: Option<&str>,
373        crypt: Option<Arc<dyn Crypt>>,
374    ) {
375        let fsck_options = FsckOptions {
376            fail_on_warning: true,
377            on_error: Box::new(|err| eprintln!("fsck error: {:?}", err)),
378            ..Default::default()
379        };
380        fsck_with_options(fs.clone(), &fsck_options).await.expect("fsck filesystem");
381        if let Some(volume_name) = volume_name {
382            let root = root_volume(fs.clone()).await.unwrap();
383            let vol = root
384                .volume(
385                    volume_name,
386                    StoreOptions { crypt: crypt.clone(), ..StoreOptions::default() },
387                )
388                .await
389                .expect("could not open volume");
390            fsck_volume_with_options(&fs, &fsck_options, vol.store_object_id(), crypt)
391                .await
392                .expect("fsck volume");
393        }
394    }
395
396    #[fuchsia::test]
397    async fn test_lookup_nonexistent_volume() {
398        let device = DeviceHolder::new(FakeDevice::new(8192, 512));
399        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
400        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
401        root_volume
402            .volume(
403                "vol",
404                StoreOptions {
405                    crypt: Some(Arc::new(new_insecure_crypt())),
406                    ..StoreOptions::default()
407                },
408            )
409            .await
410            .err()
411            .expect("Volume shouldn't exist");
412        filesystem.close().await.expect("Close failed");
413    }
414
415    #[fuchsia::test]
416    async fn test_add_volume() {
417        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
418        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
419        let crypt = Arc::new(new_insecure_crypt());
420        {
421            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
422            let store = root_volume
423                .new_volume(
424                    "vol",
425                    NewChildStoreOptions {
426                        options: StoreOptions {
427                            crypt: Some(crypt.clone()),
428                            ..StoreOptions::default()
429                        },
430                        ..Default::default()
431                    },
432                )
433                .await
434                .expect("new_volume failed");
435            let mut transaction = filesystem
436                .root_store()
437                .new_transaction(
438                    lock_keys![LockKey::object(
439                        store.store_object_id(),
440                        store.root_directory_object_id()
441                    )],
442                    Options::default(),
443                )
444                .await
445                .expect("new transaction failed");
446            let root_directory = Directory::open(&store, store.root_directory_object_id())
447                .await
448                .expect("open failed");
449            root_directory
450                .create_child_file(&mut transaction, "foo")
451                .await
452                .expect("create_child_file failed");
453            transaction.commit().await.expect("commit failed");
454            filesystem.sync(SyncOptions::default()).await.expect("sync failed");
455        };
456        {
457            filesystem.close().await.expect("Close failed");
458            let device = filesystem.take_device().await;
459            device.reopen(false);
460            let filesystem = FxFilesystem::open(device).await.expect("open failed");
461            do_fsck(&filesystem, Some("vol"), Some(crypt)).await;
462            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
463            // NOTE: The volume should have been unlocked by `do_fsck` so we omit `crypt` here.
464            let volume = root_volume
465                .volume("vol", StoreOptions { crypt: None, ..StoreOptions::default() })
466                .await
467                .expect("volume failed");
468            let root_directory = Directory::open(&volume, volume.root_directory_object_id())
469                .await
470                .expect("open failed");
471            root_directory.lookup("foo").await.expect("lookup failed").expect("not found");
472            filesystem.close().await.expect("Close failed");
473        };
474    }
475
476    #[fuchsia::test]
477    async fn test_delete_volume() {
478        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
479        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
480        let crypt = Arc::new(new_insecure_crypt());
481        let store_object_id;
482        let parent_objects;
483        // Add volume and a file (some data).
484        let store_id = {
485            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
486            let store = root_volume
487                .new_volume(
488                    "vol",
489                    NewChildStoreOptions {
490                        options: StoreOptions {
491                            crypt: Some(crypt.clone()),
492                            ..StoreOptions::default()
493                        },
494                        ..Default::default()
495                    },
496                )
497                .await
498                .expect("new_volume failed");
499            store_object_id = store.store_object_id();
500            let mut transaction = filesystem
501                .root_store()
502                .new_transaction(
503                    lock_keys![LockKey::object(store_object_id, store.root_directory_object_id())],
504                    Options::default(),
505                )
506                .await
507                .expect("new transaction failed");
508            let root_directory = Directory::open(&store, store.root_directory_object_id())
509                .await
510                .expect("open failed");
511            let handle = root_directory
512                .create_child_file(&mut transaction, "foo")
513                .await
514                .expect("create_child_file failed");
515            transaction.commit().await.expect("commit failed");
516
517            let mut buf = handle.allocate_buffer(8192).await;
518            buf.fill(0xaa);
519            handle.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
520            store.flush().await.expect("flush failed");
521            filesystem.sync(SyncOptions::default()).await.expect("sync failed");
522            parent_objects = store.parent_objects();
523            // Confirm parent objects exist.
524            for object_id in &parent_objects {
525                let _ = filesystem
526                    .root_store()
527                    .get_file_size(*object_id)
528                    .await
529                    .expect("Layer file missing? Bug in test.");
530            }
531            store.store_object_id()
532        };
533        filesystem.close().await.expect("Close failed");
534        let device = filesystem.take_device().await;
535        device.reopen(false);
536        let filesystem = FxFilesystem::open(device).await.expect("open failed");
537        do_fsck(&filesystem, Some("vol"), Some(crypt.clone())).await;
538        {
539            // Expect 8kiB accounted to the new volume.
540            assert_eq!(
541                filesystem.allocator().get_owner_allocated_bytes().get(&store_object_id),
542                Some(&8192)
543            );
544            let root = root_volume(filesystem.clone()).await.expect("root_volume failed");
545            let transaction = filesystem
546                .root_store()
547                .new_transaction(
548                    lock_keys![
549                        LockKey::object(
550                            root.volume_directory().store().store_object_id(),
551                            root.volume_directory().object_id(),
552                        ),
553                        LockKey::flush(store_id)
554                    ],
555                    Options { reservation: ReservationOptions::Borrowed, ..Default::default() },
556                )
557                .await
558                .expect("new_transaction failed");
559            root.delete_volume("vol", transaction, || {}).await.expect("delete_volume");
560            // Confirm data allocation is gone.
561            assert_eq!(
562                filesystem
563                    .allocator()
564                    .get_owner_allocated_bytes()
565                    .get(&store_object_id)
566                    .unwrap_or(&0),
567                &0,
568            );
569            // Confirm volume entry is gone.
570            root.volume(
571                "vol",
572                StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
573            )
574            .await
575            .err()
576            .expect("volume shouldn't exist anymore.");
577        }
578        filesystem.close().await.expect("Close failed");
579        let device = filesystem.take_device().await;
580        device.reopen(false);
581        // All artifacts of the original volume should be gone.
582        let filesystem = FxFilesystem::open(device).await.expect("open failed");
583        do_fsck(&filesystem, None, None).await;
584        for object_id in &parent_objects {
585            let _ = filesystem
586                .root_store()
587                .get_file_size(*object_id)
588                .await
589                .err()
590                .expect("File wasn't deleted.");
591        }
592        filesystem.close().await.expect("Close failed");
593    }
594
595    #[fuchsia::test]
596    async fn test_replace_volume() {
597        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
598        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
599        // Add volume "vol" with a file "foo".
600        {
601            let root = root_volume(fs.clone()).await.expect("root_volume failed");
602            let store = root.new_volume("vol", NewChildStoreOptions::default()).await.unwrap();
603            let mut transaction = fs
604                .root_store()
605                .new_transaction(
606                    lock_keys![LockKey::object(
607                        store.store_object_id(),
608                        store.root_directory_object_id()
609                    )],
610                    Options::default(),
611                )
612                .await
613                .unwrap();
614            let root_directory =
615                Directory::open(&store, store.root_directory_object_id()).await.unwrap();
616            let _ = root_directory.create_child_file(&mut transaction, "foo").await.unwrap();
617            transaction.commit().await.expect("commit failed");
618        }
619        // Add a second volume "vol2" with a file "foo2".
620        {
621            let root = root_volume(fs.clone()).await.expect("root_volume failed");
622            let store = root
623                .new_volume("vol2", NewChildStoreOptions::default())
624                .await
625                .expect("new_volume failed");
626            let mut transaction = fs
627                .root_store()
628                .new_transaction(
629                    lock_keys![LockKey::object(
630                        store.store_object_id(),
631                        store.root_directory_object_id()
632                    )],
633                    Options::default(),
634                )
635                .await
636                .expect("new transaction failed");
637            let root_directory = Directory::open(&store, store.root_directory_object_id())
638                .await
639                .expect("open failed");
640            let _ = root_directory
641                .create_child_file(&mut transaction, "foo2")
642                .await
643                .expect("create_child_file failed");
644            transaction.commit().await.expect("commit failed");
645        }
646        // Replace "vol" with "vol2", and ensure the filesystem and installed volume passes fsck.
647        {
648            let root = root_volume(fs.clone()).await.expect("root_volume failed");
649            let mut transaction = root
650                .acquire_transaction_for_remove_volume(
651                    "vol",
652                    [],
653                    false,
654                    Options { reservation: ReservationOptions::Borrowed, ..Default::default() },
655                )
656                .await
657                .unwrap()
658                .1;
659            root.replace_volume(&mut transaction, "vol2", "vol").await.unwrap();
660            transaction.commit().await.unwrap();
661            do_fsck(&fs, Some("vol"), None).await;
662        }
663        fs.close().await.expect("Close failed");
664        let device = fs.take_device().await;
665        device.reopen(false);
666        let fs = FxFilesystem::open(device).await.unwrap();
667        do_fsck(&fs, Some("vol"), None).await;
668        {
669            let root = root_volume(fs.clone()).await.unwrap();
670            // vol2 should now have replaced vol
671            root.volume("vol2", StoreOptions::default())
672                .await
673                .err()
674                .expect("vol2 shouldn't exist anymore.");
675            let vol = root.volume("vol", StoreOptions::default()).await.unwrap();
676            let dir = Directory::open(&vol, vol.root_directory_object_id()).await.unwrap();
677            // The contents of "foo" should have been replaced entirely with those from "foo2".
678            assert!(dir.lookup("foo").await.unwrap().is_none(), "foo should not be present");
679            assert!(dir.lookup("foo2").await.unwrap().is_some(), "foo2 should be present");
680        }
681        fs.close().await.unwrap();
682    }
683
684    #[fuchsia::test]
685    async fn test_create_volume_with_guid() {
686        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
687        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
688        let guid = [1u8; 16];
689        {
690            let root = root_volume(fs.clone()).await.expect("root_volume failed");
691            let store = root
692                .new_volume("vol", NewChildStoreOptions { guid: Some(guid), ..Default::default() })
693                .await
694                .unwrap();
695            assert_eq!(store.store_info().unwrap().guid, guid);
696        }
697        fs.close().await.expect("Close failed");
698        let device = fs.take_device().await;
699        device.reopen(false);
700        let fs = FxFilesystem::open(device).await.unwrap();
701        {
702            let root = root_volume(fs.clone()).await.unwrap();
703            let vol = root.volume("vol", StoreOptions::default()).await.unwrap();
704            assert_eq!(vol.guid(), guid);
705        }
706        fs.close().await.unwrap();
707    }
708
709    #[test_case(false; "unencrypted")]
710    #[test_case(true; "encrypted")]
711    #[fuchsia::test]
712    async fn test_flush_on_open_to_update_version(encrypted: bool) {
713        // Can't test it if we don't have any versions that we can migrate between.
714        if LATEST_VERSION == EARLIEST_SUPPORTED_VERSION {
715            return;
716        }
717
718        let device = DeviceHolder::new(FakeDevice::new(8192, 1024));
719        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
720        let crypt: Option<Arc<dyn Crypt>> =
721            if encrypted { Some(Arc::new(new_insecure_crypt())) } else { None };
722        {
723            let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
724            let store = root_vol
725                .new_volume(
726                    "test",
727                    NewChildStoreOptions {
728                        options: StoreOptions { crypt: crypt.clone(), ..StoreOptions::default() },
729                        ..Default::default()
730                    },
731                )
732                .await
733                .expect("new_volume failed");
734
735            let parent_store = store.parent_store().unwrap();
736            let txn_options = Options {
737                skip_journal_checks: true,
738                reservation: ReservationOptions::Borrowed,
739                ..Default::default()
740            };
741            let mut transaction = parent_store
742                .new_transaction(lock_keys![], txn_options)
743                .await
744                .expect("new_transaction failed");
745            let new_layer_object = if let Some(crypt) = &crypt {
746                let raw_id = parent_store.get_next_object_id(&transaction).await.unwrap();
747                let (fxfs_key, unwrapped_key) =
748                    crypt.create_key(raw_id.get(), KeyPurpose::Data).await.unwrap();
749                ObjectStore::create_object_with_key(
750                    &parent_store,
751                    &mut transaction,
752                    raw_id,
753                    HandleOptions { skip_journal_checks: true, ..Default::default() },
754                    EncryptionKey::Fxfs(fxfs_key),
755                    unwrapped_key,
756                )
757                .await
758                .expect("create_object_with_key failed")
759            } else {
760                ObjectStore::create_object(
761                    &parent_store,
762                    &mut transaction,
763                    HandleOptions { skip_journal_checks: true, ..Default::default() },
764                    None,
765                )
766                .await
767                .expect("create_object failed")
768            };
769            transaction.commit().await.expect("commit failed");
770
771            // Add an empty layer to the set with the old version.
772            {
773                let layer_writer = DirectWriter::new(&new_layer_object, txn_options).await;
774                let writer = PersistentLayerWriter::<_, ObjectKey, ObjectValue>::new_with_version(
775                    layer_writer,
776                    0,
777                    fs.block_size(),
778                    EARLIEST_SUPPORTED_VERSION,
779                )
780                .await
781                .expect("writer failed");
782                writer.complete().await.expect("writer complete failed");
783            }
784
785            let mut store_info = store.load_store_info().await.unwrap();
786            store_info.layers = vec![new_layer_object.object_id()];
787            let mut end_transaction = parent_store
788                .new_transaction(
789                    lock_keys![LockKey::object(
790                        parent_store.store_object_id(),
791                        store.store_info_handle_object_id().unwrap()
792                    )],
793                    txn_options,
794                )
795                .await
796                .expect("new_transaction failed");
797            store
798                .write_store_info(&mut end_transaction, &store_info)
799                .await
800                .expect("write_store_info failed");
801            end_transaction.commit().await.expect("commit failed");
802        }
803        fs.close().await.expect("close failed");
804
805        // Re-open filesystem.
806        let device = fs.take_device().await;
807        device.reopen(false);
808        let fs = FxFilesystem::open(device).await.expect("open failed");
809        {
810            let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
811            let _store = root_vol
812                .volume("test", StoreOptions { crypt: crypt.clone(), ..StoreOptions::default() })
813                .await
814                .expect("volume failed");
815        }
816
817        // Closing the filesystem should force the background flush to complete.
818        fs.close().await.expect("close failed");
819
820        // Re-open filesystem again to ensure the layer file version has been updated.
821        let device = fs.take_device().await;
822        device.reopen(false);
823        let fs = FxFilesystem::open(device).await.expect("open failed");
824        {
825            let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
826            let store = root_vol
827                .volume("test", StoreOptions { crypt: crypt.clone(), ..StoreOptions::default() })
828                .await
829                .expect("volume failed");
830
831            assert_eq!(store.tree.get_earliest_version(), LATEST_VERSION);
832        }
833
834        fs.close().await.expect("close failed");
835    }
836
837    #[fuchsia::test]
838    async fn test_delete_volume_borrows_and_returns_metadata_space() {
839        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
840        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
841        let root = root_volume(fs.clone()).await.expect("root_volume failed");
842        let store = root
843            .new_volume("vol", NewChildStoreOptions::default())
844            .await
845            .expect("new_volume failed");
846
847        let dir = Directory::open(&store, store.root_directory_object_id()).await.expect("open");
848        for i in 0..200 {
849            let mut transaction = store
850                .new_transaction(
851                    lock_keys![LockKey::object(store.store_object_id(), dir.object_id())],
852                    Options::default(),
853                )
854                .await
855                .expect("new_transaction failed");
856            dir.create_child_file(&mut transaction, &format!("f_{i}"))
857                .await
858                .expect("create_child_file failed");
859            transaction.commit().await.expect("commit failed");
860        }
861
862        fs.journal().force_compact().await.expect("force_compact failed");
863        fs.journal().pause_compactions().await;
864
865        // Dirty and flush `store` while compactions are paused so its new layer file in
866        // `root_store` consumes space from `metadata_reservation` that has not yet been reconciled
867        // by a full journal compaction.
868        {
869            let mut transaction = store
870                .new_transaction(
871                    lock_keys![LockKey::object(store.store_object_id(), dir.object_id())],
872                    Options::default(),
873                )
874                .await
875                .expect("new_transaction failed");
876            dir.create_child_file(&mut transaction, "extra")
877                .await
878                .expect("create_child_file failed");
879            transaction.commit().await.expect("commit failed");
880        }
881        store.flush().await.expect("flush failed");
882        std::mem::drop(dir);
883        std::mem::drop(store);
884
885        let metadata_before = fs.object_manager().metadata_reservation().amount();
886
887        // Reserve all remaining free space in the allocator so that `delete_volume` must borrow
888        // metadata space rather than reserving new space from the allocator.
889        let mut reservations = Vec::new();
890        let mut chunk = 16 * 1024 * 1024;
891        while chunk >= 4096 {
892            if let Some(r) = fs.allocator().reserve(None, chunk) {
893                reservations.push(r);
894            } else {
895                chunk /= 2;
896            }
897        }
898
899        let (_, transaction) = root
900            .acquire_transaction_for_remove_volume(
901                "vol",
902                [],
903                false,
904                Options { reservation: ReservationOptions::Borrowed, ..Default::default() },
905            )
906            .await
907            .expect("acquire_transaction_for_remove_volume failed");
908        root.delete_volume("vol", transaction, || {}).await.expect("delete_volume failed");
909
910        let metadata_after = fs.object_manager().metadata_reservation().amount();
911        assert!(
912            metadata_after > metadata_before,
913            "delete_volume should return tombstoned root_store layer blocks to \
914             metadata_reservation (before: {metadata_before}, after: {metadata_after})"
915        );
916
917        std::mem::drop(reservations);
918        fs.journal().resume_compactions();
919        fs.close().await.expect("close failed");
920    }
921}