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