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.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 .new_transaction(
559 lock_keys![
560 LockKey::object(store.store_object_id(), object_id),
561 LockKey::object(store.store_object_id(), dir.object_id()),
562 ],
563 Options::default(),
564 )
565 .await
566 .map_err(map_to_status)?;
567
568 dir.check_fscrypt_hard_link_conditions(self.fscrypt_wrapping_key_id().await?)?;
569
570 let state = self.load_state();
571 let is_unnamed_temporary = state.is_unnamed_temporary();
572 let to_be_purged = state.to_be_purged();
573 if is_unnamed_temporary {
574 dir.link_graveyard_object(transaction, &name, object_id, ObjectDescriptor::File, || {
576 self.mark_as_permanent()
577 })
578 .await
579 } else {
580 if to_be_purged {
582 return Err(zx::Status::NOT_FOUND);
583 }
584 dir.link_object(transaction, &name, object_id, ObjectDescriptor::File).await
585 }
586 }
587
588 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
589 Ok(self.handle.owner().filesystem_info_for_volume())
590 }
591
592 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, Status> {
593 self.handle.store_handle().list_extended_attributes().await.map_err(map_to_status)
594 }
595
596 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, Status> {
597 self.handle.store_handle().get_extended_attribute(name).await.map_err(map_to_status)
598 }
599
600 async fn set_extended_attribute(
601 &self,
602 name: Vec<u8>,
603 value: Vec<u8>,
604 mode: fio::SetExtendedAttributeMode,
605 ) -> Result<(), Status> {
606 self.handle
607 .store_handle()
608 .set_extended_attribute(name, value, mode.into())
609 .await
610 .map_err(map_to_status)
611 }
612
613 async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
614 self.handle.store_handle().remove_extended_attribute(name).await.map_err(map_to_status)
615 }
616}
617
618impl File for FxFile {
619 fn writable(&self) -> bool {
620 true
621 }
622
623 async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
624 Ok(())
625 }
626
627 async fn truncate(&self, length: u64) -> Result<(), Status> {
628 self.handle.truncate(length).await.map_err(map_to_status)?;
629 Ok(())
630 }
631
632 async fn enable_verity(&self, options: fio::VerificationOptions) -> Result<(), Status> {
633 self.handle.set_read_only();
634 self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
635 self.handle.uncached_handle().enable_verity(options).await.map_err(map_to_status)
636 }
637
638 async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
640 if flags.contains(fio::VmoFlags::EXECUTE) {
642 error!("get_backing_memory does not support execute rights!");
643 return Err(Status::NOT_SUPPORTED);
644 }
645
646 let vmo = self.handle.vmo();
647 let mut rights = zx::Rights::BASIC | zx::Rights::MAP | zx::Rights::GET_PROPERTY;
648 if flags.contains(fio::VmoFlags::READ) {
649 rights |= zx::Rights::READ;
650 }
651 if flags.contains(fio::VmoFlags::WRITE) {
652 rights |= zx::Rights::WRITE;
653 }
654
655 let child_vmo = if flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
656 rights |= zx::Rights::SET_PROPERTY;
658 let mut child_options = zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE;
659 if flags.contains(fio::VmoFlags::WRITE) {
660 child_options |= zx::VmoChildOptions::RESIZABLE;
661 rights |= zx::Rights::RESIZE;
662 }
663 vmo.create_child(child_options, 0, vmo.get_stream_size()?)?
664 } else {
665 vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0)?
666 };
667
668 let child_vmo = child_vmo.replace_handle(rights)?;
669 if self.handle.owner().pager().watch_for_zero_children(self).map_err(map_to_status)? {
670 self.open_count_add_one();
672 }
673 Ok(child_vmo)
674 }
675
676 async fn get_size(&self) -> Result<u64, Status> {
677 Ok(self.handle.get_size())
678 }
679
680 async fn update_attributes(
681 &self,
682 attributes: fio::MutableNodeAttributes,
683 ) -> Result<(), Status> {
684 if attributes == fio::MutableNodeAttributes::default() {
685 return Ok(());
686 }
687
688 self.handle.update_attributes(&attributes).await.map_err(map_to_status)?;
689 Ok(())
690 }
691
692 async fn allocate(
693 &self,
694 offset: u64,
695 length: u64,
696 _mode: fio::AllocateMode,
697 ) -> Result<(), Status> {
698 let range = offset..offset.checked_add(length).ok_or(Status::FILE_BIG)?;
701 self.handle.allocate(range).await.map_err(map_to_status)
702 }
703
704 async fn sync(&self, mode: SyncMode) -> Result<(), Status> {
705 self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
706
707 if mode == SyncMode::Normal {
710 self.handle
711 .store()
712 .filesystem()
713 .sync(SyncOptions::default())
714 .await
715 .map_err(map_to_status)?;
716 }
717
718 Ok(())
719 }
720}
721
722#[fxfs_trace::trace]
723impl PagerBacked for FxFile {
724 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
725 let mut old = self.load_state();
726 loop {
727 if old.open_count() == 0 {
728 return Err(self);
729 }
730
731 assert!(old.open_count() < MAX_OPEN_COUNTS);
732
733 match self.state.compare_exchange_weak(
734 old.0,
735 old.0 + 1,
736 Ordering::Relaxed,
737 Ordering::Relaxed,
738 ) {
739 Ok(_) => return Ok(OpenedNode(self)),
740 Err(new_value) => old.0 = new_value,
741 }
742 }
743 }
744
745 fn pager(&self) -> &crate::pager::Pager {
746 self.handle.owner().pager()
747 }
748
749 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
750 &self.pager_packet_receiver_registration
751 }
752
753 fn vmo(&self) -> &zx::Vmo {
754 self.handle.vmo()
755 }
756
757 fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
758 default_page_in(self, range, READ_AHEAD_SIZE);
759 }
760
761 #[trace]
762 fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
763 let (valid_pages, invalid_pages) = range.split(MAX_FILE_SIZE);
764 if let Some(invalid_pages) = invalid_pages {
765 invalid_pages.report_failure(zx::Status::FILE_BIG);
766 }
767 let range = match valid_pages {
768 Some(range) => range,
769 None => return,
770 };
771
772 let byte_count = range.len();
773 self.handle.owner().clone().report_pager_dirty(byte_count, move || {
774 match self.handle.mark_dirty(range) {
775 Ok(dirty_bytes) => {
776 if dirty_bytes > BACKGROUND_FLUSH_THRESHOLD
779 && !self.background_flush_running.swap(true, Ordering::Relaxed)
780 {
781 let owner = self.handle.owner().clone();
782 owner.spawn(async move {
783 let _ = self.handle.flush(FlushType::Background).await;
785 self.background_flush_running.store(false, Ordering::Relaxed);
788 });
789 }
790 }
791 Err(_) => {
792 self.handle.owner().report_pager_clean(byte_count)
794 }
795 }
796 });
797 }
798
799 fn on_zero_children(self: Arc<Self>) {
800 self.open_count_sub_one();
802 }
803
804 fn byte_size(&self) -> u64 {
805 self.handle.uncached_size()
806 }
807
808 #[trace("len" => (range.end - range.start))]
809 async fn aligned_read(&self, range: Range<u64>) -> Result<buffer::Buffer<'_>, Error> {
810 let buffer = self.handle.read_uncached(range).await?;
811 Ok(buffer)
812 }
813}
814
815impl GetVmo for FxFile {
816 const PAGER_ON_FIDL_EXECUTOR: bool = true;
817
818 fn get_vmo(&self) -> &zx::Vmo {
819 self.vmo()
820 }
821}
822
823#[cfg(test)]
824mod tests {
825 use super::FxFile;
826 use crate::fuchsia::paged_object_handle::BACKGROUND_FLUSH_THRESHOLD;
827 use crate::fuchsia::testing::{
828 TestFixture, TestFixtureOptions, close_file_checked, open_dir_checked, open_file,
829 open_file_checked,
830 };
831 use anyhow::format_err;
832 use fidl_fuchsia_io as fio;
833 use fsverity_merkle::{FsVerityHasher, FsVerityHasherOptions};
834 use fuchsia_async::{self as fasync, unblock};
835 use fuchsia_fs::file;
836 use futures::join;
837 use fxfs::fsck::fsck;
838 use fxfs::object_handle::INVALID_OBJECT_ID;
839 use fxfs::object_store::Timestamp;
840 use fxfs_crypto::WrappingKeyId;
841 use rand::{Rng, rng};
842 use std::sync::Arc;
843 use std::sync::atomic::{self, AtomicBool};
844 use std::time::Duration;
845 use storage_device::DeviceHolder;
846 use storage_device::fake_device::FakeDevice;
847 use zx::Status;
848
849 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
850
851 #[fuchsia::test(threads = 10)]
852 async fn test_empty_file() {
853 let fixture = TestFixture::new().await;
854 let root = fixture.root();
855
856 let file = open_file_checked(
857 &root,
858 "foo",
859 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
860 &Default::default(),
861 )
862 .await;
863
864 let buf = file
865 .read(fio::MAX_BUF)
866 .await
867 .expect("FIDL call failed")
868 .map_err(Status::from_raw)
869 .expect("read failed");
870 assert!(buf.is_empty());
871
872 let (mutable_attrs, immutable_attrs) = file
873 .get_attributes(fio::NodeAttributesQuery::all())
874 .await
875 .expect("FIDL call failed")
876 .expect("GetAttributes failed");
877 assert_ne!(immutable_attrs.id.unwrap(), INVALID_OBJECT_ID);
878 assert_eq!(immutable_attrs.content_size.unwrap(), 0u64);
879 assert_eq!(immutable_attrs.storage_size.unwrap(), 0u64);
880 assert_eq!(immutable_attrs.link_count.unwrap(), 1u64);
881 assert_ne!(mutable_attrs.creation_time.unwrap(), 0u64);
882 assert_ne!(mutable_attrs.modification_time.unwrap(), 0u64);
883 assert_eq!(mutable_attrs.creation_time.unwrap(), mutable_attrs.modification_time.unwrap());
884
885 close_file_checked(file).await;
886 fixture.close().await;
887 }
888
889 #[fuchsia::test(threads = 10)]
890 async fn test_write_read() {
891 let fixture = TestFixture::new().await;
892 let root = fixture.root();
893
894 let file = open_file_checked(
895 &root,
896 "foo",
897 fio::Flags::FLAG_MAYBE_CREATE
898 | fio::PERM_READABLE
899 | fio::PERM_WRITABLE
900 | fio::Flags::PROTOCOL_FILE,
901 &Default::default(),
902 )
903 .await;
904
905 let inputs = vec!["hello, ", "world!"];
906 let expected_output = "hello, world!";
907 for input in inputs {
908 let bytes_written = file
909 .write(input.as_bytes())
910 .await
911 .expect("write failed")
912 .map_err(Status::from_raw)
913 .expect("File write was successful");
914 assert_eq!(bytes_written as usize, input.as_bytes().len());
915 }
916
917 let buf = file
918 .read_at(fio::MAX_BUF, 0)
919 .await
920 .expect("read_at failed")
921 .map_err(Status::from_raw)
922 .expect("File read was successful");
923 assert_eq!(buf.len(), expected_output.as_bytes().len());
924 assert!(buf.iter().eq(expected_output.as_bytes().iter()));
925
926 let (_, immutable_attributes) = file
927 .get_attributes(
928 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
929 )
930 .await
931 .expect("FIDL call failed")
932 .expect("get_attributes failed");
933
934 assert_eq!(
935 immutable_attributes.content_size.unwrap(),
936 expected_output.as_bytes().len() as u64
937 );
938 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size() as u64);
939
940 let () = file
941 .sync()
942 .await
943 .expect("FIDL call failed")
944 .map_err(Status::from_raw)
945 .expect("sync failed");
946
947 let (_, immutable_attributes) = file
948 .get_attributes(
949 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
950 )
951 .await
952 .expect("FIDL call failed")
953 .expect("get_attributes failed");
954
955 assert_eq!(
956 immutable_attributes.content_size.unwrap(),
957 expected_output.as_bytes().len() as u64
958 );
959 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size() as u64);
960
961 close_file_checked(file).await;
962 fixture.close().await;
963 }
964
965 #[fuchsia::test(threads = 10)]
966 async fn test_page_in() {
967 let input = "hello, world!";
968 let reused_device = {
969 let fixture = TestFixture::new().await;
970 let root = fixture.root();
971
972 let file = open_file_checked(
973 &root,
974 "foo",
975 fio::Flags::FLAG_MAYBE_CREATE
976 | fio::PERM_READABLE
977 | fio::PERM_WRITABLE
978 | fio::Flags::PROTOCOL_FILE,
979 &Default::default(),
980 )
981 .await;
982
983 let bytes_written = file
984 .write(input.as_bytes())
985 .await
986 .expect("write failed")
987 .map_err(Status::from_raw)
988 .expect("File write was successful");
989 assert_eq!(bytes_written as usize, input.as_bytes().len());
990 assert!(file.sync().await.expect("Sync failed").is_ok());
991
992 close_file_checked(file).await;
993 fixture.close().await
994 };
995
996 let fixture = TestFixture::open(
997 reused_device,
998 TestFixtureOptions { format: false, ..Default::default() },
999 )
1000 .await;
1001 let root = fixture.root();
1002
1003 let file = open_file_checked(
1004 &root,
1005 "foo",
1006 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1007 &Default::default(),
1008 )
1009 .await;
1010
1011 let vmo =
1012 file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1013 let mut readback = vec![0; input.as_bytes().len()];
1014 assert!(vmo.read(&mut readback, 0).is_ok());
1015 assert_eq!(input.as_bytes(), readback);
1016
1017 close_file_checked(file).await;
1018 fixture.close().await;
1019 }
1020
1021 #[fuchsia::test(threads = 10)]
1022 async fn test_page_in_io_error() {
1023 let mut device = FakeDevice::new(8192, 512);
1024 let succeed_requests = Arc::new(AtomicBool::new(true));
1025 let succeed_requests_clone = succeed_requests.clone();
1026 device.set_op_callback(Box::new(move |_| {
1027 if succeed_requests_clone.load(atomic::Ordering::Relaxed) {
1028 Ok(())
1029 } else {
1030 Err(format_err!("Fake error."))
1031 }
1032 }));
1033
1034 let input = "hello, world!";
1035 let reused_device = {
1036 let fixture = TestFixture::open(
1037 DeviceHolder::new(device),
1038 TestFixtureOptions { format: true, ..Default::default() },
1039 )
1040 .await;
1041 let root = fixture.root();
1042
1043 let file = open_file_checked(
1044 &root,
1045 "foo",
1046 fio::Flags::FLAG_MAYBE_CREATE
1047 | fio::PERM_READABLE
1048 | fio::PERM_WRITABLE
1049 | fio::Flags::PROTOCOL_FILE,
1050 &Default::default(),
1051 )
1052 .await;
1053
1054 let bytes_written = file
1055 .write(input.as_bytes())
1056 .await
1057 .expect("write failed")
1058 .map_err(Status::from_raw)
1059 .expect("File write was successful");
1060 assert_eq!(bytes_written as usize, input.as_bytes().len());
1061
1062 close_file_checked(file).await;
1063 fixture.close().await
1064 };
1065
1066 let fixture = TestFixture::open(
1067 reused_device,
1068 TestFixtureOptions { format: false, ..Default::default() },
1069 )
1070 .await;
1071 let root = fixture.root();
1072
1073 let file = open_file_checked(
1074 &root,
1075 "foo",
1076 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1077 &Default::default(),
1078 )
1079 .await;
1080
1081 let vmo =
1082 file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1083 succeed_requests.store(false, atomic::Ordering::Relaxed);
1084 let mut readback = vec![0; input.as_bytes().len()];
1085 assert!(vmo.read(&mut readback, 0).is_err());
1086
1087 succeed_requests.store(true, atomic::Ordering::Relaxed);
1088 close_file_checked(file).await;
1089 fixture.close().await;
1090 }
1091
1092 #[fuchsia::test(threads = 10)]
1093 async fn test_writes_persist() {
1094 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1095 for i in 0..2 {
1096 let fixture = TestFixture::open(
1097 device,
1098 TestFixtureOptions { format: i == 0, ..Default::default() },
1099 )
1100 .await;
1101 let root = fixture.root();
1102
1103 let flags = if i == 0 {
1104 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE
1105 } else {
1106 fio::PERM_READABLE | fio::PERM_WRITABLE
1107 };
1108 let file = open_file_checked(
1109 &root,
1110 "foo",
1111 flags | fio::Flags::PROTOCOL_FILE,
1112 &Default::default(),
1113 )
1114 .await;
1115
1116 if i == 0 {
1117 let _: u64 = file
1118 .write(&vec![0xaa as u8; 8192])
1119 .await
1120 .expect("FIDL call failed")
1121 .map_err(Status::from_raw)
1122 .expect("File write was successful");
1123 } else {
1124 let buf = file
1125 .read(8192)
1126 .await
1127 .expect("FIDL call failed")
1128 .map_err(Status::from_raw)
1129 .expect("File read was successful");
1130 assert_eq!(buf, vec![0xaa as u8; 8192]);
1131 }
1132
1133 let (_, immutable_attributes) = file
1134 .get_attributes(
1135 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1136 )
1137 .await
1138 .expect("FIDL call failed")
1139 .expect("get_attributes failed");
1140
1141 assert_eq!(immutable_attributes.content_size.unwrap(), 8192u64);
1142 assert_eq!(immutable_attributes.storage_size.unwrap(), 8192u64);
1143
1144 close_file_checked(file).await;
1145 device = fixture.close().await;
1146 }
1147 }
1148
1149 #[fuchsia::test(threads = 10)]
1150 async fn test_append() {
1151 let fixture = TestFixture::new().await;
1152 let root = fixture.root();
1153
1154 let inputs = vec!["hello, ", "world!"];
1155 let expected_output = "hello, world!";
1156 for input in inputs {
1157 let file = open_file_checked(
1158 &root,
1159 "foo",
1160 fio::Flags::FLAG_MAYBE_CREATE
1161 | fio::PERM_READABLE
1162 | fio::PERM_WRITABLE
1163 | fio::Flags::FILE_APPEND
1164 | fio::Flags::PROTOCOL_FILE,
1165 &Default::default(),
1166 )
1167 .await;
1168
1169 let bytes_written = file
1170 .write(input.as_bytes())
1171 .await
1172 .expect("FIDL call failed")
1173 .map_err(Status::from_raw)
1174 .expect("File write was successful");
1175 assert_eq!(bytes_written as usize, input.as_bytes().len());
1176 close_file_checked(file).await;
1177 }
1178
1179 let file = open_file_checked(
1180 &root,
1181 "foo",
1182 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1183 &Default::default(),
1184 )
1185 .await;
1186 let buf = file
1187 .read_at(fio::MAX_BUF, 0)
1188 .await
1189 .expect("FIDL call failed")
1190 .map_err(Status::from_raw)
1191 .expect("File read was successful");
1192 assert_eq!(buf.len(), expected_output.as_bytes().len());
1193 assert_eq!(&buf[..], expected_output.as_bytes());
1194
1195 let (_, immutable_attributes) = file
1196 .get_attributes(
1197 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1198 )
1199 .await
1200 .expect("FIDL call failed")
1201 .expect("get_attributes failed");
1202
1203 assert_eq!(
1204 immutable_attributes.content_size.unwrap(),
1205 expected_output.as_bytes().len() as u64
1206 );
1207 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size() as u64);
1208
1209 close_file_checked(file).await;
1210 fixture.close().await;
1211 }
1212
1213 #[fuchsia::test(threads = 10)]
1214 async fn test_seek() {
1215 let fixture = TestFixture::new().await;
1216 let root = fixture.root();
1217
1218 let file = open_file_checked(
1219 &root,
1220 "foo",
1221 fio::Flags::FLAG_MAYBE_CREATE
1222 | fio::PERM_READABLE
1223 | fio::PERM_WRITABLE
1224 | fio::Flags::PROTOCOL_FILE,
1225 &Default::default(),
1226 )
1227 .await;
1228
1229 let input = "hello, world!";
1230 let _: u64 = file
1231 .write(input.as_bytes())
1232 .await
1233 .expect("FIDL call failed")
1234 .map_err(Status::from_raw)
1235 .expect("File write was successful");
1236
1237 {
1238 let offset = file
1239 .seek(fio::SeekOrigin::Start, 0)
1240 .await
1241 .expect("FIDL call failed")
1242 .map_err(Status::from_raw)
1243 .expect("seek was successful");
1244 assert_eq!(offset, 0);
1245 let buf = file
1246 .read(5)
1247 .await
1248 .expect("FIDL call failed")
1249 .map_err(Status::from_raw)
1250 .expect("File read was successful");
1251 assert!(buf.iter().eq("hello".as_bytes().iter()));
1252 }
1253 {
1254 let offset = file
1255 .seek(fio::SeekOrigin::Current, 2)
1256 .await
1257 .expect("FIDL call failed")
1258 .map_err(Status::from_raw)
1259 .expect("seek was successful");
1260 assert_eq!(offset, 7);
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("world".as_bytes().iter()));
1268 }
1269 {
1270 let offset = file
1271 .seek(fio::SeekOrigin::Current, -5)
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::End, -1)
1288 .await
1289 .expect("FIDL call failed")
1290 .map_err(Status::from_raw)
1291 .expect("seek was successful");
1292 assert_eq!(offset, 12);
1293 let buf = file
1294 .read(1)
1295 .await
1296 .expect("FIDL call failed")
1297 .map_err(Status::from_raw)
1298 .expect("File read was successful");
1299 assert!(buf.iter().eq("!".as_bytes().iter()));
1300 }
1301
1302 close_file_checked(file).await;
1303 fixture.close().await;
1304 }
1305
1306 #[fuchsia::test(threads = 10)]
1307 async fn test_resize_extend() {
1308 let fixture = TestFixture::new().await;
1309 let root = fixture.root();
1310
1311 let file = open_file_checked(
1312 &root,
1313 "foo",
1314 fio::Flags::FLAG_MAYBE_CREATE
1315 | fio::PERM_READABLE
1316 | fio::PERM_WRITABLE
1317 | fio::Flags::PROTOCOL_FILE,
1318 &Default::default(),
1319 )
1320 .await;
1321
1322 let input = "hello, world!";
1323 let len: usize = 16 * 1024;
1324
1325 let _: u64 = file
1326 .write(input.as_bytes())
1327 .await
1328 .expect("FIDL call failed")
1329 .map_err(Status::from_raw)
1330 .expect("File write was successful");
1331
1332 let offset = file
1333 .seek(fio::SeekOrigin::Start, 0)
1334 .await
1335 .expect("FIDL call failed")
1336 .map_err(Status::from_raw)
1337 .expect("Seek was successful");
1338 assert_eq!(offset, 0);
1339
1340 let () = file
1341 .resize(len as u64)
1342 .await
1343 .expect("resize failed")
1344 .map_err(Status::from_raw)
1345 .expect("resize error");
1346
1347 let mut expected_buf = vec![0 as u8; len];
1348 expected_buf[..input.as_bytes().len()].copy_from_slice(input.as_bytes());
1349
1350 let buf = file::read(&file).await.expect("File read was successful");
1351 assert_eq!(buf.len(), len);
1352 assert_eq!(buf, expected_buf);
1353
1354 expected_buf[len - 1..].copy_from_slice("a".as_bytes());
1356
1357 let _: u64 = file
1358 .write_at("a".as_bytes(), (len - 1) as u64)
1359 .await
1360 .expect("FIDL call failed")
1361 .map_err(Status::from_raw)
1362 .expect("File write was successful");
1363
1364 let offset = file
1365 .seek(fio::SeekOrigin::Start, 0)
1366 .await
1367 .expect("FIDL call failed")
1368 .map_err(Status::from_raw)
1369 .expect("Seek was successful");
1370 assert_eq!(offset, 0);
1371
1372 let buf = file::read(&file).await.expect("File read was successful");
1373 assert_eq!(buf.len(), len);
1374 assert_eq!(buf, expected_buf);
1375
1376 close_file_checked(file).await;
1377 fixture.close().await;
1378 }
1379
1380 #[fuchsia::test(threads = 10)]
1381 async fn test_resize_shrink() {
1382 let fixture = TestFixture::new().await;
1383 let root = fixture.root();
1384
1385 let file = open_file_checked(
1386 &root,
1387 "foo",
1388 fio::Flags::FLAG_MAYBE_CREATE
1389 | fio::PERM_READABLE
1390 | fio::PERM_WRITABLE
1391 | fio::Flags::PROTOCOL_FILE,
1392 &Default::default(),
1393 )
1394 .await;
1395
1396 let len: usize = 2 * 1024;
1397 let input = {
1398 let mut v = vec![0 as u8; len];
1399 for i in 0..v.len() {
1400 v[i] = ('a' as u8) + (i % 13) as u8;
1401 }
1402 v
1403 };
1404 let short_len: usize = 513;
1405
1406 file::write(&file, &input).await.expect("File write was successful");
1407
1408 let () = file
1409 .resize(short_len as u64)
1410 .await
1411 .expect("resize failed")
1412 .map_err(Status::from_raw)
1413 .expect("resize error");
1414
1415 let offset = file
1416 .seek(fio::SeekOrigin::Start, 0)
1417 .await
1418 .expect("FIDL call failed")
1419 .map_err(Status::from_raw)
1420 .expect("Seek was successful");
1421 assert_eq!(offset, 0);
1422
1423 let buf = file::read(&file).await.expect("File read was successful");
1424 assert_eq!(buf.len(), short_len);
1425 assert_eq!(buf, input[..short_len]);
1426
1427 let () = file
1429 .resize(len as u64)
1430 .await
1431 .expect("resize failed")
1432 .map_err(Status::from_raw)
1433 .expect("resize error");
1434
1435 let expected_buf = {
1436 let mut v = vec![0 as u8; len];
1437 v[..short_len].copy_from_slice(&input[..short_len]);
1438 v
1439 };
1440
1441 let offset = file
1442 .seek(fio::SeekOrigin::Start, 0)
1443 .await
1444 .expect("seek failed")
1445 .map_err(Status::from_raw)
1446 .expect("Seek was successful");
1447 assert_eq!(offset, 0);
1448
1449 let buf = file::read(&file).await.expect("File read was successful");
1450 assert_eq!(buf.len(), len);
1451 assert_eq!(buf, expected_buf);
1452
1453 close_file_checked(file).await;
1454 fixture.close().await;
1455 }
1456
1457 #[fuchsia::test(threads = 10)]
1458 async fn test_resize_shrink_repeated() {
1459 let fixture = TestFixture::new().await;
1460 let root = fixture.root();
1461
1462 let file = open_file_checked(
1463 &root,
1464 "foo",
1465 fio::Flags::FLAG_MAYBE_CREATE
1466 | fio::PERM_READABLE
1467 | fio::PERM_WRITABLE
1468 | fio::Flags::PROTOCOL_FILE,
1469 &Default::default(),
1470 )
1471 .await;
1472
1473 let orig_len: usize = 4 * 1024;
1474 let mut len = orig_len;
1475 let input = {
1476 let mut v = vec![0 as u8; len];
1477 for i in 0..v.len() {
1478 v[i] = ('a' as u8) + (i % 13) as u8;
1479 }
1480 v
1481 };
1482 let short_len: usize = 513;
1483
1484 file::write(&file, &input).await.expect("File write was successful");
1485
1486 while len > short_len {
1487 len -= std::cmp::min(len - short_len, 512);
1488 let () = file
1489 .resize(len as u64)
1490 .await
1491 .expect("resize failed")
1492 .map_err(Status::from_raw)
1493 .expect("resize error");
1494 }
1495
1496 let offset = file
1497 .seek(fio::SeekOrigin::Start, 0)
1498 .await
1499 .expect("Seek failed")
1500 .map_err(Status::from_raw)
1501 .expect("Seek was successful");
1502 assert_eq!(offset, 0);
1503
1504 let buf = file::read(&file).await.expect("File read was successful");
1505 assert_eq!(buf.len(), short_len);
1506 assert_eq!(buf, input[..short_len]);
1507
1508 let () = file
1510 .resize(orig_len as u64)
1511 .await
1512 .expect("resize failed")
1513 .map_err(Status::from_raw)
1514 .expect("resize error");
1515
1516 let expected_buf = {
1517 let mut v = vec![0 as u8; orig_len];
1518 v[..short_len].copy_from_slice(&input[..short_len]);
1519 v
1520 };
1521
1522 let offset = file
1523 .seek(fio::SeekOrigin::Start, 0)
1524 .await
1525 .expect("seek failed")
1526 .map_err(Status::from_raw)
1527 .expect("Seek was successful");
1528 assert_eq!(offset, 0);
1529
1530 let buf = file::read(&file).await.expect("File read was successful");
1531 assert_eq!(buf.len(), orig_len);
1532 assert_eq!(buf, expected_buf);
1533
1534 close_file_checked(file).await;
1535 fixture.close().await;
1536 }
1537
1538 #[fuchsia::test(threads = 10)]
1539 async fn test_unlink_with_open_race() {
1540 let fixture = Arc::new(TestFixture::new().await);
1541 let fixture1 = fixture.clone();
1542 let fixture2 = fixture.clone();
1543 let fixture3 = fixture.clone();
1544 let done = Arc::new(AtomicBool::new(false));
1545 let done1 = done.clone();
1546 let done2 = done.clone();
1547 join!(
1548 fasync::Task::spawn(async move {
1549 let root = fixture1.root();
1550 while !done1.load(atomic::Ordering::Relaxed) {
1551 let file = open_file_checked(
1552 &root,
1553 "foo",
1554 fio::Flags::FLAG_MAYBE_CREATE
1555 | fio::PERM_READABLE
1556 | fio::PERM_WRITABLE
1557 | fio::Flags::PROTOCOL_FILE,
1558 &Default::default(),
1559 )
1560 .await;
1561 let _: u64 = file
1562 .write(b"hello")
1563 .await
1564 .expect("write failed")
1565 .map_err(Status::from_raw)
1566 .expect("write error");
1567 }
1568 }),
1569 fasync::Task::spawn(async move {
1570 let root = fixture2.root();
1571 while !done2.load(atomic::Ordering::Relaxed) {
1572 let file = open_file_checked(
1573 &root,
1574 "foo",
1575 fio::Flags::FLAG_MAYBE_CREATE
1576 | fio::PERM_READABLE
1577 | fio::PERM_WRITABLE
1578 | fio::Flags::PROTOCOL_FILE,
1579 &Default::default(),
1580 )
1581 .await;
1582 let _: u64 = file
1583 .write(b"hello")
1584 .await
1585 .expect("write failed")
1586 .map_err(Status::from_raw)
1587 .expect("write error");
1588 }
1589 }),
1590 fasync::Task::spawn(async move {
1591 let root = fixture3.root();
1592 for _ in 0..300 {
1593 let file = open_file_checked(
1594 &root,
1595 "foo",
1596 fio::Flags::FLAG_MAYBE_CREATE
1597 | fio::PERM_READABLE
1598 | fio::PERM_WRITABLE
1599 | fio::Flags::PROTOCOL_FILE,
1600 &Default::default(),
1601 )
1602 .await;
1603 assert_eq!(
1604 file.close().await.expect("FIDL call failed").map_err(Status::from_raw),
1605 Ok(())
1606 );
1607 root.unlink("foo", &fio::UnlinkOptions::default())
1608 .await
1609 .expect("FIDL call failed")
1610 .expect("unlink failed");
1611 }
1612 done.store(true, atomic::Ordering::Relaxed);
1613 })
1614 );
1615
1616 Arc::try_unwrap(fixture).unwrap_or_else(|_| panic!()).close().await;
1617 }
1618
1619 #[fuchsia::test(threads = 10)]
1620 async fn test_get_backing_memory_shared_vmo_right_write() {
1621 let fixture = TestFixture::new().await;
1622 let root = fixture.root();
1623
1624 let file = open_file_checked(
1625 &root,
1626 "foo",
1627 fio::Flags::FLAG_MAYBE_CREATE
1628 | fio::PERM_READABLE
1629 | fio::PERM_WRITABLE
1630 | fio::Flags::PROTOCOL_FILE,
1631 &Default::default(),
1632 )
1633 .await;
1634
1635 file.resize(4096)
1636 .await
1637 .expect("resize failed")
1638 .map_err(Status::from_raw)
1639 .expect("resize error");
1640
1641 let vmo = file
1642 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1643 .await
1644 .expect("Failed to make FIDL call")
1645 .map_err(Status::from_raw)
1646 .expect("Failed to get VMO");
1647 let err = vmo.write(&[0, 1, 2, 3], 0).expect_err("VMO should not be writable");
1648 assert_eq!(Status::ACCESS_DENIED, err);
1649
1650 let vmo = file
1651 .get_backing_memory(
1652 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1653 )
1654 .await
1655 .expect("Failed to make FIDL call")
1656 .map_err(Status::from_raw)
1657 .expect("Failed to get VMO");
1658 vmo.write(&[0, 1, 2, 3], 0).expect("VMO should be writable");
1659
1660 close_file_checked(file).await;
1661 fixture.close().await;
1662 }
1663
1664 #[fuchsia::test(threads = 10)]
1665 async fn test_get_backing_memory_shared_vmo_right_read() {
1666 let fixture = TestFixture::new().await;
1667 let root = fixture.root();
1668
1669 let file = open_file_checked(
1670 &root,
1671 "foo",
1672 fio::Flags::FLAG_MAYBE_CREATE
1673 | fio::PERM_READABLE
1674 | fio::PERM_WRITABLE
1675 | fio::Flags::PROTOCOL_FILE,
1676 &Default::default(),
1677 )
1678 .await;
1679
1680 file.resize(4096)
1681 .await
1682 .expect("resize failed")
1683 .map_err(Status::from_raw)
1684 .expect("resize error");
1685
1686 let mut data = [0u8; 4];
1687 let vmo = file
1688 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER)
1689 .await
1690 .expect("Failed to make FIDL call")
1691 .map_err(Status::from_raw)
1692 .expect("Failed to get VMO");
1693 let err = vmo.read(&mut data, 0).expect_err("VMO should not be readable");
1694 assert_eq!(Status::ACCESS_DENIED, err);
1695
1696 let vmo = file
1697 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1698 .await
1699 .expect("Failed to make FIDL call")
1700 .map_err(Status::from_raw)
1701 .expect("Failed to get VMO");
1702 vmo.read(&mut data, 0).expect("VMO should be readable");
1703
1704 close_file_checked(file).await;
1705 fixture.close().await;
1706 }
1707
1708 #[fuchsia::test(threads = 10)]
1709 async fn test_get_backing_memory_shared_vmo_resize() {
1710 let fixture = TestFixture::new().await;
1711 let root = fixture.root();
1712
1713 let file = open_file_checked(
1714 &root,
1715 "foo",
1716 fio::Flags::FLAG_MAYBE_CREATE
1717 | fio::PERM_READABLE
1718 | fio::PERM_WRITABLE
1719 | fio::Flags::PROTOCOL_FILE,
1720 &Default::default(),
1721 )
1722 .await;
1723
1724 let vmo = file
1725 .get_backing_memory(
1726 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1727 )
1728 .await
1729 .expect("Failed to make FIDL call")
1730 .map_err(Status::from_raw)
1731 .expect("Failed to get VMO");
1732
1733 let err = vmo.set_size(4096).expect_err("VMO should not be resizable");
1735 assert_eq!(Status::UNAVAILABLE, err);
1736 let err =
1738 vmo.set_content_size(&10).expect_err("content size should not be directly modifiable");
1739 assert_eq!(Status::ACCESS_DENIED, err);
1740
1741 close_file_checked(file).await;
1742 fixture.close().await;
1743 }
1744
1745 #[fuchsia::test(threads = 10)]
1746 async fn test_get_backing_memory_private_vmo_resize() {
1747 let fixture = TestFixture::new().await;
1748 let root = fixture.root();
1749
1750 let file = open_file_checked(
1751 &root,
1752 "foo",
1753 fio::Flags::FLAG_MAYBE_CREATE
1754 | fio::PERM_READABLE
1755 | fio::PERM_WRITABLE
1756 | fio::Flags::PROTOCOL_FILE,
1757 &Default::default(),
1758 )
1759 .await;
1760
1761 let vmo = file
1762 .get_backing_memory(
1763 fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1764 )
1765 .await
1766 .expect("Failed to make FIDL call")
1767 .map_err(Status::from_raw)
1768 .expect("Failed to get VMO");
1769 vmo.set_size(10).expect("VMO should be resizable");
1770 vmo.set_content_size(&20).expect("content size should be modifiable");
1771 vmo.set_stream_size(20).expect("stream size should be modifiable");
1772
1773 let vmo = file
1774 .get_backing_memory(fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ)
1775 .await
1776 .expect("Failed to make FIDL call")
1777 .map_err(Status::from_raw)
1778 .expect("Failed to get VMO");
1779 let err = vmo.set_size(10).expect_err("VMO should not be resizable");
1780 assert_eq!(err, Status::ACCESS_DENIED);
1781 vmo.set_stream_size(20).expect_err("stream size is not modifiable");
1783 vmo.set_content_size(&20).expect_err("content is not modifiable");
1784
1785 close_file_checked(file).await;
1786 fixture.close().await;
1787 }
1788
1789 #[fuchsia::test(threads = 10)]
1790 async fn extended_attributes() {
1791 let fixture = TestFixture::new().await;
1792 let root = fixture.root();
1793
1794 let file = open_file_checked(
1795 &root,
1796 "foo",
1797 fio::Flags::FLAG_MAYBE_CREATE
1798 | fio::PERM_READABLE
1799 | fio::PERM_WRITABLE
1800 | fio::Flags::PROTOCOL_FILE,
1801 &Default::default(),
1802 )
1803 .await;
1804
1805 let name = b"security.selinux";
1806 let value_vec = b"bar".to_vec();
1807
1808 {
1809 let (iterator_client, iterator_server) =
1810 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1811 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1812 let (chunk, last) = iterator_client
1813 .get_next()
1814 .await
1815 .expect("Failed to make FIDL call")
1816 .expect("Failed to get next iterator chunk");
1817 assert!(last);
1818 assert_eq!(chunk, Vec::<Vec<u8>>::new());
1819 }
1820 assert_eq!(
1821 file.get_extended_attribute(name)
1822 .await
1823 .expect("Failed to make FIDL call")
1824 .expect_err("Got successful message back for missing attribute"),
1825 Status::NOT_FOUND.into_raw(),
1826 );
1827
1828 file.set_extended_attribute(
1829 name,
1830 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
1831 fio::SetExtendedAttributeMode::Set,
1832 )
1833 .await
1834 .expect("Failed to make FIDL call")
1835 .expect("Failed to set extended attribute");
1836
1837 {
1838 let (iterator_client, iterator_server) =
1839 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1840 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1841 let (chunk, last) = iterator_client
1842 .get_next()
1843 .await
1844 .expect("Failed to make FIDL call")
1845 .expect("Failed to get next iterator chunk");
1846 assert!(last);
1847 assert_eq!(chunk, vec![name]);
1848 }
1849 assert_eq!(
1850 file.get_extended_attribute(name)
1851 .await
1852 .expect("Failed to make FIDL call")
1853 .expect("Failed to get extended attribute"),
1854 fio::ExtendedAttributeValue::Bytes(value_vec)
1855 );
1856
1857 file.remove_extended_attribute(name)
1858 .await
1859 .expect("Failed to make FIDL call")
1860 .expect("Failed to remove extended attribute");
1861
1862 {
1863 let (iterator_client, iterator_server) =
1864 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1865 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1866 let (chunk, last) = iterator_client
1867 .get_next()
1868 .await
1869 .expect("Failed to make FIDL call")
1870 .expect("Failed to get next iterator chunk");
1871 assert!(last);
1872 assert_eq!(chunk, Vec::<Vec<u8>>::new());
1873 }
1874 assert_eq!(
1875 file.get_extended_attribute(name)
1876 .await
1877 .expect("Failed to make FIDL call")
1878 .expect_err("Got successful message back for missing attribute"),
1879 Status::NOT_FOUND.into_raw(),
1880 );
1881
1882 close_file_checked(file).await;
1883 fixture.close().await;
1884 }
1885
1886 #[fuchsia::test]
1887 async fn test_flush_when_closed_from_on_zero_children() {
1888 let fixture = TestFixture::new().await;
1889 let root = fixture.root();
1890
1891 let file = open_file_checked(
1892 &root,
1893 "foo",
1894 fio::Flags::FLAG_MAYBE_CREATE
1895 | fio::PERM_READABLE
1896 | fio::PERM_WRITABLE
1897 | fio::Flags::PROTOCOL_FILE,
1898 &Default::default(),
1899 )
1900 .await;
1901
1902 file.resize(50).await.expect("resize (FIDL) failed").expect("resize failed");
1903
1904 {
1905 let vmo = file
1906 .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
1907 .await
1908 .expect("get_backing_memory (FIDL) failed")
1909 .map_err(Status::from_raw)
1910 .expect("get_backing_memory failed");
1911
1912 std::mem::drop(file);
1913
1914 fasync::unblock(move || vmo.write(b"hello", 0).expect("write failed")).await;
1915 }
1916
1917 fixture.close().await;
1918 }
1919
1920 #[fuchsia::test]
1921 async fn test_background_flush() {
1922 let fixture = TestFixture::open(
1923 DeviceHolder::new(FakeDevice::new(65536, 512)),
1924 TestFixtureOptions::default(),
1925 )
1926 .await;
1927 {
1928 let root = fixture.root();
1929
1930 let file = open_file_checked(
1931 &root,
1932 "foo",
1933 fio::Flags::FLAG_MAYBE_CREATE
1934 | fio::PERM_READABLE
1935 | fio::PERM_WRITABLE
1936 | fio::Flags::PROTOCOL_FILE,
1937 &Default::default(),
1938 )
1939 .await;
1940
1941 let stream = file.describe().await.unwrap().stream.unwrap();
1942 let file_id = file
1943 .get_attributes(fio::NodeAttributesQuery::ID)
1944 .await
1945 .unwrap()
1946 .unwrap()
1947 .1
1948 .id
1949 .unwrap();
1950 let truncate_guard = fixture
1952 .fs()
1953 .truncate_guard(fixture.volume().volume().store().store_object_id(), file_id)
1954 .await;
1955
1956 let file_obj = fixture
1957 .volume()
1958 .volume()
1959 .cache()
1960 .get(file_id)
1961 .unwrap()
1962 .into_any()
1963 .downcast::<FxFile>()
1964 .unwrap();
1965 let file_clone = file_obj.clone();
1966
1967 unblock(move || {
1968 let page_size = zx::system_get_page_size() as u64;
1969 let mut offset: u64 = 0;
1970 while !file_clone
1971 .background_flush_running
1972 .load(std::sync::atomic::Ordering::Relaxed)
1973 {
1974 assert!(
1975 offset <= BACKGROUND_FLUSH_THRESHOLD * 2,
1976 "Background flush not triggering"
1977 );
1978 stream
1979 .write_at(zx::StreamWriteOptions::empty(), offset, &[0, 1, 2, 3, 4])
1980 .expect("write should succeed");
1981 offset += page_size;
1982 }
1983 })
1984 .await;
1985
1986 std::mem::drop(truncate_guard);
1988 const MAX_WAIT: Duration = Duration::from_secs(10);
1989 let wait_increments = Duration::from_millis(100);
1990 let mut total_waited = Duration::ZERO;
1991 while file_obj.background_flush_running.load(std::sync::atomic::Ordering::Relaxed) {
1992 total_waited += wait_increments;
1993 assert!(total_waited < MAX_WAIT);
1994 fasync::Timer::new(wait_increments).await;
1995 }
1996 }
1997
1998 fixture.close().await;
1999 }
2000
2001 #[fuchsia::test]
2002 async fn test_get_attributes_fsverity_enabled_file() {
2003 let fixture = TestFixture::new().await;
2004 let root = fixture.root();
2005
2006 let file = open_file_checked(
2007 &root,
2008 "foo",
2009 fio::Flags::FLAG_MAYBE_CREATE
2010 | fio::PERM_READABLE
2011 | fio::PERM_WRITABLE
2012 | fio::Flags::PROTOCOL_FILE,
2013 &Default::default(),
2014 )
2015 .await;
2016
2017 let mut data: Vec<u8> = vec![0x00u8; 1052672];
2018 rng().fill(&mut data[..]);
2019
2020 for chunk in data.chunks(8192) {
2021 file.write(chunk)
2022 .await
2023 .expect("FIDL call failed")
2024 .map_err(Status::from_raw)
2025 .expect("write failed");
2026 }
2027
2028 let tree = fsverity_merkle::from_slice(
2029 &data,
2030 FsVerityHasher::Sha256(FsVerityHasherOptions::new(vec![0xFF; 8], 4096)),
2031 );
2032 let expected_root = tree.root().to_vec();
2033
2034 let expected_descriptor = fio::VerificationOptions {
2035 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2036 salt: Some(vec![0xFF; 8]),
2037 ..Default::default()
2038 };
2039
2040 file.enable_verity(&expected_descriptor)
2041 .await
2042 .expect("FIDL transport error")
2043 .expect("enable verity failed");
2044
2045 let (_, immutable_attributes) = file
2046 .get_attributes(fio::NodeAttributesQuery::ROOT_HASH | fio::NodeAttributesQuery::OPTIONS)
2047 .await
2048 .expect("FIDL call failed")
2049 .map_err(Status::from_raw)
2050 .expect("get_attributes failed");
2051
2052 assert_eq!(
2053 immutable_attributes
2054 .options
2055 .expect("verification options not present in immutable attributes"),
2056 expected_descriptor
2057 );
2058 assert_eq!(
2059 immutable_attributes.root_hash.expect("root hash not present in immutable attributes"),
2060 expected_root
2061 );
2062
2063 fixture.close().await;
2064 }
2065
2066 #[fuchsia::test]
2069 async fn test_write_fail_fsverity_enabled_file() {
2070 let fixture = TestFixture::new().await;
2071 let root = fixture.root();
2072
2073 let file = open_file_checked(
2074 &root,
2075 "foo",
2076 fio::Flags::FLAG_MAYBE_CREATE
2077 | fio::PERM_READABLE
2078 | fio::PERM_WRITABLE
2079 | fio::Flags::PROTOCOL_FILE,
2080 &Default::default(),
2081 )
2082 .await;
2083
2084 file.write(&[8; 8192])
2085 .await
2086 .expect("FIDL call failed")
2087 .map_err(Status::from_raw)
2088 .expect("write failed");
2089
2090 let descriptor = fio::VerificationOptions {
2091 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2092 salt: Some(vec![0xFF; 8]),
2093 ..Default::default()
2094 };
2095
2096 file.enable_verity(&descriptor)
2097 .await
2098 .expect("FIDL transport error")
2099 .expect("enable verity failed");
2100
2101 async fn assert_file_is_not_writable(file: &fio::FileProxy) {
2102 file.write(&[2; 8192])
2104 .await
2105 .expect("FIDL transport error")
2106 .map_err(Status::from_raw)
2107 .expect_err("write succeeded on fsverity-enabled file");
2108 let vmo = file
2110 .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
2111 .await
2112 .expect("FIDL transport error")
2113 .map_err(Status::from_raw)
2114 .expect("get_backing_memory failed");
2115 fasync::unblock(move || {
2116 vmo.write(&[2; 8192], 0)
2117 .expect_err("write via VMO succeeded on fsverity-enabled file");
2118 })
2119 .await;
2120 file.resize(1)
2122 .await
2123 .expect("FIDL transport error")
2124 .map_err(Status::from_raw)
2125 .expect_err("resize succeeded on fsverity-enabled file");
2126 }
2127
2128 assert_file_is_not_writable(&file).await;
2129 close_file_checked(file).await;
2130
2131 let file =
2133 open_file(&root, "foo", fio::PERM_READABLE | fio::PERM_WRITABLE, &Default::default())
2134 .await
2135 .expect("failed to open fsverity-enabled file");
2136 assert_file_is_not_writable(&file).await;
2137 close_file_checked(file).await;
2138
2139 let device = fixture.close().await;
2141 device.ensure_unique();
2142 device.reopen(false);
2143 let fixture =
2144 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2145 .await;
2146
2147 let root = fixture.root();
2148 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 fixture.close().await;
2156 }
2157
2158 #[fuchsia::test]
2159 async fn test_fsverity_enabled_file_verified_reads() {
2160 let mut data: Vec<u8> = vec![0x00u8; 1052672];
2161 rng().fill(&mut data[..]);
2162 let mut num_chunks = 0;
2163
2164 let reused_device = {
2165 let fixture = TestFixture::new().await;
2166 let root = fixture.root();
2167
2168 let file = open_file_checked(
2169 &root,
2170 "foo",
2171 fio::Flags::FLAG_MAYBE_CREATE
2172 | fio::PERM_READABLE
2173 | fio::PERM_WRITABLE
2174 | fio::Flags::PROTOCOL_FILE,
2175 &Default::default(),
2176 )
2177 .await;
2178
2179 for chunk in data.chunks(fio::MAX_BUF as usize) {
2180 file.write(chunk)
2181 .await
2182 .expect("FIDL call failed")
2183 .map_err(Status::from_raw)
2184 .expect("write failed");
2185 num_chunks += 1;
2186 }
2187
2188 let descriptor = fio::VerificationOptions {
2189 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2190 salt: Some(vec![0xFF; 8]),
2191 ..Default::default()
2192 };
2193
2194 file.enable_verity(&descriptor)
2195 .await
2196 .expect("FIDL transport error")
2197 .expect("enable verity failed");
2198
2199 assert!(file.sync().await.expect("Sync failed").is_ok());
2200 close_file_checked(file).await;
2201 fixture.close().await
2202 };
2203
2204 let fixture = TestFixture::open(
2205 reused_device,
2206 TestFixtureOptions { format: false, ..Default::default() },
2207 )
2208 .await;
2209 let root = fixture.root();
2210
2211 let file = open_file_checked(
2212 &root,
2213 "foo",
2214 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2215 &Default::default(),
2216 )
2217 .await;
2218
2219 for chunk in 0..num_chunks {
2220 let buffer = file
2221 .read(fio::MAX_BUF)
2222 .await
2223 .expect("transport error on read")
2224 .expect("read failed");
2225 let start = chunk * fio::MAX_BUF as usize;
2226 assert_eq!(&buffer, &data[start..start + buffer.len()]);
2227 }
2228
2229 fixture.close().await;
2230 }
2231
2232 #[fuchsia::test]
2233 async fn test_enabling_verity_on_verified_file_fails() {
2234 let reused_device = {
2235 let fixture = TestFixture::new().await;
2236 let root = fixture.root();
2237
2238 let file = open_file_checked(
2239 &root,
2240 "foo",
2241 fio::Flags::FLAG_MAYBE_CREATE
2242 | fio::PERM_READABLE
2243 | fio::PERM_WRITABLE
2244 | fio::Flags::PROTOCOL_FILE,
2245 &Default::default(),
2246 )
2247 .await;
2248
2249 file.write(&[1; 8192])
2250 .await
2251 .expect("FIDL call failed")
2252 .map_err(Status::from_raw)
2253 .expect("write failed");
2254
2255 let descriptor = fio::VerificationOptions {
2256 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2257 salt: Some(vec![0xFF; 8]),
2258 ..Default::default()
2259 };
2260
2261 file.enable_verity(&descriptor)
2262 .await
2263 .expect("FIDL transport error")
2264 .expect("enable verity failed");
2265
2266 file.enable_verity(&descriptor)
2267 .await
2268 .expect("FIDL transport error")
2269 .expect_err("enabling verity on a verity-enabled file should fail.");
2270
2271 assert!(file.sync().await.expect("Sync failed").is_ok());
2272 close_file_checked(file).await;
2273 fixture.close().await
2274 };
2275
2276 let fixture = TestFixture::open(
2277 reused_device,
2278 TestFixtureOptions { format: false, ..Default::default() },
2279 )
2280 .await;
2281 let root = fixture.root();
2282
2283 let file = open_file_checked(
2284 &root,
2285 "foo",
2286 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2287 &Default::default(),
2288 )
2289 .await;
2290
2291 let descriptor = fio::VerificationOptions {
2292 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2293 salt: Some(vec![0xFF; 8]),
2294 ..Default::default()
2295 };
2296
2297 file.enable_verity(&descriptor)
2298 .await
2299 .expect("FIDL transport error")
2300 .expect_err("enabling verity on a verity-enabled file should fail.");
2301
2302 close_file_checked(file).await;
2303 fixture.close().await;
2304 }
2305
2306 #[fuchsia::test]
2307 async fn test_get_attributes_fsverity_not_enabled() {
2308 let fixture = TestFixture::new().await;
2309 let root = fixture.root();
2310
2311 let file = open_file_checked(
2312 &root,
2313 "foo",
2314 fio::Flags::FLAG_MAYBE_CREATE
2315 | fio::PERM_READABLE
2316 | fio::PERM_WRITABLE
2317 | fio::Flags::PROTOCOL_FILE,
2318 &Default::default(),
2319 )
2320 .await;
2321
2322 let mut data: Vec<u8> = vec![0x00u8; 8192];
2323 rng().fill(&mut data[..]);
2324
2325 file.write(&data)
2326 .await
2327 .expect("FIDL call failed")
2328 .map_err(Status::from_raw)
2329 .expect("write failed");
2330
2331 let () = file
2332 .sync()
2333 .await
2334 .expect("FIDL call failed")
2335 .map_err(Status::from_raw)
2336 .expect("sync failed");
2337
2338 let (_, immutable_attributes) = file
2339 .get_attributes(fio::NodeAttributesQuery::ROOT_HASH | fio::NodeAttributesQuery::OPTIONS)
2340 .await
2341 .expect("FIDL call failed")
2342 .map_err(Status::from_raw)
2343 .expect("get_attributes failed");
2344
2345 assert_eq!(immutable_attributes.options, None);
2346 assert_eq!(immutable_attributes.root_hash, None);
2347
2348 fixture.close().await;
2349 }
2350
2351 #[fuchsia::test]
2352 async fn test_update_attributes_also_updates_ctime() {
2353 let fixture = TestFixture::new().await;
2354 let root = fixture.root();
2355
2356 let file = open_file_checked(
2357 &root,
2358 "foo",
2359 fio::Flags::FLAG_MAYBE_CREATE
2360 | fio::PERM_READABLE
2361 | fio::PERM_WRITABLE
2362 | fio::Flags::PROTOCOL_FILE,
2363 &Default::default(),
2364 )
2365 .await;
2366
2367 file.write("hello, world!".as_bytes())
2369 .await
2370 .expect("FIDL call failed")
2371 .map_err(Status::from_raw)
2372 .expect("write failed");
2373 let (_mutable_attributes, immutable_attributes) = file
2374 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2375 .await
2376 .expect("FIDL call failed")
2377 .map_err(Status::from_raw)
2378 .expect("get_attributes failed");
2379 let ctime_after_write = immutable_attributes.change_time;
2380
2381 file.update_attributes(&fio::MutableNodeAttributes {
2383 mode: Some(111),
2384 gid: Some(222),
2385 ..Default::default()
2386 })
2387 .await
2388 .expect("FIDL call failed")
2389 .map_err(Status::from_raw)
2390 .expect("update_attributes failed");
2391 let (_mutable_attributes, immutable_attributes) = file
2392 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2393 .await
2394 .expect("FIDL call failed")
2395 .map_err(Status::from_raw)
2396 .expect("get_attributes failed");
2397 let ctime_after_update = immutable_attributes.change_time;
2398 assert!(ctime_after_update > ctime_after_write);
2399
2400 file.sync()
2402 .await
2403 .expect("FIDL call failed")
2404 .map_err(Status::from_raw)
2405 .expect("sync failed");
2406 let (_mutable_attributes, immutable_attributes) = file
2407 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2408 .await
2409 .expect("FIDL call failed")
2410 .map_err(Status::from_raw)
2411 .expect("get_attributes failed");
2412 let ctime_after_sync = immutable_attributes.change_time;
2413 assert_eq!(ctime_after_sync, ctime_after_update);
2414 fixture.close().await;
2415 }
2416
2417 #[fuchsia::test]
2418 async fn test_unnamed_temporary_file_can_read_and_write_to_it() {
2419 let fixture = TestFixture::new().await;
2420 let root = fixture.root();
2421
2422 let tmpfile = open_file_checked(
2423 &root,
2424 ".",
2425 fio::Flags::PROTOCOL_FILE
2426 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2427 | fio::PERM_READABLE
2428 | fio::PERM_WRITABLE,
2429 &fio::Options::default(),
2430 )
2431 .await;
2432
2433 let buf = vec![0xaa as u8; 8];
2434 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2435
2436 tmpfile
2437 .seek(fio::SeekOrigin::Start, 0)
2438 .await
2439 .expect("seek failed")
2440 .map_err(zx::Status::from_raw)
2441 .expect("seek error");
2442 let read_buf = file::read(&tmpfile).await.expect("read failed");
2443 assert_eq!(read_buf, buf);
2444
2445 fixture.close().await;
2446 }
2447
2448 #[fuchsia::test]
2449 async fn test_unnamed_temporary_file_get_space_back_after_closing_file() {
2450 let fixture = TestFixture::new().await;
2451 let root = fixture.root();
2452
2453 let tmpfile = open_file_checked(
2454 &root,
2455 ".",
2456 fio::Flags::PROTOCOL_FILE
2457 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2458 | fio::PERM_WRITABLE,
2459 &fio::Options::default(),
2460 )
2461 .await;
2462
2463 const BUFFER_SIZE: u64 = 1024 * 1024;
2464 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2465 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2466
2467 let info_after_writing_to_tmpfile = root
2468 .query_filesystem()
2469 .await
2470 .expect("Failed wire call to query filesystem")
2471 .1
2472 .expect("Failed to query filesystem");
2473
2474 close_file_checked(tmpfile).await;
2475
2476 for i in 1..50 {
2478 let info = root
2479 .query_filesystem()
2480 .await
2481 .expect("Failed wire call to query filesystem")
2482 .1
2483 .expect("Failed to query filesystem");
2484
2485 if info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE {
2488 break;
2489 }
2490 if i == 49 {
2491 panic!("Did not get space back from unnamed temporary file after closing it.");
2492 }
2493 }
2494
2495 fixture.close().await;
2496 }
2497
2498 #[fuchsia::test]
2499 async fn test_unnamed_temporary_file_get_space_back_after_closing_device() {
2500 const BUFFER_SIZE: u64 = 1024 * 1024;
2501
2502 let (reused_device, info_after_writing_to_tmpfile) = {
2503 let fixture = TestFixture::new().await;
2504 let root = fixture.root();
2505
2506 let tmpfile = open_file_checked(
2507 &root,
2508 ".",
2509 fio::Flags::PROTOCOL_FILE
2510 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2511 | fio::PERM_WRITABLE,
2512 &fio::Options::default(),
2513 )
2514 .await;
2515
2516 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2517 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2518
2519 let info_after_writing_to_tmpfile = root
2520 .query_filesystem()
2521 .await
2522 .expect("Failed wire call to query filesystem")
2523 .1
2524 .expect("Failed to query filesystem");
2525
2526 (fixture.close().await, info_after_writing_to_tmpfile)
2527 };
2528
2529 let fixture = TestFixture::open(
2530 reused_device,
2531 TestFixtureOptions { format: false, ..Default::default() },
2532 )
2533 .await;
2534 let root = fixture.root();
2535
2536 let info = root
2537 .query_filesystem()
2538 .await
2539 .expect("Failed wire call to query filesystem")
2540 .1
2541 .expect("Failed to query filesystem");
2542
2543 assert!(info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE);
2546
2547 fixture.close().await;
2548 }
2549
2550 #[fuchsia::test]
2551 async fn test_unnamed_temporary_file_can_link_into() {
2552 const FILE1: &str = "foo";
2553 const FILE2: &str = "bar";
2554 const BUFFER_SIZE: u64 = 1024 * 1024;
2555 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2556
2557 let reused_device = {
2558 let fixture = TestFixture::new().await;
2559 let root = fixture.root();
2560
2561 let tmpfile = open_file_checked(
2562 &root,
2563 ".",
2564 fio::Flags::PROTOCOL_FILE
2565 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2566 | fio::PERM_READABLE
2567 | fio::PERM_WRITABLE,
2568 &fio::Options::default(),
2569 )
2570 .await;
2571
2572 let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
2574 zx::Status::ok(status).expect("get_token failed");
2575 tmpfile
2576 .link_into(zx::Event::from(dst_token.unwrap()), FILE1)
2577 .await
2578 .expect("link_into wire message failed")
2579 .map_err(zx::Status::from_raw)
2580 .expect("link_into failed");
2581
2582 let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
2584 zx::Status::ok(status).expect("get_token failed");
2585 tmpfile
2586 .link_into(zx::Event::from(dst_token.unwrap()), FILE2)
2587 .await
2588 .expect("link_into wire message failed")
2589 .map_err(zx::Status::from_raw)
2590 .expect("link_into failed");
2591
2592 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2594
2595 root.unlink(FILE1, &fio::UnlinkOptions::default())
2596 .await
2597 .expect("unlink wire call failed")
2598 .map_err(zx::Status::from_raw)
2599 .expect("unlink failed");
2600 fixture.close().await
2601 };
2602
2603 let fixture = TestFixture::open(
2604 reused_device,
2605 TestFixtureOptions { format: false, ..Default::default() },
2606 )
2607 .await;
2608 let root = fixture.root();
2609
2610 assert_eq!(
2612 open_file(
2613 &root,
2614 FILE1,
2615 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2616 &fio::Options::default()
2617 )
2618 .await
2619 .expect_err("Open succeeded unexpectedly")
2620 .root_cause()
2621 .downcast_ref::<zx::Status>()
2622 .expect("No status"),
2623 &zx::Status::NOT_FOUND,
2624 );
2625
2626 let permanent_file = open_file_checked(
2629 &root,
2630 FILE2,
2631 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2632 &fio::Options::default(),
2633 )
2634 .await;
2635 permanent_file
2636 .seek(fio::SeekOrigin::Start, 0)
2637 .await
2638 .expect("seek wire message failed")
2639 .map_err(zx::Status::from_raw)
2640 .expect("seek error");
2641 let read_buf = file::read(&permanent_file).await.expect("read failed");
2642 assert!(read_buf == buf);
2643
2644 fsck(fixture.fs().clone()).await.expect("fsck failed");
2645
2646 fixture.close().await;
2647 }
2648
2649 #[fuchsia::test]
2650 async fn test_unnamed_temporary_file_in_encrypted_directory() {
2651 let fixture = TestFixture::new().await;
2652 let root = fixture.root();
2653
2654 let crypt = fixture.crypt().unwrap();
2656 let encrypted_directory = open_dir_checked(
2657 &root,
2658 "encrypted_directory",
2659 fio::Flags::FLAG_MAYBE_CREATE
2660 | fio::Flags::PROTOCOL_DIRECTORY
2661 | fio::PERM_READABLE
2662 | fio::PERM_WRITABLE,
2663 fio::Options::default(),
2664 )
2665 .await;
2666 crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2667 encrypted_directory
2668 .update_attributes(&fio::MutableNodeAttributes {
2669 wrapping_key_id: Some(WRAPPING_KEY_ID),
2670 ..Default::default()
2671 })
2672 .await
2673 .expect("update_attributes wire call failed")
2674 .map_err(zx::ok)
2675 .expect("update_attributes failed");
2676
2677 let encryped_tmpfile = open_file_checked(
2679 &encrypted_directory,
2680 ".",
2681 fio::Flags::PROTOCOL_FILE
2682 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2683 | fio::PERM_READABLE
2684 | fio::PERM_WRITABLE,
2685 &fio::Options::default(),
2686 )
2687 .await;
2688 let (mutable_attributes, _immutable_attributes) = encryped_tmpfile
2689 .get_attributes(fio::NodeAttributesQuery::WRAPPING_KEY_ID)
2690 .await
2691 .expect("get_attributes wire call failed")
2692 .map_err(zx::Status::from_raw)
2693 .expect("get_attributes failed");
2694 assert_eq!(mutable_attributes.wrapping_key_id, Some(WRAPPING_KEY_ID));
2695
2696 let (status, dst_token) = encrypted_directory.get_token().await.expect("FIDL call failed");
2699 zx::Status::ok(status).expect("get_token failed");
2700 encryped_tmpfile
2701 .link_into(zx::Event::from(dst_token.unwrap()), "foo")
2702 .await
2703 .expect("link_into wire message failed")
2704 .expect("link_into failed");
2705
2706 let unencryped_tmpfile = open_file_checked(
2707 &root,
2708 ".",
2709 fio::Flags::PROTOCOL_FILE
2710 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2711 | fio::PERM_READABLE
2712 | fio::PERM_WRITABLE,
2713 &fio::Options::default(),
2714 )
2715 .await;
2716 let (mutable_attributes, _immutable_attributes) = unencryped_tmpfile
2717 .get_attributes(fio::NodeAttributesQuery::WRAPPING_KEY_ID)
2718 .await
2719 .expect("get_attributes wire call failed")
2720 .map_err(zx::Status::from_raw)
2721 .expect("get_attributes failed");
2722 assert_eq!(mutable_attributes.wrapping_key_id, None);
2723 let (status, dst_token) = encrypted_directory.get_token().await.expect("FIDL call failed");
2724 zx::Status::ok(status).expect("get_token failed");
2725 assert_eq!(
2726 unencryped_tmpfile
2727 .link_into(zx::Event::from(dst_token.unwrap()), "bar")
2728 .await
2729 .expect("link_into wire message failed")
2730 .map_err(zx::Status::from_raw)
2731 .expect_err("link_into passed unexpectedly"),
2732 zx::Status::BAD_STATE,
2733 );
2734
2735 fixture.close().await;
2736 }
2737
2738 #[fuchsia::test]
2739 async fn test_unnamed_temporary_file_in_locked_directory() {
2740 let fixture = TestFixture::new().await;
2741 let root = fixture.root();
2742
2743 let crypt = fixture.crypt().unwrap();
2745 let encrypted_directory = open_dir_checked(
2746 &root,
2747 "encrypted_directory",
2748 fio::Flags::FLAG_MAYBE_CREATE
2749 | fio::Flags::PROTOCOL_DIRECTORY
2750 | fio::PERM_READABLE
2751 | fio::PERM_WRITABLE,
2752 fio::Options::default(),
2753 )
2754 .await;
2755 crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2756 encrypted_directory
2757 .update_attributes(&fio::MutableNodeAttributes {
2758 wrapping_key_id: Some(WRAPPING_KEY_ID),
2759 ..Default::default()
2760 })
2761 .await
2762 .expect("update_attributes wire call failed")
2763 .map_err(zx::ok)
2764 .expect("update_attributes failed");
2765
2766 crypt.forget_wrapping_key(&WRAPPING_KEY_ID).unwrap();
2768
2769 assert_eq!(
2771 open_file(
2772 &encrypted_directory,
2773 ".",
2774 fio::Flags::PROTOCOL_FILE
2775 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2776 | fio::PERM_READABLE
2777 | fio::PERM_WRITABLE,
2778 &fio::Options::default()
2779 )
2780 .await
2781 .expect_err("Open succeeded unexpectedly")
2782 .root_cause()
2783 .downcast_ref::<zx::Status>()
2784 .expect("No status"),
2785 &zx::Status::UNAVAILABLE,
2786 );
2787 fixture.close().await;
2788 }
2789
2790 #[fuchsia::test]
2791 async fn test_unnamed_temporary_file_link_into_with_race() {
2792 let fixture = TestFixture::new().await;
2793 let root = fixture.root();
2794
2795 for i in 1..100 {
2796 let tmpfile = open_file_checked(
2797 &root,
2798 ".",
2799 fio::Flags::PROTOCOL_FILE
2800 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2801 | fio::PERM_READABLE
2802 | fio::PERM_WRITABLE,
2803 &fio::Options::default(),
2804 )
2805 .await;
2806
2807 let (tmpfile_clone1, tmpfile_server1) =
2809 fidl::endpoints::create_proxy::<fio::FileMarker>();
2810 tmpfile.clone(tmpfile_server1.into_channel().into()).expect("clone failed");
2811 let (tmpfile_clone2, tmpfile_server2) =
2812 fidl::endpoints::create_proxy::<fio::FileMarker>();
2813 tmpfile.clone(tmpfile_server2.into_channel().into()).expect("clone failed");
2814
2815 let sub_dir = open_dir_checked(
2818 &root,
2819 "A",
2820 fio::Flags::PROTOCOL_DIRECTORY
2821 | fio::PERM_READABLE
2822 | fio::PERM_WRITABLE
2823 | fio::Flags::FLAG_MAYBE_CREATE,
2824 fio::Options::default(),
2825 )
2826 .await;
2827
2828 let (status, dst_token1) = sub_dir.get_token().await.expect("FIDL call failed");
2830 zx::Status::ok(status).expect("get_token failed");
2831 let (status, dst_token2) = sub_dir.get_token().await.expect("FIDL call failed");
2832 zx::Status::ok(status).expect("get_token failed");
2833
2834 join!(
2835 fasync::Task::spawn(async move {
2836 tmpfile_clone1
2837 .link_into(zx::Event::from(dst_token1.unwrap()), &(2 * i).to_string())
2838 .await
2839 .expect("link_into wire message failed")
2840 .expect("link_into failed");
2841 }),
2842 fasync::Task::spawn(async move {
2843 tmpfile_clone2
2844 .link_into(zx::Event::from(dst_token2.unwrap()), &(2 * i + 1).to_string())
2845 .await
2846 .expect("link_into wire message failed")
2847 .expect("link_into failed");
2848 })
2849 );
2850 let (_, immutable_attributes) = tmpfile
2851 .get_attributes(fio::NodeAttributesQuery::LINK_COUNT)
2852 .await
2853 .expect("Failed get_attributes wire call")
2854 .expect("get_attributes failed");
2855 assert_eq!(immutable_attributes.link_count.unwrap(), 2);
2856 close_file_checked(tmpfile).await;
2857 }
2858 fixture.close().await;
2859 }
2860
2861 #[fuchsia::test]
2862 async fn test_update_attributes_persists() {
2863 const FILE: &str = "foo";
2864 let mtime = Some(Timestamp::now().as_nanos());
2865 let atime = Some(Timestamp::now().as_nanos());
2866 let mode = Some(111);
2867
2868 let device = {
2869 let fixture = TestFixture::new().await;
2870 let root = fixture.root();
2871
2872 let file = open_file_checked(
2873 &root,
2874 FILE,
2875 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_FILE,
2876 &fio::Options::default(),
2877 )
2878 .await;
2879
2880 file.update_attributes(&fio::MutableNodeAttributes {
2881 modification_time: mtime,
2882 access_time: atime,
2883 mode: Some(111),
2884 ..Default::default()
2885 })
2886 .await
2887 .expect("update_attributes FIDL call failed")
2888 .map_err(zx::ok)
2889 .expect("update_attributes failed");
2890
2891 fixture.close().await
2893 };
2894
2895 let fixture =
2896 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2897 .await;
2898 let root = fixture.root();
2899 let file = open_file_checked(
2900 &root,
2901 FILE,
2902 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2903 &fio::Options::default(),
2904 )
2905 .await;
2906
2907 let (mutable_attributes, _immutable_attributes) = file
2908 .get_attributes(
2909 fio::NodeAttributesQuery::MODIFICATION_TIME
2910 | fio::NodeAttributesQuery::ACCESS_TIME
2911 | fio::NodeAttributesQuery::MODE,
2912 )
2913 .await
2914 .expect("update_attributesFIDL call failed")
2915 .map_err(zx::ok)
2916 .expect("get_attributes failed");
2917 assert_eq!(mutable_attributes.modification_time, mtime);
2918 assert_eq!(mutable_attributes.access_time, atime);
2919 assert_eq!(mutable_attributes.mode, mode);
2920 fixture.close().await;
2921 }
2922
2923 #[fuchsia::test]
2924 async fn test_atime_from_pending_access_time_update_request() {
2925 const FILE: &str = "foo";
2926
2927 let (device, expected_atime, expected_ctime) = {
2928 let fixture = TestFixture::new().await;
2929 let root = fixture.root();
2930
2931 let file = open_file_checked(
2932 &root,
2933 FILE,
2934 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_FILE,
2935 &fio::Options::default(),
2936 )
2937 .await;
2938
2939 let (mutable_attributes, immutable_attributes) = file
2940 .get_attributes(
2941 fio::NodeAttributesQuery::CHANGE_TIME
2942 | fio::NodeAttributesQuery::ACCESS_TIME
2943 | fio::NodeAttributesQuery::MODIFICATION_TIME,
2944 )
2945 .await
2946 .expect("update_attributes FIDL call failed")
2947 .map_err(zx::ok)
2948 .expect("get_attributes failed");
2949 let initial_ctime = immutable_attributes.change_time;
2950 let initial_atime = mutable_attributes.access_time;
2951 assert_eq!(initial_atime, initial_ctime);
2953 assert_eq!(initial_atime, mutable_attributes.modification_time);
2954
2955 let (mutable_attributes, immutable_attributes) = file
2959 .get_attributes(
2960 fio::NodeAttributesQuery::CHANGE_TIME
2961 | fio::NodeAttributesQuery::ACCESS_TIME
2962 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
2963 )
2964 .await
2965 .expect("update_attributes FIDL call failed")
2966 .map_err(zx::ok)
2967 .expect("get_attributes failed");
2968 assert!(initial_atime < mutable_attributes.access_time);
2970 let updated_atime = mutable_attributes.access_time;
2971 assert_eq!(initial_ctime, immutable_attributes.change_time);
2974
2975 let (mutable_attributes, _) = file
2976 .get_attributes(
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_eq!(updated_atime, mutable_attributes.access_time);
2986
2987 (fixture.close().await, mutable_attributes.access_time, initial_ctime)
2988 };
2989
2990 let fixture =
2991 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2992 .await;
2993 let root = fixture.root();
2994 let file = open_file_checked(
2995 &root,
2996 FILE,
2997 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2998 &fio::Options::default(),
2999 )
3000 .await;
3001
3002 let (mutable_attributes, immutable_attributes) = file
3004 .get_attributes(
3005 fio::NodeAttributesQuery::CHANGE_TIME | fio::NodeAttributesQuery::ACCESS_TIME,
3006 )
3007 .await
3008 .expect("update_attributesFIDL call failed")
3009 .map_err(zx::ok)
3010 .expect("get_attributes failed");
3011
3012 assert_eq!(immutable_attributes.change_time, expected_ctime);
3013 assert_eq!(mutable_attributes.access_time, expected_atime);
3014 fixture.close().await;
3015 }
3016
3017 #[fuchsia::test(threads = 10)]
3018 async fn test_delete_with_dirty_bytes_and_no_open_handles() {
3019 let fixture = TestFixture::new().await;
3020 let root = fixture.root();
3021
3022 let file = open_file_checked(
3023 &root,
3024 "foo",
3025 fio::Flags::FLAG_MAYBE_CREATE
3026 | fio::PERM_READABLE
3027 | fio::PERM_WRITABLE
3028 | fio::Flags::PROTOCOL_FILE,
3029 &Default::default(),
3030 )
3031 .await;
3032
3033 file.resize(4096)
3034 .await
3035 .expect("resize failed")
3036 .map_err(Status::from_raw)
3037 .expect("resize error");
3038
3039 let vmo = file
3040 .get_backing_memory(
3041 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
3042 )
3043 .await
3044 .expect("Failed to make FIDL call")
3045 .map_err(Status::from_raw)
3046 .expect("Failed to get VMO");
3047
3048 file.sync().await.unwrap().unwrap();
3050
3051 drop(file);
3053
3054 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3056
3057 vmo.write(&[1, 2, 3, 4], 0).expect("vmo write failed");
3059
3060 drop(vmo);
3063
3064 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3066
3067 root.unlink("foo", &fio::UnlinkOptions::default())
3069 .await
3070 .expect("unlink failed")
3071 .map_err(Status::from_raw)
3072 .expect("unlink error");
3073
3074 fixture.close().await;
3075 }
3076
3077 #[fuchsia::test]
3081 async fn test_close_file_before_writing_to_stream() {
3082 const FILE_NAME: &str = "foo";
3083
3084 let fixture = TestFixture::new().await;
3085 let root = fixture.root();
3086 let file = open_file_checked(
3087 &root,
3088 FILE_NAME,
3089 fio::Flags::FLAG_MAYBE_CREATE
3090 | fio::PERM_READABLE
3091 | fio::PERM_WRITABLE
3092 | fio::Flags::PROTOCOL_FILE,
3093 &Default::default(),
3094 )
3095 .await;
3096
3097 let stream = file.describe().await.unwrap().stream.unwrap();
3098
3099 close_file_checked(file).await;
3100
3101 unblock(move || {
3102 stream
3103 .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3104 .expect_err("Write should get BAD_STATE");
3105 })
3106 .await;
3107
3108 fasync::Timer::new(Duration::from_millis(100)).await;
3111
3112 root.unlink(FILE_NAME, &fio::UnlinkOptions::default())
3114 .await
3115 .expect("unlink wire call failed")
3116 .expect("unlink failed");
3117
3118 fixture.close().await;
3119 }
3120
3121 #[fuchsia::test]
3127 async fn test_close_and_reopen_file_stream() {
3128 const FILE_NAME: &str = "foo";
3129
3130 let fixture = TestFixture::new().await;
3131 let root = fixture.root();
3132 let file = open_file_checked(
3133 &root,
3134 FILE_NAME,
3135 fio::Flags::FLAG_MAYBE_CREATE
3136 | fio::PERM_READABLE
3137 | fio::PERM_WRITABLE
3138 | fio::Flags::PROTOCOL_FILE,
3139 &Default::default(),
3140 )
3141 .await;
3142
3143 let page_size = zx::system_get_page_size() as u64;
3144
3145 file.resize(8 * page_size)
3146 .await
3147 .expect("resize failed")
3148 .map_err(Status::from_raw)
3149 .expect("resize error");
3150
3151 let stream1 = file.describe().await.unwrap().stream.unwrap();
3152 let stream1_dup = stream1.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3153
3154 close_file_checked(file).await;
3155
3156 let stream1_dup_clone = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3160 unblock(move || {
3161 stream1_dup_clone
3162 .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3163 .expect_err("Write should get BAD_STATE");
3164 })
3165 .await;
3166
3167 let file2 = open_file_checked(
3170 &root,
3171 FILE_NAME,
3172 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE | fio::PERM_WRITABLE,
3173 &Default::default(),
3174 )
3175 .await;
3176
3177 let stream2 = file2.describe().await.unwrap().stream.unwrap();
3178
3179 let stream1_dup_clone2 = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3183 let stream2_clone = stream2.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3184 unblock(move || {
3185 stream1_dup_clone2
3186 .write_at(zx::StreamWriteOptions::empty(), 1 * page_size, &[5, 6, 7, 8])
3187 .expect("Write on re-opened stream 1 dup should succeed");
3188 stream2_clone
3189 .write_at(zx::StreamWriteOptions::empty(), 2 * page_size, &[9, 10, 11, 12])
3190 .expect("Write on new stream 2 should succeed");
3191 })
3192 .await;
3193
3194 close_file_checked(file2).await;
3196
3197 unblock(move || {
3201 stream1_dup
3202 .write_at(zx::StreamWriteOptions::empty(), 3 * page_size, &[13, 14, 15, 16])
3203 .expect_err("Write on stream 1 dup should fail after final close");
3204 stream2
3205 .write_at(zx::StreamWriteOptions::empty(), 4 * page_size, &[17, 18, 19, 20])
3206 .expect_err("Write on stream 2 should fail after final close");
3207 })
3208 .await;
3209
3210 fixture.close().await;
3211 }
3212}