Skip to main content

starnix_core/fs/fuchsia/
remote.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::fs::fuchsia::RemoteUnixDomainSocket;
6use crate::fs::fuchsia::remote_volume::RemoteVolume;
7use crate::fs::fuchsia::sync_file::{SyncFence, SyncFile, SyncPoint, Timeline};
8use crate::mm::memory::MemoryObject;
9use crate::mm::{ProtectionFlags, VMEX_RESOURCE};
10use crate::security;
11use crate::task::{CurrentTask, Kernel};
12use crate::vfs::buffers::{InputBuffer, OutputBuffer, with_iovec_segments};
13use crate::vfs::file_server::serve_file_tagged;
14use crate::vfs::fsverity::FsVerityState;
15use crate::vfs::socket::{Socket, SocketFile, ZxioBackedSocket};
16use crate::vfs::{
17    Anon, AppendLockWriteGuard, CacheMode, DEFAULT_BYTES_PER_BLOCK, DirectoryEntryType, DirentSink,
18    FallocMode, FileHandle, FileObject, FileOps, FileSystem, FileSystemHandle, FileSystemOps,
19    FileSystemOptions, FsNode, FsNodeFlags, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString,
20    LookupVec, RenameContext, SeekTarget, SymlinkTarget, XattrOp, XattrStorage, default_seek,
21    fileops_impl_directory, fileops_impl_nonseekable, fileops_impl_noop_sync,
22    fileops_impl_seekable, fs_node_impl_not_dir, fs_node_impl_symlink, fs_node_impl_xattr_delegate,
23};
24use bstr::ByteSlice;
25use fidl::endpoints::DiscoverableProtocolMarker as _;
26use fidl_fuchsia_io as fio;
27use fidl_fuchsia_starnix_binder as fbinder;
28use fidl_fuchsia_unknown as funknown;
29use fuchsia_runtime::UtcInstant;
30use linux_uapi::SYNC_IOC_MAGIC;
31use once_cell::sync::OnceCell;
32use smallvec::{SmallVec, smallvec};
33use starnix_crypt::EncryptionKeyId;
34use starnix_logging::{CATEGORY_STARNIX_MM, impossible_error, log_warn};
35use starnix_sync::{
36    DynamicLockDepRwLock, FileOpsCore, FuchsiaRemoteTargetLock, LockDepReadGuard, LockDepRwLock,
37    LockDepWriteGuard, LockEqualOrBefore, Locked, Unlocked,
38};
39use starnix_syscalls::{SyscallArg, SyscallResult};
40use starnix_types::vfs::default_statfs;
41use starnix_uapi::auth::{Credentials, FsCred};
42use starnix_uapi::device_id::DeviceId;
43use starnix_uapi::errors::Errno;
44use starnix_uapi::file_mode::FileMode;
45use starnix_uapi::mount_flags::FileSystemFlags;
46use starnix_uapi::open_flags::OpenFlags;
47use starnix_uapi::{
48    __kernel_fsid_t, errno, error, from_status_like_fdio, fsverity_descriptor, mode, off_t, statfs,
49};
50use std::ops::ControlFlow;
51use std::sync::atomic::{AtomicU32, Ordering};
52use std::sync::{Arc, LazyLock};
53use sync_io_client::{RemoteIo, create_with_on_representation};
54use syncio::zxio::{
55    ZXIO_NODE_PROTOCOL_DIRECTORY, ZXIO_NODE_PROTOCOL_SYMLINK, ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET,
56    ZXIO_OBJECT_TYPE_NONE, ZXIO_OBJECT_TYPE_PACKET_SOCKET, ZXIO_OBJECT_TYPE_RAW_SOCKET,
57    ZXIO_OBJECT_TYPE_STREAM_SOCKET, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET, zxio_node_attr,
58};
59use syncio::{
60    AllocateMode, XattrSetMode, Zxio, zxio_fsverity_descriptor_t, zxio_node_attr_has_t,
61    zxio_node_attributes_t,
62};
63use zx::Counter;
64
65fn is_special(file_info: &fio::FileInfo) -> bool {
66    matches!(
67        file_info,
68        fio::FileInfo {
69            attributes:
70                Some(fio::NodeAttributes2 {
71                    mutable_attributes: fio::MutableNodeAttributes { mode: Some(mode), .. },
72                    ..
73                }),
74            ..
75        } if {
76            let mode = FileMode::from_bits(*mode);
77            mode.is_chr() || mode.is_blk() || mode.is_fifo() || mode.is_sock()
78        }
79    )
80}
81
82pub fn new_remote_fs(
83    locked: &mut Locked<Unlocked>,
84    current_task: &CurrentTask,
85    options: FileSystemOptions,
86) -> Result<FileSystemHandle, Errno> {
87    let kernel = current_task.kernel();
88    let requested_path = std::str::from_utf8(&options.source)
89        .map_err(|_| errno!(EINVAL, "source path is not utf8"))?;
90    let mut create_flags =
91        fio::PERM_READABLE | fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY;
92    if !options.flags.load(Ordering::Relaxed).contains(FileSystemFlags::RDONLY) {
93        create_flags |= fio::PERM_WRITABLE;
94    }
95    let (root_proxy, subdir) = kernel.open_ns_dir(requested_path, create_flags)?;
96
97    let subdir = if subdir.is_empty() { ".".to_string() } else { subdir };
98    let mut open_rights = fio::PERM_READABLE;
99    if !options.flags.load(Ordering::Relaxed).contains(FileSystemFlags::RDONLY) {
100        open_rights |= fio::PERM_WRITABLE;
101    }
102    let mut subdir_options = options;
103    subdir_options.source = subdir.into();
104    new_remotefs_in_root(locked, kernel, &root_proxy, subdir_options, open_rights)
105}
106
107/// Create a filesystem to access the content of the fuchsia directory available
108/// at `options.source` inside `root`.
109pub fn new_remotefs_in_root<L>(
110    locked: &mut Locked<L>,
111    kernel: &Kernel,
112    root: &fio::DirectorySynchronousProxy,
113    options: FileSystemOptions,
114    rights: fio::Flags,
115) -> Result<FileSystemHandle, Errno>
116where
117    L: LockEqualOrBefore<FileOpsCore>,
118{
119    let root = syncio::directory_open_directory_async(
120        root,
121        std::str::from_utf8(&options.source)
122            .map_err(|_| errno!(EINVAL, "source path is not utf8"))?,
123        rights,
124    )
125    .map_err(|e| errno!(EIO, format!("Failed to open root: {e}")))?;
126    RemoteFs::new_fs(locked, kernel, root.into_channel(), options, rights)
127}
128
129pub struct RemoteFs {
130    // If true, trust the remote file system's IDs (which requires that the remote file system does
131    // not span mounts).  This must be true to properly support hard links.  If this is false, the
132    // same node can end up having different IDs as it leaves and reenters the node cache.
133    // TODO(https://fxbug.dev/42081972): At the time of writing, package directories do not have
134    // unique IDs so this *must* be false in that case.
135    use_remote_ids: bool,
136
137    root_proxy: fio::DirectorySynchronousProxy,
138
139    // The rights used for the root node.
140    root_rights: fio::Flags,
141}
142
143impl RemoteFs {
144    /// Returns a reference to a RemoteFs given a reference to a FileSystem.
145    ///
146    /// # Panics
147    ///
148    /// This will panic if `fs`'s ops aren't `RemoteFs`, so this should only be called when this is
149    /// known to be the case.
150    fn from_fs(fs: &FileSystem) -> &RemoteFs {
151        if let Some(remote_vol) = fs.downcast_ops::<RemoteVolume>() {
152            remote_vol.remotefs()
153        } else {
154            fs.downcast_ops::<RemoteFs>().unwrap()
155        }
156    }
157}
158
159const REMOTE_FS_MAGIC: u32 = u32::from_be_bytes(*b"f.io");
160const DYNAMIC_FS_BYTES_PER_INODE_FALLBACK: u64 = 16384; // 16 KiB
161const SYNC_IOC_FILE_INFO: u8 = 4;
162const SYNC_IOC_MERGE: u8 = 3;
163
164impl FileSystemOps for RemoteFs {
165    fn statfs(
166        &self,
167        _locked: &mut Locked<FileOpsCore>,
168        _fs: &FileSystem,
169        _current_task: &CurrentTask,
170    ) -> Result<statfs, Errno> {
171        let (status, info) = self
172            .root_proxy
173            .query_filesystem(zx::MonotonicInstant::INFINITE)
174            .map_err(|_| errno!(EIO))?;
175        // Not all remote filesystems support `QueryFilesystem`, many return ZX_ERR_NOT_SUPPORTED.
176        if status == 0 {
177            if let Some(info) = info {
178                let (total_blocks, free_blocks) = if info.block_size > 0 {
179                    (
180                        (info.total_bytes / u64::from(info.block_size))
181                            .try_into()
182                            .unwrap_or(i64::MAX),
183                        ((info.total_bytes.saturating_sub(info.used_bytes))
184                            / u64::from(info.block_size))
185                        .try_into()
186                        .unwrap_or(i64::MAX),
187                    )
188                } else {
189                    (0, 0)
190                };
191
192                let total_nodes = std::cmp::min(
193                    info.total_nodes,
194                    info.total_bytes / DYNAMIC_FS_BYTES_PER_INODE_FALLBACK,
195                );
196                let free_nodes = total_nodes.saturating_sub(info.used_nodes);
197
198                let fsid = __kernel_fsid_t {
199                    val: [
200                        (info.fs_id & 0xffffffff) as i32,
201                        ((info.fs_id >> 32) & 0xffffffff) as i32,
202                    ],
203                };
204
205                return Ok(statfs {
206                    f_type: info.fs_type as i64,
207                    f_bsize: info.block_size.into(),
208                    f_blocks: total_blocks,
209                    f_bfree: free_blocks,
210                    f_bavail: free_blocks,
211                    f_files: total_nodes.try_into().unwrap_or(i64::MAX),
212                    f_ffree: free_nodes.try_into().unwrap_or(i64::MAX),
213                    f_fsid: fsid,
214                    f_namelen: info.max_filename_size.try_into().unwrap_or(0),
215                    f_frsize: info.block_size.into(),
216                    ..statfs::default()
217                });
218            }
219        }
220        Ok(default_statfs(REMOTE_FS_MAGIC))
221    }
222
223    fn name(&self) -> &'static FsStr {
224        "remotefs".into()
225    }
226
227    fn uses_external_node_ids(&self) -> bool {
228        self.use_remote_ids
229    }
230
231    fn rename(
232        &self,
233        _locked: &mut Locked<FileOpsCore>,
234        _fs: &FileSystem,
235        current_task: &CurrentTask,
236        context: &mut RenameContext<'_>,
237        old_name: &FsStr,
238        new_name: &FsStr,
239    ) -> Result<(), Errno> {
240        let renamed = &context.renamed.node;
241        let replaced = context.replaced.map(|r| &r.node);
242        let old_parent = &context.old_parent().node;
243        let new_parent = &context.new_parent().node;
244        let old_parent_info = context.old_parent_info();
245        let new_parent_info = context.new_parent_info();
246        // Renames should fail if the src or target directory is
247        // encrypted and locked.
248        old_parent.fail_if_locked(current_task, old_parent_info)?;
249        if let Some(info) = new_parent_info {
250            new_parent.fail_if_locked(current_task, info)?;
251        }
252
253        let Some((old_parent_ops, new_parent_ops)) =
254            old_parent.downcast_ops::<RemoteNode>().zip(new_parent.downcast_ops::<RemoteNode>())
255        else {
256            return error!(EXDEV);
257        };
258
259        let mut nodes: SmallVec<[&FsNode; 4]> =
260            smallvec![&***old_parent, &***new_parent, &***renamed];
261        if let Some(r) = replaced {
262            nodes.push(r);
263        }
264
265        will_dirty(&nodes, || {
266            old_parent_ops
267                .node
268                .io
269                .rename(get_name_str(old_name)?, &new_parent_ops.node.io, get_name_str(new_name)?)
270                .map_err(map_sync_io_client_error)
271        })
272    }
273
274    fn sync(
275        &self,
276        _locked: &mut Locked<FileOpsCore>,
277        _fs: &FileSystem,
278        _current_task: &CurrentTask,
279    ) -> Result<(), Errno> {
280        self.root_proxy
281            .sync(zx::MonotonicInstant::INFINITE)
282            .map_err(|_| errno!(EIO))?
283            .map_err(|status| map_sync_error(zx::Status::from_raw(status)))
284    }
285
286    fn manages_timestamps(&self) -> bool {
287        true
288    }
289
290    fn update_flags(
291        &self,
292        fs: &FileSystem,
293        _current_task: &CurrentTask,
294        mut flags: FileSystemFlags,
295    ) -> Result<(), Errno> {
296        if !self.root_rights.contains(fio::PERM_WRITABLE) {
297            flags |= FileSystemFlags::RDONLY;
298        }
299        fs.options.flags.store(flags, Ordering::Relaxed);
300        Ok(())
301    }
302}
303
304/// Factory is a helper that creates the appropriate node type when creating a node.  See
305/// LookupFactory below for a helper that is specialised for the lookup case.  All the functions
306/// will create nodes that are initially dirty which is intentional because not all attributes are
307/// fetched when creating nodes.
308struct Factory<'a> {
309    node_info: &'a mut FsNodeInfo,
310    assume_special: bool,
311}
312
313impl<'a> sync_io_client::Factory for Factory<'a> {
314    type Result = (Box<dyn FsNodeOps>, u64);
315
316    fn create_node(self, io: RemoteIo, info: fio::NodeInfo) -> Self::Result {
317        let attrs = get_attributes(&info.attributes);
318        let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
319        update_info_from_fidl(
320            self.node_info,
321            &attrs.mutable_attributes,
322            &attrs.immutable_attributes,
323        );
324        (Box::new(RemoteNode::new(io, true)), id)
325    }
326
327    fn create_directory(self, io: RemoteIo, info: fio::DirectoryInfo) -> Self::Result {
328        let attrs = get_attributes(&info.attributes);
329        let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
330        update_info_from_fidl(
331            self.node_info,
332            &attrs.mutable_attributes,
333            &attrs.immutable_attributes,
334        );
335        (Box::new(RemoteNode::new(io, true)), id)
336    }
337
338    fn create_file(self, io: RemoteIo, info: fio::FileInfo) -> Self::Result {
339        let is_special_node = self.assume_special || is_special(&info);
340        let attrs = get_attributes(&info.attributes);
341        let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
342        let ops: Box<dyn FsNodeOps> = if is_special_node {
343            Box::new(RemoteSpecialNode { node: BaseNode::new(io, true) })
344        } else {
345            Box::new(RemoteNode::new(io, true))
346        };
347        update_info_from_fidl(
348            self.node_info,
349            &attrs.mutable_attributes,
350            &attrs.immutable_attributes,
351        );
352        (ops, id)
353    }
354
355    fn create_symlink(self, io: RemoteIo, info: fio::SymlinkInfo) -> Self::Result {
356        let attrs = get_attributes(&info.attributes);
357        let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
358        let target = info.target.unwrap_or_default();
359        update_info_from_fidl(
360            self.node_info,
361            &attrs.mutable_attributes,
362            &attrs.immutable_attributes,
363        );
364        (Box::new(RemoteSymlink::new(BaseNode::new(io, true), target)), id)
365    }
366}
367
368/// LookupFactory is an optimised version of Factory which is used only for lookup.  All the
369/// functions will create nodes that are initially clean, which works because lookup always requests
370/// all attributes.
371struct LookupFactory<'a> {
372    fs: &'a FileSystemHandle,
373    current_task: &'a CurrentTask,
374}
375
376impl<'a> LookupFactory<'a> {
377    fn get_node(
378        &self,
379        io: RemoteIo,
380        attributes: &fio::NodeAttributes2,
381        create_ops: impl FnOnce(RemoteIo) -> Box<dyn FsNodeOps>,
382    ) -> Result<FsNodeHandle, Errno> {
383        let fs = self.fs;
384        let fs_ops = RemoteFs::from_fs(fs);
385        let fio::NodeAttributes2 { mutable_attributes: mutable, immutable_attributes: immutable } =
386            attributes;
387
388        let id = immutable.id.unwrap_or(fio::INO_UNKNOWN);
389        let node_id = if fs_ops.use_remote_ids {
390            if id == fio::INO_UNKNOWN {
391                return error!(ENOTSUP);
392            }
393            id
394        } else {
395            fs.allocate_ino()
396        };
397
398        let node = fs.get_or_create_node(node_id, || {
399            let uid = mutable.uid.unwrap_or(0);
400            let gid = mutable.gid.unwrap_or(0);
401            let owner = FsCred { uid, gid };
402            let rdev = DeviceId::from_bits(mutable.rdev.unwrap_or(0));
403            let fsverity_enabled = immutable.verity_enabled.unwrap_or(false);
404            let protocols = immutable.protocols.unwrap_or(fio::NodeProtocolKinds::empty());
405            // fsverity should not be enabled for non-file nodes.
406            if fsverity_enabled && !protocols.contains(fio::NodeProtocolKinds::FILE) {
407                return error!(EINVAL);
408            }
409
410            let ops = create_ops(io);
411            let child = FsNode::new_uncached(
412                node_id,
413                ops,
414                fs,
415                FsNodeInfo {
416                    rdev,
417                    ..FsNodeInfo::new(
418                        get_mode_from_fidl(mutable, immutable, fs_ops.root_rights),
419                        owner,
420                    )
421                },
422                FsNodeFlags::empty(),
423            );
424            if fsverity_enabled {
425                *child.fsverity.lock() = FsVerityState::FsVerity;
426            }
427            // This is valid to fail if we're using mount point labelling or the provided context
428            // string is invalid.
429            if let Some(fio::SelinuxContext::Data(data)) = mutable.selinux_context.as_ref() {
430                let _ = security::fs_node_notify_security_context(
431                    self.current_task,
432                    &child,
433                    FsStr::new(data),
434                );
435            }
436            Ok(child)
437        })?;
438
439        node.update_info(|info| update_info_from_fidl(info, mutable, immutable));
440
441        Ok(node)
442    }
443}
444
445impl<'a> sync_io_client::Factory for LookupFactory<'a> {
446    type Result = Result<FsNodeHandle, Errno>;
447
448    fn create_node(self, io: RemoteIo, info: fio::NodeInfo) -> Self::Result {
449        self.get_node(io, get_attributes(&info.attributes), |io| {
450            Box::new(RemoteNode::new(io, false))
451        })
452    }
453
454    fn create_directory(self, io: RemoteIo, info: fio::DirectoryInfo) -> Self::Result {
455        self.get_node(io, get_attributes(&info.attributes), |io| {
456            Box::new(RemoteNode::new(io, false))
457        })
458    }
459
460    fn create_file(self, io: RemoteIo, info: fio::FileInfo) -> Self::Result {
461        let is_special_node = is_special(&info);
462        self.get_node(io, get_attributes(&info.attributes), |io| {
463            if is_special_node {
464                Box::new(RemoteSpecialNode { node: BaseNode::new(io, false) })
465            } else {
466                Box::new(RemoteNode::new(io, false))
467            }
468        })
469    }
470
471    fn create_symlink(self, io: RemoteIo, mut info: fio::SymlinkInfo) -> Self::Result {
472        let mut target = info.target.take();
473        if target.is_none() {
474            return error!(EIO);
475        }
476        let node = self.get_node(io, get_attributes(&info.attributes), |io| {
477            Box::new(RemoteSymlink::new(BaseNode::new(io, false), target.take().unwrap()))
478        })?;
479        // Encrypted symlinks that use fscrypt can be read as encrypted links when no key is
480        // available.  When no key is available, directories will not cache their entries.  When,
481        // the key is subsequently provided, the next time the symlink is read, we will come through
482        // here, but since the node is cached, `get_or_create_node` will not create a new node
483        // which, if we were to do nothing, would mean we'd keep the encrypted value for the target.
484        // To address this, if no new node was created, we update the target of the existing node
485        // here.  Once the key has been provided, the entry will be cached with the directory and
486        // whilst the entry remains cached, `lookup` will not be called.
487        if let Some(target) = target
488            && let Some(symlink) = node.downcast_ops::<RemoteSymlink>()
489        {
490            *symlink.target.write() = target.into_boxed_slice();
491        }
492        Ok(node)
493    }
494}
495
496// A helper that makes it easy to deal with the rare case where no FIDL attributes are returned.
497fn get_attributes(attrs: &Option<fio::NodeAttributes2>) -> &fio::NodeAttributes2 {
498    static DEFAULT_NODE_ATTRIBUTES: LazyLock<fio::NodeAttributes2> =
499        LazyLock::new(|| fio::NodeAttributes2 {
500            mutable_attributes: Default::default(),
501            immutable_attributes: Default::default(),
502        });
503    attrs.as_ref().unwrap_or_else(|| &*DEFAULT_NODE_ATTRIBUTES)
504}
505
506impl RemoteFs {
507    pub(super) fn new(
508        root: zx::Channel,
509        root_rights: fio::Flags,
510    ) -> Result<(RemoteFs, Box<dyn FsNodeOps>, FsNodeInfo, u64), Errno> {
511        let (client_end, server_end) = zx::Channel::create();
512        let root_proxy = fio::DirectorySynchronousProxy::new(root);
513        root_proxy
514            .open(
515                ".",
516                fio::Flags::PROTOCOL_DIRECTORY
517                    | fio::PERM_READABLE
518                    | fio::Flags::PERM_INHERIT_WRITE
519                    | fio::Flags::PERM_INHERIT_EXECUTE
520                    | fio::Flags::FLAG_SEND_REPRESENTATION,
521                &fio::Options {
522                    attributes: Some(
523                        fio::NodeAttributesQuery::ID | fio::NodeAttributesQuery::WRAPPING_KEY_ID,
524                    ),
525                    ..Default::default()
526                },
527                server_end,
528            )
529            .map_err(|_| errno!(EIO))?;
530
531        // Use remote IDs if the filesystem is Fxfs which we know will give us unique IDs.  Hard
532        // links need to resolve to the same underlying FsNode, so we can only support hard links if
533        // the remote file system will give us unique IDs.  The IDs are also used as the key in
534        // caches, so we can't use remote IDs if the remote filesystem is not guaranteed to provide
535        // unique IDs, or if the remote filesystem spans multiple filesystems.
536        let (status, info) =
537            root_proxy.query_filesystem(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
538
539        // Be tolerant of errors here; many filesystems return `ZX_ERR_NOT_SUPPORTED`.
540        let use_remote_ids = status == 0
541            && info
542                .map(|i| i.fs_type == fidl_fuchsia_fs::VfsType::Fxfs.into_primitive())
543                .unwrap_or(false);
544
545        // The OnRepresentation response will return an initial set of `attrs`.
546        let mut node_info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
547        let (remote_node, node_id) = create_with_on_representation(
548            client_end.into(),
549            Factory { node_info: &mut node_info, assume_special: false },
550        )
551        .map_err(map_sync_io_client_error)?;
552
553        Ok((RemoteFs { use_remote_ids, root_proxy, root_rights }, remote_node, node_info, node_id))
554    }
555
556    pub fn new_fs<L>(
557        locked: &mut Locked<L>,
558        kernel: &Kernel,
559        root: zx::Channel,
560        options: FileSystemOptions,
561        rights: fio::Flags,
562    ) -> Result<FileSystemHandle, Errno>
563    where
564        L: LockEqualOrBefore<FileOpsCore>,
565    {
566        let (remotefs, root_node, info, node_id) = RemoteFs::new(root, rights)?;
567
568        if !rights.contains(fio::PERM_WRITABLE) {
569            options.flags.fetch_or(FileSystemFlags::RDONLY, Ordering::Relaxed);
570        }
571        let use_remote_ids = remotefs.use_remote_ids;
572        let fs = FileSystem::new(
573            locked,
574            kernel,
575            CacheMode::Cached(kernel.fs_cache_config()),
576            remotefs,
577            options,
578        )?;
579
580        let node_id = if use_remote_ids { node_id } else { fs.allocate_ino() };
581        fs.create_root_with_info(node_id, root_node, info);
582
583        Ok(fs)
584    }
585
586    pub(super) fn use_remote_ids(&self) -> bool {
587        self.use_remote_ids
588    }
589}
590
591/// All nodes compose `BaseNode`.
592///
593/// NOTE: If new node types are created, the `TryFrom` implementation needs updating below.
594struct BaseNode {
595    /// The underlying I/O object for this remote node.
596    io: RemoteIo,
597
598    /// The number of active dirty operations on this node and whether the node info is in sync.
599    /// See the `will_dirty` function for semantics.
600    info_state: InfoState,
601}
602
603impl BaseNode {
604    fn new(io: RemoteIo, dirty: bool) -> Self {
605        Self { io, info_state: InfoState::new(dirty) }
606    }
607
608    fn fetch_and_refresh_info<'a>(
609        &self,
610        info: &'a DynamicLockDepRwLock<FsNodeInfo>,
611    ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
612        self.info_state.maybe_refresh(
613            info,
614            |info| {
615                let mut query = NODE_INFO_ATTRIBUTES;
616                if info.read().pending_time_access_update {
617                    query |= fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE;
618                }
619                let (mutable, immutable) =
620                    self.io.attr_get(query).map_err(map_sync_io_client_error)?;
621                let mut info = info.write();
622                info.pending_time_access_update = false;
623                update_info_from_fidl(&mut info, &mutable, &immutable);
624                Ok(LockDepWriteGuard::downgrade(info))
625            },
626            |info| Ok(info.read()),
627        )
628    }
629
630    fn update_attributes(&self, info: &FsNodeInfo, has: zxio_node_attr_has_t) -> Result<(), Errno> {
631        // Omit updating creation_time. By definition, there shouldn't be a change in creation_time.
632        will_dirty(&[self], || {
633            let res = self.io.attr_set(fio::MutableNodeAttributes {
634                modification_time: has
635                    .modification_time
636                    .then_some(info.time_modify.into_nanos() as u64),
637                access_time: has.access_time.then_some(info.time_access.into_nanos() as u64),
638                mode: has.mode.then_some(info.mode.bits()),
639                uid: has.uid.then_some(info.uid),
640                gid: has.gid.then_some(info.gid),
641                rdev: has.rdev.then_some(info.rdev.bits()),
642                casefold: has.casefold.then_some(info.casefold),
643                wrapping_key_id: if has.wrapping_key_id { info.wrapping_key_id } else { None },
644                ..Default::default()
645            });
646            res.map_err(|status| from_status_like_fdio!(status))
647        })
648    }
649}
650
651impl<'a> TryFrom<&'a FsNode> for &'a BaseNode {
652    type Error = ();
653    fn try_from(value: &FsNode) -> Result<&BaseNode, ()> {
654        value
655            .downcast_ops::<RemoteNode>()
656            .map(|n| &n.node)
657            .or_else(|| value.downcast_ops::<RemoteSpecialNode>().map(|n| &n.node))
658            .or_else(|| value.downcast_ops::<RemoteSymlink>().map(|n| &n.node))
659            .ok_or(())
660    }
661}
662
663/// This is the most common type of node.  It is used for files and directories.  Symlinks and
664/// special nodes use RemoteSymlink and RemoteSpecialNode respectively.
665struct RemoteNode {
666    node: BaseNode,
667}
668
669impl RemoteNode {
670    fn new(io: RemoteIo, dirty: bool) -> Self {
671        Self { node: BaseNode::new(io, dirty) }
672    }
673}
674
675/// Creates a file handle from a zx::NullableHandle.
676///
677/// The handle must be a channel, socket, vmo or debuglog object.  If the handle is a channel, then
678/// the channel must implement the `fuchsia.unknown/Queryable` protocol.  Not all protocols are
679/// supported; files and directories are, but symlinks are not.
680///
681/// The resulting object will be owned by root, and will have permissions derived from the `flags`
682/// used to open this object. This is not the same as the permissions set if the object was created
683/// using Starnix itself. We use this mainly for interfacing with objects created outside of Starnix
684/// where these flags represent the desired permissions already.
685pub fn new_remote_file<L>(
686    locked: &mut Locked<L>,
687    current_task: &CurrentTask,
688    handle: zx::NullableHandle,
689    flags: OpenFlags,
690) -> Result<FileHandle, Errno>
691where
692    L: LockEqualOrBefore<FileOpsCore>,
693{
694    let remote_creds = current_task.current_creds().clone();
695    let (attrs, ops) = remote_file_attrs_and_ops(current_task, handle, remote_creds)?;
696    let mut rights = fio::Flags::empty();
697    if flags.can_read() {
698        rights |= fio::PERM_READABLE;
699    }
700    if flags.can_write() {
701        rights |= fio::PERM_WRITABLE;
702    }
703    let mode = get_mode(&attrs, rights);
704    // TODO: https://fxbug.dev/407611229 - Give these nodes valid labels.
705    let mut info = FsNodeInfo::new(mode, FsCred::root());
706    update_info_from_attrs(&mut info, &attrs);
707    Ok(Anon::new_private_file_extended(locked, current_task, ops, flags, "[fuchsia:remote]", info))
708}
709
710/// Creates a FileOps from a zx::NullableHandle.
711///
712/// The handle must satisfy the same requirements as `new_remote_file`.
713pub fn new_remote_file_ops(
714    current_task: &CurrentTask,
715    handle: zx::NullableHandle,
716    creds: Arc<Credentials>,
717) -> Result<Box<dyn FileOps>, Errno> {
718    let (_, ops) = remote_file_attrs_and_ops(current_task, handle, creds)?;
719    Ok(ops)
720}
721
722fn remote_file_attrs_and_ops(
723    current_task: &CurrentTask,
724    mut handle: zx::NullableHandle,
725    remote_creds: Arc<Credentials>,
726) -> Result<(zxio_node_attr, Box<dyn FileOps>), Errno> {
727    let handle_type =
728        handle.basic_info().map_err(|status| from_status_like_fdio!(status))?.object_type;
729
730    if handle_type == zx::ObjectType::CHANNEL {
731        let channel = zx::Channel::from(handle);
732        let queryable = funknown::QueryableSynchronousProxy::new(channel);
733        let protocol = queryable.query(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
734        const UNIX_DOMAIN_SOCKET_PROTOCOL: &[u8] =
735            fbinder::UnixDomainSocketMarker::PROTOCOL_NAME.as_bytes();
736        const FILE_PROTOCOL: &[u8] = fio::FileMarker::PROTOCOL_NAME.as_bytes();
737        const DIRECTORY_PROTOCOL: &[u8] = fio::DirectoryMarker::PROTOCOL_NAME.as_bytes();
738        match &protocol[..] {
739            UNIX_DOMAIN_SOCKET_PROTOCOL => {
740                let socket_ops =
741                    RemoteUnixDomainSocket::new(queryable.into_channel(), remote_creds)?;
742                let socket = Socket::new_with_ops(Box::new(socket_ops))?;
743                let file_ops = SocketFile::new(socket);
744                let attr = zxio_node_attr {
745                    has: zxio_node_attr_has_t { mode: true, ..zxio_node_attr_has_t::default() },
746                    mode: 0o777 | FileMode::IFSOCK.bits(),
747                    ..zxio_node_attr::default()
748                };
749                return Ok((attr, file_ops));
750            }
751            FILE_PROTOCOL => {
752                let file_proxy = fio::FileSynchronousProxy::from(queryable.into_channel());
753                let info =
754                    file_proxy.describe(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
755                let io = RemoteIo::with_stream(
756                    file_proxy.into_channel().into(),
757                    info.stream.unwrap_or_else(|| zx::NullableHandle::invalid().into()),
758                );
759                let attr = io
760                    .attr_get_zxio(MODE_ATTRIBUTES | NODE_INFO_ATTRIBUTES)
761                    .map_err(map_sync_io_client_error)?;
762                return Ok((attr, Box::new(AnonymousRemoteFileObject::new(io))));
763            }
764            DIRECTORY_PROTOCOL => {
765                let io = RemoteIo::new(queryable.into_channel().into());
766                let attr = io
767                    .attr_get_zxio(MODE_ATTRIBUTES | NODE_INFO_ATTRIBUTES)
768                    .map_err(map_sync_io_client_error)?;
769                return Ok((
770                    attr,
771                    Box::new(RemoteDirectoryObject::new(io.into_proxy().into_channel().into())),
772                ));
773            }
774            _ => {
775                handle = queryable.into_channel().into_handle();
776                // Fall through for zxio.
777            }
778        }
779    } else if handle_type == zx::ObjectType::COUNTER {
780        let attr = zxio_node_attr::default();
781        let file_ops = Box::new(RemoteCounter::new(handle.into()));
782        return Ok((attr, file_ops));
783    }
784
785    // Otherwise, use zxio based objects.
786
787    // NOTE: If it's a channel, this will repeat the query, which is something we can optimize if we
788    // need to.
789    let zxio = Zxio::create(handle).map_err(|status| from_status_like_fdio!(status))?;
790    let mut attrs = zxio
791        .attr_get(zxio_node_attr_has_t {
792            protocols: true,
793            content_size: true,
794            storage_size: true,
795            link_count: true,
796            object_type: true,
797            ..Default::default()
798        })
799        .map_err(|status| from_status_like_fdio!(status))?;
800    let ops: Box<dyn FileOps> = match (handle_type, attrs.object_type) {
801        (zx::ObjectType::VMO, _) | (zx::ObjectType::DEBUGLOG, _) | (_, ZXIO_OBJECT_TYPE_NONE) => {
802            Box::new(RemoteZxioFileObject::new(zxio))
803        }
804        (zx::ObjectType::SOCKET, _)
805        | (_, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET)
806        | (_, ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET)
807        | (_, ZXIO_OBJECT_TYPE_STREAM_SOCKET)
808        | (_, ZXIO_OBJECT_TYPE_RAW_SOCKET)
809        | (_, ZXIO_OBJECT_TYPE_PACKET_SOCKET) => {
810            let socket_ops = ZxioBackedSocket::new_with_zxio(current_task, zxio);
811            let socket = Socket::new_with_ops(Box::new(socket_ops))?;
812            attrs.has.mode = true;
813            attrs.mode = FileMode::IFSOCK.bits();
814            SocketFile::new(socket)
815        }
816        _ => return error!(ENOTSUP),
817    };
818    Ok((attrs, ops))
819}
820
821pub fn create_fuchsia_pipe<L>(
822    locked: &mut Locked<L>,
823    current_task: &CurrentTask,
824    socket: zx::Socket,
825    flags: OpenFlags,
826) -> Result<FileHandle, Errno>
827where
828    L: LockEqualOrBefore<FileOpsCore>,
829{
830    new_remote_file(locked, current_task, socket.into(), flags)
831}
832
833// This only needs to include attributes that can be out of date.  There are other attributes that
834// we read when we first look up the node (see `lookup`).
835const NODE_INFO_ATTRIBUTES: fio::NodeAttributesQuery = fio::NodeAttributesQuery::CONTENT_SIZE
836    .union(fio::NodeAttributesQuery::STORAGE_SIZE)
837    .union(fio::NodeAttributesQuery::LINK_COUNT)
838    .union(fio::NodeAttributesQuery::MODIFICATION_TIME)
839    .union(fio::NodeAttributesQuery::CHANGE_TIME)
840    .union(fio::NodeAttributesQuery::ACCESS_TIME);
841
842/// Updates info from attrs if they are set.
843///
844// Keep in sync with `NODE_INFO_ATTRIBUTES`.
845pub(super) fn update_info_from_attrs(info: &mut FsNodeInfo, attrs: &zxio_node_attributes_t) {
846    // TODO - store these in FsNodeState and convert on fstat
847    if attrs.has.content_size {
848        info.size = attrs.content_size.try_into().unwrap_or(std::usize::MAX);
849    }
850    if attrs.has.storage_size {
851        info.blocks = usize::try_from(attrs.storage_size)
852            .unwrap_or(std::usize::MAX)
853            .div_ceil(DEFAULT_BYTES_PER_BLOCK)
854    }
855    info.blksize = DEFAULT_BYTES_PER_BLOCK;
856    if attrs.has.link_count {
857        info.link_count = attrs.link_count.try_into().unwrap_or(std::usize::MAX);
858    }
859    if attrs.has.modification_time {
860        info.time_modify =
861            UtcInstant::from_nanos(attrs.modification_time.try_into().unwrap_or(i64::MAX));
862    }
863    if attrs.has.change_time {
864        info.time_status_change =
865            UtcInstant::from_nanos(attrs.change_time.try_into().unwrap_or(i64::MAX));
866    }
867    if attrs.has.access_time {
868        info.time_access = UtcInstant::from_nanos(attrs.access_time.try_into().unwrap_or(i64::MAX));
869    }
870    // The following are only read once so they're not included in `NODE_INFO_ATTRIBUTES`.
871    if attrs.has.casefold {
872        info.casefold = attrs.casefold;
873    }
874    if attrs.has.wrapping_key_id {
875        info.wrapping_key_id = Some(attrs.wrapping_key_id);
876    }
877}
878
879/// Same as `update_info_from_attr` but uses FIDL.
880fn update_info_from_fidl(
881    info: &mut FsNodeInfo,
882    mutable: &fio::MutableNodeAttributes,
883    immutable: &fio::ImmutableNodeAttributes,
884) {
885    if let Some(content_size) = immutable.content_size {
886        info.size = content_size.try_into().unwrap_or(std::usize::MAX);
887    }
888    if let Some(storage_size) = immutable.storage_size {
889        info.blocks = usize::try_from(storage_size)
890            .unwrap_or(std::usize::MAX)
891            .div_ceil(DEFAULT_BYTES_PER_BLOCK);
892    }
893    info.blksize = DEFAULT_BYTES_PER_BLOCK;
894    if let Some(link_count) = immutable.link_count {
895        info.link_count = link_count.try_into().unwrap_or(std::usize::MAX);
896    }
897    if let Some(modification_time) = mutable.modification_time {
898        info.time_modify = UtcInstant::from_nanos(modification_time.try_into().unwrap_or(i64::MAX));
899    }
900    if let Some(change_time) = immutable.change_time {
901        info.time_status_change =
902            UtcInstant::from_nanos(change_time.try_into().unwrap_or(i64::MAX));
903    }
904    if !info.pending_time_access_update
905        && let Some(access_time) = mutable.access_time
906    {
907        info.time_access = UtcInstant::from_nanos(access_time.try_into().unwrap_or(i64::MAX));
908    }
909    // The following are only read once so they're not included in `NODE_INFO_ATTRIBUTES`.
910    if let Some(casefold) = mutable.casefold {
911        info.casefold = casefold;
912    }
913    if let Some(wrapping_key_id) = mutable.wrapping_key_id {
914        info.wrapping_key_id = Some(wrapping_key_id);
915    }
916}
917
918/// The attributes we need to request to compute the right mode.
919const MODE_ATTRIBUTES: fio::NodeAttributesQuery =
920    fio::NodeAttributesQuery::PROTOCOLS.union(fio::NodeAttributesQuery::MODE);
921
922// NOTE: Keep in sync with `MODE_ATTRIBUTES`.
923fn get_mode(attrs: &zxio_node_attributes_t, rights: fio::Flags) -> FileMode {
924    if attrs.protocols & ZXIO_NODE_PROTOCOL_SYMLINK != 0 {
925        // We don't set the mode for symbolic links , so we synthesize it instead.
926        FileMode::IFLNK | FileMode::ALLOW_ALL
927    } else if attrs.has.mode {
928        // If the filesystem supports POSIX mode bits, use that directly.
929        FileMode::from_bits(attrs.mode)
930    } else {
931        // The filesystem doesn't support the `mode` attribute, so synthesize it from the protocols
932        // this node supports, and the rights used to open it.
933        let is_directory =
934            attrs.protocols & ZXIO_NODE_PROTOCOL_DIRECTORY == ZXIO_NODE_PROTOCOL_DIRECTORY;
935        let mode = if is_directory { FileMode::IFDIR } else { FileMode::IFREG };
936        let mut permissions = FileMode::EMPTY;
937        if rights.contains(fio::PERM_READABLE) {
938            permissions |= FileMode::IRUSR;
939        }
940        if rights.contains(fio::PERM_WRITABLE) {
941            permissions |= FileMode::IWUSR;
942        }
943        if rights.contains(fio::PERM_EXECUTABLE) {
944            permissions |= FileMode::IXUSR;
945        }
946        // Make sure the same permissions are granted to user, group, and other.
947        permissions |= FileMode::from_bits((permissions.bits() >> 3) | (permissions.bits() >> 6));
948        mode | permissions
949    }
950}
951
952/// Same as `get_mode` but uses FIDL.
953fn get_mode_from_fidl(
954    mutable: &fio::MutableNodeAttributes,
955    immutable: &fio::ImmutableNodeAttributes,
956    rights: fio::Flags,
957) -> FileMode {
958    let protocols = immutable.protocols.unwrap_or(fio::NodeProtocolKinds::empty());
959    if protocols.contains(fio::NodeProtocolKinds::SYMLINK) {
960        // We don't set the mode for symbolic links , so we synthesize it instead.
961        FileMode::IFLNK | FileMode::ALLOW_ALL
962    } else if let Some(mode) = mutable.mode {
963        // If the filesystem supports POSIX mode bits, use that directly.
964        FileMode::from_bits(mode)
965    } else {
966        // The filesystem doesn't support the `mode` attribute, so synthesize it from the protocols
967        // this node supports, and the rights used to open it.
968        let is_directory = protocols.contains(fio::NodeProtocolKinds::DIRECTORY);
969        let mode = if is_directory { FileMode::IFDIR } else { FileMode::IFREG };
970        let mut permissions = FileMode::EMPTY;
971        if rights.contains(fio::PERM_READABLE) {
972            permissions |= FileMode::IRUSR;
973        }
974        if rights.contains(fio::PERM_WRITABLE) {
975            permissions |= FileMode::IWUSR;
976        }
977        if rights.contains(fio::PERM_EXECUTABLE) {
978            permissions |= FileMode::IXUSR;
979        }
980        // Make sure the same permissions are granted to user, group, and other.
981        permissions |= FileMode::from_bits((permissions.bits() >> 3) | (permissions.bits() >> 6));
982        mode | permissions
983    }
984}
985
986fn get_name_str<'a>(name_bytes: &'a FsStr) -> Result<&'a str, Errno> {
987    std::str::from_utf8(name_bytes.as_ref()).map_err(|_| {
988        log_warn!("bad utf8 in pathname! remote filesystems can't handle this");
989        errno!(EINVAL)
990    })
991}
992
993impl XattrStorage for BaseNode {
994    fn get_xattr(
995        &self,
996        _locked: &mut Locked<FileOpsCore>,
997        name: &FsStr,
998    ) -> Result<FsString, Errno> {
999        Ok(self
1000            .io
1001            .xattr_get(name)
1002            .map_err(|status| match status {
1003                zx::Status::NOT_FOUND => errno!(ENODATA),
1004                status => from_status_like_fdio!(status),
1005            })?
1006            .into())
1007    }
1008
1009    fn set_xattr(
1010        &self,
1011        _locked: &mut Locked<FileOpsCore>,
1012        name: &FsStr,
1013        value: &FsStr,
1014        op: XattrOp,
1015    ) -> Result<(), Errno> {
1016        let mode = match op {
1017            XattrOp::Set => XattrSetMode::Set,
1018            XattrOp::Create => XattrSetMode::Create,
1019            XattrOp::Replace => XattrSetMode::Replace,
1020        };
1021
1022        will_dirty(&[self], || {
1023            self.io.xattr_set(name, value, mode).map_err(|status| match status {
1024                zx::Status::NOT_FOUND => errno!(ENODATA),
1025                status => from_status_like_fdio!(status),
1026            })
1027        })
1028    }
1029
1030    fn remove_xattr(&self, _locked: &mut Locked<FileOpsCore>, name: &FsStr) -> Result<(), Errno> {
1031        will_dirty(&[self], || {
1032            self.io.xattr_remove(name).map_err(|status| match status {
1033                zx::Status::NOT_FOUND => errno!(ENODATA),
1034                _ => from_status_like_fdio!(status),
1035            })
1036        })
1037    }
1038
1039    fn list_xattrs(&self, _locked: &mut Locked<FileOpsCore>) -> Result<Vec<FsString>, Errno> {
1040        self.io
1041            .xattr_list()
1042            .map(|attrs| attrs.into_iter().map(FsString::new).collect::<Vec<_>>())
1043            .map_err(map_sync_io_client_error)
1044    }
1045}
1046
1047impl FsNodeOps for RemoteNode {
1048    fs_node_impl_xattr_delegate!(self, self.node);
1049
1050    fn create_file_ops(
1051        &self,
1052        _locked: &mut Locked<FileOpsCore>,
1053        node: &FsNode,
1054        current_task: &CurrentTask,
1055        flags: OpenFlags,
1056    ) -> Result<Box<dyn FileOps>, Errno> {
1057        {
1058            // It is safe to read the cached node info here because the `wrapping_key_id` is
1059            // fetched when the node is first opened, and updated when set. We don't expect this to
1060            // change out from under Starnix.
1061            let node_info = node.info();
1062            if node_info.mode.is_dir() {
1063                if let Some(wrapping_key_id) = node_info.wrapping_key_id {
1064                    if flags.can_write() {
1065                        // Locked encrypted directories cannot be opened with write access.
1066                        let crypt_service =
1067                            node.fs().crypt_service().ok_or_else(|| errno!(ENOKEY))?;
1068                        if !crypt_service.contains_key(EncryptionKeyId::from(wrapping_key_id)) {
1069                            return error!(ENOKEY);
1070                        }
1071                    }
1072                }
1073                // For directories we need to clone the connection because we rely on the seek
1074                // offset.
1075                return Ok(Box::new(RemoteDirectoryObject::new(
1076                    self.node
1077                        .io
1078                        .clone_proxy()
1079                        .map(|p| p.into_channel().into())
1080                        .map_err(map_sync_io_client_error)?,
1081                )));
1082            }
1083        }
1084
1085        // Locked encrypted files cannot be opened.
1086        node.fail_if_locked(current_task, &node.info())?;
1087
1088        // fsverity files cannot be opened in write mode, including while building.
1089        if flags.can_write() {
1090            node.fsverity.lock().check_writable()?;
1091        }
1092
1093        Ok(Box::new(RemoteFileObject::default()))
1094    }
1095
1096    fn sync(&self, _node: &FsNode, _current_task: &CurrentTask) -> Result<(), Errno> {
1097        self.node.io.sync().map_err(map_sync_io_client_error)
1098    }
1099
1100    fn mknod(
1101        &self,
1102        _locked: &mut Locked<FileOpsCore>,
1103        node: &FsNode,
1104        current_task: &CurrentTask,
1105        name: &FsStr,
1106        mode: FileMode,
1107        dev: DeviceId,
1108        owner: FsCred,
1109    ) -> Result<FsNodeHandle, Errno> {
1110        node.fail_if_locked(current_task, &node.info())?;
1111        let name = get_name_str(name)?;
1112
1113        let fs = node.fs();
1114        let fs_ops = RemoteFs::from_fs(&fs);
1115
1116        if !(mode.is_reg() || mode.is_chr() || mode.is_blk() || mode.is_fifo() || mode.is_sock()) {
1117            return error!(EINVAL, name);
1118        }
1119
1120        let mut node_info = FsNodeInfo { rdev: dev, ..FsNodeInfo::new(mode, owner) };
1121        let (ops, node_id) = will_dirty(&[&self.node], || {
1122            self.node
1123                .io
1124                .open(
1125                    name,
1126                    fio::Flags::FLAG_MUST_CREATE
1127                        | fio::Flags::PROTOCOL_FILE
1128                        | fio::PERM_READABLE
1129                        | fio::PERM_WRITABLE,
1130                    Some(fio::MutableNodeAttributes {
1131                        mode: Some(mode.bits()),
1132                        uid: Some(owner.uid),
1133                        gid: Some(owner.gid),
1134                        rdev: Some(dev.bits()),
1135                        ..Default::default()
1136                    }),
1137                    fio::NodeAttributesQuery::ID | fio::NodeAttributesQuery::WRAPPING_KEY_ID,
1138                    Factory { node_info: &mut node_info, assume_special: !mode.is_reg() },
1139                )
1140                .map_err(|status| from_status_like_fdio!(status, name))
1141        })?;
1142
1143        let node_id = if fs_ops.use_remote_ids { node_id } else { fs.allocate_ino() };
1144
1145        let child = fs.create_node(node_id, ops, node_info);
1146        Ok(child)
1147    }
1148
1149    fn mkdir(
1150        &self,
1151        _locked: &mut Locked<FileOpsCore>,
1152        node: &FsNode,
1153        current_task: &CurrentTask,
1154        name: &FsStr,
1155        mode: FileMode,
1156        owner: FsCred,
1157    ) -> Result<FsNodeHandle, Errno> {
1158        node.fail_if_locked(current_task, &node.info())?;
1159        let name = get_name_str(name)?;
1160
1161        let fs = node.fs();
1162        let fs_ops = RemoteFs::from_fs(&fs);
1163
1164        let mut node_info = FsNodeInfo::new(mode, owner);
1165        let (ops, node_id) = will_dirty(&[&self.node], || {
1166            self.node
1167                .io
1168                .open(
1169                    name,
1170                    fio::Flags::FLAG_MUST_CREATE
1171                        | fio::Flags::PROTOCOL_DIRECTORY
1172                        | fio::PERM_READABLE
1173                        | fio::PERM_WRITABLE,
1174                    Some(fio::MutableNodeAttributes {
1175                        mode: Some(mode.bits()),
1176                        uid: Some(owner.uid),
1177                        gid: Some(owner.gid),
1178                        ..Default::default()
1179                    }),
1180                    fio::NodeAttributesQuery::ID | fio::NodeAttributesQuery::WRAPPING_KEY_ID,
1181                    Factory { node_info: &mut node_info, assume_special: false },
1182                )
1183                .map_err(|status| from_status_like_fdio!(status, name))
1184        })?;
1185
1186        let node_id = if fs_ops.use_remote_ids { node_id } else { fs.allocate_ino() };
1187
1188        let child = fs.create_node(node_id, ops, node_info);
1189        Ok(child)
1190    }
1191
1192    fn lookup(
1193        &self,
1194        _locked: &mut Locked<FileOpsCore>,
1195        node: &FsNode,
1196        current_task: &CurrentTask,
1197        name: &FsStr,
1198    ) -> Result<FsNodeHandle, Errno> {
1199        let name = get_name_str(name)?;
1200
1201        let fs = node.fs();
1202        let fs_ops = RemoteFs::from_fs(&fs);
1203
1204        let mut query = MODE_ATTRIBUTES
1205            | NODE_INFO_ATTRIBUTES
1206            | fio::NodeAttributesQuery::ID
1207            | fio::NodeAttributesQuery::UID
1208            | fio::NodeAttributesQuery::GID
1209            | fio::NodeAttributesQuery::RDEV
1210            | fio::NodeAttributesQuery::WRAPPING_KEY_ID
1211            | fio::NodeAttributesQuery::VERITY_ENABLED
1212            | fio::NodeAttributesQuery::CASEFOLD;
1213
1214        if security::fs_is_xattr_labeled(node.fs()) {
1215            query |= fio::NodeAttributesQuery::SELINUX_CONTEXT;
1216        }
1217
1218        self.node
1219            .io
1220            .open(name, fs_ops.root_rights, None, query, LookupFactory { fs: &fs, current_task })
1221            .map_err(|status| from_status_like_fdio!(status, name))?
1222    }
1223
1224    fn has_lookup_pipelined(&self) -> bool {
1225        true
1226    }
1227
1228    fn lookup_pipelined(
1229        &self,
1230        _locked: &mut Locked<FileOpsCore>,
1231        node: &FsNode,
1232        current_task: &CurrentTask,
1233        names: &[&FsStr],
1234    ) -> LookupVec<Result<FsNodeHandle, Errno>> {
1235        let fs = node.fs();
1236        let fs_ops = RemoteFs::from_fs(&fs);
1237
1238        let mut query = MODE_ATTRIBUTES
1239            | NODE_INFO_ATTRIBUTES
1240            | fio::NodeAttributesQuery::ID
1241            | fio::NodeAttributesQuery::UID
1242            | fio::NodeAttributesQuery::GID
1243            | fio::NodeAttributesQuery::RDEV
1244            | fio::NodeAttributesQuery::WRAPPING_KEY_ID
1245            | fio::NodeAttributesQuery::VERITY_ENABLED
1246            | fio::NodeAttributesQuery::CASEFOLD;
1247
1248        if security::fs_is_xattr_labeled(node.fs()) {
1249            query |= fio::NodeAttributesQuery::SELINUX_CONTEXT;
1250        }
1251
1252        let names_str =
1253            match names.iter().map(|n| get_name_str(n)).collect::<Result<LookupVec<_>, Errno>>() {
1254                Ok(names_str) => names_str,
1255                Err(e) => return vec![Err(e)].into(),
1256            };
1257
1258        self.node
1259            .io
1260            .open_pipelined(&names_str, fs_ops.root_rights, query, || LookupFactory {
1261                fs: &fs,
1262                current_task,
1263            })
1264            .map(|r| r.map_err(|status| from_status_like_fdio!(status)).flatten())
1265            .collect()
1266    }
1267
1268    fn truncate(
1269        &self,
1270        _locked: &mut Locked<FileOpsCore>,
1271        _guard: &AppendLockWriteGuard<'_>,
1272        node: &FsNode,
1273        current_task: &CurrentTask,
1274        length: u64,
1275    ) -> Result<(), Errno> {
1276        node.fail_if_locked(current_task, &node.info())?;
1277
1278        let _guard = self.node.info_state.dirty_op_guard(true);
1279
1280        self.node.io.truncate(length).map_err(|status| from_status_like_fdio!(status))
1281    }
1282
1283    fn allocate(
1284        &self,
1285        _locked: &mut Locked<FileOpsCore>,
1286        _guard: &AppendLockWriteGuard<'_>,
1287        node: &FsNode,
1288        current_task: &CurrentTask,
1289        mode: FallocMode,
1290        offset: u64,
1291        length: u64,
1292    ) -> Result<(), Errno> {
1293        match mode {
1294            FallocMode::Allocate { keep_size: false } => {
1295                node.fail_if_locked(current_task, &node.info())?;
1296
1297                will_dirty(&[&self.node], || {
1298                    self.node
1299                        .io
1300                        .allocate(offset, length, AllocateMode::empty())
1301                        .map_err(|status| from_status_like_fdio!(status))
1302                })?;
1303                Ok(())
1304            }
1305            _ => error!(EINVAL),
1306        }
1307    }
1308
1309    fn fetch_and_refresh_info<'a>(
1310        &self,
1311        _locked: &mut Locked<FileOpsCore>,
1312        _node: &FsNode,
1313        _current_task: &CurrentTask,
1314        info: &'a DynamicLockDepRwLock<FsNodeInfo>,
1315    ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
1316        self.node.fetch_and_refresh_info(info)
1317    }
1318
1319    fn update_attributes(
1320        &self,
1321        _locked: &mut Locked<FileOpsCore>,
1322        _node: &FsNode,
1323        _current_task: &CurrentTask,
1324        info: &FsNodeInfo,
1325        has: zxio_node_attr_has_t,
1326    ) -> Result<(), Errno> {
1327        // Attributes of regular remote nodes (files, directory) are valid to update.
1328        // Their metadata is stored and managed by the underlying Fuchsia filesystem.
1329        self.node.update_attributes(info, has)
1330    }
1331
1332    fn unlink(
1333        &self,
1334        _locked: &mut Locked<FileOpsCore>,
1335        node: &FsNode,
1336        _current_task: &CurrentTask,
1337        name: &FsStr,
1338        child: &FsNodeHandle,
1339    ) -> Result<(), Errno> {
1340        // We don't care about the child argument because 1. unlinking already takes the parent's
1341        // children lock, so we don't have to worry about conflicts on this path, and 2. the remote
1342        // filesystem tracks the link counts so we don't need to update them here.
1343        let name = get_name_str(name)?;
1344        will_dirty(&[node, child], || {
1345            self.node
1346                .io
1347                .unlink(name, fio::UnlinkFlags::empty())
1348                .map_err(|status| from_status_like_fdio!(status))
1349        })
1350    }
1351
1352    fn create_symlink(
1353        &self,
1354        _locked: &mut Locked<FileOpsCore>,
1355        node: &FsNode,
1356        current_task: &CurrentTask,
1357        name: &FsStr,
1358        target: &FsStr,
1359        owner: FsCred,
1360    ) -> Result<FsNodeHandle, Errno> {
1361        node.fail_if_locked(current_task, &node.info())?;
1362
1363        let name = get_name_str(name)?;
1364        let io = will_dirty(&[&self.node], || {
1365            self.node
1366                .io
1367                .create_symlink(name, target)
1368                .map_err(|status| from_status_like_fdio!(status))
1369        })?;
1370
1371        let fs = node.fs();
1372        let fs_ops = RemoteFs::from_fs(&fs);
1373
1374        let node_id = if fs_ops.use_remote_ids {
1375            io.attr_get(fio::NodeAttributesQuery::ID)
1376                .map_err(|status| from_status_like_fdio!(status))?
1377                .1
1378                .id
1379                .unwrap_or_default()
1380        } else {
1381            fs.allocate_ino()
1382        };
1383        Ok(fs.create_node(
1384            node_id,
1385            RemoteSymlink::new(BaseNode::new(io, true), target.as_bytes()),
1386            FsNodeInfo {
1387                size: target.len(),
1388                ..FsNodeInfo::new(FileMode::IFLNK | FileMode::ALLOW_ALL, owner)
1389            },
1390        ))
1391    }
1392
1393    fn create_tmpfile(
1394        &self,
1395        node: &FsNode,
1396        _current_task: &CurrentTask,
1397        mode: FileMode,
1398        owner: FsCred,
1399    ) -> Result<FsNodeHandle, Errno> {
1400        let fs = node.fs();
1401        let fs_ops = RemoteFs::from_fs(&fs);
1402
1403        if !mode.is_reg() {
1404            return error!(EINVAL);
1405        }
1406
1407        // `create_tmpfile` is used by O_TMPFILE. Note that
1408        // <https://man7.org/linux/man-pages/man2/open.2.html> states that if O_EXCL is specified
1409        // with O_TMPFILE, the temporary file created cannot be linked into the filesystem. Although
1410        // there exist fuchsia flags `fio::FLAG_TEMPORARY_AS_NOT_LINKABLE`, the starnix vfs already
1411        // handles this case and makes sure that the created file is not linkable. There is also no
1412        // way of passing the open flags to this function.
1413        let mut node_info = FsNodeInfo::new(mode, owner);
1414        let (ops, node_id) = will_dirty(&[&self.node], || {
1415            self.node
1416                .io
1417                .open(
1418                    ".",
1419                    fio::Flags::PROTOCOL_FILE
1420                        | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
1421                        | fio::PERM_READABLE
1422                        | fio::PERM_WRITABLE,
1423                    Some(fio::MutableNodeAttributes {
1424                        mode: Some(mode.bits()),
1425                        uid: Some(owner.uid),
1426                        gid: Some(owner.gid),
1427                        ..Default::default()
1428                    }),
1429                    fio::NodeAttributesQuery::ID,
1430                    Factory { node_info: &mut node_info, assume_special: false },
1431                )
1432                .map_err(|status| from_status_like_fdio!(status))
1433        })?;
1434
1435        let node_id = if fs_ops.use_remote_ids { node_id } else { fs.allocate_ino() };
1436        Ok(fs.create_node(node_id, ops, node_info))
1437    }
1438
1439    fn link(
1440        &self,
1441        _locked: &mut Locked<FileOpsCore>,
1442        node: &FsNode,
1443        _current_task: &CurrentTask,
1444        name: &FsStr,
1445        child: &FsNodeHandle,
1446    ) -> Result<(), Errno> {
1447        if !RemoteFs::from_fs(&node.fs()).use_remote_ids {
1448            return error!(EPERM);
1449        }
1450        let name = get_name_str(name)?;
1451
1452        will_dirty(&[node, child], || {
1453            if let Some(child) = child.downcast_ops::<RemoteNode>() {
1454                child.node.io.link_into(&self.node.io, name).map_err(|status| match status {
1455                    zx::Status::BAD_STATE => errno!(EXDEV),
1456                    zx::Status::ACCESS_DENIED => errno!(ENOKEY),
1457                    s => from_status_like_fdio!(s),
1458                })
1459            } else if let Some(child) = child.downcast_ops::<RemoteSymlink>() {
1460                child.node.io.link_into(&self.node.io, name).map_err(|status| match status {
1461                    zx::Status::BAD_STATE => errno!(EXDEV),
1462                    zx::Status::ACCESS_DENIED => errno!(ENOKEY),
1463                    s => from_status_like_fdio!(s),
1464                })
1465            } else {
1466                error!(EXDEV)
1467            }
1468        })
1469    }
1470
1471    fn forget(
1472        self: Box<Self>,
1473        _locked: &mut Locked<FileOpsCore>,
1474        _current_task: &CurrentTask,
1475        info: FsNodeInfo,
1476    ) -> Result<(), Errno> {
1477        // Before forgetting this node, update atime if we need to.
1478        if info.pending_time_access_update {
1479            self.node
1480                .io
1481                .attr_get(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
1482                .map_err(|status| from_status_like_fdio!(status))?;
1483        }
1484        Ok(())
1485    }
1486
1487    fn enable_fsverity(
1488        &self,
1489        _locked: &mut Locked<FileOpsCore>,
1490        _node: &FsNode,
1491        _current_task: &CurrentTask,
1492        descriptor: &fsverity_descriptor,
1493    ) -> Result<(), Errno> {
1494        let descr = zxio_fsverity_descriptor_t {
1495            hash_algorithm: descriptor.hash_algorithm,
1496            salt_size: descriptor.salt_size,
1497            salt: descriptor.salt,
1498        };
1499        will_dirty(&[&self.node], || {
1500            self.node.io.enable_verity(&descr).map_err(|status| from_status_like_fdio!(status))
1501        })
1502    }
1503
1504    fn get_fsverity_descriptor(&self, log_blocksize: u8) -> Result<fsverity_descriptor, Errno> {
1505        let (_, attrs) = self
1506            .node
1507            .io
1508            .attr_get(
1509                fio::NodeAttributesQuery::CONTENT_SIZE
1510                    | fio::NodeAttributesQuery::OPTIONS
1511                    | fio::NodeAttributesQuery::ROOT_HASH,
1512            )
1513            .map_err(|status| from_status_like_fdio!(status))?;
1514        let fio::ImmutableNodeAttributes {
1515            content_size: Some(data_size),
1516            options:
1517                Some(fio::VerificationOptions {
1518                    hash_algorithm: Some(hash_algorithm),
1519                    salt: Some(salt),
1520                    ..
1521                }),
1522            root_hash: Some(root_hash),
1523            ..
1524        } = attrs
1525        else {
1526            return error!(ENODATA);
1527        };
1528        let mut descriptor = fsverity_descriptor {
1529            version: 1,
1530            hash_algorithm: hash_algorithm.into_primitive(),
1531            log_blocksize,
1532            __reserved_0x04: 0u32,
1533            data_size,
1534            ..Default::default()
1535        };
1536        if salt.len() > std::mem::size_of_val(&descriptor.salt)
1537            || root_hash.len() > std::mem::size_of_val(&descriptor.root_hash)
1538        {
1539            return error!(EIO);
1540        }
1541        descriptor.salt_size = salt.len() as u8;
1542        descriptor.salt[..salt.len()].copy_from_slice(&salt);
1543        descriptor.root_hash[..root_hash.len()].copy_from_slice(&root_hash);
1544        Ok(descriptor)
1545    }
1546
1547    fn get_size(
1548        &self,
1549        locked: &mut Locked<FileOpsCore>,
1550        node: &FsNode,
1551        current_task: &CurrentTask,
1552    ) -> Result<usize, Errno> {
1553        if self.node.info_state.is_size_accurate() {
1554            node.info()
1555        } else {
1556            node.fetch_and_refresh_info(locked, current_task)?
1557        }
1558        .size
1559        .try_into()
1560        .map_err(|_| errno!(EINVAL))
1561    }
1562}
1563
1564struct RemoteSpecialNode {
1565    node: BaseNode,
1566}
1567
1568impl FsNodeOps for RemoteSpecialNode {
1569    fs_node_impl_not_dir!();
1570    fs_node_impl_xattr_delegate!(self, self.node);
1571
1572    fn create_file_ops(
1573        &self,
1574        _locked: &mut Locked<FileOpsCore>,
1575        _node: &FsNode,
1576        _current_task: &CurrentTask,
1577        _flags: OpenFlags,
1578    ) -> Result<Box<dyn FileOps>, Errno> {
1579        unreachable!("Special nodes cannot be opened.");
1580    }
1581
1582    fn update_attributes(
1583        &self,
1584        _locked: &mut Locked<FileOpsCore>,
1585        _node: &FsNode,
1586        _current_task: &CurrentTask,
1587        info: &FsNodeInfo,
1588        has: zxio_node_attr_has_t,
1589    ) -> Result<(), Errno> {
1590        // Attributes of special remote nodes (sockets, devices, etc.) are valid to update.
1591        // Their metadata is stored and managed by the underlying Fuchsia filesystem.
1592        self.node.update_attributes(info, has)
1593    }
1594}
1595
1596struct RemoteDirectoryObject(sync_io_client::RemoteDirectory);
1597
1598impl RemoteDirectoryObject {
1599    fn new(proxy: fio::DirectorySynchronousProxy) -> Self {
1600        Self(sync_io_client::RemoteDirectory::new(proxy))
1601    }
1602}
1603
1604impl FileOps for RemoteDirectoryObject {
1605    fileops_impl_directory!();
1606
1607    fn seek(
1608        &self,
1609        _locked: &mut Locked<FileOpsCore>,
1610        _file: &FileObject,
1611        _current_task: &CurrentTask,
1612        current_offset: off_t,
1613        target: SeekTarget,
1614    ) -> Result<off_t, Errno> {
1615        Ok(self
1616            .0
1617            .seek(default_seek(current_offset, target, || error!(EINVAL))? as u64)
1618            .map_err(map_sync_io_client_error)? as i64)
1619    }
1620
1621    fn readdir(
1622        &self,
1623        _locked: &mut Locked<FileOpsCore>,
1624        file: &FileObject,
1625        _current_task: &CurrentTask,
1626        sink: &mut dyn DirentSink,
1627    ) -> Result<(), Errno> {
1628        match self
1629            .0
1630            .readdir(|mut inode_num, entry_type, name| {
1631                if name == b".." {
1632                    inode_num = if let Some(parent) = file.name.parent_within_mount() {
1633                        parent.node.ino
1634                    } else {
1635                        // For the root .. should have the same inode number as .
1636                        file.name.entry.node.ino
1637                    };
1638                }
1639                let entry_type = match entry_type {
1640                    fio::DirentType::Directory => DirectoryEntryType::DIR,
1641                    fio::DirentType::File => DirectoryEntryType::REG,
1642                    fio::DirentType::Symlink => DirectoryEntryType::LNK,
1643                    _ => DirectoryEntryType::UNKNOWN,
1644                };
1645                match sink.add(inode_num, sink.offset() + 1, entry_type, name.into()) {
1646                    Ok(()) => ControlFlow::Continue(()),
1647                    Err(e) => ControlFlow::Break(e),
1648                }
1649            })
1650            .map_err(map_sync_io_client_error)?
1651        {
1652            None => Ok(()),
1653            Some(e) => Err(e),
1654        }
1655    }
1656
1657    fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1658        self.0.sync().map_err(map_sync_error)
1659    }
1660
1661    fn to_handle(
1662        &self,
1663        _file: &FileObject,
1664        _current_task: &CurrentTask,
1665    ) -> Result<Option<zx::NullableHandle>, Errno> {
1666        // If expose a handle to a directory to a Fuchsia component, we trust that it will not
1667        // modify the directory in a way that will confuse Starnix.
1668        self.0
1669            .clone_proxy()
1670            .map_err(map_sync_io_client_error)
1671            .map(|p| Some(p.into_channel().into()))
1672    }
1673}
1674
1675#[derive(Default)]
1676pub struct RemoteFileObject {
1677    /// Cached read-only VMO handle.
1678    read_only_memory: OnceCell<Arc<MemoryObject>>,
1679
1680    /// Cached read/exec VMO handle.
1681    read_exec_memory: OnceCell<Arc<MemoryObject>>,
1682}
1683
1684impl RemoteFileObject {
1685    /// # Panics
1686    ///
1687    /// This will panic if the node's ops are not `RemoteNode`; `AnonymousRemoteFileObject` should
1688    /// be used if this won't be the case.
1689    fn io(file: &FileObject) -> &RemoteIo {
1690        &file.node().downcast_ops::<RemoteNode>().unwrap().node.io
1691    }
1692}
1693
1694trait RemoteIoExt {
1695    fn read_to_output_buffer(
1696        &self,
1697        offset: u64,
1698        buffer: &mut dyn OutputBuffer,
1699    ) -> Result<usize, Errno>;
1700    fn write_from_input_buffer(
1701        &self,
1702        offset: u64,
1703        buffer: &mut dyn InputBuffer,
1704    ) -> Result<usize, Errno>;
1705    fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno>;
1706}
1707
1708impl RemoteIoExt for RemoteIo {
1709    fn read_to_output_buffer(
1710        &self,
1711        offset: u64,
1712        buffer: &mut dyn OutputBuffer,
1713    ) -> Result<usize, Errno> {
1714        if self.supports_vectored()
1715            && let Some(actual) = with_iovec_segments(buffer, |iovecs| {
1716                // SAFETY: The iovecs are known to point to userspace, so any damage we do here is
1717                // limited to userspace.  Zircon will catch faults and return an error.
1718                unsafe { self.readv(offset, iovecs).map_err(map_stream_error) }
1719            })
1720        {
1721            let actual = actual?;
1722            // SAFETY: we successfully read `actual` bytes directly to the user's buffer
1723            // segments.
1724            unsafe { buffer.advance(actual) }?;
1725            Ok(actual)
1726        } else {
1727            self.read(
1728                offset,
1729                buffer.available(),
1730                |data| buffer.write(&data),
1731                map_sync_io_client_error,
1732            )
1733        }
1734    }
1735
1736    fn write_from_input_buffer(
1737        &self,
1738        offset: u64,
1739        buffer: &mut dyn InputBuffer,
1740    ) -> Result<usize, Errno> {
1741        let actual = if self.supports_vectored()
1742            && let Some(actual) = with_iovec_segments(buffer, |iovecs| {
1743                self.writev(offset, iovecs).map_err(map_stream_error)
1744            }) {
1745            actual?
1746        } else {
1747            self.write(offset as u64, &buffer.peek_all()?).map_err(map_sync_io_client_error)?
1748        };
1749        buffer.advance(actual)?;
1750        Ok(actual)
1751    }
1752
1753    fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno> {
1754        let without_exec = self
1755            .vmo_get(prot.to_vmar_flags() - zx::VmarFlags::PERM_EXECUTE)
1756            .map_err(|status| from_status_like_fdio!(status))?;
1757        let all_flags = if prot.contains(ProtectionFlags::EXEC) {
1758            without_exec.replace_as_executable(&VMEX_RESOURCE).map_err(impossible_error)?
1759        } else {
1760            without_exec
1761        };
1762        Ok(Arc::new(MemoryObject::from(all_flags)))
1763    }
1764}
1765
1766impl FileOps for RemoteFileObject {
1767    fileops_impl_seekable!();
1768
1769    fn read(
1770        &self,
1771        _locked: &mut Locked<FileOpsCore>,
1772        file: &FileObject,
1773        _current_task: &CurrentTask,
1774        offset: usize,
1775        data: &mut dyn OutputBuffer,
1776    ) -> Result<usize, Errno> {
1777        Self::io(file).read_to_output_buffer(offset as u64, data)
1778    }
1779
1780    fn write(
1781        &self,
1782        _locked: &mut Locked<FileOpsCore>,
1783        file: &FileObject,
1784        _current_task: &CurrentTask,
1785        offset: usize,
1786        data: &mut dyn InputBuffer,
1787    ) -> Result<usize, Errno> {
1788        will_dirty(&[&***file.node()], || {
1789            let written = Self::io(file).write_from_input_buffer(offset as u64, data)?;
1790
1791            // If we increased the file size, we need to update that here so that `NodeInfo::size`
1792            // is accurate.  This is done so that we can optimize `get_size`.  If the file has been
1793            // truncated, then the size might not be accurate, but we track that separately and
1794            // `get_size` will fetch the file size from the remote end in that case.
1795            if written > 0 {
1796                file.node().update_info(|info| {
1797                    if offset + written > info.size {
1798                        info.size = offset + written;
1799                    }
1800                });
1801            }
1802
1803            Ok(written)
1804        })
1805    }
1806
1807    fn get_memory(
1808        &self,
1809        _locked: &mut Locked<FileOpsCore>,
1810        file: &FileObject,
1811        _current_task: &CurrentTask,
1812        _length: Option<usize>,
1813        prot: ProtectionFlags,
1814    ) -> Result<Arc<MemoryObject>, Errno> {
1815        fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
1816        let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
1817            Some(&self.read_exec_memory)
1818        } else if prot == ProtectionFlags::READ {
1819            Some(&self.read_only_memory)
1820        } else {
1821            None
1822        };
1823
1824        let io = Self::io(file);
1825
1826        memory_cache
1827            .map(|c| c.get_or_try_init(|| io.fetch_remote_memory(prot)).cloned())
1828            .unwrap_or_else(|| io.fetch_remote_memory(prot))
1829    }
1830
1831    fn to_handle(
1832        &self,
1833        file: &FileObject,
1834        current_task: &CurrentTask,
1835    ) -> Result<Option<zx::NullableHandle>, Errno> {
1836        // To avoid cache coherency and security issues, we proxy remote files via the Starnix file
1837        // server.  This will incur a performance penalty which we can optimize later if we need to.
1838        serve_file_tagged(current_task, file, current_task.current_creds().clone(), "remote_files")
1839            .map(|c| Some(c.0.into_channel().into()))
1840    }
1841}
1842
1843/// A file object that is not attached to a `RemoteFs`, which means it stores its own `RemoteIo`.
1844pub struct AnonymousRemoteFileObject {
1845    io: RemoteIo,
1846
1847    /// Cached read-only VMO handle.
1848    read_only_memory: OnceCell<Arc<MemoryObject>>,
1849
1850    /// Cached read/exec VMO handle.
1851    read_exec_memory: OnceCell<Arc<MemoryObject>>,
1852}
1853
1854impl AnonymousRemoteFileObject {
1855    fn new(io: RemoteIo) -> Self {
1856        Self { io, read_only_memory: Default::default(), read_exec_memory: Default::default() }
1857    }
1858}
1859
1860impl FileOps for AnonymousRemoteFileObject {
1861    fileops_impl_seekable!();
1862
1863    fn read(
1864        &self,
1865        _locked: &mut Locked<FileOpsCore>,
1866        _file: &FileObject,
1867        _current_task: &CurrentTask,
1868        offset: usize,
1869        data: &mut dyn OutputBuffer,
1870    ) -> Result<usize, Errno> {
1871        self.io.read_to_output_buffer(offset as u64, data)
1872    }
1873
1874    fn write(
1875        &self,
1876        _locked: &mut Locked<FileOpsCore>,
1877        _file: &FileObject,
1878        _current_task: &CurrentTask,
1879        offset: usize,
1880        data: &mut dyn InputBuffer,
1881    ) -> Result<usize, Errno> {
1882        // As this is an anonymous file, there's no point marking the node info dirty because this
1883        // isn't backed by `RemoteNode` or `RemoteSymlink`.
1884        self.io.write_from_input_buffer(offset as u64, data)
1885    }
1886
1887    fn get_memory(
1888        &self,
1889        _locked: &mut Locked<FileOpsCore>,
1890        _file: &FileObject,
1891        _current_task: &CurrentTask,
1892        _length: Option<usize>,
1893        prot: ProtectionFlags,
1894    ) -> Result<Arc<MemoryObject>, Errno> {
1895        fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
1896        let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
1897            Some(&self.read_exec_memory)
1898        } else if prot == ProtectionFlags::READ {
1899            Some(&self.read_only_memory)
1900        } else {
1901            None
1902        };
1903
1904        memory_cache
1905            .map(|c| c.get_or_try_init(|| self.io.fetch_remote_memory(prot)).cloned())
1906            .unwrap_or_else(|| self.io.fetch_remote_memory(prot))
1907    }
1908
1909    fn to_handle(
1910        &self,
1911        _file: &FileObject,
1912        _current_task: &CurrentTask,
1913    ) -> Result<Option<zx::NullableHandle>, Errno> {
1914        // This is an anonymous file (not backed by `RemoteNode`).  Any external updates to the
1915        // file's attributes will not be tracked by Starnix.
1916        self.io
1917            .clone_proxy()
1918            .map_err(map_sync_io_client_error)
1919            .map(|p| Some(p.into_channel().into()))
1920    }
1921
1922    fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1923        self.io.sync().map_err(map_sync_io_client_error)
1924    }
1925}
1926
1927pub struct RemoteZxioFileObject {
1928    /// The underlying Zircon I/O object.  This is shared, so we must take care not to use any
1929    /// stateful methods on the underlying object (reading and writing is fine).
1930    zxio: Zxio,
1931
1932    /// Cached read-only VMO handle.
1933    read_only_memory: OnceCell<Arc<MemoryObject>>,
1934
1935    /// Cached read/exec VMO handle.
1936    read_exec_memory: OnceCell<Arc<MemoryObject>>,
1937}
1938
1939impl RemoteZxioFileObject {
1940    fn new(zxio: Zxio) -> RemoteZxioFileObject {
1941        RemoteZxioFileObject {
1942            zxio,
1943            read_only_memory: Default::default(),
1944            read_exec_memory: Default::default(),
1945        }
1946    }
1947
1948    fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno> {
1949        let without_exec = self
1950            .zxio
1951            .vmo_get(prot.to_vmar_flags() - zx::VmarFlags::PERM_EXECUTE)
1952            .map_err(|status| from_status_like_fdio!(status))?;
1953        let all_flags = if prot.contains(ProtectionFlags::EXEC) {
1954            without_exec.replace_as_executable(&VMEX_RESOURCE).map_err(impossible_error)?
1955        } else {
1956            without_exec
1957        };
1958        Ok(Arc::new(MemoryObject::from(all_flags)))
1959    }
1960}
1961
1962impl FileOps for RemoteZxioFileObject {
1963    fileops_impl_seekable!();
1964
1965    fn read(
1966        &self,
1967        _locked: &mut Locked<FileOpsCore>,
1968        _file: &FileObject,
1969        _current_task: &CurrentTask,
1970        offset: usize,
1971        data: &mut dyn OutputBuffer,
1972    ) -> Result<usize, Errno> {
1973        let offset = offset as u64;
1974        let read_bytes = with_iovec_segments::<_, syncio::zxio::zx_iovec, _>(data, |iovecs| {
1975            // SAFETY: The iovecs are valid for writing because they come from OutputBuffer.
1976            unsafe { self.zxio.readv_at(offset, iovecs).map_err(map_stream_error) }
1977        });
1978
1979        match read_bytes {
1980            Some(actual) => {
1981                let actual = actual?;
1982                // SAFETY: we successfully read `actual` bytes
1983                // directly to the user's buffer segments.
1984                unsafe { data.advance(actual) }?;
1985                Ok(actual)
1986            }
1987            None => {
1988                // Perform the (slower) operation by using an intermediate buffer.
1989                let total = data.available();
1990                let mut bytes = vec![0u8; total];
1991                let actual = self
1992                    .zxio
1993                    .read_at(offset, &mut bytes)
1994                    .map_err(|status| from_status_like_fdio!(status))?;
1995                data.write_all(&bytes[0..actual])
1996            }
1997        }
1998    }
1999
2000    fn write(
2001        &self,
2002        _locked: &mut Locked<FileOpsCore>,
2003        _file: &FileObject,
2004        _current_task: &CurrentTask,
2005        offset: usize,
2006        data: &mut dyn InputBuffer,
2007    ) -> Result<usize, Errno> {
2008        let offset = offset as u64;
2009        let write_bytes = with_iovec_segments::<_, syncio::zxio::zx_iovec, _>(data, |iovecs| {
2010            // SAFETY: The iovecs are valid for reading because they come from InputBuffer.
2011            unsafe { self.zxio.writev_at(offset, iovecs).map_err(map_stream_error) }
2012        });
2013
2014        match write_bytes {
2015            Some(actual) => {
2016                let actual = actual?;
2017                data.advance(actual)?;
2018                Ok(actual)
2019            }
2020            None => {
2021                // Perform the (slower) operation by using an intermediate buffer.
2022                let bytes = data.peek_all()?;
2023                let actual = self
2024                    .zxio
2025                    .write_at(offset, &bytes)
2026                    .map_err(|status| from_status_like_fdio!(status))?;
2027                data.advance(actual)?;
2028                Ok(actual)
2029            }
2030        }
2031    }
2032
2033    fn get_memory(
2034        &self,
2035        _locked: &mut Locked<FileOpsCore>,
2036        _file: &FileObject,
2037        _current_task: &CurrentTask,
2038        _length: Option<usize>,
2039        prot: ProtectionFlags,
2040    ) -> Result<Arc<MemoryObject>, Errno> {
2041        fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
2042        let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
2043            Some(&self.read_exec_memory)
2044        } else if prot == ProtectionFlags::READ {
2045            Some(&self.read_only_memory)
2046        } else {
2047            None
2048        };
2049
2050        memory_cache
2051            .map(|c| c.get_or_try_init(|| self.fetch_remote_memory(prot)).cloned())
2052            .unwrap_or_else(|| self.fetch_remote_memory(prot))
2053    }
2054
2055    fn to_handle(
2056        &self,
2057        _file: &FileObject,
2058        _current_task: &CurrentTask,
2059    ) -> Result<Option<zx::NullableHandle>, Errno> {
2060        self.zxio.clone_handle().map(Some).map_err(|status| from_status_like_fdio!(status))
2061    }
2062
2063    fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
2064        self.zxio.sync().map_err(map_sync_error)
2065    }
2066}
2067
2068struct RemoteSymlink {
2069    node: BaseNode,
2070    target: LockDepRwLock<Box<[u8]>, FuchsiaRemoteTargetLock>,
2071}
2072
2073impl RemoteSymlink {
2074    fn new(node: BaseNode, target: impl Into<Box<[u8]>>) -> Self {
2075        Self { node, target: LockDepRwLock::new(target.into()) }
2076    }
2077}
2078
2079impl FsNodeOps for RemoteSymlink {
2080    fs_node_impl_symlink!();
2081    fs_node_impl_xattr_delegate!(self, self.node);
2082
2083    fn readlink(
2084        &self,
2085        _locked: &mut Locked<FileOpsCore>,
2086        _node: &FsNode,
2087        _current_task: &CurrentTask,
2088    ) -> Result<SymlinkTarget, Errno> {
2089        Ok(SymlinkTarget::Path(FsString::new(self.target.read().to_vec())))
2090    }
2091
2092    fn fetch_and_refresh_info<'a>(
2093        &self,
2094        _locked: &mut Locked<FileOpsCore>,
2095        _node: &FsNode,
2096        _current_task: &CurrentTask,
2097        info: &'a DynamicLockDepRwLock<FsNodeInfo>,
2098    ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
2099        self.node.fetch_and_refresh_info(info)
2100    }
2101
2102    fn forget(
2103        self: Box<Self>,
2104        _locked: &mut Locked<FileOpsCore>,
2105        _current_task: &CurrentTask,
2106        info: FsNodeInfo,
2107    ) -> Result<(), Errno> {
2108        // Before forgetting this node, update atime if we need to.
2109        if info.pending_time_access_update {
2110            self.node
2111                .io
2112                .attr_get(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
2113                .map_err(|status| from_status_like_fdio!(status))?;
2114        }
2115        Ok(())
2116    }
2117}
2118
2119pub struct RemoteCounter {
2120    counter: Counter,
2121    koid: std::sync::OnceLock<zx::Koid>,
2122}
2123
2124impl RemoteCounter {
2125    fn new(counter: Counter) -> Self {
2126        Self { counter, koid: std::sync::OnceLock::new() }
2127    }
2128
2129    pub fn duplicate_handle(&self) -> Result<Counter, Errno> {
2130        self.counter.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)
2131    }
2132
2133    pub fn koid(&self) -> zx::Koid {
2134        *self.koid.get_or_init(|| self.counter.koid().unwrap())
2135    }
2136}
2137
2138impl FileOps for RemoteCounter {
2139    fileops_impl_nonseekable!();
2140    fileops_impl_noop_sync!();
2141
2142    fn read(
2143        &self,
2144        _locked: &mut Locked<FileOpsCore>,
2145        _file: &FileObject,
2146        _current_task: &CurrentTask,
2147        _offset: usize,
2148        _data: &mut dyn OutputBuffer,
2149    ) -> Result<usize, Errno> {
2150        error!(ENOTSUP)
2151    }
2152
2153    fn write(
2154        &self,
2155        _locked: &mut Locked<FileOpsCore>,
2156        _file: &FileObject,
2157        _current_task: &CurrentTask,
2158        _offset: usize,
2159        _data: &mut dyn InputBuffer,
2160    ) -> Result<usize, Errno> {
2161        error!(ENOTSUP)
2162    }
2163
2164    fn ioctl(
2165        &self,
2166        locked: &mut Locked<Unlocked>,
2167        file: &FileObject,
2168        current_task: &CurrentTask,
2169        request: u32,
2170        arg: SyscallArg,
2171    ) -> Result<SyscallResult, Errno> {
2172        let ioctl_type = (request >> 8) as u8;
2173        let ioctl_number = request as u8;
2174        if ioctl_type == SYNC_IOC_MAGIC
2175            && (ioctl_number == SYNC_IOC_FILE_INFO || ioctl_number == SYNC_IOC_MERGE)
2176        {
2177            let mut sync_points = Vec::with_capacity(1);
2178            let counter = self.duplicate_handle()?;
2179            // For other calls than SYNC_IOC_MERGE, the koid is never used, so we construct it
2180            // without fetching it, saving a Zircon syscall.
2181            let sp = if ioctl_number == SYNC_IOC_MERGE {
2182                SyncPoint::with_koid(Timeline::Hwc, counter.into(), self.koid())
2183            } else {
2184                SyncPoint::new(Timeline::Hwc, counter.into())
2185            };
2186            sync_points.push(sp);
2187            let sync_file_name: &[u8; 32] = b"remote counter\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
2188            let sync_file = SyncFile::new(*sync_file_name, SyncFence { sync_points });
2189            return sync_file.ioctl(locked, file, current_task, request, arg);
2190        }
2191
2192        error!(EINVAL)
2193    }
2194}
2195
2196#[track_caller]
2197fn map_sync_error(status: zx::Status) -> Errno {
2198    match status {
2199        zx::Status::NO_RESOURCES | zx::Status::NO_MEMORY | zx::Status::NO_SPACE => {
2200            errno!(ENOSPC)
2201        }
2202        zx::Status::INVALID_ARGS | zx::Status::NOT_FILE => errno!(EINVAL),
2203        zx::Status::BAD_HANDLE => errno!(EBADFD),
2204        zx::Status::NOT_SUPPORTED => errno!(ENOTSUP),
2205        zx::Status::INTERRUPTED_RETRY => errno!(EINTR),
2206        _ => errno!(EIO),
2207    }
2208}
2209
2210#[track_caller]
2211fn map_stream_error(status: zx::Status) -> Errno {
2212    match status {
2213        // zx::Stream may return invalid args or not found error because of invalid zx_iovec buffer
2214        // pointers.
2215        zx::Status::INVALID_ARGS | zx::Status::NOT_FOUND => errno!(EFAULT),
2216        status => from_status_like_fdio!(status),
2217    }
2218}
2219
2220#[track_caller]
2221fn map_sync_io_client_error(status: zx::Status) -> Errno {
2222    from_status_like_fdio!(status)
2223}
2224
2225/// Used to keep track of whether node info is in sync or dirty so that we can avoid communicating
2226/// exernally if we think the node information is in sync.
2227// The two top bits are special (see below).  The remaining bits are a count of the number of
2228// in-flight dirty operations.
2229struct InfoState(AtomicU32);
2230
2231impl InfoState {
2232    /// When this bit is set and the PENDING_REFRESH bit is *not* set, the node information is in
2233    /// sync with the external node.
2234    const IN_SYNC: u32 = 0x8000_0000;
2235
2236    /// When this bit is set in `info_state`, it means the node information is currently being
2237    /// refreshed.
2238    const PENDING_REFRESH: u32 = 0x4000_0000;
2239
2240    /// When this bit is set, it means the node has been truncated and so the size might not be
2241    /// accurate.
2242    const TRUNCATED: u32 = 0x2000_0000;
2243
2244    /// The remaining bits are used to track a count of the number of in-flight dirty operations.
2245    const COUNT_MASK: u32 = Self::TRUNCATED - 1;
2246
2247    fn new(dirty: bool) -> Self {
2248        Self(AtomicU32::new(if dirty { 0 } else { Self::IN_SYNC }))
2249    }
2250
2251    /// This guard should be taken whilst an operation that might result in dirty node information
2252    /// is in flight.  If `for_truncate` is true, this will also set the `TRUNCATED` bit.
2253    fn dirty_op_guard(&self, for_truncate: bool) -> DirtyOpGuard<'_> {
2254        // Increment the count indicating a dirty operation is in flight and also clear the
2255        // `IN_SYNC` bit to indicate the node information will need refreshing from its external
2256        // source.
2257        let mut current = self.0.load(Ordering::Relaxed);
2258        let for_truncate = if for_truncate { Self::TRUNCATED } else { 0 };
2259        loop {
2260            assert!(current & Self::COUNT_MASK != Self::COUNT_MASK); // Check overflow
2261            match self.0.compare_exchange_weak(
2262                current,
2263                ((current & !Self::IN_SYNC) + 1) | for_truncate,
2264                Ordering::Relaxed,
2265                Ordering::Relaxed,
2266            ) {
2267                Ok(_) => break,
2268                Err(old) => current = old,
2269            }
2270        }
2271        DirtyOpGuard(self)
2272    }
2273
2274    /// Calls `refresh` if node information needs to be refreshed, or `not_needed` if node
2275    /// information does not need refreshing.
2276    fn maybe_refresh<'a, T: 'a>(
2277        &self,
2278        info: &'a DynamicLockDepRwLock<FsNodeInfo>,
2279        refresh: impl FnOnce(&'a DynamicLockDepRwLock<FsNodeInfo>) -> Result<T, Errno>,
2280        not_needed: impl FnOnce(&'a DynamicLockDepRwLock<FsNodeInfo>) -> Result<T, Errno>,
2281    ) -> Result<T, Errno> {
2282        let mut current = self.0.load(Ordering::Relaxed);
2283
2284        // If node information is dirty, and there are no pending dirty operations, and there is no
2285        // other thread currently refreshing node information, we can set the bits indicating that a
2286        // refresh is pending.  We want to set the `IN_SYNC` bit here in case `will_dirty` runs
2287        // before we're done.
2288        //
2289        // NOTE: Multiple threads can be refreshing at the same time, but only one of them will
2290        // succeed in setting the `PENDING_REFRESH` bit.
2291        let mut did_set_pending_refresh = false;
2292        while current & !Self::TRUNCATED == 0 {
2293            match self.0.compare_exchange_weak(
2294                current,
2295                current | Self::IN_SYNC | Self::PENDING_REFRESH,
2296                Ordering::Relaxed,
2297                Ordering::Relaxed,
2298            ) {
2299                Ok(_) => {
2300                    did_set_pending_refresh = true;
2301                    break;
2302                }
2303                Err(old) => current = old,
2304            }
2305        }
2306
2307        // Skip the update if the cached information is in sync and there are no pending dirty
2308        // operations.  If there's a pending atime update, we'll skip updating that now; it
2309        // shouldn't be necessary and we can do it later.
2310        if current == Self::IN_SYNC {
2311            return not_needed(info);
2312        }
2313
2314        let result = refresh(info);
2315
2316        if did_set_pending_refresh {
2317            if result.is_ok() {
2318                // If the TRUNCATED bit was set, we can clear it now so long as no other dirty
2319                // operations took place.
2320                if current & Self::TRUNCATED != 0 {
2321                    // Assuming no other thread has changed the state, this is what we
2322                    // expect the current value to be.
2323                    let mut current = Self::TRUNCATED | Self::IN_SYNC | Self::PENDING_REFRESH;
2324                    while current == Self::TRUNCATED | Self::IN_SYNC | Self::PENDING_REFRESH {
2325                        match self.0.compare_exchange_weak(
2326                            current,
2327                            Self::IN_SYNC,
2328                            Ordering::Relaxed,
2329                            Ordering::Relaxed,
2330                        ) {
2331                            Ok(_) => return result,
2332                            Err(old) => current = old,
2333                        }
2334                    }
2335                    // In this case, we fall through and just clear the PENDING_REFRESH bit, but we
2336                    // leave the TRUNCATED bit untouched.
2337                }
2338                self.0.fetch_and(!Self::PENDING_REFRESH, Ordering::Relaxed);
2339            } else {
2340                // If there was an error, we should also clear the IN_SYNC bit to indicate the node
2341                // information is still dirty.
2342                self.0.fetch_and(!(Self::IN_SYNC | Self::PENDING_REFRESH), Ordering::Relaxed);
2343            }
2344        }
2345
2346        result
2347    }
2348
2349    /// Returns true if the size is accurate.
2350    fn is_size_accurate(&self) -> bool {
2351        // The size returned by `get_size` is accurate so long as the file hasn't been truncated.
2352        // If there are writes currently outstanding, then it's also not safe to return the current
2353        // size.  To understand why, consider the following scenario:
2354        //
2355        //    1. Thread A issues a write.
2356        //    2. Thread B performs a read which sees the write from thread A.
2357        //    3. Thread B now tries to seek to the end of the file.  It should be consistent with
2358        //       the read in #2.
2359        //
2360        // #3 needs to see the end-of-file as it is after the write, but it's possible that thread A
2361        // hasn't updated the size yet even though the write has been completed at the remote end.
2362        // For that reason, whilst there are potential writes outstanding, we must ask the remote
2363        // end for the size.
2364        let state = self.0.load(Ordering::Relaxed);
2365        state & (Self::TRUNCATED | Self::COUNT_MASK) == 0
2366    }
2367}
2368
2369struct DirtyOpGuard<'a>(&'a InfoState);
2370
2371impl Drop for DirtyOpGuard<'_> {
2372    fn drop(&mut self) {
2373        // Decrement the count we took when we created the guard.
2374        self.0.0.fetch_sub(1, Ordering::Relaxed);
2375    }
2376}
2377
2378/// A wrapper to be used around calls that will end up making node info dirty.
2379fn will_dirty<'a, N: TryInto<&'a BaseNode> + Copy, T>(nodes: &[N], f: impl FnOnce() -> T) -> T {
2380    // We are about to execute an operation that will make the cached information for one or more
2381    // nodes out of date, and we must deal with races.  If we mark the node as dirty first, another
2382    // thread could sneak in and refresh the node information before this operation has finished,
2383    // and then the information would be out of date.  If we only mark the node as dirty afterwards,
2384    // there is a window between when the operation completes and when we mark the node as dirty
2385    // where another thread could observe the changes caused by this operation, but still see old
2386    // node information.  So, the approach we take is to mark the node as dirty before the operation
2387    // starts, but indicate that this operation is ongoing.  Any threads that try and retrieve node
2388    // information will fetch fresh information, but, importantly, they'll leave the node marked as
2389    // dirty.  Once this operation has finished, we'll indicate this operation is no longer
2390    // in-flight, and then the next time information is refreshed, we'll mark the node information
2391    // as being in sync.
2392
2393    let _guards: SmallVec<[_; 4]> = nodes
2394        .iter()
2395        .filter_map(|n| N::try_into(*n).ok())
2396        .map(|n| n.info_state.dirty_op_guard(false))
2397        .collect();
2398
2399    f()
2400}
2401
2402#[cfg(test)]
2403mod test {
2404    use super::*;
2405    use crate::mm::PAGE_SIZE;
2406    use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
2407    use crate::testing::*;
2408    use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
2409    use crate::vfs::socket::{SocketFile, SocketMessageFlags};
2410    use crate::vfs::{EpollFileObject, LookupContext, Namespace, SymlinkMode, TimeUpdateType};
2411    use assert_matches::assert_matches;
2412    use fidl::endpoints::{ServerEnd, create_request_stream};
2413    use fidl_fuchsia_io as fio;
2414    use flyweights::FlyByteStr;
2415    use fuchsia_async as fasync;
2416    use fuchsia_runtime::UtcDuration;
2417    use futures::StreamExt;
2418    use fxfs_testing::{TestFixture, TestFixtureOptions};
2419    use starnix_sync::Mutex;
2420    use starnix_uapi::auth::Credentials;
2421    use starnix_uapi::errors::EINVAL;
2422    use starnix_uapi::file_mode::{AccessCheck, mode};
2423    use starnix_uapi::ino_t;
2424    use starnix_uapi::mount_flags::MountpointFlags;
2425    use starnix_uapi::open_flags::OpenFlags;
2426    use starnix_uapi::vfs::{EpollEvent, FdEvents};
2427    use std::sync::Barrier;
2428    use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
2429    use storage_device::DeviceHolder;
2430    use storage_device::fake_device::FakeDevice;
2431
2432    #[::fuchsia::test]
2433    async fn test_remote_uds() {
2434        spawn_kernel_and_run(async |locked, current_task| {
2435            let (s1, s2) = zx::Socket::create_datagram();
2436            s2.write(&vec![0]).expect("write");
2437            let file = new_remote_file(locked, &current_task, s1.into(), OpenFlags::RDWR)
2438                .expect("new_remote_file");
2439            assert!(file.node().is_sock());
2440            let socket_ops = file.downcast_file::<SocketFile>().unwrap();
2441            let flags = SocketMessageFlags::CTRUNC
2442                | SocketMessageFlags::TRUNC
2443                | SocketMessageFlags::NOSIGNAL
2444                | SocketMessageFlags::CMSG_CLOEXEC;
2445            let mut buffer = VecOutputBuffer::new(1024);
2446            let info = socket_ops
2447                .recvmsg(locked, &current_task, &file, &mut buffer, flags, None)
2448                .expect("recvmsg");
2449            assert!(info.ancillary_data.is_empty());
2450            assert_eq!(info.message_length, 1);
2451        })
2452        .await;
2453    }
2454
2455    #[::fuchsia::test]
2456    async fn test_tree() {
2457        spawn_kernel_and_run(async |locked, current_task| {
2458            let kernel = current_task.kernel();
2459            let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
2460            let (server, client) = zx::Channel::create();
2461            fdio::open("/pkg", rights, server).expect("failed to open /pkg");
2462            let fs = RemoteFs::new_fs(
2463                locked,
2464                &kernel,
2465                client,
2466                FileSystemOptions { source: FlyByteStr::new(b"/pkg"), ..Default::default() },
2467                rights,
2468            )
2469            .unwrap();
2470            let ns = Namespace::new(fs);
2471            let root = ns.root();
2472            let mut context = LookupContext::default();
2473            assert_eq!(
2474                root.lookup_child(locked, &current_task, &mut context, "nib".into()).err(),
2475                Some(errno!(ENOENT))
2476            );
2477            let mut context = LookupContext::default();
2478            root.lookup_child(locked, &current_task, &mut context, "lib".into()).unwrap();
2479
2480            let mut context = LookupContext::default();
2481            let _test_file = root
2482                .lookup_child(
2483                    locked,
2484                    &current_task,
2485                    &mut context,
2486                    "data/tests/hello_starnix".into(),
2487                )
2488                .unwrap()
2489                .open(locked, &current_task, OpenFlags::RDONLY, AccessCheck::default())
2490                .unwrap();
2491        })
2492        .await;
2493    }
2494
2495    #[::fuchsia::test]
2496    async fn test_blocking_io() {
2497        spawn_kernel_and_run(async |locked, current_task| {
2498            let (client, server) = zx::Socket::create_stream();
2499            let pipe = create_fuchsia_pipe(locked, &current_task, client, OpenFlags::RDWR).unwrap();
2500
2501            let bytes = [0u8; 64];
2502            assert_eq!(bytes.len(), server.write(&bytes).unwrap());
2503
2504            // Spawn a kthread to get the right lock context.
2505            let bytes_read =
2506                pipe.read(locked, &current_task, &mut VecOutputBuffer::new(64)).unwrap();
2507
2508            assert_eq!(bytes_read, bytes.len());
2509        })
2510        .await;
2511    }
2512
2513    #[::fuchsia::test]
2514    async fn test_poll() {
2515        spawn_kernel_and_run(async |locked, current_task| {
2516            let (client, server) = zx::Socket::create_stream();
2517            let pipe = create_fuchsia_pipe(locked, &current_task, client, OpenFlags::RDWR)
2518                .expect("create_fuchsia_pipe");
2519            let server_zxio = Zxio::create(server.into_handle()).expect("Zxio::create");
2520
2521            assert_eq!(
2522                pipe.query_events(locked, &current_task),
2523                Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
2524            );
2525
2526            let epoll_object = EpollFileObject::new_file(locked, &current_task);
2527            let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
2528            let event = EpollEvent::new(FdEvents::POLLIN, 0);
2529            epoll_file
2530                .add(locked, &current_task, &pipe, &epoll_object, event)
2531                .expect("poll_file.add");
2532
2533            let fds = epoll_file
2534                .wait(locked, &current_task, 1, zx::MonotonicInstant::ZERO)
2535                .expect("wait");
2536            assert!(fds.is_empty());
2537
2538            assert_eq!(server_zxio.write(&[0]).expect("write"), 1);
2539
2540            assert_eq!(
2541                pipe.query_events(locked, &current_task),
2542                Ok(FdEvents::POLLOUT
2543                    | FdEvents::POLLWRNORM
2544                    | FdEvents::POLLIN
2545                    | FdEvents::POLLRDNORM)
2546            );
2547            let fds = epoll_file
2548                .wait(locked, &current_task, 1, zx::MonotonicInstant::ZERO)
2549                .expect("wait");
2550            assert_eq!(fds.len(), 1);
2551
2552            assert_eq!(
2553                pipe.read(locked, &current_task, &mut VecOutputBuffer::new(64)).expect("read"),
2554                1
2555            );
2556
2557            assert_eq!(
2558                pipe.query_events(locked, &current_task),
2559                Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
2560            );
2561            let fds = epoll_file
2562                .wait(locked, &current_task, 1, zx::MonotonicInstant::ZERO)
2563                .expect("wait");
2564            assert!(fds.is_empty());
2565        })
2566        .await;
2567    }
2568
2569    #[::fuchsia::test]
2570    async fn test_new_remote_directory() {
2571        spawn_kernel_and_run(async |locked, current_task| {
2572            let (server, client) = zx::Channel::create();
2573            fdio::open("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE, server)
2574                .expect("failed to open /pkg");
2575
2576            let fd = new_remote_file(locked, &current_task, client.into(), OpenFlags::RDWR)
2577                .expect("new_remote_file");
2578            assert!(fd.node().is_dir());
2579            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
2580        })
2581        .await;
2582    }
2583
2584    #[::fuchsia::test]
2585    async fn test_new_remote_file() {
2586        spawn_kernel_and_run(async |locked, current_task| {
2587            let (server, client) = zx::Channel::create();
2588            fdio::open("/pkg/meta/contents", fio::PERM_READABLE, server)
2589                .expect("failed to open /pkg/meta/contents");
2590
2591            let fd = new_remote_file(locked, &current_task, client.into(), OpenFlags::RDONLY)
2592                .expect("new_remote_file");
2593            assert!(!fd.node().is_dir());
2594            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
2595        })
2596        .await;
2597    }
2598
2599    #[::fuchsia::test]
2600    async fn test_new_remote_counter() {
2601        spawn_kernel_and_run(async |locked, current_task| {
2602            let counter = zx::Counter::create();
2603
2604            let fd = new_remote_file(locked, &current_task, counter.into(), OpenFlags::RDONLY)
2605                .expect("new_remote_file");
2606            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
2607        })
2608        .await;
2609    }
2610
2611    #[::fuchsia::test]
2612    async fn test_new_remote_vmo() {
2613        spawn_kernel_and_run(async |locked, current_task| {
2614            let vmo = zx::Vmo::create(*PAGE_SIZE).expect("Vmo::create");
2615            let fd = new_remote_file(locked, &current_task, vmo.into(), OpenFlags::RDWR)
2616                .expect("new_remote_file");
2617            assert!(!fd.node().is_dir());
2618            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
2619        })
2620        .await;
2621    }
2622
2623    #[::fuchsia::test(threads = 2)]
2624    async fn test_symlink() {
2625        let fixture = TestFixture::new().await;
2626        let (server, client) = zx::Channel::create();
2627        fixture.root().clone(server.into()).expect("clone failed");
2628
2629        const LINK_PATH: &'static str = "symlink";
2630        const LINK_TARGET: &'static str = "私は「UTF8」です";
2631        // We expect the reported size of the symlink to be the length of the target, in bytes,
2632        // *without* a null terminator. Most Linux systems assume UTF-8 encoding.
2633        const LINK_SIZE: usize = 22;
2634        assert_eq!(LINK_SIZE, LINK_TARGET.len());
2635
2636        spawn_kernel_and_run(async move |locked, current_task| {
2637            let kernel = current_task.kernel();
2638            let fs = RemoteFs::new_fs(
2639                locked,
2640                &kernel,
2641                client,
2642                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2643                fio::PERM_READABLE | fio::PERM_WRITABLE,
2644            )
2645            .expect("new_fs failed");
2646            let ns = Namespace::new(fs);
2647            let root = ns.root();
2648            let symlink_node = root
2649                .create_symlink(locked, &current_task, LINK_PATH.into(), LINK_TARGET.into())
2650                .expect("symlink failed");
2651            assert_matches!(&*symlink_node.entry.node.info(), FsNodeInfo { size: LINK_SIZE, .. });
2652
2653            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2654            let child = root
2655                .lookup_child(locked, &current_task, &mut context, "symlink".into())
2656                .expect("lookup_child failed");
2657
2658            match child.readlink(locked, &current_task).expect("readlink failed") {
2659                SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2660                SymlinkTarget::Node(_) => panic!("readlink returned SymlinkTarget::Node"),
2661            }
2662            // Ensure the size stat reports matches what is expected.
2663            let stat_result = child.entry.node.stat(locked, &current_task).expect("stat failed");
2664            assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2665        })
2666        .await;
2667
2668        // Simulate a second run to ensure the symlink was persisted correctly.
2669        let fixture = TestFixture::open(
2670            fixture.close().await,
2671            TestFixtureOptions { format: false, ..Default::default() },
2672        )
2673        .await;
2674        let (server, client) = zx::Channel::create();
2675        fixture.root().clone(server.into()).expect("clone failed after remount");
2676
2677        spawn_kernel_and_run(async move |locked, current_task| {
2678            let kernel = current_task.kernel();
2679            let fs = RemoteFs::new_fs(
2680                locked,
2681                &kernel,
2682                client,
2683                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2684                fio::PERM_READABLE | fio::PERM_WRITABLE,
2685            )
2686            .expect("new_fs failed after remount");
2687            let ns = Namespace::new(fs);
2688            let root = ns.root();
2689            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2690            let child = root
2691                .lookup_child(locked, &current_task, &mut context, "symlink".into())
2692                .expect("lookup_child failed after remount");
2693
2694            match child.readlink(locked, &current_task).expect("readlink failed after remount") {
2695                SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2696                SymlinkTarget::Node(_) => {
2697                    panic!("readlink returned SymlinkTarget::Node after remount")
2698                }
2699            }
2700            // Ensure the size stat reports matches what is expected.
2701            let stat_result =
2702                child.entry.node.stat(locked, &current_task).expect("stat failed after remount");
2703            assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2704        })
2705        .await;
2706
2707        fixture.close().await;
2708    }
2709
2710    #[::fuchsia::test]
2711    async fn test_mode_uid_gid_and_dev_persists() {
2712        const FILE_MODE: FileMode = mode!(IFREG, 0o467);
2713        const DIR_MODE: FileMode = mode!(IFDIR, 0o647);
2714        const BLK_MODE: FileMode = mode!(IFBLK, 0o746);
2715
2716        let fixture = TestFixture::new().await;
2717        let (server, client) = zx::Channel::create();
2718        fixture.root().clone(server.into()).expect("clone failed");
2719
2720        // Simulate a first run of starnix.
2721        spawn_kernel_and_run(async move |locked, current_task| {
2722            let kernel = current_task.kernel();
2723            let creds = Credentials::clone(&current_task.current_creds());
2724            current_task.set_creds(Credentials { euid: 1, fsuid: 1, egid: 2, fsgid: 2, ..creds });
2725            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2726            let fs = RemoteFs::new_fs(
2727                locked,
2728                &kernel,
2729                client,
2730                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2731                rights,
2732            )
2733            .expect("new_fs failed");
2734            let ns = Namespace::new(fs);
2735            current_task.fs().set_umask(FileMode::from_bits(0));
2736            ns.root()
2737                .create_node(locked, &current_task, "file".into(), FILE_MODE, DeviceId::NONE)
2738                .expect("create_node failed");
2739            ns.root()
2740                .create_node(locked, &current_task, "dir".into(), DIR_MODE, DeviceId::NONE)
2741                .expect("create_node failed");
2742            ns.root()
2743                .create_node(locked, &current_task, "dev".into(), BLK_MODE, DeviceId::RANDOM)
2744                .expect("create_node failed");
2745        })
2746        .await;
2747
2748        // Simulate a second run.
2749        let fixture = TestFixture::open(
2750            fixture.close().await,
2751            TestFixtureOptions { format: false, ..Default::default() },
2752        )
2753        .await;
2754
2755        let (server, client) = zx::Channel::create();
2756        fixture.root().clone(server.into()).expect("clone failed");
2757
2758        spawn_kernel_and_run(async move |locked, current_task| {
2759            let kernel = current_task.kernel();
2760            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2761            let fs = RemoteFs::new_fs(
2762                locked,
2763                &kernel,
2764                client,
2765                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2766                rights,
2767            )
2768            .expect("new_fs failed");
2769            let ns = Namespace::new(fs);
2770            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2771            let child = ns
2772                .root()
2773                .lookup_child(locked, &current_task, &mut context, "file".into())
2774                .expect("lookup_child failed");
2775            assert_matches!(
2776                &*child.entry.node.info(),
2777                FsNodeInfo { mode: FILE_MODE, uid: 1, gid: 2, rdev: DeviceId::NONE, .. }
2778            );
2779            let child = ns
2780                .root()
2781                .lookup_child(locked, &current_task, &mut context, "dir".into())
2782                .expect("lookup_child failed");
2783            assert_matches!(
2784                &*child.entry.node.info(),
2785                FsNodeInfo { mode: DIR_MODE, uid: 1, gid: 2, rdev: DeviceId::NONE, .. }
2786            );
2787            let child = ns
2788                .root()
2789                .lookup_child(locked, &current_task, &mut context, "dev".into())
2790                .expect("lookup_child failed");
2791            assert_matches!(
2792                &*child.entry.node.info(),
2793                FsNodeInfo { mode: BLK_MODE, uid: 1, gid: 2, rdev: DeviceId::RANDOM, .. }
2794            );
2795        })
2796        .await;
2797        fixture.close().await;
2798    }
2799
2800    #[::fuchsia::test]
2801    async fn test_dot_dot_inode_numbers() {
2802        let fixture = TestFixture::new().await;
2803        let (server, client) = zx::Channel::create();
2804        fixture.root().clone(server.into()).expect("clone failed");
2805
2806        const MODE: FileMode = FileMode::from_bits(FileMode::IFDIR.bits() | 0o777);
2807
2808        spawn_kernel_and_run(async |locked, current_task| {
2809            let kernel = current_task.kernel();
2810            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2811            let fs = RemoteFs::new_fs(
2812                locked,
2813                &kernel,
2814                client,
2815                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2816                rights,
2817            )
2818            .expect("new_fs failed");
2819            let ns = Namespace::new(fs);
2820            current_task.fs().set_umask(FileMode::from_bits(0));
2821            let sub_dir1 = ns
2822                .root()
2823                .create_node(locked, &current_task, "dir".into(), MODE, DeviceId::NONE)
2824                .expect("create_node failed");
2825            let sub_dir2 = sub_dir1
2826                .create_node(locked, &current_task, "dir".into(), MODE, DeviceId::NONE)
2827                .expect("create_node failed");
2828
2829            let dir_handle = ns
2830                .root()
2831                .entry
2832                .open_anonymous(locked, &current_task, OpenFlags::RDONLY)
2833                .expect("open failed");
2834
2835            #[derive(Default)]
2836            struct Sink {
2837                offset: off_t,
2838                dot_dot_inode_num: u64,
2839            }
2840            impl DirentSink for Sink {
2841                fn add(
2842                    &mut self,
2843                    inode_num: ino_t,
2844                    offset: off_t,
2845                    entry_type: DirectoryEntryType,
2846                    name: &FsStr,
2847                ) -> Result<(), Errno> {
2848                    if name == ".." {
2849                        self.dot_dot_inode_num = inode_num;
2850                        assert_eq!(entry_type, DirectoryEntryType::DIR);
2851                    }
2852                    self.offset = offset;
2853                    Ok(())
2854                }
2855                fn offset(&self) -> off_t {
2856                    self.offset
2857                }
2858            }
2859            let mut sink = Sink::default();
2860            dir_handle.readdir(locked, &current_task, &mut sink).expect("readdir failed");
2861
2862            // inode_num for .. for the root should be the same as root.
2863            assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2864
2865            let dir_handle = sub_dir1
2866                .entry
2867                .open_anonymous(locked, &current_task, OpenFlags::RDONLY)
2868                .expect("open failed");
2869            let mut sink = Sink::default();
2870            dir_handle.readdir(locked, &current_task, &mut sink).expect("readdir failed");
2871
2872            // inode_num for .. for the first sub directory should be the same as root.
2873            assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2874
2875            let dir_handle = sub_dir2
2876                .entry
2877                .open_anonymous(locked, &current_task, OpenFlags::RDONLY)
2878                .expect("open failed");
2879            let mut sink = Sink::default();
2880            dir_handle.readdir(locked, &current_task, &mut sink).expect("readdir failed");
2881
2882            // inode_num for .. for the second subdir should be the first subdir.
2883            assert_eq!(sink.dot_dot_inode_num, sub_dir1.entry.node.ino);
2884        })
2885        .await;
2886        fixture.close().await;
2887    }
2888
2889    #[::fuchsia::test]
2890    async fn test_remote_special_node() {
2891        let fixture = TestFixture::new().await;
2892        let (server, client) = zx::Channel::create();
2893        fixture.root().clone(server.into()).expect("clone failed");
2894
2895        const FIFO_MODE: FileMode = FileMode::from_bits(FileMode::IFIFO.bits() | 0o777);
2896        const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2897
2898        spawn_kernel_and_run(async |locked, current_task| {
2899            let kernel = current_task.kernel();
2900            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2901            let fs = RemoteFs::new_fs(
2902                locked,
2903                &kernel,
2904                client,
2905                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2906                rights,
2907            )
2908            .expect("new_fs failed");
2909            let ns = Namespace::new(fs);
2910            current_task.fs().set_umask(FileMode::from_bits(0));
2911            let root = ns.root();
2912
2913            // Create RemoteSpecialNode (e.g. FIFO)
2914            root.create_node(locked, &current_task, "fifo".into(), FIFO_MODE, DeviceId::NONE)
2915                .expect("create_node failed");
2916            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2917            let fifo_node = root
2918                .lookup_child(locked, &current_task, &mut context, "fifo".into())
2919                .expect("lookup_child failed");
2920
2921            // Test that we get expected behaviour for RemoteSpecialNode operation, e.g.
2922            // test that truncate should return EINVAL
2923            match fifo_node.truncate(locked, &current_task, 0) {
2924                Ok(_) => {
2925                    panic!("truncate passed for special node")
2926                }
2927                Err(errno) if errno == EINVAL => {}
2928                Err(e) => {
2929                    panic!("truncate failed with error {:?}", e)
2930                }
2931            };
2932
2933            // Create regular RemoteNode
2934            root.create_node(locked, &current_task, "file".into(), REG_MODE, DeviceId::NONE)
2935                .expect("create_node failed");
2936            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2937            let reg_node = root
2938                .lookup_child(locked, &current_task, &mut context, "file".into())
2939                .expect("lookup_child failed");
2940
2941            // We should be able to perform truncate on regular files
2942            reg_node.truncate(locked, &current_task, 0).expect("truncate failed");
2943        })
2944        .await;
2945        fixture.close().await;
2946    }
2947
2948    #[::fuchsia::test]
2949    async fn test_hard_link() {
2950        let fixture = TestFixture::new().await;
2951        let (server, client) = zx::Channel::create();
2952        fixture.root().clone(server.into()).expect("clone failed");
2953
2954        spawn_kernel_and_run(async move |locked, current_task| {
2955            let kernel = current_task.kernel();
2956            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2957            let fs = RemoteFs::new_fs(
2958                locked,
2959                &kernel,
2960                client,
2961                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2962                rights,
2963            )
2964            .expect("new_fs failed");
2965            let ns = Namespace::new(fs);
2966            current_task.fs().set_umask(FileMode::from_bits(0));
2967            let node = ns
2968                .root()
2969                .create_node(
2970                    locked,
2971                    &current_task,
2972                    "file1".into(),
2973                    mode!(IFREG, 0o666),
2974                    DeviceId::NONE,
2975                )
2976                .expect("create_node failed");
2977            ns.root()
2978                .entry
2979                .node
2980                .link(locked, &current_task, &ns.root().mount, "file2".into(), &node.entry.node)
2981                .expect("link failed");
2982        })
2983        .await;
2984
2985        let fixture = TestFixture::open(
2986            fixture.close().await,
2987            TestFixtureOptions { format: false, ..Default::default() },
2988        )
2989        .await;
2990
2991        let (server, client) = zx::Channel::create();
2992        fixture.root().clone(server.into()).expect("clone failed");
2993
2994        spawn_kernel_and_run(async move |locked, current_task| {
2995            let kernel = current_task.kernel();
2996            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2997            let fs = RemoteFs::new_fs(
2998                locked,
2999                &kernel,
3000                client,
3001                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3002                rights,
3003            )
3004            .expect("new_fs failed");
3005            let ns = Namespace::new(fs);
3006            let mut context = LookupContext::new(SymlinkMode::NoFollow);
3007            let child1 = ns
3008                .root()
3009                .lookup_child(locked, &current_task, &mut context, "file1".into())
3010                .expect("lookup_child failed");
3011            let child2 = ns
3012                .root()
3013                .lookup_child(locked, &current_task, &mut context, "file2".into())
3014                .expect("lookup_child failed");
3015            assert!(Arc::ptr_eq(&child1.entry.node, &child2.entry.node));
3016        })
3017        .await;
3018        fixture.close().await;
3019    }
3020
3021    #[::fuchsia::test]
3022    async fn test_lookup_on_fsverity_enabled_file() {
3023        let fixture = TestFixture::new().await;
3024        let (server, client) = zx::Channel::create();
3025        fixture.root().clone(server.into()).expect("clone failed");
3026
3027        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3028
3029        spawn_kernel_and_run(async move |locked, current_task| {
3030            let kernel = current_task.kernel();
3031            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3032            let fs = RemoteFs::new_fs(
3033                locked,
3034                &kernel,
3035                client,
3036                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3037                rights,
3038            )
3039            .expect("new_fs failed");
3040            let ns = Namespace::new(fs);
3041            current_task.fs().set_umask(FileMode::from_bits(0));
3042            let file = ns
3043                .root()
3044                .create_node(locked, &current_task, "file".into(), MODE, DeviceId::NONE)
3045                .expect("create_node failed");
3046            // Enable verity on the file.
3047            let desc = fsverity_descriptor {
3048                version: 1,
3049                hash_algorithm: 1,
3050                salt_size: 32,
3051                log_blocksize: 12,
3052                ..Default::default()
3053            };
3054            file.entry
3055                .node
3056                .enable_fsverity(locked, current_task, &desc)
3057                .expect("enable fsverity failed");
3058        })
3059        .await;
3060
3061        // Tear down the kernel and open the file again. The file should no longer be cached.
3062        // Test that lookup works as expected for an fsverity-enabled file.
3063        let fixture = TestFixture::open(
3064            fixture.close().await,
3065            TestFixtureOptions { format: false, ..Default::default() },
3066        )
3067        .await;
3068        let (server, client) = zx::Channel::create();
3069        fixture.root().clone(server.into()).expect("clone failed");
3070
3071        spawn_kernel_and_run(async move |locked, current_task| {
3072            let kernel = current_task.kernel();
3073            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3074            let fs = RemoteFs::new_fs(
3075                locked,
3076                &kernel,
3077                client,
3078                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3079                rights,
3080            )
3081            .expect("new_fs failed");
3082            let ns = Namespace::new(fs);
3083            let mut context = LookupContext::new(SymlinkMode::NoFollow);
3084            let _child = ns
3085                .root()
3086                .lookup_child(locked, &current_task, &mut context, "file".into())
3087                .expect("lookup_child failed");
3088        })
3089        .await;
3090        fixture.close().await;
3091    }
3092
3093    #[::fuchsia::test]
3094    async fn test_update_attributes_persists() {
3095        let fixture = TestFixture::new().await;
3096        let (server, client) = zx::Channel::create();
3097        fixture.root().clone(server.into()).expect("clone failed");
3098
3099        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3100
3101        spawn_kernel_and_run(async move |locked, current_task| {
3102            let kernel = current_task.kernel();
3103            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3104            let fs = RemoteFs::new_fs(
3105                locked,
3106                &kernel,
3107                client,
3108                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3109                rights,
3110            )
3111            .expect("new_fs failed");
3112            let ns = Namespace::new(fs);
3113            current_task.fs().set_umask(FileMode::from_bits(0));
3114            let file = ns
3115                .root()
3116                .create_node(locked, &current_task, "file".into(), MODE, DeviceId::NONE)
3117                .expect("create_node failed");
3118            // Change the mode, this change should persist
3119            file.entry
3120                .node
3121                .chmod(locked, &current_task, &file.mount, MODE | FileMode::ALLOW_ALL)
3122                .expect("chmod failed");
3123        })
3124        .await;
3125
3126        // Tear down the kernel and open the file again. Check that changes persisted.
3127        let fixture = TestFixture::open(
3128            fixture.close().await,
3129            TestFixtureOptions { format: false, ..Default::default() },
3130        )
3131        .await;
3132        let (server, client) = zx::Channel::create();
3133        fixture.root().clone(server.into()).expect("clone failed");
3134
3135        spawn_kernel_and_run(async move |locked, current_task| {
3136            let kernel = current_task.kernel();
3137            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3138            let fs = RemoteFs::new_fs(
3139                locked,
3140                &kernel,
3141                client,
3142                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3143                rights,
3144            )
3145            .expect("new_fs failed");
3146            let ns = Namespace::new(fs);
3147            let mut context = LookupContext::new(SymlinkMode::NoFollow);
3148            let child = ns
3149                .root()
3150                .lookup_child(locked, &current_task, &mut context, "file".into())
3151                .expect("lookup_child failed");
3152            assert_eq!(child.entry.node.info().mode, MODE | FileMode::ALLOW_ALL);
3153        })
3154        .await;
3155        fixture.close().await;
3156    }
3157
3158    #[::fuchsia::test]
3159    async fn test_statfs() {
3160        let fixture = TestFixture::new().await;
3161        let (server, client) = zx::Channel::create();
3162        fixture.root().clone(server.into()).expect("clone failed");
3163
3164        spawn_kernel_and_run(async move |locked, current_task| {
3165            let kernel = current_task.kernel();
3166            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3167            let fs = RemoteFs::new_fs(
3168                locked,
3169                &kernel,
3170                client,
3171                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3172                rights,
3173            )
3174            .expect("new_fs failed");
3175
3176            let statfs = fs.statfs(locked, &current_task).expect("statfs failed");
3177            assert!(statfs.f_type != 0);
3178            assert!(statfs.f_bsize > 0);
3179            assert!(statfs.f_blocks > 0);
3180            assert!(statfs.f_bfree > 0 && statfs.f_bfree <= statfs.f_blocks);
3181            assert!(statfs.f_files > 0);
3182            assert!(statfs.f_ffree > 0 && statfs.f_ffree <= statfs.f_files);
3183            assert!(statfs.f_fsid.val[0] != 0 || statfs.f_fsid.val[1] != 0);
3184            assert!(statfs.f_namelen > 0);
3185            assert!(statfs.f_frsize > 0);
3186        })
3187        .await;
3188
3189        fixture.close().await;
3190    }
3191
3192    #[::fuchsia::test]
3193    async fn test_allocate() {
3194        let fixture = TestFixture::new().await;
3195        let (server, client) = zx::Channel::create();
3196        fixture.root().clone(server.into()).expect("clone failed");
3197
3198        spawn_kernel_and_run(async move |locked, current_task| {
3199            let kernel = current_task.kernel();
3200            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3201            let fs = RemoteFs::new_fs(
3202                locked,
3203                &kernel,
3204                client,
3205                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3206                rights,
3207            )
3208            .expect("new_fs failed");
3209            let ns = Namespace::new(fs);
3210            current_task.fs().set_umask(FileMode::from_bits(0));
3211            let root = ns.root();
3212
3213            const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
3214            root.create_node(locked, &current_task, "file".into(), REG_MODE, DeviceId::NONE)
3215                .expect("create_node failed");
3216            let mut context = LookupContext::new(SymlinkMode::NoFollow);
3217            let reg_node = root
3218                .lookup_child(locked, &current_task, &mut context, "file".into())
3219                .expect("lookup_child failed");
3220
3221            reg_node
3222                .entry
3223                .node
3224                .fallocate(locked, &current_task, FallocMode::Allocate { keep_size: false }, 0, 20)
3225                .expect("truncate failed");
3226        })
3227        .await;
3228        fixture.close().await;
3229    }
3230
3231    #[::fuchsia::test]
3232    async fn test_allocate_overflow() {
3233        let fixture = TestFixture::new().await;
3234        let (server, client) = zx::Channel::create();
3235        fixture.root().clone(server.into()).expect("clone failed");
3236
3237        spawn_kernel_and_run(async move |locked, current_task| {
3238            let kernel = current_task.kernel();
3239            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3240            let fs = RemoteFs::new_fs(
3241                locked,
3242                &kernel,
3243                client,
3244                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3245                rights,
3246            )
3247            .expect("new_fs failed");
3248            let ns = Namespace::new(fs);
3249            current_task.fs().set_umask(FileMode::from_bits(0));
3250            let root = ns.root();
3251
3252            const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
3253            root.create_node(locked, &current_task, "file".into(), REG_MODE, DeviceId::NONE)
3254                .expect("create_node failed");
3255            let mut context = LookupContext::new(SymlinkMode::NoFollow);
3256            let reg_node = root
3257                .lookup_child(locked, &current_task, &mut context, "file".into())
3258                .expect("lookup_child failed");
3259
3260            reg_node
3261                .entry
3262                .node
3263                .fallocate(
3264                    locked,
3265                    &current_task,
3266                    FallocMode::Allocate { keep_size: false },
3267                    1,
3268                    u64::MAX,
3269                )
3270                .expect_err("truncate unexpectedly passed");
3271        })
3272        .await;
3273        fixture.close().await;
3274    }
3275
3276    #[::fuchsia::test]
3277    async fn test_time_modify_persists() {
3278        let fixture = TestFixture::new().await;
3279        let (server, client) = zx::Channel::create();
3280        fixture.root().clone(server.into()).expect("clone failed");
3281
3282        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3283
3284        let last_modified = spawn_kernel_and_run(async move |locked, current_task| {
3285            let kernel = current_task.kernel();
3286            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3287            let fs = RemoteFs::new_fs(
3288                locked,
3289                &kernel,
3290                client,
3291                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3292                rights,
3293            )
3294            .expect("new_fs failed");
3295            let ns: Arc<Namespace> = Namespace::new(fs);
3296            current_task.fs().set_umask(FileMode::from_bits(0));
3297            let child = ns
3298                .root()
3299                .create_node(locked, &current_task, "file".into(), MODE, DeviceId::NONE)
3300                .expect("create_node failed");
3301            // Write to file (this should update mtime (time_modify))
3302            let file = child
3303                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
3304                .expect("open failed");
3305            // Call `fetch_and_refresh_info(..)` to refresh `time_modify` with the time managed by the
3306            // underlying filesystem
3307            let time_before_write = child
3308                .entry
3309                .node
3310                .fetch_and_refresh_info(locked, &current_task)
3311                .expect("fetch_and_refresh_info failed")
3312                .time_modify;
3313            let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3314            let written = file
3315                .write(locked, &current_task, &mut VecInputBuffer::new(&write_bytes))
3316                .expect("write failed");
3317            assert_eq!(written, write_bytes.len());
3318            let last_modified = child
3319                .entry
3320                .node
3321                .fetch_and_refresh_info(locked, &current_task)
3322                .expect("fetch_and_refresh_info failed")
3323                .time_modify;
3324            assert!(last_modified > time_before_write);
3325            last_modified
3326        })
3327        .await;
3328
3329        // Tear down the kernel and open the file again. Check that modification time is when we
3330        // last modified the contents of the file
3331        let fixture = TestFixture::open(
3332            fixture.close().await,
3333            TestFixtureOptions { format: false, ..Default::default() },
3334        )
3335        .await;
3336        let (server, client) = zx::Channel::create();
3337        fixture.root().clone(server.into()).expect("clone failed");
3338        let refreshed_modified_time = spawn_kernel_and_run(async move |locked, current_task| {
3339            let kernel = current_task.kernel();
3340            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3341            let fs = RemoteFs::new_fs(
3342                locked,
3343                &kernel,
3344                client,
3345                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3346                rights,
3347            )
3348            .expect("new_fs failed");
3349            let ns = Namespace::new(fs);
3350            let mut context = LookupContext::new(SymlinkMode::NoFollow);
3351            let child = ns
3352                .root()
3353                .lookup_child(locked, &current_task, &mut context, "file".into())
3354                .expect("lookup_child failed");
3355            let last_modified = child
3356                .entry
3357                .node
3358                .fetch_and_refresh_info(locked, &current_task)
3359                .expect("fetch_and_refresh_info failed")
3360                .time_modify;
3361            last_modified
3362        })
3363        .await;
3364        assert_eq!(last_modified, refreshed_modified_time);
3365
3366        fixture.close().await;
3367    }
3368
3369    #[::fuchsia::test]
3370    async fn test_update_atime_mtime() {
3371        let fixture = TestFixture::new().await;
3372        let (server, client) = zx::Channel::create();
3373        fixture.root().clone(server.into()).expect("clone failed");
3374
3375        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3376
3377        spawn_kernel_and_run(async move |locked, current_task| {
3378            let kernel = current_task.kernel();
3379            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3380            let fs = RemoteFs::new_fs(
3381                locked,
3382                &kernel,
3383                client,
3384                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3385                rights,
3386            )
3387            .expect("new_fs failed");
3388            let ns: Arc<Namespace> = Namespace::new(fs);
3389            current_task.fs().set_umask(FileMode::from_bits(0));
3390            let child = ns
3391                .root()
3392                .create_node(locked, &current_task, "file".into(), MODE, DeviceId::NONE)
3393                .expect("create_node failed");
3394
3395            let info_original = child
3396                .entry
3397                .node
3398                .fetch_and_refresh_info(locked, &current_task)
3399                .expect("fetch_and_refresh_info failed")
3400                .clone();
3401
3402            child
3403                .entry
3404                .node
3405                .update_atime_mtime(
3406                    locked,
3407                    &current_task,
3408                    &child.mount,
3409                    TimeUpdateType::Time(UtcInstant::from_nanos(30)),
3410                    TimeUpdateType::Omit,
3411                )
3412                .expect("update_atime_mtime failed");
3413            let info_after_update = child
3414                .entry
3415                .node
3416                .fetch_and_refresh_info(locked, &current_task)
3417                .expect("fetch_and_refresh_info failed")
3418                .clone();
3419            assert_eq!(info_after_update.time_modify, info_original.time_modify);
3420            assert_eq!(info_after_update.time_access, UtcInstant::from_nanos(30));
3421
3422            child
3423                .entry
3424                .node
3425                .update_atime_mtime(
3426                    locked,
3427                    &current_task,
3428                    &child.mount,
3429                    TimeUpdateType::Omit,
3430                    TimeUpdateType::Time(UtcInstant::from_nanos(50)),
3431                )
3432                .expect("update_atime_mtime failed");
3433            let info_after_update2 = child
3434                .entry
3435                .node
3436                .fetch_and_refresh_info(locked, &current_task)
3437                .expect("fetch_and_refresh_info failed")
3438                .clone();
3439            assert_eq!(info_after_update2.time_modify, UtcInstant::from_nanos(50));
3440            assert_eq!(info_after_update2.time_access, UtcInstant::from_nanos(30));
3441        })
3442        .await;
3443        fixture.close().await;
3444    }
3445
3446    #[::fuchsia::test]
3447    async fn test_write_updates_mtime_ctime() {
3448        let fixture = TestFixture::new().await;
3449        let (server, client) = zx::Channel::create();
3450        fixture.root().clone(server.into()).expect("clone failed");
3451
3452        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3453
3454        spawn_kernel_and_run(async move |locked, current_task| {
3455            let kernel = current_task.kernel();
3456            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3457            let fs = RemoteFs::new_fs(
3458                locked,
3459                &kernel,
3460                client,
3461                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3462                rights,
3463            )
3464            .expect("new_fs failed");
3465            let ns: Arc<Namespace> = Namespace::new(fs);
3466            current_task.fs().set_umask(FileMode::from_bits(0));
3467            let child = ns
3468                .root()
3469                .create_node(locked, &current_task, "file".into(), MODE, DeviceId::NONE)
3470                .expect("create_node failed");
3471            let file = child
3472                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
3473                .expect("open failed");
3474            // Call `fetch_and_refresh_info(..)` to refresh ctime and mtime with the time managed by the
3475            // underlying filesystem
3476            let (ctime_before_write, mtime_before_write) = {
3477                let info = child
3478                    .entry
3479                    .node
3480                    .fetch_and_refresh_info(locked, &current_task)
3481                    .expect("fetch_and_refresh_info failed");
3482                (info.time_status_change, info.time_modify)
3483            };
3484
3485            // Writing to a file should update ctime and mtime
3486            let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3487            let written = file
3488                .write(locked, &current_task, &mut VecInputBuffer::new(&write_bytes))
3489                .expect("write failed");
3490            assert_eq!(written, write_bytes.len());
3491
3492            // As Fxfs, the underlying filesystem in this test, can manage file timestamps,
3493            // we should not see an update in mtime and ctime without first refreshing the node with
3494            // the metadata from Fxfs.
3495            let (ctime_after_write_no_refresh, mtime_after_write_no_refresh) = {
3496                let info = child.entry.node.info();
3497                (info.time_status_change, info.time_modify)
3498            };
3499            assert_eq!(ctime_after_write_no_refresh, ctime_before_write);
3500            assert_eq!(mtime_after_write_no_refresh, mtime_before_write);
3501
3502            // Refresh information, we should see `info` with mtime and ctime from the remote
3503            // filesystem (assume this is true if the new timestamp values are greater than the ones
3504            // without the refresh).
3505            let (ctime_after_write_refresh, mtime_after_write_refresh) = {
3506                let info = child
3507                    .entry
3508                    .node
3509                    .fetch_and_refresh_info(locked, &current_task)
3510                    .expect("fetch_and_refresh_info failed");
3511                (info.time_status_change, info.time_modify)
3512            };
3513            assert_eq!(ctime_after_write_refresh, mtime_after_write_refresh);
3514            assert!(ctime_after_write_refresh > ctime_after_write_no_refresh);
3515        })
3516        .await;
3517        fixture.close().await;
3518    }
3519
3520    #[::fuchsia::test]
3521    async fn test_casefold_persists() {
3522        let fixture = TestFixture::new().await;
3523        let (server, client) = zx::Channel::create();
3524        fixture.root().clone(server.into()).expect("clone failed");
3525
3526        spawn_kernel_and_run(async move |locked, current_task| {
3527            let kernel = current_task.kernel();
3528            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3529            let fs = RemoteFs::new_fs(
3530                locked,
3531                &kernel,
3532                client,
3533                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3534                rights,
3535            )
3536            .expect("new_fs failed");
3537            let ns: Arc<Namespace> = Namespace::new(fs);
3538            let child = ns
3539                .root()
3540                .create_node(
3541                    locked,
3542                    &current_task,
3543                    "dir".into(),
3544                    FileMode::ALLOW_ALL.with_type(FileMode::IFDIR),
3545                    DeviceId::NONE,
3546                )
3547                .expect("create_node failed");
3548            child
3549                .entry
3550                .node
3551                .update_attributes(locked, &current_task, |info| {
3552                    info.casefold = true;
3553                    Ok(())
3554                })
3555                .expect("enable casefold")
3556        })
3557        .await;
3558
3559        // Tear down the kernel and open the dir again. Check that casefold is preserved.
3560        let fixture = TestFixture::open(
3561            fixture.close().await,
3562            TestFixtureOptions { format: false, ..Default::default() },
3563        )
3564        .await;
3565        let (server, client) = zx::Channel::create();
3566        fixture.root().clone(server.into()).expect("clone failed");
3567        let casefold = spawn_kernel_and_run(async move |locked, current_task| {
3568            let kernel = current_task.kernel();
3569            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3570            let fs = RemoteFs::new_fs(
3571                locked,
3572                &kernel,
3573                client,
3574                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3575                rights,
3576            )
3577            .expect("new_fs failed");
3578            let ns = Namespace::new(fs);
3579            let mut context = LookupContext::new(SymlinkMode::NoFollow);
3580            let child = ns
3581                .root()
3582                .lookup_child(locked, &current_task, &mut context, "dir".into())
3583                .expect("lookup_child failed");
3584            let casefold = child
3585                .entry
3586                .node
3587                .fetch_and_refresh_info(locked, &current_task)
3588                .expect("fetch_and_refresh_info failed")
3589                .casefold;
3590            casefold
3591        })
3592        .await;
3593        assert!(casefold);
3594
3595        fixture.close().await;
3596    }
3597
3598    #[::fuchsia::test]
3599    async fn test_pending_access_time() {
3600        const TEST_FILE: &str = "test_file";
3601
3602        let fixture = TestFixture::new().await;
3603        let (server, client) = zx::Channel::create();
3604        fixture.root().clone(server.into()).expect("clone failed");
3605        let (server, client2) = zx::Channel::create();
3606        fixture.root().clone(server.into()).expect("clone failed");
3607
3608        spawn_kernel_and_run(async move |locked, current_task| {
3609            let kernel = current_task.kernel.clone();
3610
3611            let atime3 = {
3612                let fs = RemoteFs::new_fs(
3613                    locked,
3614                    &kernel,
3615                    client,
3616                    FileSystemOptions {
3617                        source: FlyByteStr::new(b"/"),
3618                        flags: FileSystemFlags::empty().into(),
3619                        ..Default::default()
3620                    },
3621                    fio::PERM_READABLE | fio::PERM_WRITABLE,
3622                )
3623                .expect("new_fs failed");
3624
3625                let ns = Namespace::new_with_flags(fs, MountpointFlags::RELATIME);
3626                let child = ns
3627                    .root()
3628                    .open_create_node(
3629                        locked,
3630                        &current_task,
3631                        TEST_FILE.into(),
3632                        FileMode::ALLOW_ALL.with_type(FileMode::IFREG),
3633                        DeviceId::NONE,
3634                        OpenFlags::empty(),
3635                    )
3636                    .expect("create_node failed");
3637
3638                let atime1 = child.entry.node.info().time_access;
3639
3640                std::thread::sleep(std::time::Duration::from_micros(1));
3641
3642                let file_handle = child
3643                    .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
3644                    .expect("open failed");
3645
3646                file_handle
3647                    .read(locked, &current_task, &mut VecOutputBuffer::new(10))
3648                    .expect("read failed");
3649
3650                // Expect atime to have changed.
3651                let atime2 = child.entry.node.info().time_access;
3652                assert!(atime2 > atime1);
3653
3654                std::thread::sleep(std::time::Duration::from_micros(1));
3655
3656                file_handle
3657                    .read(locked, &current_task, &mut VecOutputBuffer::new(10))
3658                    .expect("read failed");
3659
3660                // And again.
3661                let atime3 = child.entry.node.info().time_access;
3662                assert!(atime3 > atime2);
3663
3664                atime3
3665            };
3666
3667            kernel.delayed_releaser.apply(locked.cast_locked(), current_task);
3668
3669            // After dropping the filesystem, the atime should have been persistently updated.
3670            let fs = RemoteFs::new_fs(
3671                locked,
3672                &kernel,
3673                client2,
3674                FileSystemOptions {
3675                    source: FlyByteStr::new(b"/"),
3676                    flags: FileSystemFlags::empty().into(),
3677                    ..Default::default()
3678                },
3679                fio::PERM_READABLE | fio::PERM_WRITABLE,
3680            )
3681            .expect("new_fs failed");
3682
3683            let ns = Namespace::new_with_flags(fs, MountpointFlags::RELATIME);
3684            let child = ns
3685                .root()
3686                .lookup_child(
3687                    locked,
3688                    &current_task,
3689                    &mut LookupContext::new(Default::default()),
3690                    TEST_FILE.into(),
3691                )
3692                .expect("lookup_child failed");
3693
3694            let atime4 = child.entry.node.info().time_access;
3695
3696            assert!(atime4 >= atime3);
3697        })
3698        .await;
3699
3700        fixture.close().await;
3701    }
3702
3703    #[::fuchsia::test]
3704    async fn test_read_chunking() {
3705        use futures::StreamExt;
3706        let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3707        let content = vec![0xAB; (fio::MAX_TRANSFER_SIZE + 100) as usize];
3708        let content_clone = content.clone();
3709
3710        let _server_task = fasync::Task::spawn(async move {
3711            while let Some(Ok(request)) = stream.next().await {
3712                match request {
3713                    fio::FileRequest::ReadAt { count, offset, responder } => {
3714                        let start = offset as usize;
3715                        let end = std::cmp::min(start + count as usize, content_clone.len());
3716                        let data = if start < content_clone.len() {
3717                            &content_clone[start..end]
3718                        } else {
3719                            &[]
3720                        };
3721                        responder.send(Ok(data)).unwrap();
3722                    }
3723                    _ => panic!("Unexpected request: {:?}", request),
3724                }
3725            }
3726        });
3727
3728        fasync::unblock(move || {
3729            let io = RemoteIo::new(client.into_channel().into());
3730            let mut buffer = VecOutputBuffer::new(content.len());
3731            assert_eq!(
3732                io.read_to_output_buffer(0, &mut buffer).expect("read_at failed"),
3733                content.len()
3734            );
3735            assert_eq!(buffer.data(), content.as_slice());
3736        })
3737        .await;
3738    }
3739
3740    #[::fuchsia::test]
3741    async fn test_write_chunking() {
3742        let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3743        let content = vec![0xCD; (fio::MAX_TRANSFER_SIZE + 100) as usize];
3744        let content2 = content.clone();
3745
3746        let server_task = fasync::Task::spawn(async move {
3747            let mut written = vec![0; content2.len()];
3748            while let Some(Ok(request)) = stream.next().await {
3749                match request {
3750                    fio::FileRequest::WriteAt { offset, data, responder, .. } => {
3751                        let offset = offset as usize;
3752                        written[offset..offset + data.len()].copy_from_slice(&data);
3753                        responder.send(Ok(data.len() as u64)).unwrap();
3754                    }
3755                    _ => panic!("Unexpected request: {:?}", request),
3756                }
3757            }
3758            assert_eq!(written, content2);
3759        });
3760
3761        fasync::unblock(move || {
3762            let io = RemoteIo::new(client.into_channel().into());
3763            let mut buffer = VecInputBuffer::new(&content);
3764            assert_eq!(
3765                io.write_from_input_buffer(0, &mut buffer).expect("write_at failed"),
3766                content.len()
3767            );
3768        })
3769        .await;
3770
3771        server_task.await;
3772    }
3773
3774    #[::fuchsia::test]
3775    async fn test_cached_attribute_refresh_behavior() {
3776        let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3777        let barrier = Arc::new(Barrier::new(2));
3778        let barrier_clone = barrier.clone();
3779        let get_attrs_count = Arc::new(AtomicU32::new(0));
3780        let get_attrs_count_clone = get_attrs_count.clone();
3781
3782        let server_task = fasync::Task::spawn(async move {
3783            while let Some(Ok(request)) = stream.next().await {
3784                match request {
3785                    fio::FileRequest::GetAttributes { query: _, responder } => {
3786                        get_attrs_count_clone.fetch_add(1, Ordering::SeqCst);
3787                        let mutable_attrs = fio::MutableNodeAttributes { ..Default::default() };
3788                        let immutable_attrs = fio::ImmutableNodeAttributes {
3789                            id: Some(1),
3790                            link_count: Some(1),
3791                            ..Default::default()
3792                        };
3793                        responder.send(Ok((&mutable_attrs, &immutable_attrs))).unwrap();
3794                    }
3795                    fio::FileRequest::Resize { length: _, responder } => {
3796                        let barrier_clone = barrier_clone.clone();
3797                        fasync::Task::spawn(async move {
3798                            barrier_clone.async_wait().await;
3799                            barrier_clone.async_wait().await;
3800                            responder.send(Ok(())).unwrap();
3801                        })
3802                        .detach();
3803                    }
3804                    fio::FileRequest::Close { responder } => {
3805                        responder.send(Ok(())).unwrap();
3806                    }
3807                    _ => panic!("Unexpected request: {:?}", request),
3808                }
3809            }
3810        });
3811
3812        fasync::unblock(move || {
3813            let io = RemoteIo::new(client.into_channel().into());
3814            let node = BaseNode::new(io, false);
3815            let info =
3816                DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
3817
3818            // 1. Initial fetch. Should return cached info immediately.
3819            assert_eq!(get_attrs_count.load(Ordering::SeqCst), 0);
3820            {
3821                let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3822            }
3823            assert_eq!(get_attrs_count.load(Ordering::SeqCst), 0);
3824
3825            // 2. Spawn a thread to perform a dirty operation.
3826            std::thread::scope(|s| {
3827                s.spawn(|| {
3828                    will_dirty(&[&node], || {
3829                        node.io.truncate(0).expect("truncate failed");
3830                    });
3831                });
3832
3833                // Wait for the operation to start.
3834                barrier.wait();
3835
3836                // Now the node is dirty. Fetching attributes should trigger a request.
3837                {
3838                    let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3839                }
3840                assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
3841
3842                // A second fetch should trigger another request.
3843                {
3844                    let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3845                }
3846                assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
3847
3848                // Let the operation finish.
3849                barrier.wait();
3850            });
3851
3852            // 3. Operation finished. The next fetch should trigger a request.
3853            {
3854                let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3855            }
3856            assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3857
3858            // 4. Subsequent fetch should return cached info.
3859            {
3860                let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3861            }
3862            assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3863        })
3864        .await;
3865
3866        server_task.await;
3867    }
3868
3869    #[::fuchsia::test]
3870    async fn test_attribute_refresh_during_concurrent_dirty_operation() {
3871        let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3872        let get_attrs_started = Arc::new(Barrier::new(2));
3873        let get_attrs_started_clone = get_attrs_started.clone();
3874        let finish_get_attrs = Arc::new(Barrier::new(2));
3875        let finish_get_attrs_clone = finish_get_attrs.clone();
3876
3877        let resize_started = Arc::new(Barrier::new(2));
3878        let resize_started_clone = resize_started.clone();
3879        let finish_resize = Arc::new(Barrier::new(2));
3880        let finish_resize_clone = finish_resize.clone();
3881
3882        let get_attrs_count = Arc::new(AtomicU32::new(0));
3883        let get_attrs_count_clone = get_attrs_count.clone();
3884
3885        let server_task = fasync::Task::spawn(async move {
3886            while let Some(Ok(request)) = stream.next().await {
3887                match request {
3888                    fio::FileRequest::GetAttributes { query: _, responder } => {
3889                        let count = get_attrs_count_clone.fetch_add(1, Ordering::SeqCst);
3890                        let finish_get_attrs_clone = finish_get_attrs_clone.clone();
3891                        let get_attrs_started_clone = get_attrs_started_clone.clone();
3892
3893                        fasync::Task::spawn(async move {
3894                            if count == 0 {
3895                                fasync::unblock(move || {
3896                                    get_attrs_started_clone.wait();
3897                                    finish_get_attrs_clone.wait();
3898                                })
3899                                .await;
3900                            }
3901                            let mutable_attrs = fio::MutableNodeAttributes { ..Default::default() };
3902                            let immutable_attrs = fio::ImmutableNodeAttributes {
3903                                id: Some(1),
3904                                link_count: Some(1),
3905                                ..Default::default()
3906                            };
3907                            responder.send(Ok((&mutable_attrs, &immutable_attrs))).unwrap();
3908                        })
3909                        .detach();
3910                    }
3911                    fio::FileRequest::Resize { length: _, responder } => {
3912                        let resize_started_clone = resize_started_clone.clone();
3913                        let finish_resize_clone = finish_resize_clone.clone();
3914                        fasync::Task::spawn(async move {
3915                            fasync::unblock(move || {
3916                                resize_started_clone.wait();
3917                                finish_resize_clone.wait();
3918                            })
3919                            .await;
3920                            responder.send(Ok(())).unwrap();
3921                        })
3922                        .detach();
3923                    }
3924                    fio::FileRequest::Close { responder } => {
3925                        responder.send(Ok(())).unwrap();
3926                    }
3927                    _ => panic!("Unexpected request: {:?}", request),
3928                }
3929            }
3930        });
3931
3932        fasync::unblock(move || {
3933            let io = RemoteIo::new(client.into_channel().into());
3934            let node = BaseNode::new(io, true);
3935            let info =
3936                DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
3937
3938            std::thread::scope(|s| {
3939                // 1. Start Refresh Thread
3940                let refresh_thread = s.spawn(|| {
3941                    let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3942                });
3943
3944                get_attrs_started.wait();
3945
3946                // 2. Start Dirty Thread
3947                let dirty_thread = s.spawn(|| {
3948                    will_dirty(&[&node], || {
3949                        node.io.truncate(0).expect("truncate failed");
3950                    });
3951                });
3952
3953                resize_started.wait();
3954
3955                // 3. Allow GetAttributes to finish
3956                finish_get_attrs.wait();
3957                refresh_thread.join().unwrap();
3958                assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
3959
3960                // 4. Refresh #2 (Should fetch because dirty op is in flight)
3961                {
3962                    let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3963                }
3964                assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
3965
3966                // 5. Allow Dirty Op to finish
3967                finish_resize.wait();
3968                dirty_thread.join().unwrap();
3969
3970                // 6. Refresh #3 (Should fetch because dirty op finished, but state was 0)
3971                {
3972                    let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3973                }
3974                assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3975
3976                // 7. Refresh #4 (Should be cached)
3977                {
3978                    let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3979                }
3980                assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3981            });
3982        })
3983        .await;
3984
3985        server_task.await;
3986    }
3987
3988    #[::fuchsia::test]
3989    async fn test_update_attributes_invalidates_cache() {
3990        let (client, mut stream) = create_request_stream::<fio::DirectoryMarker>();
3991        let get_attrs_count = Arc::new(AtomicU32::new(0));
3992        let get_attrs_count_clone = get_attrs_count.clone();
3993
3994        let server_task = fasync::Task::spawn(async move {
3995            let mut sub_tasks = Vec::new();
3996            while let Some(Ok(request)) = stream.next().await {
3997                match request {
3998                    fio::DirectoryRequest::Open { path, object, flags, .. } => {
3999                        assert_eq!(path, ".", "Unexpected open() for non-self");
4000                        let get_attrs_count = get_attrs_count_clone.clone();
4001                        sub_tasks.push(fasync::Task::spawn(async move {
4002                            let (mut stream, control_handle) =
4003                                ServerEnd::<fio::DirectoryMarker>::new(object)
4004                                    .into_stream_and_control_handle();
4005                            assert!(flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION));
4006
4007                            // The Representation provides the initial attributes to cache.
4008                            let mutable_attributes =
4009                                fio::MutableNodeAttributes { ..Default::default() };
4010                            let immutable_attributes = fio::ImmutableNodeAttributes {
4011                                id: Some(1),
4012                                link_count: Some(1),
4013                                ..Default::default()
4014                            };
4015                            let info = fio::DirectoryInfo {
4016                                attributes: Some(fio::NodeAttributes2 {
4017                                    mutable_attributes,
4018                                    immutable_attributes,
4019                                }),
4020                                ..Default::default()
4021                            };
4022                            let _ = control_handle
4023                                .send_on_representation(fio::Representation::Directory(info));
4024
4025                            while let Some(Ok(request)) = stream.next().await {
4026                                match request {
4027                                    fio::DirectoryRequest::GetAttributes {
4028                                        query: _,
4029                                        responder,
4030                                    } => {
4031                                        get_attrs_count.fetch_add(1, Ordering::SeqCst);
4032                                        let mutable_attrs =
4033                                            fio::MutableNodeAttributes { ..Default::default() };
4034                                        let immutable_attrs = fio::ImmutableNodeAttributes {
4035                                            id: Some(1),
4036                                            link_count: Some(1),
4037                                            ..Default::default()
4038                                        };
4039                                        responder
4040                                            .send(Ok((&mutable_attrs, &immutable_attrs)))
4041                                            .unwrap();
4042                                    }
4043                                    fio::DirectoryRequest::UpdateAttributes {
4044                                        payload: _,
4045                                        responder,
4046                                    } => {
4047                                        responder.send(Ok(())).unwrap();
4048                                    }
4049                                    fio::DirectoryRequest::Close { responder } => {
4050                                        responder.send(Ok(())).unwrap();
4051                                    }
4052                                    _ => {
4053                                        panic!("Unexpected request: {:?}", request)
4054                                    }
4055                                }
4056                            }
4057                        }));
4058                    }
4059                    fio::DirectoryRequest::Close { responder } => {
4060                        responder.send(Ok(())).unwrap();
4061                    }
4062                    fio::DirectoryRequest::QueryFilesystem { responder } => {
4063                        responder.send(0i32, None).unwrap();
4064                    }
4065                    _ => panic!("Unexpected request: {:?}", request),
4066                }
4067            }
4068
4069            for sub_task in sub_tasks {
4070                let _ = sub_task.await;
4071            }
4072        });
4073
4074        spawn_kernel_and_run(async move |locked, current_task| {
4075            let fs = RemoteFs::new_fs(
4076                locked,
4077                &current_task.kernel(),
4078                client.into_channel(),
4079                FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4080                fio::PERM_READABLE | fio::PERM_WRITABLE,
4081            )
4082            .expect("failed to mount test remote FS");
4083
4084            // 1. Initial fetch.
4085            {
4086                let _info = fs
4087                    .root()
4088                    .node
4089                    .fetch_and_refresh_info(locked, current_task)
4090                    .expect("fetch failed");
4091            }
4092            assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
4093
4094            // 2. Second time should use cached information.
4095            {
4096                let _info = fs
4097                    .root()
4098                    .node
4099                    .fetch_and_refresh_info(locked, current_task)
4100                    .expect("fetch failed");
4101            }
4102            assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
4103
4104            // 3. Update attributes. This should dirty the node.
4105            fs.root()
4106                .node
4107                .update_attributes(locked, current_task, |attrs| {
4108                    attrs.time_modify += UtcDuration::from_seconds(1);
4109                    Ok(())
4110                })
4111                .expect("update_attributes failed");
4112
4113            // 4. Fetch again. Should trigger a request.
4114            {
4115                let _info = fs
4116                    .root()
4117                    .node
4118                    .fetch_and_refresh_info(locked, current_task)
4119                    .expect("fetch failed");
4120            }
4121            assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
4122        })
4123        .await;
4124
4125        server_task.await;
4126    }
4127
4128    trait AsyncBarrier {
4129        async fn async_wait(&self);
4130    }
4131
4132    impl AsyncBarrier for Arc<Barrier> {
4133        async fn async_wait(&self) {
4134            let this = self.clone();
4135            fasync::unblock(move || this.wait()).await;
4136        }
4137    }
4138
4139    #[derive(Default)]
4140    struct MockRemoteFs {
4141        get_attrs_count: AtomicU32,
4142        file_size: AtomicUsize,
4143        write_offsets: Mutex<Vec<u64>>,
4144        data: Mutex<Vec<u8>>,
4145        get_attrs_hook: Mutex<Option<futures::future::BoxFuture<'static, ()>>>,
4146        write_hook: Mutex<Option<futures::future::BoxFuture<'static, ()>>>,
4147    }
4148
4149    impl MockRemoteFs {
4150        async fn handle_file_requests(
4151            self: Arc<Self>,
4152            mut stream: fio::FileRequestStream,
4153            control_handle: fio::FileControlHandle,
4154        ) {
4155            let size = self.file_size.load(Ordering::SeqCst) as u64;
4156            let info = fio::FileInfo {
4157                attributes: Some(fio::NodeAttributes2 {
4158                    mutable_attributes: fio::MutableNodeAttributes { ..Default::default() },
4159                    immutable_attributes: fio::ImmutableNodeAttributes {
4160                        id: Some(2),
4161                        link_count: Some(1),
4162                        content_size: Some(size),
4163                        storage_size: Some(size),
4164                        ..Default::default()
4165                    },
4166                }),
4167                ..Default::default()
4168            };
4169            let _ = control_handle.send_on_representation(fio::Representation::File(info));
4170            while let Some(Ok(request)) = stream.next().await {
4171                match request {
4172                    fio::FileRequest::GetAttributes { responder, .. } => {
4173                        // Spawn a separate task so that we can handle concurrent calls.
4174                        let this = self.clone();
4175                        fasync::Task::spawn(async move {
4176                            this.get_attrs_count.fetch_add(1, Ordering::SeqCst);
4177                            let size = this.file_size.load(Ordering::SeqCst) as u64;
4178                            let hook = this.get_attrs_hook.lock().take();
4179                            if let Some(hook) = hook {
4180                                hook.await;
4181                            }
4182                            responder
4183                                .send(Ok((
4184                                    &fio::MutableNodeAttributes { ..Default::default() },
4185                                    &fio::ImmutableNodeAttributes {
4186                                        id: Some(2),
4187                                        link_count: Some(1),
4188                                        content_size: Some(size),
4189                                        storage_size: Some(size),
4190                                        ..Default::default()
4191                                    },
4192                                )))
4193                                .unwrap();
4194                        })
4195                        .detach();
4196                    }
4197                    fio::FileRequest::ReadAt { count, offset, responder } => {
4198                        let data = self.data.lock();
4199                        let start = std::cmp::min(offset as usize, data.len());
4200                        let end = std::cmp::min(start + count as usize, data.len());
4201                        responder.send(Ok(&data[start..end])).unwrap();
4202                    }
4203                    fio::FileRequest::WriteAt { offset, data, responder, .. } => {
4204                        // Spawn a separate task so that we can test concurrent writes.
4205                        let self_clone = Arc::clone(&self);
4206                        fasync::Task::spawn(async move {
4207                            self_clone.write_offsets.lock().push(offset);
4208                            let end = offset as usize + data.len();
4209                            {
4210                                let mut mock_data = self_clone.data.lock();
4211                                if end > mock_data.len() {
4212                                    mock_data.resize(end, 0);
4213                                }
4214                                mock_data[offset as usize..end].copy_from_slice(&data);
4215                            }
4216                            let mut current_size = self_clone.file_size.load(Ordering::SeqCst);
4217                            while end > current_size {
4218                                match self_clone.file_size.compare_exchange_weak(
4219                                    current_size,
4220                                    end,
4221                                    Ordering::SeqCst,
4222                                    Ordering::SeqCst,
4223                                ) {
4224                                    Ok(_) => break,
4225                                    Err(actual) => current_size = actual,
4226                                }
4227                            }
4228                            let hook = self_clone.write_hook.lock().take();
4229                            if let Some(hook) = hook {
4230                                hook.await;
4231                            }
4232                            responder.send(Ok(data.len() as u64)).unwrap();
4233                        })
4234                        .detach();
4235                    }
4236                    fio::FileRequest::Resize { length, responder, .. } => {
4237                        self.file_size.store(length as usize, Ordering::SeqCst);
4238                        responder.send(Ok(())).unwrap();
4239                    }
4240                    fio::FileRequest::Seek { origin, offset, responder } => {
4241                        let new_offset = match origin {
4242                            fio::SeekOrigin::Start => offset as u64,
4243                            fio::SeekOrigin::Current => 0,
4244                            fio::SeekOrigin::End => {
4245                                (self.file_size.load(Ordering::SeqCst) as i64 + offset) as u64
4246                            }
4247                        };
4248                        responder.send(Ok(new_offset)).unwrap();
4249                    }
4250                    fio::FileRequest::Close { responder } => {
4251                        responder.send(Ok(())).unwrap();
4252                    }
4253                    _ => {}
4254                }
4255            }
4256        }
4257
4258        async fn handle_directory_requests(
4259            self: Arc<Self>,
4260            mut stream: fio::DirectoryRequestStream,
4261            control_handle: fio::DirectoryControlHandle,
4262        ) {
4263            let info = fio::DirectoryInfo {
4264                attributes: Some(fio::NodeAttributes2 {
4265                    mutable_attributes: fio::MutableNodeAttributes { ..Default::default() },
4266                    immutable_attributes: fio::ImmutableNodeAttributes {
4267                        id: Some(1),
4268                        link_count: Some(1),
4269                        ..Default::default()
4270                    },
4271                }),
4272                ..Default::default()
4273            };
4274            let _ = control_handle.send_on_representation(fio::Representation::Directory(info));
4275            let mut file_tasks = Vec::new();
4276            while let Some(Ok(request)) = stream.next().await {
4277                match request {
4278                    fio::DirectoryRequest::Open { path, object, .. } => {
4279                        if path == "file" {
4280                            let self_clone = Arc::clone(&self);
4281                            file_tasks.push(fasync::Task::spawn(async move {
4282                                let (stream, control_handle) =
4283                                    ServerEnd::<fio::FileMarker>::new(object)
4284                                        .into_stream_and_control_handle();
4285                                self_clone.handle_file_requests(stream, control_handle).await;
4286                            }));
4287                        }
4288                    }
4289                    fio::DirectoryRequest::Close { responder } => {
4290                        responder.send(Ok(())).unwrap();
4291                    }
4292                    _ => {}
4293                }
4294            }
4295            for task in file_tasks {
4296                let _ = task.await;
4297            }
4298        }
4299
4300        async fn run(self: Arc<Self>, mut stream: fio::DirectoryRequestStream) {
4301            let mut sub_tasks = Vec::new();
4302            while let Some(Ok(request)) = stream.next().await {
4303                match request {
4304                    fio::DirectoryRequest::Open { path, object, .. } => {
4305                        if path == "." {
4306                            let self_clone = Arc::clone(&self);
4307                            sub_tasks.push(fasync::Task::spawn(async move {
4308                                let (stream, control_handle) =
4309                                    ServerEnd::<fio::DirectoryMarker>::new(object)
4310                                        .into_stream_and_control_handle();
4311                                self_clone.handle_directory_requests(stream, control_handle).await;
4312                            }));
4313                        }
4314                    }
4315                    fio::DirectoryRequest::Close { responder } => {
4316                        responder.send(Ok(())).unwrap();
4317                    }
4318                    fio::DirectoryRequest::QueryFilesystem { responder } => {
4319                        responder.send(0i32, None).unwrap();
4320                    }
4321                    _ => {}
4322                }
4323            }
4324            for sub_task in sub_tasks {
4325                let _ = sub_task.await;
4326            }
4327        }
4328    }
4329
4330    #[::fuchsia::test]
4331    async fn test_get_size_uses_cache_unless_truncated() {
4332        let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4333        let state = Arc::new(MockRemoteFs::default());
4334
4335        let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4336
4337        spawn_kernel_and_run(async move |locked, current_task| {
4338            let fs = RemoteFs::new_fs(
4339                locked,
4340                &current_task.kernel(),
4341                client.into_channel(),
4342                FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4343                fio::PERM_READABLE | fio::PERM_WRITABLE,
4344            )
4345            .expect("failed to mount test remote FS");
4346
4347            let ns = Namespace::new(fs);
4348            let root = ns.root();
4349
4350            let mut context = LookupContext::default();
4351            let file_node = root
4352                .lookup_child(locked, current_task, &mut context, "file".into())
4353                .expect("lookup failed");
4354
4355            // 1. Initial get_size.
4356            assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4357            {
4358                let _size =
4359                    file_node.entry.node.get_size(locked, current_task).expect("get_size failed");
4360            }
4361            assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4362
4363            // 2. Open in append mode and write.
4364            let file_handle = file_node
4365                .open(
4366                    locked,
4367                    current_task,
4368                    OpenFlags::RDWR | OpenFlags::APPEND,
4369                    AccessCheck::default(),
4370                )
4371                .expect("open failed");
4372
4373            {
4374                let mut data = VecInputBuffer::new(b"foo");
4375                let written =
4376                    file_handle.write(locked, current_task, &mut data).expect("write failed");
4377                assert_eq!(written, 3);
4378            }
4379            assert_eq!(
4380                file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4381                3
4382            );
4383            assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4384            assert_eq!(*state.write_offsets.lock(), vec![0]);
4385
4386            // 3. Truncate. This should invalidate the cache.
4387            file_node
4388                .entry
4389                .node
4390                .truncate(locked, current_task, &file_node.mount, 0)
4391                .expect("truncate failed");
4392            assert_eq!(state.file_size.load(Ordering::SeqCst), 0);
4393
4394            // 4. get_size again. Should trigger a request.
4395            {
4396                let size =
4397                    file_node.entry.node.get_size(locked, current_task).expect("get_size failed");
4398                assert_eq!(size, 0);
4399            }
4400            assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 1);
4401
4402            // 5. Append again. It should append at offset 0.
4403            {
4404                let mut data = VecInputBuffer::new(b"bar");
4405                let written =
4406                    file_handle.write(locked, current_task, &mut data).expect("write failed");
4407                assert_eq!(written, 3);
4408            }
4409            assert_eq!(
4410                file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4411                3
4412            );
4413            // write calls seek(End, 0) which calls get_size, which uses the cache if it was just
4414            // refreshed.
4415            assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 1);
4416            assert_eq!(*state.write_offsets.lock(), vec![0, 0]);
4417
4418            // 6. Truncate to 10 and append.
4419            file_node
4420                .entry
4421                .node
4422                .truncate(locked, current_task, &file_node.mount, 10)
4423                .expect("truncate failed");
4424            {
4425                let mut data = VecInputBuffer::new(b"baz");
4426                let written =
4427                    file_handle.write(locked, current_task, &mut data).expect("write failed");
4428                assert_eq!(written, 3);
4429            }
4430            // write calls seek(End, 0). Since truncate was called, cache is invalid.
4431            assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 2);
4432            assert_eq!(
4433                file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4434                13
4435            );
4436            assert_eq!(*state.write_offsets.lock(), vec![0, 0, 10]);
4437        })
4438        .await;
4439
4440        server_task.await;
4441    }
4442
4443    #[::fuchsia::test]
4444    async fn test_get_size_during_refresh_after_truncate() {
4445        let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4446        let state = Arc::new(MockRemoteFs::default());
4447
4448        let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4449
4450        spawn_kernel_and_run(async move |locked, current_task| {
4451            let fs = RemoteFs::new_fs(
4452                locked,
4453                &current_task.kernel(),
4454                client.into_channel(),
4455                FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4456                fio::PERM_READABLE | fio::PERM_WRITABLE,
4457            )
4458            .expect("failed to mount test remote FS");
4459
4460            let ns = Namespace::new(fs);
4461            let root = ns.root();
4462
4463            let mut context = LookupContext::default();
4464            let file_node = root
4465                .lookup_child(locked, current_task, &mut context, "file".into())
4466                .expect("lookup failed");
4467
4468            // Fill cache.
4469            assert_eq!(
4470                file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4471                0
4472            );
4473
4474            // Truncate to 10.
4475            file_node
4476                .entry
4477                .node
4478                .truncate(locked, current_task, &file_node.mount, 10)
4479                .expect("truncate failed");
4480
4481            // Set barrier to pause GetAttributes.
4482            let barrier = Arc::new(Barrier::new(2));
4483            {
4484                let barrier = barrier.clone();
4485                *state.get_attrs_hook.lock() = Some(Box::pin(async move {
4486                    barrier.async_wait().await;
4487                    barrier.async_wait().await;
4488                }));
4489            }
4490
4491            // Spawn thread to call get_size. It will pause when it hits the first barrier.
4492            let file_node_clone = file_node.clone();
4493            let (result1, request) = SpawnRequestBuilder::new()
4494                .with_sync_closure(move |locked, current_task| {
4495                    let size = file_node_clone
4496                        .entry
4497                        .node
4498                        .get_size(locked, current_task)
4499                        .expect("get_size failed");
4500                    assert_eq!(size, 10);
4501                })
4502                .build_with_async_result();
4503            current_task.kernel().kthreads.spawner().spawn_from_request(request);
4504
4505            // Wait for the first barrier to be reached.
4506            barrier.async_wait().await;
4507
4508            // Set up the next request so it unblocks the first request.
4509            *state.get_attrs_hook.lock() =
4510                Some(Box::pin(async move { barrier.async_wait().await }));
4511
4512            // Another get_size call should not use cached size (0) while refresh is in progress.
4513            let (result2, request) = SpawnRequestBuilder::new()
4514                .with_sync_closure(move |locked, current_task| {
4515                    let size = file_node
4516                        .entry
4517                        .node
4518                        .get_size(locked, current_task)
4519                        .expect("get_size failed");
4520                    assert_eq!(size, 10);
4521                })
4522                .build_with_async_result();
4523            current_task.kernel().kthreads.spawner().spawn_from_request(request);
4524
4525            result1.await.unwrap();
4526            result2.await.unwrap();
4527        })
4528        .await;
4529
4530        server_task.await;
4531    }
4532
4533    #[::fuchsia::test]
4534    async fn test_get_size_during_outstanding_write() {
4535        let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4536        let state = Arc::new(MockRemoteFs::default());
4537
4538        let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4539
4540        spawn_kernel_and_run(async move |locked, current_task| {
4541            let fs = RemoteFs::new_fs(
4542                locked,
4543                &current_task.kernel(),
4544                client.into_channel(),
4545                FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4546                fio::PERM_READABLE | fio::PERM_WRITABLE,
4547            )
4548            .expect("failed to mount test remote FS");
4549
4550            let ns = Namespace::new(fs);
4551            let root = ns.root();
4552
4553            let mut context = LookupContext::default();
4554            let file_node = root
4555                .lookup_child(locked, current_task, &mut context, "file".into())
4556                .expect("lookup failed");
4557
4558            // Open the file.
4559            let file_handle = file_node
4560                .open(locked, current_task, OpenFlags::RDWR, AccessCheck::default())
4561                .expect("open failed");
4562
4563            // Set hook to stall the write response.
4564            let barrier = Arc::new(Barrier::new(2));
4565            {
4566                let barrier = barrier.clone();
4567                *state.write_hook.lock() = Some(Box::pin(async move {
4568                    barrier.async_wait().await;
4569                    barrier.async_wait().await;
4570                }));
4571            }
4572
4573            // Start write in another thread.
4574            let file_handle_clone = file_handle.clone();
4575            current_task.kernel().kthreads.spawner().spawn_from_request(
4576                SpawnRequestBuilder::new()
4577                    .with_sync_closure(move |locked, current_task| {
4578                        let mut data = VecInputBuffer::new(b"hello");
4579                        file_handle_clone
4580                            .write(locked, current_task, &mut data)
4581                            .expect("write failed");
4582                    })
4583                    .build(),
4584            );
4585
4586            // Wait until the mock has processed the write and hit the hook.
4587            barrier.async_wait().await;
4588
4589            // On this thread, verify that a read sees the new data.
4590            {
4591                let mut data = VecOutputBuffer::new(5);
4592                let read =
4593                    file_handle.read_at(locked, current_task, 0, &mut data).expect("read failed");
4594                assert_eq!(read, 5);
4595                assert_eq!(data.data(), b"hello");
4596            }
4597
4598            // Now call get_size and it should see the correct size.
4599            let size =
4600                file_node.entry.node.get_size(locked, current_task).expect("get_size failed");
4601            assert_eq!(
4602                size, 5,
4603                "get_size should return the updated size even if a write is outstanding"
4604            );
4605
4606            // Unblock the write.
4607            barrier.async_wait().await;
4608        })
4609        .await;
4610
4611        server_task.await;
4612    }
4613
4614    #[test]
4615    fn test_info_state_initial_state() {
4616        let state = InfoState::new(true); // dirty
4617        assert_eq!(state.0.load(Ordering::Relaxed), 0);
4618
4619        let state = InfoState::new(false); // in sync
4620        assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4621    }
4622
4623    #[test]
4624    fn test_info_state_dirty_op_guard() {
4625        let state = InfoState::new(false);
4626        {
4627            let _guard = state.dirty_op_guard(false);
4628            assert_eq!(state.0.load(Ordering::Relaxed), 1); // IN_SYNC bit cleared, count 1
4629            assert!(!state.is_size_accurate());
4630        }
4631        assert_eq!(state.0.load(Ordering::Relaxed), 0);
4632        assert!(state.is_size_accurate());
4633
4634        {
4635            let _guard = state.dirty_op_guard(true);
4636            assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED | 1);
4637            assert!(!state.is_size_accurate());
4638        }
4639        assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4640        assert!(!state.is_size_accurate());
4641
4642        {
4643            let _guard1 = state.dirty_op_guard(true);
4644            let _guard2 = state.dirty_op_guard(true);
4645            assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED | 2);
4646            assert!(!state.is_size_accurate());
4647        }
4648        assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4649        assert!(!state.is_size_accurate());
4650    }
4651
4652    #[test]
4653    fn test_info_state_refresh_clears_truncated() {
4654        let state = InfoState::new(true);
4655        // Set TRUNCATED bit.
4656        {
4657            let _guard = state.dirty_op_guard(true);
4658        }
4659        assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4660
4661        let info =
4662            DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4663        state.maybe_refresh(&info, |_| Ok(()), |_| unreachable!()).unwrap();
4664
4665        assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4666        assert!(state.is_size_accurate());
4667    }
4668
4669    #[test]
4670    fn test_info_state_maybe_refresh_success() {
4671        let state = InfoState::new(true);
4672        let info =
4673            DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4674
4675        let res = state.maybe_refresh(&info, |_| Ok(42), |_| unreachable!());
4676        assert_eq!(res.unwrap(), 42);
4677        assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4678    }
4679
4680    #[test]
4681    fn test_info_state_maybe_refresh_error() {
4682        let state = InfoState::new(true);
4683        let info =
4684            DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4685
4686        let res: Result<u32, Errno> =
4687            state.maybe_refresh(&info, |_| error!(EIO), |_| unreachable!());
4688        assert!(res.is_err());
4689        assert_eq!(state.0.load(Ordering::Relaxed), 0); // Still dirty
4690    }
4691
4692    #[test]
4693    fn test_info_state_maybe_refresh_not_needed() {
4694        let state = InfoState::new(false); // in sync
4695        let info =
4696            DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4697        let res = state.maybe_refresh(&info, |_| unreachable!(), |_| Ok(123));
4698        assert_eq!(res.unwrap(), 123);
4699    }
4700
4701    #[test]
4702    fn test_info_state_concurrent_dirty_op_during_refresh() {
4703        let state = InfoState::new(true);
4704        let info =
4705            DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4706
4707        state
4708            .maybe_refresh(
4709                &info,
4710                |_| {
4711                    // Simulate a dirty op starting while refresh is in progress
4712                    let _guard = state.dirty_op_guard(false);
4713                    assert_eq!(state.0.load(Ordering::Relaxed), InfoState::PENDING_REFRESH | 1);
4714                    Ok(())
4715                },
4716                |_| unreachable!(),
4717            )
4718            .unwrap();
4719
4720        assert_eq!(state.0.load(Ordering::Relaxed), 0);
4721    }
4722
4723    #[::fuchsia::test]
4724    async fn test_sync() {
4725        let fixture = TestFixture::new().await;
4726        let (server, client) = zx::Channel::create();
4727        fixture.root().clone(server.into()).expect("clone failed");
4728
4729        spawn_kernel_and_run(async move |locked, current_task| {
4730            let kernel = current_task.kernel();
4731            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4732            let fs = RemoteFs::new_fs(
4733                locked,
4734                &kernel,
4735                client,
4736                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
4737                rights,
4738            )
4739            .expect("new_fs failed");
4740            let ns = Namespace::new(fs);
4741            current_task.fs().set_umask(FileMode::from_bits(0));
4742            let root = ns.root();
4743
4744            const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
4745            root.create_node(locked, &current_task, "file".into(), REG_MODE, DeviceId::NONE)
4746                .expect("create_node failed");
4747            let mut context = LookupContext::new(SymlinkMode::NoFollow);
4748            let reg_node = root
4749                .lookup_child(locked, &current_task, &mut context, "file".into())
4750                .expect("lookup_child failed");
4751
4752            // sync should delegate to zxio and succeed
4753            reg_node
4754                .entry
4755                .node
4756                .ops()
4757                .sync(&reg_node.entry.node, &current_task)
4758                .expect("sync failed");
4759        })
4760        .await;
4761        fixture.close().await;
4762    }
4763
4764    #[::fuchsia::test]
4765    async fn test_msync_propagates_to_fxfs() {
4766        use crate::mm::MemoryAccessor;
4767        use crate::mm::syscalls::{sys_mmap, sys_msync};
4768        use crate::vfs::FdFlags;
4769        use starnix_uapi::user_address::UserAddress;
4770        use starnix_uapi::{MAP_SHARED, MS_SYNC, PROT_READ, PROT_WRITE};
4771
4772        // Counter to track Fxfs transactions
4773        let commit_count = Arc::new(AtomicUsize::new(0));
4774        let commit_count_clone = commit_count.clone();
4775
4776        // Open fixture with pre_commit_hook
4777        let fixture = TestFixture::open(
4778            DeviceHolder::new(FakeDevice::new(1024 * 1024, 512)),
4779            TestFixtureOptions {
4780                format: true,
4781                as_blob: false,
4782                encrypted: true,
4783                pre_commit_hook: Some(Box::new(move |_transaction| {
4784                    commit_count_clone.fetch_add(1, Ordering::SeqCst);
4785                    Ok(())
4786                })),
4787            },
4788        )
4789        .await;
4790
4791        let (server, client) = zx::Channel::create();
4792        fixture.root().clone(server.into()).expect("clone channel");
4793
4794        spawn_kernel_and_run(async move |locked, current_task| {
4795            // Setup RemoteFs
4796            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4797            let fs = RemoteFs::new_fs(
4798                locked,
4799                current_task.kernel(),
4800                client,
4801                FileSystemOptions { source: FlyByteStr::new(b"/test"), ..Default::default() },
4802                rights,
4803            )
4804            .expect("new_fs");
4805            let ns = Namespace::new(fs);
4806            let root = ns.root();
4807
4808            // Create and Open a file
4809            let node = root
4810                .create_node(
4811                    locked,
4812                    &current_task,
4813                    "test_file".into(),
4814                    mode!(IFREG, 0o666),
4815                    DeviceId::NONE,
4816                )
4817                .expect("create_node");
4818            let file_handle = node
4819                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
4820                .expect("open");
4821            let fd =
4822                current_task.add_file(locked, file_handle, FdFlags::empty()).expect("add file");
4823
4824            // Do mmap
4825            let len = *PAGE_SIZE as usize * 4;
4826            let mmap_addr = sys_mmap(
4827                locked,
4828                current_task,
4829                UserAddress::default(),
4830                len,
4831                PROT_READ | PROT_WRITE,
4832                MAP_SHARED,
4833                fd,
4834                0,
4835            )
4836            .expect("mmap");
4837
4838            // Modify memory (multiple pages)
4839            for i in 0..4 {
4840                let data = [0xAAu8; 1];
4841                current_task
4842                    .write_memory((mmap_addr + (i * *PAGE_SIZE as usize)).unwrap(), &data)
4843                    .expect("write memory");
4844            }
4845
4846            // Capture commit count before msync
4847            let commits_before_msync = commit_count.load(Ordering::SeqCst);
4848
4849            // invoke msync()
4850            sys_msync(locked, current_task, mmap_addr, len, MS_SYNC).expect("msync");
4851
4852            // Verify msync results
4853            let final_commits = commit_count.load(Ordering::SeqCst);
4854            assert!(
4855                final_commits > commits_before_msync,
4856                "msync should trigger Fxfs transaction. commits: {} -> {}",
4857                commits_before_msync,
4858                final_commits
4859            );
4860        })
4861        .await;
4862
4863        fixture.close().await;
4864    }
4865
4866    #[::fuchsia::test]
4867    async fn test_get_size() {
4868        let fixture = TestFixture::new().await;
4869        let (server, client) = zx::Channel::create();
4870        fixture.root().clone(server.into()).expect("clone failed");
4871
4872        spawn_kernel_and_run(async move |locked, current_task| {
4873            let kernel = current_task.kernel();
4874            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4875            let fs = RemoteFs::new_fs(
4876                locked,
4877                &kernel,
4878                client,
4879                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
4880                rights,
4881            )
4882            .expect("new_fs failed");
4883            let ns = Namespace::new(fs);
4884            let root = ns.root();
4885
4886            const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o666);
4887            let node = root
4888                .create_node(locked, &current_task, "file".into(), REG_MODE, DeviceId::NONE)
4889                .expect("create_node failed");
4890            let file = node
4891                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
4892                .expect("open failed");
4893
4894            // Initial size should be 0.
4895            assert_eq!(
4896                node.entry.node.get_size(locked, &current_task).expect("get_size failed"),
4897                0
4898            );
4899
4900            // Write some data.
4901            let mut data = VecInputBuffer::new(b"hello");
4902            file.write(locked, &current_task, &mut data).expect("write failed");
4903
4904            // Size should be 5.
4905            assert_eq!(
4906                node.entry.node.get_size(locked, &current_task).expect("get_size failed"),
4907                5
4908            );
4909
4910            // Truncate to 10.
4911            node.truncate(locked, &current_task, 10).expect("truncate failed");
4912
4913            // Size should be 10.
4914            assert_eq!(
4915                node.entry.node.get_size(locked, &current_task).expect("get_size failed"),
4916                10
4917            );
4918
4919            // Truncate to 3.
4920            node.truncate(locked, &current_task, 3).expect("truncate failed");
4921
4922            // Size should be 3.
4923            assert_eq!(
4924                node.entry.node.get_size(locked, &current_task).expect("get_size failed"),
4925                3
4926            );
4927        })
4928        .await;
4929        fixture.close().await;
4930    }
4931}