1use crate::fuchsia::component::map_to_raw_status;
6use crate::fuchsia::directory::FxDirectory;
7use crate::fuchsia::dirent_cache::DirentCache;
8use crate::fuchsia::file::{FlushType, FxFile};
9use crate::fuchsia::memory_pressure::{MemoryPressureLevel, MemoryPressureMonitor};
10use crate::fuchsia::node::{FxNode, GetResult, NodeCache};
11use crate::fuchsia::pager::Pager;
12use crate::fuchsia::profile::ProfileState;
13use crate::fuchsia::symlink::FxSymlink;
14use crate::fuchsia::volumes_directory::VolumesDirectory;
15use anyhow::{Error, bail, ensure};
16use async_trait::async_trait;
17use fidl::endpoints::ServerEnd;
18use fidl_fuchsia_fxfs::{
19 BytesAndNodes, FileBackedVolumeProviderRequest, FileBackedVolumeProviderRequestStream,
20 ProjectIdRequest, ProjectIdRequestStream, ProjectIterToken,
21};
22use fidl_fuchsia_io as fio;
23use fs_inspect::{FsInspectVolume, VolumeData};
24use fuchsia_async as fasync;
25use fuchsia_async::epoch::Epoch;
26use fuchsia_sync::Mutex;
27use futures::channel::oneshot;
28use futures::stream::{self, FusedStream, Stream};
29use futures::{FutureExt, StreamExt, TryStreamExt};
30use fxfs::errors::FxfsError;
31use fxfs::filesystem::{self, SyncOptions};
32use fxfs::future_with_guard::FutureWithGuard;
33use fxfs::log::*;
34use fxfs::object_store::directory::Directory;
35use fxfs::object_store::project_id::ProjectIdExt;
36use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
37use fxfs::object_store::{
38 DirType, HandleOptions, HandleOwner, ObjectDescriptor, ObjectStore, ProjectId,
39};
40use refaults_vmo::PageRefaultCounter;
41use std::future::Future;
42use std::pin::pin;
43#[cfg(any(test, feature = "testing"))]
44use std::sync::atomic::AtomicBool;
45use std::sync::atomic::{AtomicU64, Ordering};
46use std::sync::{Arc, OnceLock, Weak};
47use std::time::Duration;
48use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
49use vfs::directory::entry::DirectoryEntry;
50use vfs::directory::simple::Simple;
51use vfs::execution_scope::ExecutionScope;
52
53const DIRENT_CACHE_LIMIT: usize = 2000;
56pub const READ_AHEAD_SIZE: u64 = 128 * 1024;
61
62const BLOB_TRANSFER_VMO_SIZE: usize = 32 * 1024 * 1024;
63
64const PROFILE_DIRECTORY: &str = "profiles";
65
66#[derive(Clone, Copy, Debug)]
67pub struct MemoryPressureLevelConfig {
68 pub background_task_period: Duration,
71
72 pub cache_size_limit: usize,
74
75 pub background_task_initial_delay: Duration,
78}
79
80impl Default for MemoryPressureLevelConfig {
81 fn default() -> Self {
82 Self {
83 background_task_period: Duration::from_secs(20),
84 cache_size_limit: DIRENT_CACHE_LIMIT,
85 background_task_initial_delay: Duration::from_secs(70),
86 }
87 }
88}
89
90#[derive(Clone, Copy, Debug)]
91pub struct MemoryPressureConfig {
92 pub mem_normal: MemoryPressureLevelConfig,
94
95 pub mem_warning: MemoryPressureLevelConfig,
97
98 pub mem_critical: MemoryPressureLevelConfig,
100}
101
102impl MemoryPressureConfig {
103 pub fn for_level(&self, level: &MemoryPressureLevel) -> &MemoryPressureLevelConfig {
104 match level {
105 MemoryPressureLevel::Normal => &self.mem_normal,
106 MemoryPressureLevel::Warning => &self.mem_warning,
107 MemoryPressureLevel::Critical => &self.mem_critical,
108 }
109 }
110}
111
112impl Default for MemoryPressureConfig {
113 fn default() -> Self {
114 Self {
117 mem_normal: MemoryPressureLevelConfig {
118 background_task_period: Duration::from_secs(20),
119 cache_size_limit: DIRENT_CACHE_LIMIT,
120 background_task_initial_delay: Duration::from_secs(70),
121 },
122 mem_warning: MemoryPressureLevelConfig {
123 background_task_period: Duration::from_secs(5),
124 cache_size_limit: 100,
125 background_task_initial_delay: Duration::from_secs(5),
126 },
127 mem_critical: MemoryPressureLevelConfig {
128 background_task_period: Duration::from_millis(1500),
129 cache_size_limit: 20,
130 background_task_initial_delay: Duration::from_millis(1500),
131 },
132 }
133 }
134}
135
136pub struct FxVolume {
139 parent: Weak<VolumesDirectory>,
140 cache: NodeCache,
141 store: Arc<ObjectStore>,
142 pager: Pager,
143 executor: fasync::EHandle,
144 name: String,
145
146 background_task: Mutex<Option<(fasync::Task<()>, oneshot::Sender<()>)>>,
148
149 fs_id: u64,
151
152 scope: ExecutionScope,
154
155 dirent_cache: DirentCache,
156
157 profile_state: Mutex<Option<Box<dyn ProfileState>>>,
158
159 #[cfg(any(test, feature = "testing"))]
160 poisoned: AtomicBool,
161
162 blob_resupplied_count: Arc<PageRefaultCounter>,
163
164 pager_dirty_byte_count: AtomicU64,
167
168 blob_allocator: OnceLock<Arc<BufferAllocator>>,
175}
176
177#[fxfs_trace::trace]
178impl FxVolume {
179 pub fn new(
180 parent: Weak<VolumesDirectory>,
181 store: Arc<ObjectStore>,
182 fs_id: u64,
183 name: String,
184 blob_resupplied_count: Arc<PageRefaultCounter>,
185 memory_pressure_config: MemoryPressureConfig,
186 ) -> Result<Self, Error> {
187 let scope = ExecutionScope::new();
188 Ok(Self {
189 parent,
190 cache: NodeCache::new(),
191 store,
192 name,
193 pager: Pager::new(scope.clone())?,
194 executor: fasync::EHandle::local(),
195 background_task: Mutex::new(None),
196 fs_id,
197 scope,
198 dirent_cache: DirentCache::new(memory_pressure_config.mem_normal.cache_size_limit),
199 profile_state: Mutex::new(None),
200 #[cfg(any(test, feature = "testing"))]
201 poisoned: AtomicBool::new(false),
202 blob_resupplied_count,
203 pager_dirty_byte_count: AtomicU64::new(0),
204 blob_allocator: OnceLock::new(),
205 })
206 }
207
208 pub fn store(&self) -> &Arc<ObjectStore> {
209 &self.store
210 }
211
212 pub fn blob_allocator(&self) -> &Arc<BufferAllocator> {
216 self.blob_allocator.get_or_init(|| {
217 Arc::new(BufferAllocator::new(
218 self.store().block_size() as usize,
219 BufferSource::new_trusted(BLOB_TRANSFER_VMO_SIZE),
220 ))
221 })
222 }
223
224 pub fn cache(&self) -> &NodeCache {
225 &self.cache
226 }
227
228 pub fn dirent_cache(&self) -> &DirentCache {
229 &self.dirent_cache
230 }
231
232 pub fn pager(&self) -> &Pager {
233 &self.pager
234 }
235
236 pub fn id(&self) -> u64 {
237 self.fs_id
238 }
239
240 pub fn scope(&self) -> &ExecutionScope {
241 &self.scope
242 }
243
244 pub fn blob_resupplied_count(&self) -> &PageRefaultCounter {
245 &self.blob_resupplied_count
246 }
247
248 pub fn name(&self) -> &str {
249 &self.name
250 }
251
252 pub fn filesystem_info_for_volume(&self) -> fio::FilesystemInfo {
255 let allocator = self.store.filesystem().allocator();
256 let info =
257 if let Some(limit) = allocator.get_owner_bytes_limit(self.store.store_object_id()) {
258 filesystem::Info {
259 used_bytes: allocator.get_owner_bytes_used(self.store.store_object_id()),
260 total_bytes: limit,
261 }
262 } else {
263 self.store.filesystem().get_info()
264 };
265
266 info_to_filesystem_info(info, self.store.block_size(), self.store.object_count(), self.id())
267 }
268
269 pub async fn stop_profile_tasks(self: &Arc<Self>) {
271 let Some(mut state) = self.profile_state.lock().take() else { return };
272 state.wait_for_replay_to_finish().await;
273 self.pager.set_recorder(None);
274 state.wait_for_recording_to_finish().await;
275 }
276
277 pub async fn get_profile_directory(self: &Arc<Self>) -> Result<Directory<FxVolume>, Error> {
279 let internal_dir = self
280 .get_or_create_internal_dir()
281 .await
282 .map_err(|e| e.context("Opening internal directory"))?;
283 let mut transaction = self
285 .store()
286 .new_transaction(
287 lock_keys![LockKey::object(
288 self.store().store_object_id(),
289 internal_dir.object_id(),
290 )],
291 Options::default(),
292 )
293 .await?;
294 Ok(match internal_dir.directory().lookup(PROFILE_DIRECTORY).await? {
295 Some((object_id, _, _)) => {
296 Directory::open_unchecked(self.clone(), object_id, DirType::Normal)
297 }
298 None => {
299 let new_dir = internal_dir
300 .directory()
301 .create_child_dir(&mut transaction, PROFILE_DIRECTORY)
302 .await?;
303 transaction.commit().await?;
304 new_dir
305 }
306 })
307 }
308
309 pub async fn record_and_replay_profile(
313 self: &Arc<Self>,
314 mut state: Box<dyn ProfileState>,
315 name: &str,
316 ) -> Result<(), Error> {
317 let profile_dir = self.get_profile_directory().await?;
324 let replay_handle = if let Some((id, descriptor, _)) = profile_dir.lookup(name).await? {
325 ensure!(matches!(descriptor, ObjectDescriptor::File), FxfsError::Inconsistent);
326 Some(Box::new(
327 ObjectStore::open_object(self, id, HandleOptions::default(), None).await?,
328 ))
329 } else {
330 None
331 };
332
333 info!("Recording new profile '{name}' for volume object {}", self.store.store_object_id());
334 let recording_handle =
336 crate::fuchsia::profile::FileRecordingHandle::new(name, self.clone()).await?;
337
338 let mut profile_state = self.profile_state.lock();
339 self.pager.set_recorder(Some(state.record_new(self, Box::new(recording_handle))));
340 if let Some(handle) = replay_handle {
341 if let Some(guard) = self.scope().try_active_guard() {
342 state.replay_profile(handle, self.clone(), guard);
343 info!(
344 "Replaying existing profile '{name}' for volume object {}",
345 self.store.store_object_id()
346 );
347 }
348 }
349 *profile_state = Some(state);
350 Ok(())
351 }
352
353 pub async fn replay_xor_record_profile(
355 self: &Arc<Self>,
356 mut state: Box<dyn ProfileState>,
357 name: &str,
358 ) -> Result<(), Error> {
359 let profile_dir = self.get_profile_directory().await?;
360 let replay_handle = if let Some((id, descriptor, _)) = profile_dir.lookup(name).await? {
361 ensure!(matches!(descriptor, ObjectDescriptor::File), FxfsError::Inconsistent);
362 Some(Box::new(
363 ObjectStore::open_object(self, id, HandleOptions::default(), None).await?,
364 ))
365 } else {
366 None
367 };
368
369 if let Some(handle) = replay_handle {
370 let mut profile_state = self.profile_state.lock();
371 if let Some(guard) = self.scope().try_active_guard() {
372 state.replay_profile(handle, self.clone(), guard);
373 info!(
374 "Replaying existing profile '{name}' for volume object {}",
375 self.store.store_object_id()
376 );
377 }
378 *profile_state = Some(state);
379 } else {
380 info!(
381 "Recording new profile '{name}' for volume object {}",
382 self.store.store_object_id()
383 );
384 let recording_handle =
385 crate::fuchsia::profile::FileRecordingHandle::new(name, self.clone()).await?;
386 let mut profile_state = self.profile_state.lock();
387 self.pager.set_recorder(Some(state.record_new(self, Box::new(recording_handle))));
388 *profile_state = Some(state);
389 }
390 Ok(())
391 }
392
393 async fn get_or_create_internal_dir(self: &Arc<Self>) -> Result<Arc<FxDirectory>, Error> {
394 let internal_data_id = self.store().get_or_create_internal_directory_id().await?;
395 let internal_dir = self
396 .get_or_load_node(internal_data_id, ObjectDescriptor::Directory, None)
397 .await?
398 .into_any()
399 .downcast::<FxDirectory>()
400 .unwrap();
401 Ok(internal_dir)
402 }
403
404 pub async fn terminate(&self) {
405 let task = std::mem::replace(&mut *self.background_task.lock(), None);
406 if let Some((task, terminate)) = task {
407 let _ = terminate.send(());
408 task.await;
409 }
410
411 self.scope.shutdown();
420 self.cache.terminate();
421 self.scope.wait().await;
422
423 Epoch::global().barrier().await;
425
426 self.dirent_cache.clear();
429
430 if self.store.filesystem().options().read_only {
431 if self.store.is_unlocked() {
433 self.store.lock_read_only();
434 }
435 return;
436 }
437
438 self.flush_all_files(FlushType::LastChance).await;
439 self.store.filesystem().graveyard().flush().await;
440 if self.store.crypt().is_some() {
441 if let Err(e) = self.store.lock().await {
442 warn!(error:? = e; "Locking store error");
445 }
446 }
447 let sync_status = self
448 .store
449 .filesystem()
450 .sync(SyncOptions { flush_device: true, ..Default::default() })
451 .await;
452 if let Err(e) = sync_status {
453 error!(error:? = e; "Failed to sync filesystem; data may be lost");
454 }
455 }
456
457 pub async fn get_or_load_node(
462 self: &Arc<Self>,
463 object_id: u64,
464 object_descriptor: ObjectDescriptor,
465 parent: Option<Arc<FxDirectory>>,
466 ) -> Result<Arc<dyn FxNode>, Error> {
467 match self.cache.get_or_reserve(object_id).await {
468 GetResult::Node(node) => Ok(node),
469 GetResult::Placeholder(placeholder) => {
470 let node = match object_descriptor {
471 ObjectDescriptor::File => FxFile::new(
472 ObjectStore::open_object(self, object_id, HandleOptions::default(), None)
473 .await?,
474 ) as Arc<dyn FxNode>,
475 ObjectDescriptor::Directory => {
476 Arc::new(FxDirectory::new(parent, Directory::open(self, object_id).await?))
479 as Arc<dyn FxNode>
480 }
481 ObjectDescriptor::Symlink => Arc::new(FxSymlink::new(self.clone(), object_id)),
482 _ => bail!(FxfsError::Inconsistent),
483 };
484 placeholder.commit(&node);
485 Ok(node)
486 }
487 }
488 }
489
490 pub fn mark_directory_deleted(&self, object_id: u64) {
492 if let Some(node) = self.cache.get(object_id) {
493 if let Ok(dir) = node.into_any().downcast::<FxDirectory>() {
497 dir.set_deleted();
498 }
499 }
500 }
501
502 pub(super) async fn maybe_purge_file(&self, object_id: u64) -> Result<(), Error> {
508 if let Some(node) = self.cache.get(object_id) {
509 node.clone().mark_to_be_purged();
510 return Ok(());
511 }
512 self.store
514 .tombstone_object(
515 object_id,
516 Options { borrow_metadata_space: true, ..Default::default() },
517 )
518 .await?;
519 Ok(())
520 }
521
522 pub fn start_background_task(
528 self: &Arc<Self>,
529 config: MemoryPressureConfig,
530 mem_monitor: Option<&MemoryPressureMonitor>,
531 ) {
532 let mut background_task = self.background_task.lock();
533 if background_task.is_none() {
534 let (tx, rx) = oneshot::channel();
535
536 let task = if let Some(mem_monitor) = mem_monitor {
537 fasync::Task::spawn(self.clone().background_task(
538 config,
539 mem_monitor.get_level_stream(),
540 rx,
541 ))
542 } else {
543 fasync::Task::spawn(self.clone().background_task(config, stream::pending(), rx))
545 };
546
547 *background_task = Some((task, tx));
548 }
549 }
550
551 #[trace]
552 async fn background_task(
553 self: Arc<Self>,
554 config: MemoryPressureConfig,
555 mut level_stream: impl Stream<Item = MemoryPressureLevel> + FusedStream + Unpin,
556 terminate: oneshot::Receiver<()>,
557 ) {
558 debug!(store_id = self.store.store_object_id(); "FxVolume::background_task start");
559 let mut terminate = terminate.fuse();
560 let mut level = MemoryPressureLevel::Normal;
562 let mut timer =
563 pin!(fasync::Timer::new(config.for_level(&level).background_task_initial_delay));
564
565 loop {
566 let mut should_terminate = false;
567 let mut should_flush = false;
568 let mut low_mem = false;
569 let mut should_purge_layer_files = false;
570 let mut should_update_cache_limit = false;
571
572 futures::select_biased! {
573 _ = terminate => should_terminate = true,
574 new_level = level_stream.next() => {
575 let new_level = new_level.unwrap();
577 low_mem = matches!(new_level, MemoryPressureLevel::Critical);
580 should_purge_layer_files = true;
581 if new_level != level {
582 level = new_level;
583 should_update_cache_limit = true;
584 let level_config = config.for_level(&level);
585 timer.as_mut().reset(fasync::MonotonicInstant::after(
586 level_config.background_task_period.into())
587 );
588 debug!(
589 "Background task period changed to {:?} due to new memory pressure \
590 level ({:?}).",
591 config.for_level(&level).background_task_period, level
592 );
593 }
594 }
595 _ = timer => {
596 timer.as_mut().reset(fasync::MonotonicInstant::after(
597 config.for_level(&level).background_task_period.into())
598 );
599 should_flush = true;
600 should_purge_layer_files = !matches!(level, MemoryPressureLevel::Normal);
602 }
603 };
604 if should_terminate {
605 break;
606 }
607 if should_update_cache_limit {
609 self.dirent_cache.set_limit(config.for_level(&level).cache_size_limit);
610 }
611 if should_flush {
612 self.flush_all_files(FlushType::Sync).await;
613 self.dirent_cache.recycle_stale_files();
614 } else if low_mem {
615 self.minimize_memory().await;
617 }
618 if should_purge_layer_files {
619 for layer in self.store.tree().immutable_layer_set().layers {
620 layer.purge_cached_data();
621 }
622 }
623 }
624 debug!(store_id = self.store.store_object_id(); "FxVolume::background_task end");
625 }
626
627 pub fn report_pager_dirty(
631 self: Arc<Self>,
632 byte_count: u64,
633 mark_dirty: impl FnOnce() + Send + 'static,
634 ) {
635 let this = self.clone();
636 let callback = move || {
637 let prev = this.pager_dirty_byte_count.fetch_add(byte_count, Ordering::Relaxed);
638 mark_dirty();
639 fxfs_trace::counter!("dirty-bytes", 0, this.name => prev.saturating_add(byte_count));
640 };
641 if let Some(parent) = self.parent.upgrade() {
642 parent.report_pager_dirty(byte_count, self, callback);
643 } else {
644 callback();
645 }
646 }
647
648 pub fn report_pager_clean(&self, byte_count: u64) {
650 if let Some(parent) = self.parent.upgrade() {
651 parent.report_pager_clean(byte_count);
652 }
653 let prev = self.pager_dirty_byte_count.fetch_sub(byte_count, Ordering::Relaxed);
654 fxfs_trace::counter!("dirty-bytes", 0, self.name => prev.saturating_sub(byte_count));
655 }
656
657 #[trace]
658 pub async fn flush_all_files(&self, flush_type: FlushType) {
659 let mut flushed = 0;
660 for file in self.cache.files() {
661 if let Some(node) = file.into_opened_node() {
662 if let Err(e) = FxFile::flush(&node, flush_type).await {
663 warn!(
664 store_id = self.store.store_object_id(),
665 oid = node.object_id(),
666 error:? = e;
667 "Failed to flush",
668 )
669 }
670 if flush_type == FlushType::LastChance {
671 let file = node.clone();
672 std::mem::drop(node);
673 file.force_clean();
674 }
675 }
676 flushed += 1;
677 }
678 debug!(store_id = self.store.store_object_id(), file_count = flushed; "FxVolume flushed");
679 }
680
681 #[trace]
688 pub async fn minimize_memory(&self) {
689 for file in self.cache.files() {
690 if let Some(node) = file.into_opened_node() {
691 if let Err(e) = node.handle().minimize_memory().await {
692 warn!(
693 store_id = self.store.store_object_id(),
694 oid = node.object_id(),
695 error:? = e;
696 "Failed to flush",
697 )
698 }
699 }
700 }
701 }
702
703 pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
705 if let Some(guard) = self.scope.try_active_guard() {
706 self.executor.spawn_detached(FutureWithGuard::new(guard, task));
707 }
708 }
709
710 #[cfg(any(test, feature = "testing"))]
713 pub fn try_unwrap(self: Arc<Self>) -> Option<FxVolume> {
714 self.poisoned.store(true, Ordering::Relaxed);
715 match Arc::try_unwrap(self) {
716 Ok(volume) => {
717 volume.poisoned.store(false, Ordering::Relaxed);
718 Some(volume)
719 }
720 Err(this) => {
721 info!(
723 "background_task: {}, profile_state: {}, dirent_cache count: {}, \
724 pager strong file refs={}, no tasks={}",
725 this.background_task.lock().is_some(),
726 this.profile_state.lock().is_some(),
727 this.dirent_cache.len(),
728 crate::pager::STRONG_FILE_REFS.load(Ordering::Relaxed),
729 {
730 let mut no_tasks = pin!(this.scope.wait());
731 no_tasks
732 .poll_unpin(&mut std::task::Context::from_waker(
733 std::task::Waker::noop(),
734 ))
735 .is_ready()
736 },
737 );
738 None
739 }
740 }
741 }
742
743 pub async fn handle_file_backed_volume_provider_requests(
744 this: Weak<Self>,
745 scope: ExecutionScope,
746 mut requests: FileBackedVolumeProviderRequestStream,
747 ) -> Result<(), Error> {
748 while let Some(request) = requests.try_next().await? {
749 match request {
750 FileBackedVolumeProviderRequest::Open {
751 parent_directory_token,
752 name,
753 server_end,
754 control_handle: _,
755 } => {
756 let Some(_guard) = scope.try_active_guard() else {
758 bail!("Volume shutting down")
759 };
760 let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
761 match this
762 .scope
763 .token_registry()
764 .get_owner_and_rights(parent_directory_token)
768 .and_then(|dir| {
769 dir.ok_or(zx::Status::BAD_HANDLE).and_then(|(dir, rights)| {
770 if !rights.contains(fio::Rights::MODIFY_DIRECTORY) {
771 return Err(zx::Status::BAD_HANDLE);
772 }
773 dir.into_any()
774 .downcast::<FxDirectory>()
775 .map_err(|_| zx::Status::BAD_HANDLE)
776 })
777 }) {
778 Ok(dir) => {
779 dir.open_block_file(&name, server_end).await;
780 }
781 Err(status) => {
782 let _ = server_end.close_with_epitaph(status).unwrap_or_else(|e| {
783 error!(error:? = e; "open failed to send epitaph");
784 });
785 }
786 }
787 }
788 }
789 }
790 Ok(())
791 }
792
793 pub async fn handle_project_id_requests(
794 this: Weak<Self>,
795 scope: ExecutionScope,
796 mut requests: ProjectIdRequestStream,
797 ) -> Result<(), Error> {
798 while let Some(request) = requests.try_next().await? {
799 let Some(_guard) = scope.try_active_guard() else { bail!("Volume shutting down") };
801 let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
802 let store_id = this.store.store_object_id();
803
804 match request {
805 ProjectIdRequest::SetLimit { responder, project_id, bytes, nodes } => {
806 let result = if let Some(project_id) = ProjectId::new(project_id) {
807 this.store()
808 .set_project_limit(project_id, bytes, nodes)
809 .await
810 .map_err(|error| {
811 error!(error:?, store_id, project_id; "Failed to set project limit");
812 map_to_raw_status(error)
813 })
814 } else {
815 Err(zx::Status::OUT_OF_RANGE.into_raw())
816 };
817 responder.send(result)?
818 }
819 ProjectIdRequest::Clear { responder, project_id } => {
820 let result = if let Some(project_id) = ProjectId::new(project_id) {
821 this.store().clear_project_limit(project_id).await.map_err(|error| {
822 error!(error:?, store_id, project_id; "Failed to clear project limit");
823 map_to_raw_status(error)
824 })
825 } else {
826 Err(zx::Status::OUT_OF_RANGE.into_raw())
827 };
828 responder.send(result)?
829 }
830 ProjectIdRequest::SetForNode { responder, node_id, project_id } => {
831 let result = if let Some(project_id) = ProjectId::new(project_id) {
832 this.store()
833 .set_project_for_node(node_id, project_id)
834 .await
835 .map_err(|error| {
836 error!(error:?, store_id, node_id, project_id; "Failed to apply node.");
837 map_to_raw_status(error)
838 })
839 } else {
840 Err(zx::Status::OUT_OF_RANGE.into_raw())
841 };
842 responder.send(result)?
843 }
844 ProjectIdRequest::GetForNode { responder, node_id } => responder.send(
845 this.store()
846 .get_project_for_node(node_id)
847 .await
848 .map(ProjectIdExt::raw)
849 .map_err(|error| {
850 error!(error:?, store_id, node_id; "Failed to get node.");
851 map_to_raw_status(error)
852 }),
853 )?,
854 ProjectIdRequest::ClearForNode { responder, node_id } => responder.send(
855 this.store().clear_project_for_node(node_id).await.map_err(|error| {
856 error!(error:?, store_id, node_id; "Failed to clear for node.");
857 map_to_raw_status(error)
858 }),
859 )?,
860 ProjectIdRequest::List { responder, token } => {
861 responder.send(match this.list_projects(&token).await {
862 Ok((ref entries, ref next_token)) => Ok((entries, next_token.as_ref())),
863 Err(error) => {
864 error!(error:?, store_id, token:?; "Failed to list projects.");
865 Err(map_to_raw_status(error))
866 }
867 })?
868 }
869 ProjectIdRequest::Info { responder, project_id } => {
870 responder.send(match this.project_info(project_id).await {
871 Ok((ref limit, ref usage)) => Ok((limit, usage)),
872 Err(error) => {
873 error!(error:?, store_id, project_id; "Failed to get project info.");
874 Err(map_to_raw_status(error))
875 }
876 })?
877 }
878 }
879 }
880 Ok(())
881 }
882
883 const MAX_PROJECT_ENTRIES: usize = 8184;
887
888 async fn list_projects(
891 &self,
892 last_token: &Option<Box<ProjectIterToken>>,
893 ) -> Result<(Vec<u64>, Option<ProjectIterToken>), Error> {
894 let (entries, token) = self
895 .store()
896 .list_projects(
897 last_token.as_ref().and_then(|v| ProjectId::new(v.value)),
898 Self::MAX_PROJECT_ENTRIES,
899 )
900 .await?;
901 Ok((
902 entries.into_iter().map(ProjectId::raw).collect(),
903 token.map(|value| ProjectIterToken { value: value.raw() }),
904 ))
905 }
906
907 async fn project_info(&self, project_id: u64) -> Result<(BytesAndNodes, BytesAndNodes), Error> {
908 let project_id = ProjectId::new(project_id).ok_or(FxfsError::OutOfRange)?;
909 let (limit, usage) = self.store().project_info(project_id).await?;
910 ensure!(limit.is_some() || usage.is_some(), FxfsError::NotFound);
912 Ok((
913 limit.map_or_else(
914 || BytesAndNodes { bytes: u64::MAX, nodes: u64::MAX },
915 |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
916 ),
917 usage.map_or_else(
918 || BytesAndNodes { bytes: 0, nodes: 0 },
919 |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
920 ),
921 ))
922 }
923}
924
925#[cfg(any(test, feature = "testing"))]
926impl Drop for FxVolume {
927 fn drop(&mut self) {
928 assert!(!*self.poisoned.get_mut());
929 }
930}
931
932impl HandleOwner for FxVolume {}
933
934impl AsRef<ObjectStore> for FxVolume {
935 fn as_ref(&self) -> &ObjectStore {
936 &self.store
937 }
938}
939
940#[async_trait]
941impl FsInspectVolume for FxVolume {
942 async fn get_volume_data(&self) -> Option<VolumeData> {
943 let _guard = self.scope.try_active_guard()?;
945
946 let object_count = self.store().object_count();
947 let (used_bytes, bytes_limit) =
948 self.store.filesystem().allocator().owner_allocation_info(self.store.store_object_id());
949 let encrypted = self.store().crypt().is_some();
950 let port_koid = fasync::EHandle::local().port().as_handle_ref().koid().unwrap().raw_koid();
951 Some(VolumeData { bytes_limit, used_bytes, used_nodes: object_count, encrypted, port_koid })
952 }
953}
954
955pub trait RootDir: FxNode + DirectoryEntry {
956 fn as_directory_entry(self: Arc<Self>) -> Arc<dyn DirectoryEntry>;
957
958 fn serve(self: Arc<Self>, flags: fio::Flags, server_end: ServerEnd<fio::DirectoryMarker>);
959
960 fn as_node(self: Arc<Self>) -> Arc<dyn FxNode>;
961
962 fn register_additional_volume_services(
963 self: Arc<Self>,
964 _svc_dir: &Simple,
965 ) -> Result<(), Error> {
966 Ok(())
967 }
968}
969
970#[derive(Clone)]
971pub struct FxVolumeAndRoot {
972 volume: Arc<FxVolume>,
973 root: Arc<dyn RootDir>,
974
975 admin_scope: ExecutionScope,
977
978 outgoing_dir: Arc<Simple>,
980}
981
982impl FxVolumeAndRoot {
983 pub async fn new<T: From<Directory<FxVolume>> + RootDir>(
984 parent: Weak<VolumesDirectory>,
985 store: Arc<ObjectStore>,
986 unique_id: u64,
987 volume_name: String,
988 blob_resupplied_count: Arc<PageRefaultCounter>,
989 memory_pressure_config: MemoryPressureConfig,
990 ) -> Result<Self, Error> {
991 let volume = Arc::new(FxVolume::new(
992 parent,
993 store,
994 unique_id,
995 volume_name,
996 blob_resupplied_count.clone(),
997 memory_pressure_config,
998 )?);
999 let root_object_id = volume.store().root_directory_object_id();
1000 let root_dir = Directory::open(&volume, root_object_id).await?;
1001 let root = Arc::<T>::new(root_dir.into()) as Arc<dyn RootDir>;
1002 volume
1003 .cache
1004 .get_or_reserve(root_object_id)
1005 .await
1006 .placeholder()
1007 .unwrap()
1008 .commit(&root.clone().as_node());
1009 Ok(Self {
1010 volume,
1011 root,
1012 admin_scope: ExecutionScope::new(),
1013 outgoing_dir: vfs::directory::immutable::simple(),
1014 })
1015 }
1016
1017 pub fn volume(&self) -> &Arc<FxVolume> {
1018 &self.volume
1019 }
1020
1021 pub fn root(&self) -> &Arc<dyn RootDir> {
1022 &self.root
1023 }
1024
1025 pub fn admin_scope(&self) -> &ExecutionScope {
1026 &self.admin_scope
1027 }
1028
1029 pub fn outgoing_dir(&self) -> &Arc<Simple> {
1030 &self.outgoing_dir
1031 }
1032
1033 pub fn root_dir(&self) -> Arc<FxDirectory> {
1035 self.root().clone().into_any().downcast::<FxDirectory>().expect("Invalid type for root")
1036 }
1037
1038 pub fn into_volume(self) -> Arc<FxVolume> {
1039 self.volume
1040 }
1041}
1042
1043const TOTAL_NODES: u64 = i64::MAX as u64;
1047
1048const FXFS_INFO_NAME_FIDL: [i8; 32] = [
1051 0x66, 0x78, 0x66, 0x73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1052 0, 0, 0, 0,
1053];
1054
1055fn info_to_filesystem_info(
1056 info: filesystem::Info,
1057 block_size: u64,
1058 object_count: u64,
1059 fs_id: u64,
1060) -> fio::FilesystemInfo {
1061 fio::FilesystemInfo {
1062 total_bytes: info.total_bytes,
1063 used_bytes: info.used_bytes,
1064 total_nodes: TOTAL_NODES,
1065 used_nodes: object_count,
1066 free_shared_pool_bytes: 0,
1068 fs_id,
1069 block_size: block_size as u32,
1070 max_filename_size: fio::MAX_NAME_LENGTH as u32,
1071 fs_type: fidl_fuchsia_fs::VfsType::Fxfs.into_primitive(),
1072 padding: 0,
1073 name: FXFS_INFO_NAME_FIDL,
1074 }
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079 use super::DIRENT_CACHE_LIMIT;
1080 use crate::fuchsia::file::FxFile;
1081 use crate::fuchsia::fxblob::testing::{self as blob_testing, BlobFixture};
1082 use crate::fuchsia::memory_pressure::MemoryPressureLevel;
1083 use crate::fuchsia::pager::PagerBacked;
1084 use crate::fuchsia::profile::{RECORDED, new_profile_state};
1085 use crate::fuchsia::testing::{
1086 TestFixture, TestFixtureOptions, close_dir_checked, close_file_checked, open_dir,
1087 open_dir_checked, open_file, open_file_checked,
1088 };
1089 use crate::fuchsia::volume::{FxVolume, MemoryPressureConfig, MemoryPressureLevelConfig};
1090 use crate::fuchsia::volumes_directory::VolumesDirectory;
1091 use delivery_blob::CompressionMode;
1092 use fidl_fuchsia_fxfs::{BytesAndNodes, ProjectIdMarker};
1093 use fidl_fuchsia_io as fio;
1094 use fs_inspect::FsInspectVolume;
1095 use fuchsia_async::{self as fasync, TimeoutExt as _};
1096 use fuchsia_component_client::connect_to_protocol_at_dir_svc;
1097 use fuchsia_fs::file;
1098 use fxfs::filesystem::{FxFilesystem, FxFilesystemBuilder};
1099 use fxfs::fsck::{fsck, fsck_volume};
1100 use fxfs::object_store::directory::replace_child;
1101 use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
1102 use fxfs::object_store::volume::root_volume;
1103 use fxfs::object_store::{HandleOptions, ObjectDescriptor, ObjectStore, StoreOptions};
1104 use fxfs_crypt_common::CryptBase;
1105 use fxfs_crypto::{Crypt, WrappingKeyId};
1106 use fxfs_insecure_crypto::new_insecure_crypt;
1107 use refaults_vmo::PageRefaultCounter;
1108 use std::sync::atomic::Ordering;
1109 use std::sync::{Arc, Weak};
1110 use std::time::Duration;
1111 use storage_device::DeviceHolder;
1112 use storage_device::fake_device::FakeDevice;
1113 use zx::Status;
1114
1115 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
1116
1117 #[fuchsia::test(threads = 10)]
1118 async fn test_rename_different_dirs() {
1119 use zx::Event;
1120
1121 let fixture = TestFixture::new().await;
1122 let root = fixture.root();
1123
1124 let src = open_dir_checked(
1125 &root,
1126 "foo",
1127 fio::Flags::FLAG_MAYBE_CREATE
1128 | fio::PERM_READABLE
1129 | fio::PERM_WRITABLE
1130 | fio::Flags::PROTOCOL_DIRECTORY,
1131 Default::default(),
1132 )
1133 .await;
1134
1135 let dst = open_dir_checked(
1136 &root,
1137 "bar",
1138 fio::Flags::FLAG_MAYBE_CREATE
1139 | fio::PERM_READABLE
1140 | fio::PERM_WRITABLE
1141 | fio::Flags::PROTOCOL_DIRECTORY,
1142 Default::default(),
1143 )
1144 .await;
1145
1146 let f = open_file_checked(
1147 &root,
1148 "foo/a",
1149 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1150 &Default::default(),
1151 )
1152 .await;
1153 close_file_checked(f).await;
1154
1155 let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1156 Status::ok(status).expect("get_token failed");
1157 src.rename("a", Event::from(dst_token.unwrap()), "b")
1158 .await
1159 .expect("FIDL call failed")
1160 .expect("rename failed");
1161
1162 assert_eq!(
1163 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1164 .await
1165 .expect_err("Open succeeded")
1166 .root_cause()
1167 .downcast_ref::<Status>()
1168 .expect("No status"),
1169 &Status::NOT_FOUND,
1170 );
1171 let f =
1172 open_file_checked(&root, "bar/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1173 close_file_checked(f).await;
1174
1175 close_dir_checked(dst).await;
1176 close_dir_checked(src).await;
1177 fixture.close().await;
1178 }
1179
1180 #[fuchsia::test(threads = 10)]
1181 async fn test_rename_same_dir() {
1182 use zx::Event;
1183 let fixture = TestFixture::new().await;
1184 let root = fixture.root();
1185
1186 let src = open_dir_checked(
1187 &root,
1188 "foo",
1189 fio::Flags::FLAG_MAYBE_CREATE
1190 | fio::PERM_READABLE
1191 | fio::PERM_WRITABLE
1192 | fio::Flags::PROTOCOL_DIRECTORY,
1193 Default::default(),
1194 )
1195 .await;
1196
1197 let f = open_file_checked(
1198 &root,
1199 "foo/a",
1200 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1201 &Default::default(),
1202 )
1203 .await;
1204 close_file_checked(f).await;
1205
1206 let (status, src_token) = src.get_token().await.expect("FIDL call failed");
1207 Status::ok(status).expect("get_token failed");
1208 src.rename("a", Event::from(src_token.unwrap()), "b")
1209 .await
1210 .expect("FIDL call failed")
1211 .expect("rename failed");
1212
1213 assert_eq!(
1214 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1215 .await
1216 .expect_err("Open succeeded")
1217 .root_cause()
1218 .downcast_ref::<Status>()
1219 .expect("No status"),
1220 &Status::NOT_FOUND,
1221 );
1222 let f =
1223 open_file_checked(&root, "foo/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1224 close_file_checked(f).await;
1225
1226 close_dir_checked(src).await;
1227 fixture.close().await;
1228 }
1229
1230 #[fuchsia::test(threads = 10)]
1231 async fn test_rename_overwrites_file() {
1232 use zx::Event;
1233 let fixture = TestFixture::new().await;
1234 let root = fixture.root();
1235
1236 let src = open_dir_checked(
1237 &root,
1238 "foo",
1239 fio::Flags::FLAG_MAYBE_CREATE
1240 | fio::PERM_READABLE
1241 | fio::PERM_WRITABLE
1242 | fio::Flags::PROTOCOL_DIRECTORY,
1243 Default::default(),
1244 )
1245 .await;
1246
1247 let dst = open_dir_checked(
1248 &root,
1249 "bar",
1250 fio::Flags::FLAG_MAYBE_CREATE
1251 | fio::PERM_READABLE
1252 | fio::PERM_WRITABLE
1253 | fio::Flags::PROTOCOL_DIRECTORY,
1254 Default::default(),
1255 )
1256 .await;
1257
1258 let src_file = open_file_checked(
1260 &root,
1261 "foo/a",
1262 fio::Flags::FLAG_MAYBE_CREATE
1263 | fio::PERM_READABLE
1264 | fio::PERM_WRITABLE
1265 | fio::Flags::PROTOCOL_FILE,
1266 &Default::default(),
1267 )
1268 .await;
1269 let buf = vec![0xaa as u8; 8192];
1270 file::write(&src_file, buf.as_slice()).await.expect("Failed to write to file");
1271 close_file_checked(src_file).await;
1272
1273 let f = open_file_checked(
1275 &root,
1276 "bar/b",
1277 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1278 &Default::default(),
1279 )
1280 .await;
1281 close_file_checked(f).await;
1282
1283 let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1284 Status::ok(status).expect("get_token failed");
1285 src.rename("a", Event::from(dst_token.unwrap()), "b")
1286 .await
1287 .expect("FIDL call failed")
1288 .expect("rename failed");
1289
1290 assert_eq!(
1291 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1292 .await
1293 .expect_err("Open succeeded")
1294 .root_cause()
1295 .downcast_ref::<Status>()
1296 .expect("No status"),
1297 &Status::NOT_FOUND,
1298 );
1299 let file = open_file_checked(
1300 &root,
1301 "bar/b",
1302 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1303 &Default::default(),
1304 )
1305 .await;
1306 let buf = file::read(&file).await.expect("read file failed");
1307 assert_eq!(buf, vec![0xaa as u8; 8192]);
1308 close_file_checked(file).await;
1309
1310 close_dir_checked(dst).await;
1311 close_dir_checked(src).await;
1312 fixture.close().await;
1313 }
1314
1315 #[fuchsia::test(threads = 10)]
1316 async fn test_rename_overwrites_dir() {
1317 use zx::Event;
1318 let fixture = TestFixture::new().await;
1319 let root = fixture.root();
1320
1321 let src = open_dir_checked(
1322 &root,
1323 "foo",
1324 fio::Flags::FLAG_MAYBE_CREATE
1325 | fio::PERM_READABLE
1326 | fio::PERM_WRITABLE
1327 | fio::Flags::PROTOCOL_DIRECTORY,
1328 Default::default(),
1329 )
1330 .await;
1331
1332 let dst = open_dir_checked(
1333 &root,
1334 "bar",
1335 fio::Flags::FLAG_MAYBE_CREATE
1336 | fio::PERM_READABLE
1337 | fio::PERM_WRITABLE
1338 | fio::Flags::PROTOCOL_DIRECTORY,
1339 Default::default(),
1340 )
1341 .await;
1342
1343 open_dir_checked(
1345 &root,
1346 "foo/a",
1347 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1348 Default::default(),
1349 )
1350 .await;
1351 open_file_checked(
1352 &root,
1353 "foo/a/file",
1354 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1355 &Default::default(),
1356 )
1357 .await;
1358 open_dir_checked(
1359 &root,
1360 "bar/b",
1361 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1362 Default::default(),
1363 )
1364 .await;
1365
1366 let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1367 Status::ok(status).expect("get_token failed");
1368 src.rename("a", Event::from(dst_token.unwrap()), "b")
1369 .await
1370 .expect("FIDL call failed")
1371 .expect("rename failed");
1372
1373 assert_eq!(
1374 open_dir(&root, "foo/a", fio::Flags::PROTOCOL_DIRECTORY, &Default::default())
1375 .await
1376 .expect_err("Open succeeded")
1377 .root_cause()
1378 .downcast_ref::<Status>()
1379 .expect("No status"),
1380 &Status::NOT_FOUND,
1381 );
1382 let f =
1383 open_file_checked(&root, "bar/b/file", fio::Flags::PROTOCOL_FILE, &Default::default())
1384 .await;
1385 close_file_checked(f).await;
1386
1387 close_dir_checked(dst).await;
1388 close_dir_checked(src).await;
1389
1390 fixture.close().await;
1391 }
1392
1393 #[fuchsia::test]
1394 async fn test_background_flush() {
1395 let fixture = TestFixture::new().await;
1396 {
1397 let file = open_file_checked(
1398 fixture.root(),
1399 "file",
1400 fio::Flags::FLAG_MAYBE_CREATE
1401 | fio::PERM_READABLE
1402 | fio::PERM_WRITABLE
1403 | fio::Flags::PROTOCOL_FILE,
1404 &Default::default(),
1405 )
1406 .await;
1407 let object_id = file
1408 .get_attributes(fio::NodeAttributesQuery::ID)
1409 .await
1410 .expect("Fidl get attr")
1411 .expect("get attr")
1412 .1
1413 .id
1414 .unwrap();
1415
1416 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1418
1419 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1421 let volume = fixture.volume().volume().clone();
1422
1423 let data_has_persisted = || async {
1424 let object =
1427 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1428 .await
1429 .expect("open_object failed");
1430 let data = object.contents(8192).await.expect("read failed");
1431 data.len() == 1 && data[..] == [123u8]
1432 };
1433 assert!(!data_has_persisted().await);
1434
1435 fixture.volume().volume().start_background_task(
1436 MemoryPressureConfig {
1437 mem_normal: MemoryPressureLevelConfig {
1438 background_task_period: Duration::from_millis(100),
1439 background_task_initial_delay: Duration::from_millis(100),
1440 ..Default::default()
1441 },
1442 mem_warning: Default::default(),
1443 mem_critical: Default::default(),
1444 },
1445 None,
1446 );
1447
1448 const MAX_WAIT: Duration = Duration::from_secs(20);
1449 let wait_increments = Duration::from_millis(400);
1450 let mut total_waited = Duration::ZERO;
1451
1452 while total_waited < MAX_WAIT {
1453 fasync::Timer::new(wait_increments).await;
1454 total_waited += wait_increments;
1455
1456 if data_has_persisted().await {
1457 break;
1458 }
1459 }
1460
1461 assert!(data_has_persisted().await);
1462 }
1463
1464 fixture.close().await;
1465 }
1466
1467 #[fuchsia::test(threads = 2)]
1468 async fn test_background_flush_with_warning_memory_pressure() {
1469 let fixture = TestFixture::new().await;
1470 {
1471 let file = open_file_checked(
1472 fixture.root(),
1473 "file",
1474 fio::Flags::FLAG_MAYBE_CREATE
1475 | fio::PERM_READABLE
1476 | fio::PERM_WRITABLE
1477 | fio::Flags::PROTOCOL_FILE,
1478 &Default::default(),
1479 )
1480 .await;
1481 let object_id = file
1482 .get_attributes(fio::NodeAttributesQuery::ID)
1483 .await
1484 .expect("Fidl get attr")
1485 .expect("get attr")
1486 .1
1487 .id
1488 .unwrap();
1489
1490 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1492
1493 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1495 let volume = fixture.volume().volume().clone();
1496
1497 let data_has_persisted = || async {
1498 let object =
1501 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1502 .await
1503 .expect("open_object failed");
1504 let data = object.contents(8192).await.expect("read failed");
1505 data.len() == 1 && data[..] == [123u8]
1506 };
1507 assert!(!data_has_persisted().await);
1508
1509 let flush_config = MemoryPressureConfig {
1511 mem_normal: MemoryPressureLevelConfig {
1512 background_task_period: Duration::from_secs(20),
1513 cache_size_limit: DIRENT_CACHE_LIMIT,
1514 ..Default::default()
1515 },
1516 mem_warning: MemoryPressureLevelConfig {
1517 background_task_period: Duration::from_millis(100),
1518 cache_size_limit: 100,
1519 background_task_initial_delay: Duration::from_millis(100),
1520 ..Default::default()
1521 },
1522 mem_critical: MemoryPressureLevelConfig {
1523 background_task_period: Duration::from_secs(20),
1524 cache_size_limit: 50,
1525 ..Default::default()
1526 },
1527 };
1528 fixture.volume().volume().start_background_task(
1529 flush_config,
1530 fixture.volumes_directory().memory_pressure_monitor(),
1531 );
1532
1533 fixture
1535 .memory_pressure_proxy()
1536 .on_level_changed(MemoryPressureLevel::Warning)
1537 .await
1538 .expect("Failed to send memory pressure level change");
1539
1540 const MAX_WAIT: Duration = Duration::from_secs(3);
1543 let wait_increments = Duration::from_millis(400);
1544 let mut total_waited = Duration::ZERO;
1545
1546 while total_waited < MAX_WAIT {
1547 fasync::Timer::new(wait_increments).await;
1548 total_waited += wait_increments;
1549
1550 if data_has_persisted().await {
1551 break;
1552 }
1553 }
1554
1555 assert!(data_has_persisted().await);
1556 assert_eq!(fixture.volume().volume().dirent_cache().limit(), 100);
1557 }
1558
1559 fixture.close().await;
1560 }
1561
1562 #[fuchsia::test(threads = 2)]
1563 async fn test_background_flush_with_critical_memory_pressure() {
1564 let fixture = TestFixture::new().await;
1565 {
1566 let file = open_file_checked(
1567 fixture.root(),
1568 "file",
1569 fio::Flags::FLAG_MAYBE_CREATE
1570 | fio::PERM_READABLE
1571 | fio::PERM_WRITABLE
1572 | fio::Flags::PROTOCOL_FILE,
1573 &Default::default(),
1574 )
1575 .await;
1576 let object_id = file
1577 .get_attributes(fio::NodeAttributesQuery::ID)
1578 .await
1579 .expect("Fidl get attr")
1580 .expect("get attr")
1581 .1
1582 .id
1583 .unwrap();
1584
1585 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1587
1588 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1590 let volume = fixture.volume().volume().clone();
1591
1592 let data_has_persisted = || async {
1593 let object =
1596 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1597 .await
1598 .expect("open_object failed");
1599 let data = object.contents(8192).await.expect("read failed");
1600 data.len() == 1 && data[..] == [123u8]
1601 };
1602 assert!(!data_has_persisted().await);
1603
1604 let flush_config = MemoryPressureConfig {
1605 mem_normal: MemoryPressureLevelConfig {
1606 cache_size_limit: DIRENT_CACHE_LIMIT,
1607 ..Default::default()
1608 },
1609 mem_warning: MemoryPressureLevelConfig {
1610 cache_size_limit: 100,
1611 ..Default::default()
1612 },
1613 mem_critical: MemoryPressureLevelConfig {
1614 cache_size_limit: 50,
1615 ..Default::default()
1616 },
1617 };
1618 fixture.volume().volume().start_background_task(
1619 flush_config,
1620 fixture.volumes_directory().memory_pressure_monitor(),
1621 );
1622
1623 fixture
1625 .memory_pressure_proxy()
1626 .on_level_changed(MemoryPressureLevel::Critical)
1627 .await
1628 .expect("Failed to send memory pressure level change");
1629
1630 const MAX_WAIT: Duration = Duration::from_secs(2);
1632 let wait_increments = Duration::from_millis(400);
1633 let mut total_waited = Duration::ZERO;
1634
1635 while total_waited < MAX_WAIT {
1636 fasync::Timer::new(wait_increments).await;
1637 total_waited += wait_increments;
1638
1639 if data_has_persisted().await {
1640 break;
1641 }
1642 }
1643
1644 assert!(data_has_persisted().await);
1645 assert_eq!(fixture.volume().volume().dirent_cache().limit(), 50);
1646 }
1647
1648 fixture.close().await;
1649 }
1650
1651 #[fuchsia::test(threads = 2)]
1654 async fn test_query_info_unmounted_volume() {
1655 const TEST_VOLUME: &str = "test_1234";
1656 let crypt = Arc::new(new_insecure_crypt()) as Arc<dyn Crypt>;
1657
1658 let fixture = TestFixture::new().await;
1659 {
1660 let volumes_directory = fixture.volumes_directory();
1661 let volume = volumes_directory
1662 .create_and_mount_volume(TEST_VOLUME, Some(crypt.clone()), false, None)
1663 .await
1664 .unwrap();
1665
1666 assert!(volume.volume().get_volume_data().await.is_some());
1667
1668 volumes_directory
1670 .lock()
1671 .await
1672 .unmount(volume.volume().store().store_object_id())
1673 .await
1674 .expect("unmount failed");
1675
1676 assert!(volume.volume().get_volume_data().await.is_none());
1678 }
1679 fixture.close().await;
1680 }
1681
1682 #[fuchsia::test]
1683 async fn test_project_limit_persistence() {
1684 const BYTES_LIMIT_1: u64 = 123456;
1685 const NODES_LIMIT_1: u64 = 4321;
1686 const BYTES_LIMIT_2: u64 = 456789;
1687 const NODES_LIMIT_2: u64 = 9876;
1688 const VOLUME_NAME: &str = "A";
1689 const FILE_NAME: &str = "B";
1690 const PROJECT_ID: u64 = 42;
1691 const PROJECT_ID2: u64 = 343;
1692 let volume_store_id;
1693 let node_id;
1694 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1695 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
1696 {
1697 let blob_resupplied_count =
1698 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1699 let volumes_directory = VolumesDirectory::new(
1700 root_volume(filesystem.clone()).await.unwrap(),
1701 Weak::new(),
1702 None,
1703 blob_resupplied_count,
1704 MemoryPressureConfig::default(),
1705 )
1706 .await
1707 .unwrap();
1708
1709 let volume_and_root = volumes_directory
1710 .create_and_mount_volume(VOLUME_NAME, None, false, None)
1711 .await
1712 .expect("create unencrypted volume failed");
1713 volume_store_id = volume_and_root.volume().store().store_object_id();
1714
1715 let (volume_dir_proxy, dir_server_end) =
1716 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1717 volumes_directory
1718 .serve_volume(&volume_and_root, dir_server_end, false)
1719 .expect("serve_volume failed");
1720
1721 let project_proxy =
1722 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1723 .expect("Unable to connect to project id service");
1724
1725 project_proxy
1726 .set_limit(0, BYTES_LIMIT_1, NODES_LIMIT_1)
1727 .await
1728 .unwrap()
1729 .expect_err("Should not set limits for project id 0");
1730
1731 assert_eq!(
1732 project_proxy.clear(0).await.unwrap().expect_err("Should not clear project id 0"),
1733 Status::OUT_OF_RANGE.into_raw()
1734 );
1735
1736 assert_eq!(
1737 project_proxy
1738 .info(0)
1739 .await
1740 .unwrap()
1741 .expect_err("Should not get info for project id 0"),
1742 Status::OUT_OF_RANGE.into_raw()
1743 );
1744
1745 project_proxy
1746 .set_limit(PROJECT_ID, BYTES_LIMIT_1, NODES_LIMIT_1)
1747 .await
1748 .unwrap()
1749 .expect("To set limits");
1750 {
1751 let BytesAndNodes { bytes, nodes } =
1752 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1753 assert_eq!(bytes, BYTES_LIMIT_1);
1754 assert_eq!(nodes, NODES_LIMIT_1);
1755 }
1756
1757 let file_proxy = {
1758 let (root_proxy, root_server_end) =
1759 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1760 volume_dir_proxy
1761 .open(
1762 "root",
1763 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1764 &Default::default(),
1765 root_server_end.into_channel(),
1766 )
1767 .expect("Failed to open volume root");
1768
1769 open_file_checked(
1770 &root_proxy,
1771 FILE_NAME,
1772 fio::Flags::FLAG_MAYBE_CREATE
1773 | fio::PERM_READABLE
1774 | fio::PERM_WRITABLE
1775 | fio::Flags::PROTOCOL_FILE,
1776 &Default::default(),
1777 )
1778 .await
1779 };
1780
1781 let (_, immutable_attributes) =
1782 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
1783 node_id = immutable_attributes.id.unwrap();
1784
1785 project_proxy
1786 .set_for_node(node_id, 0)
1787 .await
1788 .unwrap()
1789 .expect_err("Should not set 0 project id");
1790
1791 project_proxy
1792 .set_for_node(node_id, PROJECT_ID)
1793 .await
1794 .unwrap()
1795 .expect("Setting project on node");
1796
1797 project_proxy
1798 .set_limit(PROJECT_ID, BYTES_LIMIT_2, NODES_LIMIT_2)
1799 .await
1800 .unwrap()
1801 .expect("To set limits");
1802 {
1803 let BytesAndNodes { bytes, nodes } =
1804 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1805 assert_eq!(bytes, BYTES_LIMIT_2);
1806 assert_eq!(nodes, NODES_LIMIT_2);
1807 }
1808
1809 assert_eq!(
1810 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1811 PROJECT_ID
1812 );
1813
1814 volumes_directory.terminate().await;
1815 filesystem.close().await.expect("close filesystem failed");
1816 }
1817 device = filesystem.take_device().await;
1818 device.ensure_unique();
1819 device.reopen(false);
1820 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
1821 {
1822 fsck(filesystem.clone()).await.expect("Fsck");
1823 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
1824 let blob_resupplied_count =
1825 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1826 let volumes_directory = VolumesDirectory::new(
1827 root_volume(filesystem.clone()).await.unwrap(),
1828 Weak::new(),
1829 None,
1830 blob_resupplied_count,
1831 MemoryPressureConfig::default(),
1832 )
1833 .await
1834 .unwrap();
1835 let volume_and_root = volumes_directory
1836 .mount_volume(VOLUME_NAME, None, false)
1837 .await
1838 .expect("mount unencrypted volume failed");
1839
1840 let (volume_proxy, _scope) = crate::volumes_directory::serve_startup_volume_proxy(
1841 &volumes_directory,
1842 VOLUME_NAME,
1843 );
1844
1845 let project_proxy = {
1846 let (volume_dir_proxy, dir_server_end) =
1847 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1848 volumes_directory
1849 .serve_volume(&volume_and_root, dir_server_end, false)
1850 .expect("serve_volume failed");
1851
1852 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1853 .expect("Unable to connect to project id service")
1854 };
1855
1856 let usage_bytes_and_nodes = {
1857 let (
1858 BytesAndNodes { bytes: limit_bytes, nodes: limit_nodes },
1859 usage_bytes_and_nodes,
1860 ) = project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info");
1861 assert_eq!(limit_bytes, BYTES_LIMIT_2);
1862 assert_eq!(limit_nodes, NODES_LIMIT_2);
1863 usage_bytes_and_nodes
1864 };
1865
1866 project_proxy.clear(PROJECT_ID).await.unwrap().expect("To clear limits");
1868
1869 assert_eq!(
1870 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1871 PROJECT_ID
1872 );
1873 project_proxy
1874 .set_for_node(node_id, PROJECT_ID2)
1875 .await
1876 .unwrap()
1877 .expect("Changing project");
1878 assert_eq!(
1879 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1880 PROJECT_ID2
1881 );
1882
1883 assert_eq!(
1884 project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
1885 Status::NOT_FOUND.into_raw()
1886 );
1887 assert_eq!(
1888 project_proxy.info(PROJECT_ID2).await.unwrap().expect("Fetching project info").1,
1889 usage_bytes_and_nodes
1890 );
1891
1892 std::mem::drop(volume_proxy);
1893 volumes_directory.terminate().await;
1894 std::mem::drop(volumes_directory);
1895 filesystem.close().await.expect("close filesystem failed");
1896 }
1897 device = filesystem.take_device().await;
1898 device.ensure_unique();
1899 device.reopen(false);
1900 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
1901 fsck(filesystem.clone()).await.expect("Fsck");
1902 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
1903 let blob_resupplied_count =
1904 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1905 let volumes_directory = VolumesDirectory::new(
1906 root_volume(filesystem.clone()).await.unwrap(),
1907 Weak::new(),
1908 None,
1909 blob_resupplied_count,
1910 MemoryPressureConfig::default(),
1911 )
1912 .await
1913 .unwrap();
1914 let volume_and_root = volumes_directory
1915 .mount_volume(VOLUME_NAME, None, false)
1916 .await
1917 .expect("mount unencrypted volume failed");
1918 let (volume_dir_proxy, dir_server_end) =
1919 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1920 volumes_directory
1921 .serve_volume(&volume_and_root, dir_server_end, false)
1922 .expect("serve_volume failed");
1923 let project_proxy = connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1924 .expect("Unable to connect to project id service");
1925 assert_eq!(
1926 project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
1927 Status::NOT_FOUND.into_raw()
1928 );
1929 volumes_directory.terminate().await;
1930 std::mem::drop(volumes_directory);
1931 filesystem.close().await.expect("close filesystem failed");
1932 }
1933
1934 #[fuchsia::test]
1935 async fn test_project_limit_accounting() {
1936 const BYTES_LIMIT: u64 = 123456;
1937 const NODES_LIMIT: u64 = 4321;
1938 const VOLUME_NAME: &str = "A";
1939 const FILE_NAME: &str = "B";
1940 const PROJECT_ID: u64 = 42;
1941 let volume_store_id;
1942 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1943 let first_object_id;
1944 let mut bytes_usage;
1945 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
1946 {
1947 let blob_resupplied_count =
1948 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1949 let volumes_directory = VolumesDirectory::new(
1950 root_volume(filesystem.clone()).await.unwrap(),
1951 Weak::new(),
1952 None,
1953 blob_resupplied_count,
1954 MemoryPressureConfig::default(),
1955 )
1956 .await
1957 .unwrap();
1958
1959 let volume_and_root = volumes_directory
1960 .create_and_mount_volume(
1961 VOLUME_NAME,
1962 Some(Arc::new(new_insecure_crypt())),
1963 false,
1964 None,
1965 )
1966 .await
1967 .expect("create unencrypted volume failed");
1968 volume_store_id = volume_and_root.volume().store().store_object_id();
1969
1970 let (volume_dir_proxy, dir_server_end) =
1971 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1972 volumes_directory
1973 .serve_volume(&volume_and_root, dir_server_end, false)
1974 .expect("serve_volume failed");
1975
1976 let project_proxy =
1977 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1978 .expect("Unable to connect to project id service");
1979
1980 project_proxy
1981 .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
1982 .await
1983 .unwrap()
1984 .expect("To set limits");
1985 {
1986 let BytesAndNodes { bytes, nodes } =
1987 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1988 assert_eq!(bytes, BYTES_LIMIT);
1989 assert_eq!(nodes, NODES_LIMIT);
1990 }
1991
1992 let file_proxy = {
1993 let (root_proxy, root_server_end) =
1994 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1995 volume_dir_proxy
1996 .open(
1997 "root",
1998 fio::PERM_READABLE | fio::PERM_WRITABLE,
1999 &Default::default(),
2000 root_server_end.into_channel(),
2001 )
2002 .expect("Failed to open volume root");
2003
2004 open_file_checked(
2005 &root_proxy,
2006 FILE_NAME,
2007 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2008 &Default::default(),
2009 )
2010 .await
2011 };
2012
2013 assert_eq!(
2014 8192,
2015 file_proxy
2016 .write(&vec![0xff as u8; 8192])
2017 .await
2018 .expect("FIDL call failed")
2019 .map_err(Status::from_raw)
2020 .expect("File write was successful")
2021 );
2022 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2023
2024 {
2025 let BytesAndNodes { bytes, nodes } =
2026 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2027 assert_eq!(bytes, 0);
2028 assert_eq!(nodes, 0);
2029 }
2030
2031 let (_, immutable_attributes) =
2032 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2033 let node_id = immutable_attributes.id.unwrap();
2034
2035 first_object_id = node_id;
2036 project_proxy
2037 .set_for_node(node_id, PROJECT_ID)
2038 .await
2039 .unwrap()
2040 .expect("Setting project on node");
2041
2042 bytes_usage = {
2043 let BytesAndNodes { bytes, nodes } =
2044 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2045 assert!(bytes > 0);
2046 assert_eq!(nodes, 1);
2047 bytes
2048 };
2049
2050 assert_eq!(
2052 8192,
2053 file_proxy
2054 .write(&vec![0xff as u8; 8192])
2055 .await
2056 .expect("FIDL call failed")
2057 .map_err(Status::from_raw)
2058 .expect("File write was successful")
2059 );
2060 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2061 bytes_usage = {
2062 let BytesAndNodes { bytes, nodes } =
2063 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2064 assert!(bytes > bytes_usage);
2065 assert_eq!(nodes, 1);
2066 bytes
2067 };
2068
2069 volumes_directory.terminate().await;
2070 filesystem.close().await.expect("close filesystem failed");
2071 }
2072 device = filesystem.take_device().await;
2073 device.ensure_unique();
2074 device.reopen(false);
2075 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2076 {
2077 fsck(filesystem.clone()).await.expect("Fsck");
2078 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2079 .await
2080 .expect("Fsck volume");
2081 let blob_resupplied_count =
2082 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2083 let volumes_directory = VolumesDirectory::new(
2084 root_volume(filesystem.clone()).await.unwrap(),
2085 Weak::new(),
2086 None,
2087 blob_resupplied_count,
2088 MemoryPressureConfig::default(),
2089 )
2090 .await
2091 .unwrap();
2092 let volume_and_root = volumes_directory
2093 .mount_volume(VOLUME_NAME, Some(Arc::new(new_insecure_crypt())), false)
2094 .await
2095 .expect("mount unencrypted volume failed");
2096
2097 let (root_proxy, project_proxy) = {
2098 let (volume_dir_proxy, dir_server_end) =
2099 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2100 volumes_directory
2101 .serve_volume(&volume_and_root, dir_server_end, false)
2102 .expect("serve_volume failed");
2103
2104 let (root_proxy, root_server_end) =
2105 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2106 volume_dir_proxy
2107 .open(
2108 "root",
2109 fio::PERM_READABLE | fio::PERM_WRITABLE,
2110 &Default::default(),
2111 root_server_end.into_channel(),
2112 )
2113 .expect("Failed to open volume root");
2114 let project_proxy = {
2115 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2116 .expect("Unable to connect to project id service")
2117 };
2118 (root_proxy, project_proxy)
2119 };
2120
2121 {
2122 let BytesAndNodes { bytes, nodes } =
2123 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2124 assert_eq!(bytes, bytes_usage);
2125 assert_eq!(nodes, 1);
2126 }
2127
2128 assert_eq!(
2129 project_proxy
2130 .get_for_node(first_object_id)
2131 .await
2132 .unwrap()
2133 .expect("Checking project"),
2134 PROJECT_ID
2135 );
2136 root_proxy
2137 .unlink(FILE_NAME, &fio::UnlinkOptions::default())
2138 .await
2139 .expect("FIDL call failed")
2140 .expect("unlink failed");
2141 filesystem.graveyard().flush().await;
2142
2143 {
2144 let BytesAndNodes { bytes, nodes } =
2145 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2146 assert_eq!(bytes, 0);
2147 assert_eq!(nodes, 0);
2148 }
2149
2150 let file_proxy = open_file_checked(
2151 &root_proxy,
2152 FILE_NAME,
2153 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2154 &Default::default(),
2155 )
2156 .await;
2157
2158 let (_, immutable_attributes) =
2159 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2160 let node_id = immutable_attributes.id.unwrap();
2161
2162 project_proxy
2163 .set_for_node(node_id, PROJECT_ID)
2164 .await
2165 .unwrap()
2166 .expect("Applying project");
2167
2168 bytes_usage = {
2169 let BytesAndNodes { bytes, nodes } =
2170 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2171 assert!(bytes < bytes_usage);
2173 assert_eq!(nodes, 1);
2174 bytes
2175 };
2176
2177 assert_eq!(
2178 8192,
2179 file_proxy
2180 .write(&vec![0xff as u8; 8192])
2181 .await
2182 .expect("FIDL call failed")
2183 .map_err(Status::from_raw)
2184 .expect("File write was successful")
2185 );
2186 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2187 bytes_usage = {
2188 let BytesAndNodes { bytes, nodes } =
2189 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2190 assert!(bytes > bytes_usage);
2191 assert_eq!(nodes, 1);
2192 bytes
2193 };
2194
2195 file_proxy.resize(0).await.expect("FIDL call failed").expect("Resize file");
2197 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2198 {
2199 let BytesAndNodes { bytes, nodes } =
2200 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2201 assert!(bytes < bytes_usage);
2202 assert_eq!(nodes, 1);
2203 };
2204
2205 project_proxy
2207 .clear_for_node(node_id)
2208 .await
2209 .expect("FIDL call failed")
2210 .expect("Clear failed.");
2211 {
2212 let BytesAndNodes { bytes, nodes } =
2213 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2214 assert_eq!(bytes, 0);
2215 assert_eq!(nodes, 0);
2216 };
2217
2218 volumes_directory.terminate().await;
2219 filesystem.close().await.expect("close filesystem failed");
2220 }
2221 device = filesystem.take_device().await;
2222 device.ensure_unique();
2223 device.reopen(false);
2224 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2225 fsck(filesystem.clone()).await.expect("Fsck");
2226 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2227 .await
2228 .expect("Fsck volume");
2229 filesystem.close().await.expect("close filesystem failed");
2230 }
2231
2232 #[fuchsia::test]
2233 async fn test_project_node_inheritance() {
2234 const BYTES_LIMIT: u64 = 123456;
2235 const NODES_LIMIT: u64 = 4321;
2236 const VOLUME_NAME: &str = "A";
2237 const DIR_NAME: &str = "B";
2238 const SUBDIR_NAME: &str = "C";
2239 const FILE_NAME: &str = "D";
2240 const PROJECT_ID: u64 = 42;
2241 let volume_store_id;
2242 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2243 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2244 {
2245 let blob_resupplied_count =
2246 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2247 let volumes_directory = VolumesDirectory::new(
2248 root_volume(filesystem.clone()).await.unwrap(),
2249 Weak::new(),
2250 None,
2251 blob_resupplied_count,
2252 MemoryPressureConfig::default(),
2253 )
2254 .await
2255 .unwrap();
2256
2257 let volume_and_root = volumes_directory
2258 .create_and_mount_volume(
2259 VOLUME_NAME,
2260 Some(Arc::new(new_insecure_crypt())),
2261 false,
2262 None,
2263 )
2264 .await
2265 .expect("create unencrypted volume failed");
2266 volume_store_id = volume_and_root.volume().store().store_object_id();
2267
2268 let (volume_dir_proxy, dir_server_end) =
2269 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2270 volumes_directory
2271 .serve_volume(&volume_and_root, dir_server_end, false)
2272 .expect("serve_volume failed");
2273
2274 let project_proxy =
2275 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2276 .expect("Unable to connect to project id service");
2277
2278 project_proxy
2279 .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
2280 .await
2281 .unwrap()
2282 .expect("To set limits");
2283
2284 let dir_proxy = {
2285 let (root_proxy, root_server_end) =
2286 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2287 volume_dir_proxy
2288 .open(
2289 "root",
2290 fio::PERM_READABLE | fio::PERM_WRITABLE,
2291 &Default::default(),
2292 root_server_end.into_channel(),
2293 )
2294 .expect("Failed to open volume root");
2295
2296 open_dir_checked(
2297 &root_proxy,
2298 DIR_NAME,
2299 fio::Flags::FLAG_MAYBE_CREATE
2300 | fio::PERM_READABLE
2301 | fio::PERM_WRITABLE
2302 | fio::Flags::PROTOCOL_DIRECTORY,
2303 Default::default(),
2304 )
2305 .await
2306 };
2307 {
2308 let (_, immutable_attributes) =
2309 dir_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2310 let node_id = immutable_attributes.id.unwrap();
2311
2312 project_proxy
2313 .set_for_node(node_id, PROJECT_ID)
2314 .await
2315 .unwrap()
2316 .expect("Setting project on node");
2317 }
2318
2319 let subdir_proxy = open_dir_checked(
2320 &dir_proxy,
2321 SUBDIR_NAME,
2322 fio::Flags::FLAG_MAYBE_CREATE
2323 | fio::PERM_READABLE
2324 | fio::PERM_WRITABLE
2325 | fio::Flags::PROTOCOL_DIRECTORY,
2326 Default::default(),
2327 )
2328 .await;
2329 {
2330 let (_, immutable_attributes) = subdir_proxy
2331 .get_attributes(fio::NodeAttributesQuery::ID)
2332 .await
2333 .unwrap()
2334 .unwrap();
2335 let node_id = immutable_attributes.id.unwrap();
2336
2337 assert_eq!(
2338 project_proxy
2339 .get_for_node(node_id)
2340 .await
2341 .unwrap()
2342 .expect("Setting project on node"),
2343 PROJECT_ID
2344 );
2345 }
2346
2347 let file_proxy = open_file_checked(
2348 &subdir_proxy,
2349 FILE_NAME,
2350 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2351 &Default::default(),
2352 )
2353 .await;
2354 {
2355 let (_, immutable_attributes) =
2356 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2357 let node_id = immutable_attributes.id.unwrap();
2358
2359 assert_eq!(
2360 project_proxy
2361 .get_for_node(node_id)
2362 .await
2363 .unwrap()
2364 .expect("Setting project on node"),
2365 PROJECT_ID
2366 );
2367 }
2368
2369 let tmpfile_proxy = open_file_checked(
2372 &subdir_proxy,
2373 ".",
2374 fio::Flags::PROTOCOL_FILE
2375 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2376 | fio::PERM_READABLE,
2377 &fio::Options::default(),
2378 )
2379 .await;
2380 {
2381 let (_, immutable_attributes) = tmpfile_proxy
2382 .get_attributes(fio::NodeAttributesQuery::ID)
2383 .await
2384 .unwrap()
2385 .unwrap();
2386 let node_id: u64 = immutable_attributes.id.unwrap();
2387 assert_eq!(
2388 project_proxy
2389 .get_for_node(node_id)
2390 .await
2391 .unwrap()
2392 .expect("Setting project on node"),
2393 PROJECT_ID
2394 );
2395 }
2396
2397 let BytesAndNodes { nodes, .. } =
2398 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2399 assert_eq!(nodes, 3);
2400 volumes_directory.terminate().await;
2401 filesystem.close().await.expect("close filesystem failed");
2402 }
2403 device = filesystem.take_device().await;
2404 device.ensure_unique();
2405 device.reopen(false);
2406 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2407 fsck(filesystem.clone()).await.expect("Fsck");
2408 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2409 .await
2410 .expect("Fsck volume");
2411 filesystem.close().await.expect("close filesystem failed");
2412 }
2413
2414 #[fuchsia::test]
2415 async fn test_project_listing() {
2416 const VOLUME_NAME: &str = "A";
2417 const FILE_NAME: &str = "B";
2418 const NON_ZERO_PROJECT_ID: u64 = 3;
2419 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2420 let volume_store_id;
2421 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2422 {
2423 let blob_resupplied_count =
2424 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2425 let volumes_directory = VolumesDirectory::new(
2426 root_volume(filesystem.clone()).await.unwrap(),
2427 Weak::new(),
2428 None,
2429 blob_resupplied_count,
2430 MemoryPressureConfig::default(),
2431 )
2432 .await
2433 .unwrap();
2434 let volume_and_root = volumes_directory
2435 .create_and_mount_volume(VOLUME_NAME, None, false, None)
2436 .await
2437 .expect("create unencrypted volume failed");
2438 volume_store_id = volume_and_root.volume().store().store_object_id();
2439
2440 let (volume_dir_proxy, dir_server_end) =
2441 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2442 volumes_directory
2443 .serve_volume(&volume_and_root, dir_server_end, false)
2444 .expect("serve_volume failed");
2445 let project_proxy =
2446 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2447 .expect("Unable to connect to project id service");
2448 assert!(FxVolume::MAX_PROJECT_ENTRIES >= 4);
2450 let num_entries = u64::try_from(FxVolume::MAX_PROJECT_ENTRIES + 3).unwrap();
2452 for project_id in 1..=num_entries {
2453 project_proxy.set_limit(project_id, 1, 1).await.unwrap().expect("To set limits");
2454 }
2455
2456 let file_proxy = {
2459 let (root_proxy, root_server_end) =
2460 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2461 volume_dir_proxy
2462 .open(
2463 "root",
2464 fio::PERM_READABLE | fio::PERM_WRITABLE,
2465 &Default::default(),
2466 root_server_end.into_channel(),
2467 )
2468 .expect("Failed to open volume root");
2469
2470 open_file_checked(
2471 &root_proxy,
2472 FILE_NAME,
2473 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2474 &Default::default(),
2475 )
2476 .await
2477 };
2478 let (_, immutable_attributes) =
2479 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2480 let node_id = immutable_attributes.id.unwrap();
2481 project_proxy
2482 .set_for_node(node_id, NON_ZERO_PROJECT_ID)
2483 .await
2484 .unwrap()
2485 .expect("Setting project on node");
2486 {
2487 let BytesAndNodes { nodes, .. } = project_proxy
2488 .info(NON_ZERO_PROJECT_ID)
2489 .await
2490 .unwrap()
2491 .expect("Fetching project info")
2492 .1;
2493 assert_eq!(nodes, 1);
2494 }
2495
2496 let (mut entries, mut next_token) =
2498 project_proxy.list(None).await.unwrap().expect("To get project listing");
2499 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2500 assert!(next_token.is_some());
2501 assert!(entries.contains(&1));
2502 assert!(entries.contains(&3));
2503 assert!(!entries.contains(&num_entries));
2504 (entries, next_token) = project_proxy
2506 .list(next_token.as_deref())
2507 .await
2508 .unwrap()
2509 .expect("To get project listing");
2510 assert_eq!(entries.len(), 3);
2511 assert!(next_token.is_none());
2512 assert!(entries.contains(&num_entries));
2513 assert!(!entries.contains(&1));
2514 assert!(!entries.contains(&3));
2515 project_proxy.clear(1).await.unwrap().expect("Clear project");
2517 project_proxy.clear(3).await.unwrap().expect("Clear project");
2518 (entries, next_token) =
2519 project_proxy.list(None).await.unwrap().expect("To get project listing");
2520 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2521 assert!(next_token.is_some());
2522 assert!(!entries.contains(&num_entries));
2523 assert!(!entries.contains(&1));
2524 assert!(entries.contains(&3));
2525 (entries, next_token) = project_proxy
2526 .list(next_token.as_deref())
2527 .await
2528 .unwrap()
2529 .expect("To get project listing");
2530 assert_eq!(entries.len(), 2);
2531 assert!(next_token.is_none());
2532 assert!(entries.contains(&num_entries));
2533 project_proxy.clear(2).await.unwrap().expect("Clear project");
2535 project_proxy.clear(4).await.unwrap().expect("Clear project");
2536 (entries, next_token) =
2537 project_proxy.list(None).await.unwrap().expect("To get project listing");
2538 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2539 assert!(next_token.is_none());
2540 assert!(entries.contains(&num_entries));
2541 volumes_directory.terminate().await;
2542 filesystem.close().await.expect("close filesystem failed");
2543 }
2544 device = filesystem.take_device().await;
2545 device.ensure_unique();
2546 device.reopen(false);
2547 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2548 fsck(filesystem.clone()).await.expect("Fsck");
2549 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
2550 filesystem.close().await.expect("close filesystem failed");
2551 }
2552
2553 #[fuchsia::test(threads = 10)]
2554 async fn test_profile_blob() {
2555 let mut hashes = Vec::new();
2556 let device = {
2557 let fixture = blob_testing::new_blob_fixture().await;
2558
2559 for i in 0..3u64 {
2560 let hash =
2561 fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
2562 hashes.push(hash);
2563 }
2564 fixture.close().await
2565 };
2566 device.ensure_unique();
2567
2568 device.reopen(false);
2569 let mut device = {
2570 let fixture = blob_testing::open_blob_fixture(device).await;
2571 fixture
2572 .volume()
2573 .volume()
2574 .record_and_replay_profile(new_profile_state(true), "foo")
2575 .await
2576 .expect("Recording");
2577
2578 let mut writable = [0u8];
2580 for hash in &hashes {
2581 let vmo = fixture.get_blob_vmo(*hash).await;
2582 vmo.read(&mut writable, 0).expect("Vmo read");
2583 }
2584 fixture.volume().volume().stop_profile_tasks().await;
2585 fixture.close().await
2586 };
2587
2588 for i in 0..3 {
2590 device.ensure_unique();
2591 device.reopen(false);
2592 let fixture = blob_testing::open_blob_fixture(device).await;
2593 {
2594 for hash in &hashes {
2596 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2597 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2598 }
2599
2600 fixture
2601 .volume()
2602 .volume()
2603 .record_and_replay_profile(new_profile_state(true), "foo")
2604 .await
2605 .expect("Replaying");
2606
2607 {
2609 let store = fixture.volume().volume().store();
2610 let store_id = store.store_object_id();
2611 let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
2612 let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
2613 let mut transaction = store
2614 .new_transaction(
2615 lock_keys!(
2616 LockKey::object(store_id, dir.object_id()),
2617 LockKey::object(store_id, old_file),
2618 ),
2619 Options::default(),
2620 )
2621 .await
2622 .unwrap();
2623 replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
2624 .await
2625 .expect("Replace old profile.");
2626 transaction.commit().await.unwrap();
2627 assert!(
2628 dir.lookup("foo").await.unwrap().is_none(),
2629 "Old profile should be moved"
2630 );
2631 }
2632
2633 async {
2635 for hash in &hashes {
2636 let _vmo = fixture.get_blob_vmo(*hash).await;
2639 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2640 while blob.vmo().info().unwrap().populated_bytes == 0 {
2641 fasync::Timer::new(Duration::from_millis(25)).await;
2642 }
2643 }
2644 }
2645 .on_timeout(std::time::Duration::from_secs(120), || {
2646 panic!("Replay did not page in for all VMOs")
2647 })
2648 .await;
2649
2650 fixture.volume().volume().stop_profile_tasks().await;
2652 }
2653 device = fixture.close().await;
2654 }
2655 }
2656
2657 #[fuchsia::test(threads = 10)]
2658 async fn test_profile_file() {
2659 let mut hashes = Vec::new();
2660 let crypt_file_id;
2661 let device = {
2662 let fixture = TestFixture::new().await;
2663 {
2665 let crypt_dir = open_dir_checked(
2666 fixture.root(),
2667 "crypt_dir",
2668 fio::Flags::FLAG_MUST_CREATE
2669 | fio::PERM_WRITABLE
2670 | fio::Flags::PERM_GET_ATTRIBUTES,
2671 Default::default(),
2672 )
2673 .await;
2674 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2675 crypt
2676 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2677 .expect("add_wrapping_key failed");
2678 crypt_dir
2679 .update_attributes(&fio::MutableNodeAttributes {
2680 wrapping_key_id: Some(WRAPPING_KEY_ID),
2681 ..Default::default()
2682 })
2683 .await
2684 .expect("update_attributes wire call failed")
2685 .expect("update_attributes failed");
2686 let crypt_file = open_file_checked(
2687 &crypt_dir,
2688 "crypt_file",
2689 fio::Flags::FLAG_MUST_CREATE
2690 | fio::PERM_WRITABLE
2691 | fio::Flags::PERM_GET_ATTRIBUTES,
2692 &Default::default(),
2693 )
2694 .await;
2695 crypt_file.write("asdf".as_bytes()).await.unwrap().expect("Writing crypt file");
2696 crypt_file_id = crypt_file
2697 .get_attributes(fio::NodeAttributesQuery::ID)
2698 .await
2699 .unwrap()
2700 .expect("Get id")
2701 .1
2702 .id
2703 .expect("Reading id in response");
2704 }
2705
2706 for i in 0..3u64 {
2707 let file_proxy = open_file_checked(
2708 fixture.root(),
2709 &i.to_string(),
2710 fio::Flags::FLAG_MUST_CREATE
2711 | fio::PERM_WRITABLE
2712 | fio::Flags::PERM_GET_ATTRIBUTES,
2713 &Default::default(),
2714 )
2715 .await;
2716 file_proxy.write(i.to_string().as_bytes()).await.unwrap().expect("Writing file");
2717 let id = file_proxy
2718 .get_attributes(fio::NodeAttributesQuery::ID)
2719 .await
2720 .unwrap()
2721 .expect("Get id")
2722 .1
2723 .id
2724 .expect("Reading id in response");
2725 hashes.push((i, id));
2726 }
2727 fixture.close().await
2728 };
2729 device.ensure_unique();
2730
2731 device.reopen(false);
2732 let mut device = {
2733 let fixture = TestFixture::open(
2734 device,
2735 TestFixtureOptions { format: false, ..Default::default() },
2736 )
2737 .await;
2738 fixture
2739 .volume()
2740 .volume()
2741 .record_and_replay_profile(new_profile_state(false), "foo")
2742 .await
2743 .expect("Recording");
2744
2745 {
2746 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2747 crypt
2748 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2749 .expect("add wrapping key failed");
2750 let crypt_file = open_file_checked(
2751 fixture.root(),
2752 "crypt_dir/crypt_file",
2753 fio::PERM_READABLE,
2754 &Default::default(),
2755 )
2756 .await;
2757 crypt_file.read(1).await.unwrap().expect("Reading crypt file");
2758 }
2759 for (i, _) in &hashes {
2761 let file_proxy = open_file_checked(
2762 fixture.root(),
2763 &i.to_string(),
2764 fio::PERM_READABLE,
2765 &Default::default(),
2766 )
2767 .await;
2768 file_proxy.read(1).await.unwrap().expect("Reading file");
2769 }
2770 fixture.volume().volume().stop_profile_tasks().await;
2771 fixture.close().await
2772 };
2773
2774 for i in 0..3 {
2776 device.ensure_unique();
2777 device.reopen(false);
2778 let fixture = TestFixture::open(
2779 device,
2780 TestFixtureOptions { format: false, ..Default::default() },
2781 )
2782 .await;
2783 {
2784 let volume = fixture.volume().volume().clone();
2786 {
2788 let crypt_file = volume
2789 .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
2790 .await
2791 .expect("Opening file internally")
2792 .into_any()
2793 .downcast::<FxFile>()
2794 .expect("Should be file");
2795 assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
2796 }
2797 for (_, id) in &hashes {
2798 let file = volume
2799 .get_or_load_node(*id, ObjectDescriptor::File, None)
2800 .await
2801 .expect("Opening file internally")
2802 .into_any()
2803 .downcast::<FxFile>()
2804 .expect("Should be file");
2805 assert_eq!(file.vmo().info().unwrap().populated_bytes, 0);
2806 }
2807
2808 fixture
2809 .volume()
2810 .volume()
2811 .record_and_replay_profile(new_profile_state(false), "foo")
2812 .await
2813 .expect("Replaying");
2814
2815 {
2817 let store = fixture.volume().volume().store();
2818 let store_id = store.store_object_id();
2819 let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
2820 let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
2821 let mut transaction = store
2822 .new_transaction(
2823 lock_keys!(
2824 LockKey::object(store_id, dir.object_id()),
2825 LockKey::object(store_id, old_file),
2826 ),
2827 Options::default(),
2828 )
2829 .await
2830 .unwrap();
2831 replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
2832 .await
2833 .expect("Replace old profile.");
2834 transaction.commit().await.unwrap();
2835 assert!(
2836 dir.lookup("foo").await.unwrap().is_none(),
2837 "Old profile should be moved"
2838 );
2839 }
2840
2841 async {
2843 for (_, id) in &hashes {
2844 let file = volume
2845 .get_or_load_node(*id, ObjectDescriptor::File, None)
2846 .await
2847 .expect("Opening file internally")
2848 .into_any()
2849 .downcast::<FxFile>()
2850 .expect("Should be file");
2851 while file.vmo().info().unwrap().populated_bytes == 0 {
2852 fasync::Timer::new(Duration::from_millis(25)).await;
2853 }
2854 }
2855 }
2856 .on_timeout(std::time::Duration::from_secs(120), || {
2857 panic!("Replay did not page in for all VMOs")
2858 })
2859 .await;
2860 {
2862 let crypt_file = volume
2863 .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
2864 .await
2865 .expect("Opening file internally")
2866 .into_any()
2867 .downcast::<FxFile>()
2868 .expect("Should be file");
2869 assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
2870 }
2871
2872 for (i, _) in &hashes {
2874 let _file_proxy = open_file_checked(
2875 fixture.root(),
2876 &i.to_string(),
2877 fio::PERM_READABLE,
2878 &Default::default(),
2879 )
2880 .await;
2881 }
2882
2883 fixture.volume().volume().stop_profile_tasks().await;
2885 }
2886 device = fixture.close().await;
2887 }
2888 }
2889
2890 #[fuchsia::test(threads = 10)]
2891 async fn test_profile_update() {
2892 let mut hashes = Vec::new();
2893 let device = {
2894 let fixture = blob_testing::new_blob_fixture().await;
2895 for i in 0..2u64 {
2896 let hash =
2897 fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
2898 hashes.push(hash);
2899 }
2900 fixture.close().await
2901 };
2902 device.ensure_unique();
2903
2904 device.reopen(false);
2905 let device = {
2906 let fixture = blob_testing::open_blob_fixture(device).await;
2907
2908 {
2909 let volume = fixture.volume().volume();
2910 volume
2911 .record_and_replay_profile(new_profile_state(true), "foo")
2912 .await
2913 .expect("Recording");
2914
2915 let original_recorded = RECORDED.load(Ordering::Relaxed);
2916
2917 {
2919 let mut writable = [0u8];
2920 let hash = &hashes[0];
2921 let vmo = fixture.get_blob_vmo(*hash).await;
2922 vmo.read(&mut writable, 0).expect("Vmo read");
2923 }
2924
2925 while RECORDED.load(Ordering::Relaxed) == original_recorded {
2928 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
2929 }
2930
2931 volume.stop_profile_tasks().await;
2932 }
2933 fixture.close().await
2934 };
2935
2936 device.ensure_unique();
2937 device.reopen(false);
2938 let fixture = blob_testing::open_blob_fixture(device).await;
2939 {
2940 for hash in &hashes {
2943 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2944 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2945 }
2946
2947 let volume = fixture.volume().volume();
2948
2949 volume
2950 .record_and_replay_profile(new_profile_state(true), "foo")
2951 .await
2952 .expect("Replaying");
2953
2954 async {
2956 let hash = &hashes[0];
2957 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2958 while blob.vmo().info().unwrap().populated_bytes == 0 {
2959 fasync::Timer::new(Duration::from_millis(25)).await;
2960 }
2961 }
2962 .on_timeout(std::time::Duration::from_secs(120), || {
2963 panic!("Replay did not page in for all VMOs")
2964 })
2965 .await;
2966
2967 let original_recorded = RECORDED.load(Ordering::Relaxed);
2968
2969 {
2971 let mut writable = [0u8];
2972 let hash = &hashes[1];
2973 let vmo = fixture.get_blob_vmo(*hash).await;
2974 vmo.read(&mut writable, 0).expect("Vmo read");
2975 }
2976
2977 while RECORDED.load(Ordering::Relaxed) == original_recorded {
2980 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
2981 }
2982
2983 volume.stop_profile_tasks().await;
2985 }
2986 let device = fixture.close().await;
2987
2988 device.ensure_unique();
2989 device.reopen(false);
2990 let fixture = blob_testing::open_blob_fixture(device).await;
2991 {
2992 for hash in &hashes {
2995 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2996 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2997 }
2998
2999 fixture
3000 .volume()
3001 .volume()
3002 .record_and_replay_profile(new_profile_state(true), "foo")
3003 .await
3004 .expect("Replaying");
3005
3006 async {
3008 let hash = &hashes[1];
3009 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3010 while blob.vmo().info().unwrap().populated_bytes == 0 {
3011 fasync::Timer::new(Duration::from_millis(25)).await;
3012 }
3013 }
3014 .on_timeout(std::time::Duration::from_secs(30), || {
3015 panic!("Replay did not page in for all VMOs")
3016 })
3017 .await;
3018
3019 fixture.volume().volume().stop_profile_tasks().await;
3021
3022 {
3024 let hash = &hashes[0];
3025 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3026 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
3027 }
3028 }
3029 fixture.close().await;
3030 }
3031
3032 #[fuchsia::test(threads = 10)]
3033 async fn test_unencrypted_volume() {
3034 let fixture = TestFixture::new_unencrypted().await;
3035 let root = fixture.root();
3036
3037 let f = open_file_checked(
3038 &root,
3039 "foo",
3040 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3041 &Default::default(),
3042 )
3043 .await;
3044 close_file_checked(f).await;
3045
3046 fixture.close().await;
3047 }
3048
3049 #[fuchsia::test]
3050 async fn test_read_only_unencrypted_volume() {
3051 let fs = {
3053 let device = fxfs::filesystem::mkfs_with_volume(
3054 DeviceHolder::new(FakeDevice::new(8192, 512)),
3055 "vol",
3056 None,
3057 )
3058 .await
3059 .unwrap();
3060 device.reopen(true);
3062 FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3063 };
3064 {
3066 let root_volume = root_volume(fs.clone()).await.unwrap();
3067 let store = root_volume.volume("vol", StoreOptions::default()).await.unwrap();
3068 let unique_id = store.store_object_id();
3069 let blob_resupplied_count =
3070 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
3071 let volume = FxVolume::new(
3072 Weak::new(),
3073 store,
3074 unique_id,
3075 "vol".to_owned(),
3076 blob_resupplied_count,
3077 MemoryPressureConfig::default(),
3078 )
3079 .unwrap();
3080 volume.terminate().await;
3081 }
3082 fs.close().await.unwrap();
3084 let device = fs.take_device().await;
3085 device.ensure_unique();
3086 }
3087
3088 #[fuchsia::test]
3089 async fn test_read_only_encrypted_volume() {
3090 let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
3091 let fs = {
3093 let device = fxfs::filesystem::mkfs_with_volume(
3094 DeviceHolder::new(FakeDevice::new(8192, 512)),
3095 "vol",
3096 Some(crypt.clone()),
3097 )
3098 .await
3099 .unwrap();
3100 device.reopen(true);
3102 FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3103 };
3104 {
3106 let root_volume = root_volume(fs.clone()).await.unwrap();
3107 let store = root_volume
3108 .volume("vol", StoreOptions { crypt: Some(crypt), ..StoreOptions::default() })
3109 .await
3110 .unwrap();
3111 let unique_id = store.store_object_id();
3112 let blob_resupplied_count =
3113 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
3114 let volume = FxVolume::new(
3115 Weak::new(),
3116 store,
3117 unique_id,
3118 "vol".to_owned(),
3119 blob_resupplied_count,
3120 MemoryPressureConfig::default(),
3121 )
3122 .unwrap();
3123 volume.terminate().await;
3124 }
3125 fs.close().await.unwrap();
3127 let device = fs.take_device().await;
3128 device.ensure_unique();
3129 }
3130}