Skip to main content

starnix_core/fs/fuchsia/
remote.rs

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