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