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