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