Skip to main content

fxfs_platform/fuchsia/
file.rs

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