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