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::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.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            .new_transaction(
559                lock_keys![
560                    LockKey::object(store.store_object_id(), object_id),
561                    LockKey::object(store.store_object_id(), dir.object_id()),
562                ],
563                Options::default(),
564            )
565            .await
566            .map_err(map_to_status)?;
567
568        dir.check_fscrypt_hard_link_conditions(self.fscrypt_wrapping_key_id().await?)?;
569
570        let state = self.load_state();
571        let is_unnamed_temporary = state.is_unnamed_temporary();
572        let to_be_purged = state.to_be_purged();
573        if is_unnamed_temporary {
574            // Remove object from graveyard and link it to `name`.
575            dir.link_graveyard_object(transaction, &name, object_id, ObjectDescriptor::File, || {
576                self.mark_as_permanent()
577            })
578            .await
579        } else {
580            // Check that we're not unlinked.
581            if to_be_purged {
582                return Err(zx::Status::NOT_FOUND);
583            }
584            dir.link_object(transaction, &name, object_id, ObjectDescriptor::File).await
585        }
586    }
587
588    fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
589        Ok(self.handle.owner().filesystem_info_for_volume())
590    }
591
592    async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, Status> {
593        self.handle.store_handle().list_extended_attributes().await.map_err(map_to_status)
594    }
595
596    async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, Status> {
597        self.handle.store_handle().get_extended_attribute(name).await.map_err(map_to_status)
598    }
599
600    async fn set_extended_attribute(
601        &self,
602        name: Vec<u8>,
603        value: Vec<u8>,
604        mode: fio::SetExtendedAttributeMode,
605    ) -> Result<(), Status> {
606        self.handle
607            .store_handle()
608            .set_extended_attribute(name, value, mode.into())
609            .await
610            .map_err(map_to_status)
611    }
612
613    async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Status> {
614        self.handle.store_handle().remove_extended_attribute(name).await.map_err(map_to_status)
615    }
616}
617
618impl File for FxFile {
619    fn writable(&self) -> bool {
620        true
621    }
622
623    async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
624        Ok(())
625    }
626
627    async fn truncate(&self, length: u64) -> Result<(), Status> {
628        self.handle.truncate(length).await.map_err(map_to_status)?;
629        Ok(())
630    }
631
632    async fn enable_verity(&self, options: fio::VerificationOptions) -> Result<(), Status> {
633        self.handle.set_read_only();
634        self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
635        self.handle.uncached_handle().enable_verity(options).await.map_err(map_to_status)
636    }
637
638    // Returns a VMO handle that supports paging.
639    async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
640        // We do not support executable VMO handles.
641        if flags.contains(fio::VmoFlags::EXECUTE) {
642            error!("get_backing_memory does not support execute rights!");
643            return Err(Status::NOT_SUPPORTED);
644        }
645
646        let vmo = self.handle.vmo();
647        let mut rights = zx::Rights::BASIC | zx::Rights::MAP | zx::Rights::GET_PROPERTY;
648        if flags.contains(fio::VmoFlags::READ) {
649            rights |= zx::Rights::READ;
650        }
651        if flags.contains(fio::VmoFlags::WRITE) {
652            rights |= zx::Rights::WRITE;
653        }
654
655        let child_vmo = if flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
656            // Allow for the VMO's content size and name to be changed even without ZX_RIGHT_WRITE.
657            rights |= zx::Rights::SET_PROPERTY;
658            let mut child_options = zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE;
659            if flags.contains(fio::VmoFlags::WRITE) {
660                child_options |= zx::VmoChildOptions::RESIZABLE;
661                rights |= zx::Rights::RESIZE;
662            }
663            vmo.create_child(child_options, 0, vmo.get_stream_size()?)?
664        } else {
665            vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0)?
666        };
667
668        let child_vmo = child_vmo.replace_handle(rights)?;
669        if self.handle.owner().pager().watch_for_zero_children(self).map_err(map_to_status)? {
670            // Take an open count so that we keep this object alive if it is unlinked.
671            self.open_count_add_one();
672        }
673        Ok(child_vmo)
674    }
675
676    async fn get_size(&self) -> Result<u64, Status> {
677        Ok(self.handle.get_size())
678    }
679
680    async fn update_attributes(
681        &self,
682        attributes: fio::MutableNodeAttributes,
683    ) -> Result<(), Status> {
684        if attributes == fio::MutableNodeAttributes::default() {
685            return Ok(());
686        }
687
688        self.handle.update_attributes(&attributes).await.map_err(map_to_status)?;
689        Ok(())
690    }
691
692    async fn allocate(
693        &self,
694        offset: u64,
695        length: u64,
696        _mode: fio::AllocateMode,
697    ) -> Result<(), Status> {
698        // NB: FILE_BIG is used so the error converts to EFBIG when passed through starnix, which
699        // is the required error code when the requested range is larger than the file size.
700        let range = offset..offset.checked_add(length).ok_or(Status::FILE_BIG)?;
701        self.handle.allocate(range).await.map_err(map_to_status)
702    }
703
704    async fn sync(&self, mode: SyncMode) -> Result<(), Status> {
705        self.handle.flush(FlushType::Sync).await.map_err(map_to_status)?;
706
707        // TODO(https://fxbug.dev/42178163): at the moment, this doesn't send a flush to the device, which
708        // doesn't match minfs.
709        if mode == SyncMode::Normal {
710            self.handle
711                .store()
712                .filesystem()
713                .sync(SyncOptions::default())
714                .await
715                .map_err(map_to_status)?;
716        }
717
718        Ok(())
719    }
720}
721
722#[fxfs_trace::trace]
723impl PagerBacked for FxFile {
724    fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
725        let mut old = self.load_state();
726        loop {
727            if old.open_count() == 0 {
728                return Err(self);
729            }
730
731            assert!(old.open_count() < MAX_OPEN_COUNTS);
732
733            match self.state.compare_exchange_weak(
734                old.0,
735                old.0 + 1,
736                Ordering::Relaxed,
737                Ordering::Relaxed,
738            ) {
739                Ok(_) => return Ok(OpenedNode(self)),
740                Err(new_value) => old.0 = new_value,
741            }
742        }
743    }
744
745    fn pager(&self) -> &crate::pager::Pager {
746        self.handle.owner().pager()
747    }
748
749    fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
750        &self.pager_packet_receiver_registration
751    }
752
753    fn vmo(&self) -> &zx::Vmo {
754        self.handle.vmo()
755    }
756
757    fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
758        default_page_in(self, range, READ_AHEAD_SIZE);
759    }
760
761    #[trace]
762    fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
763        let (valid_pages, invalid_pages) = range.split(MAX_FILE_SIZE);
764        if let Some(invalid_pages) = invalid_pages {
765            invalid_pages.report_failure(zx::Status::FILE_BIG);
766        }
767        let range = match valid_pages {
768            Some(range) => range,
769            None => return,
770        };
771
772        let byte_count = range.len();
773        self.handle.owner().clone().report_pager_dirty(byte_count, move || {
774            match self.handle.mark_dirty(range) {
775                Ok(dirty_bytes) => {
776                    // If there's a whole batch worth to write. Just write it. Spurious failures
777                    // here are fine. This is best effort so keep it cheap.
778                    if dirty_bytes > BACKGROUND_FLUSH_THRESHOLD
779                        && !self.background_flush_running.swap(true, Ordering::Relaxed)
780                    {
781                        let owner = self.handle.owner().clone();
782                        owner.spawn(async move {
783                            // Ignore the result, the flush call already logs the errors.
784                            let _ = self.handle.flush(FlushType::Background).await;
785                            // If this future gets dropped before resetting this it means the
786                            // volume is shutting down anyways.
787                            self.background_flush_running.store(false, Ordering::Relaxed);
788                        });
789                    }
790                }
791                Err(_) => {
792                    // Undo the report of the dirty pages since mark_dirty failed.
793                    self.handle.owner().report_pager_clean(byte_count)
794                }
795            }
796        });
797    }
798
799    fn on_zero_children(self: Arc<Self>) {
800        // Drop the open count that we took in `get_backing_memory`.
801        self.open_count_sub_one();
802    }
803
804    fn byte_size(&self) -> u64 {
805        self.handle.uncached_size()
806    }
807
808    #[trace("len" => (range.end - range.start))]
809    async fn aligned_read(&self, range: Range<u64>) -> Result<buffer::Buffer<'_>, Error> {
810        let buffer = self.handle.read_uncached(range).await?;
811        Ok(buffer)
812    }
813}
814
815impl GetVmo for FxFile {
816    const PAGER_ON_FIDL_EXECUTOR: bool = true;
817
818    fn get_vmo(&self) -> &zx::Vmo {
819        self.vmo()
820    }
821}
822
823#[cfg(test)]
824mod tests {
825    use super::FxFile;
826    use crate::fuchsia::paged_object_handle::BACKGROUND_FLUSH_THRESHOLD;
827    use crate::fuchsia::testing::{
828        TestFixture, TestFixtureOptions, close_file_checked, open_dir_checked, open_file,
829        open_file_checked,
830    };
831    use anyhow::format_err;
832    use fidl_fuchsia_io as fio;
833    use fsverity_merkle::{FsVerityHasher, FsVerityHasherOptions};
834    use fuchsia_async::{self as fasync, unblock};
835    use fuchsia_fs::file;
836    use futures::join;
837    use fxfs::fsck::fsck;
838    use fxfs::object_handle::INVALID_OBJECT_ID;
839    use fxfs::object_store::Timestamp;
840    use fxfs_crypto::WrappingKeyId;
841    use rand::{Rng, rng};
842    use std::sync::Arc;
843    use std::sync::atomic::{self, AtomicBool};
844    use std::time::Duration;
845    use storage_device::DeviceHolder;
846    use storage_device::fake_device::FakeDevice;
847    use zx::Status;
848
849    const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(123);
850
851    #[fuchsia::test(threads = 10)]
852    async fn test_empty_file() {
853        let fixture = TestFixture::new().await;
854        let root = fixture.root();
855
856        let file = open_file_checked(
857            &root,
858            "foo",
859            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
860            &Default::default(),
861        )
862        .await;
863
864        let buf = file
865            .read(fio::MAX_BUF)
866            .await
867            .expect("FIDL call failed")
868            .map_err(Status::from_raw)
869            .expect("read failed");
870        assert!(buf.is_empty());
871
872        let (mutable_attrs, immutable_attrs) = file
873            .get_attributes(fio::NodeAttributesQuery::all())
874            .await
875            .expect("FIDL call failed")
876            .expect("GetAttributes failed");
877        assert_ne!(immutable_attrs.id.unwrap(), INVALID_OBJECT_ID);
878        assert_eq!(immutable_attrs.content_size.unwrap(), 0u64);
879        assert_eq!(immutable_attrs.storage_size.unwrap(), 0u64);
880        assert_eq!(immutable_attrs.link_count.unwrap(), 1u64);
881        assert_ne!(mutable_attrs.creation_time.unwrap(), 0u64);
882        assert_ne!(mutable_attrs.modification_time.unwrap(), 0u64);
883        assert_eq!(mutable_attrs.creation_time.unwrap(), mutable_attrs.modification_time.unwrap());
884
885        close_file_checked(file).await;
886        fixture.close().await;
887    }
888
889    #[fuchsia::test(threads = 10)]
890    async fn test_write_read() {
891        let fixture = TestFixture::new().await;
892        let root = fixture.root();
893
894        let file = open_file_checked(
895            &root,
896            "foo",
897            fio::Flags::FLAG_MAYBE_CREATE
898                | fio::PERM_READABLE
899                | fio::PERM_WRITABLE
900                | fio::Flags::PROTOCOL_FILE,
901            &Default::default(),
902        )
903        .await;
904
905        let inputs = vec!["hello, ", "world!"];
906        let expected_output = "hello, world!";
907        for input in inputs {
908            let bytes_written = file
909                .write(input.as_bytes())
910                .await
911                .expect("write failed")
912                .map_err(Status::from_raw)
913                .expect("File write was successful");
914            assert_eq!(bytes_written as usize, input.as_bytes().len());
915        }
916
917        let buf = file
918            .read_at(fio::MAX_BUF, 0)
919            .await
920            .expect("read_at failed")
921            .map_err(Status::from_raw)
922            .expect("File read was successful");
923        assert_eq!(buf.len(), expected_output.as_bytes().len());
924        assert!(buf.iter().eq(expected_output.as_bytes().iter()));
925
926        let (_, immutable_attributes) = file
927            .get_attributes(
928                fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
929            )
930            .await
931            .expect("FIDL call failed")
932            .expect("get_attributes failed");
933
934        assert_eq!(
935            immutable_attributes.content_size.unwrap(),
936            expected_output.as_bytes().len() as u64
937        );
938        assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size() as u64);
939
940        let () = file
941            .sync()
942            .await
943            .expect("FIDL call failed")
944            .map_err(Status::from_raw)
945            .expect("sync failed");
946
947        let (_, immutable_attributes) = file
948            .get_attributes(
949                fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
950            )
951            .await
952            .expect("FIDL call failed")
953            .expect("get_attributes failed");
954
955        assert_eq!(
956            immutable_attributes.content_size.unwrap(),
957            expected_output.as_bytes().len() as u64
958        );
959        assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size() as u64);
960
961        close_file_checked(file).await;
962        fixture.close().await;
963    }
964
965    #[fuchsia::test(threads = 10)]
966    async fn test_page_in() {
967        let input = "hello, world!";
968        let reused_device = {
969            let fixture = TestFixture::new().await;
970            let root = fixture.root();
971
972            let file = open_file_checked(
973                &root,
974                "foo",
975                fio::Flags::FLAG_MAYBE_CREATE
976                    | fio::PERM_READABLE
977                    | fio::PERM_WRITABLE
978                    | fio::Flags::PROTOCOL_FILE,
979                &Default::default(),
980            )
981            .await;
982
983            let bytes_written = file
984                .write(input.as_bytes())
985                .await
986                .expect("write failed")
987                .map_err(Status::from_raw)
988                .expect("File write was successful");
989            assert_eq!(bytes_written as usize, input.as_bytes().len());
990            assert!(file.sync().await.expect("Sync failed").is_ok());
991
992            close_file_checked(file).await;
993            fixture.close().await
994        };
995
996        let fixture = TestFixture::open(
997            reused_device,
998            TestFixtureOptions { format: false, ..Default::default() },
999        )
1000        .await;
1001        let root = fixture.root();
1002
1003        let file = open_file_checked(
1004            &root,
1005            "foo",
1006            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1007            &Default::default(),
1008        )
1009        .await;
1010
1011        let vmo =
1012            file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1013        let mut readback = vec![0; input.as_bytes().len()];
1014        assert!(vmo.read(&mut readback, 0).is_ok());
1015        assert_eq!(input.as_bytes(), readback);
1016
1017        close_file_checked(file).await;
1018        fixture.close().await;
1019    }
1020
1021    #[fuchsia::test(threads = 10)]
1022    async fn test_page_in_io_error() {
1023        let mut device = FakeDevice::new(8192, 512);
1024        let succeed_requests = Arc::new(AtomicBool::new(true));
1025        let succeed_requests_clone = succeed_requests.clone();
1026        device.set_op_callback(Box::new(move |_| {
1027            if succeed_requests_clone.load(atomic::Ordering::Relaxed) {
1028                Ok(())
1029            } else {
1030                Err(format_err!("Fake error."))
1031            }
1032        }));
1033
1034        let input = "hello, world!";
1035        let reused_device = {
1036            let fixture = TestFixture::open(
1037                DeviceHolder::new(device),
1038                TestFixtureOptions { format: true, ..Default::default() },
1039            )
1040            .await;
1041            let root = fixture.root();
1042
1043            let file = open_file_checked(
1044                &root,
1045                "foo",
1046                fio::Flags::FLAG_MAYBE_CREATE
1047                    | fio::PERM_READABLE
1048                    | fio::PERM_WRITABLE
1049                    | fio::Flags::PROTOCOL_FILE,
1050                &Default::default(),
1051            )
1052            .await;
1053
1054            let bytes_written = file
1055                .write(input.as_bytes())
1056                .await
1057                .expect("write failed")
1058                .map_err(Status::from_raw)
1059                .expect("File write was successful");
1060            assert_eq!(bytes_written as usize, input.as_bytes().len());
1061
1062            close_file_checked(file).await;
1063            fixture.close().await
1064        };
1065
1066        let fixture = TestFixture::open(
1067            reused_device,
1068            TestFixtureOptions { format: false, ..Default::default() },
1069        )
1070        .await;
1071        let root = fixture.root();
1072
1073        let file = open_file_checked(
1074            &root,
1075            "foo",
1076            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1077            &Default::default(),
1078        )
1079        .await;
1080
1081        let vmo =
1082            file.get_backing_memory(fio::VmoFlags::READ).await.expect("Fidl failure").unwrap();
1083        succeed_requests.store(false, atomic::Ordering::Relaxed);
1084        let mut readback = vec![0; input.as_bytes().len()];
1085        assert!(vmo.read(&mut readback, 0).is_err());
1086
1087        succeed_requests.store(true, atomic::Ordering::Relaxed);
1088        close_file_checked(file).await;
1089        fixture.close().await;
1090    }
1091
1092    #[fuchsia::test(threads = 10)]
1093    async fn test_writes_persist() {
1094        let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1095        for i in 0..2 {
1096            let fixture = TestFixture::open(
1097                device,
1098                TestFixtureOptions { format: i == 0, ..Default::default() },
1099            )
1100            .await;
1101            let root = fixture.root();
1102
1103            let flags = if i == 0 {
1104                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE
1105            } else {
1106                fio::PERM_READABLE | fio::PERM_WRITABLE
1107            };
1108            let file = open_file_checked(
1109                &root,
1110                "foo",
1111                flags | fio::Flags::PROTOCOL_FILE,
1112                &Default::default(),
1113            )
1114            .await;
1115
1116            if i == 0 {
1117                let _: u64 = file
1118                    .write(&vec![0xaa as u8; 8192])
1119                    .await
1120                    .expect("FIDL call failed")
1121                    .map_err(Status::from_raw)
1122                    .expect("File write was successful");
1123            } else {
1124                let buf = file
1125                    .read(8192)
1126                    .await
1127                    .expect("FIDL call failed")
1128                    .map_err(Status::from_raw)
1129                    .expect("File read was successful");
1130                assert_eq!(buf, vec![0xaa as u8; 8192]);
1131            }
1132
1133            let (_, immutable_attributes) = file
1134                .get_attributes(
1135                    fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1136                )
1137                .await
1138                .expect("FIDL call failed")
1139                .expect("get_attributes failed");
1140
1141            assert_eq!(immutable_attributes.content_size.unwrap(), 8192u64);
1142            assert_eq!(immutable_attributes.storage_size.unwrap(), 8192u64);
1143
1144            close_file_checked(file).await;
1145            device = fixture.close().await;
1146        }
1147    }
1148
1149    #[fuchsia::test(threads = 10)]
1150    async fn test_append() {
1151        let fixture = TestFixture::new().await;
1152        let root = fixture.root();
1153
1154        let inputs = vec!["hello, ", "world!"];
1155        let expected_output = "hello, world!";
1156        for input in inputs {
1157            let file = open_file_checked(
1158                &root,
1159                "foo",
1160                fio::Flags::FLAG_MAYBE_CREATE
1161                    | fio::PERM_READABLE
1162                    | fio::PERM_WRITABLE
1163                    | fio::Flags::FILE_APPEND
1164                    | fio::Flags::PROTOCOL_FILE,
1165                &Default::default(),
1166            )
1167            .await;
1168
1169            let bytes_written = file
1170                .write(input.as_bytes())
1171                .await
1172                .expect("FIDL call failed")
1173                .map_err(Status::from_raw)
1174                .expect("File write was successful");
1175            assert_eq!(bytes_written as usize, input.as_bytes().len());
1176            close_file_checked(file).await;
1177        }
1178
1179        let file = open_file_checked(
1180            &root,
1181            "foo",
1182            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1183            &Default::default(),
1184        )
1185        .await;
1186        let buf = file
1187            .read_at(fio::MAX_BUF, 0)
1188            .await
1189            .expect("FIDL call failed")
1190            .map_err(Status::from_raw)
1191            .expect("File read was successful");
1192        assert_eq!(buf.len(), expected_output.as_bytes().len());
1193        assert_eq!(&buf[..], expected_output.as_bytes());
1194
1195        let (_, immutable_attributes) = file
1196            .get_attributes(
1197                fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
1198            )
1199            .await
1200            .expect("FIDL call failed")
1201            .expect("get_attributes failed");
1202
1203        assert_eq!(
1204            immutable_attributes.content_size.unwrap(),
1205            expected_output.as_bytes().len() as u64
1206        );
1207        assert_eq!(immutable_attributes.storage_size.unwrap(), fixture.fs().block_size() as u64);
1208
1209        close_file_checked(file).await;
1210        fixture.close().await;
1211    }
1212
1213    #[fuchsia::test(threads = 10)]
1214    async fn test_seek() {
1215        let fixture = TestFixture::new().await;
1216        let root = fixture.root();
1217
1218        let file = open_file_checked(
1219            &root,
1220            "foo",
1221            fio::Flags::FLAG_MAYBE_CREATE
1222                | fio::PERM_READABLE
1223                | fio::PERM_WRITABLE
1224                | fio::Flags::PROTOCOL_FILE,
1225            &Default::default(),
1226        )
1227        .await;
1228
1229        let input = "hello, world!";
1230        let _: u64 = file
1231            .write(input.as_bytes())
1232            .await
1233            .expect("FIDL call failed")
1234            .map_err(Status::from_raw)
1235            .expect("File write was successful");
1236
1237        {
1238            let offset = file
1239                .seek(fio::SeekOrigin::Start, 0)
1240                .await
1241                .expect("FIDL call failed")
1242                .map_err(Status::from_raw)
1243                .expect("seek was successful");
1244            assert_eq!(offset, 0);
1245            let buf = file
1246                .read(5)
1247                .await
1248                .expect("FIDL call failed")
1249                .map_err(Status::from_raw)
1250                .expect("File read was successful");
1251            assert!(buf.iter().eq("hello".as_bytes().iter()));
1252        }
1253        {
1254            let offset = file
1255                .seek(fio::SeekOrigin::Current, 2)
1256                .await
1257                .expect("FIDL call failed")
1258                .map_err(Status::from_raw)
1259                .expect("seek was successful");
1260            assert_eq!(offset, 7);
1261            let buf = file
1262                .read(5)
1263                .await
1264                .expect("FIDL call failed")
1265                .map_err(Status::from_raw)
1266                .expect("File read was successful");
1267            assert!(buf.iter().eq("world".as_bytes().iter()));
1268        }
1269        {
1270            let offset = file
1271                .seek(fio::SeekOrigin::Current, -5)
1272                .await
1273                .expect("FIDL call failed")
1274                .map_err(Status::from_raw)
1275                .expect("seek was successful");
1276            assert_eq!(offset, 7);
1277            let buf = file
1278                .read(5)
1279                .await
1280                .expect("FIDL call failed")
1281                .map_err(Status::from_raw)
1282                .expect("File read was successful");
1283            assert!(buf.iter().eq("world".as_bytes().iter()));
1284        }
1285        {
1286            let offset = file
1287                .seek(fio::SeekOrigin::End, -1)
1288                .await
1289                .expect("FIDL call failed")
1290                .map_err(Status::from_raw)
1291                .expect("seek was successful");
1292            assert_eq!(offset, 12);
1293            let buf = file
1294                .read(1)
1295                .await
1296                .expect("FIDL call failed")
1297                .map_err(Status::from_raw)
1298                .expect("File read was successful");
1299            assert!(buf.iter().eq("!".as_bytes().iter()));
1300        }
1301
1302        close_file_checked(file).await;
1303        fixture.close().await;
1304    }
1305
1306    #[fuchsia::test(threads = 10)]
1307    async fn test_resize_extend() {
1308        let fixture = TestFixture::new().await;
1309        let root = fixture.root();
1310
1311        let file = open_file_checked(
1312            &root,
1313            "foo",
1314            fio::Flags::FLAG_MAYBE_CREATE
1315                | fio::PERM_READABLE
1316                | fio::PERM_WRITABLE
1317                | fio::Flags::PROTOCOL_FILE,
1318            &Default::default(),
1319        )
1320        .await;
1321
1322        let input = "hello, world!";
1323        let len: usize = 16 * 1024;
1324
1325        let _: u64 = file
1326            .write(input.as_bytes())
1327            .await
1328            .expect("FIDL call failed")
1329            .map_err(Status::from_raw)
1330            .expect("File write was successful");
1331
1332        let offset = file
1333            .seek(fio::SeekOrigin::Start, 0)
1334            .await
1335            .expect("FIDL call failed")
1336            .map_err(Status::from_raw)
1337            .expect("Seek was successful");
1338        assert_eq!(offset, 0);
1339
1340        let () = file
1341            .resize(len as u64)
1342            .await
1343            .expect("resize failed")
1344            .map_err(Status::from_raw)
1345            .expect("resize error");
1346
1347        let mut expected_buf = vec![0 as u8; len];
1348        expected_buf[..input.as_bytes().len()].copy_from_slice(input.as_bytes());
1349
1350        let buf = file::read(&file).await.expect("File read was successful");
1351        assert_eq!(buf.len(), len);
1352        assert_eq!(buf, expected_buf);
1353
1354        // Write something at the end of the gap.
1355        expected_buf[len - 1..].copy_from_slice("a".as_bytes());
1356
1357        let _: u64 = file
1358            .write_at("a".as_bytes(), (len - 1) as u64)
1359            .await
1360            .expect("FIDL call failed")
1361            .map_err(Status::from_raw)
1362            .expect("File write was successful");
1363
1364        let offset = file
1365            .seek(fio::SeekOrigin::Start, 0)
1366            .await
1367            .expect("FIDL call failed")
1368            .map_err(Status::from_raw)
1369            .expect("Seek was successful");
1370        assert_eq!(offset, 0);
1371
1372        let buf = file::read(&file).await.expect("File read was successful");
1373        assert_eq!(buf.len(), len);
1374        assert_eq!(buf, expected_buf);
1375
1376        close_file_checked(file).await;
1377        fixture.close().await;
1378    }
1379
1380    #[fuchsia::test(threads = 10)]
1381    async fn test_resize_shrink() {
1382        let fixture = TestFixture::new().await;
1383        let root = fixture.root();
1384
1385        let file = open_file_checked(
1386            &root,
1387            "foo",
1388            fio::Flags::FLAG_MAYBE_CREATE
1389                | fio::PERM_READABLE
1390                | fio::PERM_WRITABLE
1391                | fio::Flags::PROTOCOL_FILE,
1392            &Default::default(),
1393        )
1394        .await;
1395
1396        let len: usize = 2 * 1024;
1397        let input = {
1398            let mut v = vec![0 as u8; len];
1399            for i in 0..v.len() {
1400                v[i] = ('a' as u8) + (i % 13) as u8;
1401            }
1402            v
1403        };
1404        let short_len: usize = 513;
1405
1406        file::write(&file, &input).await.expect("File write was successful");
1407
1408        let () = file
1409            .resize(short_len as u64)
1410            .await
1411            .expect("resize failed")
1412            .map_err(Status::from_raw)
1413            .expect("resize error");
1414
1415        let offset = file
1416            .seek(fio::SeekOrigin::Start, 0)
1417            .await
1418            .expect("FIDL call failed")
1419            .map_err(Status::from_raw)
1420            .expect("Seek was successful");
1421        assert_eq!(offset, 0);
1422
1423        let buf = file::read(&file).await.expect("File read was successful");
1424        assert_eq!(buf.len(), short_len);
1425        assert_eq!(buf, input[..short_len]);
1426
1427        // Resize to the original length and verify the data's zeroed.
1428        let () = file
1429            .resize(len as u64)
1430            .await
1431            .expect("resize failed")
1432            .map_err(Status::from_raw)
1433            .expect("resize error");
1434
1435        let expected_buf = {
1436            let mut v = vec![0 as u8; len];
1437            v[..short_len].copy_from_slice(&input[..short_len]);
1438            v
1439        };
1440
1441        let offset = file
1442            .seek(fio::SeekOrigin::Start, 0)
1443            .await
1444            .expect("seek failed")
1445            .map_err(Status::from_raw)
1446            .expect("Seek was successful");
1447        assert_eq!(offset, 0);
1448
1449        let buf = file::read(&file).await.expect("File read was successful");
1450        assert_eq!(buf.len(), len);
1451        assert_eq!(buf, expected_buf);
1452
1453        close_file_checked(file).await;
1454        fixture.close().await;
1455    }
1456
1457    #[fuchsia::test(threads = 10)]
1458    async fn test_resize_shrink_repeated() {
1459        let fixture = TestFixture::new().await;
1460        let root = fixture.root();
1461
1462        let file = open_file_checked(
1463            &root,
1464            "foo",
1465            fio::Flags::FLAG_MAYBE_CREATE
1466                | fio::PERM_READABLE
1467                | fio::PERM_WRITABLE
1468                | fio::Flags::PROTOCOL_FILE,
1469            &Default::default(),
1470        )
1471        .await;
1472
1473        let orig_len: usize = 4 * 1024;
1474        let mut len = orig_len;
1475        let input = {
1476            let mut v = vec![0 as u8; len];
1477            for i in 0..v.len() {
1478                v[i] = ('a' as u8) + (i % 13) as u8;
1479            }
1480            v
1481        };
1482        let short_len: usize = 513;
1483
1484        file::write(&file, &input).await.expect("File write was successful");
1485
1486        while len > short_len {
1487            len -= std::cmp::min(len - short_len, 512);
1488            let () = file
1489                .resize(len as u64)
1490                .await
1491                .expect("resize failed")
1492                .map_err(Status::from_raw)
1493                .expect("resize error");
1494        }
1495
1496        let offset = file
1497            .seek(fio::SeekOrigin::Start, 0)
1498            .await
1499            .expect("Seek failed")
1500            .map_err(Status::from_raw)
1501            .expect("Seek was successful");
1502        assert_eq!(offset, 0);
1503
1504        let buf = file::read(&file).await.expect("File read was successful");
1505        assert_eq!(buf.len(), short_len);
1506        assert_eq!(buf, input[..short_len]);
1507
1508        // Resize to the original length and verify the data's zeroed.
1509        let () = file
1510            .resize(orig_len as u64)
1511            .await
1512            .expect("resize failed")
1513            .map_err(Status::from_raw)
1514            .expect("resize error");
1515
1516        let expected_buf = {
1517            let mut v = vec![0 as u8; orig_len];
1518            v[..short_len].copy_from_slice(&input[..short_len]);
1519            v
1520        };
1521
1522        let offset = file
1523            .seek(fio::SeekOrigin::Start, 0)
1524            .await
1525            .expect("seek failed")
1526            .map_err(Status::from_raw)
1527            .expect("Seek was successful");
1528        assert_eq!(offset, 0);
1529
1530        let buf = file::read(&file).await.expect("File read was successful");
1531        assert_eq!(buf.len(), orig_len);
1532        assert_eq!(buf, expected_buf);
1533
1534        close_file_checked(file).await;
1535        fixture.close().await;
1536    }
1537
1538    #[fuchsia::test(threads = 10)]
1539    async fn test_unlink_with_open_race() {
1540        let fixture = Arc::new(TestFixture::new().await);
1541        let fixture1 = fixture.clone();
1542        let fixture2 = fixture.clone();
1543        let fixture3 = fixture.clone();
1544        let done = Arc::new(AtomicBool::new(false));
1545        let done1 = done.clone();
1546        let done2 = done.clone();
1547        join!(
1548            fasync::Task::spawn(async move {
1549                let root = fixture1.root();
1550                while !done1.load(atomic::Ordering::Relaxed) {
1551                    let file = open_file_checked(
1552                        &root,
1553                        "foo",
1554                        fio::Flags::FLAG_MAYBE_CREATE
1555                            | fio::PERM_READABLE
1556                            | fio::PERM_WRITABLE
1557                            | fio::Flags::PROTOCOL_FILE,
1558                        &Default::default(),
1559                    )
1560                    .await;
1561                    let _: u64 = file
1562                        .write(b"hello")
1563                        .await
1564                        .expect("write failed")
1565                        .map_err(Status::from_raw)
1566                        .expect("write error");
1567                }
1568            }),
1569            fasync::Task::spawn(async move {
1570                let root = fixture2.root();
1571                while !done2.load(atomic::Ordering::Relaxed) {
1572                    let file = open_file_checked(
1573                        &root,
1574                        "foo",
1575                        fio::Flags::FLAG_MAYBE_CREATE
1576                            | fio::PERM_READABLE
1577                            | fio::PERM_WRITABLE
1578                            | fio::Flags::PROTOCOL_FILE,
1579                        &Default::default(),
1580                    )
1581                    .await;
1582                    let _: u64 = file
1583                        .write(b"hello")
1584                        .await
1585                        .expect("write failed")
1586                        .map_err(Status::from_raw)
1587                        .expect("write error");
1588                }
1589            }),
1590            fasync::Task::spawn(async move {
1591                let root = fixture3.root();
1592                for _ in 0..300 {
1593                    let file = open_file_checked(
1594                        &root,
1595                        "foo",
1596                        fio::Flags::FLAG_MAYBE_CREATE
1597                            | fio::PERM_READABLE
1598                            | fio::PERM_WRITABLE
1599                            | fio::Flags::PROTOCOL_FILE,
1600                        &Default::default(),
1601                    )
1602                    .await;
1603                    assert_eq!(
1604                        file.close().await.expect("FIDL call failed").map_err(Status::from_raw),
1605                        Ok(())
1606                    );
1607                    root.unlink("foo", &fio::UnlinkOptions::default())
1608                        .await
1609                        .expect("FIDL call failed")
1610                        .expect("unlink failed");
1611                }
1612                done.store(true, atomic::Ordering::Relaxed);
1613            })
1614        );
1615
1616        Arc::try_unwrap(fixture).unwrap_or_else(|_| panic!()).close().await;
1617    }
1618
1619    #[fuchsia::test(threads = 10)]
1620    async fn test_get_backing_memory_shared_vmo_right_write() {
1621        let fixture = TestFixture::new().await;
1622        let root = fixture.root();
1623
1624        let file = open_file_checked(
1625            &root,
1626            "foo",
1627            fio::Flags::FLAG_MAYBE_CREATE
1628                | fio::PERM_READABLE
1629                | fio::PERM_WRITABLE
1630                | fio::Flags::PROTOCOL_FILE,
1631            &Default::default(),
1632        )
1633        .await;
1634
1635        file.resize(4096)
1636            .await
1637            .expect("resize failed")
1638            .map_err(Status::from_raw)
1639            .expect("resize error");
1640
1641        let vmo = file
1642            .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1643            .await
1644            .expect("Failed to make FIDL call")
1645            .map_err(Status::from_raw)
1646            .expect("Failed to get VMO");
1647        let err = vmo.write(&[0, 1, 2, 3], 0).expect_err("VMO should not be writable");
1648        assert_eq!(Status::ACCESS_DENIED, err);
1649
1650        let vmo = file
1651            .get_backing_memory(
1652                fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1653            )
1654            .await
1655            .expect("Failed to make FIDL call")
1656            .map_err(Status::from_raw)
1657            .expect("Failed to get VMO");
1658        vmo.write(&[0, 1, 2, 3], 0).expect("VMO should be writable");
1659
1660        close_file_checked(file).await;
1661        fixture.close().await;
1662    }
1663
1664    #[fuchsia::test(threads = 10)]
1665    async fn test_get_backing_memory_shared_vmo_right_read() {
1666        let fixture = TestFixture::new().await;
1667        let root = fixture.root();
1668
1669        let file = open_file_checked(
1670            &root,
1671            "foo",
1672            fio::Flags::FLAG_MAYBE_CREATE
1673                | fio::PERM_READABLE
1674                | fio::PERM_WRITABLE
1675                | fio::Flags::PROTOCOL_FILE,
1676            &Default::default(),
1677        )
1678        .await;
1679
1680        file.resize(4096)
1681            .await
1682            .expect("resize failed")
1683            .map_err(Status::from_raw)
1684            .expect("resize error");
1685
1686        let mut data = [0u8; 4];
1687        let vmo = file
1688            .get_backing_memory(fio::VmoFlags::SHARED_BUFFER)
1689            .await
1690            .expect("Failed to make FIDL call")
1691            .map_err(Status::from_raw)
1692            .expect("Failed to get VMO");
1693        let err = vmo.read(&mut data, 0).expect_err("VMO should not be readable");
1694        assert_eq!(Status::ACCESS_DENIED, err);
1695
1696        let vmo = file
1697            .get_backing_memory(fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ)
1698            .await
1699            .expect("Failed to make FIDL call")
1700            .map_err(Status::from_raw)
1701            .expect("Failed to get VMO");
1702        vmo.read(&mut data, 0).expect("VMO should be readable");
1703
1704        close_file_checked(file).await;
1705        fixture.close().await;
1706    }
1707
1708    #[fuchsia::test(threads = 10)]
1709    async fn test_get_backing_memory_shared_vmo_resize() {
1710        let fixture = TestFixture::new().await;
1711        let root = fixture.root();
1712
1713        let file = open_file_checked(
1714            &root,
1715            "foo",
1716            fio::Flags::FLAG_MAYBE_CREATE
1717                | fio::PERM_READABLE
1718                | fio::PERM_WRITABLE
1719                | fio::Flags::PROTOCOL_FILE,
1720            &Default::default(),
1721        )
1722        .await;
1723
1724        let vmo = file
1725            .get_backing_memory(
1726                fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1727            )
1728            .await
1729            .expect("Failed to make FIDL call")
1730            .map_err(Status::from_raw)
1731            .expect("Failed to get VMO");
1732
1733        // No RESIZE right.
1734        let err = vmo.set_size(4096).expect_err("VMO should not be resizable");
1735        assert_eq!(Status::UNAVAILABLE, err);
1736        // No SET_PROPERTY right.
1737        let err =
1738            vmo.set_content_size(&10).expect_err("content size should not be directly modifiable");
1739        assert_eq!(Status::ACCESS_DENIED, err);
1740
1741        close_file_checked(file).await;
1742        fixture.close().await;
1743    }
1744
1745    #[fuchsia::test(threads = 10)]
1746    async fn test_get_backing_memory_private_vmo_resize() {
1747        let fixture = TestFixture::new().await;
1748        let root = fixture.root();
1749
1750        let file = open_file_checked(
1751            &root,
1752            "foo",
1753            fio::Flags::FLAG_MAYBE_CREATE
1754                | fio::PERM_READABLE
1755                | fio::PERM_WRITABLE
1756                | fio::Flags::PROTOCOL_FILE,
1757            &Default::default(),
1758        )
1759        .await;
1760
1761        let vmo = file
1762            .get_backing_memory(
1763                fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
1764            )
1765            .await
1766            .expect("Failed to make FIDL call")
1767            .map_err(Status::from_raw)
1768            .expect("Failed to get VMO");
1769        vmo.set_size(10).expect("VMO should be resizable");
1770        vmo.set_content_size(&20).expect("content size should be modifiable");
1771        vmo.set_stream_size(20).expect("stream size should be modifiable");
1772
1773        let vmo = file
1774            .get_backing_memory(fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::READ)
1775            .await
1776            .expect("Failed to make FIDL call")
1777            .map_err(Status::from_raw)
1778            .expect("Failed to get VMO");
1779        let err = vmo.set_size(10).expect_err("VMO should not be resizable");
1780        assert_eq!(err, Status::ACCESS_DENIED);
1781        // This zeroes pages, which can't be done on a read-only VMO.
1782        vmo.set_stream_size(20).expect_err("stream size is not modifiable");
1783        vmo.set_content_size(&20).expect_err("content is not modifiable");
1784
1785        close_file_checked(file).await;
1786        fixture.close().await;
1787    }
1788
1789    #[fuchsia::test(threads = 10)]
1790    async fn extended_attributes() {
1791        let fixture = TestFixture::new().await;
1792        let root = fixture.root();
1793
1794        let file = open_file_checked(
1795            &root,
1796            "foo",
1797            fio::Flags::FLAG_MAYBE_CREATE
1798                | fio::PERM_READABLE
1799                | fio::PERM_WRITABLE
1800                | fio::Flags::PROTOCOL_FILE,
1801            &Default::default(),
1802        )
1803        .await;
1804
1805        let name = b"security.selinux";
1806        let value_vec = b"bar".to_vec();
1807
1808        {
1809            let (iterator_client, iterator_server) =
1810                fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1811            file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1812            let (chunk, last) = iterator_client
1813                .get_next()
1814                .await
1815                .expect("Failed to make FIDL call")
1816                .expect("Failed to get next iterator chunk");
1817            assert!(last);
1818            assert_eq!(chunk, Vec::<Vec<u8>>::new());
1819        }
1820        assert_eq!(
1821            file.get_extended_attribute(name)
1822                .await
1823                .expect("Failed to make FIDL call")
1824                .expect_err("Got successful message back for missing attribute"),
1825            Status::NOT_FOUND.into_raw(),
1826        );
1827
1828        file.set_extended_attribute(
1829            name,
1830            fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
1831            fio::SetExtendedAttributeMode::Set,
1832        )
1833        .await
1834        .expect("Failed to make FIDL call")
1835        .expect("Failed to set extended attribute");
1836
1837        {
1838            let (iterator_client, iterator_server) =
1839                fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1840            file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1841            let (chunk, last) = iterator_client
1842                .get_next()
1843                .await
1844                .expect("Failed to make FIDL call")
1845                .expect("Failed to get next iterator chunk");
1846            assert!(last);
1847            assert_eq!(chunk, vec![name]);
1848        }
1849        assert_eq!(
1850            file.get_extended_attribute(name)
1851                .await
1852                .expect("Failed to make FIDL call")
1853                .expect("Failed to get extended attribute"),
1854            fio::ExtendedAttributeValue::Bytes(value_vec)
1855        );
1856
1857        file.remove_extended_attribute(name)
1858            .await
1859            .expect("Failed to make FIDL call")
1860            .expect("Failed to remove extended attribute");
1861
1862        {
1863            let (iterator_client, iterator_server) =
1864                fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
1865            file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
1866            let (chunk, last) = iterator_client
1867                .get_next()
1868                .await
1869                .expect("Failed to make FIDL call")
1870                .expect("Failed to get next iterator chunk");
1871            assert!(last);
1872            assert_eq!(chunk, Vec::<Vec<u8>>::new());
1873        }
1874        assert_eq!(
1875            file.get_extended_attribute(name)
1876                .await
1877                .expect("Failed to make FIDL call")
1878                .expect_err("Got successful message back for missing attribute"),
1879            Status::NOT_FOUND.into_raw(),
1880        );
1881
1882        close_file_checked(file).await;
1883        fixture.close().await;
1884    }
1885
1886    #[fuchsia::test]
1887    async fn test_flush_when_closed_from_on_zero_children() {
1888        let fixture = TestFixture::new().await;
1889        let root = fixture.root();
1890
1891        let file = open_file_checked(
1892            &root,
1893            "foo",
1894            fio::Flags::FLAG_MAYBE_CREATE
1895                | fio::PERM_READABLE
1896                | fio::PERM_WRITABLE
1897                | fio::Flags::PROTOCOL_FILE,
1898            &Default::default(),
1899        )
1900        .await;
1901
1902        file.resize(50).await.expect("resize (FIDL) failed").expect("resize failed");
1903
1904        {
1905            let vmo = file
1906                .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
1907                .await
1908                .expect("get_backing_memory (FIDL) failed")
1909                .map_err(Status::from_raw)
1910                .expect("get_backing_memory failed");
1911
1912            std::mem::drop(file);
1913
1914            fasync::unblock(move || vmo.write(b"hello", 0).expect("write failed")).await;
1915        }
1916
1917        fixture.close().await;
1918    }
1919
1920    #[fuchsia::test]
1921    async fn test_background_flush() {
1922        let fixture = TestFixture::open(
1923            DeviceHolder::new(FakeDevice::new(65536, 512)),
1924            TestFixtureOptions::default(),
1925        )
1926        .await;
1927        {
1928            let root = fixture.root();
1929
1930            let file = open_file_checked(
1931                &root,
1932                "foo",
1933                fio::Flags::FLAG_MAYBE_CREATE
1934                    | fio::PERM_READABLE
1935                    | fio::PERM_WRITABLE
1936                    | fio::Flags::PROTOCOL_FILE,
1937                &Default::default(),
1938            )
1939            .await;
1940
1941            let stream = file.describe().await.unwrap().stream.unwrap();
1942            let file_id = file
1943                .get_attributes(fio::NodeAttributesQuery::ID)
1944                .await
1945                .unwrap()
1946                .unwrap()
1947                .1
1948                .id
1949                .unwrap();
1950            // Block background flush completion by holding the truncate lock.
1951            let truncate_guard = fixture
1952                .fs()
1953                .truncate_guard(fixture.volume().volume().store().store_object_id(), file_id)
1954                .await;
1955
1956            let file_obj = fixture
1957                .volume()
1958                .volume()
1959                .cache()
1960                .get(file_id)
1961                .unwrap()
1962                .into_any()
1963                .downcast::<FxFile>()
1964                .unwrap();
1965            let file_clone = file_obj.clone();
1966
1967            unblock(move || {
1968                let page_size = zx::system_get_page_size() as u64;
1969                let mut offset: u64 = 0;
1970                while !file_clone
1971                    .background_flush_running
1972                    .load(std::sync::atomic::Ordering::Relaxed)
1973                {
1974                    assert!(
1975                        offset <= BACKGROUND_FLUSH_THRESHOLD * 2,
1976                        "Background flush not triggering"
1977                    );
1978                    stream
1979                        .write_at(zx::StreamWriteOptions::empty(), offset, &[0, 1, 2, 3, 4])
1980                        .expect("write should succeed");
1981                    offset += page_size;
1982                }
1983            })
1984            .await;
1985
1986            // Release the truncate lock to unblock the writing, wait for the flush to complete.
1987            std::mem::drop(truncate_guard);
1988            const MAX_WAIT: Duration = Duration::from_secs(10);
1989            let wait_increments = Duration::from_millis(100);
1990            let mut total_waited = Duration::ZERO;
1991            while file_obj.background_flush_running.load(std::sync::atomic::Ordering::Relaxed) {
1992                total_waited += wait_increments;
1993                assert!(total_waited < MAX_WAIT);
1994                fasync::Timer::new(wait_increments).await;
1995            }
1996        }
1997
1998        fixture.close().await;
1999    }
2000
2001    #[fuchsia::test]
2002    async fn test_get_attributes_fsverity_enabled_file() {
2003        let fixture = TestFixture::new().await;
2004        let root = fixture.root();
2005
2006        let file = open_file_checked(
2007            &root,
2008            "foo",
2009            fio::Flags::FLAG_MAYBE_CREATE
2010                | fio::PERM_READABLE
2011                | fio::PERM_WRITABLE
2012                | fio::Flags::PROTOCOL_FILE,
2013            &Default::default(),
2014        )
2015        .await;
2016
2017        let mut data: Vec<u8> = vec![0x00u8; 1052672];
2018        rng().fill(&mut data[..]);
2019
2020        for chunk in data.chunks(8192) {
2021            file.write(chunk)
2022                .await
2023                .expect("FIDL call failed")
2024                .map_err(Status::from_raw)
2025                .expect("write failed");
2026        }
2027
2028        let tree = fsverity_merkle::from_slice(
2029            &data,
2030            FsVerityHasher::Sha256(FsVerityHasherOptions::new(vec![0xFF; 8], 4096)),
2031        );
2032        let expected_root = tree.root().to_vec();
2033
2034        let expected_descriptor = fio::VerificationOptions {
2035            hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2036            salt: Some(vec![0xFF; 8]),
2037            ..Default::default()
2038        };
2039
2040        file.enable_verity(&expected_descriptor)
2041            .await
2042            .expect("FIDL transport error")
2043            .expect("enable verity failed");
2044
2045        let (_, immutable_attributes) = file
2046            .get_attributes(fio::NodeAttributesQuery::ROOT_HASH | fio::NodeAttributesQuery::OPTIONS)
2047            .await
2048            .expect("FIDL call failed")
2049            .map_err(Status::from_raw)
2050            .expect("get_attributes failed");
2051
2052        assert_eq!(
2053            immutable_attributes
2054                .options
2055                .expect("verification options not present in immutable attributes"),
2056            expected_descriptor
2057        );
2058        assert_eq!(
2059            immutable_attributes.root_hash.expect("root hash not present in immutable attributes"),
2060            expected_root
2061        );
2062
2063        fixture.close().await;
2064    }
2065
2066    /// Verify that once we enable verity on a file, it can never be written to or resized.
2067    /// This applies even to connections that have [`fio::PERM_WRITABLE`].
2068    #[fuchsia::test]
2069    async fn test_write_fail_fsverity_enabled_file() {
2070        let fixture = TestFixture::new().await;
2071        let root = fixture.root();
2072
2073        let file = open_file_checked(
2074            &root,
2075            "foo",
2076            fio::Flags::FLAG_MAYBE_CREATE
2077                | fio::PERM_READABLE
2078                | fio::PERM_WRITABLE
2079                | fio::Flags::PROTOCOL_FILE,
2080            &Default::default(),
2081        )
2082        .await;
2083
2084        file.write(&[8; 8192])
2085            .await
2086            .expect("FIDL call failed")
2087            .map_err(Status::from_raw)
2088            .expect("write failed");
2089
2090        let descriptor = fio::VerificationOptions {
2091            hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2092            salt: Some(vec![0xFF; 8]),
2093            ..Default::default()
2094        };
2095
2096        file.enable_verity(&descriptor)
2097            .await
2098            .expect("FIDL transport error")
2099            .expect("enable verity failed");
2100
2101        async fn assert_file_is_not_writable(file: &fio::FileProxy) {
2102            // Writes via FIDL should fail
2103            file.write(&[2; 8192])
2104                .await
2105                .expect("FIDL transport error")
2106                .map_err(Status::from_raw)
2107                .expect_err("write succeeded on fsverity-enabled file");
2108            // Writes via the pager should fail
2109            let vmo = file
2110                .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::WRITE)
2111                .await
2112                .expect("FIDL transport error")
2113                .map_err(Status::from_raw)
2114                .expect("get_backing_memory failed");
2115            fasync::unblock(move || {
2116                vmo.write(&[2; 8192], 0)
2117                    .expect_err("write via VMO succeeded on fsverity-enabled file");
2118            })
2119            .await;
2120            // Truncation should fail
2121            file.resize(1)
2122                .await
2123                .expect("FIDL transport error")
2124                .map_err(Status::from_raw)
2125                .expect_err("resize succeeded on fsverity-enabled file");
2126        }
2127
2128        assert_file_is_not_writable(&file).await;
2129        close_file_checked(file).await;
2130
2131        // Ensure that even if new writable connections are created, those also cannot write.
2132        let file =
2133            open_file(&root, "foo", fio::PERM_READABLE | fio::PERM_WRITABLE, &Default::default())
2134                .await
2135                .expect("failed to open fsverity-enabled file");
2136        assert_file_is_not_writable(&file).await;
2137        close_file_checked(file).await;
2138
2139        // Reopen the filesystem and ensure that the file can't be written to.
2140        let device = fixture.close().await;
2141        device.ensure_unique();
2142        device.reopen(false);
2143        let fixture =
2144            TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2145                .await;
2146
2147        let root = fixture.root();
2148        let file =
2149            open_file(&root, "foo", fio::PERM_READABLE | fio::PERM_WRITABLE, &Default::default())
2150                .await
2151                .expect("failed to open fsverity-enabled file");
2152        assert_file_is_not_writable(&file).await;
2153        close_file_checked(file).await;
2154
2155        fixture.close().await;
2156    }
2157
2158    #[fuchsia::test]
2159    async fn test_fsverity_enabled_file_verified_reads() {
2160        let mut data: Vec<u8> = vec![0x00u8; 1052672];
2161        rng().fill(&mut data[..]);
2162        let mut num_chunks = 0;
2163
2164        let reused_device = {
2165            let fixture = TestFixture::new().await;
2166            let root = fixture.root();
2167
2168            let file = open_file_checked(
2169                &root,
2170                "foo",
2171                fio::Flags::FLAG_MAYBE_CREATE
2172                    | fio::PERM_READABLE
2173                    | fio::PERM_WRITABLE
2174                    | fio::Flags::PROTOCOL_FILE,
2175                &Default::default(),
2176            )
2177            .await;
2178
2179            for chunk in data.chunks(fio::MAX_BUF as usize) {
2180                file.write(chunk)
2181                    .await
2182                    .expect("FIDL call failed")
2183                    .map_err(Status::from_raw)
2184                    .expect("write failed");
2185                num_chunks += 1;
2186            }
2187
2188            let descriptor = fio::VerificationOptions {
2189                hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2190                salt: Some(vec![0xFF; 8]),
2191                ..Default::default()
2192            };
2193
2194            file.enable_verity(&descriptor)
2195                .await
2196                .expect("FIDL transport error")
2197                .expect("enable verity failed");
2198
2199            assert!(file.sync().await.expect("Sync failed").is_ok());
2200            close_file_checked(file).await;
2201            fixture.close().await
2202        };
2203
2204        let fixture = TestFixture::open(
2205            reused_device,
2206            TestFixtureOptions { format: false, ..Default::default() },
2207        )
2208        .await;
2209        let root = fixture.root();
2210
2211        let file = open_file_checked(
2212            &root,
2213            "foo",
2214            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2215            &Default::default(),
2216        )
2217        .await;
2218
2219        for chunk in 0..num_chunks {
2220            let buffer = file
2221                .read(fio::MAX_BUF)
2222                .await
2223                .expect("transport error on read")
2224                .expect("read failed");
2225            let start = chunk * fio::MAX_BUF as usize;
2226            assert_eq!(&buffer, &data[start..start + buffer.len()]);
2227        }
2228
2229        fixture.close().await;
2230    }
2231
2232    #[fuchsia::test]
2233    async fn test_enabling_verity_on_verified_file_fails() {
2234        let reused_device = {
2235            let fixture = TestFixture::new().await;
2236            let root = fixture.root();
2237
2238            let file = open_file_checked(
2239                &root,
2240                "foo",
2241                fio::Flags::FLAG_MAYBE_CREATE
2242                    | fio::PERM_READABLE
2243                    | fio::PERM_WRITABLE
2244                    | fio::Flags::PROTOCOL_FILE,
2245                &Default::default(),
2246            )
2247            .await;
2248
2249            file.write(&[1; 8192])
2250                .await
2251                .expect("FIDL call failed")
2252                .map_err(Status::from_raw)
2253                .expect("write failed");
2254
2255            let descriptor = fio::VerificationOptions {
2256                hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2257                salt: Some(vec![0xFF; 8]),
2258                ..Default::default()
2259            };
2260
2261            file.enable_verity(&descriptor)
2262                .await
2263                .expect("FIDL transport error")
2264                .expect("enable verity failed");
2265
2266            file.enable_verity(&descriptor)
2267                .await
2268                .expect("FIDL transport error")
2269                .expect_err("enabling verity on a verity-enabled file should fail.");
2270
2271            assert!(file.sync().await.expect("Sync failed").is_ok());
2272            close_file_checked(file).await;
2273            fixture.close().await
2274        };
2275
2276        let fixture = TestFixture::open(
2277            reused_device,
2278            TestFixtureOptions { format: false, ..Default::default() },
2279        )
2280        .await;
2281        let root = fixture.root();
2282
2283        let file = open_file_checked(
2284            &root,
2285            "foo",
2286            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2287            &Default::default(),
2288        )
2289        .await;
2290
2291        let descriptor = fio::VerificationOptions {
2292            hash_algorithm: Some(fio::HashAlgorithm::Sha256),
2293            salt: Some(vec![0xFF; 8]),
2294            ..Default::default()
2295        };
2296
2297        file.enable_verity(&descriptor)
2298            .await
2299            .expect("FIDL transport error")
2300            .expect_err("enabling verity on a verity-enabled file should fail.");
2301
2302        close_file_checked(file).await;
2303        fixture.close().await;
2304    }
2305
2306    #[fuchsia::test]
2307    async fn test_get_attributes_fsverity_not_enabled() {
2308        let fixture = TestFixture::new().await;
2309        let root = fixture.root();
2310
2311        let file = open_file_checked(
2312            &root,
2313            "foo",
2314            fio::Flags::FLAG_MAYBE_CREATE
2315                | fio::PERM_READABLE
2316                | fio::PERM_WRITABLE
2317                | fio::Flags::PROTOCOL_FILE,
2318            &Default::default(),
2319        )
2320        .await;
2321
2322        let mut data: Vec<u8> = vec![0x00u8; 8192];
2323        rng().fill(&mut data[..]);
2324
2325        file.write(&data)
2326            .await
2327            .expect("FIDL call failed")
2328            .map_err(Status::from_raw)
2329            .expect("write failed");
2330
2331        let () = file
2332            .sync()
2333            .await
2334            .expect("FIDL call failed")
2335            .map_err(Status::from_raw)
2336            .expect("sync failed");
2337
2338        let (_, immutable_attributes) = file
2339            .get_attributes(fio::NodeAttributesQuery::ROOT_HASH | fio::NodeAttributesQuery::OPTIONS)
2340            .await
2341            .expect("FIDL call failed")
2342            .map_err(Status::from_raw)
2343            .expect("get_attributes failed");
2344
2345        assert_eq!(immutable_attributes.options, None);
2346        assert_eq!(immutable_attributes.root_hash, None);
2347
2348        fixture.close().await;
2349    }
2350
2351    #[fuchsia::test]
2352    async fn test_update_attributes_also_updates_ctime() {
2353        let fixture = TestFixture::new().await;
2354        let root = fixture.root();
2355
2356        let file = open_file_checked(
2357            &root,
2358            "foo",
2359            fio::Flags::FLAG_MAYBE_CREATE
2360                | fio::PERM_READABLE
2361                | fio::PERM_WRITABLE
2362                | fio::Flags::PROTOCOL_FILE,
2363            &Default::default(),
2364        )
2365        .await;
2366
2367        // Writing to file should update ctime
2368        file.write("hello, world!".as_bytes())
2369            .await
2370            .expect("FIDL call failed")
2371            .map_err(Status::from_raw)
2372            .expect("write failed");
2373        let (_mutable_attributes, immutable_attributes) = file
2374            .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2375            .await
2376            .expect("FIDL call failed")
2377            .map_err(Status::from_raw)
2378            .expect("get_attributes failed");
2379        let ctime_after_write = immutable_attributes.change_time;
2380
2381        // Updating file attributes updates ctime as well
2382        file.update_attributes(&fio::MutableNodeAttributes {
2383            mode: Some(111),
2384            gid: Some(222),
2385            ..Default::default()
2386        })
2387        .await
2388        .expect("FIDL call failed")
2389        .map_err(Status::from_raw)
2390        .expect("update_attributes failed");
2391        let (_mutable_attributes, immutable_attributes) = file
2392            .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2393            .await
2394            .expect("FIDL call failed")
2395            .map_err(Status::from_raw)
2396            .expect("get_attributes failed");
2397        let ctime_after_update = immutable_attributes.change_time;
2398        assert!(ctime_after_update > ctime_after_write);
2399
2400        // Flush metadata
2401        file.sync()
2402            .await
2403            .expect("FIDL call failed")
2404            .map_err(Status::from_raw)
2405            .expect("sync failed");
2406        let (_mutable_attributes, immutable_attributes) = file
2407            .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
2408            .await
2409            .expect("FIDL call failed")
2410            .map_err(Status::from_raw)
2411            .expect("get_attributes failed");
2412        let ctime_after_sync = immutable_attributes.change_time;
2413        assert_eq!(ctime_after_sync, ctime_after_update);
2414        fixture.close().await;
2415    }
2416
2417    #[fuchsia::test]
2418    async fn test_unnamed_temporary_file_can_read_and_write_to_it() {
2419        let fixture = TestFixture::new().await;
2420        let root = fixture.root();
2421
2422        let tmpfile = open_file_checked(
2423            &root,
2424            ".",
2425            fio::Flags::PROTOCOL_FILE
2426                | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2427                | fio::PERM_READABLE
2428                | fio::PERM_WRITABLE,
2429            &fio::Options::default(),
2430        )
2431        .await;
2432
2433        let buf = vec![0xaa as u8; 8];
2434        file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2435
2436        tmpfile
2437            .seek(fio::SeekOrigin::Start, 0)
2438            .await
2439            .expect("seek failed")
2440            .map_err(zx::Status::from_raw)
2441            .expect("seek error");
2442        let read_buf = file::read(&tmpfile).await.expect("read failed");
2443        assert_eq!(read_buf, buf);
2444
2445        fixture.close().await;
2446    }
2447
2448    #[fuchsia::test]
2449    async fn test_unnamed_temporary_file_get_space_back_after_closing_file() {
2450        let fixture = TestFixture::new().await;
2451        let root = fixture.root();
2452
2453        let tmpfile = open_file_checked(
2454            &root,
2455            ".",
2456            fio::Flags::PROTOCOL_FILE
2457                | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2458                | fio::PERM_WRITABLE,
2459            &fio::Options::default(),
2460        )
2461        .await;
2462
2463        const BUFFER_SIZE: u64 = 1024 * 1024;
2464        let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2465        file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2466
2467        let info_after_writing_to_tmpfile = root
2468            .query_filesystem()
2469            .await
2470            .expect("Failed wire call to query filesystem")
2471            .1
2472            .expect("Failed to query filesystem");
2473
2474        close_file_checked(tmpfile).await;
2475
2476        // We will get space back soon after closing the file buy maybe not immediately.
2477        for i in 1..50 {
2478            let info = root
2479                .query_filesystem()
2480                .await
2481                .expect("Failed wire call to query filesystem")
2482                .1
2483                .expect("Failed to query filesystem");
2484
2485            // We should claim back at least that amount of data we wrote to the file. There might
2486            // be some metadata left that will not be removed until compaction.
2487            if info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE {
2488                break;
2489            }
2490            if i == 49 {
2491                panic!("Did not get space back from unnamed temporary file after closing it.");
2492            }
2493        }
2494
2495        fixture.close().await;
2496    }
2497
2498    #[fuchsia::test]
2499    async fn test_unnamed_temporary_file_get_space_back_after_closing_device() {
2500        const BUFFER_SIZE: u64 = 1024 * 1024;
2501
2502        let (reused_device, info_after_writing_to_tmpfile) = {
2503            let fixture = TestFixture::new().await;
2504            let root = fixture.root();
2505
2506            let tmpfile = open_file_checked(
2507                &root,
2508                ".",
2509                fio::Flags::PROTOCOL_FILE
2510                    | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2511                    | fio::PERM_WRITABLE,
2512                &fio::Options::default(),
2513            )
2514            .await;
2515
2516            let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2517            file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2518
2519            let info_after_writing_to_tmpfile = root
2520                .query_filesystem()
2521                .await
2522                .expect("Failed wire call to query filesystem")
2523                .1
2524                .expect("Failed to query filesystem");
2525
2526            (fixture.close().await, info_after_writing_to_tmpfile)
2527        };
2528
2529        let fixture = TestFixture::open(
2530            reused_device,
2531            TestFixtureOptions { format: false, ..Default::default() },
2532        )
2533        .await;
2534        let root = fixture.root();
2535
2536        let info = root
2537            .query_filesystem()
2538            .await
2539            .expect("Failed wire call to query filesystem")
2540            .1
2541            .expect("Failed to query filesystem");
2542
2543        // We should claim back at least that amount of data we wrote to the file after rebooting
2544        // device. There might be some metadata left that will not be removed until compaction.
2545        assert!(info_after_writing_to_tmpfile.used_bytes - info.used_bytes >= BUFFER_SIZE);
2546
2547        fixture.close().await;
2548    }
2549
2550    #[fuchsia::test]
2551    async fn test_unnamed_temporary_file_can_link_into() {
2552        const FILE1: &str = "foo";
2553        const FILE2: &str = "bar";
2554        const BUFFER_SIZE: u64 = 1024 * 1024;
2555        let buf = vec![0xaa as u8; BUFFER_SIZE as usize];
2556
2557        let reused_device = {
2558            let fixture = TestFixture::new().await;
2559            let root = fixture.root();
2560
2561            let tmpfile = open_file_checked(
2562                &root,
2563                ".",
2564                fio::Flags::PROTOCOL_FILE
2565                    | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2566                    | fio::PERM_READABLE
2567                    | fio::PERM_WRITABLE,
2568                &fio::Options::default(),
2569            )
2570            .await;
2571
2572            // Link temporary unnamed file into filesystem, making it permanent.
2573            let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
2574            zx::Status::ok(status).expect("get_token failed");
2575            tmpfile
2576                .link_into(zx::Event::from(dst_token.unwrap()), FILE1)
2577                .await
2578                .expect("link_into wire message failed")
2579                .map_err(zx::Status::from_raw)
2580                .expect("link_into failed");
2581
2582            // We should be able to link the temporary file proxy multiple times.
2583            let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
2584            zx::Status::ok(status).expect("get_token failed");
2585            tmpfile
2586                .link_into(zx::Event::from(dst_token.unwrap()), FILE2)
2587                .await
2588                .expect("link_into wire message failed")
2589                .map_err(zx::Status::from_raw)
2590                .expect("link_into failed");
2591
2592            // Write to tmpfile, we should see the contents of it when reading from FILE1 or FILE2.
2593            file::write(&tmpfile, buf.as_slice()).await.expect("Failed to write to file");
2594
2595            root.unlink(FILE1, &fio::UnlinkOptions::default())
2596                .await
2597                .expect("unlink wire call failed")
2598                .map_err(zx::Status::from_raw)
2599                .expect("unlink failed");
2600            fixture.close().await
2601        };
2602
2603        let fixture = TestFixture::open(
2604            reused_device,
2605            TestFixtureOptions { format: false, ..Default::default() },
2606        )
2607        .await;
2608        let root = fixture.root();
2609
2610        // FILE1 was unlinked, so we should not be able to open a connection to it.
2611        assert_eq!(
2612            open_file(
2613                &root,
2614                FILE1,
2615                fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2616                &fio::Options::default()
2617            )
2618            .await
2619            .expect_err("Open succeeded unexpectedly")
2620            .root_cause()
2621            .downcast_ref::<zx::Status>()
2622            .expect("No status"),
2623            &zx::Status::NOT_FOUND,
2624        );
2625
2626        // The temporary unnamed file was linked to FILE2. We should find the same contents written
2627        // to it.
2628        let permanent_file = open_file_checked(
2629            &root,
2630            FILE2,
2631            fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2632            &fio::Options::default(),
2633        )
2634        .await;
2635        permanent_file
2636            .seek(fio::SeekOrigin::Start, 0)
2637            .await
2638            .expect("seek wire message failed")
2639            .map_err(zx::Status::from_raw)
2640            .expect("seek error");
2641        let read_buf = file::read(&permanent_file).await.expect("read failed");
2642        assert!(read_buf == buf);
2643
2644        fsck(fixture.fs().clone()).await.expect("fsck failed");
2645
2646        fixture.close().await;
2647    }
2648
2649    #[fuchsia::test]
2650    async fn test_unnamed_temporary_file_in_encrypted_directory() {
2651        let fixture = TestFixture::new().await;
2652        let root = fixture.root();
2653
2654        // Set up encrypted directory
2655        let crypt = fixture.crypt().unwrap();
2656        let encrypted_directory = open_dir_checked(
2657            &root,
2658            "encrypted_directory",
2659            fio::Flags::FLAG_MAYBE_CREATE
2660                | fio::Flags::PROTOCOL_DIRECTORY
2661                | fio::PERM_READABLE
2662                | fio::PERM_WRITABLE,
2663            fio::Options::default(),
2664        )
2665        .await;
2666        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2667        encrypted_directory
2668            .update_attributes(&fio::MutableNodeAttributes {
2669                wrapping_key_id: Some(WRAPPING_KEY_ID),
2670                ..Default::default()
2671            })
2672            .await
2673            .expect("update_attributes wire call failed")
2674            .map_err(zx::ok)
2675            .expect("update_attributes failed");
2676
2677        // Create a temporary unnamed file in that directory, it should have the same wrapping key.
2678        let encryped_tmpfile = open_file_checked(
2679            &encrypted_directory,
2680            ".",
2681            fio::Flags::PROTOCOL_FILE
2682                | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2683                | fio::PERM_READABLE
2684                | fio::PERM_WRITABLE,
2685            &fio::Options::default(),
2686        )
2687        .await;
2688        let (mutable_attributes, _immutable_attributes) = encryped_tmpfile
2689            .get_attributes(fio::NodeAttributesQuery::WRAPPING_KEY_ID)
2690            .await
2691            .expect("get_attributes wire call failed")
2692            .map_err(zx::Status::from_raw)
2693            .expect("get_attributes failed");
2694        assert_eq!(mutable_attributes.wrapping_key_id, Some(WRAPPING_KEY_ID));
2695
2696        // Similar to a regular file, linking a temporary unnamed file into the directory will only
2697        // work if they have the same wrapping key ID.
2698        let (status, dst_token) = encrypted_directory.get_token().await.expect("FIDL call failed");
2699        zx::Status::ok(status).expect("get_token failed");
2700        encryped_tmpfile
2701            .link_into(zx::Event::from(dst_token.unwrap()), "foo")
2702            .await
2703            .expect("link_into wire message failed")
2704            .expect("link_into failed");
2705
2706        let unencryped_tmpfile = open_file_checked(
2707            &root,
2708            ".",
2709            fio::Flags::PROTOCOL_FILE
2710                | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2711                | fio::PERM_READABLE
2712                | fio::PERM_WRITABLE,
2713            &fio::Options::default(),
2714        )
2715        .await;
2716        let (mutable_attributes, _immutable_attributes) = unencryped_tmpfile
2717            .get_attributes(fio::NodeAttributesQuery::WRAPPING_KEY_ID)
2718            .await
2719            .expect("get_attributes wire call failed")
2720            .map_err(zx::Status::from_raw)
2721            .expect("get_attributes failed");
2722        assert_eq!(mutable_attributes.wrapping_key_id, None);
2723        let (status, dst_token) = encrypted_directory.get_token().await.expect("FIDL call failed");
2724        zx::Status::ok(status).expect("get_token failed");
2725        assert_eq!(
2726            unencryped_tmpfile
2727                .link_into(zx::Event::from(dst_token.unwrap()), "bar")
2728                .await
2729                .expect("link_into wire message failed")
2730                .map_err(zx::Status::from_raw)
2731                .expect_err("link_into passed unexpectedly"),
2732            zx::Status::BAD_STATE,
2733        );
2734
2735        fixture.close().await;
2736    }
2737
2738    #[fuchsia::test]
2739    async fn test_unnamed_temporary_file_in_locked_directory() {
2740        let fixture = TestFixture::new().await;
2741        let root = fixture.root();
2742
2743        // Set up encrypted directory
2744        let crypt = fixture.crypt().unwrap();
2745        let encrypted_directory = open_dir_checked(
2746            &root,
2747            "encrypted_directory",
2748            fio::Flags::FLAG_MAYBE_CREATE
2749                | fio::Flags::PROTOCOL_DIRECTORY
2750                | fio::PERM_READABLE
2751                | fio::PERM_WRITABLE,
2752            fio::Options::default(),
2753        )
2754        .await;
2755        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
2756        encrypted_directory
2757            .update_attributes(&fio::MutableNodeAttributes {
2758                wrapping_key_id: Some(WRAPPING_KEY_ID),
2759                ..Default::default()
2760            })
2761            .await
2762            .expect("update_attributes wire call failed")
2763            .map_err(zx::ok)
2764            .expect("update_attributes failed");
2765
2766        // This locks the directory
2767        crypt.forget_wrapping_key(&WRAPPING_KEY_ID).unwrap();
2768
2769        // Open unnamed temporary file in a locked directory and should return (key) UNAVAILABLE.
2770        assert_eq!(
2771            open_file(
2772                &encrypted_directory,
2773                ".",
2774                fio::Flags::PROTOCOL_FILE
2775                    | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2776                    | fio::PERM_READABLE
2777                    | fio::PERM_WRITABLE,
2778                &fio::Options::default()
2779            )
2780            .await
2781            .expect_err("Open succeeded unexpectedly")
2782            .root_cause()
2783            .downcast_ref::<zx::Status>()
2784            .expect("No status"),
2785            &zx::Status::UNAVAILABLE,
2786        );
2787        fixture.close().await;
2788    }
2789
2790    #[fuchsia::test]
2791    async fn test_unnamed_temporary_file_link_into_with_race() {
2792        let fixture = TestFixture::new().await;
2793        let root = fixture.root();
2794
2795        for i in 1..100 {
2796            let tmpfile = open_file_checked(
2797                &root,
2798                ".",
2799                fio::Flags::PROTOCOL_FILE
2800                    | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
2801                    | fio::PERM_READABLE
2802                    | fio::PERM_WRITABLE,
2803                &fio::Options::default(),
2804            )
2805            .await;
2806
2807            // Clone tmpfile proxy to use in the separate threads.
2808            let (tmpfile_clone1, tmpfile_server1) =
2809                fidl::endpoints::create_proxy::<fio::FileMarker>();
2810            tmpfile.clone(tmpfile_server1.into_channel().into()).expect("clone failed");
2811            let (tmpfile_clone2, tmpfile_server2) =
2812                fidl::endpoints::create_proxy::<fio::FileMarker>();
2813            tmpfile.clone(tmpfile_server2.into_channel().into()).expect("clone failed");
2814
2815            // Get the open connection to the sub directory which we would attempt to link the
2816            // unnamed temporary file into.
2817            let sub_dir = open_dir_checked(
2818                &root,
2819                "A",
2820                fio::Flags::PROTOCOL_DIRECTORY
2821                    | fio::PERM_READABLE
2822                    | fio::PERM_WRITABLE
2823                    | fio::Flags::FLAG_MAYBE_CREATE,
2824                fio::Options::default(),
2825            )
2826            .await;
2827
2828            // Get tokens to the sub directory to use for `link_into`.
2829            let (status, dst_token1) = sub_dir.get_token().await.expect("FIDL call failed");
2830            zx::Status::ok(status).expect("get_token failed");
2831            let (status, dst_token2) = sub_dir.get_token().await.expect("FIDL call failed");
2832            zx::Status::ok(status).expect("get_token failed");
2833
2834            join!(
2835                fasync::Task::spawn(async move {
2836                    tmpfile_clone1
2837                        .link_into(zx::Event::from(dst_token1.unwrap()), &(2 * i).to_string())
2838                        .await
2839                        .expect("link_into wire message failed")
2840                        .expect("link_into failed");
2841                }),
2842                fasync::Task::spawn(async move {
2843                    tmpfile_clone2
2844                        .link_into(zx::Event::from(dst_token2.unwrap()), &(2 * i + 1).to_string())
2845                        .await
2846                        .expect("link_into wire message failed")
2847                        .expect("link_into failed");
2848                })
2849            );
2850            let (_, immutable_attributes) = tmpfile
2851                .get_attributes(fio::NodeAttributesQuery::LINK_COUNT)
2852                .await
2853                .expect("Failed get_attributes wire call")
2854                .expect("get_attributes failed");
2855            assert_eq!(immutable_attributes.link_count.unwrap(), 2);
2856            close_file_checked(tmpfile).await;
2857        }
2858        fixture.close().await;
2859    }
2860
2861    #[fuchsia::test]
2862    async fn test_update_attributes_persists() {
2863        const FILE: &str = "foo";
2864        let mtime = Some(Timestamp::now().as_nanos());
2865        let atime = Some(Timestamp::now().as_nanos());
2866        let mode = Some(111);
2867
2868        let device = {
2869            let fixture = TestFixture::new().await;
2870            let root = fixture.root();
2871
2872            let file = open_file_checked(
2873                &root,
2874                FILE,
2875                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_FILE,
2876                &fio::Options::default(),
2877            )
2878            .await;
2879
2880            file.update_attributes(&fio::MutableNodeAttributes {
2881                modification_time: mtime,
2882                access_time: atime,
2883                mode: Some(111),
2884                ..Default::default()
2885            })
2886            .await
2887            .expect("update_attributes FIDL call failed")
2888            .map_err(zx::ok)
2889            .expect("update_attributes failed");
2890
2891            // Calling close should flush the node attributes to the device.
2892            fixture.close().await
2893        };
2894
2895        let fixture =
2896            TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2897                .await;
2898        let root = fixture.root();
2899        let file = open_file_checked(
2900            &root,
2901            FILE,
2902            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2903            &fio::Options::default(),
2904        )
2905        .await;
2906
2907        let (mutable_attributes, _immutable_attributes) = file
2908            .get_attributes(
2909                fio::NodeAttributesQuery::MODIFICATION_TIME
2910                    | fio::NodeAttributesQuery::ACCESS_TIME
2911                    | fio::NodeAttributesQuery::MODE,
2912            )
2913            .await
2914            .expect("update_attributesFIDL call failed")
2915            .map_err(zx::ok)
2916            .expect("get_attributes failed");
2917        assert_eq!(mutable_attributes.modification_time, mtime);
2918        assert_eq!(mutable_attributes.access_time, atime);
2919        assert_eq!(mutable_attributes.mode, mode);
2920        fixture.close().await;
2921    }
2922
2923    #[fuchsia::test]
2924    async fn test_atime_from_pending_access_time_update_request() {
2925        const FILE: &str = "foo";
2926
2927        let (device, expected_atime, expected_ctime) = {
2928            let fixture = TestFixture::new().await;
2929            let root = fixture.root();
2930
2931            let file = open_file_checked(
2932                &root,
2933                FILE,
2934                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_FILE,
2935                &fio::Options::default(),
2936            )
2937            .await;
2938
2939            let (mutable_attributes, immutable_attributes) = file
2940                .get_attributes(
2941                    fio::NodeAttributesQuery::CHANGE_TIME
2942                        | fio::NodeAttributesQuery::ACCESS_TIME
2943                        | fio::NodeAttributesQuery::MODIFICATION_TIME,
2944                )
2945                .await
2946                .expect("update_attributes FIDL call failed")
2947                .map_err(zx::ok)
2948                .expect("get_attributes failed");
2949            let initial_ctime = immutable_attributes.change_time;
2950            let initial_atime = mutable_attributes.access_time;
2951            // When creating a file, ctime, mtime, and atime are all updated to the current time.
2952            assert_eq!(initial_atime, initial_ctime);
2953            assert_eq!(initial_atime, mutable_attributes.modification_time);
2954
2955            // Client manages atime and they signal to Fxfs that a file access has occurred and it
2956            // may require an access time update. They do so by querying with
2957            // `fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE`.
2958            let (mutable_attributes, immutable_attributes) = file
2959                .get_attributes(
2960                    fio::NodeAttributesQuery::CHANGE_TIME
2961                        | fio::NodeAttributesQuery::ACCESS_TIME
2962                        | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
2963                )
2964                .await
2965                .expect("update_attributes FIDL call failed")
2966                .map_err(zx::ok)
2967                .expect("get_attributes failed");
2968            // atime will be updated as atime <= ctime (or mtime)
2969            assert!(initial_atime < mutable_attributes.access_time);
2970            let updated_atime = mutable_attributes.access_time;
2971            // Calling get_attributes with PENDING_ACCESS_TIME_UPDATE will trigger an update of
2972            // object attributes if access_time needs to be updated. Check that ctime isn't updated.
2973            assert_eq!(initial_ctime, immutable_attributes.change_time);
2974
2975            let (mutable_attributes, _) = file
2976                .get_attributes(
2977                    fio::NodeAttributesQuery::ACCESS_TIME
2978                        | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
2979                )
2980                .await
2981                .expect("update_attributes FIDL call failed")
2982                .map_err(zx::ok)
2983                .expect("get_attributes failed");
2984            // atime will be not be updated as atime > ctime (or mtime)
2985            assert_eq!(updated_atime, mutable_attributes.access_time);
2986
2987            (fixture.close().await, mutable_attributes.access_time, initial_ctime)
2988        };
2989
2990        let fixture =
2991            TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
2992                .await;
2993        let root = fixture.root();
2994        let file = open_file_checked(
2995            &root,
2996            FILE,
2997            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
2998            &fio::Options::default(),
2999        )
3000        .await;
3001
3002        // Make sure that the pending atime update persisted.
3003        let (mutable_attributes, immutable_attributes) = file
3004            .get_attributes(
3005                fio::NodeAttributesQuery::CHANGE_TIME | fio::NodeAttributesQuery::ACCESS_TIME,
3006            )
3007            .await
3008            .expect("update_attributesFIDL call failed")
3009            .map_err(zx::ok)
3010            .expect("get_attributes failed");
3011
3012        assert_eq!(immutable_attributes.change_time, expected_ctime);
3013        assert_eq!(mutable_attributes.access_time, expected_atime);
3014        fixture.close().await;
3015    }
3016
3017    #[fuchsia::test(threads = 10)]
3018    async fn test_delete_with_dirty_bytes_and_no_open_handles() {
3019        let fixture = TestFixture::new().await;
3020        let root = fixture.root();
3021
3022        let file = open_file_checked(
3023            &root,
3024            "foo",
3025            fio::Flags::FLAG_MAYBE_CREATE
3026                | fio::PERM_READABLE
3027                | fio::PERM_WRITABLE
3028                | fio::Flags::PROTOCOL_FILE,
3029            &Default::default(),
3030        )
3031        .await;
3032
3033        file.resize(4096)
3034            .await
3035            .expect("resize failed")
3036            .map_err(Status::from_raw)
3037            .expect("resize error");
3038
3039        let vmo = file
3040            .get_backing_memory(
3041                fio::VmoFlags::SHARED_BUFFER | fio::VmoFlags::READ | fio::VmoFlags::WRITE,
3042            )
3043            .await
3044            .expect("Failed to make FIDL call")
3045            .map_err(Status::from_raw)
3046            .expect("Failed to get VMO");
3047
3048        // Flush the file so that the file isn't dirty.
3049        file.sync().await.unwrap().unwrap();
3050
3051        // Close the file handle.
3052        drop(file);
3053
3054        // Allow time for the close to be noticed.
3055        fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3056
3057        // Modify the file through the VMO (this should mark it dirty).
3058        vmo.write(&[1, 2, 3, 4], 0).expect("vmo write failed");
3059
3060        // Drop the VMO so that the open count reaches zero, but unlike in the close case, this
3061        // will not flush the file immediately.
3062        drop(vmo);
3063
3064        // Allow time for the VMO being dropped to be noticed.
3065        fasync::Timer::new(std::time::Duration::from_millis(10)).await;
3066
3067        // Delete the file.  The file should be dirty at this point.
3068        root.unlink("foo", &fio::UnlinkOptions::default())
3069            .await
3070            .expect("unlink failed")
3071            .map_err(Status::from_raw)
3072            .expect("unlink error");
3073
3074        fixture.close().await;
3075    }
3076
3077    // Ensures that closing a file connection immediately blocks any future stream writes to the
3078    // underlying VMO with a BAD_STATE error, and that the file can still be safely unlinked
3079    // once the stream write has been rejected and cleanup has occurred.
3080    #[fuchsia::test]
3081    async fn test_close_file_before_writing_to_stream() {
3082        const FILE_NAME: &str = "foo";
3083
3084        let fixture = TestFixture::new().await;
3085        let root = fixture.root();
3086        let file = open_file_checked(
3087            &root,
3088            FILE_NAME,
3089            fio::Flags::FLAG_MAYBE_CREATE
3090                | fio::PERM_READABLE
3091                | fio::PERM_WRITABLE
3092                | fio::Flags::PROTOCOL_FILE,
3093            &Default::default(),
3094        )
3095        .await;
3096
3097        let stream = file.describe().await.unwrap().stream.unwrap();
3098
3099        close_file_checked(file).await;
3100
3101        unblock(move || {
3102            stream
3103                .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3104                .expect_err("Write should get BAD_STATE");
3105        })
3106        .await;
3107
3108        // Wait a bit to ensure that the stream has been closed and the zero children signal has
3109        // been processed.
3110        fasync::Timer::new(Duration::from_millis(100)).await;
3111
3112        // Now unlink the file.
3113        root.unlink(FILE_NAME, &fio::UnlinkOptions::default())
3114            .await
3115            .expect("unlink wire call failed")
3116            .expect("unlink failed");
3117
3118        fixture.close().await;
3119    }
3120
3121    // Ensures that closing a file connection blocks stream writes, but reopening the file
3122    // restores write capabilities. By using a duplicated stream handle from the first open,
3123    // we prove that when the file is reopened, the pager is marked open again, and writes
3124    // on the original stream handle succeed once more. This also confirms that the file remains
3125    // in the Fxfs node caches and is correctly reused when reopened.
3126    #[fuchsia::test]
3127    async fn test_close_and_reopen_file_stream() {
3128        const FILE_NAME: &str = "foo";
3129
3130        let fixture = TestFixture::new().await;
3131        let root = fixture.root();
3132        let file = open_file_checked(
3133            &root,
3134            FILE_NAME,
3135            fio::Flags::FLAG_MAYBE_CREATE
3136                | fio::PERM_READABLE
3137                | fio::PERM_WRITABLE
3138                | fio::Flags::PROTOCOL_FILE,
3139            &Default::default(),
3140        )
3141        .await;
3142
3143        let page_size = zx::system_get_page_size() as u64;
3144
3145        file.resize(8 * page_size)
3146            .await
3147            .expect("resize failed")
3148            .map_err(Status::from_raw)
3149            .expect("resize error");
3150
3151        let stream1 = file.describe().await.unwrap().stream.unwrap();
3152        let stream1_dup = stream1.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3153
3154        close_file_checked(file).await;
3155
3156        // Stream writes on the duplicated stream handle should now fail because all active
3157        // connections are closed and the handle open status is false.
3158        // Write at offset 0 (page 0).
3159        let stream1_dup_clone = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3160        unblock(move || {
3161            stream1_dup_clone
3162                .write_at(zx::StreamWriteOptions::empty(), 0, &[1, 2, 3, 4])
3163                .expect_err("Write should get BAD_STATE");
3164        })
3165        .await;
3166
3167        // Open the file again. This retrieves the node from the dirent cache and sets its pager
3168        // status to open, enabling stream writes on all stream handles pointing to its VMO.
3169        let file2 = open_file_checked(
3170            &root,
3171            FILE_NAME,
3172            fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE | fio::PERM_WRITABLE,
3173            &Default::default(),
3174        )
3175        .await;
3176
3177        let stream2 = file2.describe().await.unwrap().stream.unwrap();
3178
3179        // Now both the old duplicated stream and the new stream should succeed.
3180        // Write at page-separated offsets to prevent kernel dirty page caching from hiding races:
3181        // Page 1 and Page 2.
3182        let stream1_dup_clone2 = stream1_dup.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3183        let stream2_clone = stream2.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
3184        unblock(move || {
3185            stream1_dup_clone2
3186                .write_at(zx::StreamWriteOptions::empty(), 1 * page_size, &[5, 6, 7, 8])
3187                .expect("Write on re-opened stream 1 dup should succeed");
3188            stream2_clone
3189                .write_at(zx::StreamWriteOptions::empty(), 2 * page_size, &[9, 10, 11, 12])
3190                .expect("Write on new stream 2 should succeed");
3191        })
3192        .await;
3193
3194        // Close the reopened connection.
3195        close_file_checked(file2).await;
3196
3197        // Writes on both streams should fail again.
3198        // Write at new page-separated offsets:
3199        // Page 3 and Page 4.
3200        unblock(move || {
3201            stream1_dup
3202                .write_at(zx::StreamWriteOptions::empty(), 3 * page_size, &[13, 14, 15, 16])
3203                .expect_err("Write on stream 1 dup should fail after final close");
3204            stream2
3205                .write_at(zx::StreamWriteOptions::empty(), 4 * page_size, &[17, 18, 19, 20])
3206                .expect_err("Write on stream 2 should fail after final close");
3207        })
3208        .await;
3209
3210        fixture.close().await;
3211    }
3212}