1use crate::errors::FxfsError;
6use crate::fsck::{FsckOptions, fsck_volume_with_options, fsck_with_options};
7use crate::hooks::HooksHandle;
8use crate::log::*;
9use crate::metrics;
10use crate::object_store::allocator::{Allocator, Hold, 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, LockManager, MetadataReservation, Mutation,
18 TRANSACTION_METADATA_MAX_AMOUNT, Transaction, WriteGuard, lock_keys,
19};
20use crate::object_store::volume::{VOLUMES_DIRECTORY, root_volume};
21use crate::object_store::{NewChildStoreOptions, ObjectStore, StoreOptions};
22use crate::range::RangeExt;
23use crate::serialized_types::{LATEST_VERSION, Version};
24use anyhow::{Context, Error, anyhow, bail};
25use async_trait::async_trait;
26use event_listener::Event;
27use fuchsia_async as fasync;
28use fuchsia_async::condition::Condition;
29use fuchsia_inspect::{Inspector, LazyNode, NumericProperty as _, UintProperty};
30use fuchsia_sync::Mutex;
31use futures::{FutureExt, Stream};
32use fxfs_crypto::Crypt;
33use fxfs_trace::{TraceFutureExt, trace_future_args};
34use static_assertions::const_assert;
35use std::pin::pin;
36use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
37use std::sync::{Arc, OnceLock, Weak};
38use std::task::Poll;
39use std::time::{Duration, Instant};
40use storage_device::{Device, DeviceHolder};
41
42pub const MIN_BLOCK_SIZE: u64 = 4096;
43pub const MAX_BLOCK_SIZE: u64 = u16::MAX as u64 + 1;
44
45pub const MAX_FILE_SIZE: u64 = i64::MAX as u64 - 4095;
50const_assert!(9223372036854771712 == MAX_FILE_SIZE);
51
52use futures::stream::StreamExt;
53
54const MAX_IN_FLIGHT_TRANSACTIONS: u64 = 4;
56
57const TRIM_AFTER_BOOT_TIMER: Duration = Duration::from_secs(60 * 60);
61
62const TRIM_INTERVAL_TIMER: Duration = Duration::from_secs(60 * 60 * 24);
64
65const CLEAN_TRANSFER_BUFFER_INTERVAL: Duration = Duration::from_secs(60);
68
69#[cfg(target_os = "fuchsia")]
70pub type WakeLease = zx::NullableHandle;
71
72#[cfg(not(target_os = "fuchsia"))]
73pub type WakeLease = fasync::emulated_handle::Handle;
74
75pub trait PowerManager: Send + Sync {
76 fn watch_battery(self: Arc<Self>) -> futures::stream::BoxStream<'static, (bool, WakeLease)>;
78}
79
80pub struct Info {
82 pub total_bytes: u64,
83 pub used_bytes: u64,
84}
85
86pub type PostCommitHook =
87 Option<Box<dyn Fn() -> futures::future::BoxFuture<'static, ()> + Send + Sync>>;
88
89pub struct Options {
90 pub read_only: bool,
92
93 pub roll_metadata_key_byte_count: u64,
97
98 pub hooks: Arc<HooksHandle>,
100
101 pub post_commit_hook: PostCommitHook,
104
105 pub skip_initial_reap: bool,
108
109 pub trim_config: Option<(Duration, Duration)>,
114
115 pub image_builder_mode: Option<SuperBlockInstance>,
118
119 pub inline_crypto_enabled: bool,
126
127 pub barriers_enabled: bool,
132
133 pub power_manager: Option<Arc<dyn PowerManager>>,
135
136 pub trim_charger_wait: Duration,
138
139 pub allow_type3_blobs: bool,
142}
143
144impl Default for Options {
145 fn default() -> Self {
146 Options {
147 roll_metadata_key_byte_count: 128 * 1024 * 1024,
148 read_only: false,
149 hooks: Arc::<HooksHandle>::default(),
150 post_commit_hook: None,
151 skip_initial_reap: false,
152 trim_config: Some((TRIM_AFTER_BOOT_TIMER, TRIM_INTERVAL_TIMER)),
153 image_builder_mode: None,
154 inline_crypto_enabled: false,
155 barriers_enabled: false,
156 power_manager: None,
157 trim_charger_wait: Duration::from_secs(10),
158 allow_type3_blobs: false,
159 }
160 }
161}
162
163pub struct ApplyContext<'a, 'b> {
165 pub mode: ApplyMode<'a, 'b>,
167
168 pub checkpoint: JournalCheckpoint,
170}
171
172pub enum ApplyMode<'a, 'b> {
175 Replay,
176 Live(&'a Transaction<'b>),
177}
178
179impl ApplyMode<'_, '_> {
180 pub fn is_replay(&self) -> bool {
181 matches!(self, ApplyMode::Replay)
182 }
183
184 pub fn is_live(&self) -> bool {
185 matches!(self, ApplyMode::Live(_))
186 }
187}
188
189#[async_trait]
192pub trait JournalingObject: Send + Sync {
193 fn apply_mutation(
197 &self,
198 mutation: Mutation,
199 context: &ApplyContext<'_, '_>,
200 assoc_obj: AssocObj<'_>,
201 ) -> Result<(), Error>;
202
203 fn drop_mutation(&self, mutation: Mutation, transaction: &Transaction<'_>);
205
206 async fn prepare_commit<'a>(
209 &self,
210 _filesystem: &'a FxFilesystem,
211 _transaction: &Transaction<'_>,
212 ) -> Result<Option<WriteGuard<'a>>, Error> {
213 Ok(None)
214 }
215
216 async fn flush(&self) -> Result<Version, Error>;
220
221 fn write_mutation(&self, mutation: &Mutation, mut writer: journal::Writer<'_>) {
224 writer.write(mutation.clone());
225 }
226}
227
228#[derive(Default)]
229pub struct SyncOptions<'a> {
230 pub flush_device: bool,
237
238 pub precondition: Option<Box<dyn FnOnce() -> bool + 'a + Send>>,
241}
242
243pub struct OpenFxFilesystem(Arc<FxFilesystem>);
244
245impl OpenFxFilesystem {
246 pub async fn take_device(self) -> DeviceHolder {
249 let fut = self.device.take_when_dropped();
250 std::mem::drop(self);
251 debug_assert_not_too_long!(fut)
252 }
253}
254
255impl From<Arc<FxFilesystem>> for OpenFxFilesystem {
256 fn from(fs: Arc<FxFilesystem>) -> Self {
257 Self(fs)
258 }
259}
260
261impl Drop for OpenFxFilesystem {
262 fn drop(&mut self) {
263 if self.options.image_builder_mode.is_some()
264 && self.journal().image_builder_mode().is_some()
265 {
266 error!("OpenFxFilesystem in image_builder_mode dropped without calling close().");
267 }
268 if !self.options.read_only && !self.closed.load(Ordering::SeqCst) {
269 error!("OpenFxFilesystem dropped without first being closed. Data loss may occur.");
270 }
271 }
272}
273
274impl std::ops::Deref for OpenFxFilesystem {
275 type Target = Arc<FxFilesystem>;
276
277 fn deref(&self) -> &Self::Target {
278 &self.0
279 }
280}
281
282pub struct FxFilesystemBuilder {
283 format: bool,
284 trace: bool,
285 options: Options,
286 journal_options: JournalOptions,
287 on_new_allocator: Option<Box<dyn Fn(Arc<Allocator>) + Send + Sync>>,
288 on_new_store: Option<Box<dyn Fn(&ObjectStore) + Send + Sync>>,
289 fsck_after_every_transaction: bool,
290}
291
292impl FxFilesystemBuilder {
293 pub fn new() -> Self {
294 Self {
295 format: false,
296 trace: false,
297 options: Options::default(),
298 journal_options: JournalOptions::default(),
299 on_new_allocator: None,
300 on_new_store: None,
301 fsck_after_every_transaction: false,
302 }
303 }
304
305 pub fn format(mut self, format: bool) -> Self {
307 self.format = format;
308 self
309 }
310
311 pub fn trace(mut self, trace: bool) -> Self {
313 self.trace = trace;
314 self
315 }
316
317 pub fn read_only(mut self, read_only: bool) -> Self {
320 self.options.read_only = read_only;
321 self
322 }
323
324 pub fn allow_type3_blobs(mut self, allow: bool) -> Self {
326 self.options.allow_type3_blobs = allow;
327 self
328 }
329
330 pub fn image_builder_mode(mut self, mode: Option<SuperBlockInstance>) -> Self {
337 self.options.image_builder_mode = mode;
338 self
339 }
340
341 pub fn roll_metadata_key_byte_count(mut self, roll_metadata_key_byte_count: u64) -> Self {
343 self.options.roll_metadata_key_byte_count = roll_metadata_key_byte_count;
344 self
345 }
346
347 pub fn post_commit_hook(
350 mut self,
351 hook: impl Fn() -> futures::future::BoxFuture<'static, ()> + Send + Sync + 'static,
352 ) -> Self {
353 self.options.post_commit_hook = Some(Box::new(hook));
354 self
355 }
356
357 pub fn skip_initial_reap(mut self, skip_initial_reap: bool) -> Self {
360 self.options.skip_initial_reap = skip_initial_reap;
361 self
362 }
363
364 pub fn journal_options(mut self, journal_options: JournalOptions) -> Self {
366 self.journal_options = journal_options;
367 self
368 }
369
370 pub fn on_new_allocator(
372 mut self,
373 on_new_allocator: impl Fn(Arc<Allocator>) + Send + Sync + 'static,
374 ) -> Self {
375 self.on_new_allocator = Some(Box::new(on_new_allocator));
376 self
377 }
378
379 pub fn on_new_store(
381 mut self,
382 on_new_store: impl Fn(&ObjectStore) + Send + Sync + 'static,
383 ) -> Self {
384 self.on_new_store = Some(Box::new(on_new_store));
385 self
386 }
387
388 pub fn fsck_after_every_transaction(mut self, fsck_after_every_transaction: bool) -> Self {
390 self.fsck_after_every_transaction = fsck_after_every_transaction;
391 self
392 }
393
394 pub fn trim_config(mut self, delay_and_interval: Option<(Duration, Duration)>) -> Self {
395 self.options.trim_config = delay_and_interval;
396 self
397 }
398
399 pub fn power_manager(mut self, power_manager: Arc<dyn PowerManager>) -> Self {
400 self.options.power_manager = Some(power_manager);
401 self
402 }
403
404 pub fn trim_charger_wait(mut self, wait: Duration) -> Self {
405 self.options.trim_charger_wait = wait;
406 self
407 }
408
409 pub fn inline_crypto_enabled(mut self, inline_crypto_enabled: bool) -> Self {
411 self.options.inline_crypto_enabled = inline_crypto_enabled;
412 self
413 }
414
415 pub fn barriers_enabled(mut self, barriers_enabled: bool) -> Self {
418 self.options.barriers_enabled = barriers_enabled;
419 self.journal_options.barriers_enabled = barriers_enabled;
420 self
421 }
422
423 pub fn hooks(mut self, hooks: Arc<crate::hooks::HooksHandle>) -> Self {
424 self.options.hooks = hooks;
425 self
426 }
427
428 pub async fn open(self, device: DeviceHolder) -> Result<OpenFxFilesystem, Error> {
430 let read_only = self.options.read_only;
431 if self.format && read_only {
432 bail!("Cannot initialize a filesystem as read-only");
433 }
434
435 if self.options.inline_crypto_enabled && !self.options.barriers_enabled {
437 bail!("A filesystem using inline encryption requires barriers");
438 }
439
440 let objects = Arc::new(ObjectManager::new(self.on_new_store));
441 let journal = Arc::new(Journal::new(objects.clone(), self.journal_options));
442
443 let image_builder_mode = self.options.image_builder_mode;
444
445 let block_size = std::cmp::max(device.block_size().into(), MIN_BLOCK_SIZE);
446 assert_eq!(block_size % MIN_BLOCK_SIZE, 0);
447 assert!(block_size <= MAX_BLOCK_SIZE, "Max supported block size is 64KiB");
448
449 let mut fsck_after_every_transaction = None;
450 let mut filesystem_options = self.options;
451 if self.fsck_after_every_transaction {
452 let instance =
453 FsckAfterEveryTransaction::new(filesystem_options.post_commit_hook.take());
454 fsck_after_every_transaction = Some(instance.clone());
455 filesystem_options.post_commit_hook =
456 Some(Box::new(move || Box::pin(instance.clone().run())));
457 }
458
459 if !read_only && !self.format {
460 device.flush().await.context("Device flush failed")?;
463 }
464
465 let filesystem = Arc::new_cyclic(|weak: &Weak<FxFilesystem>| {
466 let weak = weak.clone();
467 FxFilesystem {
468 device,
469 block_size,
470 objects: objects.clone(),
471 journal,
472 commit_mutex: futures::lock::Mutex::new(()),
473 lock_manager: LockManager::new(),
474 flush_task: Mutex::new(None),
475 background_tasks: fasync::Scope::new(),
476 closed: AtomicBool::new(true),
477 trace: self.trace,
478 graveyard: Graveyard::new(objects.clone()),
479 completed_transactions: metrics::detail().create_uint("completed_transactions", 0),
480 options: filesystem_options,
481 in_flight_transactions: AtomicU64::new(0),
482 transaction_limit_event: Event::new(),
483 _stores_node: metrics::register_fs(move || {
484 let weak = weak.clone();
485 Box::pin(async move {
486 if let Some(fs) = weak.upgrade() {
487 fs.populate_stores_node().await
488 } else {
489 Err(anyhow!("Filesystem has been dropped"))
490 }
491 })
492 }),
493 }
494 });
495
496 filesystem.journal().set_image_builder_mode(image_builder_mode);
497
498 filesystem.journal.set_trace(self.trace);
499 if self.format {
500 filesystem.journal.init_empty(filesystem.clone()).await?;
501 if image_builder_mode.is_none() {
502 filesystem.journal.init_superblocks().await?;
505
506 filesystem.graveyard.clone().reap_async();
508 }
509
510 let root_store = filesystem.root_store();
512 root_store.set_trace(self.trace);
513 let root_directory =
514 Directory::open(&root_store, root_store.root_directory_object_id())
515 .await
516 .context("Unable to open root volume directory")?;
517 let mut transaction = root_store
518 .new_transaction(
519 lock_keys![LockKey::object(
520 root_store.store_object_id(),
521 root_directory.object_id()
522 )],
523 transaction::Options::default(),
524 )
525 .await?;
526 let volume_directory =
527 root_directory.create_child_dir(&mut transaction, VOLUMES_DIRECTORY).await?;
528 transaction.commit().await?;
529 objects.set_volume_directory(volume_directory);
530 } else {
531 filesystem
532 .journal
533 .replay(filesystem.clone(), self.on_new_allocator)
534 .await
535 .context("Journal replay failed")?;
536 filesystem.root_store().set_trace(self.trace);
537
538 if !read_only {
539 for store in objects.unlocked_stores() {
543 filesystem.graveyard.initial_reap(&store).await?;
544 }
545 }
546 }
547
548 if let Some(fsck_after_every_transaction) = fsck_after_every_transaction {
550 fsck_after_every_transaction
551 .fs
552 .set(Arc::downgrade(&filesystem))
553 .unwrap_or_else(|_| unreachable!());
554 }
555
556 filesystem.closed.store(false, Ordering::SeqCst);
557
558 if !read_only && image_builder_mode.is_none() {
559 filesystem.graveyard.clone().reap_async();
561
562 if filesystem.options.trim_config.is_some() {
563 filesystem.start_trim_task();
564 }
565 filesystem.start_clean_transfer_buffer_task();
566 }
567
568 Ok(filesystem.into())
569 }
570}
571
572pub struct FxFilesystem {
573 block_size: u64,
574 objects: Arc<ObjectManager>,
575 journal: Arc<Journal>,
576 commit_mutex: futures::lock::Mutex<()>,
577 lock_manager: LockManager,
578 flush_task: Mutex<Option<fasync::Task<()>>>,
579 background_tasks: fasync::Scope,
580 closed: AtomicBool,
581 trace: bool,
583 graveyard: Arc<Graveyard>,
584 completed_transactions: UintProperty,
585 options: Options,
586
587 in_flight_transactions: AtomicU64,
589
590 transaction_limit_event: Event,
593
594 device: DeviceHolder,
597
598 _stores_node: LazyNode,
600}
601
602#[fxfs_trace::trace]
603impl FxFilesystem {
604 pub async fn new_empty(device: DeviceHolder) -> Result<OpenFxFilesystem, Error> {
605 FxFilesystemBuilder::new().format(true).open(device).await
606 }
607
608 pub async fn open(device: DeviceHolder) -> Result<OpenFxFilesystem, Error> {
609 FxFilesystemBuilder::new().open(device).await
610 }
611
612 pub fn root_parent_store(&self) -> Arc<ObjectStore> {
613 self.objects.root_parent_store()
614 }
615
616 pub async fn close(&self) -> Result<(), Error> {
617 if self.journal().image_builder_mode().is_some() {
618 self.journal().allocate_journal().await?;
619 self.journal().set_image_builder_mode(None);
620 self.journal().force_compact().await?;
621 }
622 assert_eq!(self.closed.swap(true, Ordering::SeqCst), false);
623 debug_assert_not_too_long!(self.graveyard.wait_for_reap());
624 debug_assert_not_too_long!(self.background_tasks.clone().cancel());
625 self.journal.stop_compactions().await;
626 let sync_status =
627 if self.journal().image_builder_mode().is_some() || self.options().read_only {
628 Ok(None)
629 } else {
630 self.journal.sync(SyncOptions { flush_device: true, ..Default::default() }).await
631 };
632 match &sync_status {
633 Ok(None) => {}
634 Ok(checkpoint) => info!(
635 "Filesystem closed (checkpoint={}, metadata_reservation={:?}, \
636 reservation_required={}, borrowed={})",
637 checkpoint.as_ref().unwrap().0.file_offset,
638 self.object_manager().metadata_reservation(),
639 self.object_manager().required_reservation(),
640 self.object_manager().borrowed_metadata_space(),
641 ),
642 Err(e) => error!(error:? = e; "Failed to sync filesystem; data may be lost"),
643 }
644 self.journal.terminate();
645 let flush_task = self.flush_task.lock().take();
646 if let Some(task) = flush_task {
647 debug_assert_not_too_long!(task);
648 }
649 self.device().close().await.context("Failed to close device")?;
652 sync_status.map(|_| ())
653 }
654
655 pub fn device(&self) -> Arc<dyn Device> {
656 Arc::clone(&self.device)
657 }
658
659 pub fn root_store(&self) -> Arc<ObjectStore> {
660 self.objects.root_store()
661 }
662
663 pub fn allocator(&self) -> Arc<Allocator> {
664 self.objects.allocator()
665 }
666
667 pub fn enable_allocations(&self) {
671 self.allocator().enable_allocations();
672 }
673
674 pub fn object_manager(&self) -> &Arc<ObjectManager> {
675 &self.objects
676 }
677
678 pub fn journal(&self) -> &Arc<Journal> {
679 &self.journal
680 }
681
682 pub async fn sync(&self, options: SyncOptions<'_>) -> Result<(), Error> {
683 self.journal.sync(options).await.map(|_| ())
684 }
685
686 pub fn block_size(&self) -> u64 {
687 self.block_size
688 }
689
690 pub fn get_info(&self) -> Info {
691 Info {
692 total_bytes: self.device.size(),
693 used_bytes: self.object_manager().allocator().get_used_bytes().0,
694 }
695 }
696
697 pub fn super_block_header(&self) -> SuperBlockHeader {
698 self.journal.super_block_header()
699 }
700
701 pub fn graveyard(&self) -> &Arc<Graveyard> {
702 &self.graveyard
703 }
704
705 pub fn trace(&self) -> bool {
706 self.trace
707 }
708
709 pub fn options(&self) -> &Options {
710 &self.options
711 }
712
713 pub fn scope(&self) -> &fasync::Scope {
714 &self.background_tasks
715 }
716
717 pub async fn lock_commits(&self) -> futures::lock::MutexGuard<'_, ()> {
724 self.commit_mutex.lock().await
725 }
726
727 #[trace]
728 pub async fn commit_transaction<R: Send>(
729 &self,
730 transaction: &mut Transaction<'_>,
731 callback: impl FnOnce(u64) -> R + Send,
732 ) -> Result<R, Error> {
733 self.hooks().on_pre_commit(transaction)?;
734 debug_assert_not_too_long!(self.lock_manager.commit_prepare(&transaction));
735
736 let mut guards = Vec::new();
741 let mut last_object_id = 0;
742 for mutation in transaction.mutations() {
743 let object_id = mutation.object_id;
744
745 if matches!(mutation.mutation, Mutation::BeginFlush | Mutation::EndFlush) {
747 continue;
748 }
749
750 if object_id == last_object_id {
751 continue;
752 }
753 assert!(object_id > last_object_id);
754 last_object_id = object_id;
755
756 if let Some(obj) = self.object_manager().journaling_object(object_id) {
757 if let Some(guard) = obj.prepare_commit(self, transaction).await? {
758 guards.push(guard);
759 }
760 }
761 }
762
763 self.maybe_start_flush_task();
764
765 self.hooks().on_before_commit();
766
767 let _guard = debug_assert_not_too_long!(self.commit_mutex.lock());
768 let journal_offset = if self.journal().image_builder_mode().is_some() {
769 let journal_checkpoint =
770 JournalCheckpoint { file_offset: 0, checksum: 0, version: LATEST_VERSION };
771 let maybe_mutation = self
772 .object_manager()
773 .apply_transaction(transaction, &journal_checkpoint)
774 .expect("Transactions must not fail in image_builder_mode");
775 if let Some(mutation) = maybe_mutation {
776 assert!(matches!(mutation, Mutation::UpdateBorrowed(_)));
777 }
781 self.object_manager().did_commit_transaction(transaction, &journal_checkpoint, 0);
782 0
783 } else {
784 self.journal.commit(transaction).await?
785 };
786
787 std::mem::drop(guards);
788 self.completed_transactions.add(1);
789
790 let result = callback(journal_offset);
795
796 if let Some(hook) = self.options.post_commit_hook.as_ref() {
797 hook().await;
798 }
799
800 Ok(result)
801 }
802
803 pub fn lock_manager(&self) -> &LockManager {
804 &self.lock_manager
805 }
806
807 pub fn hooks(&self) -> &Arc<HooksHandle> {
808 &self.options.hooks
809 }
810
811 pub(crate) fn drop_transaction(&self, transaction: &mut Transaction<'_>) {
812 if !matches!(transaction.metadata_reservation, MetadataReservation::None) {
813 self.sub_transaction();
814 }
815 if let MetadataReservation::Hold(hold_amount) =
817 std::mem::replace(&mut transaction.metadata_reservation, MetadataReservation::None)
818 {
819 let hold = transaction
820 .allocator_reservation
821 .unwrap()
822 .reserve(0)
823 .expect("Zero should always succeed.");
824 hold.add(hold_amount);
825 }
826 self.objects.drop_transaction(transaction);
827 self.lock_manager.drop_transaction(transaction);
828 }
829
830 fn maybe_start_flush_task(&self) {
831 if self.journal.image_builder_mode().is_some() {
832 return;
833 }
834 let mut flush_task = self.flush_task.lock();
835 if flush_task.is_none() {
836 let journal = self.journal.clone();
837 *flush_task = Some(fasync::Task::spawn(
838 journal.flush_task().trace(trace_future_args!("Journal::flush_task")),
839 ));
840 }
841 }
842
843 fn start_trim_task(self: &Arc<Self>) {
844 if !self.device.supports_trim() {
845 info!("Device does not support trim; not scheduling trimming");
846 return;
847 }
848 let this = self.clone();
849 self.background_tasks
850 .spawn(this.trim_task().trace(trace_future_args!("Filesystem::trim_task")));
851 }
852
853 async fn trim_task(self: Arc<Self>) {
854 let Some((mut next_timer, _)) = self.options.trim_config else { return };
856 loop {
857 fasync::Timer::new(next_timer.clone()).await;
858
859 let start = Instant::now();
862 let result = if let Some(pm) = &self.options.power_manager {
863 let mut watcher = pm.clone().watch_battery();
864
865 let pauser = Pauser::new(self.options.trim_charger_wait);
867
868 let mut pause_future = pin!(
869 async {
870 let mut wake_lease = WakeLease::invalid();
871 loop {
872 let Some((using_battery, new_lease)) = watcher.next_latest().await
873 else {
874 pauser.set_pause(false);
877 drop(wake_lease); return;
879 };
880
881 pauser.set_pause(using_battery);
883
884 if using_battery {
887 wake_lease = WakeLease::invalid();
888 } else if !new_lease.is_invalid() {
889 wake_lease = new_lease;
890 }
891 }
892 }
893 .fuse()
894 );
895
896 let mut do_trim = pin!(self.do_trim(Some(&pauser)).fuse());
897
898 loop {
899 futures::select! {
900 _ = pause_future => {}
901 result = do_trim => break result,
902 }
903 }
904
905 } else {
908 self.do_trim(None).await
909 };
910
911 let duration = start.elapsed();
912 match result {
913 Ok(bytes_trimmed) => info!(
914 "Trimmed {bytes_trimmed} bytes in {duration:?}. Next trim in \
915 {next_timer:?}",
916 ),
917 Err(error) => error!(error:?; "Failed to trim"),
918 }
919
920 let Some((_, interval)) = self.options.trim_config else { return };
921 next_timer = interval;
922 if next_timer.is_zero() {
923 fasync::yield_now().await;
924 }
925 }
926 }
927
928 async fn do_trim(&self, pauser: Option<&Pauser>) -> Result<usize, Error> {
930 const MAX_EXTENTS_PER_BATCH: usize = 8;
931 const MAX_EXTENT_SIZE: usize = 256 * 1024;
932 let mut offset = 0;
933 let mut bytes_trimmed = 0;
934 loop {
935 let allocator = self.allocator();
936 if let Some(pauser) = pauser {
937 pauser.maybe_pause().await;
938 }
939 let trimmable_extents =
940 allocator.take_for_trimming(offset, MAX_EXTENT_SIZE, MAX_EXTENTS_PER_BATCH).await?;
941 for device_range in trimmable_extents.extents() {
942 self.device.trim(device_range.clone()).await?;
943 bytes_trimmed += device_range.length()? as usize;
944 }
945 if let Some(device_range) = trimmable_extents.extents().last() {
946 offset = device_range.end;
947 } else {
948 break;
949 }
950 }
951 Ok(bytes_trimmed)
952 }
953
954 fn start_clean_transfer_buffer_task(self: &Arc<Self>) {
955 let this = self.clone();
956 self.background_tasks.spawn(
957 async move {
958 loop {
959 fasync::Timer::new(CLEAN_TRANSFER_BUFFER_INTERVAL).await;
960 this.device().clean_transfer_buffer();
961 }
962 }
963 .trace(trace_future_args!("Filesystem::clean_transfer_buffer_task")),
964 );
965 }
966
967 pub(crate) async fn reservation_for_transaction<'a>(
968 self: &Arc<Self>,
969 options: transaction::Options<'a>,
970 ) -> Result<(MetadataReservation, Option<&'a Reservation>, Option<Hold<'a>>), Error> {
971 if self.options.image_builder_mode.is_some() {
972 return Ok((MetadataReservation::Borrowed, None, None));
975 }
976 if !options.skip_journal_checks {
977 self.maybe_start_flush_task();
978 self.journal.check_journal_space().await?;
979 }
980
981 let mut hold = None;
995 let metadata_reservation = if options.borrow_metadata_space {
996 MetadataReservation::Borrowed
997 } else {
998 match options.allocator_reservation {
999 Some(reservation) => {
1000 hold = Some(
1001 reservation
1002 .reserve(TRANSACTION_METADATA_MAX_AMOUNT)
1003 .ok_or(FxfsError::NoSpace)?,
1004 );
1005 MetadataReservation::Hold(TRANSACTION_METADATA_MAX_AMOUNT)
1006 }
1007 None => {
1008 let reservation = self
1009 .allocator()
1010 .reserve(None, TRANSACTION_METADATA_MAX_AMOUNT)
1011 .ok_or(FxfsError::NoSpace)?;
1012 MetadataReservation::Reservation(reservation)
1013 }
1014 }
1015 };
1016 Ok((metadata_reservation, options.allocator_reservation, hold))
1017 }
1018
1019 pub(crate) async fn add_transaction(&self, skip_journal_checks: bool) {
1020 if skip_journal_checks {
1021 self.in_flight_transactions.fetch_add(1, Ordering::Relaxed);
1022 } else {
1023 let inc = || {
1024 let mut in_flights = self.in_flight_transactions.load(Ordering::Relaxed);
1025 while in_flights < MAX_IN_FLIGHT_TRANSACTIONS {
1026 match self.in_flight_transactions.compare_exchange_weak(
1027 in_flights,
1028 in_flights + 1,
1029 Ordering::Relaxed,
1030 Ordering::Relaxed,
1031 ) {
1032 Ok(_) => return true,
1033 Err(x) => in_flights = x,
1034 }
1035 }
1036 return false;
1037 };
1038 while !inc() {
1039 let listener = self.transaction_limit_event.listen();
1040 if inc() {
1041 break;
1042 }
1043 listener.await;
1044 }
1045 }
1046 }
1047
1048 pub(crate) fn sub_transaction(&self) {
1049 let old = self.in_flight_transactions.fetch_sub(1, Ordering::Relaxed);
1050 assert!(old != 0);
1051 if old <= MAX_IN_FLIGHT_TRANSACTIONS {
1052 self.transaction_limit_event.notify(usize::MAX);
1053 }
1054 }
1055
1056 pub async fn truncate_guard(&self, store_id: u64, object_id: u64) -> TruncateGuard<'_> {
1057 let keys = lock_keys![LockKey::truncate(store_id, object_id,)];
1058 TruncateGuard(self.lock_manager().write_lock(keys).await)
1059 }
1060
1061 async fn populate_stores_node(&self) -> Result<Inspector, Error> {
1062 let inspector = fuchsia_inspect::Inspector::default();
1063 let root = inspector.root();
1064 root.record_child("__root", |n| self.root_store().record_data(n));
1065 root.record_child("__root_parent", |n| self.root_parent_store().record_data(n));
1066 let object_manager = self.object_manager();
1067 let volume_directory = object_manager.volume_directory();
1068 let layer_set = volume_directory.store().tree().layer_set();
1069 let mut merger = layer_set.merger();
1070 let mut iter = volume_directory.iter(&mut merger).await?;
1071 while let Some((name, id, _)) = iter.get() {
1072 if let Some(store) = object_manager.store(id) {
1073 root.record_child(name.to_string(), |n| store.record_data(n));
1074 }
1075 iter.advance().await?;
1076 }
1077 Ok(inspector)
1078 }
1079}
1080
1081#[allow(dead_code)]
1083pub struct TruncateGuard<'a>(WriteGuard<'a>);
1084
1085pub async fn mkfs(device: DeviceHolder) -> Result<DeviceHolder, Error> {
1087 let fs = FxFilesystem::new_empty(device).await?;
1088 fs.close().await?;
1089 Ok(fs.take_device().await)
1090}
1091
1092pub async fn mkfs_with_volume(
1096 device: DeviceHolder,
1097 volume_name: &str,
1098 crypt: Option<Arc<dyn Crypt>>,
1099) -> Result<DeviceHolder, Error> {
1100 let fs = FxFilesystem::new_empty(device).await?;
1101 {
1102 let root_volume = root_volume(fs.clone()).await.expect("Open root_volume failed");
1105 root_volume
1106 .new_volume(
1107 volume_name,
1108 NewChildStoreOptions {
1109 options: StoreOptions { crypt, ..StoreOptions::default() },
1110 ..Default::default()
1111 },
1112 )
1113 .await
1114 .expect("Create volume failed");
1115 }
1116 fs.close().await?;
1117 Ok(fs.take_device().await)
1118}
1119
1120struct FsckAfterEveryTransaction {
1121 fs: OnceLock<Weak<FxFilesystem>>,
1122 old_hook: PostCommitHook,
1123}
1124
1125impl FsckAfterEveryTransaction {
1126 fn new(old_hook: PostCommitHook) -> Arc<Self> {
1127 Arc::new(Self { fs: OnceLock::new(), old_hook })
1128 }
1129
1130 async fn run(self: Arc<Self>) {
1131 if let Some(fs) = self.fs.get().and_then(Weak::upgrade) {
1132 let options = FsckOptions {
1133 fail_on_warning: true,
1134 no_lock: true,
1135 quiet: true,
1136 ..Default::default()
1137 };
1138 fsck_with_options(fs.clone(), &options).await.expect("fsck failed");
1139 let object_manager = fs.object_manager();
1140 for store in object_manager.unlocked_stores() {
1141 let store_id = store.store_object_id();
1142 if !object_manager.is_system_store(store_id) {
1143 fsck_volume_with_options(fs.as_ref(), &options, store_id, None)
1144 .await
1145 .expect("fsck_volume_with_options failed");
1146 }
1147 }
1148 }
1149 if let Some(old_hook) = self.old_hook.as_ref() {
1150 old_hook().await;
1151 }
1152 }
1153}
1154
1155struct Pauser {
1156 pause: Condition<bool>,
1157 bounce_delay: Duration,
1158}
1159
1160impl Pauser {
1161 fn new(bounce_delay: Duration) -> Self {
1163 Self { pause: Condition::new(true), bounce_delay }
1164 }
1165
1166 async fn maybe_pause(&self) {
1167 loop {
1168 if !*self.pause.lock() {
1169 return;
1170 }
1171 self.pause.when(|p| if **p { Poll::Pending } else { Poll::Ready(()) }).await;
1172 fasync::Timer::new(self.bounce_delay).await;
1173 }
1174 }
1175
1176 fn set_pause(&self, v: bool) {
1177 let mut guard = self.pause.lock();
1178 if *guard == v {
1179 return;
1180 }
1181 *guard = v;
1182 for waker in guard.drain_wakers() {
1183 waker.wake();
1184 }
1185 }
1186}
1187
1188trait NextLatest: Stream + Unpin {
1189 async fn next_latest(&mut self) -> Option<Self::Item> {
1191 let Some(mut next) = self.next().await else { return None };
1192
1193 loop {
1195 match self.next().now_or_never() {
1196 None => return Some(next),
1197 Some(None) => return None,
1198 Some(Some(n)) => next = n,
1199 }
1200 }
1201 }
1202}
1203
1204impl<T: ?Sized + Unpin> NextLatest for T where T: Stream {}
1205
1206#[cfg(test)]
1207mod tests {
1208 use super::{FxFilesystem, FxFilesystemBuilder, FxfsError, SyncOptions};
1209 use crate::fsck::{fsck, fsck_volume};
1210 use crate::log::*;
1211 use crate::lsm_tree::Operation;
1212 use crate::lsm_tree::types::Item;
1213 use crate::object_handle::{
1214 INVALID_OBJECT_ID, ObjectHandle, ReadObjectHandle, WriteObjectHandle,
1215 };
1216 use crate::object_store::directory::{Directory, replace_child};
1217 use crate::object_store::journal::JournalOptions;
1218 use crate::object_store::journal::super_block::SuperBlockInstance;
1219 use crate::object_store::transaction::{LockKey, Options, lock_keys};
1220 use crate::object_store::volume::root_volume;
1221 use crate::object_store::{
1222 HandleOptions, NewChildStoreOptions, ObjectDescriptor, ObjectStore, StoreOptions,
1223 };
1224 use crate::range::RangeExt;
1225 use fuchsia_async as fasync;
1226 use fuchsia_sync::Mutex;
1227 use futures::future::join_all;
1228 use futures::stream::{FuturesUnordered, TryStreamExt};
1229 use fxfs_insecure_crypto::new_insecure_crypt;
1230 use rustc_hash::FxHashMap as HashMap;
1231 use std::ops::Range;
1232 use std::sync::Arc;
1233 use std::sync::atomic::{AtomicU32, Ordering};
1234 use std::time::Duration;
1235 use storage_device::DeviceHolder;
1236 use storage_device::fake_device::{self, FakeDevice};
1237 use test_case::test_case;
1238
1239 const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
1240
1241 #[fuchsia::test(threads = 10)]
1242 async fn test_compaction() {
1243 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1244
1245 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1247 let root_store = fs.root_store();
1248 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
1249 .await
1250 .expect("open failed");
1251
1252 let mut tasks = Vec::new();
1253 for i in 0..2 {
1254 let mut transaction = fs
1255 .root_store()
1256 .new_transaction(
1257 lock_keys![LockKey::object(
1258 root_store.store_object_id(),
1259 root_directory.object_id()
1260 )],
1261 Options::default(),
1262 )
1263 .await
1264 .expect("new_transaction failed");
1265 let handle = root_directory
1266 .create_child_file(&mut transaction, &format!("{}", i))
1267 .await
1268 .expect("create_child_file failed");
1269 transaction.commit().await.expect("commit failed");
1270 tasks.push(fasync::Task::spawn(async move {
1271 const TEST_DATA: &[u8] = b"hello";
1272 let mut buf = handle.allocate_buffer(TEST_DATA.len()).await;
1273 buf.copy_from_slice(TEST_DATA);
1274 for _ in 0..1500 {
1275 handle.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
1276 }
1277 }));
1278 }
1279 join_all(tasks).await;
1280 fs.sync(SyncOptions::default()).await.expect("sync failed");
1281
1282 fsck(fs.clone()).await.expect("fsck failed");
1283 fs.close().await.expect("Close failed");
1284 }
1285
1286 #[fuchsia::test]
1287 async fn test_enable_allocations() {
1288 {
1290 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1291 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1292 fs.enable_allocations();
1293 let root_store = fs.root_store();
1294 let root_directory =
1295 Directory::open(&root_store, root_store.root_directory_object_id())
1296 .await
1297 .expect("open failed");
1298 let mut transaction = fs
1299 .root_store()
1300 .new_transaction(
1301 lock_keys![LockKey::object(
1302 root_store.store_object_id(),
1303 root_directory.object_id()
1304 )],
1305 Options::default(),
1306 )
1307 .await
1308 .expect("new_transaction failed");
1309 root_directory
1310 .create_child_file(&mut transaction, "test")
1311 .await
1312 .expect("create_child_file failed");
1313 transaction.commit().await.expect("commit failed");
1314 fs.close().await.expect("close failed");
1315 }
1316
1317 {
1319 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1320 let fs = FxFilesystemBuilder::new()
1321 .format(true)
1322 .image_builder_mode(Some(SuperBlockInstance::A))
1323 .open(device)
1324 .await
1325 .expect("open failed");
1326 let root_store = fs.root_store();
1327 let root_directory =
1328 Directory::open(&root_store, root_store.root_directory_object_id())
1329 .await
1330 .expect("open failed");
1331
1332 let mut transaction = fs
1333 .root_store()
1334 .new_transaction(
1335 lock_keys![LockKey::object(
1336 root_store.store_object_id(),
1337 root_directory.object_id()
1338 )],
1339 Options::default(),
1340 )
1341 .await
1342 .expect("new_transaction failed");
1343 let handle = root_directory
1344 .create_child_file(&mut transaction, "test_fail")
1345 .await
1346 .expect("create_child_file failed");
1347 transaction.commit().await.expect("commit failed");
1348
1349 assert!(
1351 FxfsError::Unavailable
1352 .matches(&handle.allocate(0..4096).await.expect_err("allocate should fail"))
1353 );
1354
1355 fs.enable_allocations();
1357 handle.allocate(0..4096).await.expect("allocate should work after enable_allocations");
1358
1359 fs.close().await.expect("close failed");
1363 }
1364 }
1368
1369 #[fuchsia::test(threads = 10)]
1370 async fn test_replay_is_identical() {
1371 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1372 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1373
1374 fs.close().await.expect("close failed");
1377 let device = fs.take_device().await;
1378 device.reopen(false);
1379
1380 struct Mutations<K, V>(Mutex<Vec<(Operation, Item<K, V>)>>);
1381
1382 impl<K: Clone, V: Clone> Mutations<K, V> {
1383 fn new() -> Self {
1384 Mutations(Mutex::new(Vec::new()))
1385 }
1386
1387 fn push(&self, operation: Operation, item: &Item<K, V>) {
1388 self.0.lock().push((operation, item.clone()));
1389 }
1390 }
1391
1392 let open_fs = |device,
1393 object_mutations: Arc<Mutex<HashMap<_, _>>>,
1394 allocator_mutations: Arc<Mutations<_, _>>| async {
1395 FxFilesystemBuilder::new()
1396 .journal_options(JournalOptions { reclaim_size: u64::MAX, ..Default::default() })
1397 .on_new_allocator(move |allocator| {
1398 let allocator_mutations = allocator_mutations.clone();
1399 allocator.tree().set_mutation_callback(Some(Box::new(move |op, item| {
1400 allocator_mutations.push(op, item)
1401 })));
1402 })
1403 .on_new_store(move |store| {
1404 let mutations = Arc::new(Mutations::new());
1405 object_mutations.lock().insert(store.store_object_id(), mutations.clone());
1406 store.tree().set_mutation_callback(Some(Box::new(move |op, item| {
1407 mutations.push(op, item)
1408 })));
1409 })
1410 .open(device)
1411 .await
1412 .expect("open failed")
1413 };
1414
1415 let allocator_mutations = Arc::new(Mutations::new());
1416 let object_mutations = Arc::new(Mutex::new(HashMap::default()));
1417 let fs = open_fs(device, object_mutations.clone(), allocator_mutations.clone()).await;
1418
1419 let root_store = fs.root_store();
1420 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
1421 .await
1422 .expect("open failed");
1423
1424 let mut transaction = fs
1425 .root_store()
1426 .new_transaction(
1427 lock_keys![LockKey::object(
1428 root_store.store_object_id(),
1429 root_directory.object_id()
1430 )],
1431 Options::default(),
1432 )
1433 .await
1434 .expect("new_transaction failed");
1435 let object = root_directory
1436 .create_child_file(&mut transaction, "test")
1437 .await
1438 .expect("create_child_file failed");
1439 transaction.commit().await.expect("commit failed");
1440
1441 let buf = object.allocate_buffer(10000).await;
1443 object.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
1444
1445 object.write_or_append(Some(5000), buf.as_ref()).await.expect("write failed");
1447
1448 object.truncate(3000).await.expect("truncate failed");
1450
1451 let mut transaction = fs
1453 .root_store()
1454 .new_transaction(
1455 lock_keys![
1456 LockKey::object(root_store.store_object_id(), root_directory.object_id()),
1457 LockKey::object(root_store.store_object_id(), object.object_id()),
1458 ],
1459 Options::default(),
1460 )
1461 .await
1462 .expect("new_transaction failed");
1463
1464 replace_child(&mut transaction, None, (&root_directory, "test"))
1465 .await
1466 .expect("replace_child failed");
1467
1468 transaction.commit().await.expect("commit failed");
1469
1470 root_store
1472 .tombstone_object(object.object_id(), Options::default())
1473 .await
1474 .expect("tombstone failed");
1475
1476 fs.close().await.expect("close failed");
1478
1479 let metadata_reservation_amount = fs.object_manager().metadata_reservation().amount();
1480
1481 let device = fs.take_device().await;
1482 device.reopen(false);
1483
1484 let replayed_object_mutations = Arc::new(Mutex::new(HashMap::default()));
1485 let replayed_allocator_mutations = Arc::new(Mutations::new());
1486 let fs = open_fs(
1487 device,
1488 replayed_object_mutations.clone(),
1489 replayed_allocator_mutations.clone(),
1490 )
1491 .await;
1492
1493 let m1 = object_mutations.lock();
1494 let m2 = replayed_object_mutations.lock();
1495 assert_eq!(m1.len(), m2.len());
1496 for (store_id, mutations) in &*m1 {
1497 let mutations = mutations.0.lock();
1498 let replayed = m2.get(&store_id).expect("Found unexpected store").0.lock();
1499 assert_eq!(mutations.len(), replayed.len());
1500 for ((op1, i1), (op2, i2)) in mutations.iter().zip(replayed.iter()) {
1501 assert_eq!(op1, op2);
1502 assert_eq!(i1.key, i2.key);
1503 assert_eq!(i1.value, i2.value);
1504 }
1505 }
1506
1507 let a1 = allocator_mutations.0.lock();
1508 let a2 = replayed_allocator_mutations.0.lock();
1509 assert_eq!(a1.len(), a2.len());
1510 for ((op1, i1), (op2, i2)) in a1.iter().zip(a2.iter()) {
1511 assert_eq!(op1, op2);
1512 assert_eq!(i1.key, i2.key);
1513 assert_eq!(i1.value, i2.value);
1514 }
1515
1516 assert_eq!(
1517 fs.object_manager().metadata_reservation().amount(),
1518 metadata_reservation_amount
1519 );
1520 }
1521
1522 #[fuchsia::test]
1523 async fn test_max_in_flight_transactions() {
1524 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1525 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1526
1527 let store = fs.root_store();
1528 let transactions = FuturesUnordered::new();
1529 for _ in 0..super::MAX_IN_FLIGHT_TRANSACTIONS {
1530 transactions.push(store.new_transaction(lock_keys![], Options::default()));
1531 }
1532 let mut transactions: Vec<_> = transactions.try_collect().await.unwrap();
1533
1534 let mut fut = std::pin::pin!(store.new_transaction(lock_keys![], Options::default()));
1536 assert!(futures::poll!(&mut fut).is_pending());
1537
1538 transactions.pop();
1540
1541 assert!(futures::poll!(&mut fut).is_ready());
1542 }
1543
1544 #[fuchsia::test(threads = 10)]
1546 async fn test_continuously_trim() {
1547 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1548 let fs = FxFilesystemBuilder::new()
1549 .trim_config(Some((Duration::ZERO, Duration::ZERO)))
1550 .format(true)
1551 .open(device)
1552 .await
1553 .expect("open failed");
1554 fasync::Timer::new(Duration::from_millis(10)).await;
1556
1557 let root_store = fs.root_store();
1560 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
1561 .await
1562 .expect("open failed");
1563 for _ in 0..100 {
1564 let mut transaction = fs
1565 .root_store()
1566 .new_transaction(
1567 lock_keys![LockKey::object(
1568 root_store.store_object_id(),
1569 root_directory.object_id()
1570 )],
1571 Options::default(),
1572 )
1573 .await
1574 .expect("new_transaction failed");
1575 let object = root_directory
1576 .create_child_file(&mut transaction, "test")
1577 .await
1578 .expect("create_child_file failed");
1579 transaction.commit().await.expect("commit failed");
1580
1581 {
1582 let buf = object.allocate_buffer(1024).await;
1583 object.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
1584 }
1585 std::mem::drop(object);
1586
1587 let mut transaction = root_directory
1588 .acquire_context_for_replace(None, "test", true)
1589 .await
1590 .expect("acquire_context_for_replace failed")
1591 .transaction;
1592 replace_child(&mut transaction, None, (&root_directory, "test"))
1593 .await
1594 .expect("replace_child failed");
1595 transaction.commit().await.expect("commit failed");
1596 }
1597 fs.close().await.expect("close failed");
1598 }
1599
1600 #[test_case(true; "test power fail with barriers")]
1601 #[test_case(false; "test power fail with checksums")]
1602 #[fuchsia::test]
1603 async fn test_power_fail(barriers_enabled: bool) {
1604 for _ in 0..10 {
1607 let (store_id, device, test_file_object_id) = {
1608 let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
1609 let fs = if barriers_enabled {
1610 FxFilesystemBuilder::new()
1611 .barriers_enabled(true)
1612 .format(true)
1613 .open(device)
1614 .await
1615 .expect("new filesystem failed")
1616 } else {
1617 FxFilesystem::new_empty(device).await.expect("new_empty failed")
1618 };
1619 let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
1620
1621 fs.sync(SyncOptions { flush_device: true, ..SyncOptions::default() })
1622 .await
1623 .expect("sync failed");
1624
1625 let store = root_volume
1626 .new_volume(
1627 "test",
1628 NewChildStoreOptions {
1629 options: StoreOptions {
1630 crypt: Some(Arc::new(new_insecure_crypt())),
1631 ..StoreOptions::default()
1632 },
1633 ..Default::default()
1634 },
1635 )
1636 .await
1637 .expect("new_volume failed");
1638 let root_directory = Directory::open(&store, store.root_directory_object_id())
1639 .await
1640 .expect("open failed");
1641
1642 async fn create_files(store: &Arc<ObjectStore>, prefix: &str) {
1644 let fs = store.filesystem();
1645 let root_directory = Directory::open(store, store.root_directory_object_id())
1646 .await
1647 .expect("open failed");
1648 for i in 0..100 {
1649 let mut transaction = fs
1650 .root_store()
1651 .new_transaction(
1652 lock_keys![LockKey::object(
1653 store.store_object_id(),
1654 store.root_directory_object_id()
1655 )],
1656 Options::default(),
1657 )
1658 .await
1659 .expect("new_transaction failed");
1660 root_directory
1661 .create_child_file(&mut transaction, &format!("{prefix} {i}"))
1662 .await
1663 .expect("create_child_file failed");
1664 transaction.commit().await.expect("commit failed");
1665 }
1666 }
1667
1668 create_files(&store, "A").await;
1670
1671 let mut transaction = fs
1674 .root_store()
1675 .new_transaction(
1676 lock_keys![LockKey::object(
1677 store.store_object_id(),
1678 store.root_directory_object_id()
1679 )],
1680 Options::default(),
1681 )
1682 .await
1683 .expect("new_transaction failed");
1684 let object = root_directory
1685 .create_child_file(&mut transaction, "test")
1686 .await
1687 .expect("create_child_file failed");
1688 transaction.commit().await.expect("commit failed");
1689
1690 let mut transaction =
1691 object.new_transaction().await.expect("new_transaction failed");
1692 let mut buffer = object.allocate_buffer(4096).await;
1693 buffer.fill(0xed);
1694 object
1695 .txn_write(&mut transaction, 0, buffer.as_ref())
1696 .await
1697 .expect("txn_write failed");
1698 transaction.commit().await.expect("commit failed");
1699
1700 create_files(&store, "B").await;
1702
1703 fs.sync(SyncOptions::default()).await.expect("sync failed");
1706
1707 fasync::Timer::new(Duration::from_millis(10)).await;
1713
1714 (
1715 store.store_object_id(),
1716 fs.device().snapshot().expect("snapshot failed"),
1717 object.object_id(),
1718 )
1719 };
1720
1721 device
1724 .discard_random_since_last_flush()
1725 .expect("discard_random_since_last_flush failed");
1726
1727 let fs = FxFilesystem::open(device).await.expect("open failed");
1728 fsck(fs.clone()).await.expect("fsck failed");
1729
1730 let mut check_test_file = false;
1731
1732 let object_id = if fs.object_manager().store(store_id).is_some() {
1735 fsck_volume(&fs, store_id, Some(Arc::new(new_insecure_crypt())))
1736 .await
1737 .expect("fsck_volume failed");
1738
1739 let store = root_volume(fs.clone())
1743 .await
1744 .expect("root_volume failed")
1745 .volume(
1746 "test",
1747 StoreOptions {
1748 crypt: Some(Arc::new(new_insecure_crypt())),
1749 ..StoreOptions::default()
1750 },
1751 )
1752 .await
1753 .expect("volume failed");
1754
1755 let root_directory = Directory::open(&store, store.root_directory_object_id())
1756 .await
1757 .expect("open failed");
1758
1759 let mut transaction = fs
1760 .root_store()
1761 .new_transaction(
1762 lock_keys![LockKey::object(
1763 store.store_object_id(),
1764 store.root_directory_object_id()
1765 )],
1766 Options::default(),
1767 )
1768 .await
1769 .expect("new_transaction failed");
1770 let object = root_directory
1771 .create_child_file(&mut transaction, &format!("C"))
1772 .await
1773 .expect("create_child_file failed");
1774 transaction.commit().await.expect("commit failed");
1775
1776 if let Ok(test_file) = ObjectStore::open_object(
1778 &store,
1779 test_file_object_id,
1780 HandleOptions::default(),
1781 None,
1782 )
1783 .await
1784 {
1785 let mut buffer = test_file.allocate_buffer(4096).await;
1787 let bytes = test_file.read(0, buffer.as_mut()).await.expect("read failed");
1788 if bytes == 4096 {
1789 let expected = [0xed; 4096];
1790 assert_eq!(buffer.to_vec(), expected);
1791 } else {
1792 assert_eq!(bytes, 0);
1794 }
1795
1796 let mut transaction =
1798 test_file.new_transaction().await.expect("new_transaction failed");
1799 buffer.fill(0x37);
1800 test_file
1801 .txn_write(&mut transaction, 0, buffer.as_ref())
1802 .await
1803 .expect("txn_write failed");
1804 transaction.commit().await.expect("commit failed");
1805 check_test_file = true;
1806 }
1807
1808 object.object_id()
1809 } else {
1810 INVALID_OBJECT_ID
1811 };
1812
1813 fs.close().await.expect("close failed");
1815 let device = fs.take_device().await;
1816 device.reopen(false);
1817
1818 let fs = FxFilesystem::open(device).await.expect("open failed");
1819 fsck(fs.clone()).await.expect("fsck failed");
1820
1821 if object_id != INVALID_OBJECT_ID {
1824 fsck_volume(&fs, store_id, Some(Arc::new(new_insecure_crypt())))
1825 .await
1826 .expect("fsck_volume failed");
1827
1828 let store = root_volume(fs.clone())
1829 .await
1830 .expect("root_volume failed")
1831 .volume(
1832 "test",
1833 StoreOptions {
1834 crypt: Some(Arc::new(new_insecure_crypt())),
1835 ..StoreOptions::default()
1836 },
1837 )
1838 .await
1839 .expect("volume failed");
1840 ObjectStore::open_object(&store, object_id, HandleOptions::default(), None)
1842 .await
1843 .expect("open_object failed");
1844
1845 if check_test_file {
1847 info!("Checking test file for modification");
1848 let test_file = ObjectStore::open_object(
1849 &store,
1850 test_file_object_id,
1851 HandleOptions::default(),
1852 None,
1853 )
1854 .await
1855 .expect("open_object failed");
1856 let mut buffer = test_file.allocate_buffer(4096).await;
1857 assert_eq!(
1858 test_file.read(0, buffer.as_mut()).await.expect("read failed"),
1859 4096
1860 );
1861 let expected = [0x37; 4096];
1862 let data = buffer.to_vec();
1863 assert_eq!(data, expected);
1864 }
1865 }
1866
1867 fs.close().await.expect("close failed");
1868 }
1869 }
1870
1871 #[fuchsia::test]
1872 async fn test_barrier_not_emitted_when_transaction_has_no_data() {
1873 let barrier_count = Arc::new(AtomicU32::new(0));
1874
1875 struct Observer(Arc<AtomicU32>);
1876
1877 impl fake_device::Observer for Observer {
1878 fn barrier(&self) {
1879 self.0.fetch_add(1, Ordering::Relaxed);
1880 }
1881 }
1882
1883 let mut fake_device = FakeDevice::new(8192, 4096);
1884 fake_device.set_observer(Box::new(Observer(barrier_count.clone())));
1885 let device = DeviceHolder::new(fake_device);
1886 let fs = FxFilesystemBuilder::new()
1887 .barriers_enabled(true)
1888 .format(true)
1889 .open(device)
1890 .await
1891 .expect("new filesystem failed");
1892
1893 {
1894 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
1895 root_vol
1896 .new_volume(
1897 "test",
1898 NewChildStoreOptions {
1899 options: StoreOptions {
1900 crypt: Some(Arc::new(new_insecure_crypt())),
1901 ..StoreOptions::default()
1902 },
1903 ..NewChildStoreOptions::default()
1904 },
1905 )
1906 .await
1907 .expect("there is no test volume");
1908 fs.close().await.expect("close failed");
1909 }
1910 let device = fs.take_device().await;
1913 device.reopen(false);
1914 let fs = FxFilesystemBuilder::new()
1915 .barriers_enabled(true)
1916 .open(device)
1917 .await
1918 .expect("new filesystem failed");
1919 let expected_barrier_count = barrier_count.load(Ordering::Relaxed);
1920
1921 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
1922 let store = root_vol
1923 .volume(
1924 "test",
1925 StoreOptions {
1926 crypt: Some(Arc::new(new_insecure_crypt())),
1927 ..StoreOptions::default()
1928 },
1929 )
1930 .await
1931 .expect("there is no test volume");
1932
1933 let fs = store.filesystem();
1935 let root_directory =
1936 Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
1937 for i in 0..100 {
1938 let mut transaction = fs
1939 .root_store()
1940 .new_transaction(
1941 lock_keys![LockKey::object(
1942 store.store_object_id(),
1943 store.root_directory_object_id()
1944 )],
1945 Options::default(),
1946 )
1947 .await
1948 .expect("new_transaction failed");
1949 root_directory
1950 .create_child_file(&mut transaction, &format!("A {i}"))
1951 .await
1952 .expect("create_child_file failed");
1953 transaction.commit().await.expect("commit failed");
1954 }
1955
1956 fs.close().await.expect("close failed");
1958 assert_eq!(expected_barrier_count, barrier_count.load(Ordering::Relaxed));
1960 }
1961
1962 #[fuchsia::test]
1963 async fn test_barrier_emitted_when_transaction_includes_data() {
1964 let barrier_count = Arc::new(AtomicU32::new(0));
1965
1966 struct Observer(Arc<AtomicU32>);
1967
1968 impl fake_device::Observer for Observer {
1969 fn barrier(&self) {
1970 self.0.fetch_add(1, Ordering::Relaxed);
1971 }
1972 }
1973
1974 let mut fake_device = FakeDevice::new(8192, 4096);
1975 fake_device.set_observer(Box::new(Observer(barrier_count.clone())));
1976 let device = DeviceHolder::new(fake_device);
1977 let fs = FxFilesystemBuilder::new()
1978 .barriers_enabled(true)
1979 .format(true)
1980 .open(device)
1981 .await
1982 .expect("new filesystem failed");
1983
1984 {
1985 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
1986 root_vol
1987 .new_volume(
1988 "test",
1989 NewChildStoreOptions {
1990 options: StoreOptions {
1991 crypt: Some(Arc::new(new_insecure_crypt())),
1992 ..StoreOptions::default()
1993 },
1994 ..NewChildStoreOptions::default()
1995 },
1996 )
1997 .await
1998 .expect("there is no test volume");
1999 fs.close().await.expect("close failed");
2000 }
2001 let device = fs.take_device().await;
2004 device.reopen(false);
2005 let fs = FxFilesystemBuilder::new()
2006 .barriers_enabled(true)
2007 .open(device)
2008 .await
2009 .expect("new filesystem failed");
2010 let expected_barrier_count = barrier_count.load(Ordering::Relaxed);
2011
2012 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2013 let store = root_vol
2014 .volume(
2015 "test",
2016 StoreOptions {
2017 crypt: Some(Arc::new(new_insecure_crypt())),
2018 ..StoreOptions::default()
2019 },
2020 )
2021 .await
2022 .expect("there is no test volume");
2023
2024 let fs: Arc<FxFilesystem> = store.filesystem();
2026 let root_directory =
2027 Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2028
2029 let mut transaction = fs
2030 .root_store()
2031 .new_transaction(
2032 lock_keys![LockKey::object(
2033 store.store_object_id(),
2034 store.root_directory_object_id()
2035 )],
2036 Options::default(),
2037 )
2038 .await
2039 .expect("new_transaction failed");
2040 let object = root_directory
2041 .create_child_file(&mut transaction, "test")
2042 .await
2043 .expect("create_child_file failed");
2044 transaction.commit().await.expect("commit failed");
2045
2046 let mut transaction = object.new_transaction().await.expect("new_transaction failed");
2047 let mut buffer = object.allocate_buffer(4096).await;
2048 buffer.fill(0xed);
2049 object.txn_write(&mut transaction, 0, buffer.as_ref()).await.expect("txn_write failed");
2050 transaction.commit().await.expect("commit failed");
2051
2052 fs.close().await.expect("close failed");
2054 assert!(expected_barrier_count < barrier_count.load(Ordering::Relaxed));
2056 }
2057
2058 #[test_case(true; "fail when original filesystem has barriers enabled")]
2059 #[test_case(false; "fail when original filesystem has barriers disabled")]
2060 #[fuchsia::test]
2061 async fn test_switching_barrier_mode_on_existing_filesystem(original_barrier_mode: bool) {
2062 let crypt = Some(Arc::new(new_insecure_crypt()) as Arc<dyn fxfs_crypto::Crypt>);
2063 let fake_device = FakeDevice::new(8192, 4096);
2064 let device = DeviceHolder::new(fake_device);
2065 let fs: super::OpenFxFilesystem = FxFilesystemBuilder::new()
2066 .barriers_enabled(original_barrier_mode)
2067 .format(true)
2068 .open(device)
2069 .await
2070 .expect("new filesystem failed");
2071
2072 {
2074 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2075 let store = root_vol
2076 .new_volume(
2077 "test",
2078 NewChildStoreOptions {
2079 options: StoreOptions { crypt: crypt.clone(), ..Default::default() },
2080 ..Default::default()
2081 },
2082 )
2083 .await
2084 .expect("creating test volume");
2085 let root_dir = Directory::open(&store, store.root_directory_object_id())
2086 .await
2087 .expect("open failed");
2088 let mut transaction = fs
2089 .root_store()
2090 .new_transaction(
2091 lock_keys![LockKey::object(
2092 store.store_object_id(),
2093 store.root_directory_object_id()
2094 )],
2095 Default::default(),
2096 )
2097 .await
2098 .expect("new_transaction failed");
2099 let object = root_dir
2100 .create_child_file(&mut transaction, "file")
2101 .await
2102 .expect("create_child_file failed");
2103 transaction.commit().await.expect("commit failed");
2104 let mut buffer = object.allocate_buffer(4096).await;
2105 buffer.fill(0xA7);
2106 let new_size = object.write_or_append(None, buffer.as_ref()).await.unwrap();
2107 assert_eq!(new_size, 4096);
2108 }
2109
2110 fs.close().await.expect("close failed");
2112 let device = fs.take_device().await;
2113 device.reopen(false);
2114 let fs = FxFilesystemBuilder::new()
2115 .barriers_enabled(!original_barrier_mode)
2116 .open(device)
2117 .await
2118 .expect("new filesystem failed");
2119 {
2120 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2121 let store = root_vol
2122 .volume("test", StoreOptions { crypt: crypt.clone(), ..Default::default() })
2123 .await
2124 .expect("opening test volume");
2125 let root_dir = Directory::open(&store, store.root_directory_object_id())
2126 .await
2127 .expect("open failed");
2128 let (object_id, _, _) =
2129 root_dir.lookup("file").await.expect("lookup failed").expect("missing file");
2130 let test_file = ObjectStore::open_object(&store, object_id, Default::default(), None)
2131 .await
2132 .expect("open failed");
2133 let mut buffer = test_file.allocate_buffer(4096).await;
2135 buffer.fill(0xA8);
2136 let new_size = test_file.write_or_append(None, buffer.as_ref()).await.unwrap();
2137 assert_eq!(new_size, 8192);
2138 }
2139
2140 fs.close().await.expect("close failed");
2143 let device = fs.take_device().await;
2144 device.reopen(false);
2145 let fs = FxFilesystemBuilder::new()
2146 .barriers_enabled(original_barrier_mode)
2147 .open(device)
2148 .await
2149 .expect("new filesystem failed");
2150 {
2151 let root_vol = root_volume(fs.clone()).await.expect("root_volume failed");
2152 let store = root_vol
2153 .volume("test", StoreOptions { crypt: crypt.clone(), ..Default::default() })
2154 .await
2155 .expect("opening test volume");
2156 let root_dir = Directory::open(&store, store.root_directory_object_id())
2157 .await
2158 .expect("open failed");
2159 let (object_id, _, _) =
2160 root_dir.lookup("file").await.expect("lookup failed").expect("missing file");
2161 let test_file = ObjectStore::open_object(&store, object_id, Default::default(), None)
2162 .await
2163 .expect("open failed");
2164 let mut buffer = test_file.allocate_buffer(8192).await;
2165 assert_eq!(
2166 test_file.read(0, buffer.as_mut()).await.expect("read failed"),
2167 8192,
2168 "short read"
2169 );
2170 let data = buffer.to_vec();
2171 assert_eq!(data[0..4096], [0xA7; 4096]);
2172 assert_eq!(data[4096..8192], [0xA8; 4096]);
2173 }
2174 fs.close().await.expect("close failed");
2175 }
2176
2177 #[fuchsia::test]
2178 async fn test_image_builder_mode_no_early_writes() {
2179 const BLOCK_SIZE: u32 = 4096;
2180 let device = DeviceHolder::new(FakeDevice::new(2048, BLOCK_SIZE));
2181 device.reopen(true);
2182 let fs = FxFilesystemBuilder::new()
2183 .format(true)
2184 .image_builder_mode(Some(SuperBlockInstance::A))
2185 .open(device)
2186 .await
2187 .expect("open failed");
2188 fs.enable_allocations();
2189 fs.device().reopen(false);
2191 fs.close().await.expect("closed");
2192 }
2193
2194 #[fuchsia::test]
2195 async fn test_image_builder_mode() {
2196 const BLOCK_SIZE: u32 = 4096;
2197 const EXISTING_FILE_RANGE: Range<u64> = 4096 * 1024..4096 * 1025;
2198 let device = DeviceHolder::new(FakeDevice::new(2048, BLOCK_SIZE));
2199
2200 {
2202 let mut write_buf =
2203 device.allocate_buffer(EXISTING_FILE_RANGE.length().unwrap() as usize).await;
2204 write_buf.fill(0xf0);
2205 device.write(EXISTING_FILE_RANGE.start, write_buf.as_ref()).await.expect("write");
2206 }
2207
2208 device.reopen(true);
2209
2210 let device = {
2211 let fs = FxFilesystemBuilder::new()
2212 .format(true)
2213 .image_builder_mode(Some(SuperBlockInstance::B))
2214 .open(device)
2215 .await
2216 .expect("open failed");
2217 fs.enable_allocations();
2218 {
2219 let root_store = fs.root_store();
2220 let root_directory =
2221 Directory::open(&root_store, root_store.root_directory_object_id())
2222 .await
2223 .expect("open failed");
2224 let handle;
2226 {
2227 let mut transaction = fs
2228 .root_store()
2229 .new_transaction(
2230 lock_keys![LockKey::object(
2231 root_directory.store().store_object_id(),
2232 root_directory.object_id()
2233 )],
2234 Options::default(),
2235 )
2236 .await
2237 .expect("new transaction");
2238 handle = root_directory
2239 .create_child_file(&mut transaction, "test")
2240 .await
2241 .expect("create file");
2242 handle.extend(&mut transaction, EXISTING_FILE_RANGE).await.expect("extend");
2243 transaction.commit().await.expect("commit");
2244 }
2245 }
2246 fs.device().reopen(false);
2247 fs.close().await.expect("close");
2248 fs.take_device().await
2249 };
2250 device.reopen(false);
2251 let fs = FxFilesystem::open(device).await.expect("open failed");
2252 fsck(fs.clone()).await.expect("fsck failed");
2253
2254 let root_store = fs.root_store();
2256 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
2257 .await
2258 .expect("open failed");
2259 let (object_id, descriptor, _) =
2260 root_directory.lookup("test").await.expect("lookup failed").unwrap();
2261 assert_eq!(descriptor, ObjectDescriptor::File);
2262 let test_file =
2263 ObjectStore::open_object(&root_store, object_id, HandleOptions::default(), None)
2264 .await
2265 .expect("open failed");
2266 let mut read_buf =
2267 test_file.allocate_buffer(EXISTING_FILE_RANGE.length().unwrap() as usize).await;
2268 test_file.read(0, read_buf.as_mut()).await.expect("read failed");
2269 let data = read_buf.to_vec();
2270 assert_eq!(data, [0xf0; 4096]);
2271 fs.close().await.expect("closed");
2272 }
2273
2274 #[fuchsia::test]
2275 async fn test_read_only_mount_on_full_filesystem() {
2276 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2277 let fs =
2278 FxFilesystemBuilder::new().format(true).open(device).await.expect("new_empty failed");
2279 let root_store = fs.root_store();
2280 let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
2281 .await
2282 .expect("open failed");
2283
2284 let mut transaction = fs
2285 .root_store()
2286 .new_transaction(
2287 lock_keys![LockKey::object(
2288 root_store.store_object_id(),
2289 root_directory.object_id()
2290 )],
2291 Options::default(),
2292 )
2293 .await
2294 .expect("new_transaction failed");
2295 let handle = root_directory
2296 .create_child_file(&mut transaction, "test")
2297 .await
2298 .expect("create_child_file failed");
2299 transaction.commit().await.expect("commit failed");
2300
2301 let mut buf = handle.allocate_buffer(4096).await;
2302 buf.fill(0xaa);
2303 loop {
2304 if handle.write_or_append(None, buf.as_ref()).await.is_err() {
2305 break;
2306 }
2307 }
2308
2309 let max_offset = fs.allocator().maximum_offset();
2310 fs.close().await.expect("Close failed");
2311
2312 let device = fs.take_device().await;
2313 device.reopen(false);
2314 let mut buffer = device
2315 .allocate_buffer(
2316 crate::round::round_up(max_offset, TEST_DEVICE_BLOCK_SIZE).unwrap() as usize
2317 )
2318 .await;
2319 device.read(0, buffer.as_mut()).await.expect("read failed");
2320
2321 let image_data = buffer.to_vec();
2322 let device = DeviceHolder::new(
2323 FakeDevice::from_image(image_data.as_slice(), TEST_DEVICE_BLOCK_SIZE)
2324 .expect("from_image failed"),
2325 );
2326 let fs =
2327 FxFilesystemBuilder::new().read_only(true).open(device).await.expect("open failed");
2328 fs.close().await.expect("Close failed");
2329 }
2330
2331 #[test_case(SuperBlockInstance::A; "Superblock instance A")]
2332 #[test_case(SuperBlockInstance::B; "Superblock instance B")]
2333 #[fuchsia::test]
2334 async fn test_image_builder_mode_flush_on_close_sb_a(target_sb: SuperBlockInstance) {
2335 const BLOCK_SIZE: u32 = 4096;
2336 let device = DeviceHolder::new(FakeDevice::new(2048, BLOCK_SIZE));
2337
2338 device.reopen(true);
2340 let fs = FxFilesystemBuilder::new()
2341 .format(true)
2342 .image_builder_mode(Some(target_sb))
2343 .open(device)
2344 .await
2345 .expect("open failed");
2346
2347 fs.enable_allocations();
2348
2349 fs.device().reopen(false);
2351
2352 {
2354 let root_store = fs.root_store();
2355 let root_directory =
2356 Directory::open(&root_store, root_store.root_directory_object_id())
2357 .await
2358 .expect("open failed");
2359
2360 let mut transaction = fs
2361 .root_store()
2362 .new_transaction(
2363 lock_keys![LockKey::object(
2364 root_directory.store().store_object_id(),
2365 root_directory.object_id()
2366 )],
2367 Options::default(),
2368 )
2369 .await
2370 .expect("new transaction");
2371 let handle = root_directory
2372 .create_child_file(&mut transaction, "post_finalize_file")
2373 .await
2374 .expect("create file");
2375 transaction.commit().await.expect("commit");
2376
2377 let mut buf = handle.allocate_buffer(BLOCK_SIZE as usize).await;
2378 buf.fill(0xaa);
2379 handle.write_or_append(None, buf.as_ref()).await.expect("write failed");
2380 }
2381
2382 fs.close().await.expect("close failed");
2384
2385 let other_sb = target_sb.next();
2386
2387 let device = fs.take_device().await;
2389 device.reopen(true); let mut buf = device.allocate_buffer(BLOCK_SIZE as usize).await;
2391
2392 device.read(target_sb.first_extent().start, buf.as_mut()).await.expect("read target_sb");
2393 let data = buf.to_vec();
2394 assert_eq!(&data[..8], b"FxfsSupr", "target_sb should have magic bytes");
2395
2396 buf.fill(0); device.read(other_sb.first_extent().start, buf.as_mut()).await.expect("read other_sb");
2398 let data2 = buf.to_vec();
2400 assert_eq!(data2, &[0; 4096], "other_sb should be zeroed");
2401 }
2402
2403 #[cfg(target_os = "fuchsia")]
2404 #[fuchsia::test(allow_stalls = false)]
2405 async fn test_trim_with_power_manager() {
2406 use anyhow::Error;
2407 use async_trait::async_trait;
2408 use fuchsia_async::TestExecutor;
2409 use futures::StreamExt;
2410
2411 TestExecutor::advance_to(fasync::MonotonicInstant::ZERO).await;
2412
2413 #[derive(Default)]
2414 struct MockPowerManager {
2415 on_battery: Mutex<bool>,
2416 event: event_listener::Event,
2417 wake_lease: Mutex<Option<zx::EventPair>>,
2418 }
2419
2420 impl MockPowerManager {
2421 fn set_on_battery(&self, v: bool) {
2422 *self.on_battery.lock() = v;
2423 self.event.notify(usize::MAX);
2424 }
2425
2426 fn is_lease_held(&self) -> bool {
2427 self.wake_lease.lock().as_ref().is_some_and(|handle| {
2428 handle
2429 .wait_one(
2430 zx::Signals::EVENTPAIR_PEER_CLOSED,
2431 zx::MonotonicInstant::INFINITE_PAST,
2432 )
2433 .is_err()
2434 })
2435 }
2436 }
2437
2438 impl super::PowerManager for MockPowerManager {
2439 fn watch_battery(
2440 self: Arc<Self>,
2441 ) -> futures::stream::BoxStream<'static, (bool, super::WakeLease)> {
2442 futures::stream::unfold(true, move |first| {
2443 let this = self.clone();
2444 async move {
2445 if !first {
2446 this.event.listen().await;
2447 }
2448 let val = *this.on_battery.lock();
2449 let handle = if val {
2450 zx::NullableHandle::invalid()
2451 } else {
2452 let (h1, h2) = zx::EventPair::create();
2453 *this.wake_lease.lock() = Some(h2);
2454 h1.into_handle()
2456 };
2457 Some(((val, handle), false))
2458 }
2459 })
2460 .boxed()
2461 }
2462 }
2463
2464 let trim_count = Arc::new(AtomicU32::new(0));
2465
2466 struct TrimTrackingDevice {
2467 inner: DeviceHolder,
2468 trim_count: Arc<AtomicU32>,
2469 power_manager: Arc<MockPowerManager>,
2470 }
2471
2472 #[async_trait]
2473 impl storage_device::Device for TrimTrackingDevice {
2474 fn allocate_buffer(&self, size: usize) -> storage_device::buffer::BufferFuture<'_> {
2475 self.inner.allocate_buffer(size)
2476 }
2477 fn block_size(&self) -> u32 {
2478 self.inner.block_size()
2479 }
2480 fn block_count(&self) -> u64 {
2481 self.inner.block_count()
2482 }
2483 async fn read_with_opts(
2484 &self,
2485 offset: u64,
2486 buffer: storage_device::buffer::MutableBufferRef<'_>,
2487 opts: storage_device::ReadOptions,
2488 ) -> Result<(), Error> {
2489 self.inner.read_with_opts(offset, buffer, opts).await
2490 }
2491 async fn write_with_opts(
2492 &self,
2493 offset: u64,
2494 buffer: storage_device::buffer::BufferRef<'_>,
2495 opts: storage_device::WriteOptions,
2496 ) -> Result<(), Error> {
2497 self.inner.write_with_opts(offset, buffer, opts).await
2498 }
2499 async fn trim(&self, range: std::ops::Range<u64>) -> Result<(), Error> {
2500 assert!(self.power_manager.is_lease_held());
2501 self.trim_count.fetch_add(1, Ordering::SeqCst);
2502 self.inner.trim(range).await
2503 }
2504 async fn flush(&self) -> Result<(), Error> {
2505 self.inner.flush().await
2506 }
2507 async fn close(&self) -> Result<(), Error> {
2508 self.inner.close().await
2509 }
2510 fn barrier(&self) {
2511 self.inner.barrier()
2512 }
2513 fn supports_trim(&self) -> bool {
2514 true
2515 }
2516 fn is_read_only(&self) -> bool {
2517 self.inner.is_read_only()
2518 }
2519 fn snapshot(&self) -> Result<DeviceHolder, Error> {
2520 Ok(DeviceHolder::new(TrimTrackingDevice {
2521 inner: self.inner.snapshot()?,
2522 trim_count: self.trim_count.clone(),
2523 power_manager: self.power_manager.clone(),
2524 }))
2525 }
2526 fn reopen(&self, read_only: bool) {
2527 self.inner.reopen(read_only)
2528 }
2529 }
2530
2531 let pm = Arc::new(MockPowerManager::default());
2532
2533 pm.set_on_battery(true);
2535
2536 let fake_device = FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE);
2537 let device = DeviceHolder::new(TrimTrackingDevice {
2538 inner: DeviceHolder::new(fake_device),
2539 trim_count: trim_count.clone(),
2540 power_manager: pm.clone(),
2541 });
2542
2543 let fs = FxFilesystemBuilder::new()
2544 .format(true)
2545 .power_manager(pm.clone())
2546 .trim_config(Some((Duration::ZERO, Duration::from_millis(100))))
2547 .trim_charger_wait(Duration::from_millis(10))
2548 .open(device)
2549 .await
2550 .expect("open failed");
2551
2552 TestExecutor::advance_to(fasync::MonotonicInstant::after(
2554 Duration::from_millis(500).into(),
2555 ))
2556 .await;
2557 let _ = TestExecutor::poll_until_stalled(std::future::pending::<()>()).await;
2558
2559 assert_eq!(trim_count.load(Ordering::SeqCst), 0);
2560
2561 {
2563 let root_store = fs.root_store();
2564 let root_directory =
2565 Directory::open(&root_store, root_store.root_directory_object_id())
2566 .await
2567 .expect("open failed");
2568 let mut transaction = fs
2569 .root_store()
2570 .new_transaction(
2571 lock_keys![LockKey::object(
2572 root_store.store_object_id(),
2573 root_directory.object_id()
2574 )],
2575 Options::default(),
2576 )
2577 .await
2578 .expect("new_transaction failed");
2579 let handle = root_directory
2580 .create_child_file(&mut transaction, "test")
2581 .await
2582 .expect("create_child_file failed");
2583 transaction.commit().await.expect("commit failed");
2584 handle.allocate(0..4096).await.expect("allocate failed");
2585 let mut transaction = fs
2587 .root_store()
2588 .new_transaction(
2589 lock_keys![
2590 LockKey::object(root_store.store_object_id(), root_directory.object_id()),
2591 LockKey::object(root_store.store_object_id(), handle.object_id()),
2592 ],
2593 Options::default(),
2594 )
2595 .await
2596 .expect("new_transaction failed");
2597 replace_child(&mut transaction, None, (&root_directory, "test"))
2598 .await
2599 .expect("delete failed");
2600 transaction.commit().await.expect("commit failed");
2601 fs.root_store()
2602 .tombstone_object(handle.object_id(), Options::default())
2603 .await
2604 .expect("tombstone failed");
2605 }
2606
2607 pm.set_on_battery(false);
2609
2610 TestExecutor::advance_to(fasync::MonotonicInstant::after(Duration::from_millis(10).into()))
2612 .await;
2613
2614 let _ = TestExecutor::poll_until_stalled(std::future::pending::<()>()).await;
2615
2616 assert!(trim_count.load(Ordering::SeqCst) > 0);
2617
2618 trim_count.store(0, Ordering::SeqCst);
2620 pm.set_on_battery(true);
2621
2622 TestExecutor::advance_to(fasync::MonotonicInstant::after(
2624 Duration::from_millis(500).into(),
2625 ))
2626 .await;
2627
2628 let _ = TestExecutor::poll_until_stalled(std::future::pending::<()>()).await;
2629
2630 assert_eq!(trim_count.load(Ordering::SeqCst), 0);
2631
2632 fs.close().await.expect("close failed");
2633 }
2634
2635 #[fuchsia::test]
2636 async fn test_concurrent_do_trim_returns_error() {
2637 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2638 let fs = FxFilesystemBuilder::new()
2639 .trim_config(None)
2640 .format(true)
2641 .open(device)
2642 .await
2643 .expect("open failed");
2644
2645 let max_extent_size = fs.device().size() as usize;
2646 const EXTENTS_PER_BATCH: usize = usize::MAX;
2647
2648 let allocator = fs.allocator();
2650 let _trimmable_extents = allocator
2651 .take_for_trimming(0, max_extent_size, EXTENTS_PER_BATCH)
2652 .await
2653 .expect("take_for_trimming failed");
2654
2655 let res = fs.do_trim(None).await;
2658 assert!(matches!(res, Err(e) if FxfsError::AlreadyBound.matches(&e)));
2659
2660 fs.close().await.expect("close failed");
2661 }
2662}