1use 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
18pub const VOLUMES_DIRECTORY: &str = "volumes";
26
27pub 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 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 self.filesystem.object_manager().add_store(store.clone());
67
68 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 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 pub async fn volume(
92 &self,
93 volume_name: &str,
94 options: StoreOptions,
95 ) -> Result<Arc<ObjectStore>, Error> {
96 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 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 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 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 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 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 self.filesystem.allocator().mark_for_deletion(transaction, object_id);
169 self.volume_directory().delete_child_volume(transaction, volume_name, object_id)?;
171 Ok(objects_to_delete)
172 }
173
174 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 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 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 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 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 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
293pub 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
302pub 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 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 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 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 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 assert_eq!(
529 filesystem
530 .allocator()
531 .get_owner_allocated_bytes()
532 .get(&store_object_id)
533 .unwrap_or(&0),
534 &0,
535 );
536 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 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 {
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 {
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 {
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 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 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 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 {
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 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 assert_eq!(store.counters.lock().num_flushes, 1);
779
780 fs.close().await.expect("close failed");
781 }
782}