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