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