Skip to main content

starnix_core/fs/fuchsia/
remote.rs

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