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