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