Skip to main content

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