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