1use crate::fsck::{FsckOptions, fsck_volume_with_options, fsck_with_options};
6use crate::hooks::HooksHandle;
7use crate::log::*;
8use crate::metrics;
9use crate::object_handle::LayerObject;
10use crate::object_store::allocator::{Allocator, Reservation};
11use crate::object_store::directory::Directory;
12use crate::object_store::graveyard::Graveyard;
13use crate::object_store::journal::super_block::{SuperBlockHeader, SuperBlockInstance};
14use crate::object_store::journal::{self, Journal, JournalCheckpoint, JournalOptions};
15use crate::object_store::object_manager::ObjectManager;
16use crate::object_store::transaction::{
17 self, AssocObj, LockKey, LockKeys, LockManager, Mutation, ObjectMutationIterator, Transaction,
18 TransactionConcurrencyLimiter, TransactionSink, WriteGuard, lock_keys,
19};
20use crate::object_store::volume::{VOLUMES_DIRECTORY, root_volume};
21use crate::object_store::{
22 AttributeId, DataObjectHandle, NewChildStoreOptions, ObjectStore, StoreOptions,
23};
24use crate::range::RangeExt;
25use crate::serialized_types::{LATEST_VERSION, Version};
26use anyhow::{Context, Error, anyhow, bail};
27use async_trait::async_trait;
28use fuchsia_async as fasync;
29use fuchsia_async::condition::Condition;
30use fuchsia_inspect::{Inspector, LazyNode, NumericProperty as _, UintProperty};
31use fuchsia_sync::Mutex;
32use futures::future::BoxFuture;
33use futures::stream::BoxStream;
34use futures::{FutureExt, Stream};
35use fxfs_crypto::{Crypt, UnwrappedKey};
36use fxfs_trace::{TraceFutureExt, trace_future_args};
37use static_assertions::const_assert;
38use std::pin::pin;
39use std::sync::atomic::{AtomicBool, Ordering};
40use std::sync::{Arc, OnceLock, Weak};
41use std::task::Poll;
42use std::time::{Duration, Instant};
43use storage_device::{Device, DeviceHolder};
44use storage_units::BlockSize;
45
46pub const MIN_BLOCK_SIZE: BlockSize = BlockSize::SIZE_4KIB;
47pub const MAX_BLOCK_SIZE: BlockSize = BlockSize::SIZE_64KIB;
48
49pub const MAX_FILE_SIZE: u64 = i64::MAX as u64 - 4095;
54const_assert!(9223372036854771712 == MAX_FILE_SIZE);
55
56use futures::stream::StreamExt;
57
58pub(crate) const MAX_IN_FLIGHT_TRANSACTIONS: u64 = 4;
60
61const TRIM_AFTER_BOOT_TIMER: Duration = Duration::from_secs(60 * 60);
65
66const TRIM_INTERVAL_TIMER: Duration = Duration::from_secs(60 * 60 * 24);
68
69const CLEAN_TRANSFER_BUFFER_INTERVAL: Duration = Duration::from_secs(60);
72
73#[derive(Debug)]
76pub struct WakeLease {
77 #[cfg(target_os = "fuchsia")]
78 _lease: zx::Handle,
79}
80
81#[cfg(target_os = "fuchsia")]
82impl WakeLease {
83 pub fn new<H: TryInto<zx::Handle>>(lease: H) -> Self
84 where
85 H::Error: std::fmt::Debug,
86 {
87 Self { _lease: lease.try_into().expect("lease handle must be valid") }
88 }
89}
90
91pub trait PowerManager: Send + Sync {
92 fn watch_battery(self: Arc<Self>) -> BoxStream<'static, (bool, Option<WakeLease>)>;
94}
95
96#[async_trait]
98pub trait LayerPager: Send + Sync + 'static {
99 async fn open_layer(
100 &self,
101 handle: DataObjectHandle<ObjectStore>,
102 unwrapped_key: Option<UnwrappedKey>,
103 ) -> Result<Arc<dyn LayerObject>, Error>;
104}
105
106pub struct Info {
108 pub total_bytes: u64,
109 pub used_bytes: u64,
110}
111
112pub type PostCommitHook = Option<Box<dyn Fn() -> BoxFuture<'static, ()> + Send + Sync>>;
113
114pub struct Options {
115 pub read_only: bool,
117
118 pub hooks: Arc<HooksHandle>,
120
121 pub post_commit_hook: PostCommitHook,
124
125 pub skip_initial_reap: bool,
128
129 pub trim_config: Option<(Duration, Duration)>,
134
135 pub image_builder_mode: Option<SuperBlockInstance>,
138
139 pub inline_crypto_enabled: bool,
146
147 pub barriers_enabled: bool,
152
153 pub power_manager: Option<Arc<dyn PowerManager>>,
155
156 pub trim_charger_wait: Duration,
158
159 pub allow_type3_blobs: bool,
162}
163
164impl Default for Options {
165 fn default() -> Self {
166 Options {
167 read_only: false,
168 hooks: Arc::<HooksHandle>::default(),
169 post_commit_hook: None,
170 skip_initial_reap: false,
171 trim_config: Some((TRIM_AFTER_BOOT_TIMER, TRIM_INTERVAL_TIMER)),
172 image_builder_mode: None,
173 inline_crypto_enabled: false,
174 barriers_enabled: false,
175 power_manager: None,
176 trim_charger_wait: Duration::from_secs(10),
177 allow_type3_blobs: false,
178 }
179 }
180}
181
182pub struct ApplyContext<'a, 'b> {
184 pub mode: ApplyMode<'a, 'b>,
186
187 pub checkpoint: JournalCheckpoint,
189}
190
191pub enum ApplyMode<'a, 'b> {
194 Replay,
195 Live(&'a Transaction<'b>),
196}
197
198impl ApplyMode<'_, '_> {
199 pub fn is_replay(&self) -> bool {
200 matches!(self, ApplyMode::Replay)
201 }
202
203 pub fn is_live(&self) -> bool {
204 matches!(self, ApplyMode::Live(_))
205 }
206}
207
208#[derive(Copy, Clone, Debug, PartialEq, Eq)]
209pub enum ForceMajor {
210 True,
211 False,
212}
213
214#[derive(Copy, Clone, Debug, PartialEq, Eq)]
215pub enum FlushReason {
216 Journal(ForceMajor),
218
219 EncryptedMutations,
221
222 UpgradeVersion,
224}
225
226#[async_trait]
229pub trait JournalingObject: Send + Sync {
230 fn apply_mutation(
234 &self,
235 mutation: Mutation,
236 context: &ApplyContext<'_, '_>,
237 assoc_obj: AssocObj<'_>,
238 ) -> Result<(), Error>;
239
240 fn drop_mutation(&self, mutation: Mutation, transaction: &Transaction<'_>);
242
243 async fn prepare_commit<'a>(
246 &self,
247 _filesystem: &'a FxFilesystem,
248 _transaction: &Transaction<'_>,
249 ) -> Result<Option<WriteGuard<'a>>, Error> {
250 Ok(None)
251 }
252
253 async fn flush(&self, reason: FlushReason) -> Result<Version, Error>;
257
258 fn write_mutations(
261 &self,
262 mutations: ObjectMutationIterator<'_, '_>,
263 mut writer: journal::Writer<'_>,
264 ) {
265 for mutation in mutations {
266 writer.write(mutation.clone());
267 }
268 }
269}
270
271#[derive(Default)]
272pub struct SyncOptions<'a> {
273 pub flush_device: bool,
280
281 pub precondition: Option<Box<dyn FnOnce() -> bool + 'a + Send>>,
284}
285
286pub struct OpenFxFilesystem(Arc<FxFilesystem>);
287
288impl OpenFxFilesystem {
289 pub async fn take_device(self) -> DeviceHolder {
292 let fut = self.device.take_when_dropped();
293 std::mem::drop(self);
294 debug_assert_not_too_long!(fut)
295 }
296}
297
298impl From<Arc<FxFilesystem>> for OpenFxFilesystem {
299 fn from(fs: Arc<FxFilesystem>) -> Self {
300 Self(fs)
301 }
302}
303
304impl Drop for OpenFxFilesystem {
305 fn drop(&mut self) {
306 if self.options.image_builder_mode.is_some()
307 && self.journal().image_builder_mode().is_some()
308 {
309 error!("OpenFxFilesystem in image_builder_mode dropped without calling close().");
310 }
311 if !self.options.read_only && !self.closed.load(Ordering::SeqCst) {
312 error!("OpenFxFilesystem dropped without first being closed. Data loss may occur.");
313 }
314 }
315}
316
317impl std::ops::Deref for OpenFxFilesystem {
318 type Target = Arc<FxFilesystem>;
319
320 fn deref(&self) -> &Self::Target {
321 &self.0
322 }
323}
324
325pub struct FxFilesystemBuilder {
326 format: bool,
327 trace: bool,
328 options: Options,
329 journal_options: JournalOptions,
330 on_new_allocator: Option<Box<dyn Fn(Arc<Allocator>) + Send + Sync>>,
331 on_new_store: Option<Box<dyn Fn(&ObjectStore) + Send + Sync>>,
332 fsck_after_every_transaction: bool,
333 layer_pager: Option<Arc<dyn LayerPager>>,
334}
335
336impl FxFilesystemBuilder {
337 pub fn new() -> Self {
338 Self {
339 format: false,
340 trace: false,
341 options: Options::default(),
342 journal_options: JournalOptions::default(),
343 on_new_allocator: None,
344 on_new_store: None,
345 fsck_after_every_transaction: false,
346 layer_pager: None,
347 }
348 }
349
350 pub fn format(mut self, format: bool) -> Self {
352 self.format = format;
353 self
354 }
355
356 pub fn trace(mut self, trace: bool) -> Self {
358 self.trace = trace;
359 self
360 }
361
362 pub fn read_only(mut self, read_only: bool) -> Self {
365 self.options.read_only = read_only;
366 self
367 }
368
369 pub fn allow_type3_blobs(mut self, allow: bool) -> Self {
371 self.options.allow_type3_blobs = allow;
372 self
373 }
374
375 pub fn image_builder_mode(mut self, mode: Option<SuperBlockInstance>) -> Self {
382 self.options.image_builder_mode = mode;
383 self
384 }
385
386 pub fn post_commit_hook(
389 mut self,
390 hook: impl Fn() -> futures::future::BoxFuture<'static, ()> + Send + Sync + 'static,
391 ) -> Self {
392 self.options.post_commit_hook = Some(Box::new(hook));
393 self
394 }
395
396 pub fn skip_initial_reap(mut self, skip_initial_reap: bool) -> Self {
399 self.options.skip_initial_reap = skip_initial_reap;
400 self
401 }
402
403 pub fn journal_options(mut self, journal_options: JournalOptions) -> Self {
405 self.journal_options = journal_options;
406 self
407 }
408
409 pub fn on_new_allocator(
411 mut self,
412 on_new_allocator: impl Fn(Arc<Allocator>) + Send + Sync + 'static,
413 ) -> Self {
414 self.on_new_allocator = Some(Box::new(on_new_allocator));
415 self
416 }
417
418 pub fn on_new_store(
420 mut self,
421 on_new_store: impl Fn(&ObjectStore) + Send + Sync + 'static,
422 ) -> Self {
423 self.on_new_store = Some(Box::new(on_new_store));
424 self
425 }
426
427 pub fn fsck_after_every_transaction(mut self, fsck_after_every_transaction: bool) -> Self {
429 self.fsck_after_every_transaction = fsck_after_every_transaction;
430 self
431 }
432
433 pub fn trim_config(mut self, delay_and_interval: Option<(Duration, Duration)>) -> Self {
434 self.options.trim_config = delay_and_interval;
435 self
436 }
437
438 pub fn power_manager(mut self, power_manager: Arc<dyn PowerManager>) -> Self {
439 self.options.power_manager = Some(power_manager);
440 self
441 }
442
443 pub fn trim_charger_wait(mut self, wait: Duration) -> Self {
444 self.options.trim_charger_wait = wait;
445 self
446 }
447
448 pub fn inline_crypto_enabled(mut self, inline_crypto_enabled: bool) -> Self {
450 self.options.inline_crypto_enabled = inline_crypto_enabled;
451 self
452 }
453
454 pub fn barriers_enabled(mut self, barriers_enabled: bool) -> Self {
457 self.options.barriers_enabled = barriers_enabled;
458 self.journal_options.barriers_enabled = barriers_enabled;
459 self
460 }
461
462 pub fn hooks(mut self, hooks: Arc<crate::hooks::HooksHandle>) -> Self {
463 self.options.hooks = hooks;
464 self
465 }
466
467 pub fn layer_pager(mut self, layer_pager: Option<Arc<dyn LayerPager>>) -> Self {
468 self.layer_pager = layer_pager;
469 self
470 }
471
472 pub async fn open(self, device: DeviceHolder) -> Result<OpenFxFilesystem, Error> {
474 let read_only = self.options.read_only;
475 if self.format && read_only {
476 bail!("Cannot initialize a filesystem as read-only");
477 }
478
479 if self.options.inline_crypto_enabled && !self.options.barriers_enabled {
481 bail!("A filesystem using inline encryption requires barriers");
482 }
483
484 let objects = Arc::new(ObjectManager::new(self.on_new_store));
485 let journal = Arc::new(Journal::new(
486 objects.clone(),
487 self.journal_options,
488 self.options.hooks.clone(),
489 ));
490
491 let image_builder_mode = self.options.image_builder_mode;
492
493 let device_block_size =
494 BlockSize::new(device.block_size()).expect("Device block size is not a power of 2");
495 let block_size = std::cmp::max(device_block_size, MIN_BLOCK_SIZE);
496 assert!(block_size <= MAX_BLOCK_SIZE, "Max supported block size is 64KiB");
497
498 let mut fsck_after_every_transaction = None;
499 let mut filesystem_options = self.options;
500 if self.fsck_after_every_transaction {
501 let instance =
502 FsckAfterEveryTransaction::new(filesystem_options.post_commit_hook.take());
503 fsck_after_every_transaction = Some(instance.clone());
504 filesystem_options.post_commit_hook =
505 Some(Box::new(move || Box::pin(instance.clone().run())));
506 }
507
508 if !read_only && !self.format {
509 device.flush().await.context("Device flush failed")?;
512 }
513
514 let filesystem = Arc::new_cyclic(|weak: &Weak<FxFilesystem>| {
515 let weak = weak.clone();
516 FxFilesystem {
517 device,
518 block_size,
519 objects: objects.clone(),
520 journal,
521 commit_mutex: futures::lock::Mutex::new(()),
522 lock_manager: LockManager::default(),
523 flush_task: Mutex::new(None),
524 background_tasks: fasync::Scope::new(),
525 closed: AtomicBool::new(true),
526 trace: self.trace,
527 graveyard: Graveyard::new(weak.clone()),
528 completed_transactions: metrics::detail().create_uint("completed_transactions", 0),
529 options: filesystem_options,
530 transaction_limiter: TransactionConcurrencyLimiter::new(MAX_IN_FLIGHT_TRANSACTIONS),
531 layer_pager: self.layer_pager,
532 _stores_node: metrics::register_fs(move || {
533 let weak = weak.clone();
534 Box::pin(async move {
535 if let Some(fs) = weak.upgrade() {
536 fs.populate_stores_node().await
537 } else {
538 Err(anyhow!("Filesystem has been dropped"))
539 }
540 })
541 }),
542 }
543 });
544
545 filesystem.journal().set_image_builder_mode(image_builder_mode);
546
547 filesystem.journal.set_trace(self.trace);
548 if self.format {
549 filesystem.journal.init_empty(filesystem.clone()).await?;
550 if image_builder_mode.is_none() {
551 filesystem.journal.init_superblocks().await?;
554
555 filesystem.graveyard.clone().reap_async();
557 }
558
559 let root_store = filesystem.root_store();
561 root_store.set_trace(self.trace);
562 let root_directory =
563 Directory::open(&root_store, root_store.root_directory_object_id())
564 .await
565 .context("Unable to open root volume directory")?;
566 let mut transaction = root_store
567 .new_transaction(
568 lock_keys![LockKey::object(
569 root_store.store_object_id(),
570 root_directory.object_id()
571 )],
572 transaction::Options::default(),
573 )
574 .await?;
575 let volume_directory =
576 root_directory.create_child_dir(&mut transaction, VOLUMES_DIRECTORY).await?;
577 transaction.commit().await?;
578 objects.set_volume_directory(volume_directory);
579 } else {
580 filesystem
581 .journal
582 .replay(filesystem.clone(), self.on_new_allocator)
583 .await
584 .context("Journal replay failed")?;
585 filesystem.root_store().set_trace(self.trace);
586
587 if !read_only {
588 for store in objects.unlocked_stores() {
592 filesystem.graveyard.initial_reap(&store).await?;
593 }
594 }
595 }
596
597 if let Some(fsck_after_every_transaction) = fsck_after_every_transaction {
599 fsck_after_every_transaction
600 .fs
601 .set(Arc::downgrade(&filesystem))
602 .unwrap_or_else(|_| unreachable!());
603 }
604
605 filesystem.closed.store(false, Ordering::SeqCst);
606
607 if !read_only && image_builder_mode.is_none() {
608 filesystem.graveyard.clone().reap_async();
610
611 if filesystem.options.trim_config.is_some() {
612 filesystem.start_trim_task();
613 }
614 filesystem.start_clean_transfer_buffer_task();
615 }
616
617 Ok(filesystem.into())
618 }
619}
620
621pub struct FxFilesystem {
622 block_size: BlockSize,
623 objects: Arc<ObjectManager>,
624 journal: Arc<Journal>,
625 commit_mutex: futures::lock::Mutex<()>,
626 lock_manager: LockManager,
627 flush_task: Mutex<Option<fasync::Task<()>>>,
628 background_tasks: fasync::Scope,
629 closed: AtomicBool,
630 trace: bool,
632 graveyard: Arc<Graveyard>,
633 completed_transactions: UintProperty,
634 options: Options,
635
636 transaction_limiter: TransactionConcurrencyLimiter,
637
638 _stores_node: LazyNode,
640
641 layer_pager: Option<Arc<dyn LayerPager>>,
642
643 device: DeviceHolder,
646}
647
648#[fxfs_trace::trace]
649impl FxFilesystem {
650 pub async fn new_empty(device: DeviceHolder) -> Result<OpenFxFilesystem, Error> {
651 FxFilesystemBuilder::new().format(true).open(device).await
652 }
653
654 pub async fn open(device: DeviceHolder) -> Result<OpenFxFilesystem, Error> {
655 FxFilesystemBuilder::new().open(device).await
656 }
657
658 pub fn root_parent_store(&self) -> Arc<ObjectStore> {
659 self.objects.root_parent_store()
660 }
661
662 pub fn layer_pager(&self) -> Option<&Arc<dyn LayerPager>> {
663 self.layer_pager.as_ref()
664 }
665
666 pub async fn close(&self) -> Result<(), Error> {
667 if self.journal().image_builder_mode().is_some() {
668 self.journal().finalize_image_builder().await?;
669 }
670 assert_eq!(self.closed.swap(true, Ordering::SeqCst), false);
671 debug_assert_not_too_long!(self.graveyard.wait_for_reap());
672 debug_assert_not_too_long!(self.background_tasks.clone().cancel());
673 self.journal.stop_compactions().await;
674 let sync_status =
675 if self.journal().image_builder_mode().is_some() || self.options().read_only {
676 Ok(None)
677 } else {
678 self.journal.sync(SyncOptions { flush_device: true, ..Default::default() }).await
679 };
680 match &sync_status {
681 Ok(None) => {}
682 Ok(checkpoint) => info!(
683 "Filesystem closed (checkpoint={}, metadata_reservation={:?}, \
684 reservation_required={}, borrowed={})",
685 checkpoint.as_ref().unwrap().file_offset,
686 self.object_manager().metadata_reservation(),
687 self.object_manager().required_reservation(),
688 self.object_manager().borrowed_metadata_space(),
689 ),
690 Err(e) => error!(error:? = e; "Failed to sync filesystem; data may be lost"),
691 }
692 self.journal.terminate();
693 let flush_task = self.flush_task.lock().take();
694 if let Some(task) = flush_task {
695 debug_assert_not_too_long!(task);
696 }
697 self.device().close().await.context("Failed to close device")?;
700 sync_status.map(|_| ())
701 }
702
703 pub fn device(&self) -> Arc<dyn Device> {
704 Arc::clone(&self.device)
705 }
706
707 pub fn root_store(&self) -> Arc<ObjectStore> {
708 self.objects.root_store()
709 }
710
711 pub fn allocator(&self) -> Arc<Allocator> {
712 self.objects.allocator()
713 }
714
715 pub fn enable_allocations(&self) {
719 self.allocator().enable_allocations();
720 }
721
722 pub fn object_manager(&self) -> &Arc<ObjectManager> {
723 &self.objects
724 }
725
726 pub fn journal(&self) -> &Arc<Journal> {
727 &self.journal
728 }
729
730 pub async fn sync(&self, options: SyncOptions<'_>) -> Result<(), Error> {
731 self.journal.sync(options).await.map(|_| ())
732 }
733
734 pub fn block_size(&self) -> BlockSize {
735 self.block_size
736 }
737
738 pub fn get_info(&self) -> Info {
739 Info {
740 total_bytes: self.device.size(),
741 used_bytes: self.object_manager().allocator().get_used_bytes().0,
742 }
743 }
744
745 pub fn super_block_header(&self) -> SuperBlockHeader {
746 self.journal.super_block_header()
747 }
748
749 pub fn graveyard(&self) -> &Arc<Graveyard> {
750 &self.graveyard
751 }
752
753 pub fn trace(&self) -> bool {
754 self.trace
755 }
756
757 pub fn options(&self) -> &Options {
758 &self.options
759 }
760
761 pub fn scope(&self) -> &fasync::Scope {
762 &self.background_tasks
763 }
764
765 pub async fn lock_commits(&self) -> futures::lock::MutexGuard<'_, ()> {
772 self.commit_mutex.lock().await
773 }
774
775 #[trace]
776 pub async fn commit_transaction<R: Send>(
777 &self,
778 transaction: &mut Transaction<'_>,
779 callback: impl FnOnce(u64) -> R + Send,
780 ) -> Result<R, Error> {
781 self.hooks().on_pre_commit(transaction)?;
782 debug_assert_not_too_long!(self.lock_manager.commit_prepare(&transaction));
783
784 let mut guards = Vec::new();
789 let mut last_object_id = 0;
790 for mutation in transaction.mutations() {
791 let object_id = mutation.object_id;
792
793 if matches!(mutation.mutation, Mutation::BeginFlush | Mutation::EndFlush(_)) {
795 continue;
796 }
797
798 if object_id == last_object_id {
799 continue;
800 }
801 assert!(object_id > last_object_id);
802 last_object_id = object_id;
803
804 if let Some(obj) = self.object_manager().journaling_object(object_id) {
805 if let Some(guard) = obj.prepare_commit(self, transaction).await? {
806 guards.push(guard);
807 }
808 }
809 }
810
811 self.maybe_start_flush_task();
812
813 self.hooks().on_before_commit();
814
815 let _guard = debug_assert_not_too_long!(self.commit_mutex.lock());
816 let journal_offset = if self.journal().image_builder_mode().is_some() {
817 let journal_checkpoint =
818 JournalCheckpoint { file_offset: 0, checksum: 0, version: LATEST_VERSION };
819 self.object_manager()
820 .apply_transaction(transaction, &journal_checkpoint)
821 .expect("Transactions must not fail in image_builder_mode");
822 self.object_manager().did_commit_transaction(transaction, 0, &[]);
823 0
824 } else {
825 self.journal.commit(transaction).await?
826 };
827
828 std::mem::drop(guards);
829 self.completed_transactions.add(1);
830
831 let result = callback(journal_offset);
836
837 if let Some(hook) = self.options.post_commit_hook.as_ref() {
838 hook().await;
839 }
840
841 Ok(result)
842 }
843
844 pub fn lock_manager(&self) -> &LockManager {
845 &self.lock_manager
846 }
847
848 pub fn hooks(&self) -> &Arc<HooksHandle> {
849 &self.options.hooks
850 }
851
852 pub(crate) fn drop_transaction(&self, transaction: &mut Transaction<'_>) {
853 self.objects.drop_transaction(transaction);
854 self.lock_manager.drop_transaction(transaction);
855 }
856}
857
858#[async_trait]
859pub trait Filesystem: TransactionSink + Send + Sync + 'static {
860 fn is_remote(&self) -> bool {
863 true
864 }
865
866 fn ensure_local(&self) -> &FxFilesystem {
870 panic!("not a local filesystem");
871 }
872
873 fn layer_pager(&self) -> Option<&Arc<dyn LayerPager>> {
875 None
876 }
877
878 fn queue_tombstone_object(&self, store_object_id: u64, object_id: u64);
880
881 async fn tombstone_object(
883 &self,
884 store_object_id: u64,
885 object_id: u64,
886 truncate_guard: Option<&TruncateGuard<'_>>,
887 ) -> Result<(), Error>;
888
889 fn options(&self) -> &Options;
891
892 fn block_size(&self) -> BlockSize;
894
895 fn get_info(&self) -> Info;
897
898 async fn sync(&self, options: SyncOptions<'_>) -> Result<(), Error>;
900
901 async fn truncate_guard(&self, store_id: u64, object_id: u64) -> TruncateGuard<'_> {
903 let keys = lock_keys![LockKey::truncate(store_id, object_id)];
904 TruncateGuard(self.lock_manager().write_lock(keys).await)
905 }
906
907 fn hooks(&self) -> &Arc<HooksHandle> {
909 &self.options().hooks
910 }
911}
912
913impl dyn Filesystem {
914 pub async fn truncate_guard_owned(
915 self: &Arc<Self>,
916 store_id: u64,
917 object_id: u64,
918 ) -> TruncateGuard<'static> {
919 self.truncate_guard(store_id, object_id).await.into_owned(self.clone())
920 }
921}
922
923#[async_trait]
924impl TransactionSink for FxFilesystem {
925 async fn new_transaction<'a>(
926 self: Arc<Self>,
927 txn_locks: LockKeys,
928 options: transaction::Options<'a>,
929 ) -> Result<Transaction<'a>, Error> {
930 Transaction::new(self, options, txn_locks).await
931 }
932
933 async fn check_space(&self) -> Result<(), Error> {
934 if self.options.image_builder_mode.is_none() {
935 self.maybe_start_flush_task();
936 self.journal.check_journal_space().await?;
937 }
938 Ok(())
939 }
940
941 fn reserve(&self, owner_object_id: Option<u64>, amount: u64) -> Option<Reservation> {
942 self.allocator().reserve(owner_object_id, amount)
943 }
944
945 fn image_builder_mode(&self) -> bool {
946 self.options.image_builder_mode.is_some()
947 }
948
949 async fn commit(&self, transaction: &mut Transaction<'_>) -> Result<u64, Error> {
950 self.commit_transaction(transaction, |x| x).await
951 }
952
953 async fn commit_with_callback<'a>(
954 &self,
955 transaction: &mut Transaction<'_>,
956 callback: Box<dyn FnOnce(u64) + Send + 'a>,
957 ) -> Result<u64, Error> {
958 let mut journal_offset = 0;
959 self.commit_transaction(transaction, |offset| {
960 journal_offset = offset;
961 callback(offset);
962 })
963 .await?;
964 Ok(journal_offset)
965 }
966
967 fn downgrade_locks(&self, lock_keys: &LockKeys) {
968 self.lock_manager().downgrade_locks(lock_keys);
969 }
970
971 fn drop_transaction(&self, transaction: &mut Transaction<'_>) {
972 FxFilesystem::drop_transaction(self, transaction);
973 }
974
975 fn lock_manager(&self) -> &LockManager {
976 self.lock_manager()
977 }
978
979 fn transaction_limiter(&self) -> &TransactionConcurrencyLimiter {
980 &self.transaction_limiter
981 }
982
983 fn metadata_reservation(&self) -> Option<&Reservation> {
984 Some(self.object_manager().metadata_reservation())
985 }
986
987 fn is_system_store(&self, store_id: u64) -> bool {
988 self.object_manager().is_system_store(store_id)
989 }
990}
991
992#[async_trait]
993impl Filesystem for FxFilesystem {
994 fn is_remote(&self) -> bool {
995 false
996 }
997
998 fn ensure_local(&self) -> &FxFilesystem {
999 self
1000 }
1001
1002 fn layer_pager(&self) -> Option<&Arc<dyn LayerPager>> {
1003 self.layer_pager()
1004 }
1005
1006 fn queue_tombstone_object(&self, store_object_id: u64, object_id: u64) {
1007 self.graveyard().queue_tombstone_object(store_object_id, object_id);
1008 }
1009
1010 async fn tombstone_object(
1011 &self,
1012 store_object_id: u64,
1013 object_id: u64,
1014 truncate_guard: Option<&TruncateGuard<'_>>,
1015 ) -> Result<(), Error> {
1016 FxFilesystem::tombstone_object(self, store_object_id, object_id, truncate_guard).await
1017 }
1018
1019 fn block_size(&self) -> BlockSize {
1020 self.block_size()
1021 }
1022
1023 fn get_info(&self) -> Info {
1024 FxFilesystem::get_info(self)
1025 }
1026
1027 async fn sync(&self, options: SyncOptions<'_>) -> Result<(), Error> {
1028 FxFilesystem::sync(self, options).await
1029 }
1030
1031 fn options(&self) -> &Options {
1032 &self.options
1033 }
1034}
1035
1036impl FxFilesystem {
1037 fn maybe_start_flush_task(&self) {
1038 if self.journal.image_builder_mode().is_some() {
1039 return;
1040 }
1041 let mut flush_task = self.flush_task.lock();
1042 if flush_task.is_none() {
1043 let journal = self.journal.clone();
1044 *flush_task = Some(fasync::Task::spawn(
1045 journal.flush_task().trace(trace_future_args!("Journal::flush_task")),
1046 ));
1047 }
1048 }
1049
1050 fn start_trim_task(self: &Arc<Self>) {
1051 if !self.device.supports_trim() {
1052 info!("Device does not support trim; not scheduling trimming");
1053 return;
1054 }
1055 let this = self.clone();
1056 self.background_tasks
1057 .spawn(this.trim_task().trace(trace_future_args!("Filesystem::trim_task")));
1058 }
1059
1060 async fn trim_task(self: Arc<Self>) {
1061 let Some((mut next_timer, _)) = self.options.trim_config else { return };
1063 loop {
1064 fasync::Timer::new(next_timer.clone()).await;
1065
1066 let start = Instant::now();
1069 let result = if let Some(pm) = &self.options.power_manager {
1070 let mut watcher = pm.clone().watch_battery();
1071
1072 let pauser = Pauser::new(self.options.trim_charger_wait);
1074
1075 let mut pause_future = pin!(
1076 async {
1077 let mut wake_lease: Option<WakeLease> = None;
1078 loop {
1079 let Some((using_battery, new_lease)) = watcher.next_latest().await
1080 else {
1081 pauser.set_pause(false);
1084 drop(wake_lease); return;
1086 };
1087
1088 pauser.set_pause(using_battery);
1090
1091 if using_battery {
1094 wake_lease = None;
1095 } else if new_lease.is_some() {
1096 wake_lease = new_lease;
1097 }
1098 }
1099 }
1100 .fuse()
1101 );
1102
1103 let mut do_trim = pin!(self.do_trim(Some(&pauser)).fuse());
1104
1105 loop {
1106 futures::select! {
1107 _ = pause_future => {}
1108 result = do_trim => break result,
1109 }
1110 }
1111
1112 } else {
1115 self.do_trim(None).await
1116 };
1117
1118 let duration = start.elapsed();
1119 match result {
1120 Ok(bytes_trimmed) => info!(
1121 "Trimmed {bytes_trimmed} bytes in {duration:?}. Next trim in \
1122 {next_timer:?}",
1123 ),
1124 Err(error) => error!(error:?; "Failed to trim"),
1125 }
1126
1127 let Some((_, interval)) = self.options.trim_config else { return };
1128 next_timer = interval;
1129 if next_timer.is_zero() {
1130 fasync::yield_now().await;
1131 }
1132 }
1133 }
1134
1135 async fn do_trim(&self, pauser: Option<&Pauser>) -> Result<usize, Error> {
1137 const MAX_EXTENTS_PER_BATCH: usize = 8;
1138 const MAX_EXTENT_SIZE: usize = 256 * 1024;
1139 let mut offset = 0;
1140 let mut bytes_trimmed = 0;
1141 loop {
1142 let allocator = self.allocator();
1143 if let Some(pauser) = pauser {
1144 pauser.maybe_pause().await;
1145 }
1146 let trimmable_extents =
1147 allocator.take_for_trimming(offset, MAX_EXTENT_SIZE, MAX_EXTENTS_PER_BATCH).await?;
1148 for device_range in trimmable_extents.extents() {
1149 self.device.trim(device_range.clone()).await?;
1150 bytes_trimmed += device_range.length()? as usize;
1151 }
1152 if let Some(device_range) = trimmable_extents.extents().last() {
1153 offset = device_range.end;
1154 } else {
1155 break;
1156 }
1157 }
1158 Ok(bytes_trimmed)
1159 }
1160
1161 fn start_clean_transfer_buffer_task(self: &Arc<Self>) {
1162 let this = self.clone();
1163 self.background_tasks.spawn(
1164 async move {
1165 loop {
1166 fasync::Timer::new(CLEAN_TRANSFER_BUFFER_INTERVAL).await;
1167 this.device().clean_transfer_buffer();
1168 }
1169 }
1170 .trace(trace_future_args!("Filesystem::clean_transfer_buffer_task")),
1171 );
1172 }
1173
1174 pub async fn truncate_guard(&self, store_id: u64, object_id: u64) -> TruncateGuard<'_> {
1175 let keys = lock_keys![LockKey::truncate(store_id, object_id,)];
1176 TruncateGuard(self.lock_manager().write_lock(keys).await)
1177 }
1178
1179 pub async fn truncate_guard_owned(
1180 self: &Arc<Self>,
1181 store_id: u64,
1182 object_id: u64,
1183 ) -> TruncateGuard<'static> {
1184 self.truncate_guard(store_id, object_id).await.into_owned(self.clone())
1185 }
1186
1187 pub async fn tombstone_object(
1197 &self,
1198 store_id: u64,
1199 object_id: u64,
1200 truncate_guard: Option<&TruncateGuard<'_>>,
1201 ) -> Result<(), Error> {
1202 let store = self
1203 .objects
1204 .store(store_id)
1205 .with_context(|| format!("Failed to get store {}", store_id))?;
1206 store
1207 .tombstone_object(
1208 object_id,
1209 transaction::Options {
1210 reservation: transaction::ReservationOptions::Borrowed,
1211 ..Default::default()
1212 },
1213 truncate_guard,
1214 )
1215 .await
1216 }
1217
1218 pub async fn tombstone_attribute(
1225 &self,
1226 store_id: u64,
1227 object_id: u64,
1228 attribute_id: AttributeId,
1229 ) -> Result<(), Error> {
1230 let store = self
1231 .objects
1232 .store(store_id)
1233 .with_context(|| format!("Failed to get store {}", store_id))?;
1234 store
1235 .tombstone_attribute(
1236 object_id,
1237 attribute_id,
1238 transaction::Options {
1239 reservation: transaction::ReservationOptions::Borrowed,
1240 ..Default::default()
1241 },
1242 )
1243 .await
1244 }
1245
1246 async fn populate_stores_node(&self) -> Result<Inspector, Error> {
1247 let inspector = fuchsia_inspect::Inspector::default();
1248 let root = inspector.root();
1249 root.record_child("__root", |n| self.root_store().record_data(n));
1250 root.record_child("__root_parent", |n| self.root_parent_store().record_data(n));
1251 let object_manager = self.object_manager();
1252 let volume_directory = object_manager.volume_directory();
1253 let layer_set = volume_directory.store().tree().layer_set();
1254 let mut merger = layer_set.merger();
1255 let mut iter = volume_directory.iter(&mut merger).await?;
1256 while let Some((name, id, _)) = iter.get() {
1257 if let Some(store) = object_manager.store(id) {
1258 root.record_child(name.to_string(), |n| store.record_data(n));
1259 }
1260 iter.advance().await?;
1261 }
1262 Ok(inspector)
1263 }
1264}
1265
1266#[allow(dead_code)]
1268pub struct TruncateGuard<'a>(WriteGuard<'a>);
1269
1270impl<'a> TruncateGuard<'a> {
1271 pub fn into_owned(self, fs: Arc<dyn Filesystem>) -> TruncateGuard<'static> {
1272 TruncateGuard(self.0.into_owned(fs))
1273 }
1274}
1275
1276pub async fn mkfs(device: DeviceHolder) -> Result<DeviceHolder, Error> {
1278 let fs = FxFilesystem::new_empty(device).await?;
1279 fs.close().await?;
1280 Ok(fs.take_device().await)
1281}
1282
1283pub async fn mkfs_with_volume(
1287 device: DeviceHolder,
1288 volume_name: &str,
1289 crypt: Option<Arc<dyn Crypt>>,
1290) -> Result<DeviceHolder, Error> {
1291 let fs = FxFilesystem::new_empty(device).await?;
1292 {
1293 let root_volume = root_volume(fs.clone()).await.expect("Open root_volume failed");
1296 root_volume
1297 .new_volume(
1298 volume_name,
1299 NewChildStoreOptions {
1300 options: StoreOptions { crypt, ..StoreOptions::default() },
1301 ..Default::default()
1302 },
1303 )
1304 .await
1305 .expect("Create volume failed");
1306 }
1307 fs.close().await?;
1308 Ok(fs.take_device().await)
1309}
1310
1311struct FsckAfterEveryTransaction {
1312 fs: OnceLock<Weak<FxFilesystem>>,
1313 old_hook: PostCommitHook,
1314}
1315
1316impl FsckAfterEveryTransaction {
1317 fn new(old_hook: PostCommitHook) -> Arc<Self> {
1318 Arc::new(Self { fs: OnceLock::new(), old_hook })
1319 }
1320
1321 async fn run(self: Arc<Self>) {
1322 if let Some(fs) = self.fs.get().and_then(Weak::upgrade) {
1323 let options = FsckOptions {
1324 fail_on_warning: true,
1325 no_lock: true,
1326 quiet: true,
1327 ..Default::default()
1328 };
1329 fsck_with_options(fs.clone(), &options).await.expect("fsck failed");
1330 let object_manager = fs.object_manager();
1331 for store in object_manager.unlocked_stores() {
1332 let store_id = store.store_object_id();
1333 if !object_manager.is_system_store(store_id) {
1334 fsck_volume_with_options(fs.as_ref(), &options, store_id, None)
1335 .await
1336 .expect("fsck_volume_with_options failed");
1337 }
1338 }
1339 }
1340 if let Some(old_hook) = self.old_hook.as_ref() {
1341 old_hook().await;
1342 }
1343 }
1344}
1345
1346struct Pauser {
1347 pause: Condition<bool>,
1348 bounce_delay: Duration,
1349}
1350
1351impl Pauser {
1352 fn new(bounce_delay: Duration) -> Self {
1354 Self { pause: Condition::new(true), bounce_delay }
1355 }
1356
1357 async fn maybe_pause(&self) {
1358 loop {
1359 if !*self.pause.lock() {
1360 return;
1361 }
1362 self.pause.when(|p| if **p { Poll::Pending } else { Poll::Ready(()) }).await;
1363 fasync::Timer::new(self.bounce_delay).await;
1364 }
1365 }
1366
1367 fn set_pause(&self, v: bool) {
1368 let mut guard = self.pause.lock();
1369 if *guard == v {
1370 return;
1371 }
1372 *guard = v;
1373 for waker in guard.drain_wakers() {
1374 waker.wake();
1375 }
1376 }
1377}
1378
1379trait NextLatest: Stream + Unpin {
1380 async fn next_latest(&mut self) -> Option<Self::Item> {
1382 let Some(mut next) = self.next().await else { return None };
1383
1384 loop {
1386 match self.next().now_or_never() {
1387 None => return Some(next),
1388 Some(None) => return None,
1389 Some(Some(n)) => next = n,
1390 }
1391 }
1392 }
1393}
1394
1395impl<T: ?Sized + Unpin> NextLatest for T where T: Stream {}
1396
1397#[cfg(test)]
1398mod tests {
1399 use super::{FlushReason, ForceMajor, FxFilesystem, FxFilesystemBuilder, SyncOptions};
1400 use crate::errors::FxfsError;
1401 use crate::fsck::{fsck, fsck_volume};
1402 use crate::log::*;
1403 use crate::lsm_tree::Operation;
1404 use crate::lsm_tree::types::Item;
1405 use crate::object_handle::{INVALID_OBJECT_ID, ObjectHandle, WriteObjectHandle};
1406 use crate::object_store::directory::{Directory, replace_child};
1407 use crate::object_store::journal::JournalOptions;
1408 use crate::object_store::journal::super_block::SuperBlockInstance;
1409 use crate::object_store::transaction::{LockKey, Mutation, Options, lock_keys};
1410 use crate::object_store::volume::root_volume;
1411 use crate::object_store::{
1412 HandleOptions, NewChildStoreOptions, ObjectDescriptor, ObjectKey, ObjectStore, ObjectValue,
1413 StoreOptions,
1414 };
1415 use crate::range::RangeExt;
1416 use fuchsia_async as fasync;
1417 use fuchsia_sync::Mutex;
1418 use futures::future::join_all;
1419 use futures::stream::{FuturesUnordered, TryStreamExt};
1420 use fxfs_insecure_crypto::new_insecure_crypt;
1421 use rustc_hash::FxHashMap as HashMap;
1422 use std::ops::Range;
1423 use std::sync::Arc;
1424 use std::sync::atomic::{AtomicU32, Ordering};
1425 use std::time::Duration;
1426 use storage_device::DeviceHolder;
1427 use storage_device::fake_device::{self, FakeDevice};
1428 use test_case::test_case;
1429
1430 const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
1431
1432 #[fuchsia::test(threads = 10)]
1433 async fn test_compaction() {
1434 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1435
1436 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1438 let root_store = fs.root_store();
1439 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
1440 .await
1441 .expect("open failed");
1442
1443 let mut tasks = Vec::new();
1444 for i in 0..2 {
1445 let mut transaction = fs
1446 .root_store()
1447 .new_transaction(
1448 lock_keys![LockKey::object(
1449 root_store.store_object_id(),
1450 root_directory.object_id()
1451 )],
1452 Options::default(),
1453 )
1454 .await
1455 .expect("new_transaction failed");
1456 let handle = root_directory
1457 .create_child_file(&mut transaction, &format!("{}", i))
1458 .await
1459 .expect("create_child_file failed");
1460 transaction.commit().await.expect("commit failed");
1461 tasks.push(fasync::Task::spawn(async move {
1462 const TEST_DATA: &[u8] = b"hello";
1463 let mut buf = handle.allocate_buffer(TEST_DATA.len()).await;
1464 buf.copy_from_slice(TEST_DATA);
1465 for _ in 0..1500 {
1466 handle.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
1467 }
1468 }));
1469 }
1470 join_all(tasks).await;
1471 fs.sync(SyncOptions::default()).await.expect("sync failed");
1472
1473 fsck(fs.clone()).await.expect("fsck failed");
1474 fs.close().await.expect("Close failed");
1475 }
1476
1477 #[fuchsia::test]
1478 async fn test_enable_allocations() {
1479 {
1481 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1482 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1483 fs.enable_allocations();
1484 let root_store = fs.root_store();
1485 let root_directory =
1486 Directory::open(&root_store, root_store.root_directory_object_id())
1487 .await
1488 .expect("open failed");
1489 let mut transaction = fs
1490 .root_store()
1491 .new_transaction(
1492 lock_keys![LockKey::object(
1493 root_store.store_object_id(),
1494 root_directory.object_id()
1495 )],
1496 Options::default(),
1497 )
1498 .await
1499 .expect("new_transaction failed");
1500 root_directory
1501 .create_child_file(&mut transaction, "test")
1502 .await
1503 .expect("create_child_file failed");
1504 transaction.commit().await.expect("commit failed");
1505 fs.close().await.expect("close failed");
1506 }
1507
1508 {
1510 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1511 let fs = FxFilesystemBuilder::new()
1512 .format(true)
1513 .image_builder_mode(Some(SuperBlockInstance::A))
1514 .open(device)
1515 .await
1516 .expect("open failed");
1517 let root_store = fs.root_store();
1518 let root_directory =
1519 Directory::open(&root_store, root_store.root_directory_object_id())
1520 .await
1521 .expect("open failed");
1522
1523 let mut transaction = fs
1524 .root_store()
1525 .new_transaction(
1526 lock_keys![LockKey::object(
1527 root_store.store_object_id(),
1528 root_directory.object_id()
1529 )],
1530 Options::default(),
1531 )
1532 .await
1533 .expect("new_transaction failed");
1534 let handle = root_directory
1535 .create_child_file(&mut transaction, "test_fail")
1536 .await
1537 .expect("create_child_file failed");
1538 transaction.commit().await.expect("commit failed");
1539
1540 assert!(
1542 FxfsError::Unavailable
1543 .matches(&handle.allocate(0..4096).await.expect_err("allocate should fail"))
1544 );
1545
1546 fs.enable_allocations();
1548 handle.allocate(0..4096).await.expect("allocate should work after enable_allocations");
1549
1550 fs.close().await.expect("close failed");
1554 }
1555 }
1559
1560 #[fuchsia::test(threads = 10)]
1561 async fn test_replay_is_identical() {
1562 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1563 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1564
1565 fs.close().await.expect("close failed");
1568 let device = fs.take_device().await;
1569 device.reopen(false);
1570
1571 struct Mutations<K, V>(Mutex<Vec<(Operation, Item<K, V>)>>);
1572
1573 impl<K: Clone, V: Clone> Mutations<K, V> {
1574 fn new() -> Self {
1575 Mutations(Mutex::new(Vec::new()))
1576 }
1577
1578 fn push(&self, operation: Operation, item: &Item<K, V>) {
1579 self.0.lock().push((operation, item.clone()));
1580 }
1581 }
1582
1583 let open_fs = |device,
1584 object_mutations: Arc<Mutex<HashMap<_, _>>>,
1585 allocator_mutations: Arc<Mutations<_, _>>| async {
1586 FxFilesystemBuilder::new()
1587 .journal_options(JournalOptions { reclaim_size: u64::MAX, ..Default::default() })
1588 .on_new_allocator(move |allocator| {
1589 let allocator_mutations = allocator_mutations.clone();
1590 allocator.tree().set_mutation_callback(Some(Box::new(move |op, item| {
1591 allocator_mutations.push(op, item)
1592 })));
1593 })
1594 .on_new_store(move |store| {
1595 let mutations = Arc::new(Mutations::new());
1596 object_mutations.lock().insert(store.store_object_id(), mutations.clone());
1597 store.tree().set_mutation_callback(Some(Box::new(move |op, item| {
1598 mutations.push(op, item)
1599 })));
1600 })
1601 .open(device)
1602 .await
1603 .expect("open failed")
1604 };
1605
1606 let allocator_mutations = Arc::new(Mutations::new());
1607 let object_mutations = Arc::new(Mutex::new(HashMap::default()));
1608 let fs = open_fs(device, object_mutations.clone(), allocator_mutations.clone()).await;
1609
1610 let root_store = fs.root_store();
1611 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
1612 .await
1613 .expect("open failed");
1614
1615 let mut transaction = fs
1616 .root_store()
1617 .new_transaction(
1618 lock_keys![LockKey::object(
1619 root_store.store_object_id(),
1620 root_directory.object_id()
1621 )],
1622 Options::default(),
1623 )
1624 .await
1625 .expect("new_transaction failed");
1626 let object = root_directory
1627 .create_child_file(&mut transaction, "test")
1628 .await
1629 .expect("create_child_file failed");
1630 transaction.commit().await.expect("commit failed");
1631
1632 let buf = object.allocate_buffer(10000).await;
1634 object.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
1635
1636 object.write_or_append(Some(5000), buf.as_ref()).await.expect("write failed");
1638
1639 object.truncate(3000).await.expect("truncate failed");
1641
1642 let mut transaction = fs
1644 .root_store()
1645 .new_transaction(
1646 lock_keys![
1647 LockKey::object(root_store.store_object_id(), root_directory.object_id()),
1648 LockKey::object(root_store.store_object_id(), object.object_id()),
1649 ],
1650 Options::default(),
1651 )
1652 .await
1653 .expect("new_transaction failed");
1654
1655 replace_child(&mut transaction, None, (&root_directory, "test"))
1656 .await
1657 .expect("replace_child failed");
1658
1659 transaction.commit().await.expect("commit failed");
1660
1661 root_store
1663 .tombstone_object(object.object_id(), Options::default(), None)
1664 .await
1665 .expect("tombstone failed");
1666
1667 fs.close().await.expect("close failed");
1669
1670 let metadata_reservation_amount = fs.object_manager().metadata_reservation().amount();
1671
1672 let device = fs.take_device().await;
1673 device.reopen(false);
1674
1675 let replayed_object_mutations = Arc::new(Mutex::new(HashMap::default()));
1676 let replayed_allocator_mutations = Arc::new(Mutations::new());
1677 let fs = open_fs(
1678 device,
1679 replayed_object_mutations.clone(),
1680 replayed_allocator_mutations.clone(),
1681 )
1682 .await;
1683
1684 let m1 = object_mutations.lock();
1685 let m2 = replayed_object_mutations.lock();
1686 assert_eq!(m1.len(), m2.len());
1687 for (store_id, mutations) in &*m1 {
1688 let mutations = mutations.0.lock();
1689 let replayed = m2.get(&store_id).expect("Found unexpected store").0.lock();
1690 assert_eq!(mutations.len(), replayed.len());
1691 for ((op1, i1), (op2, i2)) in mutations.iter().zip(replayed.iter()) {
1692 assert_eq!(op1, op2);
1693 assert_eq!(i1.key, i2.key);
1694 assert_eq!(i1.value, i2.value);
1695 }
1696 }
1697
1698 let a1 = allocator_mutations.0.lock();
1699 let a2 = replayed_allocator_mutations.0.lock();
1700 assert_eq!(a1.len(), a2.len());
1701 for ((op1, i1), (op2, i2)) in a1.iter().zip(a2.iter()) {
1702 assert_eq!(op1, op2);
1703 assert_eq!(i1.key, i2.key);
1704 assert_eq!(i1.value, i2.value);
1705 }
1706
1707 assert_eq!(
1708 fs.object_manager().metadata_reservation().amount(),
1709 metadata_reservation_amount
1710 );
1711 }
1712
1713 #[fuchsia::test]
1714 async fn test_max_in_flight_transactions() {
1715 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1716 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1717
1718 let store = fs.root_store();
1719 let transactions = FuturesUnordered::new();
1720 for _ in 0..super::MAX_IN_FLIGHT_TRANSACTIONS {
1721 transactions.push(store.new_transaction(lock_keys![], Options::default()));
1722 }
1723 let mut transactions: Vec<_> = transactions.try_collect().await.unwrap();
1724
1725 let mut fut = std::pin::pin!(store.new_transaction(lock_keys![], Options::default()));
1727 assert!(futures::poll!(&mut fut).is_pending());
1728
1729 transactions.pop();
1731
1732 assert!(futures::poll!(&mut fut).is_ready());
1733 }
1734
1735 #[fuchsia::test(threads = 10)]
1737 async fn test_continuously_trim() {
1738 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1739 let fs = FxFilesystemBuilder::new()
1740 .trim_config(Some((Duration::ZERO, Duration::ZERO)))
1741 .format(true)
1742 .open(device)
1743 .await
1744 .expect("open failed");
1745 fasync::Timer::new(Duration::from_millis(10)).await;
1747
1748 let root_store = fs.root_store();
1751 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
1752 .await
1753 .expect("open failed");
1754 for _ in 0..100 {
1755 let mut transaction = fs
1756 .root_store()
1757 .new_transaction(
1758 lock_keys![LockKey::object(
1759 root_store.store_object_id(),
1760 root_directory.object_id()
1761 )],
1762 Options::default(),
1763 )
1764 .await
1765 .expect("new_transaction failed");
1766 let object = root_directory
1767 .create_child_file(&mut transaction, "test")
1768 .await
1769 .expect("create_child_file failed");
1770 transaction.commit().await.expect("commit failed");
1771
1772 {
1773 let buf = object.allocate_buffer(1024).await;
1774 object.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
1775 }
1776 std::mem::drop(object);
1777
1778 let mut transaction = root_directory
1779 .acquire_context_for_replace(None, "test", true)
1780 .await
1781 .expect("acquire_context_for_replace failed")
1782 .transaction;
1783 replace_child(&mut transaction, None, (&root_directory, "test"))
1784 .await
1785 .expect("replace_child failed");
1786 transaction.commit().await.expect("commit failed");
1787 }
1788 fs.close().await.expect("close failed");
1789 }
1790
1791 #[test_case(true; "test power fail with barriers")]
1792 #[test_case(false; "test power fail with checksums")]
1793 #[fuchsia::test]
1794 async fn test_power_fail(barriers_enabled: bool) {
1795 for _ in 0..10 {
1798 let (store_id, device, test_file_object_id) = {
1799 let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
1800 let fs = if barriers_enabled {
1801 FxFilesystemBuilder::new()
1802 .barriers_enabled(true)
1803 .format(true)
1804 .open(device)
1805 .await
1806 .expect("new filesystem failed")
1807 } else {
1808 FxFilesystem::new_empty(device).await.expect("new_empty failed")
1809 };
1810 let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
1811
1812 fs.sync(SyncOptions { flush_device: true, ..SyncOptions::default() })
1813 .await
1814 .expect("sync failed");
1815
1816 let store = root_volume
1817 .new_volume(
1818 "test",
1819 NewChildStoreOptions {
1820 options: StoreOptions {
1821 crypt: Some(Arc::new(new_insecure_crypt())),
1822 ..StoreOptions::default()
1823 },
1824 ..Default::default()
1825 },
1826 )
1827 .await
1828 .expect("new_volume failed");
1829 let root_directory = Directory::open(&store, store.root_directory_object_id())
1830 .await
1831 .expect("open failed");
1832
1833 async fn create_files(store: &Arc<ObjectStore>, prefix: &str) {
1835 let fs = store.filesystem();
1836 let root_directory = Directory::open(store, store.root_directory_object_id())
1837 .await
1838 .expect("open failed");
1839 for i in 0..100 {
1840 let mut transaction = fs
1841 .ensure_local()
1842 .root_store()
1843 .new_transaction(
1844 lock_keys![LockKey::object(
1845 store.store_object_id(),
1846 store.root_directory_object_id()
1847 )],
1848 Options::default(),
1849 )
1850 .await
1851 .expect("new_transaction failed");
1852 root_directory
1853 .create_child_file(&mut transaction, &format!("{prefix} {i}"))
1854 .await
1855 .expect("create_child_file failed");
1856 transaction.commit().await.expect("commit failed");
1857 }
1858 }
1859
1860 create_files(&store, "A").await;
1862
1863 let mut transaction = fs
1866 .root_store()
1867 .new_transaction(
1868 lock_keys![LockKey::object(
1869 store.store_object_id(),
1870 store.root_directory_object_id()
1871 )],
1872 Options::default(),
1873 )
1874 .await
1875 .expect("new_transaction failed");
1876 let object = root_directory
1877 .create_child_file(&mut transaction, "test")
1878 .await
1879 .expect("create_child_file failed");
1880 transaction.commit().await.expect("commit failed");
1881
1882 let mut transaction =
1883 object.new_transaction().await.expect("new_transaction failed");
1884 let mut buffer = object.allocate_buffer(4096).await;
1885 buffer.fill(0xed);
1886 object
1887 .txn_write(&mut transaction, 0, buffer.as_ref())
1888 .await
1889 .expect("txn_write failed");
1890 transaction.commit().await.expect("commit failed");
1891
1892 create_files(&store, "B").await;
1894
1895 fs.sync(SyncOptions::default()).await.expect("sync failed");
1898
1899 fasync::Timer::new(Duration::from_millis(10)).await;
1905
1906 (
1907 store.store_object_id(),
1908 fs.device().snapshot().expect("snapshot failed"),
1909 object.object_id(),
1910 )
1911 };
1912
1913 device
1916 .discard_random_since_last_flush()
1917 .expect("discard_random_since_last_flush failed");
1918
1919 let fs = FxFilesystem::open(device).await.expect("open failed");
1920 fsck(fs.clone()).await.expect("fsck failed");
1921
1922 let mut check_test_file = false;
1923
1924 let object_id = if fs.object_manager().store(store_id).is_some() {
1927 fsck_volume(&fs, store_id, Some(Arc::new(new_insecure_crypt())))
1928 .await
1929 .expect("fsck_volume failed");
1930
1931 let store = root_volume(fs.clone())
1935 .await
1936 .expect("root_volume failed")
1937 .volume(
1938 "test",
1939 StoreOptions {
1940 crypt: Some(Arc::new(new_insecure_crypt())),
1941 ..StoreOptions::default()
1942 },
1943 )
1944 .await
1945 .expect("volume failed");
1946
1947 let root_directory = Directory::open(&store, store.root_directory_object_id())
1948 .await
1949 .expect("open failed");
1950
1951 let mut transaction = fs
1952 .root_store()
1953 .new_transaction(
1954 lock_keys![LockKey::object(
1955 store.store_object_id(),
1956 store.root_directory_object_id()
1957 )],
1958 Options::default(),
1959 )
1960 .await
1961 .expect("new_transaction failed");
1962 let object = root_directory
1963 .create_child_file(&mut transaction, &format!("C"))
1964 .await
1965 .expect("create_child_file failed");
1966 transaction.commit().await.expect("commit failed");
1967
1968 if let Ok(test_file) = ObjectStore::open_object(
1970 &store,
1971 test_file_object_id,
1972 HandleOptions::default(),
1973 None,
1974 )
1975 .await
1976 {
1977 let mut buffer = test_file.allocate_buffer(4096).await;
1979 let bytes =
1980 test_file.read_aligned(0, buffer.as_mut()).await.expect("read failed");
1981 if bytes == 4096 {
1982 let expected = [0xed; 4096];
1983 assert_eq!(buffer.to_vec(), expected);
1984 } else {
1985 assert_eq!(bytes, 0);
1987 }
1988
1989 let mut transaction =
1991 test_file.new_transaction().await.expect("new_transaction failed");
1992 buffer.fill(0x37);
1993 test_file
1994 .txn_write(&mut transaction, 0, buffer.as_ref())
1995 .await
1996 .expect("txn_write failed");
1997 transaction.commit().await.expect("commit failed");
1998 check_test_file = true;
1999 }
2000
2001 object.object_id()
2002 } else {
2003 INVALID_OBJECT_ID
2004 };
2005
2006 fs.close().await.expect("close failed");
2008 let device = fs.take_device().await;
2009 device.reopen(false);
2010
2011 let fs = FxFilesystem::open(device).await.expect("open failed");
2012 fsck(fs.clone()).await.expect("fsck failed");
2013
2014 if object_id != INVALID_OBJECT_ID {
2017 fsck_volume(&fs, store_id, Some(Arc::new(new_insecure_crypt())))
2018 .await
2019 .expect("fsck_volume failed");
2020
2021 let store = root_volume(fs.clone())
2022 .await
2023 .expect("root_volume failed")
2024 .volume(
2025 "test",
2026 StoreOptions {
2027 crypt: Some(Arc::new(new_insecure_crypt())),
2028 ..StoreOptions::default()
2029 },
2030 )
2031 .await
2032 .expect("volume failed");
2033 ObjectStore::open_object(&store, object_id, HandleOptions::default(), None)
2035 .await
2036 .expect("open_object failed");
2037
2038 if check_test_file {
2040 info!("Checking test file for modification");
2041 let test_file = ObjectStore::open_object(
2042 &store,
2043 test_file_object_id,
2044 HandleOptions::default(),
2045 None,
2046 )
2047 .await
2048 .expect("open_object failed");
2049 let mut buffer = test_file.allocate_buffer(4096).await;
2050 assert_eq!(
2051 test_file.read_aligned(0, buffer.as_mut()).await.expect("read failed"),
2052 4096
2053 );
2054 let expected = [0x37; 4096];
2055 let data = buffer.to_vec();
2056 assert_eq!(data, expected);
2057 }
2058 }
2059
2060 fs.close().await.expect("close failed");
2061 }
2062 }
2063
2064 #[fuchsia::test]
2065 async fn test_barrier_not_emitted_when_transaction_has_no_data() {
2066 let barrier_count = Arc::new(AtomicU32::new(0));
2067
2068 struct Observer(Arc<AtomicU32>);
2069
2070 impl fake_device::Observer for Observer {
2071 fn barrier(&self) {
2072 self.0.fetch_add(1, Ordering::Relaxed);
2073 }
2074 }
2075
2076 let mut fake_device = FakeDevice::new(8192, 4096);
2077 fake_device.set_observer(Box::new(Observer(barrier_count.clone())));
2078 let device = DeviceHolder::new(fake_device);
2079 let fs = FxFilesystemBuilder::new()
2080 .barriers_enabled(true)
2081 .format(true)
2082 .open(device)
2083 .await
2084 .expect("new filesystem failed");
2085
2086 {
2087 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2088 root_vol
2089 .new_volume(
2090 "test",
2091 NewChildStoreOptions {
2092 options: StoreOptions {
2093 crypt: Some(Arc::new(new_insecure_crypt())),
2094 ..StoreOptions::default()
2095 },
2096 ..NewChildStoreOptions::default()
2097 },
2098 )
2099 .await
2100 .expect("there is no test volume");
2101 fs.close().await.expect("close failed");
2102 }
2103 let device = fs.take_device().await;
2106 device.reopen(false);
2107 let fs = FxFilesystemBuilder::new()
2108 .barriers_enabled(true)
2109 .open(device)
2110 .await
2111 .expect("new filesystem failed");
2112 let expected_barrier_count = barrier_count.load(Ordering::Relaxed);
2113
2114 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2115 let store = root_vol
2116 .volume(
2117 "test",
2118 StoreOptions {
2119 crypt: Some(Arc::new(new_insecure_crypt())),
2120 ..StoreOptions::default()
2121 },
2122 )
2123 .await
2124 .expect("there is no test volume");
2125
2126 let root_directory =
2128 Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2129 for i in 0..100 {
2130 let mut transaction = fs
2131 .root_store()
2132 .new_transaction(
2133 lock_keys![LockKey::object(
2134 store.store_object_id(),
2135 store.root_directory_object_id()
2136 )],
2137 Options::default(),
2138 )
2139 .await
2140 .expect("new_transaction failed");
2141 root_directory
2142 .create_child_file(&mut transaction, &format!("A {i}"))
2143 .await
2144 .expect("create_child_file failed");
2145 transaction.commit().await.expect("commit failed");
2146 }
2147
2148 fs.close().await.expect("close failed");
2150 assert_eq!(expected_barrier_count, barrier_count.load(Ordering::Relaxed));
2152 }
2153
2154 #[fuchsia::test]
2155 async fn test_barrier_emitted_when_transaction_includes_data() {
2156 let barrier_count = Arc::new(AtomicU32::new(0));
2157
2158 struct Observer(Arc<AtomicU32>);
2159
2160 impl fake_device::Observer for Observer {
2161 fn barrier(&self) {
2162 self.0.fetch_add(1, Ordering::Relaxed);
2163 }
2164 }
2165
2166 let mut fake_device = FakeDevice::new(8192, 4096);
2167 fake_device.set_observer(Box::new(Observer(barrier_count.clone())));
2168 let device = DeviceHolder::new(fake_device);
2169 let fs = FxFilesystemBuilder::new()
2170 .barriers_enabled(true)
2171 .format(true)
2172 .open(device)
2173 .await
2174 .expect("new filesystem failed");
2175
2176 {
2177 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2178 root_vol
2179 .new_volume(
2180 "test",
2181 NewChildStoreOptions {
2182 options: StoreOptions {
2183 crypt: Some(Arc::new(new_insecure_crypt())),
2184 ..StoreOptions::default()
2185 },
2186 ..NewChildStoreOptions::default()
2187 },
2188 )
2189 .await
2190 .expect("there is no test volume");
2191 fs.close().await.expect("close failed");
2192 }
2193 let device = fs.take_device().await;
2196 device.reopen(false);
2197 let fs = FxFilesystemBuilder::new()
2198 .barriers_enabled(true)
2199 .open(device)
2200 .await
2201 .expect("new filesystem failed");
2202 let expected_barrier_count = barrier_count.load(Ordering::Relaxed);
2203
2204 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2205 let store = root_vol
2206 .volume(
2207 "test",
2208 StoreOptions {
2209 crypt: Some(Arc::new(new_insecure_crypt())),
2210 ..StoreOptions::default()
2211 },
2212 )
2213 .await
2214 .expect("there is no test volume");
2215
2216 let root_directory =
2218 Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2219
2220 let mut transaction = fs
2221 .root_store()
2222 .new_transaction(
2223 lock_keys![LockKey::object(
2224 store.store_object_id(),
2225 store.root_directory_object_id()
2226 )],
2227 Options::default(),
2228 )
2229 .await
2230 .expect("new_transaction failed");
2231 let object = root_directory
2232 .create_child_file(&mut transaction, "test")
2233 .await
2234 .expect("create_child_file failed");
2235 transaction.commit().await.expect("commit failed");
2236
2237 let mut transaction = object.new_transaction().await.expect("new_transaction failed");
2238 let mut buffer = object.allocate_buffer(4096).await;
2239 buffer.fill(0xed);
2240 object.txn_write(&mut transaction, 0, buffer.as_ref()).await.expect("txn_write failed");
2241 transaction.commit().await.expect("commit failed");
2242
2243 fs.close().await.expect("close failed");
2245 assert!(expected_barrier_count < barrier_count.load(Ordering::Relaxed));
2247 }
2248
2249 #[test_case(true; "fail when original filesystem has barriers enabled")]
2250 #[test_case(false; "fail when original filesystem has barriers disabled")]
2251 #[fuchsia::test]
2252 async fn test_switching_barrier_mode_on_existing_filesystem(original_barrier_mode: bool) {
2253 let crypt = Some(Arc::new(new_insecure_crypt()) as Arc<dyn fxfs_crypto::Crypt>);
2254 let fake_device = FakeDevice::new(8192, 4096);
2255 let device = DeviceHolder::new(fake_device);
2256 let fs: super::OpenFxFilesystem = FxFilesystemBuilder::new()
2257 .barriers_enabled(original_barrier_mode)
2258 .format(true)
2259 .open(device)
2260 .await
2261 .expect("new filesystem failed");
2262
2263 {
2265 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2266 let store = root_vol
2267 .new_volume(
2268 "test",
2269 NewChildStoreOptions {
2270 options: StoreOptions { crypt: crypt.clone(), ..Default::default() },
2271 ..Default::default()
2272 },
2273 )
2274 .await
2275 .expect("creating test volume");
2276 let root_dir = Directory::open(&store, store.root_directory_object_id())
2277 .await
2278 .expect("open failed");
2279 let mut transaction = fs
2280 .root_store()
2281 .new_transaction(
2282 lock_keys![LockKey::object(
2283 store.store_object_id(),
2284 store.root_directory_object_id()
2285 )],
2286 Default::default(),
2287 )
2288 .await
2289 .expect("new_transaction failed");
2290 let object = root_dir
2291 .create_child_file(&mut transaction, "file")
2292 .await
2293 .expect("create_child_file failed");
2294 transaction.commit().await.expect("commit failed");
2295 let mut buffer = object.allocate_buffer(4096).await;
2296 buffer.fill(0xA7);
2297 let new_size = object.write_or_append(None, buffer.as_ref()).await.unwrap();
2298 assert_eq!(new_size, 4096);
2299 }
2300
2301 fs.close().await.expect("close failed");
2303 let device = fs.take_device().await;
2304 device.reopen(false);
2305 let fs = FxFilesystemBuilder::new()
2306 .barriers_enabled(!original_barrier_mode)
2307 .open(device)
2308 .await
2309 .expect("new filesystem failed");
2310 {
2311 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2312 let store = root_vol
2313 .volume("test", StoreOptions { crypt: crypt.clone(), ..Default::default() })
2314 .await
2315 .expect("opening test volume");
2316 let root_dir = Directory::open(&store, store.root_directory_object_id())
2317 .await
2318 .expect("open failed");
2319 let (object_id, _, _) =
2320 root_dir.lookup("file").await.expect("lookup failed").expect("missing file");
2321 let test_file = ObjectStore::open_object(&store, object_id, Default::default(), None)
2322 .await
2323 .expect("open failed");
2324 let mut buffer = test_file.allocate_buffer(4096).await;
2326 buffer.fill(0xA8);
2327 let new_size = test_file.write_or_append(None, buffer.as_ref()).await.unwrap();
2328 assert_eq!(new_size, 8192);
2329 }
2330
2331 fs.close().await.expect("close failed");
2334 let device = fs.take_device().await;
2335 device.reopen(false);
2336 let fs = FxFilesystemBuilder::new()
2337 .barriers_enabled(original_barrier_mode)
2338 .open(device)
2339 .await
2340 .expect("new filesystem failed");
2341 {
2342 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2343 let store = root_vol
2344 .volume("test", StoreOptions { crypt: crypt.clone(), ..Default::default() })
2345 .await
2346 .expect("opening test volume");
2347 let root_dir = Directory::open(&store, store.root_directory_object_id())
2348 .await
2349 .expect("open failed");
2350 let (object_id, _, _) =
2351 root_dir.lookup("file").await.expect("lookup failed").expect("missing file");
2352 let test_file = ObjectStore::open_object(&store, object_id, Default::default(), None)
2353 .await
2354 .expect("open failed");
2355 let mut buffer = test_file.allocate_buffer(8192).await;
2356 assert_eq!(
2357 test_file.read_aligned(0, buffer.as_mut()).await.expect("read failed"),
2358 8192,
2359 "short read"
2360 );
2361 let data = buffer.to_vec();
2362 assert_eq!(data[0..4096], [0xA7; 4096]);
2363 assert_eq!(data[4096..8192], [0xA8; 4096]);
2364 }
2365 fs.close().await.expect("close failed");
2366 }
2367
2368 #[fuchsia::test]
2369 async fn test_image_builder_mode_no_early_writes() {
2370 const BLOCK_SIZE: u32 = 4096;
2371 let device = DeviceHolder::new(FakeDevice::new(2048, BLOCK_SIZE));
2372 device.reopen(true);
2373 let fs = FxFilesystemBuilder::new()
2374 .format(true)
2375 .image_builder_mode(Some(SuperBlockInstance::A))
2376 .open(device)
2377 .await
2378 .expect("open failed");
2379 fs.enable_allocations();
2380 fs.device().reopen(false);
2382 fs.close().await.expect("closed");
2383 }
2384
2385 #[fuchsia::test]
2386 async fn test_image_builder_mode() {
2387 const BLOCK_SIZE: u32 = 4096;
2388 const EXISTING_FILE_RANGE: Range<u64> = 4096 * 1024..4096 * 1025;
2389 let device = DeviceHolder::new(FakeDevice::new(2048, BLOCK_SIZE));
2390
2391 {
2393 let mut write_buf =
2394 device.allocate_buffer(EXISTING_FILE_RANGE.length().unwrap() as usize).await;
2395 write_buf.fill(0xf0);
2396 device.write(EXISTING_FILE_RANGE.start, write_buf.as_ref()).await.expect("write");
2397 }
2398
2399 device.reopen(true);
2400
2401 let device = {
2402 let fs = FxFilesystemBuilder::new()
2403 .format(true)
2404 .image_builder_mode(Some(SuperBlockInstance::B))
2405 .open(device)
2406 .await
2407 .expect("open failed");
2408 fs.enable_allocations();
2409 {
2410 let root_store = fs.root_store();
2411 let root_directory =
2412 Directory::open(&root_store, root_store.root_directory_object_id())
2413 .await
2414 .expect("open failed");
2415 let handle;
2417 {
2418 let mut transaction = fs
2419 .root_store()
2420 .new_transaction(
2421 lock_keys![LockKey::object(
2422 root_directory.store().store_object_id(),
2423 root_directory.object_id()
2424 )],
2425 Options::default(),
2426 )
2427 .await
2428 .expect("new transaction");
2429 handle = root_directory
2430 .create_child_file(&mut transaction, "test")
2431 .await
2432 .expect("create file");
2433 handle.extend(&mut transaction, EXISTING_FILE_RANGE).await.expect("extend");
2434 transaction.commit().await.expect("commit");
2435 }
2436 }
2437 fs.device().reopen(false);
2438 fs.close().await.expect("close");
2439 fs.take_device().await
2440 };
2441 device.reopen(false);
2442 let fs = FxFilesystem::open(device).await.expect("open failed");
2443 fsck(fs.clone()).await.expect("fsck failed");
2444
2445 let root_store = fs.root_store();
2447 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
2448 .await
2449 .expect("open failed");
2450 let (object_id, descriptor, _) =
2451 root_directory.lookup("test").await.expect("lookup failed").unwrap();
2452 assert_eq!(descriptor, ObjectDescriptor::File);
2453 let test_file =
2454 ObjectStore::open_object(&root_store, object_id, HandleOptions::default(), None)
2455 .await
2456 .expect("open failed");
2457 let mut read_buf =
2458 test_file.allocate_buffer(EXISTING_FILE_RANGE.length().unwrap() as usize).await;
2459 test_file.read_aligned(0, read_buf.as_mut()).await.expect("read failed");
2460 let data = read_buf.to_vec();
2461 assert_eq!(data, [0xf0; 4096]);
2462 fs.close().await.expect("closed");
2463 }
2464
2465 #[fuchsia::test]
2466 async fn test_image_builder_mode_large_store_can_major_compact() {
2467 const BLOCK_SIZE: u32 = 4096;
2468 let device = DeviceHolder::new(FakeDevice::new(8192, BLOCK_SIZE));
2469 let device = {
2470 let fs = FxFilesystemBuilder::new()
2471 .format(true)
2472 .image_builder_mode(Some(SuperBlockInstance::A))
2473 .open(device)
2474 .await
2475 .expect("open failed");
2476 fs.enable_allocations();
2477 {
2478 let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2479 let store = root_volume
2480 .new_volume("vol", NewChildStoreOptions::default())
2481 .await
2482 .expect("new_volume failed");
2483 let root_dir = Directory::open(&store, store.root_directory_object_id())
2484 .await
2485 .expect("open failed");
2486 let handle = {
2487 let mut transaction = store
2488 .new_transaction(
2489 lock_keys![LockKey::object(
2490 store.store_object_id(),
2491 root_dir.object_id()
2492 )],
2493 Options::default(),
2494 )
2495 .await
2496 .expect("new_transaction failed");
2497 let handle = root_dir
2498 .create_child_file(&mut transaction, "file")
2499 .await
2500 .expect("create_child_file failed");
2501 transaction.commit().await.expect("commit failed");
2502 handle
2503 };
2504
2505 for batch in 0..64 {
2508 let mut transaction = store
2509 .new_transaction(
2510 lock_keys![LockKey::object(
2511 store.store_object_id(),
2512 handle.object_id()
2513 )],
2514 Options::default(),
2515 )
2516 .await
2517 .expect("new_transaction failed");
2518 for j in 0..20 {
2519 let idx = batch * 20 + j;
2520 transaction.add(
2521 store.store_object_id(),
2522 Mutation::replace_or_insert_object(
2523 ObjectKey::extended_attribute(
2524 handle.object_id(),
2525 format!("attr_{idx:04}").into_bytes(),
2526 ),
2527 ObjectValue::inline_extended_attribute(vec![0xaa; 1024]),
2528 ),
2529 );
2530 }
2531 transaction.commit().await.expect("commit failed");
2532 }
2533 }
2534 fs.close().await.expect("close failed");
2535 fs.take_device().await
2536 };
2537 device.reopen(false);
2538
2539 let fs = FxFilesystem::open(device).await.expect("open failed");
2540 let object_manager = fs.object_manager();
2541 assert!(
2542 object_manager.metadata_reservation().amount()
2543 >= object_manager.max_store_reservation(),
2544 "metadata reservation ({}) is smaller than max store reservation ({}); borrowed={}",
2545 object_manager.metadata_reservation().amount(),
2546 object_manager.max_store_reservation(),
2547 object_manager.borrowed_metadata_space(),
2548 );
2549
2550 {
2553 let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2554 let store =
2555 root_volume.volume("vol", StoreOptions::default()).await.expect("volume failed");
2556 let root_dir = Directory::open(&store, store.root_directory_object_id())
2557 .await
2558 .expect("open failed");
2559 let mut transaction = store
2560 .new_transaction(
2561 lock_keys![LockKey::object(store.store_object_id(), root_dir.object_id())],
2562 Options::default(),
2563 )
2564 .await
2565 .expect("new_transaction failed");
2566 root_dir.create_child_file(&mut transaction, "extra").await.expect("create failed");
2567 transaction.commit().await.expect("commit failed");
2568 }
2569 let _hold = fs.allocator().reserve_with(None, |limit| limit);
2570 object_manager
2571 .flush(FlushReason::Journal(ForceMajor::True))
2572 .await
2573 .expect("forced major compaction failed");
2574 std::mem::drop(_hold);
2575 fsck(fs.clone()).await.expect("fsck failed");
2576 fs.close().await.expect("close failed");
2577 }
2578
2579 #[fuchsia::test]
2580 async fn test_read_only_mount_on_full_filesystem() {
2581 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2582 let fs =
2583 FxFilesystemBuilder::new().format(true).open(device).await.expect("new_empty failed");
2584 let root_store = fs.root_store();
2585 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
2586 .await
2587 .expect("open failed");
2588
2589 let mut transaction = fs
2590 .root_store()
2591 .new_transaction(
2592 lock_keys![LockKey::object(
2593 root_store.store_object_id(),
2594 root_directory.object_id()
2595 )],
2596 Options::default(),
2597 )
2598 .await
2599 .expect("new_transaction failed");
2600 let handle = root_directory
2601 .create_child_file(&mut transaction, "test")
2602 .await
2603 .expect("create_child_file failed");
2604 transaction.commit().await.expect("commit failed");
2605
2606 let mut buf = handle.allocate_buffer(4096).await;
2607 buf.fill(0xaa);
2608 loop {
2609 if handle.write_or_append(None, buf.as_ref()).await.is_err() {
2610 break;
2611 }
2612 }
2613
2614 let max_offset = fs.allocator().maximum_offset();
2615 fs.close().await.expect("Close failed");
2616
2617 let device = fs.take_device().await;
2618 device.reopen(false);
2619 let mut buffer = device
2620 .allocate_buffer(
2621 crate::round::round_up(max_offset, TEST_DEVICE_BLOCK_SIZE).unwrap() as usize
2622 )
2623 .await;
2624 device.read(0, buffer.as_mut()).await.expect("read failed");
2625
2626 let image_data = buffer.to_vec();
2627 let device = DeviceHolder::new(
2628 FakeDevice::from_image(image_data.as_slice(), TEST_DEVICE_BLOCK_SIZE)
2629 .expect("from_image failed"),
2630 );
2631 let fs =
2632 FxFilesystemBuilder::new().read_only(true).open(device).await.expect("open failed");
2633 fs.close().await.expect("Close failed");
2634 }
2635
2636 #[test_case(SuperBlockInstance::A; "Superblock instance A")]
2637 #[test_case(SuperBlockInstance::B; "Superblock instance B")]
2638 #[fuchsia::test]
2639 async fn test_image_builder_mode_flush_on_close_sb_a(target_sb: SuperBlockInstance) {
2640 const BLOCK_SIZE: u32 = 4096;
2641 let device = DeviceHolder::new(FakeDevice::new(2048, BLOCK_SIZE));
2642
2643 device.reopen(true);
2645 let fs = FxFilesystemBuilder::new()
2646 .format(true)
2647 .image_builder_mode(Some(target_sb))
2648 .open(device)
2649 .await
2650 .expect("open failed");
2651
2652 fs.enable_allocations();
2653
2654 fs.device().reopen(false);
2656
2657 {
2659 let root_store = fs.root_store();
2660 let root_directory =
2661 Directory::open(&root_store, root_store.root_directory_object_id())
2662 .await
2663 .expect("open failed");
2664
2665 let mut transaction = fs
2666 .root_store()
2667 .new_transaction(
2668 lock_keys![LockKey::object(
2669 root_directory.store().store_object_id(),
2670 root_directory.object_id()
2671 )],
2672 Options::default(),
2673 )
2674 .await
2675 .expect("new transaction");
2676 let handle = root_directory
2677 .create_child_file(&mut transaction, "post_finalize_file")
2678 .await
2679 .expect("create file");
2680 transaction.commit().await.expect("commit");
2681
2682 let mut buf = handle.allocate_buffer(BLOCK_SIZE as usize).await;
2683 buf.fill(0xaa);
2684 handle.write_or_append(None, buf.as_ref()).await.expect("write failed");
2685 }
2686
2687 fs.close().await.expect("close failed");
2689
2690 let other_sb = target_sb.next();
2691
2692 let device = fs.take_device().await;
2694 device.reopen(true); let mut buf = device.allocate_buffer(BLOCK_SIZE as usize).await;
2696
2697 device.read(target_sb.first_extent().start, buf.as_mut()).await.expect("read target_sb");
2698 let data = buf.to_vec();
2699 assert_eq!(&data[..8], b"FxfsSupr", "target_sb should have magic bytes");
2700
2701 buf.fill(0); device.read(other_sb.first_extent().start, buf.as_mut()).await.expect("read other_sb");
2703 let data2 = buf.to_vec();
2705 assert_eq!(data2, &[0; 4096], "other_sb should be zeroed");
2706 }
2707
2708 #[cfg(target_os = "fuchsia")]
2709 #[fuchsia::test(allow_stalls = false)]
2710 async fn test_trim_with_power_manager() {
2711 use anyhow::Error;
2712 use async_trait::async_trait;
2713 use fuchsia_async::TestExecutor;
2714 use futures::StreamExt;
2715
2716 TestExecutor::advance_to(fasync::MonotonicInstant::ZERO).await;
2717
2718 #[derive(Default)]
2719 struct MockPowerManager {
2720 on_battery: Mutex<bool>,
2721 event: event_listener::Event,
2722 wake_lease: Mutex<Option<zx::EventPair>>,
2723 }
2724
2725 impl MockPowerManager {
2726 fn set_on_battery(&self, v: bool) {
2727 *self.on_battery.lock() = v;
2728 self.event.notify(usize::MAX);
2729 }
2730
2731 fn is_lease_held(&self) -> bool {
2732 self.wake_lease.lock().as_ref().is_some_and(|handle| {
2733 handle
2734 .wait_one(
2735 zx::Signals::EVENTPAIR_PEER_CLOSED,
2736 zx::MonotonicInstant::INFINITE_PAST,
2737 )
2738 .is_err()
2739 })
2740 }
2741 }
2742
2743 impl super::PowerManager for MockPowerManager {
2744 fn watch_battery(
2745 self: Arc<Self>,
2746 ) -> futures::stream::BoxStream<'static, (bool, Option<super::WakeLease>)> {
2747 futures::stream::unfold(true, move |first| {
2748 let this = self.clone();
2749 async move {
2750 if !first {
2751 this.event.listen().await;
2752 }
2753 let val = *this.on_battery.lock();
2754 let handle = if val {
2755 None
2756 } else {
2757 let (h1, h2) = zx::EventPair::create();
2758 *this.wake_lease.lock() = Some(h2);
2759 Some(super::WakeLease::new(h1))
2760 };
2761 Some(((val, handle), false))
2762 }
2763 })
2764 .boxed()
2765 }
2766 }
2767
2768 let trim_count = Arc::new(AtomicU32::new(0));
2769
2770 struct TrimTrackingDevice {
2771 inner: DeviceHolder,
2772 trim_count: Arc<AtomicU32>,
2773 power_manager: Arc<MockPowerManager>,
2774 }
2775
2776 #[async_trait]
2777 impl storage_device::Device for TrimTrackingDevice {
2778 fn allocate_buffer(&self, size: usize) -> storage_device::buffer::BufferFuture<'_> {
2779 self.inner.allocate_buffer(size)
2780 }
2781 fn block_size(&self) -> u32 {
2782 self.inner.block_size()
2783 }
2784 fn block_count(&self) -> u64 {
2785 self.inner.block_count()
2786 }
2787 async fn read_with_opts(
2788 &self,
2789 offset: u64,
2790 buffer: storage_device::buffer::MutableBufferRef<'_>,
2791 opts: storage_device::ReadOptions,
2792 ) -> Result<(), Error> {
2793 self.inner.read_with_opts(offset, buffer, opts).await
2794 }
2795 async fn write_with_opts(
2796 &self,
2797 offset: u64,
2798 buffer: storage_device::buffer::BufferRef<'_>,
2799 opts: storage_device::WriteOptions,
2800 ) -> Result<(), Error> {
2801 self.inner.write_with_opts(offset, buffer, opts).await
2802 }
2803 async fn trim(&self, range: std::ops::Range<u64>) -> Result<(), Error> {
2804 assert!(self.power_manager.is_lease_held());
2805 self.trim_count.fetch_add(1, Ordering::SeqCst);
2806 self.inner.trim(range).await
2807 }
2808 async fn flush(&self) -> Result<(), Error> {
2809 self.inner.flush().await
2810 }
2811 async fn close(&self) -> Result<(), Error> {
2812 self.inner.close().await
2813 }
2814 fn supports_trim(&self) -> bool {
2815 true
2816 }
2817 fn is_read_only(&self) -> bool {
2818 self.inner.is_read_only()
2819 }
2820 fn snapshot(&self) -> Result<DeviceHolder, Error> {
2821 Ok(DeviceHolder::new(TrimTrackingDevice {
2822 inner: self.inner.snapshot()?,
2823 trim_count: self.trim_count.clone(),
2824 power_manager: self.power_manager.clone(),
2825 }))
2826 }
2827 fn reopen(&self, read_only: bool) {
2828 self.inner.reopen(read_only)
2829 }
2830 }
2831
2832 let pm = Arc::new(MockPowerManager::default());
2833
2834 pm.set_on_battery(true);
2836
2837 let fake_device = FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE);
2838 let device = DeviceHolder::new(TrimTrackingDevice {
2839 inner: DeviceHolder::new(fake_device),
2840 trim_count: trim_count.clone(),
2841 power_manager: pm.clone(),
2842 });
2843
2844 let fs = FxFilesystemBuilder::new()
2845 .format(true)
2846 .power_manager(pm.clone())
2847 .trim_config(Some((Duration::ZERO, Duration::from_millis(100))))
2848 .trim_charger_wait(Duration::from_millis(10))
2849 .open(device)
2850 .await
2851 .expect("open failed");
2852
2853 TestExecutor::advance_to(fasync::MonotonicInstant::after(
2855 Duration::from_millis(500).into(),
2856 ))
2857 .await;
2858 let _ = TestExecutor::poll_until_stalled(std::future::pending::<()>()).await;
2859
2860 assert_eq!(trim_count.load(Ordering::SeqCst), 0);
2861
2862 {
2864 let root_store = fs.root_store();
2865 let root_directory =
2866 Directory::open(&root_store, root_store.root_directory_object_id())
2867 .await
2868 .expect("open failed");
2869 let mut transaction = fs
2870 .root_store()
2871 .new_transaction(
2872 lock_keys![LockKey::object(
2873 root_store.store_object_id(),
2874 root_directory.object_id()
2875 )],
2876 Options::default(),
2877 )
2878 .await
2879 .expect("new_transaction failed");
2880 let handle = root_directory
2881 .create_child_file(&mut transaction, "test")
2882 .await
2883 .expect("create_child_file failed");
2884 transaction.commit().await.expect("commit failed");
2885 handle.allocate(0..4096).await.expect("allocate failed");
2886 let mut transaction = fs
2888 .root_store()
2889 .new_transaction(
2890 lock_keys![
2891 LockKey::object(root_store.store_object_id(), root_directory.object_id()),
2892 LockKey::object(root_store.store_object_id(), handle.object_id()),
2893 ],
2894 Options::default(),
2895 )
2896 .await
2897 .expect("new_transaction failed");
2898 replace_child(&mut transaction, None, (&root_directory, "test"))
2899 .await
2900 .expect("delete failed");
2901 transaction.commit().await.expect("commit failed");
2902 fs.root_store()
2903 .tombstone_object(handle.object_id(), Options::default(), None)
2904 .await
2905 .expect("tombstone failed");
2906 }
2907
2908 pm.set_on_battery(false);
2910
2911 TestExecutor::advance_to(fasync::MonotonicInstant::after(Duration::from_millis(10).into()))
2913 .await;
2914
2915 let _ = TestExecutor::poll_until_stalled(std::future::pending::<()>()).await;
2916
2917 assert!(trim_count.load(Ordering::SeqCst) > 0);
2918
2919 trim_count.store(0, Ordering::SeqCst);
2921 pm.set_on_battery(true);
2922
2923 TestExecutor::advance_to(fasync::MonotonicInstant::after(
2925 Duration::from_millis(500).into(),
2926 ))
2927 .await;
2928
2929 let _ = TestExecutor::poll_until_stalled(std::future::pending::<()>()).await;
2930
2931 assert_eq!(trim_count.load(Ordering::SeqCst), 0);
2932
2933 fs.close().await.expect("close failed");
2934 }
2935
2936 #[fuchsia::test]
2937 async fn test_concurrent_do_trim_returns_error() {
2938 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2939 let fs = FxFilesystemBuilder::new()
2940 .trim_config(None)
2941 .format(true)
2942 .open(device)
2943 .await
2944 .expect("open failed");
2945
2946 let max_extent_size = fs.device().size() as usize;
2947 const EXTENTS_PER_BATCH: usize = usize::MAX;
2948
2949 let allocator = fs.allocator();
2951 let _trimmable_extents = allocator
2952 .take_for_trimming(0, max_extent_size, EXTENTS_PER_BATCH)
2953 .await
2954 .expect("take_for_trimming failed");
2955
2956 let res = fs.do_trim(None).await;
2959 assert!(matches!(res, Err(e) if FxfsError::AlreadyBound.matches(&e)));
2960
2961 fs.close().await.expect("close failed");
2962 }
2963}