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