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