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