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