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