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