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