Skip to main content

starnix_core/fs/fuchsia/
remote.rs

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