1use crate::fuchsia::directory::FxDirectory;
6use crate::fuchsia::errors::map_to_status;
7use crate::fuchsia::node::{FxNode, OpenedNode};
8use crate::fuchsia::paged_object_handle::{BACKGROUND_FLUSH_THRESHOLD, PagedObjectHandle};
9use crate::fuchsia::pager::{
10 MarkDirtyRange, PageInRange, PagerBacked, PagerPacketReceiverRegistration, default_page_in,
11};
12use crate::fuchsia::volume::{FxVolume, READ_AHEAD_SIZE};
13use anyhow::Error;
14use fidl_fuchsia_io as fio;
15use fxfs::filesystem::{MAX_FILE_SIZE, SyncOptions};
16use fxfs::future_with_guard::FutureWithGuard;
17use fxfs::log::*;
18use fxfs::object_handle::{ObjectHandle, ReadObjectHandle};
19use fxfs::object_store::data_object_handle::OverwriteOptions;
20use fxfs::object_store::object_record::EncryptionKey;
21use fxfs::object_store::transaction::{LockKey, Options, lock_keys};
22use fxfs::object_store::{DataObjectHandle, FSCRYPT_KEY_ID, ObjectDescriptor};
23use fxfs_crypto::WrappingKeyId;
24use fxfs_macros::ToWeakNode;
25use fxfs_trace::{TraceFutureExt, trace_future_args};
26use std::fmt::{Debug, Formatter};
27use std::ops::Range;
28use std::sync::Arc;
29use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
30use storage_device::buffer;
31use storage_units::BlockSize;
32use vfs::directory::entry::{EntryInfo, GetEntryInfo};
33use vfs::directory::entry_container::MutableDirectory;
34use vfs::execution_scope::ExecutionScope;
35use vfs::file::{File, FileOptions, GetVmo, StreamIoConnection, SyncMode};
36use vfs::name::Name;
37use vfs::{ObjectRequestRef, ProtocolsExt, attributes};
38use zx::Status;
39
40const TO_BE_PURGED: u64 = 1 << (u64::BITS - 1);
49
50const IS_TEMPORARILY_IN_GRAVEYARD: u64 = 1 << (u64::BITS - 2);
59
60const IS_DIRTY: u64 = 1 << (u64::BITS - 3);
63
64const IS_UNNAMED_TEMPORARY: u64 = IS_TEMPORARILY_IN_GRAVEYARD | TO_BE_PURGED;
67
68const MAX_OPEN_COUNTS: u64 = IS_DIRTY - 1;
71
72#[derive(Clone, Copy)]
73struct State(u64);
74
75impl Debug for State {
76 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
77 f.debug_struct("State")
78 .field("open_count", &self.open_count())
79 .field("to_be_purged", &self.to_be_purged())
80 .field("is_temporarily_in_graveyard", &self.is_temporarily_in_graveyard())
81 .field("is_dirty", &self.is_dirty())
82 .finish()
83 }
84}
85
86impl State {
87 fn open_count(&self) -> u64 {
88 self.0 & MAX_OPEN_COUNTS
89 }
90
91 fn to_be_purged(&self) -> bool {
92 self.0 & TO_BE_PURGED != 0
93 }
94
95 fn is_temporarily_in_graveyard(&self) -> bool {
96 self.0 & IS_TEMPORARILY_IN_GRAVEYARD != 0
97 }
98
99 fn is_unnamed_temporary(&self) -> bool {
100 self.0 & IS_UNNAMED_TEMPORARY == IS_UNNAMED_TEMPORARY
101 }
102
103 fn will_be_tombstoned(&self) -> bool {
104 self.to_be_purged() && self.open_count() == 0
105 }
106
107 fn is_dirty(&self) -> bool {
108 self.0 & IS_DIRTY != 0
109 }
110}
111
112#[derive(Clone, Copy, Debug, Default, PartialEq)]
114pub enum FlushType {
115 #[default]
118 Sync,
119
120 LastChance,
123
124 Background,
127}
128
129#[derive(ToWeakNode)]
131pub struct FxFile {
132 handle: PagedObjectHandle,
133 state: AtomicU64,
134 pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
135 background_flush_running: AtomicBool,
136}
137
138#[fxfs_trace::trace]
139impl FxFile {
140 pub fn new(handle: DataObjectHandle<FxVolume>) -> Arc<Self> {
142 let size = handle.get_size();
143 Arc::new_cyclic(|weak| {
144 let (vmo, pager_packet_receiver_registration) = handle
145 .owner()
146 .pager()
147 .create_vmo(
148 weak.clone(),
149 size,
150 zx::VmoOptions::UNBOUNDED | zx::VmoOptions::TRAP_DIRTY,
151 )
152 .unwrap();
153 vmo.set_name(&zx::Name::new("fxfs-file").unwrap()).unwrap();
154 Self {
155 handle: PagedObjectHandle::new(handle, vmo),
156 state: AtomicU64::new(0),
157 pager_packet_receiver_registration,
158 background_flush_running: AtomicBool::new(false),
159 }
160 })
161 }
162
163 async fn prepare_connection(this: &OpenedNode<FxFile>, flags: &impl ProtocolsExt) {
164 {
165 let mut guard = this.pager().recorder();
166 if let Some(recorder) = &mut (*guard) {
167 let _ = recorder.record_open((**this).clone() as Arc<dyn FxNode>);
168 }
169 }
170 if let Some(rights) = flags.rights() {
171 if rights.intersects(fio::Operations::READ_BYTES | fio::Operations::WRITE_BYTES) {
172 if let Some(fut) = this.handle.pre_fetch_keys() {
173 let store = this.handle.owner().store();
174 let fs = store.filesystem();
175 let read_lock = fs
177 .clone()
178 .lock_manager()
179 .read_lock(lock_keys!(LockKey::object(
180 store.store_object_id(),
181 this.object_id()
182 )))
183 .await
184 .into_owned(fs);
185 this.handle.owner().scope().spawn(
186 FutureWithGuard::new(read_lock, fut)
187 .trace(trace_future_args!("FxFile::pre_fetch_keys")),
188 );
189 }
190 }
191 }
192 }
193
194 pub async fn create_connection_async(
196 this: OpenedNode<FxFile>,
197 scope: ExecutionScope,
198 flags: impl ProtocolsExt,
199 object_request: ObjectRequestRef<'_>,
200 ) -> Result<(), zx::Status> {
201 Self::prepare_connection(&this, &flags).await;
202 object_request
203 .create_connection::<StreamIoConnection<_>, _>(scope, this.take(), flags)
204 .await
205 }
206
207 pub async fn create_connection_with_stream_async(
209 this: OpenedNode<FxFile>,
210 scope: ExecutionScope,
211 flags: impl ProtocolsExt,
212 object_request: ObjectRequestRef<'_>,
213 stream: zx::Stream,
214 ) -> Result<(), zx::Status> {
215 Self::prepare_connection(&this, &flags).await;
216 StreamIoConnection::create_with_stream(scope, this.take(), stream, flags, object_request)
217 .await
218 }
219
220 pub fn open_as_temporary(self: Arc<Self>) -> OpenedNode<dyn FxNode> {
223 assert_eq!(self.state.swap(1 | IS_UNNAMED_TEMPORARY, Ordering::Relaxed), 0);
224 OpenedNode(self)
225 }
226
227 pub fn mark_as_permanent(&self) {
229 assert!(
230 State(self.state.fetch_and(!IS_UNNAMED_TEMPORARY, Ordering::Relaxed))
231 .is_unnamed_temporary()
232 );
233 }
234
235 pub fn is_verified_file(&self) -> bool {
236 self.handle.uncached_handle().is_verified_file()
237 }
238
239 pub fn handle(&self) -> &PagedObjectHandle {
240 &self.handle
241 }
242
243 pub fn into_opened_node(self: Arc<Self>) -> Option<OpenedNode<FxFile>> {
246 self.increment_open_count().then(|| OpenedNode(self))
247 }
248
249 #[trace]
255 pub async fn flush(this: &OpenedNode<FxFile>, flush_type: FlushType) -> Result<(), Error> {
256 this.handle.flush(flush_type).await.map(|_| ())
257 }
258
259 pub fn get_block_size(&self) -> BlockSize {
260 self.handle.block_size()
261 }
262
263 pub async fn is_allocated(&self, start_offset: u64) -> Result<(bool, u64), Status> {
264 self.handle.uncached_handle().is_allocated(start_offset).await.map_err(map_to_status)
265 }
266
267 pub async fn write_at_uncached(&self, offset: u64, content: &[u8]) -> Result<u64, Status> {
276 let mut buf = self.handle.uncached_handle().allocate_buffer(content.len()).await;
277 buf.copy_from_slice(content);
278 let _ = self
279 .handle
280 .uncached_handle()
281 .overwrite(
282 offset,
283 buf.as_mut(),
284 OverwriteOptions { allow_allocations: true, ..Default::default() },
285 )
286 .await
287 .map_err(map_to_status)?;
288 Ok(content.len() as u64)
289 }
290
291 pub async fn read_at_uncached(
300 &self,
301 offset: u64,
302 length: usize,
303 ) -> Result<buffer::Buffer<'_>, Status> {
304 let block_size = self.get_block_size();
305 if !block_size.is_aligned(offset) || !block_size.is_aligned(length as u64) {
306 return Err(Status::INVALID_ARGS);
307 }
308 let end = offset.checked_add(length as u64).ok_or(Status::INVALID_ARGS)?;
309 self.handle.read_uncached(offset..end).await.map_err(map_to_status)
310 }
311
312 pub fn get_size_uncached(&self) -> u64 {
313 self.handle.uncached_handle().get_size()
314 }
315
316 async fn fscrypt_wrapping_key_id(&self) -> Result<Option<WrappingKeyId>, zx::Status> {
317 Ok(self.fscrypt_policy().await?.map(|p| p.key_identifier))
318 }
319
320 async fn fscrypt_policy(&self) -> Result<Option<fio::FscryptPolicy>, zx::Status> {
321 if self.handle.store().is_encrypted() {
322 if let Some(key) = self
323 .handle
324 .store()
325 .get_keys(self.object_id())
326 .await
327 .map_err(map_to_status)?
328 .get(FSCRYPT_KEY_ID)
329 {
330 match key {
331 EncryptionKey::Fxfs(fxfs_key) => {
332 return Ok(Some(fio::FscryptPolicy {
333 key_identifier: fxfs_key.wrapping_key_id,
334 flags: fio::FscryptPolicyFlags::PAD_16,
335 }));
336 }
337 EncryptionKey::FscryptInoLblk32File { key_identifier } => {
338 return Ok(Some(fio::FscryptPolicy {
339 key_identifier: *key_identifier,
340 flags: fio::FscryptPolicyFlags::PAD_16
341 | fio::FscryptPolicyFlags::IV_INO_LBLK_32,
342 }));
343 }
344 EncryptionKey::FscryptInoLblk64File { key_identifier } => {
345 return Ok(Some(fio::FscryptPolicy {
346 key_identifier: *key_identifier,
347 flags: fio::FscryptPolicyFlags::PAD_16
348 | fio::FscryptPolicyFlags::IV_INO_LBLK_64,
349 }));
350 }
351 EncryptionKey::FscryptInoLblk32Dir { .. }
352 | EncryptionKey::FscryptInoLblk64Dir { .. } => {
353 error!("Unexpected key type for file: {:?}", key);
354 return Ok(None);
355 }
356 EncryptionKey::LegacyFxfs(_) => unreachable!(),
357 }
358 }
359 }
360 Ok(None)
361 }
362
363 pub fn force_clean(&self) {
365 let old = State(self.state.fetch_and(!IS_DIRTY, Ordering::Relaxed));
366 if old.is_dirty() {
367 if self.handle.needs_flush() {
368 warn!("File {} was forcibly marked clean; data may be lost", self.object_id(),);
369 self.handle.forget_dirty_pages();
370 }
371 unsafe {
373 let _ = Arc::from_raw(self);
374 }
375 }
376 }
377
378 #[must_use]
380 fn increment_open_count(&self) -> bool {
381 let mut old = self.load_state();
382 loop {
383 if old.will_be_tombstoned() {
384 return false;
385 }
386
387 assert!(old.open_count() < MAX_OPEN_COUNTS);
388
389 match self.state.compare_exchange_weak(
390 old.0,
391 old.0 + 1,
392 Ordering::Relaxed,
393 Ordering::Relaxed,
394 ) {
395 Ok(_) => return true,
396 Err(new_value) => old.0 = new_value,
397 }
398 }
399 }
400
401 fn load_state(&self) -> State {
402 State(self.state.load(Ordering::Relaxed))
403 }
404
405 fn update_state(self: &Arc<Self>, callback: impl Fn(State) -> State) {
408 let mut old = self.load_state();
409 loop {
410 let mut new = callback(old);
411 if new.will_be_tombstoned() {
412 new.0 &= !IS_DIRTY;
415 }
416 match self.state.compare_exchange_weak(
417 old.0,
418 new.0,
419 Ordering::Relaxed,
420 Ordering::Relaxed,
421 ) {
422 Ok(_) => {
423 if !old.is_dirty() && new.is_dirty() {
424 let _ = Arc::into_raw(self.clone());
431 } else if old.is_dirty() && !new.is_dirty() {
432 unsafe {
434 let _ = Arc::from_raw(Arc::as_ptr(&self));
435 }
436 }
437 if new.will_be_tombstoned() {
438 self.handle.forget_dirty_pages();
443 let store = self.handle.store();
444 store
445 .filesystem()
446 .queue_tombstone_object(store.store_object_id(), self.object_id());
447 }
448 return;
449 }
450 Err(v) => old.0 = v,
451 }
452 }
453 }
454}
455
456impl OpenedNode<FxFile> {
457 pub fn vmo(&self) -> &zx::Vmo {
458 self.0.vmo()
459 }
460}
461
462impl Drop for FxFile {
463 fn drop(&mut self) {
464 let volume = self.handle.owner();
465 volume.cache().remove(self);
466 }
467}
468
469impl FxNode for FxFile {
470 fn object_id(&self) -> u64 {
471 self.handle.object_id()
472 }
473
474 fn parent(&self) -> Option<Arc<FxDirectory>> {
475 unreachable!(); }
477
478 fn set_parent(&self, _parent: Arc<FxDirectory>) {
479 }
481
482 fn open_count_add_one(&self) {
483 assert!(self.increment_open_count());
484 }
485
486 fn open_count_sub_one(self: Arc<Self>) {
487 self.update_state(|old| {
488 let mut new = State(old.0 - 1);
489
490 if new.open_count() == 0 && !new.to_be_purged() {
493 if self.handle.needs_flush() {
494 new.0 |= IS_DIRTY;
495 } else {
496 new.0 &= !IS_DIRTY;
497 }
498 }
499
500 new
501 });
502 }
503
504 fn object_descriptor(&self) -> ObjectDescriptor {
505 ObjectDescriptor::File
506 }
507
508 fn terminate(&self) {
509 self.pager_packet_receiver_registration.stop_watching_for_zero_children();
510 }
511
512 fn mark_to_be_purged(self: Arc<Self>) {
513 self.update_state(|old| State(old.0 | TO_BE_PURGED));
514 }
515}
516
517impl GetEntryInfo for FxFile {
518 fn entry_info(&self) -> EntryInfo {
519 EntryInfo::new(self.object_id(), fio::DirentType::File)
520 }
521}
522
523impl vfs::node::Node for FxFile {
524 async fn get_attributes(
525 &self,
526 requested_attributes: fio::NodeAttributesQuery,
527 ) -> Result<fio::NodeAttributes2, zx::Status> {
528 let needs_props = requested_attributes.intersects(
529 !(fio::NodeAttributesQuery::PROTOCOLS
530 | fio::NodeAttributesQuery::ABILITIES
531 | fio::NodeAttributesQuery::ID),
532 );
533 let mut props = if needs_props {
534 Some(self.handle.get_properties().await.map_err(map_to_status)?)
535 } else {
536 None
537 };
538
539 let to_be_purged = self.load_state().to_be_purged();
545 let link_count =
546 props.as_ref().map(|p| if to_be_purged && p.refs == 1 { 0 } else { p.refs });
547
548 if requested_attributes.contains(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE) {
549 self.handle
550 .store()
551 .update_access_time(self.handle.object_id(), props.as_mut().unwrap(), || true)
552 .await
553 .map_err(map_to_status)?;
554 }
555
556 let (verification_options, root_hash) = if requested_attributes.intersects(
557 fio::NodeAttributesQuery::OPTIONS.union(fio::NodeAttributesQuery::ROOT_HASH),
558 ) {
559 self.handle.uncached_handle().get_descriptor().unzip()
560 } else {
561 (None, None)
562 };
563
564 let mut abilities = fio::Operations::GET_ATTRIBUTES
565 | fio::Operations::UPDATE_ATTRIBUTES
566 | fio::Operations::READ_BYTES;
567 if !self.is_verified_file() && !self.handle.is_read_only() {
568 abilities |= fio::Operations::WRITE_BYTES;
569 }
570
571 Ok(attributes!(
572 requested_attributes,
573 Mutable {
574 creation_time: props.as_ref().map(|p| p.creation_time.as_nanos()),
575 modification_time: props.as_ref().map(|p| p.modification_time.as_nanos()),
576 access_time: props.as_ref().map(|p| p.access_time.as_nanos()),
577 mode: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.mode)),
578 uid: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.uid)),
579 gid: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.gid)),
580 rdev: props.as_ref().and_then(|p| p.posix_attributes.map(|a| a.rdev)),
581 selinux_context: self
582 .handle
583 .uncached_handle()
584 .get_inline_selinux_context()
585 .await
586 .map_err(map_to_status)?,
587 encryption_policy: self.fscrypt_policy().await?,
588 },
589 Immutable {
590 protocols: fio::NodeProtocolKinds::FILE,
591 abilities: abilities,
592 content_size: self.handle.get_size(),
593 storage_size: props.as_ref().map(|p| p.allocated_size),
594 link_count: link_count,
595 id: self.handle.object_id(),
596 change_time: props.as_ref().map(|p| p.change_time.as_nanos()),
597 options: verification_options,
598 root_hash: root_hash,
599 verity_enabled: self.is_verified_file(),
600 }
601 ))
602 }
603
604 fn will_clone(&self) {
605 self.open_count_add_one();
606 }
607
608 fn close(self: Arc<Self>) {
609 self.open_count_sub_one();
610 }
611
612 async fn link_into(
613 self: Arc<Self>,
614 destination_dir: Arc<dyn MutableDirectory>,
615 name: Name,
616 ) -> Result<(), zx::Status> {
617 let dir = destination_dir.into_any().downcast::<FxDirectory>().unwrap();
618 let store = self.handle.store();
619 let object_id = self.object_id();
620 let transaction = store
621 .new_transaction(
622 lock_keys![
623 LockKey::object(store.store_object_id(), object_id),
624 LockKey::object(store.store_object_id(), dir.object_id()),
625 ],
626 Options::default(),
627 )
628 .await
629 .map_err(map_to_status)?;
630
631 dir.check_fscrypt_policy_equivalence(self.fscrypt_wrapping_key_id().await?)?;
632
633 let state = self.load_state();
634 let is_unnamed_temporary = state.is_unnamed_temporary();
635 let to_be_purged = state.to_be_purged();
636 if is_unnamed_temporary {
637 dir.link_graveyard_object(transaction, &name, object_id, ObjectDescriptor::File, || {
639 self.mark_as_permanent()
640 })
641 .await
642 } else {
643 if to_be_purged {
645 return Err(zx::Status::NOT_FOUND);
646 }
647 dir.link_object(transaction, &name, object_id, ObjectDescriptor::File).await
648 }
649 }
650
651 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
652 Ok(self.handle.owner().filesystem_info_for_volume())
653 }
654
655 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, Status> {
656 self.handle.store_handle().list_extended_attributes().await.map_err(map_to_status)
657 }
658
659 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, Status> {
660 self.handle.store_handle().get_extended_attribute(name).await.map_err(map_to_status)
661 }
662
663 async fn set_extended_attribute(
664 &self,
665 name: Vec<u8>,
666 value: Vec<u8>,
667 mode: fio::SetExtendedAttributeMode,
668 ) -> Result<(), Status> {
669 self.handle
670 .store_handle()
671 .set_extended_attribute(name, value, mode.into())
672 .await
673 .map_err(map_to_status)
674 }
675
676 async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
677 self.handle.store_handle().remove_extended_attribute(name).await.map_err(map_to_status)
678 }
679}
680
681impl File for FxFile {
682 fn writable(&self) -> bool {
683 true
684 }
685
686 async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
687 Ok(())
688 }
689
690 async fn truncate(&self, length: u64) -> Result<(), Status> {
691 self.handle.truncate(length).await.map_err(map_to_status)?;
692 Ok(())
693 }
694
695 async fn enable_verity(&self, options: fio::VerificationOptions) -> Result<(), Status> {
696 self.handle.set_read_only();
701 self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
702 self.handle.uncached_handle().enable_verity(options).await.map_err(map_to_status)
703 }
704
705 async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
707 if flags.contains(fio::VmoFlags::EXECUTE) {
709 error!("get_backing_memory does not support execute rights!");
710 return Err(Status::NOT_SUPPORTED);
711 }
712
713 if flags.contains(fio::VmoFlags::WRITE)
718 && !flags.contains(fio::VmoFlags::PRIVATE_CLONE)
719 && (self.is_verified_file() || self.handle.is_read_only())
720 {
721 return Err(Status::ACCESS_DENIED);
722 }
723
724 let vmo = self.handle.vmo();
725 let mut rights = zx::Rights::BASIC | zx::Rights::MAP | zx::Rights::GET_PROPERTY;
726 if flags.contains(fio::VmoFlags::READ) {
727 rights |= zx::Rights::READ;
728 }
729 if flags.contains(fio::VmoFlags::WRITE) {
730 rights |= zx::Rights::WRITE;
731 }
732
733 let child_vmo = if flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
734 rights |= zx::Rights::SET_PROPERTY;
736 let mut child_options = zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE;
737 if flags.contains(fio::VmoFlags::WRITE) {
738 child_options |= zx::VmoChildOptions::RESIZABLE;
739 rights |= zx::Rights::RESIZE;
740 }
741 vmo.create_child(child_options, 0, vmo.get_stream_size()?)?
742 } else {
743 vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0)?
744 };
745
746 let child_vmo = child_vmo.replace_handle(rights)?;
747 if self.handle.owner().pager().watch_for_zero_children(self).map_err(map_to_status)? {
748 self.open_count_add_one();
750 }
751 Ok(child_vmo)
752 }
753
754 async fn get_size(&self) -> Result<u64, Status> {
755 Ok(self.handle.get_size())
756 }
757
758 async fn update_attributes(
759 &self,
760 attributes: fio::MutableNodeAttributes,
761 ) -> Result<(), Status> {
762 if attributes == fio::MutableNodeAttributes::default() {
763 return Ok(());
764 }
765
766 self.handle.update_attributes(&attributes).await.map_err(map_to_status)?;
767 Ok(())
768 }
769
770 async fn allocate(
771 &self,
772 offset: u64,
773 length: u64,
774 _mode: fio::AllocateMode,
775 ) -> Result<(), Status> {
776 let range = offset..offset.checked_add(length).ok_or(Status::FILE_BIG)?;
779 self.handle.allocate(range).await.map_err(map_to_status)
780 }
781
782 async fn sync(&self, mode: SyncMode) -> Result<(), Status> {
783 self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
784
785 if mode == SyncMode::Normal {
788 self.handle
789 .store()
790 .filesystem()
791 .sync(SyncOptions::default())
792 .await
793 .map_err(map_to_status)?;
794 }
795
796 Ok(())
797 }
798}
799
800#[fxfs_trace::trace]
801impl PagerBacked for FxFile {
802 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
803 let mut old = self.load_state();
804 loop {
805 if old.open_count() == 0 {
806 return Err(self);
807 }
808
809 assert!(old.open_count() < MAX_OPEN_COUNTS);
810
811 match self.state.compare_exchange_weak(
812 old.0,
813 old.0 + 1,
814 Ordering::Relaxed,
815 Ordering::Relaxed,
816 ) {
817 Ok(_) => return Ok(OpenedNode(self)),
818 Err(new_value) => old.0 = new_value,
819 }
820 }
821 }
822
823 fn pager(&self) -> &crate::pager::Pager {
824 self.handle.owner().pager()
825 }
826
827 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
828 &self.pager_packet_receiver_registration
829 }
830
831 fn vmo(&self) -> &zx::Vmo {
832 self.handle.vmo()
833 }
834
835 fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
836 default_page_in(self, range, READ_AHEAD_SIZE);
837 }
838
839 #[trace]
840 fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
841 let (valid_pages, invalid_pages) = range.split(MAX_FILE_SIZE);
842 if let Some(invalid_pages) = invalid_pages {
843 invalid_pages.report_failure(zx::Status::FILE_BIG);
844 }
845 let range = match valid_pages {
846 Some(range) => range,
847 None => return,
848 };
849
850 let byte_count = range.len();
851 self.handle.owner().clone().report_pager_dirty(byte_count, move || {
852 match self.handle.mark_dirty(range) {
853 Ok(dirty_bytes) => {
854 if dirty_bytes > BACKGROUND_FLUSH_THRESHOLD
857 && !self.background_flush_running.swap(true, Ordering::Relaxed)
858 {
859 let owner = self.handle.owner().clone();
860 owner.spawn(async move {
861 let _ = self.handle.flush(FlushType::Background).await;
863 self.background_flush_running.store(false, Ordering::Relaxed);
866 });
867 }
868 }
869 Err(_) => {
870 self.handle.owner().report_pager_clean(byte_count)
872 }
873 }
874 });
875 }
876
877 fn on_zero_children(self: Arc<Self>) {
878 self.open_count_sub_one();
880 }
881
882 fn byte_size(&self) -> u64 {
883 self.handle.uncached_size()
884 }
885
886 #[trace("len" => (range.end - range.start))]
887 async fn aligned_read(&self, range: Range<u64>) -> Result<buffer::Buffer<'_>, Error> {
888 let buffer = self.handle.read_uncached(range).await?;
894 Ok(buffer)
895 }
896}
897
898impl GetVmo for FxFile {
899 const PAGER_ON_FIDL_EXECUTOR: bool = true;
900
901 fn get_vmo(&self) -> &zx::Vmo {
902 self.vmo()
903 }
904}
905
906#[cfg(test)]
907mod tests {
908 use super::FxFile;
909 use crate::fuchsia::paged_object_handle::BACKGROUND_FLUSH_THRESHOLD;
910 use crate::fuchsia::testing::{
911 TestFixture, TestFixtureOptions, close_file_checked, open_dir_checked, open_file,
912 open_file_checked,
913 };
914 use anyhow::format_err;
915 use fidl_fuchsia_io as fio;
916 use fsverity_merkle::{FsVerityHasher, FsVerityHasherOptions};
917 use fuchsia_async::{self as fasync, unblock};
918 use fuchsia_fs::file;
919 use futures::join;
920 use fxfs::fsck::fsck;
921 use fxfs::object_handle::INVALID_OBJECT_ID;
922 use fxfs::object_store::Timestamp;
923 use fxfs_crypto::WrappingKeyId;
924 use rand::{RngExt as _, rng};
925 use std::sync::Arc;
926 use std::sync::atomic::{self, AtomicBool};
927 use std::time::Duration;
928 use storage_device::DeviceHolder;
929 use storage_device::fake_device::FakeDevice;
930 use storage_units::page_size;
931 use zx::Status;
932
933 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
934
935 #[fuchsia::test(threads = 10)]
936 async fn test_empty_file() {
937 let fixture = TestFixture::new().await;
938 let root = fixture.root();
939
940 let file = open_file_checked(
941 &root,
942 "foo",
943 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
944 &Default::default(),
945 )
946 .await;
947
948 let buf = file
949 .read(fio::MAX_BUF)
950 .await
951 .expect("FIDL call failed")
952 .map_err(Status::err_from_raw)
953 .expect("read failed");
954 assert!(buf.is_empty());
955
956 let (mutable_attrs, immutable_attrs) = file
957 .get_attributes(fio::NodeAttributesQuery::all())
958 .await
959 .expect("FIDL call failed")
960 .expect("GetAttributes failed");
961 assert_ne!(immutable_attrs.id.unwrap(), INVALID_OBJECT_ID);
962 assert_eq!(immutable_attrs.content_size.unwrap(), 0u64);
963 assert_eq!(immutable_attrs.storage_size.unwrap(), 0u64);
964 assert_eq!(immutable_attrs.link_count.unwrap(), 1u64);
965 assert_ne!(mutable_attrs.creation_time.unwrap(), 0u64);
966 assert_ne!(mutable_attrs.modification_time.unwrap(), 0u64);
967 assert_eq!(mutable_attrs.creation_time.unwrap(), mutable_attrs.modification_time.unwrap());
968
969 close_file_checked(file).await;
970 fixture.close().await;
971 }
972
973 #[fuchsia::test(threads = 10)]
974 async fn test_write_read() {
975 let fixture = TestFixture::new().await;
976 let root = fixture.root();
977
978 let file = open_file_checked(
979 &root,
980 "foo",
981 fio::Flags::FLAG_MAYBE_CREATE
982 | fio::PERM_READABLE
983 | fio::PERM_WRITABLE
984 | fio::Flags::PROTOCOL_FILE,
985 &Default::default(),
986 )
987 .await;
988
989 let inputs = vec!["hello, ", "world!"];
990 let expected_output = "hello, world!";
991 for input in inputs {
992 let bytes_written = file
993 .write(input.as_bytes())
994 .await
995 .expect("write failed")
996 .map_err(Status::err_from_raw)
997 .expect("File write was successful");
998 assert_eq!(bytes_written as usize, input.as_bytes().len());
999 }
1000
1001 let buf = file
1002 .read_at(fio::MAX_BUF, 0)
1003 .await
1004 .expect("read_at failed")
1005 .map_err(Status::err_from_raw)
1006 .expect("File read was successful");
1007 assert_eq!(buf.len(), expected_output.as_bytes().len());
1008 assert!(buf.iter().eq(expected_output.as_bytes().iter()));
1009
1010 let (_, immutable_attributes) = file
1011 .get_attributes(
1012 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1013 )
1014 .await
1015 .expect("FIDL call failed")
1016 .expect("get_attributes failed");
1017
1018 assert_eq!(
1019 immutable_attributes.content_size.unwrap(),
1020 expected_output.as_bytes().len() as u64
1021 );
1022 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size());
1023
1024 let () = file
1025 .sync()
1026 .await
1027 .expect("FIDL call failed")
1028 .map_err(Status::err_from_raw)
1029 .expect("sync failed");
1030
1031 let (_, immutable_attributes) = file
1032 .get_attributes(
1033 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1034 )
1035 .await
1036 .expect("FIDL call failed")
1037 .expect("get_attributes failed");
1038
1039 assert_eq!(
1040 immutable_attributes.content_size.unwrap(),
1041 expected_output.as_bytes().len() as u64
1042 );
1043 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size());
1044
1045 close_file_checked(file).await;
1046 fixture.close().await;
1047 }
1048
1049 #[fuchsia::test(threads = 10)]
1050 async fn test_page_in() {
1051 let input = "hello, world!";
1052 let reused_device = {
1053 let fixture = TestFixture::new().await;
1054 let root = fixture.root();
1055
1056 let file = open_file_checked(
1057 &root,
1058 "foo",
1059 fio::Flags::FLAG_MAYBE_CREATE
1060 | fio::PERM_READABLE
1061 | fio::PERM_WRITABLE
1062 | fio::Flags::PROTOCOL_FILE,
1063 &Default::default(),
1064 )
1065 .await;
1066
1067 let bytes_written = file
1068 .write(input.as_bytes())
1069 .await
1070 .expect("write failed")
1071 .map_err(Status::err_from_raw)
1072 .expect("File write was successful");
1073 assert_eq!(bytes_written as usize, input.as_bytes().len());
1074 assert!(file.sync().await.expect("Sync failed").is_ok());
1075
1076 close_file_checked(file).await;
1077 fixture.close().await
1078 };
1079
1080 let fixture = TestFixture::open(
1081 reused_device,
1082 TestFixtureOptions { format: false, ..Default::default() },
1083 )
1084 .await;
1085 let root = fixture.root();
1086
1087 let file = open_file_checked(
1088 &root,
1089 "foo",
1090 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1091 &Default::default(),
1092 )
1093 .await;
1094
1095 let vmo =
1096 file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1097 let mut readback = vec![0; input.as_bytes().len()];
1098 assert!(vmo.read(&mut readback, 0).is_ok());
1099 assert_eq!(input.as_bytes(), readback);
1100
1101 close_file_checked(file).await;
1102 fixture.close().await;
1103 }
1104
1105 #[fuchsia::test(threads = 10)]
1106 async fn test_page_in_io_error() {
1107 let mut device = FakeDevice::new(8192, 512);
1108 let succeed_requests = Arc::new(AtomicBool::new(true));
1109 let succeed_requests_clone = succeed_requests.clone();
1110 device.set_op_callback(Box::new(move |_| {
1111 if succeed_requests_clone.load(atomic::Ordering::Relaxed) {
1112 Ok(())
1113 } else {
1114 Err(format_err!("Fake error."))
1115 }
1116 }));
1117
1118 let input = "hello, world!";
1119 let reused_device = {
1120 let fixture = TestFixture::open(
1121 DeviceHolder::new(device),
1122 TestFixtureOptions { format: true, ..Default::default() },
1123 )
1124 .await;
1125 let root = fixture.root();
1126
1127 let file = open_file_checked(
1128 &root,
1129 "foo",
1130 fio::Flags::FLAG_MAYBE_CREATE
1131 | fio::PERM_READABLE
1132 | fio::PERM_WRITABLE
1133 | fio::Flags::PROTOCOL_FILE,
1134 &Default::default(),
1135 )
1136 .await;
1137
1138 let bytes_written = file
1139 .write(input.as_bytes())
1140 .await
1141 .expect("write failed")
1142 .map_err(Status::err_from_raw)
1143 .expect("File write was successful");
1144 assert_eq!(bytes_written as usize, input.as_bytes().len());
1145
1146 close_file_checked(file).await;
1147 fixture.close().await
1148 };
1149
1150 let fixture = TestFixture::open(
1151 reused_device,
1152 TestFixtureOptions { format: false, ..Default::default() },
1153 )
1154 .await;
1155 let root = fixture.root();
1156
1157 let file = open_file_checked(
1158 &root,
1159 "foo",
1160 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1161 &Default::default(),
1162 )
1163 .await;
1164
1165 let vmo =
1166 file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1167 succeed_requests.store(false, atomic::Ordering::Relaxed);
1168 let mut readback = vec![0; input.as_bytes().len()];
1169 assert!(vmo.read(&mut readback, 0).is_err());
1170
1171 succeed_requests.store(true, atomic::Ordering::Relaxed);
1172 close_file_checked(file).await;
1173 fixture.close().await;
1174 }
1175
1176 #[fuchsia::test(threads = 10)]
1177 async fn test_writes_persist() {
1178 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1179 for i in 0..2 {
1180 let fixture = TestFixture::open(
1181 device,
1182 TestFixtureOptions { format: i == 0, ..Default::default() },
1183 )
1184 .await;
1185 let root = fixture.root();
1186
1187 let flags = if i == 0 {
1188 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE
1189 } else {
1190 fio::PERM_READABLE | fio::PERM_WRITABLE
1191 };
1192 let file = open_file_checked(
1193 &root,
1194 "foo",
1195 flags | fio::Flags::PROTOCOL_FILE,
1196 &Default::default(),
1197 )
1198 .await;
1199
1200 if i == 0 {
1201 let _: u64 = file
1202 .write(&vec![0xaa as u8; 8192])
1203 .await
1204 .expect("FIDL call failed")
1205 .map_err(Status::err_from_raw)
1206 .expect("File write was successful");
1207 } else {
1208 let buf = file
1209 .read(8192)
1210 .await
1211 .expect("FIDL call failed")
1212 .map_err(Status::err_from_raw)
1213 .expect("File read was successful");
1214 assert_eq!(buf, vec![0xaa as u8; 8192]);
1215 }
1216
1217 let (_, immutable_attributes) = file
1218 .get_attributes(
1219 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1220 )
1221 .await
1222 .expect("FIDL call failed")
1223 .expect("get_attributes failed");
1224
1225 assert_eq!(immutable_attributes.content_size.unwrap(), 8192u64);
1226 assert_eq!(immutable_attributes.storage_size.unwrap(), 8192u64);
1227
1228 close_file_checked(file).await;
1229 device = fixture.close().await;
1230 }
1231 }
1232
1233 #[fuchsia::test(threads = 10)]
1234 async fn test_append() {
1235 let fixture = TestFixture::new().await;
1236 let root = fixture.root();
1237
1238 let inputs = vec!["hello, ", "world!"];
1239 let expected_output = "hello, world!";
1240 for input in inputs {
1241 let file = open_file_checked(
1242 &root,
1243 "foo",
1244 fio::Flags::FLAG_MAYBE_CREATE
1245 | fio::PERM_READABLE
1246 | fio::PERM_WRITABLE
1247 | fio::Flags::FILE_APPEND
1248 | fio::Flags::PROTOCOL_FILE,
1249 &Default::default(),
1250 )
1251 .await;
1252
1253 let bytes_written = file
1254 .write(input.as_bytes())
1255 .await
1256 .expect("FIDL call failed")
1257 .map_err(Status::err_from_raw)
1258 .expect("File write was successful");
1259 assert_eq!(bytes_written as usize, input.as_bytes().len());
1260 close_file_checked(file).await;
1261 }
1262
1263 let file = open_file_checked(
1264 &root,
1265 "foo",
1266 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1267 &Default::default(),
1268 )
1269 .await;
1270 let buf = file
1271 .read_at(fio::MAX_BUF, 0)
1272 .await
1273 .expect("FIDL call failed")
1274 .map_err(Status::err_from_raw)
1275 .expect("File read was successful");
1276 assert_eq!(buf.len(), expected_output.as_bytes().len());
1277 assert_eq!(&buf[..], expected_output.as_bytes());
1278
1279 let (_, immutable_attributes) = file
1280 .get_attributes(
1281 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1282 )
1283 .await
1284 .expect("FIDL call failed")
1285 .expect("get_attributes failed");
1286
1287 assert_eq!(
1288 immutable_attributes.content_size.unwrap(),
1289 expected_output.as_bytes().len() as u64
1290 );
1291 assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size());
1292
1293 close_file_checked(file).await;
1294 fixture.close().await;
1295 }
1296
1297 #[fuchsia::test(threads = 10)]
1298 async fn test_seek() {
1299 let fixture = TestFixture::new().await;
1300 let root = fixture.root();
1301
1302 let file = open_file_checked(
1303 &root,
1304 "foo",
1305 fio::Flags::FLAG_MAYBE_CREATE
1306 | fio::PERM_READABLE
1307 | fio::PERM_WRITABLE
1308 | fio::Flags::PROTOCOL_FILE,
1309 &Default::default(),
1310 )
1311 .await;
1312
1313 let input = "hello, world!";
1314 let _: u64 = file
1315 .write(input.as_bytes())
1316 .await
1317 .expect("FIDL call failed")
1318 .map_err(Status::err_from_raw)
1319 .expect("File write was successful");
1320
1321 {
1322 let offset = file
1323 .seek(fio::SeekOrigin::Start, 0)
1324 .await
1325 .expect("FIDL call failed")
1326 .map_err(Status::err_from_raw)
1327 .expect("seek was successful");
1328 assert_eq!(offset, 0);
1329 let buf = file
1330 .read(5)
1331 .await
1332 .expect("FIDL call failed")
1333 .map_err(Status::err_from_raw)
1334 .expect("File read was successful");
1335 assert!(buf.iter().eq("hello".as_bytes().iter()));
1336 }
1337 {
1338 let offset = file
1339 .seek(fio::SeekOrigin::Current, 2)
1340 .await
1341 .expect("FIDL call failed")
1342 .map_err(Status::err_from_raw)
1343 .expect("seek was successful");
1344 assert_eq!(offset, 7);
1345 let buf = file
1346 .read(5)
1347 .await
1348 .expect("FIDL call failed")
1349 .map_err(Status::err_from_raw)
1350 .expect("File read was successful");
1351 assert!(buf.iter().eq("world".as_bytes().iter()));
1352 }
1353 {
1354 let offset = file
1355 .seek(fio::SeekOrigin::Current, -5)
1356 .await
1357 .expect("FIDL call failed")
1358 .map_err(Status::err_from_raw)
1359 .expect("seek was successful");
1360 assert_eq!(offset, 7);
1361 let buf = file
1362 .read(5)
1363 .await
1364 .expect("FIDL call failed")
1365 .map_err(Status::err_from_raw)
1366 .expect("File read was successful");
1367 assert!(buf.iter().eq("world".as_bytes().iter()));
1368 }
1369 {
1370 let offset = file
1371 .seek(fio::SeekOrigin::End, -1)
1372 .await
1373 .expect("FIDL call failed")
1374 .map_err(Status::err_from_raw)
1375 .expect("seek was successful");
1376 assert_eq!(offset, 12);
1377 let buf = file
1378 .read(1)
1379 .await
1380 .expect("FIDL call failed")
1381 .map_err(Status::err_from_raw)
1382 .expect("File read was successful");
1383 assert!(buf.iter().eq("!".as_bytes().iter()));
1384 }
1385
1386 close_file_checked(file).await;
1387 fixture.close().await;
1388 }
1389
1390 #[fuchsia::test(threads = 10)]
1391 async fn test_resize_extend() {
1392 let fixture = TestFixture::new().await;
1393 let root = fixture.root();
1394
1395 let file = open_file_checked(
1396 &root,
1397 "foo",
1398 fio::Flags::FLAG_MAYBE_CREATE
1399 | fio::PERM_READABLE
1400 | fio::PERM_WRITABLE
1401 | fio::Flags::PROTOCOL_FILE,
1402 &Default::default(),
1403 )
1404 .await;
1405
1406 let input = "hello, world!";
1407 let len: usize = 16 * 1024;
1408
1409 let _: u64 = file
1410 .write(input.as_bytes())
1411 .await
1412 .expect("FIDL call failed")
1413 .map_err(Status::err_from_raw)
1414 .expect("File write was successful");
1415
1416 let offset = file
1417 .seek(fio::SeekOrigin::Start, 0)
1418 .await
1419 .expect("FIDL call failed")
1420 .map_err(Status::err_from_raw)
1421 .expect("Seek was successful");
1422 assert_eq!(offset, 0);
1423
1424 let () = file
1425 .resize(len as u64)
1426 .await
1427 .expect("resize failed")
1428 .map_err(Status::err_from_raw)
1429 .expect("resize error");
1430
1431 let mut expected_buf = vec![0 as u8; len];
1432 expected_buf[..input.as_bytes().len()].copy_from_slice(input.as_bytes());
1433
1434 let buf = file::read(&file).await.expect("File read was successful");
1435 assert_eq!(buf.len(), len);
1436 assert_eq!(buf, expected_buf);
1437
1438 expected_buf[len - 1..].copy_from_slice("a".as_bytes());
1440
1441 let _: u64 = file
1442 .write_at("a".as_bytes(), (len - 1) as u64)
1443 .await
1444 .expect("FIDL call failed")
1445 .map_err(Status::err_from_raw)
1446 .expect("File write was successful");
1447
1448 let offset = file
1449 .seek(fio::SeekOrigin::Start, 0)
1450 .await
1451 .expect("FIDL call failed")
1452 .map_err(Status::err_from_raw)
1453 .expect("Seek was successful");
1454 assert_eq!(offset, 0);
1455
1456 let buf = file::read(&file).await.expect("File read was successful");
1457 assert_eq!(buf.len(), len);
1458 assert_eq!(buf, expected_buf);
1459
1460 close_file_checked(file).await;
1461 fixture.close().await;
1462 }
1463
1464 #[fuchsia::test(threads = 10)]
1465 async fn test_resize_shrink() {
1466 let fixture = TestFixture::new().await;
1467 let root = fixture.root();
1468
1469 let file = open_file_checked(
1470 &root,
1471 "foo",
1472 fio::Flags::FLAG_MAYBE_CREATE
1473 | fio::PERM_READABLE
1474 | fio::PERM_WRITABLE
1475 | fio::Flags::PROTOCOL_FILE,
1476 &Default::default(),
1477 )
1478 .await;
1479
1480 let len: usize = 2 * 1024;
1481 let input = {
1482 let mut v = vec![0 as u8; len];
1483 for i in 0..v.len() {
1484 v[i] = ('a' as u8) + (i % 13) as u8;
1485 }
1486 v
1487 };
1488 let short_len: usize = 513;
1489
1490 file::write(&file, &input).await.expect("File write was successful");
1491
1492 let () = file
1493 .resize(short_len as u64)
1494 .await
1495 .expect("resize failed")
1496 .map_err(Status::err_from_raw)
1497 .expect("resize error");
1498
1499 let offset = file
1500 .seek(fio::SeekOrigin::Start, 0)
1501 .await
1502 .expect("FIDL call failed")
1503 .map_err(Status::err_from_raw)
1504 .expect("Seek was successful");
1505 assert_eq!(offset, 0);
1506
1507 let buf = file::read(&file).await.expect("File read was successful");
1508 assert_eq!(buf.len(), short_len);
1509 assert_eq!(buf, input[..short_len]);
1510
1511 let () = file
1513 .resize(len as u64)
1514 .await
1515 .expect("resize failed")
1516 .map_err(Status::err_from_raw)
1517 .expect("resize error");
1518
1519 let expected_buf = {
1520 let mut v = vec![0 as u8; len];
1521 v[..short_len].copy_from_slice(&input[..short_len]);
1522 v
1523 };
1524
1525 let offset = file
1526 .seek(fio::SeekOrigin::Start, 0)
1527 .await
1528 .expect("seek failed")
1529 .map_err(Status::err_from_raw)
1530 .expect("Seek was successful");
1531 assert_eq!(offset, 0);
1532
1533 let buf = file::read(&file).await.expect("File read was successful");
1534 assert_eq!(buf.len(), len);
1535 assert_eq!(buf, expected_buf);
1536
1537 close_file_checked(file).await;
1538 fixture.close().await;
1539 }
1540
1541 #[fuchsia::test(threads = 10)]
1542 async fn test_resize_shrink_repeated() {
1543 let fixture = TestFixture::new().await;
1544 let root = fixture.root();
1545
1546 let file = open_file_checked(
1547 &root,
1548 "foo",
1549 fio::Flags::FLAG_MAYBE_CREATE
1550 | fio::PERM_READABLE
1551 | fio::PERM_WRITABLE
1552 | fio::Flags::PROTOCOL_FILE,
1553 &Default::default(),
1554 )
1555 .await;
1556
1557 let orig_len: usize = 4 * 1024;
1558 let mut len = orig_len;
1559 let input = {
1560 let mut v = vec![0 as u8; len];
1561 for i in 0..v.len() {
1562 v[i] = ('a' as u8) + (i % 13) as u8;
1563 }
1564 v
1565 };
1566 let short_len: usize = 513;
1567
1568 file::write(&file, &input).await.expect("File write was successful");
1569
1570 while len > short_len {
1571 len -= std::cmp::min(len - short_len, 512);
1572 let () = file
1573 .resize(len as u64)
1574 .await
1575 .expect("resize failed")
1576 .map_err(Status::err_from_raw)
1577 .expect("resize error");
1578 }
1579
1580 let offset = file
1581 .seek(fio::SeekOrigin::Start, 0)
1582 .await
1583 .expect("Seek failed")
1584 .map_err(Status::err_from_raw)
1585 .expect("Seek was successful");
1586 assert_eq!(offset, 0);
1587
1588 let buf = file::read(&file).await.expect("File read was successful");
1589 assert_eq!(buf.len(), short_len);
1590 assert_eq!(buf, input[..short_len]);
1591
1592 let () = file
1594 .resize(orig_len as u64)
1595 .await
1596 .expect("resize failed")
1597 .map_err(Status::err_from_raw)
1598 .expect("resize error");
1599
1600 let expected_buf = {
1601 let mut v = vec![0 as u8; orig_len];
1602 v[..short_len].copy_from_slice(&input[..short_len]);
1603 v
1604 };
1605
1606 let offset = file
1607 .seek(fio::SeekOrigin::Start, 0)
1608 .await
1609 .expect("seek failed")
1610 .map_err(Status::err_from_raw)
1611 .expect("Seek was successful");
1612 assert_eq!(offset, 0);
1613
1614 let buf = file::read(&file).await.expect("File read was successful");
1615 assert_eq!(buf.len(), orig_len);
1616 assert_eq!(buf, expected_buf);
1617
1618 close_file_checked(file).await;
1619 fixture.close().await;
1620 }
1621
1622 #[fuchsia::test(threads = 10)]
1623 async fn test_unlink_with_open_race() {
1624 let fixture = Arc::new(TestFixture::new().await);
1625 let fixture1 = fixture.clone();
1626 let fixture2 = fixture.clone();
1627 let fixture3 = fixture.clone();
1628 let done = Arc::new(AtomicBool::new(false));
1629 let done1 = done.clone();
1630 let done2 = done.clone();
1631 join!(
1632 fasync::Task::spawn(async move {
1633 let root = fixture1.root();
1634 while !done1.load(atomic::Ordering::Relaxed) {
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 let _: u64 = file
1646 .write(b"hello")
1647 .await
1648 .expect("write failed")
1649 .map_err(Status::err_from_raw)
1650 .expect("write error");
1651 }
1652 }),
1653 fasync::Task::spawn(async move {
1654 let root = fixture2.root();
1655 while !done2.load(atomic::Ordering::Relaxed) {
1656 let file = open_file_checked(
1657 &root,
1658 "foo",
1659 fio::Flags::FLAG_MAYBE_CREATE
1660 | fio::PERM_READABLE
1661 | fio::PERM_WRITABLE
1662 | fio::Flags::PROTOCOL_FILE,
1663 &Default::default(),
1664 )
1665 .await;
1666 let _: u64 = file
1667 .write(b"hello")
1668 .await
1669 .expect("write failed")
1670 .map_err(Status::err_from_raw)
1671 .expect("write error");
1672 }
1673 }),
1674 fasync::Task::spawn(async move {
1675 let root = fixture3.root();
1676 for _ in 0..300 {
1677 let file = open_file_checked(
1678 &root,
1679 "foo",
1680 fio::Flags::FLAG_MAYBE_CREATE
1681 | fio::PERM_READABLE
1682 | fio::PERM_WRITABLE
1683 | fio::Flags::PROTOCOL_FILE,
1684 &Default::default(),
1685 )
1686 .await;
1687 assert_eq!(
1688 file.close().await.expect("FIDL call failed").map_err(Status::err_from_raw),
1689 Ok(())
1690 );
1691 root.unlink("foo", &fio::UnlinkOptions::default())
1692 .await
1693 .expect("FIDL call failed")
1694 .expect("unlink failed");
1695 }
1696 done.store(true, atomic::Ordering::Relaxed);
1697 })
1698 );
1699
1700 Arc::try_unwrap(fixture).unwrap_or_else(|_| panic!()).close().await;
1701 }
1702
1703 #[fuchsia::test(threads = 10)]
1704 async fn test_get_backing_memory_shared_vmo_right_write() {
1705 let fixture = TestFixture::new().await;
1706 let root = fixture.root();
1707
1708 let file = open_file_checked(
1709 &root,
1710 "foo",
1711 fio::Flags::FLAG_MAYBE_CREATE
1712 | fio::PERM_READABLE
1713 | fio::PERM_WRITABLE
1714 | fio::Flags::PROTOCOL_FILE,
1715 &Default::default(),
1716 )
1717 .await;
1718
1719 file.resize(4096)
1720 .await
1721 .expect("resize failed")
1722 .map_err(Status::err_from_raw)
1723 .expect("resize error");
1724
1725 let vmo = file
1726 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1727 .await
1728 .expect("Failed to make FIDL call")
1729 .map_err(Status::err_from_raw)
1730 .expect("Failed to get VMO");
1731 let err = vmo.write(&[0, 1, 2, 3], 0).expect_err("VMO should not be writable");
1732 assert_eq!(Status::ACCESS_DENIED, err);
1733
1734 let vmo = file
1735 .get_backing_memory(
1736 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1737 )
1738 .await
1739 .expect("Failed to make FIDL call")
1740 .map_err(Status::err_from_raw)
1741 .expect("Failed to get VMO");
1742 vmo.write(&[0, 1, 2, 3], 0).expect("VMO should be writable");
1743
1744 close_file_checked(file).await;
1745 fixture.close().await;
1746 }
1747
1748 #[fuchsia::test(threads = 10)]
1749 async fn test_get_backing_memory_shared_vmo_right_read() {
1750 let fixture = TestFixture::new().await;
1751 let root = fixture.root();
1752
1753 let file = open_file_checked(
1754 &root,
1755 "foo",
1756 fio::Flags::FLAG_MAYBE_CREATE
1757 | fio::PERM_READABLE
1758 | fio::PERM_WRITABLE
1759 | fio::Flags::PROTOCOL_FILE,
1760 &Default::default(),
1761 )
1762 .await;
1763
1764 file.resize(4096)
1765 .await
1766 .expect("resize failed")
1767 .map_err(Status::err_from_raw)
1768 .expect("resize error");
1769
1770 let mut data = [0u8; 4];
1771 let vmo = file
1772 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER)
1773 .await
1774 .expect("Failed to make FIDL call")
1775 .map_err(Status::err_from_raw)
1776 .expect("Failed to get VMO");
1777 let err = vmo.read(&mut data, 0).expect_err("VMO should not be readable");
1778 assert_eq!(Status::ACCESS_DENIED, err);
1779
1780 let vmo = file
1781 .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1782 .await
1783 .expect("Failed to make FIDL call")
1784 .map_err(Status::err_from_raw)
1785 .expect("Failed to get VMO");
1786 vmo.read(&mut data, 0).expect("VMO should be readable");
1787
1788 close_file_checked(file).await;
1789 fixture.close().await;
1790 }
1791
1792 #[fuchsia::test(threads = 10)]
1793 async fn test_get_backing_memory_shared_vmo_resize() {
1794 let fixture = TestFixture::new().await;
1795 let root = fixture.root();
1796
1797 let file = open_file_checked(
1798 &root,
1799 "foo",
1800 fio::Flags::FLAG_MAYBE_CREATE
1801 | fio::PERM_READABLE
1802 | fio::PERM_WRITABLE
1803 | fio::Flags::PROTOCOL_FILE,
1804 &Default::default(),
1805 )
1806 .await;
1807
1808 let vmo = file
1809 .get_backing_memory(
1810 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1811 )
1812 .await
1813 .expect("Failed to make FIDL call")
1814 .map_err(Status::err_from_raw)
1815 .expect("Failed to get VMO");
1816
1817 let err = vmo.set_size(4096).expect_err("VMO should not be resizable");
1819 assert_eq!(Status::UNAVAILABLE, err);
1820 let err =
1822 vmo.set_content_size(&10).expect_err("content size should not be directly modifiable");
1823 assert_eq!(Status::ACCESS_DENIED, err);
1824
1825 close_file_checked(file).await;
1826 fixture.close().await;
1827 }
1828
1829 #[fuchsia::test(threads = 10)]
1830 async fn test_get_backing_memory_private_vmo_resize() {
1831 let fixture = TestFixture::new().await;
1832 let root = fixture.root();
1833
1834 let file = open_file_checked(
1835 &root,
1836 "foo",
1837 fio::Flags::FLAG_MAYBE_CREATE
1838 | fio::PERM_READABLE
1839 | fio::PERM_WRITABLE
1840 | fio::Flags::PROTOCOL_FILE,
1841 &Default::default(),
1842 )
1843 .await;
1844
1845 let vmo = file
1846 .get_backing_memory(
1847 fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1848 )
1849 .await
1850 .expect("Failed to make FIDL call")
1851 .map_err(Status::err_from_raw)
1852 .expect("Failed to get VMO");
1853 vmo.set_size(10).expect("VMO should be resizable");
1854 vmo.set_content_size(&20).expect("content size should be modifiable");
1855 vmo.set_stream_size(20).expect("stream size should be modifiable");
1856
1857 let vmo = file
1858 .get_backing_memory(fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ)
1859 .await
1860 .expect("Failed to make FIDL call")
1861 .map_err(Status::err_from_raw)
1862 .expect("Failed to get VMO");
1863 let err = vmo.set_size(10).expect_err("VMO should not be resizable");
1864 assert_eq!(err, Status::ACCESS_DENIED);
1865 vmo.set_stream_size(20).expect_err("stream size is not modifiable");
1867 vmo.set_content_size(&20).expect_err("content is not modifiable");
1868
1869 close_file_checked(file).await;
1870 fixture.close().await;
1871 }
1872
1873 #[fuchsia::test(threads = 10)]
1874 async fn extended_attributes() {
1875 let fixture = TestFixture::new().await;
1876 let root = fixture.root();
1877
1878 let file = open_file_checked(
1879 &root,
1880 "foo",
1881 fio::Flags::FLAG_MAYBE_CREATE
1882 | fio::PERM_READABLE
1883 | fio::PERM_WRITABLE
1884 | fio::Flags::PROTOCOL_FILE,
1885 &Default::default(),
1886 )
1887 .await;
1888
1889 let name = b"security.selinux";
1890 let value_vec = b"bar".to_vec();
1891
1892 {
1893 let (iterator_client, iterator_server) =
1894 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1895 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1896 let (chunk, last) = iterator_client
1897 .get_next()
1898 .await
1899 .expect("Failed to make FIDL call")
1900 .expect("Failed to get next iterator chunk");
1901 assert!(last);
1902 assert_eq!(chunk, Vec::<Vec<u8>>::new());
1903 }
1904 assert_eq!(
1905 file.get_extended_attribute(name)
1906 .await
1907 .expect("Failed to make FIDL call")
1908 .expect_err("Got successful message back for missing attribute"),
1909 Status::NOT_FOUND.into_raw(),
1910 );
1911
1912 file.set_extended_attribute(
1913 name,
1914 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
1915 fio::SetExtendedAttributeMode::Set,
1916 )
1917 .await
1918 .expect("Failed to make FIDL call")
1919 .expect("Failed to set extended attribute");
1920
1921 {
1922 let (iterator_client, iterator_server) =
1923 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1924 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1925 let (chunk, last) = iterator_client
1926 .get_next()
1927 .await
1928 .expect("Failed to make FIDL call")
1929 .expect("Failed to get next iterator chunk");
1930 assert!(last);
1931 assert_eq!(chunk, vec![name]);
1932 }
1933 assert_eq!(
1934 file.get_extended_attribute(name)
1935 .await
1936 .expect("Failed to make FIDL call")
1937 .expect("Failed to get extended attribute"),
1938 fio::ExtendedAttributeValue::Bytes(value_vec)
1939 );
1940
1941 file.remove_extended_attribute(name)
1942 .await
1943 .expect("Failed to make FIDL call")
1944 .expect("Failed to remove extended attribute");
1945
1946 {
1947 let (iterator_client, iterator_server) =
1948 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1949 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1950 let (chunk, last) = iterator_client
1951 .get_next()
1952 .await
1953 .expect("Failed to make FIDL call")
1954 .expect("Failed to get next iterator chunk");
1955 assert!(last);
1956 assert_eq!(chunk, Vec::<Vec<u8>>::new());
1957 }
1958 assert_eq!(
1959 file.get_extended_attribute(name)
1960 .await
1961 .expect("Failed to make FIDL call")
1962 .expect_err("Got successful message back for missing attribute"),
1963 Status::NOT_FOUND.into_raw(),
1964 );
1965
1966 close_file_checked(file).await;
1967 fixture.close().await;
1968 }
1969
1970 #[fuchsia::test]
1971 async fn test_flush_when_closed_from_on_zero_children() {
1972 let fixture = TestFixture::new().await;
1973 let root = fixture.root();
1974
1975 let file = open_file_checked(
1976 &root,
1977 "foo",
1978 fio::Flags::FLAG_MAYBE_CREATE
1979 | fio::PERM_READABLE
1980 | fio::PERM_WRITABLE
1981 | fio::Flags::PROTOCOL_FILE,
1982 &Default::default(),
1983 )
1984 .await;
1985
1986 file.resize(50).await.expect("resize (FIDL) failed").expect("resize failed");
1987
1988 {
1989 let vmo = file
1990 .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
1991 .await
1992 .expect("get_backing_memory (FIDL) failed")
1993 .map_err(Status::err_from_raw)
1994 .expect("get_backing_memory failed");
1995
1996 std::mem::drop(file);
1997
1998 fasync::unblock(move || vmo.write(b"hello", 0).expect("write failed")).await;
1999 }
2000
2001 fixture.close().await;
2002 }
2003
2004 #[fuchsia::test]
2005 async fn test_background_flush() {
2006 let fixture = TestFixture::open(
2007 DeviceHolder::new(FakeDevice::new(65536, 512)),
2008 TestFixtureOptions::default(),
2009 )
2010 .await;
2011 {
2012 let root = fixture.root();
2013
2014 let file = open_file_checked(
2015 &root,
2016 "foo",
2017 fio::Flags::FLAG_MAYBE_CREATE
2018 | fio::PERM_READABLE
2019 | fio::PERM_WRITABLE
2020 | fio::Flags::PROTOCOL_FILE,
2021 &Default::default(),
2022 )
2023 .await;
2024
2025 let stream = file.describe().await.unwrap().stream.unwrap();
2026 let file_id = file
2027 .get_attributes(fio::NodeAttributesQuery::ID)
2028 .await
2029 .unwrap()
2030 .unwrap()
2031 .1
2032 .id
2033 .unwrap();
2034 let truncate_guard = fixture
2036 .fs()
2037 .truncate_guard(fixture.volume().volume().store().store_object_id(), file_id)
2038 .await;
2039
2040 let file_obj = fixture
2041 .volume()
2042 .volume()
2043 .cache()
2044 .get(file_id)
2045 .unwrap()
2046 .into_any()
2047 .downcast::<FxFile>()
2048 .unwrap();
2049 let file_clone = file_obj.clone();
2050
2051 unblock(move || {
2052 let page_size = page_size().get();
2053 let mut offset: u64 = 0;
2054 while !file_clone
2055 .background_flush_running
2056 .load(std::sync::atomic::Ordering::Relaxed)
2057 {
2058 assert!(
2059 offset <= BACKGROUND_FLUSH_THRESHOLD * 2,
2060 "Background flush not triggering"
2061 );
2062 stream
2063 .write_at(zx::StreamWriteOptions::empty(), offset, &[0, 1, 2, 3, 4])
2064 .expect("write should succeed");
2065 offset += page_size;
2066 }
2067 })
2068 .await;
2069
2070 std::mem::drop(truncate_guard);
2072 const MAX_WAIT: Duration = Duration::from_secs(10);
2073 let wait_increments = Duration::from_millis(100);
2074 let mut total_waited = Duration::ZERO;
2075 while file_obj.background_flush_running.load(std::sync::atomic::Ordering::Relaxed) {
2076 total_waited += wait_increments;
2077 assert!(total_waited < MAX_WAIT);
2078 fasync::Timer::new(wait_increments).await;
2079 }
2080 }
2081
2082 fixture.close().await;
2083 }
2084
2085 #[fuchsia::test]
2086 async fn test_get_attributes_fsverity_enabled_file() {
2087 let fixture = TestFixture::new().await;
2088 let root = fixture.root();
2089
2090 let file = open_file_checked(
2091 &root,
2092 "foo",
2093 fio::Flags::FLAG_MAYBE_CREATE
2094 | fio::PERM_READABLE
2095 | fio::PERM_WRITABLE
2096 | fio::Flags::PROTOCOL_FILE,
2097 &Default::default(),
2098 )
2099 .await;
2100
2101 let mut data: Vec<u8> = vec![0x00u8; 1052672];
2102 rng().fill(&mut data[..]);
2103
2104 for chunk in data.chunks(8192) {
2105 file.write(chunk)
2106 .await
2107 .expect("FIDL call failed")
2108 .map_err(Status::err_from_raw)
2109 .expect("write failed");
2110 }
2111
2112 let tree = fsverity_merkle::MerkleTree::from_data(
2113 &data,
2114 FsVerityHasher::Sha256(FsVerityHasherOptions::new(vec![0xFF; 8], 4096)),
2115 );
2116 let expected_root = tree.root().to_vec();
2117
2118 let expected_descriptor = fio::VerificationOptions {
2119 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2120 salt: Some(vec![0xFF; 8]),
2121 ..Default::default()
2122 };
2123
2124 file.enable_verity(&expected_descriptor)
2125 .await
2126 .expect("FIDL transport error")
2127 .expect("enable verity failed");
2128
2129 let (_, immutable_attributes) = file
2130 .get_attributes(
2131 fio::NodeAttributesQuery::ROOT_HASH
2132 | fio::NodeAttributesQuery::OPTIONS
2133 | fio::NodeAttributesQuery::ABILITIES,
2134 )
2135 .await
2136 .expect("FIDL call failed")
2137 .map_err(Status::err_from_raw)
2138 .expect("get_attributes failed");
2139
2140 assert_eq!(
2141 immutable_attributes
2142 .options
2143 .expect("verification options not present in immutable attributes"),
2144 expected_descriptor
2145 );
2146 assert_eq!(
2147 immutable_attributes.root_hash.expect("root hash not present in immutable attributes"),
2148 expected_root
2149 );
2150 assert_eq!(
2151 immutable_attributes.abilities.expect("abilities not present"),
2152 fio::Operations::GET_ATTRIBUTES
2153 | fio::Operations::UPDATE_ATTRIBUTES
2154 | fio::Operations::READ_BYTES
2155 );
2156
2157 fixture.close().await;
2158 }
2159
2160 #[fuchsia::test]
2163 async fn test_write_fail_fsverity_enabled_file() {
2164 let fixture = TestFixture::new().await;
2165 let root = fixture.root();
2166
2167 let file = open_file_checked(
2168 &root,
2169 "foo",
2170 fio::Flags::FLAG_MAYBE_CREATE
2171 | fio::PERM_READABLE
2172 | fio::PERM_WRITABLE
2173 | fio::Flags::PROTOCOL_FILE,
2174 &Default::default(),
2175 )
2176 .await;
2177
2178 file.write(&[8; 8192])
2179 .await
2180 .expect("FIDL call failed")
2181 .map_err(Status::err_from_raw)
2182 .expect("write failed");
2183
2184 let existing_vmo = file
2186 .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
2187 .await
2188 .expect("FIDL transport error")
2189 .map_err(Status::err_from_raw)
2190 .expect("get_backing_memory failed");
2191
2192 let descriptor = fio::VerificationOptions {
2193 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2194 salt: Some(vec![0xFF; 8]),
2195 ..Default::default()
2196 };
2197
2198 file.enable_verity(&descriptor)
2199 .await
2200 .expect("FIDL transport error")
2201 .expect("enable verity failed");
2202
2203 fasync::unblock(move || {
2206 existing_vmo
2207 .write(&[2; 8192], 0)
2208 .expect_err("write via existing VMO succeeded on fsverity-enabled file");
2209 })
2210 .await;
2211
2212 async fn assert_file_is_not_writable(file: &fio::FileProxy) {
2213 file.write(&[2; 8192])
2215 .await
2216 .expect("FIDL transport error")
2217 .map_err(Status::err_from_raw)
2218 .expect_err("write succeeded on fsverity-enabled file");
2219 file.get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
2221 .await
2222 .expect("FIDL transport error")
2223 .map_err(Status::err_from_raw)
2224 .expect_err("get_backing_memory with WRITE succeeded on fsverity-enabled file");
2225 let vmo = file
2227 .get_backing_memory(
2228 fio::VmoFlags::READ | fio::VmoFlags::WRITE | fio::VmoFlags::PRIVATE_CLONE,
2229 )
2230 .await
2231 .expect("FIDL transport error")
2232 .map_err(Status::err_from_raw)
2233 .expect("get_backing_memory with PRIVATE_CLONE failed");
2234 fasync::unblock(move || {
2235 vmo.write(&[2; 8192], 0).expect("write via private clone VMO failed");
2236 })
2237 .await;
2238 file.resize(1)
2240 .await
2241 .expect("FIDL transport error")
2242 .map_err(Status::err_from_raw)
2243 .expect_err("resize succeeded on fsverity-enabled file");
2244 }
2245
2246 assert_file_is_not_writable(&file).await;
2247 close_file_checked(file).await;
2248
2249 let file =
2251 open_file(&root, "foo", fio::PERM_READABLE | fio::PERM_WRITABLE, &Default::default())
2252 .await
2253 .expect("failed to open fsverity-enabled file");
2254 assert_file_is_not_writable(&file).await;
2255 close_file_checked(file).await;
2256
2257 let device = fixture.close().await;
2259 device.ensure_unique();
2260 device.reopen(false);
2261 let fixture =
2262 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2263 .await;
2264
2265 let root = fixture.root();
2266 let file =
2267 open_file(&root, "foo", fio::PERM_READABLE | fio::PERM_WRITABLE, &Default::default())
2268 .await
2269 .expect("failed to open fsverity-enabled file");
2270 assert_file_is_not_writable(&file).await;
2271 close_file_checked(file).await;
2272
2273 fixture.close().await;
2274 }
2275
2276 #[fuchsia::test]
2277 async fn test_truncate_fail_fsverity_enabled_file() {
2278 let fixture = TestFixture::new().await;
2279 let root = fixture.root();
2280
2281 let file = open_file_checked(
2282 &root,
2283 "foo",
2284 fio::Flags::FLAG_MAYBE_CREATE
2285 | fio::PERM_READABLE
2286 | fio::PERM_WRITABLE
2287 | fio::Flags::PROTOCOL_FILE,
2288 &Default::default(),
2289 )
2290 .await;
2291
2292 file.write(&[8; 8192])
2293 .await
2294 .expect("FIDL call failed")
2295 .map_err(Status::err_from_raw)
2296 .expect("write failed");
2297
2298 let descriptor = fio::VerificationOptions {
2299 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2300 salt: Some(vec![0xFF; 8]),
2301 ..Default::default()
2302 };
2303
2304 file.enable_verity(&descriptor)
2305 .await
2306 .expect("FIDL transport error")
2307 .expect("enable verity failed");
2308
2309 file.resize(1)
2311 .await
2312 .expect("FIDL transport error")
2313 .map_err(Status::err_from_raw)
2314 .expect_err("shrink succeeded on fsverity-enabled file");
2315
2316 file.resize(16384)
2318 .await
2319 .expect("FIDL transport error")
2320 .map_err(Status::err_from_raw)
2321 .expect_err("grow succeeded on fsverity-enabled file");
2322
2323 close_file_checked(file).await;
2324 fixture.close().await;
2325 }
2326
2327 #[fuchsia::test]
2328 async fn test_fsverity_enabled_file_verified_reads() {
2329 let mut data: Vec<u8> = vec![0x00u8; 1052672];
2330 rng().fill(&mut data[..]);
2331 let mut num_chunks = 0;
2332
2333 let reused_device = {
2334 let fixture = TestFixture::new().await;
2335 let root = fixture.root();
2336
2337 let file = open_file_checked(
2338 &root,
2339 "foo",
2340 fio::Flags::FLAG_MAYBE_CREATE
2341 | fio::PERM_READABLE
2342 | fio::PERM_WRITABLE
2343 | fio::Flags::PROTOCOL_FILE,
2344 &Default::default(),
2345 )
2346 .await;
2347
2348 for chunk in data.chunks(fio::MAX_BUF as usize) {
2349 file.write(chunk)
2350 .await
2351 .expect("FIDL call failed")
2352 .map_err(Status::err_from_raw)
2353 .expect("write failed");
2354 num_chunks += 1;
2355 }
2356
2357 let descriptor = fio::VerificationOptions {
2358 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2359 salt: Some(vec![0xFF; 8]),
2360 ..Default::default()
2361 };
2362
2363 file.enable_verity(&descriptor)
2364 .await
2365 .expect("FIDL transport error")
2366 .expect("enable verity failed");
2367
2368 assert!(file.sync().await.expect("Sync failed").is_ok());
2369 close_file_checked(file).await;
2370 fixture.close().await
2371 };
2372
2373 let fixture = TestFixture::open(
2374 reused_device,
2375 TestFixtureOptions { format: false, ..Default::default() },
2376 )
2377 .await;
2378 let root = fixture.root();
2379
2380 let file = open_file_checked(
2381 &root,
2382 "foo",
2383 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2384 &Default::default(),
2385 )
2386 .await;
2387
2388 for chunk in 0..num_chunks {
2389 let buffer = file
2390 .read(fio::MAX_BUF)
2391 .await
2392 .expect("transport error on read")
2393 .expect("read failed");
2394 let start = chunk * fio::MAX_BUF as usize;
2395 assert_eq!(&buffer, &data[start..start + buffer.len()]);
2396 }
2397
2398 fixture.close().await;
2399 }
2400
2401 #[fuchsia::test]
2402 async fn test_enabling_verity_on_verified_file_fails() {
2403 let reused_device = {
2404 let fixture = TestFixture::new().await;
2405 let root = fixture.root();
2406
2407 let file = open_file_checked(
2408 &root,
2409 "foo",
2410 fio::Flags::FLAG_MAYBE_CREATE
2411 | fio::PERM_READABLE
2412 | fio::PERM_WRITABLE
2413 | fio::Flags::PROTOCOL_FILE,
2414 &Default::default(),
2415 )
2416 .await;
2417
2418 file.write(&[1; 8192])
2419 .await
2420 .expect("FIDL call failed")
2421 .map_err(Status::err_from_raw)
2422 .expect("write failed");
2423
2424 let descriptor = fio::VerificationOptions {
2425 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2426 salt: Some(vec![0xFF; 8]),
2427 ..Default::default()
2428 };
2429
2430 file.enable_verity(&descriptor)
2431 .await
2432 .expect("FIDL transport error")
2433 .expect("enable verity failed");
2434
2435 file.enable_verity(&descriptor)
2436 .await
2437 .expect("FIDL transport error")
2438 .expect_err("enabling verity on a verity-enabled file should fail.");
2439
2440 assert!(file.sync().await.expect("Sync failed").is_ok());
2441 close_file_checked(file).await;
2442 fixture.close().await
2443 };
2444
2445 let fixture = TestFixture::open(
2446 reused_device,
2447 TestFixtureOptions { format: false, ..Default::default() },
2448 )
2449 .await;
2450 let root = fixture.root();
2451
2452 let file = open_file_checked(
2453 &root,
2454 "foo",
2455 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2456 &Default::default(),
2457 )
2458 .await;
2459
2460 let descriptor = fio::VerificationOptions {
2461 hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2462 salt: Some(vec![0xFF; 8]),
2463 ..Default::default()
2464 };
2465
2466 file.enable_verity(&descriptor)
2467 .await
2468 .expect("FIDL transport error")
2469 .expect_err("enabling verity on a verity-enabled file should fail.");
2470
2471 close_file_checked(file).await;
2472 fixture.close().await;
2473 }
2474
2475 #[fuchsia::test]
2476 async fn test_get_attributes_fsverity_not_enabled() {
2477 let fixture = TestFixture::new().await;
2478 let root = fixture.root();
2479
2480 let file = open_file_checked(
2481 &root,
2482 "foo",
2483 fio::Flags::FLAG_MAYBE_CREATE
2484 | fio::PERM_READABLE
2485 | fio::PERM_WRITABLE
2486 | fio::Flags::PROTOCOL_FILE,
2487 &Default::default(),
2488 )
2489 .await;
2490
2491 let mut data: Vec<u8> = vec![0x00u8; 8192];
2492 rng().fill(&mut data[..]);
2493
2494 file.write(&data)
2495 .await
2496 .expect("FIDL call failed")
2497 .map_err(Status::err_from_raw)
2498 .expect("write failed");
2499
2500 let () = file
2501 .sync()
2502 .await
2503 .expect("FIDL call failed")
2504 .map_err(Status::err_from_raw)
2505 .expect("sync failed");
2506
2507 let (_, immutable_attributes) = file
2508 .get_attributes(fio::NodeAttributesQuery::ROOT_HASH | fio::NodeAttributesQuery::OPTIONS)
2509 .await
2510 .expect("FIDL call failed")
2511 .map_err(Status::err_from_raw)
2512 .expect("get_attributes failed");
2513
2514 assert_eq!(immutable_attributes.options, None);
2515 assert_eq!(immutable_attributes.root_hash, None);
2516
2517 fixture.close().await;
2518 }
2519
2520 #[fuchsia::test]
2521 async fn test_update_attributes_also_updates_ctime() {
2522 let fixture = TestFixture::new().await;
2523 let root = fixture.root();
2524
2525 let file = open_file_checked(
2526 &root,
2527 "foo",
2528 fio::Flags::FLAG_MAYBE_CREATE
2529 | fio::PERM_READABLE
2530 | fio::PERM_WRITABLE
2531 | fio::Flags::PROTOCOL_FILE,
2532 &Default::default(),
2533 )
2534 .await;
2535
2536 file.write("hello, world!".as_bytes())
2538 .await
2539 .expect("FIDL call failed")
2540 .map_err(Status::err_from_raw)
2541 .expect("write failed");
2542 let (_mutable_attributes, immutable_attributes) = file
2543 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2544 .await
2545 .expect("FIDL call failed")
2546 .map_err(Status::err_from_raw)
2547 .expect("get_attributes failed");
2548 let ctime_after_write = immutable_attributes.change_time;
2549
2550 file.update_attributes(&fio::MutableNodeAttributes {
2552 mode: Some(111),
2553 gid: Some(222),
2554 ..Default::default()
2555 })
2556 .await
2557 .expect("FIDL call failed")
2558 .map_err(Status::err_from_raw)
2559 .expect("update_attributes failed");
2560 let (_mutable_attributes, immutable_attributes) = file
2561 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2562 .await
2563 .expect("FIDL call failed")
2564 .map_err(Status::err_from_raw)
2565 .expect("get_attributes failed");
2566 let ctime_after_update = immutable_attributes.change_time;
2567 assert!(ctime_after_update > ctime_after_write);
2568
2569 file.sync()
2571 .await
2572 .expect("FIDL call failed")
2573 .map_err(Status::err_from_raw)
2574 .expect("sync failed");
2575 let (_mutable_attributes, immutable_attributes) = file
2576 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2577 .await
2578 .expect("FIDL call failed")
2579 .map_err(Status::err_from_raw)
2580 .expect("get_attributes failed");
2581 let ctime_after_sync = immutable_attributes.change_time;
2582 assert_eq!(ctime_after_sync, ctime_after_update);
2583 fixture.close().await;
2584 }
2585
2586 #[fuchsia::test]
2587 async fn test_unnamed_temporary_file_can_read_and_write_to_it() {
2588 let fixture = TestFixture::new().await;
2589 let root = fixture.root();
2590
2591 let 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
2602 let buf = vec![0xaa as u8; 8];
2603 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2604
2605 tmpfile
2606 .seek(fio::SeekOrigin::Start, 0)
2607 .await
2608 .expect("seek failed")
2609 .map_err(zx::Status::err_from_raw)
2610 .expect("seek error");
2611 let read_buf = file::read(&tmpfile).await.expect("read failed");
2612 assert_eq!(read_buf, buf);
2613
2614 fixture.close().await;
2615 }
2616
2617 #[fuchsia::test]
2618 async fn test_unnamed_temporary_file_get_space_back_after_closing_file() {
2619 let fixture = TestFixture::new().await;
2620 let root = fixture.root();
2621
2622 let tmpfile = open_file_checked(
2623 &root,
2624 ".",
2625 fio::Flags::PROTOCOL_FILE
2626 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2627 | fio::PERM_WRITABLE,
2628 &fio::Options::default(),
2629 )
2630 .await;
2631
2632 const BUFFER_SIZE: u64 = 1024 * 1024;
2633 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2634 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2635
2636 let info_after_writing_to_tmpfile = root
2637 .query_filesystem()
2638 .await
2639 .expect("Failed wire call to query filesystem")
2640 .1
2641 .expect("Failed to query filesystem");
2642
2643 close_file_checked(tmpfile).await;
2644
2645 for i in 1..50 {
2647 let info = root
2648 .query_filesystem()
2649 .await
2650 .expect("Failed wire call to query filesystem")
2651 .1
2652 .expect("Failed to query filesystem");
2653
2654 if info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE {
2657 break;
2658 }
2659 if i == 49 {
2660 panic!("Did not get space back from unnamed temporary file after closing it.");
2661 }
2662 }
2663
2664 fixture.close().await;
2665 }
2666
2667 #[fuchsia::test]
2668 async fn test_unnamed_temporary_file_get_space_back_after_closing_device() {
2669 const BUFFER_SIZE: u64 = 1024 * 1024;
2670
2671 let (reused_device, info_after_writing_to_tmpfile) = {
2672 let fixture = TestFixture::new().await;
2673 let root = fixture.root();
2674
2675 let tmpfile = open_file_checked(
2676 &root,
2677 ".",
2678 fio::Flags::PROTOCOL_FILE
2679 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2680 | fio::PERM_WRITABLE,
2681 &fio::Options::default(),
2682 )
2683 .await;
2684
2685 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2686 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2687
2688 let info_after_writing_to_tmpfile = root
2689 .query_filesystem()
2690 .await
2691 .expect("Failed wire call to query filesystem")
2692 .1
2693 .expect("Failed to query filesystem");
2694
2695 (fixture.close().await, info_after_writing_to_tmpfile)
2696 };
2697
2698 let fixture = TestFixture::open(
2699 reused_device,
2700 TestFixtureOptions { format: false, ..Default::default() },
2701 )
2702 .await;
2703 let root = fixture.root();
2704
2705 let info = root
2706 .query_filesystem()
2707 .await
2708 .expect("Failed wire call to query filesystem")
2709 .1
2710 .expect("Failed to query filesystem");
2711
2712 assert!(info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE);
2715
2716 fixture.close().await;
2717 }
2718
2719 #[fuchsia::test]
2720 async fn test_unnamed_temporary_file_can_link_into() {
2721 const FILE1: &str = "foo";
2722 const FILE2: &str = "bar";
2723 const BUFFER_SIZE: u64 = 1024 * 1024;
2724 let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2725
2726 let reused_device = {
2727 let fixture = TestFixture::new().await;
2728 let root = fixture.root();
2729
2730 let tmpfile = open_file_checked(
2731 &root,
2732 ".",
2733 fio::Flags::PROTOCOL_FILE
2734 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2735 | fio::PERM_READABLE
2736 | fio::PERM_WRITABLE,
2737 &fio::Options::default(),
2738 )
2739 .await;
2740
2741 let (status, dst_token) = root.deprecated_get_token().await.expect("FIDL call failed");
2743 zx::Status::ok(status).expect("get_token failed");
2744 tmpfile
2745 .link_into(zx::Event::from(dst_token.unwrap()), FILE1)
2746 .await
2747 .expect("link_into wire message failed")
2748 .map_err(zx::Status::err_from_raw)
2749 .expect("link_into failed");
2750
2751 let (status, dst_token) = root.deprecated_get_token().await.expect("FIDL call failed");
2753 zx::Status::ok(status).expect("get_token failed");
2754 tmpfile
2755 .link_into(zx::Event::from(dst_token.unwrap()), FILE2)
2756 .await
2757 .expect("link_into wire message failed")
2758 .map_err(zx::Status::err_from_raw)
2759 .expect("link_into failed");
2760
2761 file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2763
2764 root.unlink(FILE1, &fio::UnlinkOptions::default())
2765 .await
2766 .expect("unlink wire call failed")
2767 .map_err(zx::Status::err_from_raw)
2768 .expect("unlink failed");
2769 fixture.close().await
2770 };
2771
2772 let fixture = TestFixture::open(
2773 reused_device,
2774 TestFixtureOptions { format: false, ..Default::default() },
2775 )
2776 .await;
2777 let root = fixture.root();
2778
2779 assert_eq!(
2781 open_file(
2782 &root,
2783 FILE1,
2784 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2785 &fio::Options::default()
2786 )
2787 .await
2788 .expect_err("Open succeeded unexpectedly")
2789 .root_cause()
2790 .downcast_ref::<zx::Status>()
2791 .expect("No status"),
2792 &zx::Status::NOT_FOUND,
2793 );
2794
2795 let permanent_file = open_file_checked(
2798 &root,
2799 FILE2,
2800 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2801 &fio::Options::default(),
2802 )
2803 .await;
2804 permanent_file
2805 .seek(fio::SeekOrigin::Start, 0)
2806 .await
2807 .expect("seek wire message failed")
2808 .map_err(zx::Status::err_from_raw)
2809 .expect("seek error");
2810 let read_buf = file::read(&permanent_file).await.expect("read failed");
2811 assert!(read_buf == buf);
2812
2813 fsck(fixture.fs().clone()).await.expect("fsck failed");
2814
2815 fixture.close().await;
2816 }
2817
2818 #[fuchsia::test]
2819 async fn test_unnamed_temporary_file_in_encrypted_directory() {
2820 let fixture = TestFixture::new().await;
2821 let root = fixture.root();
2822
2823 let crypt = fixture.crypt().unwrap();
2825 let encrypted_directory = open_dir_checked(
2826 &root,
2827 "encrypted_directory",
2828 fio::Flags::FLAG_MAYBE_CREATE
2829 | fio::Flags::PROTOCOL_DIRECTORY
2830 | fio::PERM_READABLE
2831 | fio::PERM_WRITABLE,
2832 fio::Options::default(),
2833 )
2834 .await;
2835 crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2836 encrypted_directory
2837 .update_attributes(&fio::MutableNodeAttributes {
2838 encryption_policy: Some(fio::FscryptPolicy {
2839 key_identifier: WRAPPING_KEY_ID,
2840 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2841 }),
2842 ..Default::default()
2843 })
2844 .await
2845 .expect("update_attributes wire call failed")
2846 .map_err(zx::ok)
2847 .expect("update_attributes failed");
2848
2849 let encryped_tmpfile = open_file_checked(
2851 &encrypted_directory,
2852 ".",
2853 fio::Flags::PROTOCOL_FILE
2854 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2855 | fio::PERM_READABLE
2856 | fio::PERM_WRITABLE,
2857 &fio::Options::default(),
2858 )
2859 .await;
2860 let (mutable_attributes, _immutable_attributes) = encryped_tmpfile
2861 .get_attributes(fio::NodeAttributesQuery::ENCRYPTION_POLICY)
2862 .await
2863 .expect("get_attributes wire call failed")
2864 .map_err(zx::Status::err_from_raw)
2865 .expect("get_attributes failed");
2866 assert_eq!(
2867 mutable_attributes.encryption_policy.map(|p| p.key_identifier),
2868 Some(WRAPPING_KEY_ID)
2869 );
2870
2871 let (status, dst_token) =
2874 encrypted_directory.deprecated_get_token().await.expect("FIDL call failed");
2875 zx::Status::ok(status).expect("get_token failed");
2876 encryped_tmpfile
2877 .link_into(zx::Event::from(dst_token.unwrap()), "foo")
2878 .await
2879 .expect("link_into wire message failed")
2880 .expect("link_into failed");
2881
2882 let unencryped_tmpfile = open_file_checked(
2883 &root,
2884 ".",
2885 fio::Flags::PROTOCOL_FILE
2886 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2887 | fio::PERM_READABLE
2888 | fio::PERM_WRITABLE,
2889 &fio::Options::default(),
2890 )
2891 .await;
2892 let (mutable_attributes, _immutable_attributes) = unencryped_tmpfile
2893 .get_attributes(fio::NodeAttributesQuery::ENCRYPTION_POLICY)
2894 .await
2895 .expect("get_attributes wire call failed")
2896 .map_err(zx::Status::err_from_raw)
2897 .expect("get_attributes failed");
2898 assert_eq!(mutable_attributes.encryption_policy, None);
2899 let (status, dst_token) =
2900 encrypted_directory.deprecated_get_token().await.expect("FIDL call failed");
2901 zx::Status::ok(status).expect("get_token failed");
2902 assert_eq!(
2903 unencryped_tmpfile
2904 .link_into(zx::Event::from(dst_token.unwrap()), "bar")
2905 .await
2906 .expect("link_into wire message failed")
2907 .map_err(zx::Status::err_from_raw)
2908 .expect_err("link_into passed unexpectedly"),
2909 zx::Status::BAD_STATE,
2910 );
2911
2912 fixture.close().await;
2913 }
2914
2915 #[fuchsia::test]
2916 async fn test_unnamed_temporary_file_in_locked_directory() {
2917 let fixture = TestFixture::new().await;
2918 let root = fixture.root();
2919
2920 let crypt = fixture.crypt().unwrap();
2922 let encrypted_directory = open_dir_checked(
2923 &root,
2924 "encrypted_directory",
2925 fio::Flags::FLAG_MAYBE_CREATE
2926 | fio::Flags::PROTOCOL_DIRECTORY
2927 | fio::PERM_READABLE
2928 | fio::PERM_WRITABLE,
2929 fio::Options::default(),
2930 )
2931 .await;
2932 crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2933 encrypted_directory
2934 .update_attributes(&fio::MutableNodeAttributes {
2935 encryption_policy: Some(fio::FscryptPolicy {
2936 key_identifier: WRAPPING_KEY_ID,
2937 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2938 }),
2939 ..Default::default()
2940 })
2941 .await
2942 .expect("update_attributes wire call failed")
2943 .map_err(zx::ok)
2944 .expect("update_attributes failed");
2945
2946 crypt.forget_wrapping_key(&WRAPPING_KEY_ID).unwrap();
2948
2949 assert_eq!(
2951 open_file(
2952 &encrypted_directory,
2953 ".",
2954 fio::Flags::PROTOCOL_FILE
2955 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2956 | fio::PERM_READABLE
2957 | fio::PERM_WRITABLE,
2958 &fio::Options::default()
2959 )
2960 .await
2961 .expect_err("Open succeeded unexpectedly")
2962 .root_cause()
2963 .downcast_ref::<zx::Status>()
2964 .expect("No status"),
2965 &zx::Status::UNAVAILABLE,
2966 );
2967 fixture.close().await;
2968 }
2969
2970 #[fuchsia::test]
2971 async fn test_unnamed_temporary_file_link_into_with_race() {
2972 let fixture = TestFixture::new().await;
2973 let root = fixture.root();
2974
2975 for i in 1..100 {
2976 let tmpfile = open_file_checked(
2977 &root,
2978 ".",
2979 fio::Flags::PROTOCOL_FILE
2980 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2981 | fio::PERM_READABLE
2982 | fio::PERM_WRITABLE,
2983 &fio::Options::default(),
2984 )
2985 .await;
2986
2987 let (tmpfile_clone1, tmpfile_server1) =
2989 fidl::endpoints::create_proxy::<fio::FileMarker>();
2990 tmpfile.clone(tmpfile_server1.into_channel().into()).expect("clone failed");
2991 let (tmpfile_clone2, tmpfile_server2) =
2992 fidl::endpoints::create_proxy::<fio::FileMarker>();
2993 tmpfile.clone(tmpfile_server2.into_channel().into()).expect("clone failed");
2994
2995 let sub_dir = open_dir_checked(
2998 &root,
2999 "A",
3000 fio::Flags::PROTOCOL_DIRECTORY
3001 | fio::PERM_READABLE
3002 | fio::PERM_WRITABLE
3003 | fio::Flags::FLAG_MAYBE_CREATE,
3004 fio::Options::default(),
3005 )
3006 .await;
3007
3008 let (status, dst_token1) =
3010 sub_dir.deprecated_get_token().await.expect("FIDL call failed");
3011 zx::Status::ok(status).expect("get_token failed");
3012 let (status, dst_token2) =
3013 sub_dir.deprecated_get_token().await.expect("FIDL call failed");
3014 zx::Status::ok(status).expect("get_token failed");
3015
3016 join!(
3017 fasync::Task::spawn(async move {
3018 tmpfile_clone1
3019 .link_into(zx::Event::from(dst_token1.unwrap()), &(2 * i).to_string())
3020 .await
3021 .expect("link_into wire message failed")
3022 .expect("link_into failed");
3023 }),
3024 fasync::Task::spawn(async move {
3025 tmpfile_clone2
3026 .link_into(zx::Event::from(dst_token2.unwrap()), &(2 * i + 1).to_string())
3027 .await
3028 .expect("link_into wire message failed")
3029 .expect("link_into failed");
3030 })
3031 );
3032 let (_, immutable_attributes) = tmpfile
3033 .get_attributes(fio::NodeAttributesQuery::LINK_COUNT)
3034 .await
3035 .expect("Failed get_attributes wire call")
3036 .expect("get_attributes failed");
3037 assert_eq!(immutable_attributes.link_count.unwrap(), 2);
3038 close_file_checked(tmpfile).await;
3039 }
3040 fixture.close().await;
3041 }
3042
3043 #[fuchsia::test]
3044 async fn test_update_attributes_persists() {
3045 const FILE: &str = "foo";
3046 let mtime = Some(Timestamp::now().as_nanos());
3047 let atime = Some(Timestamp::now().as_nanos());
3048 let mode = Some(111);
3049
3050 let device = {
3051 let fixture = TestFixture::new().await;
3052 let root = fixture.root();
3053
3054 let file = open_file_checked(
3055 &root,
3056 FILE,
3057 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_FILE,
3058 &fio::Options::default(),
3059 )
3060 .await;
3061
3062 file.update_attributes(&fio::MutableNodeAttributes {
3063 modification_time: mtime,
3064 access_time: atime,
3065 mode: Some(111),
3066 ..Default::default()
3067 })
3068 .await
3069 .expect("update_attributes FIDL call failed")
3070 .map_err(zx::ok)
3071 .expect("update_attributes failed");
3072
3073 fixture.close().await
3075 };
3076
3077 let fixture =
3078 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
3079 .await;
3080 let root = fixture.root();
3081 let file = open_file_checked(
3082 &root,
3083 FILE,
3084 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
3085 &fio::Options::default(),
3086 )
3087 .await;
3088
3089 let (mutable_attributes, _immutable_attributes) = file
3090 .get_attributes(
3091 fio::NodeAttributesQuery::MODIFICATION_TIME
3092 | fio::NodeAttributesQuery::ACCESS_TIME
3093 | fio::NodeAttributesQuery::MODE,
3094 )
3095 .await
3096 .expect("update_attributesFIDL call failed")
3097 .map_err(zx::ok)
3098 .expect("get_attributes failed");
3099 assert_eq!(mutable_attributes.modification_time, mtime);
3100 assert_eq!(mutable_attributes.access_time, atime);
3101 assert_eq!(mutable_attributes.mode, mode);
3102 fixture.close().await;
3103 }
3104
3105 #[fuchsia::test]
3106 async fn test_atime_from_pending_access_time_update_request() {
3107 const FILE: &str = "foo";
3108
3109 let (device, expected_atime, expected_ctime) = {
3110 let fixture = TestFixture::new().await;
3111 let root = fixture.root();
3112
3113 let file = open_file_checked(
3114 &root,
3115 FILE,
3116 fio::Flags::FLAG_MAYBE_CREATE
3117 | fio::PERM_WRITABLE
3118 | fio::Flags::PROTOCOL_FILE
3119 | fio::Flags::PERM_GET_ATTRIBUTES,
3120 &fio::Options::default(),
3121 )
3122 .await;
3123
3124 let (mutable_attributes, immutable_attributes) = file
3125 .get_attributes(
3126 fio::NodeAttributesQuery::CHANGE_TIME
3127 | fio::NodeAttributesQuery::ACCESS_TIME
3128 | fio::NodeAttributesQuery::MODIFICATION_TIME,
3129 )
3130 .await
3131 .expect("update_attributes FIDL call failed")
3132 .map_err(zx::ok)
3133 .expect("get_attributes failed");
3134 let initial_ctime = immutable_attributes.change_time;
3135 let initial_atime = mutable_attributes.access_time;
3136 assert_eq!(initial_atime, initial_ctime);
3138 assert_eq!(initial_atime, mutable_attributes.modification_time);
3139
3140 let (mutable_attributes, immutable_attributes) = file
3144 .get_attributes(
3145 fio::NodeAttributesQuery::CHANGE_TIME
3146 | fio::NodeAttributesQuery::ACCESS_TIME
3147 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
3148 )
3149 .await
3150 .expect("update_attributes FIDL call failed")
3151 .map_err(zx::ok)
3152 .expect("get_attributes failed");
3153 assert!(initial_atime < mutable_attributes.access_time);
3155 let updated_atime = mutable_attributes.access_time;
3156 assert_eq!(initial_ctime, immutable_attributes.change_time);
3159
3160 let (mutable_attributes, _) = file
3161 .get_attributes(
3162 fio::NodeAttributesQuery::ACCESS_TIME
3163 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
3164 )
3165 .await
3166 .expect("update_attributes FIDL call failed")
3167 .map_err(zx::ok)
3168 .expect("get_attributes failed");
3169 assert_eq!(updated_atime, mutable_attributes.access_time);
3171
3172 (fixture.close().await, mutable_attributes.access_time, initial_ctime)
3173 };
3174
3175 let fixture =
3176 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
3177 .await;
3178 let root = fixture.root();
3179 let file = open_file_checked(
3180 &root,
3181 FILE,
3182 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
3183 &fio::Options::default(),
3184 )
3185 .await;
3186
3187 let (mutable_attributes, immutable_attributes) = file
3189 .get_attributes(
3190 fio::NodeAttributesQuery::CHANGE_TIME | fio::NodeAttributesQuery::ACCESS_TIME,
3191 )
3192 .await
3193 .expect("update_attributesFIDL call failed")
3194 .map_err(zx::ok)
3195 .expect("get_attributes failed");
3196
3197 assert_eq!(immutable_attributes.change_time, expected_ctime);
3198 assert_eq!(mutable_attributes.access_time, expected_atime);
3199 fixture.close().await;
3200 }
3201
3202 #[fuchsia::test(threads = 10)]
3203 async fn test_delete_with_dirty_bytes_and_no_open_handles() {
3204 let fixture = TestFixture::new().await;
3205 let root = fixture.root();
3206
3207 let file = open_file_checked(
3208 &root,
3209 "foo",
3210 fio::Flags::FLAG_MAYBE_CREATE
3211 | fio::PERM_READABLE
3212 | fio::PERM_WRITABLE
3213 | fio::Flags::PROTOCOL_FILE,
3214 &Default::default(),
3215 )
3216 .await;
3217
3218 file.resize(4096)
3219 .await
3220 .expect("resize failed")
3221 .map_err(Status::err_from_raw)
3222 .expect("resize error");
3223
3224 let vmo = file
3225 .get_backing_memory(
3226 fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
3227 )
3228 .await
3229 .expect("Failed to make FIDL call")
3230 .map_err(Status::err_from_raw)
3231 .expect("Failed to get VMO");
3232
3233 file.sync().await.unwrap().unwrap();
3235
3236 drop(file);
3238
3239 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3241
3242 vmo.write(&[1, 2, 3, 4], 0).expect("vmo write failed");
3244
3245 drop(vmo);
3248
3249 fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3251
3252 root.unlink("foo", &fio::UnlinkOptions::default())
3254 .await
3255 .expect("unlink failed")
3256 .map_err(Status::err_from_raw)
3257 .expect("unlink error");
3258
3259 fixture.close().await;
3260 }
3261
3262 #[fuchsia::test]
3266 async fn test_close_file_before_writing_to_stream() {
3267 const FILE_NAME: &str = "foo";
3268
3269 let fixture = TestFixture::new().await;
3270 let root = fixture.root();
3271 let file = open_file_checked(
3272 &root,
3273 FILE_NAME,
3274 fio::Flags::FLAG_MAYBE_CREATE
3275 | fio::PERM_READABLE
3276 | fio::PERM_WRITABLE
3277 | fio::Flags::PROTOCOL_FILE,
3278 &Default::default(),
3279 )
3280 .await;
3281
3282 let stream = file.describe().await.unwrap().stream.unwrap();
3283
3284 close_file_checked(file).await;
3285
3286 unblock(move || {
3287 stream
3288 .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3289 .expect_err("Write should get BAD_STATE");
3290 })
3291 .await;
3292
3293 fasync::Timer::new(Duration::from_millis(100)).await;
3296
3297 root.unlink(FILE_NAME, &fio::UnlinkOptions::default())
3299 .await
3300 .expect("unlink wire call failed")
3301 .expect("unlink failed");
3302
3303 fixture.close().await;
3304 }
3305
3306 #[fuchsia::test]
3312 async fn test_close_and_reopen_file_stream() {
3313 const FILE_NAME: &str = "foo";
3314
3315 let fixture = TestFixture::new().await;
3316 let root = fixture.root();
3317 let file = open_file_checked(
3318 &root,
3319 FILE_NAME,
3320 fio::Flags::FLAG_MAYBE_CREATE
3321 | fio::PERM_READABLE
3322 | fio::PERM_WRITABLE
3323 | fio::Flags::PROTOCOL_FILE,
3324 &Default::default(),
3325 )
3326 .await;
3327
3328 let page_size = page_size();
3329 file.resize(8 * page_size)
3330 .await
3331 .expect("resize failed")
3332 .map_err(Status::err_from_raw)
3333 .expect("resize error");
3334
3335 let stream1 = file.describe().await.unwrap().stream.unwrap();
3336 let stream1_dup = stream1.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3337
3338 close_file_checked(file).await;
3339
3340 let stream1_dup_clone = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3344 unblock(move || {
3345 stream1_dup_clone
3346 .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3347 .expect_err("Write should get BAD_STATE");
3348 })
3349 .await;
3350
3351 let file2 = open_file_checked(
3354 &root,
3355 FILE_NAME,
3356 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE | fio::PERM_WRITABLE,
3357 &Default::default(),
3358 )
3359 .await;
3360
3361 let stream2 = file2.describe().await.unwrap().stream.unwrap();
3362
3363 let stream1_dup_clone2 = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3367 let stream2_clone = stream2.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3368 unblock(move || {
3369 stream1_dup_clone2
3370 .write_at(zx::StreamWriteOptions::empty(), 1 * page_size, &[5, 6, 7, 8])
3371 .expect("Write on re-opened stream 1 dup should succeed");
3372 stream2_clone
3373 .write_at(zx::StreamWriteOptions::empty(), 2 * page_size, &[9, 10, 11, 12])
3374 .expect("Write on new stream 2 should succeed");
3375 })
3376 .await;
3377
3378 close_file_checked(file2).await;
3380
3381 unblock(move || {
3385 stream1_dup
3386 .write_at(zx::StreamWriteOptions::empty(), 3 * page_size, &[13, 14, 15, 16])
3387 .expect_err("Write on stream 1 dup should fail after final close");
3388 stream2
3389 .write_at(zx::StreamWriteOptions::empty(), 4 * page_size, &[17, 18, 19, 20])
3390 .expect_err("Write on stream 2 should fail after final close");
3391 })
3392 .await;
3393
3394 fixture.close().await;
3395 }
3396
3397 use test_case::test_case;
3398
3399 #[test_case(
3400 fxfs_crypto::EncryptionKey::Fxfs(fxfs_crypto::FxfsKey {
3401 wrapping_key_id: WRAPPING_KEY_ID,
3402 key: fxfs_crypto::WrappedKeyBytes::from([0xff; fxfs_crypto::FXFS_WRAPPED_KEY_SIZE]),
3403 });
3404 "fxfs"
3405 )]
3406 #[test_case(
3407 fxfs_crypto::EncryptionKey::FscryptInoLblk32File {
3408 key_identifier: WRAPPING_KEY_ID,
3409 };
3410 "fscrypt_file"
3411 )]
3412 #[fuchsia::test]
3413 async fn test_supported_wrapping_key_ids(key: fxfs_crypto::EncryptionKey) {
3414 use fxfs::object_store::transaction::{LockKey, Mutation, Options, lock_keys};
3415 use fxfs::object_store::{FSCRYPT_KEY_ID, ObjectKey, ObjectValue};
3416
3417 let fixture = TestFixture::new().await;
3418 let root = fixture.root();
3419
3420 let file = open_file_checked(
3421 &root,
3422 "key_test_file",
3423 fio::Flags::FLAG_MAYBE_CREATE
3424 | fio::Flags::PROTOCOL_FILE
3425 | fio::PERM_READABLE
3426 | fio::PERM_WRITABLE,
3427 &fio::Options::default(),
3428 )
3429 .await;
3430
3431 let (_, immutable_attributes) = file
3432 .get_attributes(fio::NodeAttributesQuery::ID)
3433 .await
3434 .expect("get_attributes wire call failed")
3435 .map_err(zx::Status::err_from_raw)
3436 .expect("get_attributes failed");
3437 let file_id = immutable_attributes.id.unwrap();
3438
3439 let store = fixture.volume().volume().store();
3440 let mut transaction = store
3441 .new_transaction(
3442 lock_keys![LockKey::object(store.store_object_id(), file_id)],
3443 Options::default(),
3444 )
3445 .await
3446 .expect("new_transaction failed");
3447
3448 transaction.add(
3449 store.store_object_id(),
3450 Mutation::replace_or_insert_object(
3451 ObjectKey::keys(file_id),
3452 ObjectValue::Keys(vec![(FSCRYPT_KEY_ID, key)].into()),
3453 ),
3454 );
3455 transaction.commit().await.expect("commit failed");
3456
3457 let (mutable_attributes, _) = file
3458 .get_attributes(fio::NodeAttributesQuery::ENCRYPTION_POLICY)
3459 .await
3460 .expect("get_attributes wire call failed")
3461 .map_err(zx::Status::err_from_raw)
3462 .expect("get_attributes failed");
3463
3464 assert_eq!(
3465 mutable_attributes.encryption_policy.map(|p| p.key_identifier),
3466 Some(WRAPPING_KEY_ID)
3467 );
3468
3469 fixture.close().await;
3470 }
3471
3472 #[fuchsia::test(threads = 3)]
3473 async fn test_teardown_with_pending_shrink_and_commit_failure() {
3474 use fxfs::errors::FxfsError;
3475
3476 let fail = Arc::new(AtomicBool::new(false));
3477 let fail_clone = fail.clone();
3478 let (mut hooks, fs_hooks) = fxfs::hooks::Hooks::new();
3479 hooks.set_pre_commit(move |_| {
3480 if fail_clone.load(std::sync::atomic::Ordering::Relaxed) {
3481 Err(FxfsError::Unavailable.into())
3482 } else {
3483 Ok(())
3484 }
3485 });
3486 let fixture = TestFixture::open(
3487 DeviceHolder::new(FakeDevice::new(16384, 512)),
3488 TestFixtureOptions { encrypted: false, hooks: Some(fs_hooks), ..Default::default() },
3489 )
3490 .await;
3491
3492 let root = fixture.root();
3493 let file = open_file_checked(
3494 &root,
3495 "test_file",
3496 fio::Flags::FLAG_MAYBE_CREATE
3497 | fio::Flags::PROTOCOL_FILE
3498 | fio::PERM_READABLE
3499 | fio::PERM_WRITABLE,
3500 &fio::Options::default(),
3501 )
3502 .await;
3503
3504 let page_size = page_size();
3506 file.resize(page_size * 4).await.unwrap().expect("resize failed");
3507 file.sync().await.unwrap().expect("sync failed");
3508
3509 fail.store(true, std::sync::atomic::Ordering::Relaxed);
3511
3512 let stream = file.describe().await.unwrap().stream.unwrap();
3514 unblock(move || {
3515 for i in 0..2 {
3516 stream
3517 .write_at(zx::StreamWriteOptions::empty(), i * page_size, &[1u8])
3518 .expect("write_at failed");
3519 }
3520 })
3521 .await;
3522
3523 file.resize(page_size * 2).await.unwrap().expect("resize failed");
3525
3526 drop(file);
3529
3530 fixture.close().await;
3535 }
3536}