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