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