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::{FileRecordingHandle, 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, DebugRequest, DebugRequestStream, FileBackedVolumeProviderRequest,
20 FileBackedVolumeProviderRequestStream, ProjectIdRequest, ProjectIdRequestStream,
21 ProjectIterToken,
22};
23use fidl_fuchsia_io as fio;
24use fs_inspect::{FsInspectVolume, VolumeData};
25use fuchsia_async as fasync;
26use fuchsia_async::epoch::Epoch;
27use fuchsia_sync::Mutex;
28use futures::channel::oneshot;
29use futures::stream::{self, FusedStream, Stream};
30use futures::{FutureExt, StreamExt, TryStreamExt};
31use fxfs::errors::FxfsError;
32use fxfs::filesystem::{self, SyncOptions, TruncateGuard};
33use fxfs::future_with_guard::FutureWithGuard;
34use fxfs::log::*;
35use fxfs::object_store::directory::Directory;
36use fxfs::object_store::project_id::ProjectIdExt;
37use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
38use fxfs::object_store::{
39 DirType, HandleOptions, HandleOwner, ObjectDescriptor, ObjectStore, ProjectId,
40};
41use refaults_vmo::PageRefaultCounter;
42use std::future::Future;
43use std::pin::pin;
44#[cfg(any(test, feature = "testing"))]
45use std::sync::atomic::AtomicBool;
46use std::sync::atomic::{AtomicU64, Ordering};
47use std::sync::{Arc, OnceLock, Weak};
48use std::time::Duration;
49use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
50use storage_units::BlockSize;
51use vfs::directory::entry::DirectoryEntry;
52use vfs::directory::simple::Simple;
53use vfs::execution_scope::ExecutionScope;
54
55const DIRENT_CACHE_LIMIT: usize = 2000;
58pub const READ_AHEAD_SIZE: u64 = 128 * 1024;
63
64const BLOB_TRANSFER_VMO_SIZE: usize = 32 * 1024 * 1024;
65
66const PROFILE_DIRECTORY: &str = "profiles";
67
68#[derive(Clone, Copy, Debug)]
69pub struct MemoryPressureLevelConfig {
70 pub background_task_period: Duration,
73
74 pub cache_size_limit: usize,
76
77 pub background_task_initial_delay: Duration,
80}
81
82impl Default for MemoryPressureLevelConfig {
83 fn default() -> Self {
84 Self {
85 background_task_period: Duration::from_secs(20),
86 cache_size_limit: DIRENT_CACHE_LIMIT,
87 background_task_initial_delay: Duration::from_secs(70),
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug)]
93pub struct MemoryPressureConfig {
94 pub mem_normal: MemoryPressureLevelConfig,
96
97 pub mem_warning: MemoryPressureLevelConfig,
99
100 pub mem_critical: MemoryPressureLevelConfig,
102}
103
104impl MemoryPressureConfig {
105 pub fn for_level(&self, level: &MemoryPressureLevel) -> &MemoryPressureLevelConfig {
106 match level {
107 MemoryPressureLevel::Normal => &self.mem_normal,
108 MemoryPressureLevel::Warning => &self.mem_warning,
109 MemoryPressureLevel::Critical => &self.mem_critical,
110 }
111 }
112}
113
114impl Default for MemoryPressureConfig {
115 fn default() -> Self {
116 Self {
119 mem_normal: MemoryPressureLevelConfig {
120 background_task_period: Duration::from_secs(20),
121 cache_size_limit: DIRENT_CACHE_LIMIT,
122 background_task_initial_delay: Duration::from_secs(70),
123 },
124 mem_warning: MemoryPressureLevelConfig {
125 background_task_period: Duration::from_secs(5),
126 cache_size_limit: 100,
127 background_task_initial_delay: Duration::from_secs(5),
128 },
129 mem_critical: MemoryPressureLevelConfig {
130 background_task_period: Duration::from_millis(1500),
131 cache_size_limit: 20,
132 background_task_initial_delay: Duration::from_millis(1500),
133 },
134 }
135 }
136}
137
138pub struct FxVolume {
141 parent: Weak<VolumesDirectory>,
142 cache: NodeCache,
143 store: Arc<ObjectStore>,
144 pager: Pager,
145 executor: fasync::EHandle,
146 name: String,
147
148 background_task: Mutex<Option<(fasync::Task<()>, oneshot::Sender<()>)>>,
150
151 fs_id: u64,
153
154 scope: ExecutionScope,
156
157 dirent_cache: DirentCache,
158
159 profile_state: Mutex<Option<Box<dyn ProfileState>>>,
160
161 #[cfg(any(test, feature = "testing"))]
162 poisoned: AtomicBool,
163
164 blob_resupplied_count: Arc<PageRefaultCounter>,
165
166 pager_dirty_byte_count: AtomicU64,
169
170 blob_allocator: OnceLock<Arc<BufferAllocator>>,
177}
178
179#[fxfs_trace::trace]
180impl FxVolume {
181 pub fn new(
182 parent: Weak<VolumesDirectory>,
183 store: Arc<ObjectStore>,
184 fs_id: u64,
185 name: String,
186 blob_resupplied_count: Arc<PageRefaultCounter>,
187 memory_pressure_config: MemoryPressureConfig,
188 ) -> Result<Self, Error> {
189 let scope = ExecutionScope::new();
190 Ok(Self {
191 parent,
192 cache: NodeCache::new(),
193 store,
194 name,
195 pager: Pager::new(scope.clone())?,
196 executor: fasync::EHandle::local(),
197 background_task: Mutex::new(None),
198 fs_id,
199 scope,
200 dirent_cache: DirentCache::new(memory_pressure_config.mem_normal.cache_size_limit),
201 profile_state: Mutex::new(None),
202 #[cfg(any(test, feature = "testing"))]
203 poisoned: AtomicBool::new(false),
204 blob_resupplied_count,
205 pager_dirty_byte_count: AtomicU64::new(0),
206 blob_allocator: OnceLock::new(),
207 })
208 }
209
210 pub fn store(&self) -> &Arc<ObjectStore> {
211 &self.store
212 }
213
214 pub fn blob_allocator(&self) -> &Arc<BufferAllocator> {
218 self.blob_allocator.get_or_init(|| {
219 Arc::new(BufferAllocator::new(
220 self.store().block_size().get() as usize,
221 BufferSource::new_trusted(BLOB_TRANSFER_VMO_SIZE),
222 ))
223 })
224 }
225
226 pub fn cache(&self) -> &NodeCache {
227 &self.cache
228 }
229
230 pub fn dirent_cache(&self) -> &DirentCache {
231 &self.dirent_cache
232 }
233
234 pub fn pager(&self) -> &Pager {
235 &self.pager
236 }
237
238 pub fn id(&self) -> u64 {
239 self.fs_id
240 }
241
242 pub fn scope(&self) -> &ExecutionScope {
243 &self.scope
244 }
245
246 pub fn blob_resupplied_count(&self) -> &PageRefaultCounter {
247 &self.blob_resupplied_count
248 }
249
250 pub fn name(&self) -> &str {
251 &self.name
252 }
253
254 pub fn filesystem_info_for_volume(&self) -> fio::FilesystemInfo {
257 let allocator = self.store.filesystem().allocator();
258 let info =
259 if let Some(limit) = allocator.get_owner_bytes_limit(self.store.store_object_id()) {
260 filesystem::Info {
261 used_bytes: allocator.get_owner_bytes_used(self.store.store_object_id()),
262 total_bytes: limit,
263 }
264 } else {
265 self.store.filesystem().get_info()
266 };
267
268 info_to_filesystem_info(info, self.store.block_size(), self.store.object_count(), self.id())
269 }
270
271 pub async fn stop_profile_tasks(self: &Arc<Self>) {
273 let Some(mut state) = self.profile_state.lock().take() else { return };
274 state.wait_for_replay_to_finish().await;
275 self.pager.set_recorder(None);
276 state.wait_for_recording_to_finish().await;
277 }
278
279 pub async fn get_profile_directory(self: &Arc<Self>) -> Result<Directory<FxVolume>, Error> {
281 let internal_dir = self
282 .get_or_create_internal_dir()
283 .await
284 .map_err(|e| e.context("Opening internal directory"))?;
285 let mut transaction = self
287 .store()
288 .new_transaction(
289 lock_keys![LockKey::object(
290 self.store().store_object_id(),
291 internal_dir.object_id(),
292 )],
293 Options::default(),
294 )
295 .await?;
296 Ok(match internal_dir.directory().lookup(PROFILE_DIRECTORY).await? {
297 Some((object_id, _, _)) => {
298 Directory::open_unchecked(self.clone(), object_id, DirType::Normal)
299 }
300 None => {
301 let new_dir = internal_dir
302 .directory()
303 .create_child_dir(&mut transaction, PROFILE_DIRECTORY)
304 .await?;
305 transaction.commit().await?;
306 new_dir
307 }
308 })
309 }
310
311 pub async fn record_and_replay_profile(
315 self: &Arc<Self>,
316 mut state: Box<dyn ProfileState>,
317 name: &str,
318 ) -> Result<(), Error> {
319 let profile_dir = self.get_profile_directory().await?;
326 let replay_handle = if let Some((id, descriptor, _)) = profile_dir.lookup(name).await? {
327 ensure!(matches!(descriptor, ObjectDescriptor::File), FxfsError::Inconsistent);
328 Some(Box::new(
329 ObjectStore::open_object(self, id, HandleOptions::default(), None).await?,
330 ))
331 } else {
332 None
333 };
334
335 info!("Recording new profile '{name}' for volume object {}", self.store.store_object_id());
336 let recording_handle = FileRecordingHandle::new(name, self.clone()).await?;
338
339 let mut profile_state = self.profile_state.lock();
340 self.pager.set_recorder(Some(state.record_new(self, Box::new(recording_handle))));
341 if let Some(handle) = replay_handle {
342 if let Some(guard) = self.scope().try_active_guard() {
343 state.replay_profile(handle, self.clone(), guard);
344 info!(
345 "Replaying existing profile '{name}' for volume object {}",
346 self.store.store_object_id()
347 );
348 }
349 }
350 *profile_state = Some(state);
351 Ok(())
352 }
353
354 pub async fn replay_xor_record_profile(
356 self: &Arc<Self>,
357 mut state: Box<dyn ProfileState>,
358 name: &str,
359 ) -> Result<(), Error> {
360 let profile_dir = self.get_profile_directory().await?;
361 let replay_handle = if let Some((id, descriptor, _)) = profile_dir.lookup(name).await? {
362 ensure!(matches!(descriptor, ObjectDescriptor::File), FxfsError::Inconsistent);
363 Some(Box::new(
364 ObjectStore::open_object(self, id, HandleOptions::default(), None).await?,
365 ))
366 } else {
367 None
368 };
369
370 if let Some(handle) = replay_handle {
371 let mut profile_state = self.profile_state.lock();
372 if let Some(guard) = self.scope().try_active_guard() {
373 state.replay_profile(handle, self.clone(), guard);
374 info!(
375 "Replaying existing profile '{name}' for volume object {}",
376 self.store.store_object_id()
377 );
378 }
379 *profile_state = Some(state);
380 } else {
381 info!(
382 "Recording new profile '{name}' for volume object {}",
383 self.store.store_object_id()
384 );
385 let recording_handle = 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(
508 &self,
509 object_id: u64,
510 truncate_guard: Option<&TruncateGuard<'_>>,
511 ) -> Result<(), Error> {
512 if let Some(node) = self.cache.get(object_id) {
513 node.clone().mark_to_be_purged();
514 return Ok(());
515 }
516 let txn_options = Options { borrow_metadata_space: true, ..Default::default() };
518 self.store.tombstone_object(object_id, txn_options, truncate_guard).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 pub fn clear_caches(&self) {
885 self.dirent_cache.clear();
886 self.store.clear_caches();
887 }
888
889 pub async fn handle_debug_requests(
890 this: Weak<Self>,
891 scope: ExecutionScope,
892 mut requests: DebugRequestStream,
893 ) -> Result<(), Error> {
894 while let Some(request) = requests.try_next().await? {
895 let Some(_guard) = scope.try_active_guard() else { bail!("Volume shutting down") };
897 let Some(this) = this.upgrade() else { bail!("FxVolume dropped") };
898
899 match request {
900 DebugRequest::ClearCaches { responder } => {
901 this.clear_caches();
902 let fs = this.store.filesystem();
903 fs.root_store().clear_caches();
904 fs.root_parent_store().clear_caches();
905 fs.allocator().tree().clear_cache();
906 responder.send(Ok(()))?;
907 }
908 DebugRequest::Compact { responder } => {
909 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
910 }
911 DebugRequest::DeleteProfile { responder, .. } => {
912 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
913 }
914 DebugRequest::RecordAndReplayProfile { responder, .. } => {
915 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
916 }
917 DebugRequest::ReplayXorRecordProfile { responder, .. } => {
918 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
919 }
920 DebugRequest::StopProfileTasks { responder } => {
921 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()))?;
922 }
923 }
924 }
925 Ok(())
926 }
927
928 const MAX_PROJECT_ENTRIES: usize = 8184;
932
933 async fn list_projects(
936 &self,
937 last_token: &Option<Box<ProjectIterToken>>,
938 ) -> Result<(Vec<u64>, Option<ProjectIterToken>), Error> {
939 let (entries, token) = self
940 .store()
941 .list_projects(
942 last_token.as_ref().and_then(|v| ProjectId::new(v.value)),
943 Self::MAX_PROJECT_ENTRIES,
944 )
945 .await?;
946 Ok((
947 entries.into_iter().map(ProjectId::raw).collect(),
948 token.map(|value| ProjectIterToken { value: value.raw() }),
949 ))
950 }
951
952 async fn project_info(&self, project_id: u64) -> Result<(BytesAndNodes, BytesAndNodes), Error> {
953 let project_id = ProjectId::new(project_id).ok_or(FxfsError::OutOfRange)?;
954 let (limit, usage) = self.store().project_info(project_id).await?;
955 ensure!(limit.is_some() || usage.is_some(), FxfsError::NotFound);
957 Ok((
958 limit.map_or_else(
959 || BytesAndNodes { bytes: u64::MAX, nodes: u64::MAX },
960 |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
961 ),
962 usage.map_or_else(
963 || BytesAndNodes { bytes: 0, nodes: 0 },
964 |v| BytesAndNodes { bytes: v.0, nodes: v.1 },
965 ),
966 ))
967 }
968}
969
970#[cfg(any(test, feature = "testing"))]
971impl Drop for FxVolume {
972 fn drop(&mut self) {
973 assert!(!*self.poisoned.get_mut());
974 }
975}
976
977impl HandleOwner for FxVolume {}
978
979impl AsRef<ObjectStore> for FxVolume {
980 fn as_ref(&self) -> &ObjectStore {
981 &self.store
982 }
983}
984
985#[async_trait]
986impl FsInspectVolume for FxVolume {
987 async fn get_volume_data(&self) -> Option<VolumeData> {
988 let _guard = self.scope.try_active_guard()?;
990
991 let object_count = self.store().object_count();
992 let (used_bytes, bytes_limit) =
993 self.store.filesystem().allocator().owner_allocation_info(self.store.store_object_id());
994 let encrypted = self.store().crypt().is_some();
995 let port_koid = fasync::EHandle::local().port().as_handle_ref().koid().unwrap().raw_koid();
996 Some(VolumeData { bytes_limit, used_bytes, used_nodes: object_count, encrypted, port_koid })
997 }
998}
999
1000pub trait RootDir: FxNode + DirectoryEntry {
1001 fn as_directory_entry(self: Arc<Self>) -> Arc<dyn DirectoryEntry>;
1002
1003 fn serve(self: Arc<Self>, flags: fio::Flags, server_end: ServerEnd<fio::DirectoryMarker>);
1004
1005 fn as_node(self: Arc<Self>) -> Arc<dyn FxNode>;
1006
1007 fn register_additional_volume_services(
1008 self: Arc<Self>,
1009 _svc_dir: &Simple,
1010 ) -> Result<(), Error> {
1011 Ok(())
1012 }
1013}
1014
1015#[derive(Clone)]
1016pub struct FxVolumeAndRoot {
1017 volume: Arc<FxVolume>,
1018 root: Arc<dyn RootDir>,
1019
1020 admin_scope: ExecutionScope,
1022
1023 outgoing_dir: Arc<Simple>,
1025}
1026
1027impl FxVolumeAndRoot {
1028 pub async fn new<T: From<Directory<FxVolume>> + RootDir>(
1029 parent: Weak<VolumesDirectory>,
1030 store: Arc<ObjectStore>,
1031 unique_id: u64,
1032 volume_name: String,
1033 blob_resupplied_count: Arc<PageRefaultCounter>,
1034 memory_pressure_config: MemoryPressureConfig,
1035 ) -> Result<Self, Error> {
1036 let volume = Arc::new(FxVolume::new(
1037 parent,
1038 store,
1039 unique_id,
1040 volume_name,
1041 blob_resupplied_count.clone(),
1042 memory_pressure_config,
1043 )?);
1044 let root_object_id = volume.store().root_directory_object_id();
1045 let root_dir = Directory::open(&volume, root_object_id).await?;
1046 let root = Arc::<T>::new(root_dir.into()) as Arc<dyn RootDir>;
1047 volume
1048 .cache
1049 .get_or_reserve(root_object_id)
1050 .await
1051 .placeholder()
1052 .unwrap()
1053 .commit(&root.clone().as_node());
1054 Ok(Self {
1055 volume,
1056 root,
1057 admin_scope: ExecutionScope::new(),
1058 outgoing_dir: vfs::directory::immutable::simple(),
1059 })
1060 }
1061
1062 pub fn volume(&self) -> &Arc<FxVolume> {
1063 &self.volume
1064 }
1065
1066 pub fn root(&self) -> &Arc<dyn RootDir> {
1067 &self.root
1068 }
1069
1070 pub fn admin_scope(&self) -> &ExecutionScope {
1071 &self.admin_scope
1072 }
1073
1074 pub fn outgoing_dir(&self) -> &Arc<Simple> {
1075 &self.outgoing_dir
1076 }
1077
1078 pub fn root_dir(&self) -> Arc<FxDirectory> {
1080 self.root().clone().into_any().downcast::<FxDirectory>().expect("Invalid type for root")
1081 }
1082
1083 pub fn into_volume(self) -> Arc<FxVolume> {
1084 self.volume
1085 }
1086}
1087
1088const TOTAL_NODES: u64 = i64::MAX as u64;
1092
1093const FXFS_INFO_NAME_FIDL: [i8; 32] = [
1096 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,
1097 0, 0, 0, 0,
1098];
1099
1100fn info_to_filesystem_info(
1101 info: filesystem::Info,
1102 block_size: BlockSize,
1103 object_count: u64,
1104 fs_id: u64,
1105) -> fio::FilesystemInfo {
1106 fio::FilesystemInfo {
1107 total_bytes: info.total_bytes,
1108 used_bytes: info.used_bytes,
1109 total_nodes: TOTAL_NODES,
1110 used_nodes: object_count,
1111 free_shared_pool_bytes: 0,
1113 fs_id,
1114 block_size: block_size.get() as u32,
1115 max_filename_size: fio::MAX_NAME_LENGTH as u32,
1116 fs_type: fidl_fuchsia_fs::VfsType::Fxfs.into_primitive(),
1117 padding: 0,
1118 name: FXFS_INFO_NAME_FIDL,
1119 }
1120}
1121
1122#[cfg(test)]
1123mod tests {
1124 use super::DIRENT_CACHE_LIMIT;
1125 use crate::fuchsia::file::FxFile;
1126 use crate::fuchsia::fxblob::testing::{self as blob_testing, BlobFixture};
1127 use crate::fuchsia::memory_pressure::MemoryPressureLevel;
1128 use crate::fuchsia::pager::PagerBacked;
1129 use crate::fuchsia::profile::{RECORDED, new_profile_state};
1130 use crate::fuchsia::testing::{
1131 TestFixture, TestFixtureOptions, close_dir_checked, close_file_checked, open_dir,
1132 open_dir_checked, open_file, open_file_checked,
1133 };
1134 use crate::fuchsia::volume::{FxVolume, MemoryPressureConfig, MemoryPressureLevelConfig};
1135 use crate::fuchsia::volumes_directory::VolumesDirectory;
1136 use delivery_blob::CompressionMode;
1137 use fidl_fuchsia_fs_startup::CreateOptions;
1138 use fidl_fuchsia_fxfs::{BytesAndNodes, ProjectIdMarker};
1139 use fidl_fuchsia_io as fio;
1140 use fs_inspect::FsInspectVolume;
1141 use fuchsia_async::{self as fasync, TimeoutExt as _};
1142 use fuchsia_component_client::connect_to_protocol_at_dir_svc;
1143 use fuchsia_fs::file;
1144 use fxfs::filesystem::{FxFilesystem, FxFilesystemBuilder};
1145 use fxfs::fsck::{fsck, fsck_volume};
1146 use fxfs::object_store::directory::replace_child;
1147 use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
1148 use fxfs::object_store::volume::root_volume;
1149 use fxfs::object_store::{HandleOptions, ObjectDescriptor, ObjectStore, StoreOptions};
1150 use fxfs_crypt_common::CryptBase;
1151 use fxfs_crypto::{Crypt, WrappingKeyId};
1152 use fxfs_insecure_crypto::new_insecure_crypt;
1153 use refaults_vmo::PageRefaultCounter;
1154 use std::sync::atomic::Ordering;
1155 use std::sync::{Arc, Weak};
1156 use std::time::Duration;
1157 use storage_device::DeviceHolder;
1158 use storage_device::fake_device::FakeDevice;
1159 use zx::Status;
1160
1161 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
1162
1163 #[fuchsia::test(threads = 10)]
1164 async fn test_rename_different_dirs() {
1165 use zx::Event;
1166
1167 let fixture = TestFixture::new().await;
1168 let root = fixture.root();
1169
1170 let src = open_dir_checked(
1171 &root,
1172 "foo",
1173 fio::Flags::FLAG_MAYBE_CREATE
1174 | fio::PERM_READABLE
1175 | fio::PERM_WRITABLE
1176 | fio::Flags::PROTOCOL_DIRECTORY,
1177 Default::default(),
1178 )
1179 .await;
1180
1181 let dst = open_dir_checked(
1182 &root,
1183 "bar",
1184 fio::Flags::FLAG_MAYBE_CREATE
1185 | fio::PERM_READABLE
1186 | fio::PERM_WRITABLE
1187 | fio::Flags::PROTOCOL_DIRECTORY,
1188 Default::default(),
1189 )
1190 .await;
1191
1192 let f = open_file_checked(
1193 &root,
1194 "foo/a",
1195 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1196 &Default::default(),
1197 )
1198 .await;
1199 close_file_checked(f).await;
1200
1201 let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1202 Status::ok(status).expect("get_token failed");
1203 src.rename("a", Event::from(dst_token.unwrap()), "b")
1204 .await
1205 .expect("FIDL call failed")
1206 .expect("rename failed");
1207
1208 assert_eq!(
1209 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1210 .await
1211 .expect_err("Open succeeded")
1212 .root_cause()
1213 .downcast_ref::<Status>()
1214 .expect("No status"),
1215 &Status::NOT_FOUND,
1216 );
1217 let f =
1218 open_file_checked(&root, "bar/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1219 close_file_checked(f).await;
1220
1221 close_dir_checked(dst).await;
1222 close_dir_checked(src).await;
1223 fixture.close().await;
1224 }
1225
1226 #[fuchsia::test(threads = 10)]
1227 async fn test_rename_same_dir() {
1228 use zx::Event;
1229 let fixture = TestFixture::new().await;
1230 let root = fixture.root();
1231
1232 let src = open_dir_checked(
1233 &root,
1234 "foo",
1235 fio::Flags::FLAG_MAYBE_CREATE
1236 | fio::PERM_READABLE
1237 | fio::PERM_WRITABLE
1238 | fio::Flags::PROTOCOL_DIRECTORY,
1239 Default::default(),
1240 )
1241 .await;
1242
1243 let f = open_file_checked(
1244 &root,
1245 "foo/a",
1246 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1247 &Default::default(),
1248 )
1249 .await;
1250 close_file_checked(f).await;
1251
1252 let (status, src_token) = src.get_token().await.expect("FIDL call failed");
1253 Status::ok(status).expect("get_token failed");
1254 src.rename("a", Event::from(src_token.unwrap()), "b")
1255 .await
1256 .expect("FIDL call failed")
1257 .expect("rename failed");
1258
1259 assert_eq!(
1260 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1261 .await
1262 .expect_err("Open succeeded")
1263 .root_cause()
1264 .downcast_ref::<Status>()
1265 .expect("No status"),
1266 &Status::NOT_FOUND,
1267 );
1268 let f =
1269 open_file_checked(&root, "foo/b", fio::Flags::PROTOCOL_FILE, &Default::default()).await;
1270 close_file_checked(f).await;
1271
1272 close_dir_checked(src).await;
1273 fixture.close().await;
1274 }
1275
1276 #[fuchsia::test(threads = 10)]
1277 async fn test_rename_overwrites_file() {
1278 use zx::Event;
1279 let fixture = TestFixture::new().await;
1280 let root = fixture.root();
1281
1282 let src = open_dir_checked(
1283 &root,
1284 "foo",
1285 fio::Flags::FLAG_MAYBE_CREATE
1286 | fio::PERM_READABLE
1287 | fio::PERM_WRITABLE
1288 | fio::Flags::PROTOCOL_DIRECTORY,
1289 Default::default(),
1290 )
1291 .await;
1292
1293 let dst = open_dir_checked(
1294 &root,
1295 "bar",
1296 fio::Flags::FLAG_MAYBE_CREATE
1297 | fio::PERM_READABLE
1298 | fio::PERM_WRITABLE
1299 | fio::Flags::PROTOCOL_DIRECTORY,
1300 Default::default(),
1301 )
1302 .await;
1303
1304 let src_file = open_file_checked(
1306 &root,
1307 "foo/a",
1308 fio::Flags::FLAG_MAYBE_CREATE
1309 | fio::PERM_READABLE
1310 | fio::PERM_WRITABLE
1311 | fio::Flags::PROTOCOL_FILE,
1312 &Default::default(),
1313 )
1314 .await;
1315 let buf = vec![0xaa as u8; 8192];
1316 file::write(&src_file, buf.as_slice()).await.expect("Failed to write to file");
1317 close_file_checked(src_file).await;
1318
1319 let f = open_file_checked(
1321 &root,
1322 "bar/b",
1323 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1324 &Default::default(),
1325 )
1326 .await;
1327 close_file_checked(f).await;
1328
1329 let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1330 Status::ok(status).expect("get_token failed");
1331 src.rename("a", Event::from(dst_token.unwrap()), "b")
1332 .await
1333 .expect("FIDL call failed")
1334 .expect("rename failed");
1335
1336 assert_eq!(
1337 open_file(&root, "foo/a", fio::Flags::PROTOCOL_FILE, &Default::default())
1338 .await
1339 .expect_err("Open succeeded")
1340 .root_cause()
1341 .downcast_ref::<Status>()
1342 .expect("No status"),
1343 &Status::NOT_FOUND,
1344 );
1345 let file = open_file_checked(
1346 &root,
1347 "bar/b",
1348 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1349 &Default::default(),
1350 )
1351 .await;
1352 let buf = file::read(&file).await.expect("read file failed");
1353 assert_eq!(buf, vec![0xaa as u8; 8192]);
1354 close_file_checked(file).await;
1355
1356 close_dir_checked(dst).await;
1357 close_dir_checked(src).await;
1358 fixture.close().await;
1359 }
1360
1361 #[fuchsia::test(threads = 10)]
1362 async fn test_rename_overwrites_dir() {
1363 use zx::Event;
1364 let fixture = TestFixture::new().await;
1365 let root = fixture.root();
1366
1367 let src = open_dir_checked(
1368 &root,
1369 "foo",
1370 fio::Flags::FLAG_MAYBE_CREATE
1371 | fio::PERM_READABLE
1372 | fio::PERM_WRITABLE
1373 | fio::Flags::PROTOCOL_DIRECTORY,
1374 Default::default(),
1375 )
1376 .await;
1377
1378 let dst = open_dir_checked(
1379 &root,
1380 "bar",
1381 fio::Flags::FLAG_MAYBE_CREATE
1382 | fio::PERM_READABLE
1383 | fio::PERM_WRITABLE
1384 | fio::Flags::PROTOCOL_DIRECTORY,
1385 Default::default(),
1386 )
1387 .await;
1388
1389 open_dir_checked(
1391 &root,
1392 "foo/a",
1393 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1394 Default::default(),
1395 )
1396 .await;
1397 open_file_checked(
1398 &root,
1399 "foo/a/file",
1400 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1401 &Default::default(),
1402 )
1403 .await;
1404 open_dir_checked(
1405 &root,
1406 "bar/b",
1407 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1408 Default::default(),
1409 )
1410 .await;
1411
1412 let (status, dst_token) = dst.get_token().await.expect("FIDL call failed");
1413 Status::ok(status).expect("get_token failed");
1414 src.rename("a", Event::from(dst_token.unwrap()), "b")
1415 .await
1416 .expect("FIDL call failed")
1417 .expect("rename failed");
1418
1419 assert_eq!(
1420 open_dir(&root, "foo/a", fio::Flags::PROTOCOL_DIRECTORY, &Default::default())
1421 .await
1422 .expect_err("Open succeeded")
1423 .root_cause()
1424 .downcast_ref::<Status>()
1425 .expect("No status"),
1426 &Status::NOT_FOUND,
1427 );
1428 let f =
1429 open_file_checked(&root, "bar/b/file", fio::Flags::PROTOCOL_FILE, &Default::default())
1430 .await;
1431 close_file_checked(f).await;
1432
1433 close_dir_checked(dst).await;
1434 close_dir_checked(src).await;
1435
1436 fixture.close().await;
1437 }
1438
1439 #[fuchsia::test]
1440 async fn test_background_flush() {
1441 let fixture = TestFixture::new().await;
1442 {
1443 let file = open_file_checked(
1444 fixture.root(),
1445 "file",
1446 fio::Flags::FLAG_MAYBE_CREATE
1447 | fio::PERM_READABLE
1448 | fio::PERM_WRITABLE
1449 | fio::Flags::PROTOCOL_FILE,
1450 &Default::default(),
1451 )
1452 .await;
1453 let object_id = file
1454 .get_attributes(fio::NodeAttributesQuery::ID)
1455 .await
1456 .expect("Fidl get attr")
1457 .expect("get attr")
1458 .1
1459 .id
1460 .unwrap();
1461
1462 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1464
1465 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1467 let volume = fixture.volume().volume().clone();
1468
1469 let data_has_persisted = || async {
1470 let object =
1473 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1474 .await
1475 .expect("open_object failed");
1476 let data = object.contents(8192).await.expect("read failed");
1477 data.len() == 1 && data[..] == [123u8]
1478 };
1479 assert!(!data_has_persisted().await);
1480
1481 fixture.volume().volume().start_background_task(
1482 MemoryPressureConfig {
1483 mem_normal: MemoryPressureLevelConfig {
1484 background_task_period: Duration::from_millis(100),
1485 background_task_initial_delay: Duration::from_millis(100),
1486 ..Default::default()
1487 },
1488 mem_warning: Default::default(),
1489 mem_critical: Default::default(),
1490 },
1491 None,
1492 );
1493
1494 const MAX_WAIT: Duration = Duration::from_secs(20);
1495 let wait_increments = Duration::from_millis(400);
1496 let mut total_waited = Duration::ZERO;
1497
1498 while total_waited < MAX_WAIT {
1499 fasync::Timer::new(wait_increments).await;
1500 total_waited += wait_increments;
1501
1502 if data_has_persisted().await {
1503 break;
1504 }
1505 }
1506
1507 assert!(data_has_persisted().await);
1508 }
1509
1510 fixture.close().await;
1511 }
1512
1513 #[fuchsia::test(threads = 2)]
1514 async fn test_background_flush_with_warning_memory_pressure() {
1515 let fixture = TestFixture::new().await;
1516 {
1517 let file = open_file_checked(
1518 fixture.root(),
1519 "file",
1520 fio::Flags::FLAG_MAYBE_CREATE
1521 | fio::PERM_READABLE
1522 | fio::PERM_WRITABLE
1523 | fio::Flags::PROTOCOL_FILE,
1524 &Default::default(),
1525 )
1526 .await;
1527 let object_id = file
1528 .get_attributes(fio::NodeAttributesQuery::ID)
1529 .await
1530 .expect("Fidl get attr")
1531 .expect("get attr")
1532 .1
1533 .id
1534 .unwrap();
1535
1536 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1538
1539 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1541 let volume = fixture.volume().volume().clone();
1542
1543 let data_has_persisted = || async {
1544 let object =
1547 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1548 .await
1549 .expect("open_object failed");
1550 let data = object.contents(8192).await.expect("read failed");
1551 data.len() == 1 && data[..] == [123u8]
1552 };
1553 assert!(!data_has_persisted().await);
1554
1555 let flush_config = MemoryPressureConfig {
1557 mem_normal: MemoryPressureLevelConfig {
1558 background_task_period: Duration::from_secs(20),
1559 cache_size_limit: DIRENT_CACHE_LIMIT,
1560 ..Default::default()
1561 },
1562 mem_warning: MemoryPressureLevelConfig {
1563 background_task_period: Duration::from_millis(100),
1564 cache_size_limit: 100,
1565 background_task_initial_delay: Duration::from_millis(100),
1566 ..Default::default()
1567 },
1568 mem_critical: MemoryPressureLevelConfig {
1569 background_task_period: Duration::from_secs(20),
1570 cache_size_limit: 50,
1571 ..Default::default()
1572 },
1573 };
1574 fixture.volume().volume().start_background_task(
1575 flush_config,
1576 fixture.volumes_directory().memory_pressure_monitor(),
1577 );
1578
1579 fixture
1581 .memory_pressure_proxy()
1582 .on_level_changed(MemoryPressureLevel::Warning)
1583 .await
1584 .expect("Failed to send memory pressure level change");
1585
1586 const MAX_WAIT: Duration = Duration::from_secs(3);
1589 let wait_increments = Duration::from_millis(400);
1590 let mut total_waited = Duration::ZERO;
1591
1592 while total_waited < MAX_WAIT {
1593 fasync::Timer::new(wait_increments).await;
1594 total_waited += wait_increments;
1595
1596 if data_has_persisted().await {
1597 break;
1598 }
1599 }
1600
1601 assert!(data_has_persisted().await);
1602 assert_eq!(fixture.volume().volume().dirent_cache().limit(), 100);
1603 }
1604
1605 fixture.close().await;
1606 }
1607
1608 #[fuchsia::test(threads = 2)]
1609 async fn test_background_flush_with_critical_memory_pressure() {
1610 let fixture = TestFixture::new().await;
1611 {
1612 let file = open_file_checked(
1613 fixture.root(),
1614 "file",
1615 fio::Flags::FLAG_MAYBE_CREATE
1616 | fio::PERM_READABLE
1617 | fio::PERM_WRITABLE
1618 | fio::Flags::PROTOCOL_FILE,
1619 &Default::default(),
1620 )
1621 .await;
1622 let object_id = file
1623 .get_attributes(fio::NodeAttributesQuery::ID)
1624 .await
1625 .expect("Fidl get attr")
1626 .expect("get attr")
1627 .1
1628 .id
1629 .unwrap();
1630
1631 file.write_at(&[123u8], 0).await.expect("FIDL write_at").expect("write_at");
1633
1634 assert_eq!(fixture.volume().volume().dirent_cache().limit(), DIRENT_CACHE_LIMIT);
1636 let volume = fixture.volume().volume().clone();
1637
1638 let data_has_persisted = || async {
1639 let object =
1642 ObjectStore::open_object(&volume, object_id, HandleOptions::default(), None)
1643 .await
1644 .expect("open_object failed");
1645 let data = object.contents(8192).await.expect("read failed");
1646 data.len() == 1 && data[..] == [123u8]
1647 };
1648 assert!(!data_has_persisted().await);
1649
1650 let flush_config = MemoryPressureConfig {
1651 mem_normal: MemoryPressureLevelConfig {
1652 cache_size_limit: DIRENT_CACHE_LIMIT,
1653 ..Default::default()
1654 },
1655 mem_warning: MemoryPressureLevelConfig {
1656 cache_size_limit: 100,
1657 ..Default::default()
1658 },
1659 mem_critical: MemoryPressureLevelConfig {
1660 cache_size_limit: 50,
1661 ..Default::default()
1662 },
1663 };
1664 fixture.volume().volume().start_background_task(
1665 flush_config,
1666 fixture.volumes_directory().memory_pressure_monitor(),
1667 );
1668
1669 fixture
1671 .memory_pressure_proxy()
1672 .on_level_changed(MemoryPressureLevel::Critical)
1673 .await
1674 .expect("Failed to send memory pressure level change");
1675
1676 const MAX_WAIT: Duration = Duration::from_secs(2);
1678 let wait_increments = Duration::from_millis(400);
1679 let mut total_waited = Duration::ZERO;
1680
1681 while total_waited < MAX_WAIT {
1682 fasync::Timer::new(wait_increments).await;
1683 total_waited += wait_increments;
1684
1685 if data_has_persisted().await {
1686 break;
1687 }
1688 }
1689
1690 assert!(data_has_persisted().await);
1691 assert_eq!(fixture.volume().volume().dirent_cache().limit(), 50);
1692 }
1693
1694 fixture.close().await;
1695 }
1696
1697 #[fuchsia::test(threads = 2)]
1700 async fn test_query_info_unmounted_volume() {
1701 const TEST_VOLUME: &str = "test_1234";
1702 let crypt = Arc::new(new_insecure_crypt()) as Arc<dyn Crypt>;
1703
1704 let fixture = TestFixture::new().await;
1705 {
1706 let volumes_directory = fixture.volumes_directory();
1707 let volume = volumes_directory
1708 .create_and_mount_volume(
1709 TEST_VOLUME,
1710 Some(crypt.clone()),
1711 false,
1712 CreateOptions::default(),
1713 )
1714 .await
1715 .unwrap();
1716
1717 assert!(volume.volume().get_volume_data().await.is_some());
1718
1719 volumes_directory
1721 .lock()
1722 .await
1723 .unmount(volume.volume().store().store_object_id())
1724 .await
1725 .expect("unmount failed");
1726
1727 assert!(volume.volume().get_volume_data().await.is_none());
1729 }
1730 fixture.close().await;
1731 }
1732
1733 #[fuchsia::test]
1734 async fn test_project_limit_persistence() {
1735 const BYTES_LIMIT_1: u64 = 123456;
1736 const NODES_LIMIT_1: u64 = 4321;
1737 const BYTES_LIMIT_2: u64 = 456789;
1738 const NODES_LIMIT_2: u64 = 9876;
1739 const VOLUME_NAME: &str = "A";
1740 const FILE_NAME: &str = "B";
1741 const PROJECT_ID: u64 = 42;
1742 const PROJECT_ID2: u64 = 343;
1743 let volume_store_id;
1744 let node_id;
1745 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1746 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
1747 {
1748 let blob_resupplied_count =
1749 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1750 let volumes_directory = VolumesDirectory::new(
1751 root_volume(filesystem.clone()).await.unwrap(),
1752 Weak::new(),
1753 None,
1754 blob_resupplied_count,
1755 MemoryPressureConfig::default(),
1756 )
1757 .await
1758 .unwrap();
1759
1760 let volume_and_root = volumes_directory
1761 .create_and_mount_volume(VOLUME_NAME, None, false, CreateOptions::default())
1762 .await
1763 .expect("create unencrypted volume failed");
1764 volume_store_id = volume_and_root.volume().store().store_object_id();
1765
1766 let (volume_dir_proxy, dir_server_end) =
1767 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1768 volumes_directory
1769 .serve_volume(&volume_and_root, dir_server_end, false)
1770 .expect("serve_volume failed");
1771
1772 let project_proxy =
1773 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1774 .expect("Unable to connect to project id service");
1775
1776 project_proxy
1777 .set_limit(0, BYTES_LIMIT_1, NODES_LIMIT_1)
1778 .await
1779 .unwrap()
1780 .expect_err("Should not set limits for project id 0");
1781
1782 assert_eq!(
1783 project_proxy.clear(0).await.unwrap().expect_err("Should not clear project id 0"),
1784 Status::OUT_OF_RANGE.into_raw()
1785 );
1786
1787 assert_eq!(
1788 project_proxy
1789 .info(0)
1790 .await
1791 .unwrap()
1792 .expect_err("Should not get info for project id 0"),
1793 Status::OUT_OF_RANGE.into_raw()
1794 );
1795
1796 project_proxy
1797 .set_limit(PROJECT_ID, BYTES_LIMIT_1, NODES_LIMIT_1)
1798 .await
1799 .unwrap()
1800 .expect("To set limits");
1801 {
1802 let BytesAndNodes { bytes, nodes } =
1803 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1804 assert_eq!(bytes, BYTES_LIMIT_1);
1805 assert_eq!(nodes, NODES_LIMIT_1);
1806 }
1807
1808 let file_proxy = {
1809 let (root_proxy, root_server_end) =
1810 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1811 volume_dir_proxy
1812 .open(
1813 "root",
1814 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1815 &Default::default(),
1816 root_server_end.into_channel(),
1817 )
1818 .expect("Failed to open volume root");
1819
1820 open_file_checked(
1821 &root_proxy,
1822 FILE_NAME,
1823 fio::Flags::FLAG_MAYBE_CREATE
1824 | fio::PERM_READABLE
1825 | fio::PERM_WRITABLE
1826 | fio::Flags::PROTOCOL_FILE,
1827 &Default::default(),
1828 )
1829 .await
1830 };
1831
1832 let (_, immutable_attributes) =
1833 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
1834 node_id = immutable_attributes.id.unwrap();
1835
1836 project_proxy
1837 .set_for_node(node_id, 0)
1838 .await
1839 .unwrap()
1840 .expect_err("Should not set 0 project id");
1841
1842 project_proxy
1843 .set_for_node(node_id, PROJECT_ID)
1844 .await
1845 .unwrap()
1846 .expect("Setting project on node");
1847
1848 project_proxy
1849 .set_limit(PROJECT_ID, BYTES_LIMIT_2, NODES_LIMIT_2)
1850 .await
1851 .unwrap()
1852 .expect("To set limits");
1853 {
1854 let BytesAndNodes { bytes, nodes } =
1855 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
1856 assert_eq!(bytes, BYTES_LIMIT_2);
1857 assert_eq!(nodes, NODES_LIMIT_2);
1858 }
1859
1860 assert_eq!(
1861 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1862 PROJECT_ID
1863 );
1864
1865 volumes_directory.terminate().await;
1866 filesystem.close().await.expect("close filesystem failed");
1867 }
1868 device = filesystem.take_device().await;
1869 device.ensure_unique();
1870 device.reopen(false);
1871 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
1872 {
1873 fsck(filesystem.clone()).await.expect("Fsck");
1874 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
1875 let blob_resupplied_count =
1876 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1877 let volumes_directory = VolumesDirectory::new(
1878 root_volume(filesystem.clone()).await.unwrap(),
1879 Weak::new(),
1880 None,
1881 blob_resupplied_count,
1882 MemoryPressureConfig::default(),
1883 )
1884 .await
1885 .unwrap();
1886 let volume_and_root = volumes_directory
1887 .mount_volume(VOLUME_NAME, None, false)
1888 .await
1889 .expect("mount unencrypted volume failed");
1890
1891 let (volume_proxy, _scope) = crate::volumes_directory::serve_startup_volume_proxy(
1892 &volumes_directory,
1893 VOLUME_NAME,
1894 );
1895
1896 let project_proxy = {
1897 let (volume_dir_proxy, dir_server_end) =
1898 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1899 volumes_directory
1900 .serve_volume(&volume_and_root, dir_server_end, false)
1901 .expect("serve_volume failed");
1902
1903 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1904 .expect("Unable to connect to project id service")
1905 };
1906
1907 let usage_bytes_and_nodes = {
1908 let (
1909 BytesAndNodes { bytes: limit_bytes, nodes: limit_nodes },
1910 usage_bytes_and_nodes,
1911 ) = project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info");
1912 assert_eq!(limit_bytes, BYTES_LIMIT_2);
1913 assert_eq!(limit_nodes, NODES_LIMIT_2);
1914 usage_bytes_and_nodes
1915 };
1916
1917 project_proxy.clear(PROJECT_ID).await.unwrap().expect("To clear limits");
1919
1920 assert_eq!(
1921 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1922 PROJECT_ID
1923 );
1924 project_proxy
1925 .set_for_node(node_id, PROJECT_ID2)
1926 .await
1927 .unwrap()
1928 .expect("Changing project");
1929 assert_eq!(
1930 project_proxy.get_for_node(node_id).await.unwrap().expect("Checking project"),
1931 PROJECT_ID2
1932 );
1933
1934 assert_eq!(
1935 project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
1936 Status::NOT_FOUND.into_raw()
1937 );
1938 assert_eq!(
1939 project_proxy.info(PROJECT_ID2).await.unwrap().expect("Fetching project info").1,
1940 usage_bytes_and_nodes
1941 );
1942
1943 std::mem::drop(volume_proxy);
1944 volumes_directory.terminate().await;
1945 std::mem::drop(volumes_directory);
1946 filesystem.close().await.expect("close filesystem failed");
1947 }
1948 device = filesystem.take_device().await;
1949 device.ensure_unique();
1950 device.reopen(false);
1951 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
1952 fsck(filesystem.clone()).await.expect("Fsck");
1953 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
1954 let blob_resupplied_count =
1955 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
1956 let volumes_directory = VolumesDirectory::new(
1957 root_volume(filesystem.clone()).await.unwrap(),
1958 Weak::new(),
1959 None,
1960 blob_resupplied_count,
1961 MemoryPressureConfig::default(),
1962 )
1963 .await
1964 .unwrap();
1965 let volume_and_root = volumes_directory
1966 .mount_volume(VOLUME_NAME, None, false)
1967 .await
1968 .expect("mount unencrypted volume failed");
1969 let (volume_dir_proxy, dir_server_end) =
1970 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1971 volumes_directory
1972 .serve_volume(&volume_and_root, dir_server_end, false)
1973 .expect("serve_volume failed");
1974 let project_proxy = connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
1975 .expect("Unable to connect to project id service");
1976 assert_eq!(
1977 project_proxy.info(PROJECT_ID).await.unwrap().expect_err("Expect missing limits"),
1978 Status::NOT_FOUND.into_raw()
1979 );
1980 volumes_directory.terminate().await;
1981 std::mem::drop(volumes_directory);
1982 filesystem.close().await.expect("close filesystem failed");
1983 }
1984
1985 #[fuchsia::test]
1986 async fn test_project_limit_accounting() {
1987 const BYTES_LIMIT: u64 = 123456;
1988 const NODES_LIMIT: u64 = 4321;
1989 const VOLUME_NAME: &str = "A";
1990 const FILE_NAME: &str = "B";
1991 const PROJECT_ID: u64 = 42;
1992 let volume_store_id;
1993 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1994 let first_object_id;
1995 let mut bytes_usage;
1996 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
1997 {
1998 let blob_resupplied_count =
1999 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2000 let volumes_directory = VolumesDirectory::new(
2001 root_volume(filesystem.clone()).await.unwrap(),
2002 Weak::new(),
2003 None,
2004 blob_resupplied_count,
2005 MemoryPressureConfig::default(),
2006 )
2007 .await
2008 .unwrap();
2009
2010 let volume_and_root = volumes_directory
2011 .create_and_mount_volume(
2012 VOLUME_NAME,
2013 Some(Arc::new(new_insecure_crypt())),
2014 false,
2015 CreateOptions::default(),
2016 )
2017 .await
2018 .expect("create unencrypted volume failed");
2019 volume_store_id = volume_and_root.volume().store().store_object_id();
2020
2021 let (volume_dir_proxy, dir_server_end) =
2022 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2023 volumes_directory
2024 .serve_volume(&volume_and_root, dir_server_end, false)
2025 .expect("serve_volume failed");
2026
2027 let project_proxy =
2028 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2029 .expect("Unable to connect to project id service");
2030
2031 project_proxy
2032 .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
2033 .await
2034 .unwrap()
2035 .expect("To set limits");
2036 {
2037 let BytesAndNodes { bytes, nodes } =
2038 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").0;
2039 assert_eq!(bytes, BYTES_LIMIT);
2040 assert_eq!(nodes, NODES_LIMIT);
2041 }
2042
2043 let file_proxy = {
2044 let (root_proxy, root_server_end) =
2045 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2046 volume_dir_proxy
2047 .open(
2048 "root",
2049 fio::PERM_READABLE | fio::PERM_WRITABLE,
2050 &Default::default(),
2051 root_server_end.into_channel(),
2052 )
2053 .expect("Failed to open volume root");
2054
2055 open_file_checked(
2056 &root_proxy,
2057 FILE_NAME,
2058 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2059 &Default::default(),
2060 )
2061 .await
2062 };
2063
2064 assert_eq!(
2065 8192,
2066 file_proxy
2067 .write(&vec![0xff as u8; 8192])
2068 .await
2069 .expect("FIDL call failed")
2070 .map_err(Status::err_from_raw)
2071 .expect("File write was successful")
2072 );
2073 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2074
2075 {
2076 let BytesAndNodes { bytes, nodes } =
2077 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2078 assert_eq!(bytes, 0);
2079 assert_eq!(nodes, 0);
2080 }
2081
2082 let (_, immutable_attributes) =
2083 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2084 let node_id = immutable_attributes.id.unwrap();
2085
2086 first_object_id = node_id;
2087 project_proxy
2088 .set_for_node(node_id, PROJECT_ID)
2089 .await
2090 .unwrap()
2091 .expect("Setting project on node");
2092
2093 bytes_usage = {
2094 let BytesAndNodes { bytes, nodes } =
2095 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2096 assert!(bytes > 0);
2097 assert_eq!(nodes, 1);
2098 bytes
2099 };
2100
2101 assert_eq!(
2103 8192,
2104 file_proxy
2105 .write(&vec![0xff as u8; 8192])
2106 .await
2107 .expect("FIDL call failed")
2108 .map_err(Status::err_from_raw)
2109 .expect("File write was successful")
2110 );
2111 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2112 bytes_usage = {
2113 let BytesAndNodes { bytes, nodes } =
2114 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2115 assert!(bytes > bytes_usage);
2116 assert_eq!(nodes, 1);
2117 bytes
2118 };
2119
2120 volumes_directory.terminate().await;
2121 filesystem.close().await.expect("close filesystem failed");
2122 }
2123 device = filesystem.take_device().await;
2124 device.ensure_unique();
2125 device.reopen(false);
2126 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2127 {
2128 fsck(filesystem.clone()).await.expect("Fsck");
2129 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2130 .await
2131 .expect("Fsck volume");
2132 let blob_resupplied_count =
2133 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2134 let volumes_directory = VolumesDirectory::new(
2135 root_volume(filesystem.clone()).await.unwrap(),
2136 Weak::new(),
2137 None,
2138 blob_resupplied_count,
2139 MemoryPressureConfig::default(),
2140 )
2141 .await
2142 .unwrap();
2143 let volume_and_root = volumes_directory
2144 .mount_volume(VOLUME_NAME, Some(Arc::new(new_insecure_crypt())), false)
2145 .await
2146 .expect("mount unencrypted volume failed");
2147
2148 let (root_proxy, project_proxy) = {
2149 let (volume_dir_proxy, dir_server_end) =
2150 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2151 volumes_directory
2152 .serve_volume(&volume_and_root, dir_server_end, false)
2153 .expect("serve_volume failed");
2154
2155 let (root_proxy, root_server_end) =
2156 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2157 volume_dir_proxy
2158 .open(
2159 "root",
2160 fio::PERM_READABLE | fio::PERM_WRITABLE,
2161 &Default::default(),
2162 root_server_end.into_channel(),
2163 )
2164 .expect("Failed to open volume root");
2165 let project_proxy = {
2166 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2167 .expect("Unable to connect to project id service")
2168 };
2169 (root_proxy, project_proxy)
2170 };
2171
2172 {
2173 let BytesAndNodes { bytes, nodes } =
2174 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2175 assert_eq!(bytes, bytes_usage);
2176 assert_eq!(nodes, 1);
2177 }
2178
2179 assert_eq!(
2180 project_proxy
2181 .get_for_node(first_object_id)
2182 .await
2183 .unwrap()
2184 .expect("Checking project"),
2185 PROJECT_ID
2186 );
2187 root_proxy
2188 .unlink(FILE_NAME, &fio::UnlinkOptions::default())
2189 .await
2190 .expect("FIDL call failed")
2191 .expect("unlink failed");
2192 filesystem.graveyard().flush().await;
2193
2194 {
2195 let BytesAndNodes { bytes, nodes } =
2196 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2197 assert_eq!(bytes, 0);
2198 assert_eq!(nodes, 0);
2199 }
2200
2201 let file_proxy = open_file_checked(
2202 &root_proxy,
2203 FILE_NAME,
2204 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2205 &Default::default(),
2206 )
2207 .await;
2208
2209 let (_, immutable_attributes) =
2210 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2211 let node_id = immutable_attributes.id.unwrap();
2212
2213 project_proxy
2214 .set_for_node(node_id, PROJECT_ID)
2215 .await
2216 .unwrap()
2217 .expect("Applying project");
2218
2219 bytes_usage = {
2220 let BytesAndNodes { bytes, nodes } =
2221 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2222 assert!(bytes < bytes_usage);
2224 assert_eq!(nodes, 1);
2225 bytes
2226 };
2227
2228 assert_eq!(
2229 8192,
2230 file_proxy
2231 .write(&vec![0xff as u8; 8192])
2232 .await
2233 .expect("FIDL call failed")
2234 .map_err(Status::err_from_raw)
2235 .expect("File write was successful")
2236 );
2237 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2238 bytes_usage = {
2239 let BytesAndNodes { bytes, nodes } =
2240 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2241 assert!(bytes > bytes_usage);
2242 assert_eq!(nodes, 1);
2243 bytes
2244 };
2245
2246 file_proxy.resize(0).await.expect("FIDL call failed").expect("Resize file");
2248 file_proxy.sync().await.expect("FIDL call failed").expect("Sync failed.");
2249 {
2250 let BytesAndNodes { bytes, nodes } =
2251 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2252 assert!(bytes < bytes_usage);
2253 assert_eq!(nodes, 1);
2254 };
2255
2256 project_proxy
2258 .clear_for_node(node_id)
2259 .await
2260 .expect("FIDL call failed")
2261 .expect("Clear failed.");
2262 {
2263 let BytesAndNodes { bytes, nodes } =
2264 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2265 assert_eq!(bytes, 0);
2266 assert_eq!(nodes, 0);
2267 };
2268
2269 volumes_directory.terminate().await;
2270 filesystem.close().await.expect("close filesystem failed");
2271 }
2272 device = filesystem.take_device().await;
2273 device.ensure_unique();
2274 device.reopen(false);
2275 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2276 fsck(filesystem.clone()).await.expect("Fsck");
2277 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2278 .await
2279 .expect("Fsck volume");
2280 filesystem.close().await.expect("close filesystem failed");
2281 }
2282
2283 #[fuchsia::test]
2284 async fn test_project_node_inheritance() {
2285 const BYTES_LIMIT: u64 = 123456;
2286 const NODES_LIMIT: u64 = 4321;
2287 const VOLUME_NAME: &str = "A";
2288 const DIR_NAME: &str = "B";
2289 const SUBDIR_NAME: &str = "C";
2290 const FILE_NAME: &str = "D";
2291 const PROJECT_ID: u64 = 42;
2292 let volume_store_id;
2293 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2294 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2295 {
2296 let blob_resupplied_count =
2297 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2298 let volumes_directory = VolumesDirectory::new(
2299 root_volume(filesystem.clone()).await.unwrap(),
2300 Weak::new(),
2301 None,
2302 blob_resupplied_count,
2303 MemoryPressureConfig::default(),
2304 )
2305 .await
2306 .unwrap();
2307
2308 let volume_and_root = volumes_directory
2309 .create_and_mount_volume(
2310 VOLUME_NAME,
2311 Some(Arc::new(new_insecure_crypt())),
2312 false,
2313 CreateOptions::default(),
2314 )
2315 .await
2316 .expect("create unencrypted volume failed");
2317 volume_store_id = volume_and_root.volume().store().store_object_id();
2318
2319 let (volume_dir_proxy, dir_server_end) =
2320 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2321 volumes_directory
2322 .serve_volume(&volume_and_root, dir_server_end, false)
2323 .expect("serve_volume failed");
2324
2325 let project_proxy =
2326 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2327 .expect("Unable to connect to project id service");
2328
2329 project_proxy
2330 .set_limit(PROJECT_ID, BYTES_LIMIT, NODES_LIMIT)
2331 .await
2332 .unwrap()
2333 .expect("To set limits");
2334
2335 let dir_proxy = {
2336 let (root_proxy, root_server_end) =
2337 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2338 volume_dir_proxy
2339 .open(
2340 "root",
2341 fio::PERM_READABLE | fio::PERM_WRITABLE,
2342 &Default::default(),
2343 root_server_end.into_channel(),
2344 )
2345 .expect("Failed to open volume root");
2346
2347 open_dir_checked(
2348 &root_proxy,
2349 DIR_NAME,
2350 fio::Flags::FLAG_MAYBE_CREATE
2351 | fio::PERM_READABLE
2352 | fio::PERM_WRITABLE
2353 | fio::Flags::PROTOCOL_DIRECTORY,
2354 Default::default(),
2355 )
2356 .await
2357 };
2358 {
2359 let (_, immutable_attributes) =
2360 dir_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2361 let node_id = immutable_attributes.id.unwrap();
2362
2363 project_proxy
2364 .set_for_node(node_id, PROJECT_ID)
2365 .await
2366 .unwrap()
2367 .expect("Setting project on node");
2368 }
2369
2370 let subdir_proxy = open_dir_checked(
2371 &dir_proxy,
2372 SUBDIR_NAME,
2373 fio::Flags::FLAG_MAYBE_CREATE
2374 | fio::PERM_READABLE
2375 | fio::PERM_WRITABLE
2376 | fio::Flags::PROTOCOL_DIRECTORY,
2377 Default::default(),
2378 )
2379 .await;
2380 {
2381 let (_, immutable_attributes) = subdir_proxy
2382 .get_attributes(fio::NodeAttributesQuery::ID)
2383 .await
2384 .unwrap()
2385 .unwrap();
2386 let node_id = immutable_attributes.id.unwrap();
2387
2388 assert_eq!(
2389 project_proxy
2390 .get_for_node(node_id)
2391 .await
2392 .unwrap()
2393 .expect("Setting project on node"),
2394 PROJECT_ID
2395 );
2396 }
2397
2398 let file_proxy = open_file_checked(
2399 &subdir_proxy,
2400 FILE_NAME,
2401 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2402 &Default::default(),
2403 )
2404 .await;
2405 {
2406 let (_, immutable_attributes) =
2407 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2408 let node_id = immutable_attributes.id.unwrap();
2409
2410 assert_eq!(
2411 project_proxy
2412 .get_for_node(node_id)
2413 .await
2414 .unwrap()
2415 .expect("Setting project on node"),
2416 PROJECT_ID
2417 );
2418 }
2419
2420 let tmpfile_proxy = open_file_checked(
2423 &subdir_proxy,
2424 ".",
2425 fio::Flags::PROTOCOL_FILE
2426 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2427 | fio::PERM_READABLE,
2428 &fio::Options::default(),
2429 )
2430 .await;
2431 {
2432 let (_, immutable_attributes) = tmpfile_proxy
2433 .get_attributes(fio::NodeAttributesQuery::ID)
2434 .await
2435 .unwrap()
2436 .unwrap();
2437 let node_id: u64 = immutable_attributes.id.unwrap();
2438 assert_eq!(
2439 project_proxy
2440 .get_for_node(node_id)
2441 .await
2442 .unwrap()
2443 .expect("Setting project on node"),
2444 PROJECT_ID
2445 );
2446 }
2447
2448 let BytesAndNodes { nodes, .. } =
2449 project_proxy.info(PROJECT_ID).await.unwrap().expect("Fetching project info").1;
2450 assert_eq!(nodes, 3);
2451 volumes_directory.terminate().await;
2452 filesystem.close().await.expect("close filesystem failed");
2453 }
2454 device = filesystem.take_device().await;
2455 device.ensure_unique();
2456 device.reopen(false);
2457 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2458 fsck(filesystem.clone()).await.expect("Fsck");
2459 fsck_volume(filesystem.as_ref(), volume_store_id, Some(Arc::new(new_insecure_crypt())))
2460 .await
2461 .expect("Fsck volume");
2462 filesystem.close().await.expect("close filesystem failed");
2463 }
2464
2465 #[fuchsia::test]
2466 async fn test_project_listing() {
2467 const VOLUME_NAME: &str = "A";
2468 const FILE_NAME: &str = "B";
2469 const NON_ZERO_PROJECT_ID: u64 = 3;
2470 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
2471 let volume_store_id;
2472 let filesystem = FxFilesystem::new_empty(device).await.unwrap();
2473 {
2474 let blob_resupplied_count =
2475 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
2476 let volumes_directory = VolumesDirectory::new(
2477 root_volume(filesystem.clone()).await.unwrap(),
2478 Weak::new(),
2479 None,
2480 blob_resupplied_count,
2481 MemoryPressureConfig::default(),
2482 )
2483 .await
2484 .unwrap();
2485 let volume_and_root = volumes_directory
2486 .create_and_mount_volume(VOLUME_NAME, None, false, CreateOptions::default())
2487 .await
2488 .expect("create unencrypted volume failed");
2489 volume_store_id = volume_and_root.volume().store().store_object_id();
2490
2491 let (volume_dir_proxy, dir_server_end) =
2492 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2493 volumes_directory
2494 .serve_volume(&volume_and_root, dir_server_end, false)
2495 .expect("serve_volume failed");
2496 let project_proxy =
2497 connect_to_protocol_at_dir_svc::<ProjectIdMarker>(&volume_dir_proxy)
2498 .expect("Unable to connect to project id service");
2499 assert!(FxVolume::MAX_PROJECT_ENTRIES >= 4);
2501 let num_entries = u64::try_from(FxVolume::MAX_PROJECT_ENTRIES + 3).unwrap();
2503 for project_id in 1..=num_entries {
2504 project_proxy.set_limit(project_id, 1, 1).await.unwrap().expect("To set limits");
2505 }
2506
2507 let file_proxy = {
2510 let (root_proxy, root_server_end) =
2511 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
2512 volume_dir_proxy
2513 .open(
2514 "root",
2515 fio::PERM_READABLE | fio::PERM_WRITABLE,
2516 &Default::default(),
2517 root_server_end.into_channel(),
2518 )
2519 .expect("Failed to open volume root");
2520
2521 open_file_checked(
2522 &root_proxy,
2523 FILE_NAME,
2524 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
2525 &Default::default(),
2526 )
2527 .await
2528 };
2529 let (_, immutable_attributes) =
2530 file_proxy.get_attributes(fio::NodeAttributesQuery::ID).await.unwrap().unwrap();
2531 let node_id = immutable_attributes.id.unwrap();
2532 project_proxy
2533 .set_for_node(node_id, NON_ZERO_PROJECT_ID)
2534 .await
2535 .unwrap()
2536 .expect("Setting project on node");
2537 {
2538 let BytesAndNodes { nodes, .. } = project_proxy
2539 .info(NON_ZERO_PROJECT_ID)
2540 .await
2541 .unwrap()
2542 .expect("Fetching project info")
2543 .1;
2544 assert_eq!(nodes, 1);
2545 }
2546
2547 let (mut entries, mut next_token) =
2549 project_proxy.list(None).await.unwrap().expect("To get project listing");
2550 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2551 assert!(next_token.is_some());
2552 assert!(entries.contains(&1));
2553 assert!(entries.contains(&3));
2554 assert!(!entries.contains(&num_entries));
2555 (entries, next_token) = project_proxy
2557 .list(next_token.as_deref())
2558 .await
2559 .unwrap()
2560 .expect("To get project listing");
2561 assert_eq!(entries.len(), 3);
2562 assert!(next_token.is_none());
2563 assert!(entries.contains(&num_entries));
2564 assert!(!entries.contains(&1));
2565 assert!(!entries.contains(&3));
2566 project_proxy.clear(1).await.unwrap().expect("Clear project");
2568 project_proxy.clear(3).await.unwrap().expect("Clear project");
2569 (entries, next_token) =
2570 project_proxy.list(None).await.unwrap().expect("To get project listing");
2571 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2572 assert!(next_token.is_some());
2573 assert!(!entries.contains(&num_entries));
2574 assert!(!entries.contains(&1));
2575 assert!(entries.contains(&3));
2576 (entries, next_token) = project_proxy
2577 .list(next_token.as_deref())
2578 .await
2579 .unwrap()
2580 .expect("To get project listing");
2581 assert_eq!(entries.len(), 2);
2582 assert!(next_token.is_none());
2583 assert!(entries.contains(&num_entries));
2584 project_proxy.clear(2).await.unwrap().expect("Clear project");
2586 project_proxy.clear(4).await.unwrap().expect("Clear project");
2587 (entries, next_token) =
2588 project_proxy.list(None).await.unwrap().expect("To get project listing");
2589 assert_eq!(entries.len(), FxVolume::MAX_PROJECT_ENTRIES);
2590 assert!(next_token.is_none());
2591 assert!(entries.contains(&num_entries));
2592 volumes_directory.terminate().await;
2593 filesystem.close().await.expect("close filesystem failed");
2594 }
2595 device = filesystem.take_device().await;
2596 device.ensure_unique();
2597 device.reopen(false);
2598 let filesystem = FxFilesystem::open(device as DeviceHolder).await.unwrap();
2599 fsck(filesystem.clone()).await.expect("Fsck");
2600 fsck_volume(filesystem.as_ref(), volume_store_id, None).await.expect("Fsck volume");
2601 filesystem.close().await.expect("close filesystem failed");
2602 }
2603
2604 #[fuchsia::test(threads = 10)]
2605 async fn test_profile_blob() {
2606 let mut hashes = Vec::new();
2607 let device = {
2608 let fixture = blob_testing::new_blob_fixture().await;
2609
2610 for i in 0..3u64 {
2611 let hash =
2612 fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
2613 hashes.push(hash);
2614 }
2615 fixture.close().await
2616 };
2617 device.ensure_unique();
2618
2619 device.reopen(false);
2620 let mut device = {
2621 let fixture = blob_testing::open_blob_fixture(device).await;
2622 fixture
2623 .volume()
2624 .volume()
2625 .record_and_replay_profile(new_profile_state(true), "foo")
2626 .await
2627 .expect("Recording");
2628
2629 let mut writable = [0u8];
2631 for hash in &hashes {
2632 let vmo = fixture.get_blob_vmo(*hash).await;
2633 vmo.read(&mut writable, 0).expect("Vmo read");
2634 }
2635 fixture.volume().volume().stop_profile_tasks().await;
2636 fixture.close().await
2637 };
2638
2639 for i in 0..3 {
2641 device.ensure_unique();
2642 device.reopen(false);
2643 let fixture = blob_testing::open_blob_fixture(device).await;
2644 {
2645 for hash in &hashes {
2647 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2648 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2649 }
2650
2651 fixture
2652 .volume()
2653 .volume()
2654 .record_and_replay_profile(new_profile_state(true), "foo")
2655 .await
2656 .expect("Replaying");
2657
2658 {
2660 let store = fixture.volume().volume().store();
2661 let store_id = store.store_object_id();
2662 let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
2663 let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
2664 let mut transaction = store
2665 .new_transaction(
2666 lock_keys!(
2667 LockKey::object(store_id, dir.object_id()),
2668 LockKey::object(store_id, old_file),
2669 ),
2670 Options::default(),
2671 )
2672 .await
2673 .unwrap();
2674 replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
2675 .await
2676 .expect("Replace old profile.");
2677 transaction.commit().await.unwrap();
2678 assert!(
2679 dir.lookup("foo").await.unwrap().is_none(),
2680 "Old profile should be moved"
2681 );
2682 }
2683
2684 async {
2686 for hash in &hashes {
2687 let _vmo = fixture.get_blob_vmo(*hash).await;
2690 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2691 while blob.vmo().info().unwrap().populated_bytes == 0 {
2692 fasync::Timer::new(Duration::from_millis(25)).await;
2693 }
2694 }
2695 }
2696 .on_timeout(std::time::Duration::from_secs(120), || {
2697 panic!("Replay did not page in for all VMOs")
2698 })
2699 .await;
2700
2701 fixture.volume().volume().stop_profile_tasks().await;
2703 }
2704 device = fixture.close().await;
2705 }
2706 }
2707
2708 #[fuchsia::test(threads = 10)]
2709 async fn test_profile_file() {
2710 let mut hashes = Vec::new();
2711 let crypt_file_id;
2712 let device = {
2713 let fixture = TestFixture::new().await;
2714 {
2716 let crypt_dir = open_dir_checked(
2717 fixture.root(),
2718 "crypt_dir",
2719 fio::Flags::FLAG_MUST_CREATE
2720 | fio::PERM_WRITABLE
2721 | fio::Flags::PERM_GET_ATTRIBUTES,
2722 Default::default(),
2723 )
2724 .await;
2725 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2726 crypt
2727 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2728 .expect("add_wrapping_key failed");
2729 crypt_dir
2730 .update_attributes(&fio::MutableNodeAttributes {
2731 wrapping_key_id: Some(WRAPPING_KEY_ID),
2732 ..Default::default()
2733 })
2734 .await
2735 .expect("update_attributes wire call failed")
2736 .expect("update_attributes failed");
2737 let crypt_file = open_file_checked(
2738 &crypt_dir,
2739 "crypt_file",
2740 fio::Flags::FLAG_MUST_CREATE
2741 | fio::PERM_WRITABLE
2742 | fio::Flags::PERM_GET_ATTRIBUTES,
2743 &Default::default(),
2744 )
2745 .await;
2746 crypt_file.write("asdf".as_bytes()).await.unwrap().expect("Writing crypt file");
2747 crypt_file_id = crypt_file
2748 .get_attributes(fio::NodeAttributesQuery::ID)
2749 .await
2750 .unwrap()
2751 .expect("Get id")
2752 .1
2753 .id
2754 .expect("Reading id in response");
2755 }
2756
2757 for i in 0..3u64 {
2758 let file_proxy = open_file_checked(
2759 fixture.root(),
2760 &i.to_string(),
2761 fio::Flags::FLAG_MUST_CREATE
2762 | fio::PERM_WRITABLE
2763 | fio::Flags::PERM_GET_ATTRIBUTES,
2764 &Default::default(),
2765 )
2766 .await;
2767 file_proxy.write(i.to_string().as_bytes()).await.unwrap().expect("Writing file");
2768 let id = file_proxy
2769 .get_attributes(fio::NodeAttributesQuery::ID)
2770 .await
2771 .unwrap()
2772 .expect("Get id")
2773 .1
2774 .id
2775 .expect("Reading id in response");
2776 hashes.push((i, id));
2777 }
2778 fixture.close().await
2779 };
2780 device.ensure_unique();
2781
2782 device.reopen(false);
2783 let mut device = {
2784 let fixture = TestFixture::open(
2785 device,
2786 TestFixtureOptions { format: false, ..Default::default() },
2787 )
2788 .await;
2789 fixture
2790 .volume()
2791 .volume()
2792 .record_and_replay_profile(new_profile_state(false), "foo")
2793 .await
2794 .expect("Recording");
2795
2796 {
2797 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2798 crypt
2799 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2800 .expect("add wrapping key failed");
2801 let crypt_file = open_file_checked(
2802 fixture.root(),
2803 "crypt_dir/crypt_file",
2804 fio::PERM_READABLE,
2805 &Default::default(),
2806 )
2807 .await;
2808 crypt_file.read(1).await.unwrap().expect("Reading crypt file");
2809 }
2810 for (i, _) in &hashes {
2812 let file_proxy = open_file_checked(
2813 fixture.root(),
2814 &i.to_string(),
2815 fio::PERM_READABLE,
2816 &Default::default(),
2817 )
2818 .await;
2819 file_proxy.read(1).await.unwrap().expect("Reading file");
2820 }
2821 fixture.volume().volume().stop_profile_tasks().await;
2822 fixture.close().await
2823 };
2824
2825 for i in 0..3 {
2827 device.ensure_unique();
2828 device.reopen(false);
2829 let fixture = TestFixture::open(
2830 device,
2831 TestFixtureOptions { format: false, ..Default::default() },
2832 )
2833 .await;
2834 {
2835 let volume = fixture.volume().volume().clone();
2837 {
2839 let crypt_file = volume
2840 .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
2841 .await
2842 .expect("Opening file internally")
2843 .into_any()
2844 .downcast::<FxFile>()
2845 .expect("Should be file");
2846 assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
2847 }
2848 for (_, id) in &hashes {
2849 let file = volume
2850 .get_or_load_node(*id, ObjectDescriptor::File, None)
2851 .await
2852 .expect("Opening file internally")
2853 .into_any()
2854 .downcast::<FxFile>()
2855 .expect("Should be file");
2856 assert_eq!(file.vmo().info().unwrap().populated_bytes, 0);
2857 }
2858
2859 fixture
2860 .volume()
2861 .volume()
2862 .record_and_replay_profile(new_profile_state(false), "foo")
2863 .await
2864 .expect("Replaying");
2865
2866 {
2868 let store = fixture.volume().volume().store();
2869 let store_id = store.store_object_id();
2870 let dir = fixture.volume().volume().get_profile_directory().await.unwrap();
2871 let old_file = dir.lookup("foo").await.unwrap().unwrap().0;
2872 let mut transaction = store
2873 .new_transaction(
2874 lock_keys!(
2875 LockKey::object(store_id, dir.object_id()),
2876 LockKey::object(store_id, old_file),
2877 ),
2878 Options::default(),
2879 )
2880 .await
2881 .unwrap();
2882 replace_child(&mut transaction, Some((&dir, "foo")), (&dir, &i.to_string()))
2883 .await
2884 .expect("Replace old profile.");
2885 transaction.commit().await.unwrap();
2886 assert!(
2887 dir.lookup("foo").await.unwrap().is_none(),
2888 "Old profile should be moved"
2889 );
2890 }
2891
2892 async {
2894 for (_, id) in &hashes {
2895 let file = volume
2896 .get_or_load_node(*id, ObjectDescriptor::File, None)
2897 .await
2898 .expect("Opening file internally")
2899 .into_any()
2900 .downcast::<FxFile>()
2901 .expect("Should be file");
2902 while file.vmo().info().unwrap().populated_bytes == 0 {
2903 fasync::Timer::new(Duration::from_millis(25)).await;
2904 }
2905 }
2906 }
2907 .on_timeout(std::time::Duration::from_secs(120), || {
2908 panic!("Replay did not page in for all VMOs")
2909 })
2910 .await;
2911 {
2913 let crypt_file = volume
2914 .get_or_load_node(crypt_file_id, ObjectDescriptor::File, None)
2915 .await
2916 .expect("Opening file internally")
2917 .into_any()
2918 .downcast::<FxFile>()
2919 .expect("Should be file");
2920 assert_eq!(crypt_file.vmo().info().unwrap().populated_bytes, 0);
2921 }
2922
2923 for (i, _) in &hashes {
2925 let _file_proxy = open_file_checked(
2926 fixture.root(),
2927 &i.to_string(),
2928 fio::PERM_READABLE,
2929 &Default::default(),
2930 )
2931 .await;
2932 }
2933
2934 fixture.volume().volume().stop_profile_tasks().await;
2936 }
2937 device = fixture.close().await;
2938 }
2939 }
2940
2941 #[fuchsia::test(threads = 10)]
2942 async fn test_profile_update() {
2943 let mut hashes = Vec::new();
2944 let device = {
2945 let fixture = blob_testing::new_blob_fixture().await;
2946 for i in 0..2u64 {
2947 let hash =
2948 fixture.write_blob(i.to_string().as_bytes(), CompressionMode::Never).await;
2949 hashes.push(hash);
2950 }
2951 fixture.close().await
2952 };
2953 device.ensure_unique();
2954
2955 device.reopen(false);
2956 let device = {
2957 let fixture = blob_testing::open_blob_fixture(device).await;
2958
2959 {
2960 let volume = fixture.volume().volume();
2961 volume
2962 .record_and_replay_profile(new_profile_state(true), "foo")
2963 .await
2964 .expect("Recording");
2965
2966 let original_recorded = RECORDED.load(Ordering::Relaxed);
2967
2968 {
2970 let mut writable = [0u8];
2971 let hash = &hashes[0];
2972 let vmo = fixture.get_blob_vmo(*hash).await;
2973 vmo.read(&mut writable, 0).expect("Vmo read");
2974 }
2975
2976 while RECORDED.load(Ordering::Relaxed) == original_recorded {
2979 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
2980 }
2981
2982 volume.stop_profile_tasks().await;
2983 }
2984 fixture.close().await
2985 };
2986
2987 device.ensure_unique();
2988 device.reopen(false);
2989 let fixture = blob_testing::open_blob_fixture(device).await;
2990 {
2991 for hash in &hashes {
2994 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
2995 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
2996 }
2997
2998 let volume = fixture.volume().volume();
2999
3000 volume
3001 .record_and_replay_profile(new_profile_state(true), "foo")
3002 .await
3003 .expect("Replaying");
3004
3005 async {
3007 let hash = &hashes[0];
3008 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3009 while blob.vmo().info().unwrap().populated_bytes == 0 {
3010 fasync::Timer::new(Duration::from_millis(25)).await;
3011 }
3012 }
3013 .on_timeout(std::time::Duration::from_secs(120), || {
3014 panic!("Replay did not page in for all VMOs")
3015 })
3016 .await;
3017
3018 let original_recorded = RECORDED.load(Ordering::Relaxed);
3019
3020 {
3022 let mut writable = [0u8];
3023 let hash = &hashes[1];
3024 let vmo = fixture.get_blob_vmo(*hash).await;
3025 vmo.read(&mut writable, 0).expect("Vmo read");
3026 }
3027
3028 while RECORDED.load(Ordering::Relaxed) == original_recorded {
3031 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3032 }
3033
3034 volume.stop_profile_tasks().await;
3036 }
3037 let device = fixture.close().await;
3038
3039 device.ensure_unique();
3040 device.reopen(false);
3041 let fixture = blob_testing::open_blob_fixture(device).await;
3042 {
3043 for hash in &hashes {
3046 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3047 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
3048 }
3049
3050 fixture
3051 .volume()
3052 .volume()
3053 .record_and_replay_profile(new_profile_state(true), "foo")
3054 .await
3055 .expect("Replaying");
3056
3057 async {
3059 let hash = &hashes[1];
3060 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3061 while blob.vmo().info().unwrap().populated_bytes == 0 {
3062 fasync::Timer::new(Duration::from_millis(25)).await;
3063 }
3064 }
3065 .on_timeout(std::time::Duration::from_secs(30), || {
3066 panic!("Replay did not page in for all VMOs")
3067 })
3068 .await;
3069
3070 fixture.volume().volume().stop_profile_tasks().await;
3072
3073 {
3075 let hash = &hashes[0];
3076 let blob = fixture.get_blob(*hash).await.expect("Opening blob");
3077 assert_eq!(blob.vmo().info().unwrap().populated_bytes, 0);
3078 }
3079 }
3080 fixture.close().await;
3081 }
3082
3083 #[fuchsia::test(threads = 10)]
3084 async fn test_unencrypted_volume() {
3085 let fixture = TestFixture::new_unencrypted().await;
3086 let root = fixture.root();
3087
3088 let f = open_file_checked(
3089 &root,
3090 "foo",
3091 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3092 &Default::default(),
3093 )
3094 .await;
3095 close_file_checked(f).await;
3096
3097 fixture.close().await;
3098 }
3099
3100 #[fuchsia::test]
3101 async fn test_read_only_unencrypted_volume() {
3102 let fs = {
3104 let device = fxfs::filesystem::mkfs_with_volume(
3105 DeviceHolder::new(FakeDevice::new(8192, 512)),
3106 "vol",
3107 None,
3108 )
3109 .await
3110 .unwrap();
3111 device.reopen(true);
3113 FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3114 };
3115 {
3117 let root_volume = root_volume(fs.clone()).await.unwrap();
3118 let store = root_volume.volume("vol", StoreOptions::default()).await.unwrap();
3119 let unique_id = store.store_object_id();
3120 let blob_resupplied_count =
3121 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
3122 let volume = FxVolume::new(
3123 Weak::new(),
3124 store,
3125 unique_id,
3126 "vol".to_owned(),
3127 blob_resupplied_count,
3128 MemoryPressureConfig::default(),
3129 )
3130 .unwrap();
3131 volume.terminate().await;
3132 }
3133 fs.close().await.unwrap();
3135 let device = fs.take_device().await;
3136 device.ensure_unique();
3137 }
3138
3139 #[fuchsia::test]
3140 async fn test_read_only_encrypted_volume() {
3141 let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
3142 let fs = {
3144 let device = fxfs::filesystem::mkfs_with_volume(
3145 DeviceHolder::new(FakeDevice::new(8192, 512)),
3146 "vol",
3147 Some(crypt.clone()),
3148 )
3149 .await
3150 .unwrap();
3151 device.reopen(true);
3153 FxFilesystemBuilder::new().read_only(true).open(device).await.unwrap()
3154 };
3155 {
3157 let root_volume = root_volume(fs.clone()).await.unwrap();
3158 let store = root_volume
3159 .volume("vol", StoreOptions { crypt: Some(crypt), ..StoreOptions::default() })
3160 .await
3161 .unwrap();
3162 let unique_id = store.store_object_id();
3163 let blob_resupplied_count =
3164 Arc::new(PageRefaultCounter::new().expect("Failed to create PageRefaultCounter"));
3165 let volume = FxVolume::new(
3166 Weak::new(),
3167 store,
3168 unique_id,
3169 "vol".to_owned(),
3170 blob_resupplied_count,
3171 MemoryPressureConfig::default(),
3172 )
3173 .unwrap();
3174 volume.terminate().await;
3175 }
3176 fs.close().await.unwrap();
3178 let device = fs.take_device().await;
3179 device.ensure_unique();
3180 }
3181}