1use crate::fuchsia::directory::FxDirectory;
6use crate::fuchsia::errors::map_to_status;
7use crate::fuchsia::node::{FxNode, OpenedNode};
8use crate::fuchsia::paged_object_handle::{BACKGROUND_FLUSH_THRESHOLD, PagedObjectHandle};
9use crate::fuchsia::pager::{
10 MarkDirtyRange, PageInRange, PagerBacked, PagerPacketReceiverRegistration, default_page_in,
11};
12use crate::fuchsia::volume::{FxVolume, READ_AHEAD_SIZE};
13use anyhow::Error;
14use fidl_fuchsia_io as fio;
15use fxfs::filesystem::{MAX_FILE_SIZE, SyncOptions};
16use fxfs::future_with_guard::FutureWithGuard;
17use fxfs::log::*;
18use fxfs::object_handle::{ObjectHandle, ReadObjectHandle};
19use fxfs::object_store::data_object_handle::OverwriteOptions;
20use fxfs::object_store::object_record::EncryptionKey;
21use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
22use fxfs::object_store::{DataObjectHandle, FSCRYPT_KEY_ID, ObjectDescriptor};
23use fxfs_crypto::WrappingKeyId;
24use fxfs_macros::ToWeakNode;
25use fxfs_trace::{TraceFutureExt, trace_future_args};
26use std::fmt::{Debug, Formatter};
27use std::ops::Range;
28use std::sync::Arc;
29use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
30use storage_device::buffer;
31use storage_units::BlockSize;
32use vfs::directory::entry::{EntryInfo, GetEntryInfo};
33use vfs::directory::entry_container::MutableDirectory;
34use vfs::execution_scope::ExecutionScope;
35use vfs::file::{File, FileOptions, GetVmo, StreamIoConnection, SyncMode};
36use vfs::name::Name;
37use vfs::{ObjectRequestRef, ProtocolsExt, attributes};
38use zx::Status;
39
40const TO_BE_PURGED: u64 = 1 << (u64::BITS - 1);
49
50const IS_TEMPORARILY_IN_GRAVEYARD: u64 = 1 << (u64::BITS - 2);
59
60const IS_DIRTY: u64 = 1 << (u64::BITS - 3);
63
64const IS_UNNAMED_TEMPORARY: u64 = IS_TEMPORARILY_IN_GRAVEYARD | TO_BE_PURGED;
67
68const MAX_OPEN_COUNTS: u64 = IS_DIRTY - 1;
71
72#[derive(Clone, Copy)]
73struct State(u64);
74
75impl Debug for State {
76 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
77 f.debug_struct("State")
78 .field("open_count", &self.open_count())
79 .field("to_be_purged", &self.to_be_purged())
80 .field("is_temporarily_in_graveyard", &self.is_temporarily_in_graveyard())
81 .field("is_dirty", &self.is_dirty())
82 .finish()
83 }
84}
85
86impl State {
87 fn open_count(&self) -> u64 {
88 self.0 & MAX_OPEN_COUNTS
89 }
90
91 fn to_be_purged(&self) -> bool {
92 self.0 & TO_BE_PURGED != 0
93 }
94
95 fn is_temporarily_in_graveyard(&self) -> bool {
96 self.0 & IS_TEMPORARILY_IN_GRAVEYARD != 0
97 }
98
99 fn is_unnamed_temporary(&self) -> bool {
100 self.0 & IS_UNNAMED_TEMPORARY == IS_UNNAMED_TEMPORARY
101 }
102
103 fn will_be_tombstoned(&self) -> bool {
104 self.to_be_purged() && self.open_count() == 0
105 }
106
107 fn is_dirty(&self) -> bool {
108 self.0 & IS_DIRTY != 0
109 }
110}
111
112#[derive(Clone, Copy, Debug, Default, PartialEq)]
114pub enum FlushType {
115 #[default]
118 Sync,
119
120 LastChance,
123
124 Background,
127}
128
129#[derive(ToWeakNode)]
131pub struct FxFile {
132 handle: PagedObjectHandle,
133 state: AtomicU64,
134 pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
135 background_flush_running: AtomicBool,
136}
137
138#[fxfs_trace::trace]
139impl FxFile {
140 pub fn new(handle: DataObjectHandle<FxVolume>) -> Arc<Self> {
142 let size = handle.get_size();
143 Arc::new_cyclic(|weak| {
144 let (vmo, pager_packet_receiver_registration) = handle
145 .owner()
146 .pager()
147 .create_vmo(
148 weak.clone(),
149 size,
150 zx::VmoOptions::UNBOUNDED | zx::VmoOptions::TRAP_DIRTY,
151 )
152 .unwrap();
153 vmo.set_name(&zx::Name::new("fxfs-file").unwrap()).unwrap();
154 Self {
155 handle: PagedObjectHandle::new(handle, vmo),
156 state: AtomicU64::new(0),
157 pager_packet_receiver_registration,
158 background_flush_running: AtomicBool::new(false),
159 }
160 })
161 }
162
163 pub async fn create_connection_async(
165 this: OpenedNode<FxFile>,
166 scope: ExecutionScope,
167 flags: impl ProtocolsExt,
168 object_request: ObjectRequestRef<'_>,
169 ) -> Result<(), zx::Status> {
170 {
171 let mut guard = this.pager().recorder();
172 if let Some(recorder) = &mut (*guard) {
173 let _ = recorder.record_open(this.clone() as Arc<dyn FxNode>);
174 }
175 }
176 if let Some(rights) = flags.rights() {
177 if rights.intersects(fio::Operations::READ_BYTES | fio::Operations::WRITE_BYTES) {
178 if let Some(fut) = this.handle.pre_fetch_keys() {
179 let fs = this.handle.owner().store().filesystem();
181 let read_lock = fs
182 .clone()
183 .lock_manager()
184 .read_lock(lock_keys!(LockKey::object(
185 this.handle.owner().store().store_object_id(),
186 this.object_id()
187 )))
188 .await
189 .into_owned(fs);
190 this.handle.owner().scope().spawn(
191 FutureWithGuard::new(read_lock, fut)
192 .trace(trace_future_args!("FxFile::pre_fetch_keys")),
193 );
194 }
195 }
196 }
197 object_request
198 .create_connection::<StreamIoConnection<_>, _>(scope, this.take(), flags)
199 .await
200 }
201
202 pub fn open_as_temporary(self: Arc<Self>) -> OpenedNode<dyn FxNode> {
205 assert_eq!(self.state.swap(1 | IS_UNNAMED_TEMPORARY, Ordering::Relaxed), 0);
206 OpenedNode(self)
207 }
208
209 pub fn mark_as_permanent(&self) {
211 assert!(
212 State(self.state.fetch_and(!IS_UNNAMED_TEMPORARY, Ordering::Relaxed))
213 .is_unnamed_temporary()
214 );
215 }
216
217 pub fn is_verified_file(&self) -> bool {
218 self.handle.uncached_handle().is_verified_file()
219 }
220
221 pub fn handle(&self) -> &PagedObjectHandle {
222 &self.handle
223 }
224
225 pub fn into_opened_node(self: Arc<Self>) -> Option<OpenedNode<FxFile>> {
228 self.increment_open_count().then(|| OpenedNode(self))
229 }
230
231 #[trace]
237 pub async fn flush(this: &OpenedNode<FxFile>, flush_type: FlushType) -> Result<(), Error> {
238 this.handle.flush(flush_type).await.map(|_| ())
239 }
240
241 pub fn get_block_size(&self) -> BlockSize {
242 self.handle.block_size()
243 }
244
245 pub async fn is_allocated(&self, start_offset: u64) -> Result<(bool, u64), Status> {
246 self.handle.uncached_handle().is_allocated(start_offset).await.map_err(map_to_status)
247 }
248
249 pub async fn write_at_uncached(&self, offset: u64, content: &[u8]) -> Result<u64, Status> {
258 let mut buf = self.handle.uncached_handle().allocate_buffer(content.len()).await;
259 buf.copy_from_slice(content);
260 let _ = self
261 .handle
262 .uncached_handle()
263 .overwrite(
264 offset,
265 buf.as_mut(),
266 OverwriteOptions { allow_allocations: true, ..Default::default() },
267 )
268 .await
269 .map_err(map_to_status)?;
270 Ok(content.len() as u64)
271 }
272
273 pub async fn read_at_uncached(
282 &self,
283 offset: u64,
284 length: usize,
285 ) -> Result<buffer::Buffer<'_>, Status> {
286 let block_size = self.get_block_size();
287 if !block_size.is_aligned(offset) || !block_size.is_aligned(length as u64) {
288 return Err(Status::INVALID_ARGS);
289 }
290 let end = offset.checked_add(length as u64).ok_or(Status::INVALID_ARGS)?;
291 self.handle.read_uncached(offset..end).await.map_err(map_to_status)
292 }
293
294 pub fn get_size_uncached(&self) -> u64 {
295 self.handle.uncached_handle().get_size()
296 }
297
298 async fn fscrypt_wrapping_key_id(&self) -> Result<Option<WrappingKeyId>, zx::Status> {
299 if self.handle.store().is_encrypted() {
300 if let Some(key) = self
301 .handle
302 .store()
303 .get_keys(self.object_id())
304 .await
305 .map_err(map_to_status)?
306 .get(FSCRYPT_KEY_ID)
307 {
308 match key {
309 EncryptionKey::Fxfs(fxfs_key) => {
310 return Ok(Some(fxfs_key.wrapping_key_id));
311 }
312 EncryptionKey::FscryptInoLblk32File { key_identifier } => {
313 return Ok(Some(*key_identifier));
314 }
315 EncryptionKey::FscryptInoLblk32Dir { .. } => {
316 error!("Unexpected key type for file: {:?}", key);
317 return Ok(None);
318 }
319 EncryptionKey::LegacyFxfs(_) => unreachable!(),
320 }
321 }
322 }
323 Ok(None)
324 }
325
326 pub fn force_clean(&self) {
328 let old = State(self.state.fetch_and(!IS_DIRTY, Ordering::Relaxed));
329 if old.is_dirty() {
330 if self.handle.needs_flush() {
331 warn!("File {} was forcibly marked clean; data may be lost", self.object_id(),);
332 self.handle.forget_dirty_pages();
333 }
334 unsafe {
336 let _ = Arc::from_raw(self);
337 }
338 }
339 }
340
341 #[must_use]
343 fn increment_open_count(&self) -> bool {
344 let mut old = self.load_state();
345 loop {
346 if old.will_be_tombstoned() {
347 return false;
348 }
349
350 assert!(old.open_count() < MAX_OPEN_COUNTS);
351
352 match self.state.compare_exchange_weak(
353 old.0,
354 old.0 + 1,
355 Ordering::Relaxed,
356 Ordering::Relaxed,
357 ) {
358 Ok(_) => return true,
359 Err(new_value) => old.0 = new_value,
360 }
361 }
362 }
363
364 fn load_state(&self) -> State {
365 State(self.state.load(Ordering::Relaxed))
366 }
367
368 fn update_state(self: &Arc<Self>, callback: impl Fn(State) -> State) {
371 let mut old = self.load_state();
372 loop {
373 let mut new = callback(old);
374 if new.will_be_tombstoned() {
375 new.0 &= !IS_DIRTY;
378 }
379 match self.state.compare_exchange_weak(
380 old.0,
381 new.0,
382 Ordering::Relaxed,
383 Ordering::Relaxed,
384 ) {
385 Ok(_) => {
386 if !old.is_dirty() && new.is_dirty() {
387 let _ = Arc::into_raw(self.clone());
394 } else if old.is_dirty() && !new.is_dirty() {
395 unsafe {
397 let _ = Arc::from_raw(Arc::as_ptr(&self));
398 }
399 }
400 if new.will_be_tombstoned() {
401 self.handle.forget_dirty_pages();
406 let store = self.handle.store();
407 store
408 .filesystem()
409 .graveyard()
410 .queue_tombstone_object(store.store_object_id(), self.object_id());
411 }
412 return;
413 }
414 Err(v) => old.0 = v,
415 }
416 }
417 }
418}
419
420impl Drop for FxFile {
421 fn drop(&mut self) {
422 let volume = self.handle.owner();
423 volume.cache().remove(self);
424 }
425}
426
427impl FxNode for FxFile {
428 fn object_id(&self) -> u64 {
429 self.handle.object_id()
430 }
431
432 fn parent(&self) -> Option<Arc<FxDirectory>> {
433 unreachable!(); }
435
436 fn set_parent(&self, _parent: Arc<FxDirectory>) {
437 }
439
440 fn open_count_add_one(&self) {
441 assert!(self.increment_open_count());
442 }
443
444 fn open_count_sub_one(self: Arc<Self>) {
445 self.update_state(|old| {
446 let mut new = State(old.0 - 1);
447
448 if new.open_count() == 0 && !new.to_be_purged() {
451 if self.handle.needs_flush() {
452 new.0 |= IS_DIRTY;
453 } else {
454 new.0 &= !IS_DIRTY;
455 }
456 }
457
458 new
459 });
460 }
461
462 fn object_descriptor(&self) -> ObjectDescriptor {
463 ObjectDescriptor::File
464 }
465
466 fn terminate(&self) {
467 self.pager_packet_receiver_registration.stop_watching_for_zero_children();
468 }
469
470 fn mark_to_be_purged(self: Arc<Self>) {
471 self.update_state(|old| State(old.0 | TO_BE_PURGED));
472 }
473}
474
475impl GetEntryInfo for FxFile {
476 fn entry_info(&self) -> EntryInfo {
477 EntryInfo::new(self.object_id(), fio::DirentType::File)
478 }
479}
480
481impl vfs::node::Node for FxFile {
482 async fn get_attributes(
483 &self,
484 requested_attributes: fio::NodeAttributesQuery,
485 ) -> Result<fio::NodeAttributes2, zx::Status> {
486 let needs_props = requested_attributes.intersects(
487 !(fio::NodeAttributesQuery::PROTOCOLS
488 | fio::NodeAttributesQuery::ABILITIES
489 | fio::NodeAttributesQuery::ID),
490 );
491 let mut props = if needs_props {
492 Some(self.handle.get_properties().await.map_err(map_to_status)?)
493 } else {
494 None
495 };
496
497 let to_be_purged = self.load_state().to_be_purged();
503 let link_count =
504 props.as_ref().map(|p| if to_be_purged && p.refs == 1 { 0 } else { p.refs });
505
506 if requested_attributes.contains(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE) {
507 self.handle
508 .store()
509 .update_access_time(self.handle.object_id(), props.as_mut().unwrap(), || true)
510 .await
511 .map_err(map_to_status)?;
512 }
513
514 let (verification_options, root_hash) = if requested_attributes.intersects(
515 fio::NodeAttributesQuery::OPTIONS.union(fio::NodeAttributesQuery::ROOT_HASH),
516 ) {
517 self.handle.uncached_handle().get_descriptor().unzip()
518 } else {
519 (None, None)
520 };
521
522 let mut abilities = fio::Operations::GET_ATTRIBUTES
523 | fio::Operations::UPDATE_ATTRIBUTES
524 | fio::Operations::READ_BYTES;
525 if !self.is_verified_file() && !self.handle.is_read_only() {
526 abilities |= fio::Operations::WRITE_BYTES;
527 }
528
529 Ok(attributes!(
530 requested_attributes,
531 Mutable {
532 creation_time: props.as_ref().map(|p| p.creation_time.as_nanos()),
533 modification_time: props.as_ref().map(|p| p.modification_time.as_nanos()),
534 access_time: props.as_ref().map(|p| p.access_time.as_nanos()),
535 mode: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.mode)),
536 uid: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.uid)),
537 gid: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.gid)),
538 rdev: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.rdev)),
539 selinux_context: self
540 .handle
541 .uncached_handle()
542 .get_inline_selinux_context()
543 .await
544 .map_err(map_to_status)?,
545 wrapping_key_id: self.fscrypt_wrapping_key_id().await?,
546 },
547 Immutable {
548 protocols: fio::NodeProtocolKinds::FILE,
549 abilities: abilities,
550 content_size: self.handle.get_size(),
551 storage_size: props.as_ref().map(|p| p.allocated_size),
552 link_count: link_count,
553 id: self.handle.object_id(),
554 change_time: props.as_ref().map(|p| p.change_time.as_nanos()),
555 options: verification_options,
556 root_hash: root_hash,
557 verity_enabled: self.is_verified_file(),
558 }
559 ))
560 }
561
562 fn will_clone(&self) {
563 self.open_count_add_one();
564 }
565
566 fn close(self: Arc<Self>) {
567 self.open_count_sub_one();
568 }
569
570 async fn link_into(
571 self: Arc<Self>,
572 destination_dir: Arc<dyn MutableDirectory>,
573 name: Name,
574 ) -> Result<(), zx::Status> {
575 let dir = destination_dir.into_any().downcast::<FxDirectory>().unwrap();
576 let store = self.handle.store();
577 let object_id = self.object_id();
578 let transaction = store
579 .new_transaction(
580 lock_keys![
581 LockKey::object(store.store_object_id(), object_id),
582 LockKey::object(store.store_object_id(), dir.object_id()),
583 ],
584 Options::default(),
585 )
586 .await
587 .map_err(map_to_status)?;
588
589 dir.check_fscrypt_policy_equivalence(self.fscrypt_wrapping_key_id().await?)?;
590
591 let state = self.load_state();
592 let is_unnamed_temporary = state.is_unnamed_temporary();
593 let to_be_purged = state.to_be_purged();
594 if is_unnamed_temporary {
595 dir.link_graveyard_object(transaction, &name, object_id, ObjectDescriptor::File, || {
597 self.mark_as_permanent()
598 })
599 .await
600 } else {
601 if to_be_purged {
603 return Err(zx::Status::NOT_FOUND);
604 }
605 dir.link_object(transaction, &name, object_id, ObjectDescriptor::File).await
606 }
607 }
608
609 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
610 Ok(self.handle.owner().filesystem_info_for_volume())
611 }
612
613 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, Status> {
614 self.handle.store_handle().list_extended_attributes().await.map_err(map_to_status)
615 }
616
617 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, Status> {
618 self.handle.store_handle().get_extended_attribute(name).await.map_err(map_to_status)
619 }
620
621 async fn set_extended_attribute(
622 &self,
623 name: Vec<u8>,
624 value: Vec<u8>,
625 mode: fio::SetExtendedAttributeMode,
626 ) -> Result<(), Status> {
627 self.handle
628 .store_handle()
629 .set_extended_attribute(name, value, mode.into())
630 .await
631 .map_err(map_to_status)
632 }
633
634 async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
635 self.handle.store_handle().remove_extended_attribute(name).await.map_err(map_to_status)
636 }
637}
638
639impl File for FxFile {
640 fn writable(&self) -> bool {
641 true
642 }
643
644 async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
645 Ok(())
646 }
647
648 async fn truncate(&self, length: u64) -> Result<(), Status> {
649 self.handle.truncate(length).await.map_err(map_to_status)?;
650 Ok(())
651 }
652
653 async fn enable_verity(&self, options: fio::VerificationOptions) -> Result<(), Status> {
654 self.handle.set_read_only();
659 self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
660 self.handle.uncached_handle().enable_verity(options).await.map_err(map_to_status)
661 }
662
663 async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
665 if flags.contains(fio::VmoFlags::EXECUTE) {
667 error!("get_backing_memory does not support execute rights!");
668 return Err(Status::NOT_SUPPORTED);
669 }
670
671 if flags.contains(fio::VmoFlags::WRITE)
676 && !flags.contains(fio::VmoFlags::PRIVATE_CLONE)
677 && (self.is_verified_file() || self.handle.is_read_only())
678 {
679 return Err(Status::ACCESS_DENIED);
680 }
681
682 let vmo = self.handle.vmo();
683 let mut rights = zx::Rights::BASIC | zx::Rights::MAP | zx::Rights::GET_PROPERTY;
684 if flags.contains(fio::VmoFlags::READ) {
685 rights |= zx::Rights::READ;
686 }
687 if flags.contains(fio::VmoFlags::WRITE) {
688 rights |= zx::Rights::WRITE;
689 }
690
691 let child_vmo = if flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
692 rights |= zx::Rights::SET_PROPERTY;
694 let mut child_options = zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE;
695 if flags.contains(fio::VmoFlags::WRITE) {
696 child_options |= zx::VmoChildOptions::RESIZABLE;
697 rights |= zx::Rights::RESIZE;
698 }
699 vmo.create_child(child_options, 0, vmo.get_stream_size()?)?
700 } else {
701 vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0)?
702 };
703
704 let child_vmo = child_vmo.replace_handle(rights)?;
705 if self.handle.owner().pager().watch_for_zero_children(self).map_err(map_to_status)? {
706 self.open_count_add_one();
708 }
709 Ok(child_vmo)
710 }
711
712 async fn get_size(&self) -> Result<u64, Status> {
713 Ok(self.handle.get_size())
714 }
715
716 async fn update_attributes(
717 &self,
718 attributes: fio::MutableNodeAttributes,
719 ) -> Result<(), Status> {
720 if attributes == fio::MutableNodeAttributes::default() {
721 return Ok(());
722 }
723
724 self.handle.update_attributes(&attributes).await.map_err(map_to_status)?;
725 Ok(())
726 }
727
728 async fn allocate(
729 &self,
730 offset: u64,
731 length: u64,
732 _mode: fio::AllocateMode,
733 ) -> Result<(), Status> {
734 let range = offset..offset.checked_add(length).ok_or(Status::FILE_BIG)?;
737 self.handle.allocate(range).await.map_err(map_to_status)
738 }
739
740 async fn sync(&self, mode: SyncMode) -> Result<(), Status> {
741 self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
742
743 if mode == SyncMode::Normal {
746 self.handle
747 .store()
748 .filesystem()
749 .sync(SyncOptions::default())
750 .await
751 .map_err(map_to_status)?;
752 }
753
754 Ok(())
755 }
756}
757
758#[fxfs_trace::trace]
759impl PagerBacked for FxFile {
760 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
761 let mut old = self.load_state();
762 loop {
763 if old.open_count() == 0 {
764 return Err(self);
765 }
766
767 assert!(old.open_count() < MAX_OPEN_COUNTS);
768
769 match self.state.compare_exchange_weak(
770 old.0,
771 old.0 + 1,
772 Ordering::Relaxed,
773 Ordering::Relaxed,
774 ) {
775 Ok(_) => return Ok(OpenedNode(self)),
776 Err(new_value) => old.0 = new_value,
777 }
778 }
779 }
780
781 fn pager(&self) -> &crate::pager::Pager {
782 self.handle.owner().pager()
783 }
784
785 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
786 &self.pager_packet_receiver_registration
787 }
788
789 fn vmo(&self) -> &zx::Vmo {
790 self.handle.vmo()
791 }
792
793 fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
794 default_page_in(self, range, READ_AHEAD_SIZE);
795 }
796
797 #[trace]
798 fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
799 let (valid_pages, invalid_pages) = range.split(MAX_FILE_SIZE);
800 if let Some(invalid_pages) = invalid_pages {
801 invalid_pages.report_failure(zx::Status::FILE_BIG);
802 }
803 let range = match valid_pages {
804 Some(range) => range,
805 None => return,
806 };
807
808 let byte_count = range.len();
809 self.handle.owner().clone().report_pager_dirty(byte_count, move || {
810 match self.handle.mark_dirty(range) {
811 Ok(dirty_bytes) => {
812 if dirty_bytes > BACKGROUND_FLUSH_THRESHOLD
815 && !self.background_flush_running.swap(true, Ordering::Relaxed)
816 {
817 let owner = self.handle.owner().clone();
818 owner.spawn(async move {
819 let _ = self.handle.flush(FlushType::Background).await;
821 self.background_flush_running.store(false, Ordering::Relaxed);
824 });
825 }
826 }
827 Err(_) => {
828 self.handle.owner().report_pager_clean(byte_count)
830 }
831 }
832 });
833 }
834
835 fn on_zero_children(self: Arc<Self>) {
836 self.open_count_sub_one();
838 }
839
840 fn byte_size(&self) -> u64 {
841 self.handle.uncached_size()
842 }
843
844 #[trace("len" => (range.end - range.start))]
845 async fn aligned_read(&self, range: Range<u64>) -> Result<buffer::Buffer<'_>, Error> {
846 let buffer = self.handle.read_uncached(range).await?;
847 Ok(buffer)
848 }
849}
850
851impl GetVmo for FxFile {
852 const PAGER_ON_FIDL_EXECUTOR: bool = true;
853
854 fn get_vmo(&self) -> &zx::Vmo {
855 self.vmo()
856 }
857}
858
859#[cfg(test)]
860mod tests {
861 use super::FxFile;
862 use crate::fuchsia::paged_object_handle::BACKGROUND_FLUSH_THRESHOLD;
863 use crate::fuchsia::testing::{
864 TestFixture, TestFixtureOptions, close_file_checked, open_dir_checked, open_file,
865 open_file_checked,
866 };
867 use anyhow::format_err;
868 use fidl_fuchsia_io as fio;
869 use fsverity_merkle::{FsVerityHasher, FsVerityHasherOptions};
870 use fuchsia_async::{self as fasync, unblock};
871 use fuchsia_fs::file;
872 use futures::join;
873 use fxfs::fsck::fsck;
874 use fxfs::object_handle::INVALID_OBJECT_ID;
875 use fxfs::object_store::Timestamp;
876 use fxfs_crypto::WrappingKeyId;
877 use rand::{RngExt as _, rng};
878 use std::sync::Arc;
879 use std::sync::atomic::{self, AtomicBool};
880 use std::time::Duration;
881 use storage_device::DeviceHolder;
882 use storage_device::fake_device::FakeDevice;
883 use storage_units::page_size;
884 use zx::Status;
885
886 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
887
888 #[fuchsia::test(threads = 10)]
889 async fn test_empty_file() {
890 let fixture = TestFixture::new().await;
891 let root = fixture.root();
892
893 let file = open_file_checked(
894 &root,
895 "foo",
896 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
897 &Default::default(),
898 )
899 .await;
900
901 let buf = file
902 .read(fio::MAX_BUF)
903 .await
904 .expect("FIDL call failed")
905 .map_err(Status::err_from_raw)
906 .expect("read failed");
907 assert!(buf.is_empty());
908
909 let (mutable_attrs, immutable_attrs) = file
910 .get_attributes(fio::NodeAttributesQuery::all())
911 .await
912 .expect("FIDL call failed")
913 .expect("GetAttributes failed");
914 assert_ne!(immutable_attrs.id.unwrap(), INVALID_OBJECT_ID);
915 assert_eq!(immutable_attrs.content_size.unwrap(), 0u64);
916 assert_eq!(immutable_attrs.storage_size.unwrap(), 0u64);
917 assert_eq!(immutable_attrs.link_count.unwrap(), 1u64);
918 assert_ne!(mutable_attrs.creation_time.unwrap(), 0u64);
919 assert_ne!(mutable_attrs.modification_time.unwrap(), 0u64);
920 assert_eq!(mutable_attrs.creation_time.unwrap(), mutable_attrs.modification_time.unwrap());
921
922 close_file_checked(file).await;
923 fixture.close().await;
924 }
925
926 #[fuchsia::test(threads = 10)]
927 async fn test_write_read() {
928 let fixture = TestFixture::new().await;
929 let root = fixture.root();
930
931 let file = open_file_checked(
932 &root,
933 "foo",
934 fio::Flags::FLAG_MAYBE_CREATE
935 | fio::PERM_READABLE
936 | fio::PERM_WRITABLE
937 | fio::Flags::PROTOCOL_FILE,
938 &Default::default(),
939 )
940 .await;
941
942 let inputs = vec!["hello, ", "world!"];
943 let expected_output = "hello, world!";
944 for input in inputs {
945 let bytes_written = file
946 .write(input.as_bytes())
947 .await
948 .expect("write failed")
949 .map_err(Status::err_from_raw)
950 .expect("File write was successful");
951 assert_eq!(bytes_written as usize, input.as_bytes().len());
952 }
953
954 let buf = file
955 .read_at(fio::MAX_BUF, 0)
956 .await
957 .expect("read_at failed")
958 .map_err(Status::err_from_raw)
959 .expect("File read was successful");
960 assert_eq!(buf.len(), expected_output.as_bytes().len());
961 assert!(buf.iter().eq(expected_output.as_bytes().iter()));
962
963 let (_, immutable_attributes) = file
964 .get_attributes(
965 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
966 )
967 .await
968 .expect("FIDL call failed")
969 .expect("get_attributes failed");
970
971 assert_eq!(
972 immutable_attributes.content_size.unwrap(),
973 expected_output.as_bytes().len() as u64
974 );
975 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size());
976
977 let () = file
978 .sync()
979 .await
980 .expect("FIDL call failed")
981 .map_err(Status::err_from_raw)
982 .expect("sync failed");
983
984 let (_, immutable_attributes) = file
985 .get_attributes(
986 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
987 )
988 .await
989 .expect("FIDL call failed")
990 .expect("get_attributes failed");
991
992 assert_eq!(
993 immutable_attributes.content_size.unwrap(),
994 expected_output.as_bytes().len() as u64
995 );
996 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size());
997
998 close_file_checked(file).await;
999 fixture.close().await;
1000 }
1001
1002 #[fuchsia::test(threads = 10)]
1003 async fn test_page_in() {
1004 let input = "hello, world!";
1005 let reused_device = {
1006 let fixture = TestFixture::new().await;
1007 let root = fixture.root();
1008
1009 let file = open_file_checked(
1010 &root,
1011 "foo",
1012 fio::Flags::FLAG_MAYBE_CREATE
1013 | fio::PERM_READABLE
1014 | fio::PERM_WRITABLE
1015 | fio::Flags::PROTOCOL_FILE,
1016 &Default::default(),
1017 )
1018 .await;
1019
1020 let bytes_written = file
1021 .write(input.as_bytes())
1022 .await
1023 .expect("write failed")
1024 .map_err(Status::err_from_raw)
1025 .expect("File write was successful");
1026 assert_eq!(bytes_written as usize, input.as_bytes().len());
1027 assert!(file.sync().await.expect("Sync failed").is_ok());
1028
1029 close_file_checked(file).await;
1030 fixture.close().await
1031 };
1032
1033 let fixture = TestFixture::open(
1034 reused_device,
1035 TestFixtureOptions { format: false, ..Default::default() },
1036 )
1037 .await;
1038 let root = fixture.root();
1039
1040 let file = open_file_checked(
1041 &root,
1042 "foo",
1043 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1044 &Default::default(),
1045 )
1046 .await;
1047
1048 let vmo =
1049 file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1050 let mut readback = vec![0; input.as_bytes().len()];
1051 assert!(vmo.read(&mut readback, 0).is_ok());
1052 assert_eq!(input.as_bytes(), readback);
1053
1054 close_file_checked(file).await;
1055 fixture.close().await;
1056 }
1057
1058 #[fuchsia::test(threads = 10)]
1059 async fn test_page_in_io_error() {
1060 let mut device = FakeDevice::new(8192, 512);
1061 let succeed_requests = Arc::new(AtomicBool::new(true));
1062 let succeed_requests_clone = succeed_requests.clone();
1063 device.set_op_callback(Box::new(move |_| {
1064 if succeed_requests_clone.load(atomic::Ordering::Relaxed) {
1065 Ok(())
1066 } else {
1067 Err(format_err!("Fake error."))
1068 }
1069 }));
1070
1071 let input = "hello, world!";
1072 let reused_device = {
1073 let fixture = TestFixture::open(
1074 DeviceHolder::new(device),
1075 TestFixtureOptions { format: true, ..Default::default() },
1076 )
1077 .await;
1078 let root = fixture.root();
1079
1080 let file = open_file_checked(
1081 &root,
1082 "foo",
1083 fio::Flags::FLAG_MAYBE_CREATE
1084 | fio::PERM_READABLE
1085 | fio::PERM_WRITABLE
1086 | fio::Flags::PROTOCOL_FILE,
1087 &Default::default(),
1088 )
1089 .await;
1090
1091 let bytes_written = file
1092 .write(input.as_bytes())
1093 .await
1094 .expect("write failed")
1095 .map_err(Status::err_from_raw)
1096 .expect("File write was successful");
1097 assert_eq!(bytes_written as usize, input.as_bytes().len());
1098
1099 close_file_checked(file).await;
1100 fixture.close().await
1101 };
1102
1103 let fixture = TestFixture::open(
1104 reused_device,
1105 TestFixtureOptions { format: false, ..Default::default() },
1106 )
1107 .await;
1108 let root = fixture.root();
1109
1110 let file = open_file_checked(
1111 &root,
1112 "foo",
1113 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1114 &Default::default(),
1115 )
1116 .await;
1117
1118 let vmo =
1119 file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1120 succeed_requests.store(false, atomic::Ordering::Relaxed);
1121 let mut readback = vec![0; input.as_bytes().len()];
1122 assert!(vmo.read(&mut readback, 0).is_err());
1123
1124 succeed_requests.store(true, atomic::Ordering::Relaxed);
1125 close_file_checked(file).await;
1126 fixture.close().await;
1127 }
1128
1129 #[fuchsia::test(threads = 10)]
1130 async fn test_writes_persist() {
1131 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1132 for i in 0..2 {
1133 let fixture = TestFixture::open(
1134 device,
1135 TestFixtureOptions { format: i == 0, ..Default::default() },
1136 )
1137 .await;
1138 let root = fixture.root();
1139
1140 let flags = if i == 0 {
1141 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE
1142 } else {
1143 fio::PERM_READABLE | fio::PERM_WRITABLE
1144 };
1145 let file = open_file_checked(
1146 &root,
1147 "foo",
1148 flags | fio::Flags::PROTOCOL_FILE,
1149 &Default::default(),
1150 )
1151 .await;
1152
1153 if i == 0 {
1154 let _: u64 = file
1155 .write(&vec![0xaa as u8; 8192])
1156 .await
1157 .expect("FIDL call failed")
1158 .map_err(Status::err_from_raw)
1159 .expect("File write was successful");
1160 } else {
1161 let buf = file
1162 .read(8192)
1163 .await
1164 .expect("FIDL call failed")
1165 .map_err(Status::err_from_raw)
1166 .expect("File read was successful");
1167 assert_eq!(buf, vec![0xaa as u8; 8192]);
1168 }
1169
1170 let (_, immutable_attributes) = file
1171 .get_attributes(
1172 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1173 )
1174 .await
1175 .expect("FIDL call failed")
1176 .expect("get_attributes failed");
1177
1178 assert_eq!(immutable_attributes.content_size.unwrap(), 8192u64);
1179 assert_eq!(immutable_attributes.storage_size.unwrap(), 8192u64);
1180
1181 close_file_checked(file).await;
1182 device = fixture.close().await;
1183 }
1184 }
1185
1186 #[fuchsia::test(threads = 10)]
1187 async fn test_append() {
1188 let fixture = TestFixture::new().await;
1189 let root = fixture.root();
1190
1191 let inputs = vec!["hello, ", "world!"];
1192 let expected_output = "hello, world!";
1193 for input in inputs {
1194 let file = open_file_checked(
1195 &root,
1196 "foo",
1197 fio::Flags::FLAG_MAYBE_CREATE
1198 | fio::PERM_READABLE
1199 | fio::PERM_WRITABLE
1200 | fio::Flags::FILE_APPEND
1201 | fio::Flags::PROTOCOL_FILE,
1202 &Default::default(),
1203 )
1204 .await;
1205
1206 let bytes_written = file
1207 .write(input.as_bytes())
1208 .await
1209 .expect("FIDL call failed")
1210 .map_err(Status::err_from_raw)
1211 .expect("File write was successful");
1212 assert_eq!(bytes_written as usize, input.as_bytes().len());
1213 close_file_checked(file).await;
1214 }
1215
1216 let file = open_file_checked(
1217 &root,
1218 "foo",
1219 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1220 &Default::default(),
1221 )
1222 .await;
1223 let buf = file
1224 .read_at(fio::MAX_BUF, 0)
1225 .await
1226 .expect("FIDL call failed")
1227 .map_err(Status::err_from_raw)
1228 .expect("File read was successful");
1229 assert_eq!(buf.len(), expected_output.as_bytes().len());
1230 assert_eq!(&buf[..], expected_output.as_bytes());
1231
1232 let (_, immutable_attributes) = file
1233 .get_attributes(
1234 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1235 )
1236 .await
1237 .expect("FIDL call failed")
1238 .expect("get_attributes failed");
1239
1240 assert_eq!(
1241 immutable_attributes.content_size.unwrap(),
1242 expected_output.as_bytes().len() as u64
1243 );
1244 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size());
1245
1246 close_file_checked(file).await;
1247 fixture.close().await;
1248 }
1249
1250 #[fuchsia::test(threads = 10)]
1251 async fn test_seek() {
1252 let fixture = TestFixture::new().await;
1253 let root = fixture.root();
1254
1255 let file = open_file_checked(
1256 &root,
1257 "foo",
1258 fio::Flags::FLAG_MAYBE_CREATE
1259 | fio::PERM_READABLE
1260 | fio::PERM_WRITABLE
1261 | fio::Flags::PROTOCOL_FILE,
1262 &Default::default(),
1263 )
1264 .await;
1265
1266 let input = "hello, world!";
1267 let _: u64 = file
1268 .write(input.as_bytes())
1269 .await
1270 .expect("FIDL call failed")
1271 .map_err(Status::err_from_raw)
1272 .expect("File write was successful");
1273
1274 {
1275 let offset = file
1276 .seek(fio::SeekOrigin::Start, 0)
1277 .await
1278 .expect("FIDL call failed")
1279 .map_err(Status::err_from_raw)
1280 .expect("seek was successful");
1281 assert_eq!(offset, 0);
1282 let buf = file
1283 .read(5)
1284 .await
1285 .expect("FIDL call failed")
1286 .map_err(Status::err_from_raw)
1287 .expect("File read was successful");
1288 assert!(buf.iter().eq("hello".as_bytes().iter()));
1289 }
1290 {
1291 let offset = file
1292 .seek(fio::SeekOrigin::Current, 2)
1293 .await
1294 .expect("FIDL call failed")
1295 .map_err(Status::err_from_raw)
1296 .expect("seek was successful");
1297 assert_eq!(offset, 7);
1298 let buf = file
1299 .read(5)
1300 .await
1301 .expect("FIDL call failed")
1302 .map_err(Status::err_from_raw)
1303 .expect("File read was successful");
1304 assert!(buf.iter().eq("world".as_bytes().iter()));
1305 }
1306 {
1307 let offset = file
1308 .seek(fio::SeekOrigin::Current, -5)
1309 .await
1310 .expect("FIDL call failed")
1311 .map_err(Status::err_from_raw)
1312 .expect("seek was successful");
1313 assert_eq!(offset, 7);
1314 let buf = file
1315 .read(5)
1316 .await
1317 .expect("FIDL call failed")
1318 .map_err(Status::err_from_raw)
1319 .expect("File read was successful");
1320 assert!(buf.iter().eq("world".as_bytes().iter()));
1321 }
1322 {
1323 let offset = file
1324 .seek(fio::SeekOrigin::End, -1)
1325 .await
1326 .expect("FIDL call failed")
1327 .map_err(Status::err_from_raw)
1328 .expect("seek was successful");
1329 assert_eq!(offset, 12);
1330 let buf = file
1331 .read(1)
1332 .await
1333 .expect("FIDL call failed")
1334 .map_err(Status::err_from_raw)
1335 .expect("File read was successful");
1336 assert!(buf.iter().eq("!".as_bytes().iter()));
1337 }
1338
1339 close_file_checked(file).await;
1340 fixture.close().await;
1341 }
1342
1343 #[fuchsia::test(threads = 10)]
1344 async fn test_resize_extend() {
1345 let fixture = TestFixture::new().await;
1346 let root = fixture.root();
1347
1348 let file = open_file_checked(
1349 &root,
1350 "foo",
1351 fio::Flags::FLAG_MAYBE_CREATE
1352 | fio::PERM_READABLE
1353 | fio::PERM_WRITABLE
1354 | fio::Flags::PROTOCOL_FILE,
1355 &Default::default(),
1356 )
1357 .await;
1358
1359 let input = "hello, world!";
1360 let len: usize = 16 * 1024;
1361
1362 let _: u64 = file
1363 .write(input.as_bytes())
1364 .await
1365 .expect("FIDL call failed")
1366 .map_err(Status::err_from_raw)
1367 .expect("File write was successful");
1368
1369 let offset = file
1370 .seek(fio::SeekOrigin::Start, 0)
1371 .await
1372 .expect("FIDL call failed")
1373 .map_err(Status::err_from_raw)
1374 .expect("Seek was successful");
1375 assert_eq!(offset, 0);
1376
1377 let () = file
1378 .resize(len as u64)
1379 .await
1380 .expect("resize failed")
1381 .map_err(Status::err_from_raw)
1382 .expect("resize error");
1383
1384 let mut expected_buf = vec![0 as u8; len];
1385 expected_buf[..input.as_bytes().len()].copy_from_slice(input.as_bytes());
1386
1387 let buf = file::read(&file).await.expect("File read was successful");
1388 assert_eq!(buf.len(), len);
1389 assert_eq!(buf, expected_buf);
1390
1391 expected_buf[len - 1..].copy_from_slice("a".as_bytes());
1393
1394 let _: u64 = file
1395 .write_at("a".as_bytes(), (len - 1) as u64)
1396 .await
1397 .expect("FIDL call failed")
1398 .map_err(Status::err_from_raw)
1399 .expect("File write was successful");
1400
1401 let offset = file
1402 .seek(fio::SeekOrigin::Start, 0)
1403 .await
1404 .expect("FIDL call failed")
1405 .map_err(Status::err_from_raw)
1406 .expect("Seek was successful");
1407 assert_eq!(offset, 0);
1408
1409 let buf = file::read(&file).await.expect("File read was successful");
1410 assert_eq!(buf.len(), len);
1411 assert_eq!(buf, expected_buf);
1412
1413 close_file_checked(file).await;
1414 fixture.close().await;
1415 }
1416
1417 #[fuchsia::test(threads = 10)]
1418 async fn test_resize_shrink() {
1419 let fixture = TestFixture::new().await;
1420 let root = fixture.root();
1421
1422 let file = open_file_checked(
1423 &root,
1424 "foo",
1425 fio::Flags::FLAG_MAYBE_CREATE
1426 | fio::PERM_READABLE
1427 | fio::PERM_WRITABLE
1428 | fio::Flags::PROTOCOL_FILE,
1429 &Default::default(),
1430 )
1431 .await;
1432
1433 let len: usize = 2 * 1024;
1434 let input = {
1435 let mut v = vec![0 as u8; len];
1436 for i in 0..v.len() {
1437 v[i] = ('a' as u8) + (i % 13) as u8;
1438 }
1439 v
1440 };
1441 let short_len: usize = 513;
1442
1443 file::write(&file, &input).await.expect("File write was successful");
1444
1445 let () = file
1446 .resize(short_len as u64)
1447 .await
1448 .expect("resize failed")
1449 .map_err(Status::err_from_raw)
1450 .expect("resize error");
1451
1452 let offset = file
1453 .seek(fio::SeekOrigin::Start, 0)
1454 .await
1455 .expect("FIDL call failed")
1456 .map_err(Status::err_from_raw)
1457 .expect("Seek was successful");
1458 assert_eq!(offset, 0);
1459
1460 let buf = file::read(&file).await.expect("File read was successful");
1461 assert_eq!(buf.len(), short_len);
1462 assert_eq!(buf, input[..short_len]);
1463
1464 let () = file
1466 .resize(len as u64)
1467 .await
1468 .expect("resize failed")
1469 .map_err(Status::err_from_raw)
1470 .expect("resize error");
1471
1472 let expected_buf = {
1473 let mut v = vec![0 as u8; len];
1474 v[..short_len].copy_from_slice(&input[..short_len]);
1475 v
1476 };
1477
1478 let offset = file
1479 .seek(fio::SeekOrigin::Start, 0)
1480 .await
1481 .expect("seek failed")
1482 .map_err(Status::err_from_raw)
1483 .expect("Seek was successful");
1484 assert_eq!(offset, 0);
1485
1486 let buf = file::read(&file).await.expect("File read was successful");
1487 assert_eq!(buf.len(), len);
1488 assert_eq!(buf, expected_buf);
1489
1490 close_file_checked(file).await;
1491 fixture.close().await;
1492 }
1493
1494 #[fuchsia::test(threads = 10)]
1495 async fn test_resize_shrink_repeated() {
1496 let fixture = TestFixture::new().await;
1497 let root = fixture.root();
1498
1499 let file = open_file_checked(
1500 &root,
1501 "foo",
1502 fio::Flags::FLAG_MAYBE_CREATE
1503 | fio::PERM_READABLE
1504 | fio::PERM_WRITABLE
1505 | fio::Flags::PROTOCOL_FILE,
1506 &Default::default(),
1507 )
1508 .await;
1509
1510 let orig_len: usize = 4 * 1024;
1511 let mut len = orig_len;
1512 let input = {
1513 let mut v = vec![0 as u8; len];
1514 for i in 0..v.len() {
1515 v[i] = ('a' as u8) + (i % 13) as u8;
1516 }
1517 v
1518 };
1519 let short_len: usize = 513;
1520
1521 file::write(&file, &input).await.expect("File write was successful");
1522
1523 while len > short_len {
1524 len -= std::cmp::min(len - short_len, 512);
1525 let () = file
1526 .resize(len as u64)
1527 .await
1528 .expect("resize failed")
1529 .map_err(Status::err_from_raw)
1530 .expect("resize error");
1531 }
1532
1533 let offset = file
1534 .seek(fio::SeekOrigin::Start, 0)
1535 .await
1536 .expect("Seek failed")
1537 .map_err(Status::err_from_raw)
1538 .expect("Seek was successful");
1539 assert_eq!(offset, 0);
1540
1541 let buf = file::read(&file).await.expect("File read was successful");
1542 assert_eq!(buf.len(), short_len);
1543 assert_eq!(buf, input[..short_len]);
1544
1545 let () = file
1547 .resize(orig_len as u64)
1548 .await
1549 .expect("resize failed")
1550 .map_err(Status::err_from_raw)
1551 .expect("resize error");
1552
1553 let expected_buf = {
1554 let mut v = vec![0 as u8; orig_len];
1555 v[..short_len].copy_from_slice(&input[..short_len]);
1556 v
1557 };
1558
1559 let offset = file
1560 .seek(fio::SeekOrigin::Start, 0)
1561 .await
1562 .expect("seek failed")
1563 .map_err(Status::err_from_raw)
1564 .expect("Seek was successful");
1565 assert_eq!(offset, 0);
1566
1567 let buf = file::read(&file).await.expect("File read was successful");
1568 assert_eq!(buf.len(), orig_len);
1569 assert_eq!(buf, expected_buf);
1570
1571 close_file_checked(file).await;
1572 fixture.close().await;
1573 }
1574
1575 #[fuchsia::test(threads = 10)]
1576 async fn test_unlink_with_open_race() {
1577 let fixture = Arc::new(TestFixture::new().await);
1578 let fixture1 = fixture.clone();
1579 let fixture2 = fixture.clone();
1580 let fixture3 = fixture.clone();
1581 let done = Arc::new(AtomicBool::new(false));
1582 let done1 = done.clone();
1583 let done2 = done.clone();
1584 join!(
1585 fasync::Task::spawn(async move {
1586 let root = fixture1.root();
1587 while !done1.load(atomic::Ordering::Relaxed) {
1588 let file = open_file_checked(
1589 &root,
1590 "foo",
1591 fio::Flags::FLAG_MAYBE_CREATE
1592 | fio::PERM_READABLE
1593 | fio::PERM_WRITABLE
1594 | fio::Flags::PROTOCOL_FILE,
1595 &Default::default(),
1596 )
1597 .await;
1598 let _: u64 = file
1599 .write(b"hello")
1600 .await
1601 .expect("write failed")
1602 .map_err(Status::err_from_raw)
1603 .expect("write error");
1604 }
1605 }),
1606 fasync::Task::spawn(async move {
1607 let root = fixture2.root();
1608 while !done2.load(atomic::Ordering::Relaxed) {
1609 let file = open_file_checked(
1610 &root,
1611 "foo",
1612 fio::Flags::FLAG_MAYBE_CREATE
1613 | fio::PERM_READABLE
1614 | fio::PERM_WRITABLE
1615 | fio::Flags::PROTOCOL_FILE,
1616 &Default::default(),
1617 )
1618 .await;
1619 let _: u64 = file
1620 .write(b"hello")
1621 .await
1622 .expect("write failed")
1623 .map_err(Status::err_from_raw)
1624 .expect("write error");
1625 }
1626 }),
1627 fasync::Task::spawn(async move {
1628 let root = fixture3.root();
1629 for _ in 0..300 {
1630 let file = open_file_checked(
1631 &root,
1632 "foo",
1633 fio::Flags::FLAG_MAYBE_CREATE
1634 | fio::PERM_READABLE
1635 | fio::PERM_WRITABLE
1636 | fio::Flags::PROTOCOL_FILE,
1637 &Default::default(),
1638 )
1639 .await;
1640 assert_eq!(
1641 file.close().await.expect("FIDL call failed").map_err(Status::err_from_raw),
1642 Ok(())
1643 );
1644 root.unlink("foo", &fio::UnlinkOptions::default())
1645 .await
1646 .expect("FIDL call failed")
1647 .expect("unlink failed");
1648 }
1649 done.store(true, atomic::Ordering::Relaxed);
1650 })
1651 );
1652
1653 Arc::try_unwrap(fixture).unwrap_or_else(|_| panic!()).close().await;
1654 }
1655
1656 #[fuchsia::test(threads = 10)]
1657 async fn test_get_backing_memory_shared_vmo_right_write() {
1658 let fixture = TestFixture::new().await;
1659 let root = fixture.root();
1660
1661 let file = open_file_checked(
1662 &root,
1663 "foo",
1664 fio::Flags::FLAG_MAYBE_CREATE
1665 | fio::PERM_READABLE
1666 | fio::PERM_WRITABLE
1667 | fio::Flags::PROTOCOL_FILE,
1668 &Default::default(),
1669 )
1670 .await;
1671
1672 file.resize(4096)
1673 .await
1674 .expect("resize failed")
1675 .map_err(Status::err_from_raw)
1676 .expect("resize error");
1677
1678 let vmo = file
1679 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1680 .await
1681 .expect("Failed to make FIDL call")
1682 .map_err(Status::err_from_raw)
1683 .expect("Failed to get VMO");
1684 let err = vmo.write(&[0, 1, 2, 3], 0).expect_err("VMO should not be writable");
1685 assert_eq!(Status::ACCESS_DENIED, err);
1686
1687 let vmo = file
1688 .get_backing_memory(
1689 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1690 )
1691 .await
1692 .expect("Failed to make FIDL call")
1693 .map_err(Status::err_from_raw)
1694 .expect("Failed to get VMO");
1695 vmo.write(&[0, 1, 2, 3], 0).expect("VMO should be writable");
1696
1697 close_file_checked(file).await;
1698 fixture.close().await;
1699 }
1700
1701 #[fuchsia::test(threads = 10)]
1702 async fn test_get_backing_memory_shared_vmo_right_read() {
1703 let fixture = TestFixture::new().await;
1704 let root = fixture.root();
1705
1706 let file = open_file_checked(
1707 &root,
1708 "foo",
1709 fio::Flags::FLAG_MAYBE_CREATE
1710 | fio::PERM_READABLE
1711 | fio::PERM_WRITABLE
1712 | fio::Flags::PROTOCOL_FILE,
1713 &Default::default(),
1714 )
1715 .await;
1716
1717 file.resize(4096)
1718 .await
1719 .expect("resize failed")
1720 .map_err(Status::err_from_raw)
1721 .expect("resize error");
1722
1723 let mut data = [0u8; 4];
1724 let vmo = file
1725 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER)
1726 .await
1727 .expect("Failed to make FIDL call")
1728 .map_err(Status::err_from_raw)
1729 .expect("Failed to get VMO");
1730 let err = vmo.read(&mut data, 0).expect_err("VMO should not be readable");
1731 assert_eq!(Status::ACCESS_DENIED, err);
1732
1733 let vmo = file
1734 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1735 .await
1736 .expect("Failed to make FIDL call")
1737 .map_err(Status::err_from_raw)
1738 .expect("Failed to get VMO");
1739 vmo.read(&mut data, 0).expect("VMO should be readable");
1740
1741 close_file_checked(file).await;
1742 fixture.close().await;
1743 }
1744
1745 #[fuchsia::test(threads = 10)]
1746 async fn test_get_backing_memory_shared_vmo_resize() {
1747 let fixture = TestFixture::new().await;
1748 let root = fixture.root();
1749
1750 let file = open_file_checked(
1751 &root,
1752 "foo",
1753 fio::Flags::FLAG_MAYBE_CREATE
1754 | fio::PERM_READABLE
1755 | fio::PERM_WRITABLE
1756 | fio::Flags::PROTOCOL_FILE,
1757 &Default::default(),
1758 )
1759 .await;
1760
1761 let vmo = file
1762 .get_backing_memory(
1763 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1764 )
1765 .await
1766 .expect("Failed to make FIDL call")
1767 .map_err(Status::err_from_raw)
1768 .expect("Failed to get VMO");
1769
1770 let err = vmo.set_size(4096).expect_err("VMO should not be resizable");
1772 assert_eq!(Status::UNAVAILABLE, err);
1773 let err =
1775 vmo.set_content_size(&10).expect_err("content size should not be directly modifiable");
1776 assert_eq!(Status::ACCESS_DENIED, err);
1777
1778 close_file_checked(file).await;
1779 fixture.close().await;
1780 }
1781
1782 #[fuchsia::test(threads = 10)]
1783 async fn test_get_backing_memory_private_vmo_resize() {
1784 let fixture = TestFixture::new().await;
1785 let root = fixture.root();
1786
1787 let file = open_file_checked(
1788 &root,
1789 "foo",
1790 fio::Flags::FLAG_MAYBE_CREATE
1791 | fio::PERM_READABLE
1792 | fio::PERM_WRITABLE
1793 | fio::Flags::PROTOCOL_FILE,
1794 &Default::default(),
1795 )
1796 .await;
1797
1798 let vmo = file
1799 .get_backing_memory(
1800 fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1801 )
1802 .await
1803 .expect("Failed to make FIDL call")
1804 .map_err(Status::err_from_raw)
1805 .expect("Failed to get VMO");
1806 vmo.set_size(10).expect("VMO should be resizable");
1807 vmo.set_content_size(&20).expect("content size should be modifiable");
1808 vmo.set_stream_size(20).expect("stream size should be modifiable");
1809
1810 let vmo = file
1811 .get_backing_memory(fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ)
1812 .await
1813 .expect("Failed to make FIDL call")
1814 .map_err(Status::err_from_raw)
1815 .expect("Failed to get VMO");
1816 let err = vmo.set_size(10).expect_err("VMO should not be resizable");
1817 assert_eq!(err, Status::ACCESS_DENIED);
1818 vmo.set_stream_size(20).expect_err("stream size is not modifiable");
1820 vmo.set_content_size(&20).expect_err("content is not modifiable");
1821
1822 close_file_checked(file).await;
1823 fixture.close().await;
1824 }
1825
1826 #[fuchsia::test(threads = 10)]
1827 async fn extended_attributes() {
1828 let fixture = TestFixture::new().await;
1829 let root = fixture.root();
1830
1831 let file = open_file_checked(
1832 &root,
1833 "foo",
1834 fio::Flags::FLAG_MAYBE_CREATE
1835 | fio::PERM_READABLE
1836 | fio::PERM_WRITABLE
1837 | fio::Flags::PROTOCOL_FILE,
1838 &Default::default(),
1839 )
1840 .await;
1841
1842 let name = b"security.selinux";
1843 let value_vec = b"bar".to_vec();
1844
1845 {
1846 let (iterator_client, iterator_server) =
1847 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1848 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1849 let (chunk, last) = iterator_client
1850 .get_next()
1851 .await
1852 .expect("Failed to make FIDL call")
1853 .expect("Failed to get next iterator chunk");
1854 assert!(last);
1855 assert_eq!(chunk, Vec::<Vec<u8>>::new());
1856 }
1857 assert_eq!(
1858 file.get_extended_attribute(name)
1859 .await
1860 .expect("Failed to make FIDL call")
1861 .expect_err("Got successful message back for missing attribute"),
1862 Status::NOT_FOUND.into_raw(),
1863 );
1864
1865 file.set_extended_attribute(
1866 name,
1867 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
1868 fio::SetExtendedAttributeMode::Set,
1869 )
1870 .await
1871 .expect("Failed to make FIDL call")
1872 .expect("Failed to set extended attribute");
1873
1874 {
1875 let (iterator_client, iterator_server) =
1876 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1877 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1878 let (chunk, last) = iterator_client
1879 .get_next()
1880 .await
1881 .expect("Failed to make FIDL call")
1882 .expect("Failed to get next iterator chunk");
1883 assert!(last);
1884 assert_eq!(chunk, vec![name]);
1885 }
1886 assert_eq!(
1887 file.get_extended_attribute(name)
1888 .await
1889 .expect("Failed to make FIDL call")
1890 .expect("Failed to get extended attribute"),
1891 fio::ExtendedAttributeValue::Bytes(value_vec)
1892 );
1893
1894 file.remove_extended_attribute(name)
1895 .await
1896 .expect("Failed to make FIDL call")
1897 .expect("Failed to remove extended attribute");
1898
1899 {
1900 let (iterator_client, iterator_server) =
1901 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1902 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1903 let (chunk, last) = iterator_client
1904 .get_next()
1905 .await
1906 .expect("Failed to make FIDL call")
1907 .expect("Failed to get next iterator chunk");
1908 assert!(last);
1909 assert_eq!(chunk, Vec::<Vec<u8>>::new());
1910 }
1911 assert_eq!(
1912 file.get_extended_attribute(name)
1913 .await
1914 .expect("Failed to make FIDL call")
1915 .expect_err("Got successful message back for missing attribute"),
1916 Status::NOT_FOUND.into_raw(),
1917 );
1918
1919 close_file_checked(file).await;
1920 fixture.close().await;
1921 }
1922
1923 #[fuchsia::test]
1924 async fn test_flush_when_closed_from_on_zero_children() {
1925 let fixture = TestFixture::new().await;
1926 let root = fixture.root();
1927
1928 let file = open_file_checked(
1929 &root,
1930 "foo",
1931 fio::Flags::FLAG_MAYBE_CREATE
1932 | fio::PERM_READABLE
1933 | fio::PERM_WRITABLE
1934 | fio::Flags::PROTOCOL_FILE,
1935 &Default::default(),
1936 )
1937 .await;
1938
1939 file.resize(50).await.expect("resize (FIDL) failed").expect("resize failed");
1940
1941 {
1942 let vmo = file
1943 .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
1944 .await
1945 .expect("get_backing_memory (FIDL) failed")
1946 .map_err(Status::err_from_raw)
1947 .expect("get_backing_memory failed");
1948
1949 std::mem::drop(file);
1950
1951 fasync::unblock(move || vmo.write(b"hello", 0).expect("write failed")).await;
1952 }
1953
1954 fixture.close().await;
1955 }
1956
1957 #[fuchsia::test]
1958 async fn test_background_flush() {
1959 let fixture = TestFixture::open(
1960 DeviceHolder::new(FakeDevice::new(65536, 512)),
1961 TestFixtureOptions::default(),
1962 )
1963 .await;
1964 {
1965 let root = fixture.root();
1966
1967 let file = open_file_checked(
1968 &root,
1969 "foo",
1970 fio::Flags::FLAG_MAYBE_CREATE
1971 | fio::PERM_READABLE
1972 | fio::PERM_WRITABLE
1973 | fio::Flags::PROTOCOL_FILE,
1974 &Default::default(),
1975 )
1976 .await;
1977
1978 let stream = file.describe().await.unwrap().stream.unwrap();
1979 let file_id = file
1980 .get_attributes(fio::NodeAttributesQuery::ID)
1981 .await
1982 .unwrap()
1983 .unwrap()
1984 .1
1985 .id
1986 .unwrap();
1987 let truncate_guard = fixture
1989 .fs()
1990 .truncate_guard(fixture.volume().volume().store().store_object_id(), file_id)
1991 .await;
1992
1993 let file_obj = fixture
1994 .volume()
1995 .volume()
1996 .cache()
1997 .get(file_id)
1998 .unwrap()
1999 .into_any()
2000 .downcast::<FxFile>()
2001 .unwrap();
2002 let file_clone = file_obj.clone();
2003
2004 unblock(move || {
2005 let page_size = page_size().get();
2006 let mut offset: u64 = 0;
2007 while !file_clone
2008 .background_flush_running
2009 .load(std::sync::atomic::Ordering::Relaxed)
2010 {
2011 assert!(
2012 offset <= BACKGROUND_FLUSH_THRESHOLD * 2,
2013 "Background flush not triggering"
2014 );
2015 stream
2016 .write_at(zx::StreamWriteOptions::empty(), offset, &[0, 1, 2, 3, 4])
2017 .expect("write should succeed");
2018 offset += page_size;
2019 }
2020 })
2021 .await;
2022
2023 std::mem::drop(truncate_guard);
2025 const MAX_WAIT: Duration = Duration::from_secs(10);
2026 let wait_increments = Duration::from_millis(100);
2027 let mut total_waited = Duration::ZERO;
2028 while file_obj.background_flush_running.load(std::sync::atomic::Ordering::Relaxed) {
2029 total_waited += wait_increments;
2030 assert!(total_waited < MAX_WAIT);
2031 fasync::Timer::new(wait_increments).await;
2032 }
2033 }
2034
2035 fixture.close().await;
2036 }
2037
2038 #[fuchsia::test]
2039 async fn test_get_attributes_fsverity_enabled_file() {
2040 let fixture = TestFixture::new().await;
2041 let root = fixture.root();
2042
2043 let file = open_file_checked(
2044 &root,
2045 "foo",
2046 fio::Flags::FLAG_MAYBE_CREATE
2047 | fio::PERM_READABLE
2048 | fio::PERM_WRITABLE
2049 | fio::Flags::PROTOCOL_FILE,
2050 &Default::default(),
2051 )
2052 .await;
2053
2054 let mut data: Vec<u8> = vec![0x00u8; 1052672];
2055 rng().fill(&mut data[..]);
2056
2057 for chunk in data.chunks(8192) {
2058 file.write(chunk)
2059 .await
2060 .expect("FIDL call failed")
2061 .map_err(Status::err_from_raw)
2062 .expect("write failed");
2063 }
2064
2065 let tree = fsverity_merkle::MerkleTree::from_data(
2066 &data,
2067 FsVerityHasher::Sha256(FsVerityHasherOptions::new(vec![0xFF; 8], 4096)),
2068 );
2069 let expected_root = tree.root().to_vec();
2070
2071 let expected_descriptor = fio::VerificationOptions {
2072 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2073 salt: Some(vec![0xFF; 8]),
2074 ..Default::default()
2075 };
2076
2077 file.enable_verity(&expected_descriptor)
2078 .await
2079 .expect("FIDL transport error")
2080 .expect("enable verity failed");
2081
2082 let (_, immutable_attributes) = file
2083 .get_attributes(
2084 fio::NodeAttributesQuery::ROOT_HASH
2085 | fio::NodeAttributesQuery::OPTIONS
2086 | fio::NodeAttributesQuery::ABILITIES,
2087 )
2088 .await
2089 .expect("FIDL call failed")
2090 .map_err(Status::err_from_raw)
2091 .expect("get_attributes failed");
2092
2093 assert_eq!(
2094 immutable_attributes
2095 .options
2096 .expect("verification options not present in immutable attributes"),
2097 expected_descriptor
2098 );
2099 assert_eq!(
2100 immutable_attributes.root_hash.expect("root hash not present in immutable attributes"),
2101 expected_root
2102 );
2103 assert_eq!(
2104 immutable_attributes.abilities.expect("abilities not present"),
2105 fio::Operations::GET_ATTRIBUTES
2106 | fio::Operations::UPDATE_ATTRIBUTES
2107 | fio::Operations::READ_BYTES
2108 );
2109
2110 fixture.close().await;
2111 }
2112
2113 #[fuchsia::test]
2116 async fn test_write_fail_fsverity_enabled_file() {
2117 let fixture = TestFixture::new().await;
2118 let root = fixture.root();
2119
2120 let file = open_file_checked(
2121 &root,
2122 "foo",
2123 fio::Flags::FLAG_MAYBE_CREATE
2124 | fio::PERM_READABLE
2125 | fio::PERM_WRITABLE
2126 | fio::Flags::PROTOCOL_FILE,
2127 &Default::default(),
2128 )
2129 .await;
2130
2131 file.write(&[8; 8192])
2132 .await
2133 .expect("FIDL call failed")
2134 .map_err(Status::err_from_raw)
2135 .expect("write failed");
2136
2137 let existing_vmo = file
2139 .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
2140 .await
2141 .expect("FIDL transport error")
2142 .map_err(Status::err_from_raw)
2143 .expect("get_backing_memory failed");
2144
2145 let descriptor = fio::VerificationOptions {
2146 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2147 salt: Some(vec![0xFF; 8]),
2148 ..Default::default()
2149 };
2150
2151 file.enable_verity(&descriptor)
2152 .await
2153 .expect("FIDL transport error")
2154 .expect("enable verity failed");
2155
2156 fasync::unblock(move || {
2159 existing_vmo
2160 .write(&[2; 8192], 0)
2161 .expect_err("write via existing VMO succeeded on fsverity-enabled file");
2162 })
2163 .await;
2164
2165 async fn assert_file_is_not_writable(file: &fio::FileProxy) {
2166 file.write(&[2; 8192])
2168 .await
2169 .expect("FIDL transport error")
2170 .map_err(Status::err_from_raw)
2171 .expect_err("write succeeded on fsverity-enabled file");
2172 file.get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
2174 .await
2175 .expect("FIDL transport error")
2176 .map_err(Status::err_from_raw)
2177 .expect_err("get_backing_memory with WRITE succeeded on fsverity-enabled file");
2178 let vmo = file
2180 .get_backing_memory(
2181 fio::VmoFlags::READ | fio::VmoFlags::WRITE | fio::VmoFlags::PRIVATE_CLONE,
2182 )
2183 .await
2184 .expect("FIDL transport error")
2185 .map_err(Status::err_from_raw)
2186 .expect("get_backing_memory with PRIVATE_CLONE failed");
2187 fasync::unblock(move || {
2188 vmo.write(&[2; 8192], 0).expect("write via private clone VMO failed");
2189 })
2190 .await;
2191 file.resize(1)
2193 .await
2194 .expect("FIDL transport error")
2195 .map_err(Status::err_from_raw)
2196 .expect_err("resize succeeded on fsverity-enabled file");
2197 }
2198
2199 assert_file_is_not_writable(&file).await;
2200 close_file_checked(file).await;
2201
2202 let file =
2204 open_file(&root, "foo", fio::PERM_READABLE | fio::PERM_WRITABLE, &Default::default())
2205 .await
2206 .expect("failed to open fsverity-enabled file");
2207 assert_file_is_not_writable(&file).await;
2208 close_file_checked(file).await;
2209
2210 let device = fixture.close().await;
2212 device.ensure_unique();
2213 device.reopen(false);
2214 let fixture =
2215 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2216 .await;
2217
2218 let root = fixture.root();
2219 let file =
2220 open_file(&root, "foo", fio::PERM_READABLE | fio::PERM_WRITABLE, &Default::default())
2221 .await
2222 .expect("failed to open fsverity-enabled file");
2223 assert_file_is_not_writable(&file).await;
2224 close_file_checked(file).await;
2225
2226 fixture.close().await;
2227 }
2228
2229 #[fuchsia::test]
2230 async fn test_truncate_fail_fsverity_enabled_file() {
2231 let fixture = TestFixture::new().await;
2232 let root = fixture.root();
2233
2234 let file = open_file_checked(
2235 &root,
2236 "foo",
2237 fio::Flags::FLAG_MAYBE_CREATE
2238 | fio::PERM_READABLE
2239 | fio::PERM_WRITABLE
2240 | fio::Flags::PROTOCOL_FILE,
2241 &Default::default(),
2242 )
2243 .await;
2244
2245 file.write(&[8; 8192])
2246 .await
2247 .expect("FIDL call failed")
2248 .map_err(Status::err_from_raw)
2249 .expect("write failed");
2250
2251 let descriptor = fio::VerificationOptions {
2252 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2253 salt: Some(vec![0xFF; 8]),
2254 ..Default::default()
2255 };
2256
2257 file.enable_verity(&descriptor)
2258 .await
2259 .expect("FIDL transport error")
2260 .expect("enable verity failed");
2261
2262 file.resize(1)
2264 .await
2265 .expect("FIDL transport error")
2266 .map_err(Status::err_from_raw)
2267 .expect_err("shrink succeeded on fsverity-enabled file");
2268
2269 file.resize(16384)
2271 .await
2272 .expect("FIDL transport error")
2273 .map_err(Status::err_from_raw)
2274 .expect_err("grow succeeded on fsverity-enabled file");
2275
2276 close_file_checked(file).await;
2277 fixture.close().await;
2278 }
2279
2280 #[fuchsia::test]
2281 async fn test_fsverity_enabled_file_verified_reads() {
2282 let mut data: Vec<u8> = vec![0x00u8; 1052672];
2283 rng().fill(&mut data[..]);
2284 let mut num_chunks = 0;
2285
2286 let reused_device = {
2287 let fixture = TestFixture::new().await;
2288 let root = fixture.root();
2289
2290 let file = open_file_checked(
2291 &root,
2292 "foo",
2293 fio::Flags::FLAG_MAYBE_CREATE
2294 | fio::PERM_READABLE
2295 | fio::PERM_WRITABLE
2296 | fio::Flags::PROTOCOL_FILE,
2297 &Default::default(),
2298 )
2299 .await;
2300
2301 for chunk in data.chunks(fio::MAX_BUF as usize) {
2302 file.write(chunk)
2303 .await
2304 .expect("FIDL call failed")
2305 .map_err(Status::err_from_raw)
2306 .expect("write failed");
2307 num_chunks += 1;
2308 }
2309
2310 let descriptor = fio::VerificationOptions {
2311 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2312 salt: Some(vec![0xFF; 8]),
2313 ..Default::default()
2314 };
2315
2316 file.enable_verity(&descriptor)
2317 .await
2318 .expect("FIDL transport error")
2319 .expect("enable verity failed");
2320
2321 assert!(file.sync().await.expect("Sync failed").is_ok());
2322 close_file_checked(file).await;
2323 fixture.close().await
2324 };
2325
2326 let fixture = TestFixture::open(
2327 reused_device,
2328 TestFixtureOptions { format: false, ..Default::default() },
2329 )
2330 .await;
2331 let root = fixture.root();
2332
2333 let file = open_file_checked(
2334 &root,
2335 "foo",
2336 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2337 &Default::default(),
2338 )
2339 .await;
2340
2341 for chunk in 0..num_chunks {
2342 let buffer = file
2343 .read(fio::MAX_BUF)
2344 .await
2345 .expect("transport error on read")
2346 .expect("read failed");
2347 let start = chunk * fio::MAX_BUF as usize;
2348 assert_eq!(&buffer, &data[start..start + buffer.len()]);
2349 }
2350
2351 fixture.close().await;
2352 }
2353
2354 #[fuchsia::test]
2355 async fn test_enabling_verity_on_verified_file_fails() {
2356 let reused_device = {
2357 let fixture = TestFixture::new().await;
2358 let root = fixture.root();
2359
2360 let file = open_file_checked(
2361 &root,
2362 "foo",
2363 fio::Flags::FLAG_MAYBE_CREATE
2364 | fio::PERM_READABLE
2365 | fio::PERM_WRITABLE
2366 | fio::Flags::PROTOCOL_FILE,
2367 &Default::default(),
2368 )
2369 .await;
2370
2371 file.write(&[1; 8192])
2372 .await
2373 .expect("FIDL call failed")
2374 .map_err(Status::err_from_raw)
2375 .expect("write failed");
2376
2377 let descriptor = fio::VerificationOptions {
2378 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2379 salt: Some(vec![0xFF; 8]),
2380 ..Default::default()
2381 };
2382
2383 file.enable_verity(&descriptor)
2384 .await
2385 .expect("FIDL transport error")
2386 .expect("enable verity failed");
2387
2388 file.enable_verity(&descriptor)
2389 .await
2390 .expect("FIDL transport error")
2391 .expect_err("enabling verity on a verity-enabled file should fail.");
2392
2393 assert!(file.sync().await.expect("Sync failed").is_ok());
2394 close_file_checked(file).await;
2395 fixture.close().await
2396 };
2397
2398 let fixture = TestFixture::open(
2399 reused_device,
2400 TestFixtureOptions { format: false, ..Default::default() },
2401 )
2402 .await;
2403 let root = fixture.root();
2404
2405 let file = open_file_checked(
2406 &root,
2407 "foo",
2408 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2409 &Default::default(),
2410 )
2411 .await;
2412
2413 let descriptor = fio::VerificationOptions {
2414 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2415 salt: Some(vec![0xFF; 8]),
2416 ..Default::default()
2417 };
2418
2419 file.enable_verity(&descriptor)
2420 .await
2421 .expect("FIDL transport error")
2422 .expect_err("enabling verity on a verity-enabled file should fail.");
2423
2424 close_file_checked(file).await;
2425 fixture.close().await;
2426 }
2427
2428 #[fuchsia::test]
2429 async fn test_get_attributes_fsverity_not_enabled() {
2430 let fixture = TestFixture::new().await;
2431 let root = fixture.root();
2432
2433 let file = open_file_checked(
2434 &root,
2435 "foo",
2436 fio::Flags::FLAG_MAYBE_CREATE
2437 | fio::PERM_READABLE
2438 | fio::PERM_WRITABLE
2439 | fio::Flags::PROTOCOL_FILE,
2440 &Default::default(),
2441 )
2442 .await;
2443
2444 let mut data: Vec<u8> = vec![0x00u8; 8192];
2445 rng().fill(&mut data[..]);
2446
2447 file.write(&data)
2448 .await
2449 .expect("FIDL call failed")
2450 .map_err(Status::err_from_raw)
2451 .expect("write failed");
2452
2453 let () = file
2454 .sync()
2455 .await
2456 .expect("FIDL call failed")
2457 .map_err(Status::err_from_raw)
2458 .expect("sync failed");
2459
2460 let (_, immutable_attributes) = file
2461 .get_attributes(fio::NodeAttributesQuery::ROOT_HASH | fio::NodeAttributesQuery::OPTIONS)
2462 .await
2463 .expect("FIDL call failed")
2464 .map_err(Status::err_from_raw)
2465 .expect("get_attributes failed");
2466
2467 assert_eq!(immutable_attributes.options, None);
2468 assert_eq!(immutable_attributes.root_hash, None);
2469
2470 fixture.close().await;
2471 }
2472
2473 #[fuchsia::test]
2474 async fn test_update_attributes_also_updates_ctime() {
2475 let fixture = TestFixture::new().await;
2476 let root = fixture.root();
2477
2478 let file = open_file_checked(
2479 &root,
2480 "foo",
2481 fio::Flags::FLAG_MAYBE_CREATE
2482 | fio::PERM_READABLE
2483 | fio::PERM_WRITABLE
2484 | fio::Flags::PROTOCOL_FILE,
2485 &Default::default(),
2486 )
2487 .await;
2488
2489 file.write("hello, world!".as_bytes())
2491 .await
2492 .expect("FIDL call failed")
2493 .map_err(Status::err_from_raw)
2494 .expect("write failed");
2495 let (_mutable_attributes, immutable_attributes) = file
2496 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2497 .await
2498 .expect("FIDL call failed")
2499 .map_err(Status::err_from_raw)
2500 .expect("get_attributes failed");
2501 let ctime_after_write = immutable_attributes.change_time;
2502
2503 file.update_attributes(&fio::MutableNodeAttributes {
2505 mode: Some(111),
2506 gid: Some(222),
2507 ..Default::default()
2508 })
2509 .await
2510 .expect("FIDL call failed")
2511 .map_err(Status::err_from_raw)
2512 .expect("update_attributes failed");
2513 let (_mutable_attributes, immutable_attributes) = file
2514 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2515 .await
2516 .expect("FIDL call failed")
2517 .map_err(Status::err_from_raw)
2518 .expect("get_attributes failed");
2519 let ctime_after_update = immutable_attributes.change_time;
2520 assert!(ctime_after_update > ctime_after_write);
2521
2522 file.sync()
2524 .await
2525 .expect("FIDL call failed")
2526 .map_err(Status::err_from_raw)
2527 .expect("sync failed");
2528 let (_mutable_attributes, immutable_attributes) = file
2529 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2530 .await
2531 .expect("FIDL call failed")
2532 .map_err(Status::err_from_raw)
2533 .expect("get_attributes failed");
2534 let ctime_after_sync = immutable_attributes.change_time;
2535 assert_eq!(ctime_after_sync, ctime_after_update);
2536 fixture.close().await;
2537 }
2538
2539 #[fuchsia::test]
2540 async fn test_unnamed_temporary_file_can_read_and_write_to_it() {
2541 let fixture = TestFixture::new().await;
2542 let root = fixture.root();
2543
2544 let tmpfile = open_file_checked(
2545 &root,
2546 ".",
2547 fio::Flags::PROTOCOL_FILE
2548 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2549 | fio::PERM_READABLE
2550 | fio::PERM_WRITABLE,
2551 &fio::Options::default(),
2552 )
2553 .await;
2554
2555 let buf = vec![0xaa as u8; 8];
2556 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2557
2558 tmpfile
2559 .seek(fio::SeekOrigin::Start, 0)
2560 .await
2561 .expect("seek failed")
2562 .map_err(zx::Status::err_from_raw)
2563 .expect("seek error");
2564 let read_buf = file::read(&tmpfile).await.expect("read failed");
2565 assert_eq!(read_buf, buf);
2566
2567 fixture.close().await;
2568 }
2569
2570 #[fuchsia::test]
2571 async fn test_unnamed_temporary_file_get_space_back_after_closing_file() {
2572 let fixture = TestFixture::new().await;
2573 let root = fixture.root();
2574
2575 let tmpfile = open_file_checked(
2576 &root,
2577 ".",
2578 fio::Flags::PROTOCOL_FILE
2579 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2580 | fio::PERM_WRITABLE,
2581 &fio::Options::default(),
2582 )
2583 .await;
2584
2585 const BUFFER_SIZE: u64 = 1024 * 1024;
2586 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2587 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2588
2589 let info_after_writing_to_tmpfile = root
2590 .query_filesystem()
2591 .await
2592 .expect("Failed wire call to query filesystem")
2593 .1
2594 .expect("Failed to query filesystem");
2595
2596 close_file_checked(tmpfile).await;
2597
2598 for i in 1..50 {
2600 let info = root
2601 .query_filesystem()
2602 .await
2603 .expect("Failed wire call to query filesystem")
2604 .1
2605 .expect("Failed to query filesystem");
2606
2607 if info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE {
2610 break;
2611 }
2612 if i == 49 {
2613 panic!("Did not get space back from unnamed temporary file after closing it.");
2614 }
2615 }
2616
2617 fixture.close().await;
2618 }
2619
2620 #[fuchsia::test]
2621 async fn test_unnamed_temporary_file_get_space_back_after_closing_device() {
2622 const BUFFER_SIZE: u64 = 1024 * 1024;
2623
2624 let (reused_device, info_after_writing_to_tmpfile) = {
2625 let fixture = TestFixture::new().await;
2626 let root = fixture.root();
2627
2628 let tmpfile = open_file_checked(
2629 &root,
2630 ".",
2631 fio::Flags::PROTOCOL_FILE
2632 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2633 | fio::PERM_WRITABLE,
2634 &fio::Options::default(),
2635 )
2636 .await;
2637
2638 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2639 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2640
2641 let info_after_writing_to_tmpfile = root
2642 .query_filesystem()
2643 .await
2644 .expect("Failed wire call to query filesystem")
2645 .1
2646 .expect("Failed to query filesystem");
2647
2648 (fixture.close().await, info_after_writing_to_tmpfile)
2649 };
2650
2651 let fixture = TestFixture::open(
2652 reused_device,
2653 TestFixtureOptions { format: false, ..Default::default() },
2654 )
2655 .await;
2656 let root = fixture.root();
2657
2658 let info = root
2659 .query_filesystem()
2660 .await
2661 .expect("Failed wire call to query filesystem")
2662 .1
2663 .expect("Failed to query filesystem");
2664
2665 assert!(info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE);
2668
2669 fixture.close().await;
2670 }
2671
2672 #[fuchsia::test]
2673 async fn test_unnamed_temporary_file_can_link_into() {
2674 const FILE1: &str = "foo";
2675 const FILE2: &str = "bar";
2676 const BUFFER_SIZE: u64 = 1024 * 1024;
2677 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2678
2679 let reused_device = {
2680 let fixture = TestFixture::new().await;
2681 let root = fixture.root();
2682
2683 let tmpfile = open_file_checked(
2684 &root,
2685 ".",
2686 fio::Flags::PROTOCOL_FILE
2687 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2688 | fio::PERM_READABLE
2689 | fio::PERM_WRITABLE,
2690 &fio::Options::default(),
2691 )
2692 .await;
2693
2694 let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
2696 zx::Status::ok(status).expect("get_token failed");
2697 tmpfile
2698 .link_into(zx::Event::from(dst_token.unwrap()), FILE1)
2699 .await
2700 .expect("link_into wire message failed")
2701 .map_err(zx::Status::err_from_raw)
2702 .expect("link_into failed");
2703
2704 let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
2706 zx::Status::ok(status).expect("get_token failed");
2707 tmpfile
2708 .link_into(zx::Event::from(dst_token.unwrap()), FILE2)
2709 .await
2710 .expect("link_into wire message failed")
2711 .map_err(zx::Status::err_from_raw)
2712 .expect("link_into failed");
2713
2714 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2716
2717 root.unlink(FILE1, &fio::UnlinkOptions::default())
2718 .await
2719 .expect("unlink wire call failed")
2720 .map_err(zx::Status::err_from_raw)
2721 .expect("unlink failed");
2722 fixture.close().await
2723 };
2724
2725 let fixture = TestFixture::open(
2726 reused_device,
2727 TestFixtureOptions { format: false, ..Default::default() },
2728 )
2729 .await;
2730 let root = fixture.root();
2731
2732 assert_eq!(
2734 open_file(
2735 &root,
2736 FILE1,
2737 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2738 &fio::Options::default()
2739 )
2740 .await
2741 .expect_err("Open succeeded unexpectedly")
2742 .root_cause()
2743 .downcast_ref::<zx::Status>()
2744 .expect("No status"),
2745 &zx::Status::NOT_FOUND,
2746 );
2747
2748 let permanent_file = open_file_checked(
2751 &root,
2752 FILE2,
2753 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2754 &fio::Options::default(),
2755 )
2756 .await;
2757 permanent_file
2758 .seek(fio::SeekOrigin::Start, 0)
2759 .await
2760 .expect("seek wire message failed")
2761 .map_err(zx::Status::err_from_raw)
2762 .expect("seek error");
2763 let read_buf = file::read(&permanent_file).await.expect("read failed");
2764 assert!(read_buf == buf);
2765
2766 fsck(fixture.fs().clone()).await.expect("fsck failed");
2767
2768 fixture.close().await;
2769 }
2770
2771 #[fuchsia::test]
2772 async fn test_unnamed_temporary_file_in_encrypted_directory() {
2773 let fixture = TestFixture::new().await;
2774 let root = fixture.root();
2775
2776 let crypt = fixture.crypt().unwrap();
2778 let encrypted_directory = open_dir_checked(
2779 &root,
2780 "encrypted_directory",
2781 fio::Flags::FLAG_MAYBE_CREATE
2782 | fio::Flags::PROTOCOL_DIRECTORY
2783 | fio::PERM_READABLE
2784 | fio::PERM_WRITABLE,
2785 fio::Options::default(),
2786 )
2787 .await;
2788 crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2789 encrypted_directory
2790 .update_attributes(&fio::MutableNodeAttributes {
2791 wrapping_key_id: Some(WRAPPING_KEY_ID),
2792 ..Default::default()
2793 })
2794 .await
2795 .expect("update_attributes wire call failed")
2796 .map_err(zx::ok)
2797 .expect("update_attributes failed");
2798
2799 let encryped_tmpfile = open_file_checked(
2801 &encrypted_directory,
2802 ".",
2803 fio::Flags::PROTOCOL_FILE
2804 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2805 | fio::PERM_READABLE
2806 | fio::PERM_WRITABLE,
2807 &fio::Options::default(),
2808 )
2809 .await;
2810 let (mutable_attributes, _immutable_attributes) = encryped_tmpfile
2811 .get_attributes(fio::NodeAttributesQuery::WRAPPING_KEY_ID)
2812 .await
2813 .expect("get_attributes wire call failed")
2814 .map_err(zx::Status::err_from_raw)
2815 .expect("get_attributes failed");
2816 assert_eq!(mutable_attributes.wrapping_key_id, Some(WRAPPING_KEY_ID));
2817
2818 let (status, dst_token) = encrypted_directory.get_token().await.expect("FIDL call failed");
2821 zx::Status::ok(status).expect("get_token failed");
2822 encryped_tmpfile
2823 .link_into(zx::Event::from(dst_token.unwrap()), "foo")
2824 .await
2825 .expect("link_into wire message failed")
2826 .expect("link_into failed");
2827
2828 let unencryped_tmpfile = open_file_checked(
2829 &root,
2830 ".",
2831 fio::Flags::PROTOCOL_FILE
2832 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2833 | fio::PERM_READABLE
2834 | fio::PERM_WRITABLE,
2835 &fio::Options::default(),
2836 )
2837 .await;
2838 let (mutable_attributes, _immutable_attributes) = unencryped_tmpfile
2839 .get_attributes(fio::NodeAttributesQuery::WRAPPING_KEY_ID)
2840 .await
2841 .expect("get_attributes wire call failed")
2842 .map_err(zx::Status::err_from_raw)
2843 .expect("get_attributes failed");
2844 assert_eq!(mutable_attributes.wrapping_key_id, None);
2845 let (status, dst_token) = encrypted_directory.get_token().await.expect("FIDL call failed");
2846 zx::Status::ok(status).expect("get_token failed");
2847 assert_eq!(
2848 unencryped_tmpfile
2849 .link_into(zx::Event::from(dst_token.unwrap()), "bar")
2850 .await
2851 .expect("link_into wire message failed")
2852 .map_err(zx::Status::err_from_raw)
2853 .expect_err("link_into passed unexpectedly"),
2854 zx::Status::BAD_STATE,
2855 );
2856
2857 fixture.close().await;
2858 }
2859
2860 #[fuchsia::test]
2861 async fn test_unnamed_temporary_file_in_locked_directory() {
2862 let fixture = TestFixture::new().await;
2863 let root = fixture.root();
2864
2865 let crypt = fixture.crypt().unwrap();
2867 let encrypted_directory = open_dir_checked(
2868 &root,
2869 "encrypted_directory",
2870 fio::Flags::FLAG_MAYBE_CREATE
2871 | fio::Flags::PROTOCOL_DIRECTORY
2872 | fio::PERM_READABLE
2873 | fio::PERM_WRITABLE,
2874 fio::Options::default(),
2875 )
2876 .await;
2877 crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2878 encrypted_directory
2879 .update_attributes(&fio::MutableNodeAttributes {
2880 wrapping_key_id: Some(WRAPPING_KEY_ID),
2881 ..Default::default()
2882 })
2883 .await
2884 .expect("update_attributes wire call failed")
2885 .map_err(zx::ok)
2886 .expect("update_attributes failed");
2887
2888 crypt.forget_wrapping_key(&WRAPPING_KEY_ID).unwrap();
2890
2891 assert_eq!(
2893 open_file(
2894 &encrypted_directory,
2895 ".",
2896 fio::Flags::PROTOCOL_FILE
2897 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2898 | fio::PERM_READABLE
2899 | fio::PERM_WRITABLE,
2900 &fio::Options::default()
2901 )
2902 .await
2903 .expect_err("Open succeeded unexpectedly")
2904 .root_cause()
2905 .downcast_ref::<zx::Status>()
2906 .expect("No status"),
2907 &zx::Status::UNAVAILABLE,
2908 );
2909 fixture.close().await;
2910 }
2911
2912 #[fuchsia::test]
2913 async fn test_unnamed_temporary_file_link_into_with_race() {
2914 let fixture = TestFixture::new().await;
2915 let root = fixture.root();
2916
2917 for i in 1..100 {
2918 let tmpfile = open_file_checked(
2919 &root,
2920 ".",
2921 fio::Flags::PROTOCOL_FILE
2922 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2923 | fio::PERM_READABLE
2924 | fio::PERM_WRITABLE,
2925 &fio::Options::default(),
2926 )
2927 .await;
2928
2929 let (tmpfile_clone1, tmpfile_server1) =
2931 fidl::endpoints::create_proxy::<fio::FileMarker>();
2932 tmpfile.clone(tmpfile_server1.into_channel().into()).expect("clone failed");
2933 let (tmpfile_clone2, tmpfile_server2) =
2934 fidl::endpoints::create_proxy::<fio::FileMarker>();
2935 tmpfile.clone(tmpfile_server2.into_channel().into()).expect("clone failed");
2936
2937 let sub_dir = open_dir_checked(
2940 &root,
2941 "A",
2942 fio::Flags::PROTOCOL_DIRECTORY
2943 | fio::PERM_READABLE
2944 | fio::PERM_WRITABLE
2945 | fio::Flags::FLAG_MAYBE_CREATE,
2946 fio::Options::default(),
2947 )
2948 .await;
2949
2950 let (status, dst_token1) = sub_dir.get_token().await.expect("FIDL call failed");
2952 zx::Status::ok(status).expect("get_token failed");
2953 let (status, dst_token2) = sub_dir.get_token().await.expect("FIDL call failed");
2954 zx::Status::ok(status).expect("get_token failed");
2955
2956 join!(
2957 fasync::Task::spawn(async move {
2958 tmpfile_clone1
2959 .link_into(zx::Event::from(dst_token1.unwrap()), &(2 * i).to_string())
2960 .await
2961 .expect("link_into wire message failed")
2962 .expect("link_into failed");
2963 }),
2964 fasync::Task::spawn(async move {
2965 tmpfile_clone2
2966 .link_into(zx::Event::from(dst_token2.unwrap()), &(2 * i + 1).to_string())
2967 .await
2968 .expect("link_into wire message failed")
2969 .expect("link_into failed");
2970 })
2971 );
2972 let (_, immutable_attributes) = tmpfile
2973 .get_attributes(fio::NodeAttributesQuery::LINK_COUNT)
2974 .await
2975 .expect("Failed get_attributes wire call")
2976 .expect("get_attributes failed");
2977 assert_eq!(immutable_attributes.link_count.unwrap(), 2);
2978 close_file_checked(tmpfile).await;
2979 }
2980 fixture.close().await;
2981 }
2982
2983 #[fuchsia::test]
2984 async fn test_update_attributes_persists() {
2985 const FILE: &str = "foo";
2986 let mtime = Some(Timestamp::now().as_nanos());
2987 let atime = Some(Timestamp::now().as_nanos());
2988 let mode = Some(111);
2989
2990 let device = {
2991 let fixture = TestFixture::new().await;
2992 let root = fixture.root();
2993
2994 let file = open_file_checked(
2995 &root,
2996 FILE,
2997 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_FILE,
2998 &fio::Options::default(),
2999 )
3000 .await;
3001
3002 file.update_attributes(&fio::MutableNodeAttributes {
3003 modification_time: mtime,
3004 access_time: atime,
3005 mode: Some(111),
3006 ..Default::default()
3007 })
3008 .await
3009 .expect("update_attributes FIDL call failed")
3010 .map_err(zx::ok)
3011 .expect("update_attributes failed");
3012
3013 fixture.close().await
3015 };
3016
3017 let fixture =
3018 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
3019 .await;
3020 let root = fixture.root();
3021 let file = open_file_checked(
3022 &root,
3023 FILE,
3024 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
3025 &fio::Options::default(),
3026 )
3027 .await;
3028
3029 let (mutable_attributes, _immutable_attributes) = file
3030 .get_attributes(
3031 fio::NodeAttributesQuery::MODIFICATION_TIME
3032 | fio::NodeAttributesQuery::ACCESS_TIME
3033 | fio::NodeAttributesQuery::MODE,
3034 )
3035 .await
3036 .expect("update_attributesFIDL call failed")
3037 .map_err(zx::ok)
3038 .expect("get_attributes failed");
3039 assert_eq!(mutable_attributes.modification_time, mtime);
3040 assert_eq!(mutable_attributes.access_time, atime);
3041 assert_eq!(mutable_attributes.mode, mode);
3042 fixture.close().await;
3043 }
3044
3045 #[fuchsia::test]
3046 async fn test_atime_from_pending_access_time_update_request() {
3047 const FILE: &str = "foo";
3048
3049 let (device, expected_atime, expected_ctime) = {
3050 let fixture = TestFixture::new().await;
3051 let root = fixture.root();
3052
3053 let file = open_file_checked(
3054 &root,
3055 FILE,
3056 fio::Flags::FLAG_MAYBE_CREATE
3057 | fio::PERM_WRITABLE
3058 | fio::Flags::PROTOCOL_FILE
3059 | fio::Flags::PERM_GET_ATTRIBUTES,
3060 &fio::Options::default(),
3061 )
3062 .await;
3063
3064 let (mutable_attributes, immutable_attributes) = file
3065 .get_attributes(
3066 fio::NodeAttributesQuery::CHANGE_TIME
3067 | fio::NodeAttributesQuery::ACCESS_TIME
3068 | fio::NodeAttributesQuery::MODIFICATION_TIME,
3069 )
3070 .await
3071 .expect("update_attributes FIDL call failed")
3072 .map_err(zx::ok)
3073 .expect("get_attributes failed");
3074 let initial_ctime = immutable_attributes.change_time;
3075 let initial_atime = mutable_attributes.access_time;
3076 assert_eq!(initial_atime, initial_ctime);
3078 assert_eq!(initial_atime, mutable_attributes.modification_time);
3079
3080 let (mutable_attributes, immutable_attributes) = file
3084 .get_attributes(
3085 fio::NodeAttributesQuery::CHANGE_TIME
3086 | fio::NodeAttributesQuery::ACCESS_TIME
3087 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
3088 )
3089 .await
3090 .expect("update_attributes FIDL call failed")
3091 .map_err(zx::ok)
3092 .expect("get_attributes failed");
3093 assert!(initial_atime < mutable_attributes.access_time);
3095 let updated_atime = mutable_attributes.access_time;
3096 assert_eq!(initial_ctime, immutable_attributes.change_time);
3099
3100 let (mutable_attributes, _) = file
3101 .get_attributes(
3102 fio::NodeAttributesQuery::ACCESS_TIME
3103 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
3104 )
3105 .await
3106 .expect("update_attributes FIDL call failed")
3107 .map_err(zx::ok)
3108 .expect("get_attributes failed");
3109 assert_eq!(updated_atime, mutable_attributes.access_time);
3111
3112 (fixture.close().await, mutable_attributes.access_time, initial_ctime)
3113 };
3114
3115 let fixture =
3116 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
3117 .await;
3118 let root = fixture.root();
3119 let file = open_file_checked(
3120 &root,
3121 FILE,
3122 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
3123 &fio::Options::default(),
3124 )
3125 .await;
3126
3127 let (mutable_attributes, immutable_attributes) = file
3129 .get_attributes(
3130 fio::NodeAttributesQuery::CHANGE_TIME | fio::NodeAttributesQuery::ACCESS_TIME,
3131 )
3132 .await
3133 .expect("update_attributesFIDL call failed")
3134 .map_err(zx::ok)
3135 .expect("get_attributes failed");
3136
3137 assert_eq!(immutable_attributes.change_time, expected_ctime);
3138 assert_eq!(mutable_attributes.access_time, expected_atime);
3139 fixture.close().await;
3140 }
3141
3142 #[fuchsia::test(threads = 10)]
3143 async fn test_delete_with_dirty_bytes_and_no_open_handles() {
3144 let fixture = TestFixture::new().await;
3145 let root = fixture.root();
3146
3147 let file = open_file_checked(
3148 &root,
3149 "foo",
3150 fio::Flags::FLAG_MAYBE_CREATE
3151 | fio::PERM_READABLE
3152 | fio::PERM_WRITABLE
3153 | fio::Flags::PROTOCOL_FILE,
3154 &Default::default(),
3155 )
3156 .await;
3157
3158 file.resize(4096)
3159 .await
3160 .expect("resize failed")
3161 .map_err(Status::err_from_raw)
3162 .expect("resize error");
3163
3164 let vmo = file
3165 .get_backing_memory(
3166 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
3167 )
3168 .await
3169 .expect("Failed to make FIDL call")
3170 .map_err(Status::err_from_raw)
3171 .expect("Failed to get VMO");
3172
3173 file.sync().await.unwrap().unwrap();
3175
3176 drop(file);
3178
3179 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3181
3182 vmo.write(&[1, 2, 3, 4], 0).expect("vmo write failed");
3184
3185 drop(vmo);
3188
3189 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3191
3192 root.unlink("foo", &fio::UnlinkOptions::default())
3194 .await
3195 .expect("unlink failed")
3196 .map_err(Status::err_from_raw)
3197 .expect("unlink error");
3198
3199 fixture.close().await;
3200 }
3201
3202 #[fuchsia::test]
3206 async fn test_close_file_before_writing_to_stream() {
3207 const FILE_NAME: &str = "foo";
3208
3209 let fixture = TestFixture::new().await;
3210 let root = fixture.root();
3211 let file = open_file_checked(
3212 &root,
3213 FILE_NAME,
3214 fio::Flags::FLAG_MAYBE_CREATE
3215 | fio::PERM_READABLE
3216 | fio::PERM_WRITABLE
3217 | fio::Flags::PROTOCOL_FILE,
3218 &Default::default(),
3219 )
3220 .await;
3221
3222 let stream = file.describe().await.unwrap().stream.unwrap();
3223
3224 close_file_checked(file).await;
3225
3226 unblock(move || {
3227 stream
3228 .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3229 .expect_err("Write should get BAD_STATE");
3230 })
3231 .await;
3232
3233 fasync::Timer::new(Duration::from_millis(100)).await;
3236
3237 root.unlink(FILE_NAME, &fio::UnlinkOptions::default())
3239 .await
3240 .expect("unlink wire call failed")
3241 .expect("unlink failed");
3242
3243 fixture.close().await;
3244 }
3245
3246 #[fuchsia::test]
3252 async fn test_close_and_reopen_file_stream() {
3253 const FILE_NAME: &str = "foo";
3254
3255 let fixture = TestFixture::new().await;
3256 let root = fixture.root();
3257 let file = open_file_checked(
3258 &root,
3259 FILE_NAME,
3260 fio::Flags::FLAG_MAYBE_CREATE
3261 | fio::PERM_READABLE
3262 | fio::PERM_WRITABLE
3263 | fio::Flags::PROTOCOL_FILE,
3264 &Default::default(),
3265 )
3266 .await;
3267
3268 let page_size = page_size();
3269 file.resize(8 * page_size)
3270 .await
3271 .expect("resize failed")
3272 .map_err(Status::err_from_raw)
3273 .expect("resize error");
3274
3275 let stream1 = file.describe().await.unwrap().stream.unwrap();
3276 let stream1_dup = stream1.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3277
3278 close_file_checked(file).await;
3279
3280 let stream1_dup_clone = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3284 unblock(move || {
3285 stream1_dup_clone
3286 .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3287 .expect_err("Write should get BAD_STATE");
3288 })
3289 .await;
3290
3291 let file2 = open_file_checked(
3294 &root,
3295 FILE_NAME,
3296 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE | fio::PERM_WRITABLE,
3297 &Default::default(),
3298 )
3299 .await;
3300
3301 let stream2 = file2.describe().await.unwrap().stream.unwrap();
3302
3303 let stream1_dup_clone2 = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3307 let stream2_clone = stream2.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3308 unblock(move || {
3309 stream1_dup_clone2
3310 .write_at(zx::StreamWriteOptions::empty(), 1 * page_size, &[5, 6, 7, 8])
3311 .expect("Write on re-opened stream 1 dup should succeed");
3312 stream2_clone
3313 .write_at(zx::StreamWriteOptions::empty(), 2 * page_size, &[9, 10, 11, 12])
3314 .expect("Write on new stream 2 should succeed");
3315 })
3316 .await;
3317
3318 close_file_checked(file2).await;
3320
3321 unblock(move || {
3325 stream1_dup
3326 .write_at(zx::StreamWriteOptions::empty(), 3 * page_size, &[13, 14, 15, 16])
3327 .expect_err("Write on stream 1 dup should fail after final close");
3328 stream2
3329 .write_at(zx::StreamWriteOptions::empty(), 4 * page_size, &[17, 18, 19, 20])
3330 .expect_err("Write on stream 2 should fail after final close");
3331 })
3332 .await;
3333
3334 fixture.close().await;
3335 }
3336
3337 use test_case::test_case;
3338
3339 #[test_case(
3340 fxfs_crypto::EncryptionKey::Fxfs(fxfs_crypto::FxfsKey {
3341 wrapping_key_id: WRAPPING_KEY_ID,
3342 key: fxfs_crypto::WrappedKeyBytes::from([0xff; fxfs_crypto::FXFS_WRAPPED_KEY_SIZE]),
3343 });
3344 "fxfs"
3345 )]
3346 #[test_case(
3347 fxfs_crypto::EncryptionKey::FscryptInoLblk32File {
3348 key_identifier: WRAPPING_KEY_ID,
3349 };
3350 "fscrypt_file"
3351 )]
3352 #[fuchsia::test]
3353 async fn test_supported_wrapping_key_ids(key: fxfs_crypto::EncryptionKey) {
3354 use fxfs::object_store::transaction::{LockKey, Mutation, Options, lock_keys};
3355 use fxfs::object_store::{FSCRYPT_KEY_ID, ObjectKey, ObjectValue};
3356
3357 let fixture = TestFixture::new().await;
3358 let root = fixture.root();
3359
3360 let file = open_file_checked(
3361 &root,
3362 "key_test_file",
3363 fio::Flags::FLAG_MAYBE_CREATE
3364 | fio::Flags::PROTOCOL_FILE
3365 | fio::PERM_READABLE
3366 | fio::PERM_WRITABLE,
3367 &fio::Options::default(),
3368 )
3369 .await;
3370
3371 let (_, immutable_attributes) = file
3372 .get_attributes(fio::NodeAttributesQuery::ID)
3373 .await
3374 .expect("get_attributes wire call failed")
3375 .map_err(zx::Status::err_from_raw)
3376 .expect("get_attributes failed");
3377 let file_id = immutable_attributes.id.unwrap();
3378
3379 let store = fixture.volume().volume().store();
3380 let mut transaction = store
3381 .new_transaction(
3382 lock_keys![LockKey::object(store.store_object_id(), file_id)],
3383 Options::default(),
3384 )
3385 .await
3386 .expect("new_transaction failed");
3387
3388 transaction.add(
3389 store.store_object_id(),
3390 Mutation::replace_or_insert_object(
3391 ObjectKey::keys(file_id),
3392 ObjectValue::Keys(vec![(FSCRYPT_KEY_ID, key)].into()),
3393 ),
3394 );
3395 transaction.commit().await.expect("commit failed");
3396
3397 let (mutable_attributes, _) = file
3398 .get_attributes(fio::NodeAttributesQuery::WRAPPING_KEY_ID)
3399 .await
3400 .expect("get_attributes wire call failed")
3401 .map_err(zx::Status::err_from_raw)
3402 .expect("get_attributes failed");
3403
3404 assert_eq!(mutable_attributes.wrapping_key_id, Some(WRAPPING_KEY_ID));
3405
3406 fixture.close().await;
3407 }
3408
3409 #[fuchsia::test(threads = 3)]
3410 async fn test_teardown_with_pending_shrink_and_commit_failure() {
3411 use fxfs::errors::FxfsError;
3412
3413 let fail = Arc::new(AtomicBool::new(false));
3414 let fail_clone = fail.clone();
3415 let (mut hooks, fs_hooks) = fxfs::hooks::Hooks::new();
3416 hooks.set_pre_commit(move |_| {
3417 if fail_clone.load(std::sync::atomic::Ordering::Relaxed) {
3418 Err(FxfsError::Unavailable.into())
3419 } else {
3420 Ok(())
3421 }
3422 });
3423 let fixture = TestFixture::open(
3424 DeviceHolder::new(FakeDevice::new(16384, 512)),
3425 TestFixtureOptions { encrypted: false, hooks: Some(fs_hooks), ..Default::default() },
3426 )
3427 .await;
3428
3429 let root = fixture.root();
3430 let file = open_file_checked(
3431 &root,
3432 "test_file",
3433 fio::Flags::FLAG_MAYBE_CREATE
3434 | fio::Flags::PROTOCOL_FILE
3435 | fio::PERM_READABLE
3436 | fio::PERM_WRITABLE,
3437 &fio::Options::default(),
3438 )
3439 .await;
3440
3441 let page_size = page_size();
3443 file.resize(page_size * 4).await.unwrap().expect("resize failed");
3444 file.sync().await.unwrap().expect("sync failed");
3445
3446 fail.store(true, std::sync::atomic::Ordering::Relaxed);
3448
3449 let stream = file.describe().await.unwrap().stream.unwrap();
3451 unblock(move || {
3452 for i in 0..2 {
3453 stream
3454 .write_at(zx::StreamWriteOptions::empty(), i * page_size, &[1u8])
3455 .expect("write_at failed");
3456 }
3457 })
3458 .await;
3459
3460 file.resize(page_size * 2).await.unwrap().expect("resize failed");
3462
3463 drop(file);
3466
3467 fixture.close().await;
3472 }
3473}