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, FullCredentials, Kernel};
12use crate::vfs::buffers::{InputBuffer, OutputBuffer, with_iovec_segments};
13use crate::vfs::fsverity::FsVerityState;
14use crate::vfs::socket::{Socket, SocketFile, ZxioBackedSocket};
15use crate::vfs::{
16    Anon, AppendLockGuard, CacheConfig, CacheMode, DEFAULT_BYTES_PER_BLOCK, DirectoryEntryType,
17    DirentSink, FallocMode, FileHandle, FileObject, FileOps, FileSystem, FileSystemHandle,
18    FileSystemOps, FileSystemOptions, FsNode, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString,
19    SeekTarget, SymlinkTarget, XattrOp, XattrStorage, default_ioctl, default_seek,
20    fileops_impl_directory, fileops_impl_nonseekable, fileops_impl_noop_sync,
21    fileops_impl_seekable, fs_node_impl_not_dir, fs_node_impl_symlink, fs_node_impl_xattr_delegate,
22};
23use bstr::ByteSlice;
24use fidl::AsHandleRef;
25use fidl::endpoints::DiscoverableProtocolMarker as _;
26use fuchsia_runtime::UtcInstant;
27use linux_uapi::SYNC_IOC_MAGIC;
28use once_cell::sync::OnceCell;
29use starnix_crypt::EncryptionKeyId;
30use starnix_logging::{CATEGORY_STARNIX_MM, impossible_error, log_warn, trace_duration};
31use starnix_sync::{
32    FileOpsCore, LockEqualOrBefore, Locked, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
33    Unlocked,
34};
35use starnix_syscalls::{SyscallArg, SyscallResult};
36use starnix_types::vfs::default_statfs;
37use starnix_uapi::auth::FsCred;
38use starnix_uapi::device_type::DeviceType;
39use starnix_uapi::errors::Errno;
40use starnix_uapi::file_mode::FileMode;
41use starnix_uapi::mount_flags::MountFlags;
42use starnix_uapi::open_flags::OpenFlags;
43use starnix_uapi::{
44    __kernel_fsid_t, errno, error, from_status_like_fdio, fsverity_descriptor, ino_t, off_t, statfs,
45};
46use std::mem::MaybeUninit;
47use std::sync::Arc;
48use syncio::zxio::{
49    ZXIO_NODE_PROTOCOL_DIRECTORY, ZXIO_NODE_PROTOCOL_FILE, ZXIO_NODE_PROTOCOL_SYMLINK,
50    ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET, ZXIO_OBJECT_TYPE_DIR, ZXIO_OBJECT_TYPE_FILE,
51    ZXIO_OBJECT_TYPE_NONE, ZXIO_OBJECT_TYPE_PACKET_SOCKET, ZXIO_OBJECT_TYPE_RAW_SOCKET,
52    ZXIO_OBJECT_TYPE_STREAM_SOCKET, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET, zxio_node_attr,
53};
54use syncio::{
55    AllocateMode, DirentIterator, SelinuxContextAttr, XattrSetMode, ZXIO_ROOT_HASH_LENGTH, Zxio,
56    ZxioDirent, ZxioOpenOptions, zxio_fsverity_descriptor_t, zxio_node_attr_has_t,
57    zxio_node_attributes_t,
58};
59use zx::{Counter, HandleBased};
60use {
61    fidl_fuchsia_io as fio, fidl_fuchsia_starnix_binder as fbinder,
62    fidl_fuchsia_unknown as funknown,
63};
64
65pub fn new_remote_fs(
66    locked: &mut Locked<Unlocked>,
67    current_task: &CurrentTask,
68    options: FileSystemOptions,
69) -> Result<FileSystemHandle, Errno> {
70    let kernel = current_task.kernel();
71    // TODO(379929394): After soft transition of fstab is complete, we should
72    // validate the requested_path is a non-empty, non-root path.
73    let requested_path = std::str::from_utf8(&options.source)
74        .map_err(|_| errno!(EINVAL, "source path is not utf8"))?;
75    let mut create_flags =
76        fio::PERM_READABLE | fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY;
77    if !options.flags.contains(MountFlags::RDONLY) {
78        create_flags |= fio::PERM_WRITABLE;
79    }
80    let (root_proxy, subdir) = kernel.open_ns_dir(requested_path, create_flags)?;
81
82    let subdir = if subdir.is_empty() { ".".to_string() } else { subdir };
83    let mut open_rights = fio::PERM_READABLE;
84    if !options.flags.contains(MountFlags::RDONLY) {
85        open_rights |= fio::PERM_WRITABLE;
86    }
87    let mut subdir_options = options;
88    subdir_options.source = subdir.into();
89    create_remotefs_filesystem(locked, kernel, &root_proxy, subdir_options, open_rights)
90}
91
92/// Create a filesystem to access the content of the fuchsia directory available at `fs_src` inside
93/// `pkg`.
94pub fn create_remotefs_filesystem<L>(
95    locked: &mut Locked<L>,
96    kernel: &Kernel,
97    root: &fio::DirectorySynchronousProxy,
98    options: FileSystemOptions,
99    rights: fio::Flags,
100) -> Result<FileSystemHandle, Errno>
101where
102    L: LockEqualOrBefore<FileOpsCore>,
103{
104    let root = syncio::directory_open_directory_async(
105        root,
106        std::str::from_utf8(&options.source)
107            .map_err(|_| errno!(EINVAL, "source path is not utf8"))?,
108        rights,
109    )
110    .map_err(|e| errno!(EIO, format!("Failed to open root: {e}")))?;
111    RemoteFs::new_fs(locked, kernel, root.into_channel(), options, rights)
112}
113
114pub struct RemoteFs {
115    // If true, trust the remote file system's IDs (which requires that the remote file system does
116    // not span mounts).  This must be true to properly support hard links.  If this is false, the
117    // same node can end up having different IDs as it leaves and reenters the node cache.
118    // TODO(https://fxbug.dev/42081972): At the time of writing, package directories do not have
119    // unique IDs so this *must* be false in that case.
120    use_remote_ids: bool,
121
122    root_proxy: fio::DirectorySynchronousProxy,
123}
124
125impl RemoteFs {
126    /// Returns a reference to a RemoteFs given a reference to a FileSystem.
127    ///
128    /// # Panics
129    ///
130    /// This will panic if `fs`'s ops aren't `RemoteFs`, so this should only be called when this is
131    /// known to be the case.
132    fn from_fs(fs: &FileSystem) -> &RemoteFs {
133        if let Some(remote_vol) = fs.downcast_ops::<RemoteVolume>() {
134            remote_vol.remotefs()
135        } else {
136            fs.downcast_ops::<RemoteFs>().unwrap()
137        }
138    }
139}
140
141const REMOTE_FS_MAGIC: u32 = u32::from_be_bytes(*b"f.io");
142const SYNC_IOC_FILE_INFO: u8 = 4;
143const SYNC_IOC_MERGE: u8 = 3;
144
145impl FileSystemOps for RemoteFs {
146    fn statfs(
147        &self,
148        _locked: &mut Locked<FileOpsCore>,
149        _fs: &FileSystem,
150        _current_task: &CurrentTask,
151    ) -> Result<statfs, Errno> {
152        let (status, info) = self
153            .root_proxy
154            .query_filesystem(zx::MonotonicInstant::INFINITE)
155            .map_err(|_| errno!(EIO))?;
156        // Not all remote filesystems support `QueryFilesystem`, many return ZX_ERR_NOT_SUPPORTED.
157        if status == 0 {
158            if let Some(info) = info {
159                let (total_blocks, free_blocks) = if info.block_size > 0 {
160                    (
161                        (info.total_bytes / u64::from(info.block_size))
162                            .try_into()
163                            .unwrap_or(i64::MAX),
164                        ((info.total_bytes.saturating_sub(info.used_bytes))
165                            / u64::from(info.block_size))
166                        .try_into()
167                        .unwrap_or(i64::MAX),
168                    )
169                } else {
170                    (0, 0)
171                };
172
173                let fsid = __kernel_fsid_t {
174                    val: [
175                        (info.fs_id & 0xffffffff) as i32,
176                        ((info.fs_id >> 32) & 0xffffffff) as i32,
177                    ],
178                };
179
180                return Ok(statfs {
181                    f_type: info.fs_type as i64,
182                    f_bsize: info.block_size.into(),
183                    f_blocks: total_blocks,
184                    f_bfree: free_blocks,
185                    f_bavail: free_blocks,
186                    f_files: info.total_nodes.try_into().unwrap_or(i64::MAX),
187                    f_ffree: (info.total_nodes.saturating_sub(info.used_nodes))
188                        .try_into()
189                        .unwrap_or(i64::MAX),
190                    f_fsid: fsid,
191                    f_namelen: info.max_filename_size.try_into().unwrap_or(0),
192                    f_frsize: info.block_size.into(),
193                    ..statfs::default()
194                });
195            }
196        }
197        Ok(default_statfs(REMOTE_FS_MAGIC))
198    }
199
200    fn name(&self) -> &'static FsStr {
201        "remotefs".into()
202    }
203
204    fn uses_external_node_ids(&self) -> bool {
205        self.use_remote_ids
206    }
207
208    fn rename(
209        &self,
210        _locked: &mut Locked<FileOpsCore>,
211        _fs: &FileSystem,
212        current_task: &CurrentTask,
213        old_parent: &FsNodeHandle,
214        old_name: &FsStr,
215        new_parent: &FsNodeHandle,
216        new_name: &FsStr,
217        _renamed: &FsNodeHandle,
218        _replaced: Option<&FsNodeHandle>,
219    ) -> Result<(), Errno> {
220        // Renames should fail if the src or target directory is encrypted and locked.
221        old_parent.fail_if_locked(current_task)?;
222        new_parent.fail_if_locked(current_task)?;
223
224        let Some(old_parent) = old_parent.downcast_ops::<RemoteNode>() else {
225            return error!(EXDEV);
226        };
227        let Some(new_parent) = new_parent.downcast_ops::<RemoteNode>() else {
228            return error!(EXDEV);
229        };
230        old_parent
231            .zxio
232            .rename(get_name_str(old_name)?, &new_parent.zxio, get_name_str(new_name)?)
233            .map_err(|status| from_status_like_fdio!(status))
234    }
235
236    fn manages_timestamps(&self) -> bool {
237        true
238    }
239}
240
241impl RemoteFs {
242    pub fn new(root: zx::Channel, server_end: zx::Channel) -> Result<RemoteFs, Errno> {
243        // See if open3 works.  We assume that if open3 works on the root, it will work for all
244        // descendent nodes in this filesystem.  At the time of writing, this is true for Fxfs.
245        let root_proxy = fio::DirectorySynchronousProxy::new(root);
246        root_proxy
247            .open(
248                ".",
249                fio::Flags::PROTOCOL_DIRECTORY
250                    | fio::PERM_READABLE
251                    | fio::Flags::PERM_INHERIT_WRITE
252                    | fio::Flags::PERM_INHERIT_EXECUTE
253                    | fio::Flags::FLAG_SEND_REPRESENTATION,
254                &fio::Options {
255                    attributes: Some(fio::NodeAttributesQuery::ID),
256                    ..Default::default()
257                },
258                server_end,
259            )
260            .map_err(|_| errno!(EIO))?;
261        // Use remote IDs if the filesystem is Fxfs which we know will give us unique IDs.  Hard
262        // links need to resolve to the same underlying FsNode, so we can only support hard links if
263        // the remote file system will give us unique IDs.  The IDs are also used as the key in
264        // caches, so we can't use remote IDs if the remote filesystem is not guaranteed to provide
265        // unique IDs, or if the remote filesystem spans multiple filesystems.
266        let (status, info) =
267            root_proxy.query_filesystem(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
268        // Be tolerant of errors here; many filesystems return `ZX_ERR_NOT_SUPPORTED`.
269        let use_remote_ids = status == 0
270            && info
271                .map(|i| i.fs_type == fidl_fuchsia_fs::VfsType::Fxfs.into_primitive())
272                .unwrap_or(false);
273        Ok(RemoteFs { use_remote_ids, root_proxy })
274    }
275
276    pub fn new_fs<L>(
277        locked: &mut Locked<L>,
278        kernel: &Kernel,
279        root: zx::Channel,
280        mut options: FileSystemOptions,
281        rights: fio::Flags,
282    ) -> Result<FileSystemHandle, Errno>
283    where
284        L: LockEqualOrBefore<FileOpsCore>,
285    {
286        let (client_end, server_end) = zx::Channel::create();
287        let remotefs = RemoteFs::new(root, server_end)?;
288        let mut attrs = zxio_node_attributes_t {
289            has: zxio_node_attr_has_t { id: true, ..Default::default() },
290            ..Default::default()
291        };
292        let (remote_node, node_id) =
293            match Zxio::create_with_on_representation(client_end.into(), Some(&mut attrs)) {
294                Err(status) => return Err(from_status_like_fdio!(status)),
295                Ok(zxio) => (RemoteNode { zxio, rights }, attrs.id),
296            };
297
298        if !rights.contains(fio::PERM_WRITABLE) {
299            options.flags |= MountFlags::RDONLY;
300        }
301        let use_remote_ids = remotefs.use_remote_ids;
302        let fs = FileSystem::new(
303            locked,
304            kernel,
305            CacheMode::Cached(CacheConfig::default()),
306            remotefs,
307            options,
308        )?;
309        if use_remote_ids {
310            fs.create_root(node_id, remote_node);
311        } else {
312            let root_ino = fs.allocate_ino();
313            fs.create_root(root_ino, remote_node);
314        }
315        Ok(fs)
316    }
317
318    pub fn use_remote_ids(&self) -> bool {
319        self.use_remote_ids
320    }
321}
322
323pub struct RemoteNode {
324    /// The underlying Zircon I/O object for this remote node.
325    ///
326    /// We delegate to the zxio library for actually doing I/O with remote
327    /// objects, including fuchsia.io.Directory and fuchsia.io.File objects.
328    /// This structure lets us share code with FDIO and other Fuchsia clients.
329    zxio: syncio::Zxio,
330
331    /// The fuchsia.io rights for the dir handle. Subdirs will be opened with
332    /// the same rights.
333    rights: fio::Flags,
334}
335
336impl RemoteNode {
337    pub fn new(zxio: syncio::Zxio, rights: fio::Flags) -> Self {
338        Self { zxio, rights }
339    }
340}
341
342/// Create a file handle from a zx::NullableHandle.
343///
344/// The handle must be a channel, socket, vmo or debuglog object.  If the handle is a channel, then
345/// the channel must implement the `fuchsia.unknown/Queryable` protocol.
346///
347/// The resulting object will be owned by root, and will have permissions derived from the `flags`
348/// used to open this object. This is not the same as the permissions set if the object was created
349/// using Starnix itself. We use this mainly for interfacing with objects created outside of Starnix
350/// where these flags represent the desired permissions already.
351pub fn new_remote_file<L>(
352    locked: &mut Locked<L>,
353    current_task: &CurrentTask,
354    handle: zx::NullableHandle,
355    flags: OpenFlags,
356) -> Result<FileHandle, Errno>
357where
358    L: LockEqualOrBefore<FileOpsCore>,
359{
360    let remote_creds = current_task.full_current_creds();
361    let (attrs, ops) = remote_file_attrs_and_ops(current_task, handle.into(), remote_creds)?;
362    let mut rights = fio::Flags::empty();
363    if flags.can_read() {
364        rights |= fio::PERM_READABLE;
365    }
366    if flags.can_write() {
367        rights |= fio::PERM_WRITABLE;
368    }
369    let mode = get_mode(&attrs, rights);
370    // TODO: https://fxbug.dev/407611229 - Give these nodes valid labels.
371    let mut info = FsNodeInfo::new(mode, FsCred::root());
372    update_info_from_attrs(&mut info, &attrs);
373    Ok(Anon::new_private_file_extended(locked, current_task, ops, flags, "[fuchsia:remote]", info))
374}
375
376// Create a FileOps from a zx::NullableHandle.
377//
378// The handle must satisfy the same requirements as `new_remote_file`.
379pub fn new_remote_file_ops(
380    current_task: &CurrentTask,
381    handle: zx::NullableHandle,
382    creds: FullCredentials,
383) -> Result<Box<dyn FileOps>, Errno> {
384    let (_, ops) = remote_file_attrs_and_ops(current_task, handle, creds)?;
385    Ok(ops)
386}
387
388fn remote_file_attrs_and_ops(
389    current_task: &CurrentTask,
390    mut handle: zx::NullableHandle,
391    remote_creds: FullCredentials,
392) -> Result<(zxio_node_attr, Box<dyn FileOps>), Errno> {
393    let handle_type =
394        handle.basic_info().map_err(|status| from_status_like_fdio!(status))?.object_type;
395
396    // Check whether the channel implements a Starnix specific protoocol.
397    if handle_type == zx::ObjectType::CHANNEL {
398        let channel = zx::Channel::from(handle);
399        let queryable = funknown::QueryableSynchronousProxy::new(channel);
400        if let Ok(name) = queryable.query(zx::MonotonicInstant::INFINITE) {
401            if name == fbinder::UnixDomainSocketMarker::PROTOCOL_NAME.as_bytes() {
402                let socket_ops =
403                    RemoteUnixDomainSocket::new(queryable.into_channel(), remote_creds)?;
404                let socket = Socket::new_with_ops(Box::new(socket_ops))?;
405                let file_ops = SocketFile::new(socket);
406                let attr = zxio_node_attr {
407                    has: zxio_node_attr_has_t { mode: true, ..zxio_node_attr_has_t::default() },
408                    mode: 0o777 | FileMode::IFSOCK.bits(),
409                    ..zxio_node_attr::default()
410                };
411                return Ok((attr, file_ops));
412            }
413        };
414        handle = queryable.into_channel().into_handle();
415    } else if handle_type == zx::ObjectType::COUNTER {
416        let attr = zxio_node_attr::default();
417        let file_ops = Box::new(RemoteCounter::new(handle.into()));
418        return Ok((attr, file_ops));
419    }
420
421    // Otherwise, use zxio based objects.
422    let zxio = Zxio::create(handle).map_err(|status| from_status_like_fdio!(status))?;
423    let mut attrs = zxio
424        .attr_get(zxio_node_attr_has_t {
425            protocols: true,
426            abilities: true,
427            content_size: true,
428            storage_size: true,
429            link_count: true,
430            object_type: true,
431            ..Default::default()
432        })
433        .map_err(|status| from_status_like_fdio!(status))?;
434    let ops: Box<dyn FileOps> = match (handle_type, attrs.object_type) {
435        (_, ZXIO_OBJECT_TYPE_DIR) => Box::new(RemoteDirectoryObject::new(zxio)),
436        (zx::ObjectType::VMO, _)
437        | (zx::ObjectType::DEBUGLOG, _)
438        | (_, ZXIO_OBJECT_TYPE_FILE)
439        | (_, ZXIO_OBJECT_TYPE_NONE) => Box::new(RemoteFileObject::new(zxio)),
440        (zx::ObjectType::SOCKET, _)
441        | (_, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET)
442        | (_, ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET)
443        | (_, ZXIO_OBJECT_TYPE_STREAM_SOCKET)
444        | (_, ZXIO_OBJECT_TYPE_RAW_SOCKET)
445        | (_, ZXIO_OBJECT_TYPE_PACKET_SOCKET) => {
446            let socket_ops = ZxioBackedSocket::new_with_zxio(current_task, zxio);
447            let socket = Socket::new_with_ops(Box::new(socket_ops))?;
448            attrs.has.mode = true;
449            attrs.mode = FileMode::IFSOCK.bits();
450            SocketFile::new(socket)
451        }
452        _ => return error!(ENOTSUP),
453    };
454    Ok((attrs, ops))
455}
456
457pub fn create_fuchsia_pipe<L>(
458    locked: &mut Locked<L>,
459    current_task: &CurrentTask,
460    socket: zx::Socket,
461    flags: OpenFlags,
462) -> Result<FileHandle, Errno>
463where
464    L: LockEqualOrBefore<FileOpsCore>,
465{
466    new_remote_file(locked, current_task, socket.into(), flags)
467}
468
469fn fetch_and_refresh_info_impl<'a>(
470    zxio: &syncio::Zxio,
471    info: &'a RwLock<FsNodeInfo>,
472) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
473    let attrs = zxio
474        .attr_get(zxio_node_attr_has_t {
475            content_size: true,
476            storage_size: true,
477            link_count: true,
478            modification_time: true,
479            change_time: true,
480            access_time: true,
481            casefold: true,
482            wrapping_key_id: true,
483            pending_access_time_update: info.read().pending_time_access_update,
484            ..Default::default()
485        })
486        .map_err(|status| from_status_like_fdio!(status))?;
487    let mut info = info.write();
488    update_info_from_attrs(&mut info, &attrs);
489    info.pending_time_access_update = false;
490    Ok(RwLockWriteGuard::downgrade(info))
491}
492
493// Update info from attrs if they are set.
494pub fn update_info_from_attrs(info: &mut FsNodeInfo, attrs: &zxio_node_attributes_t) {
495    // TODO - store these in FsNodeState and convert on fstat
496    if attrs.has.content_size {
497        info.size = attrs.content_size.try_into().unwrap_or(std::usize::MAX);
498    }
499    if attrs.has.storage_size {
500        info.blocks = usize::try_from(attrs.storage_size)
501            .unwrap_or(std::usize::MAX)
502            .div_ceil(DEFAULT_BYTES_PER_BLOCK)
503    }
504    info.blksize = DEFAULT_BYTES_PER_BLOCK;
505    if attrs.has.link_count {
506        info.link_count = attrs.link_count.try_into().unwrap_or(std::usize::MAX);
507    }
508    if attrs.has.modification_time {
509        info.time_modify =
510            UtcInstant::from_nanos(attrs.modification_time.try_into().unwrap_or(i64::MAX));
511    }
512    if attrs.has.change_time {
513        info.time_status_change =
514            UtcInstant::from_nanos(attrs.change_time.try_into().unwrap_or(i64::MAX));
515    }
516    if attrs.has.access_time {
517        info.time_access = UtcInstant::from_nanos(attrs.access_time.try_into().unwrap_or(i64::MAX));
518    }
519    if attrs.has.wrapping_key_id {
520        info.wrapping_key_id = Some(attrs.wrapping_key_id);
521    }
522}
523
524fn get_mode(attrs: &zxio_node_attributes_t, rights: fio::Flags) -> FileMode {
525    if attrs.protocols & ZXIO_NODE_PROTOCOL_SYMLINK != 0 {
526        // We don't set the mode for symbolic links , so we synthesize it instead.
527        FileMode::IFLNK | FileMode::ALLOW_ALL
528    } else if attrs.has.mode {
529        // If the filesystem supports POSIX mode bits, use that directly.
530        FileMode::from_bits(attrs.mode)
531    } else {
532        // The filesystem doesn't support the `mode` attribute, so synthesize it from the protocols
533        // this node supports, and the rights used to open it.
534        let is_directory =
535            attrs.protocols & ZXIO_NODE_PROTOCOL_DIRECTORY == ZXIO_NODE_PROTOCOL_DIRECTORY;
536        let mode = if is_directory { FileMode::IFDIR } else { FileMode::IFREG };
537        let mut permissions = FileMode::EMPTY;
538        if rights.contains(fio::PERM_READABLE) {
539            permissions |= FileMode::IRUSR;
540        }
541        if rights.contains(fio::PERM_WRITABLE) {
542            permissions |= FileMode::IWUSR;
543        }
544        if rights.contains(fio::PERM_EXECUTABLE) {
545            permissions |= FileMode::IXUSR;
546        }
547        // Make sure the same permissions are granted to user, group, and other.
548        permissions |= FileMode::from_bits((permissions.bits() >> 3) | (permissions.bits() >> 6));
549        mode | permissions
550    }
551}
552
553fn get_name_str<'a>(name_bytes: &'a FsStr) -> Result<&'a str, Errno> {
554    std::str::from_utf8(name_bytes.as_ref()).map_err(|_| {
555        log_warn!("bad utf8 in pathname! remote filesystems can't handle this");
556        errno!(EINVAL)
557    })
558}
559
560impl XattrStorage for syncio::Zxio {
561    fn get_xattr(
562        &self,
563        _locked: &mut Locked<FileOpsCore>,
564        name: &FsStr,
565    ) -> Result<FsString, Errno> {
566        Ok(self
567            .xattr_get(name)
568            .map_err(|status| match status {
569                zx::Status::NOT_FOUND => errno!(ENODATA),
570                status => from_status_like_fdio!(status),
571            })?
572            .into())
573    }
574
575    fn set_xattr(
576        &self,
577        _locked: &mut Locked<FileOpsCore>,
578        name: &FsStr,
579        value: &FsStr,
580        op: XattrOp,
581    ) -> Result<(), Errno> {
582        let mode = match op {
583            XattrOp::Set => XattrSetMode::Set,
584            XattrOp::Create => XattrSetMode::Create,
585            XattrOp::Replace => XattrSetMode::Replace,
586        };
587
588        self.xattr_set(name, value, mode).map_err(|status| match status {
589            zx::Status::NOT_FOUND => errno!(ENODATA),
590            status => from_status_like_fdio!(status),
591        })
592    }
593
594    fn remove_xattr(&self, _locked: &mut Locked<FileOpsCore>, name: &FsStr) -> Result<(), Errno> {
595        self.xattr_remove(name).map_err(|status| match status {
596            zx::Status::NOT_FOUND => errno!(ENODATA),
597            _ => from_status_like_fdio!(status),
598        })
599    }
600
601    fn list_xattrs(&self, _locked: &mut Locked<FileOpsCore>) -> Result<Vec<FsString>, Errno> {
602        self.xattr_list()
603            .map(|attrs| attrs.into_iter().map(FsString::new).collect::<Vec<_>>())
604            .map_err(|status| from_status_like_fdio!(status))
605    }
606}
607
608impl FsNodeOps for RemoteNode {
609    fs_node_impl_xattr_delegate!(self, self.zxio);
610
611    fn create_file_ops(
612        &self,
613        locked: &mut Locked<FileOpsCore>,
614        node: &FsNode,
615        current_task: &CurrentTask,
616        flags: OpenFlags,
617    ) -> Result<Box<dyn FileOps>, Errno> {
618        {
619            let node_info = node.fetch_and_refresh_info(locked, current_task)?;
620            if node_info.mode.is_dir() {
621                if let Some(wrapping_key_id) = node_info.wrapping_key_id {
622                    if flags.can_write() {
623                        // Locked encrypted directories cannot be opened with write access.
624                        let crypt_service =
625                            node.fs().crypt_service().ok_or_else(|| errno!(ENOKEY))?;
626                        if !crypt_service.contains_key(EncryptionKeyId::from(wrapping_key_id)) {
627                            return error!(ENOKEY);
628                        }
629                    }
630                }
631                // For directories we need to deep-clone the connection because we rely on the seek
632                // offset.
633                return Ok(Box::new(RemoteDirectoryObject::new(
634                    self.zxio.deep_clone().map_err(|status| from_status_like_fdio!(status))?,
635                )));
636            }
637        }
638
639        // Locked encrypted files cannot be opened.
640        node.fail_if_locked(current_task)?;
641
642        // fsverity files cannot be opened in write mode, including while building.
643        if flags.can_write() {
644            node.fsverity.lock().check_writable()?;
645        }
646
647        // For files we can clone the `Zxio` because we don't rely on any per-connection state
648        // (i.e. the file offset).
649        Ok(Box::new(RemoteFileObject::new(self.zxio.clone())))
650    }
651
652    fn mknod(
653        &self,
654        _locked: &mut Locked<FileOpsCore>,
655        node: &FsNode,
656        current_task: &CurrentTask,
657        name: &FsStr,
658        mode: FileMode,
659        dev: DeviceType,
660        owner: FsCred,
661    ) -> Result<FsNodeHandle, Errno> {
662        node.fail_if_locked(current_task)?;
663        let name = get_name_str(name)?;
664
665        let fs = node.fs();
666        let fs_ops = RemoteFs::from_fs(&fs);
667
668        let zxio;
669        let mut node_id;
670        if !(mode.is_reg() || mode.is_chr() || mode.is_blk() || mode.is_fifo() || mode.is_sock()) {
671            return error!(EINVAL, name);
672        }
673        let mut attrs = zxio_node_attributes_t {
674            has: zxio_node_attr_has_t { id: true, ..Default::default() },
675            ..Default::default()
676        };
677        zxio = self
678            .zxio
679            .open(
680                name,
681                fio::Flags::FLAG_MUST_CREATE
682                    | fio::Flags::PROTOCOL_FILE
683                    | fio::PERM_READABLE
684                    | fio::PERM_WRITABLE,
685                ZxioOpenOptions::new(
686                    Some(&mut attrs),
687                    Some(zxio_node_attributes_t {
688                        mode: mode.bits(),
689                        uid: owner.uid,
690                        gid: owner.gid,
691                        rdev: dev.bits(),
692                        has: zxio_node_attr_has_t {
693                            mode: true,
694                            uid: true,
695                            gid: true,
696                            rdev: true,
697                            ..Default::default()
698                        },
699                        ..Default::default()
700                    }),
701                ),
702            )
703            .map_err(|status| from_status_like_fdio!(status, name))?;
704        node_id = attrs.id;
705
706        let ops = if mode.is_reg() {
707            Box::new(RemoteNode { zxio, rights: self.rights }) as Box<dyn FsNodeOps>
708        } else {
709            Box::new(RemoteSpecialNode { zxio }) as Box<dyn FsNodeOps>
710        };
711
712        if !fs_ops.use_remote_ids {
713            node_id = fs.allocate_ino();
714        }
715        let child =
716            fs.create_node(node_id, ops, FsNodeInfo { rdev: dev, ..FsNodeInfo::new(mode, owner) });
717        Ok(child)
718    }
719
720    fn mkdir(
721        &self,
722        _locked: &mut Locked<FileOpsCore>,
723        node: &FsNode,
724        current_task: &CurrentTask,
725        name: &FsStr,
726        mode: FileMode,
727        owner: FsCred,
728    ) -> Result<FsNodeHandle, Errno> {
729        node.fail_if_locked(current_task)?;
730        let name = get_name_str(name)?;
731
732        let fs = node.fs();
733        let fs_ops = RemoteFs::from_fs(&fs);
734
735        let zxio;
736        let mut node_id;
737        let mut attrs = zxio_node_attributes_t {
738            has: zxio_node_attr_has_t { id: true, ..Default::default() },
739            ..Default::default()
740        };
741        zxio = self
742            .zxio
743            .open(
744                name,
745                fio::Flags::FLAG_MUST_CREATE
746                    | fio::Flags::PROTOCOL_DIRECTORY
747                    | fio::PERM_READABLE
748                    | fio::PERM_WRITABLE,
749                ZxioOpenOptions::new(
750                    Some(&mut attrs),
751                    Some(zxio_node_attributes_t {
752                        mode: mode.bits(),
753                        uid: owner.uid,
754                        gid: owner.gid,
755                        has: zxio_node_attr_has_t {
756                            mode: true,
757                            uid: true,
758                            gid: true,
759                            ..Default::default()
760                        },
761                        ..Default::default()
762                    }),
763                ),
764            )
765            .map_err(|status| from_status_like_fdio!(status, name))?;
766        node_id = attrs.id;
767
768        let ops = RemoteNode { zxio, rights: self.rights };
769        if !fs_ops.use_remote_ids {
770            node_id = fs.allocate_ino();
771        }
772        let child = fs.create_node(node_id, ops, FsNodeInfo::new(mode, owner));
773        Ok(child)
774    }
775
776    fn lookup(
777        &self,
778        _locked: &mut Locked<FileOpsCore>,
779        node: &FsNode,
780        current_task: &CurrentTask,
781        name: &FsStr,
782    ) -> Result<FsNodeHandle, Errno> {
783        let name = get_name_str(name)?;
784
785        let fs = node.fs();
786        let fs_ops = RemoteFs::from_fs(&fs);
787
788        let mut attrs = zxio_node_attributes_t {
789            has: zxio_node_attr_has_t {
790                protocols: true,
791                abilities: true,
792                mode: true,
793                uid: true,
794                gid: true,
795                rdev: true,
796                id: true,
797                fsverity_enabled: true,
798                casefold: true,
799                modification_time: true,
800                change_time: true,
801                access_time: true,
802                ..Default::default()
803            },
804            ..Default::default()
805        };
806        let mut options = ZxioOpenOptions::new(Some(&mut attrs), None);
807        let mut selinux_context_buffer =
808            MaybeUninit::<[u8; fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize]>::uninit();
809        let mut cached_context = security::fs_is_xattr_labeled(node.fs())
810            .then(|| SelinuxContextAttr::new(&mut selinux_context_buffer));
811        if let Some(buffer) = &mut cached_context {
812            options = options.with_selinux_context_read(buffer).unwrap();
813        }
814        let zxio = self
815            .zxio
816            .open(name, self.rights, options)
817            .map_err(|status| from_status_like_fdio!(status, name))?;
818        let symlink_zxio = zxio.clone();
819        let mode = get_mode(&attrs, self.rights);
820        let node_id = if fs_ops.use_remote_ids {
821            if attrs.id == fio::INO_UNKNOWN {
822                return error!(ENOTSUP);
823            }
824            attrs.id
825        } else {
826            fs.allocate_ino()
827        };
828        let owner = FsCred { uid: attrs.uid, gid: attrs.gid };
829        let rdev = DeviceType::from_bits(attrs.rdev);
830        let fsverity_enabled = attrs.fsverity_enabled;
831        // fsverity should not be enabled for non-file nodes.
832        if fsverity_enabled && (attrs.protocols & ZXIO_NODE_PROTOCOL_FILE == 0) {
833            return error!(EINVAL);
834        }
835        let casefold = attrs.casefold;
836        let time_modify =
837            UtcInstant::from_nanos(attrs.modification_time.try_into().unwrap_or(i64::MAX));
838        let time_status_change =
839            UtcInstant::from_nanos(attrs.change_time.try_into().unwrap_or(i64::MAX));
840        let time_access = UtcInstant::from_nanos(attrs.access_time.try_into().unwrap_or(i64::MAX));
841
842        let node = fs.get_or_create_node(node_id, || {
843            let ops = if mode.is_lnk() {
844                Box::new(RemoteSymlink { zxio: Mutex::new(zxio) }) as Box<dyn FsNodeOps>
845            } else if mode.is_reg() || mode.is_dir() {
846                Box::new(RemoteNode { zxio, rights: self.rights }) as Box<dyn FsNodeOps>
847            } else {
848                Box::new(RemoteSpecialNode { zxio }) as Box<dyn FsNodeOps>
849            };
850            let child = FsNode::new_uncached(
851                node_id,
852                ops,
853                &fs,
854                FsNodeInfo {
855                    rdev,
856                    casefold,
857                    time_status_change,
858                    time_modify,
859                    time_access,
860                    ..FsNodeInfo::new(mode, owner)
861                },
862            );
863            if fsverity_enabled {
864                *child.fsverity.lock() = FsVerityState::FsVerity;
865            }
866            if let Some(buffer) = cached_context.as_ref().and_then(|buffer| buffer.get()) {
867                // This is valid to fail if we're using mount point labelling or the
868                // provided context string is invalid.
869                let _ = security::fs_node_notify_security_context(
870                    current_task,
871                    &child,
872                    FsStr::new(buffer),
873                );
874            }
875            Ok(child)
876        })?;
877        if let Some(symlink) = node.downcast_ops::<RemoteSymlink>() {
878            let mut zxio_guard = symlink.zxio.lock();
879            *zxio_guard = symlink_zxio;
880        }
881        Ok(node)
882    }
883
884    fn truncate(
885        &self,
886        _locked: &mut Locked<FileOpsCore>,
887        _guard: &AppendLockGuard<'_>,
888        node: &FsNode,
889        current_task: &CurrentTask,
890        length: u64,
891    ) -> Result<(), Errno> {
892        node.fail_if_locked(current_task)?;
893        self.zxio.truncate(length).map_err(|status| from_status_like_fdio!(status))
894    }
895
896    fn allocate(
897        &self,
898        _locked: &mut Locked<FileOpsCore>,
899        _guard: &AppendLockGuard<'_>,
900        node: &FsNode,
901        current_task: &CurrentTask,
902        mode: FallocMode,
903        offset: u64,
904        length: u64,
905    ) -> Result<(), Errno> {
906        match mode {
907            FallocMode::Allocate { keep_size: false } => {
908                node.fail_if_locked(current_task)?;
909                self.zxio
910                    .allocate(offset, length, AllocateMode::empty())
911                    .map_err(|status| from_status_like_fdio!(status))?;
912                Ok(())
913            }
914            _ => error!(EINVAL),
915        }
916    }
917
918    fn fetch_and_refresh_info<'a>(
919        &self,
920        _locked: &mut Locked<FileOpsCore>,
921        _node: &FsNode,
922        _current_task: &CurrentTask,
923        info: &'a RwLock<FsNodeInfo>,
924    ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
925        fetch_and_refresh_info_impl(&self.zxio, info)
926    }
927
928    fn update_attributes(
929        &self,
930        _locked: &mut Locked<FileOpsCore>,
931        _current_task: &CurrentTask,
932        info: &FsNodeInfo,
933        has: zxio_node_attr_has_t,
934    ) -> Result<(), Errno> {
935        // Omit updating creation_time. By definition, there shouldn't be a change in creation_time.
936        let mut mutable_node_attributes = zxio_node_attributes_t {
937            modification_time: info.time_modify.into_nanos() as u64,
938            access_time: info.time_access.into_nanos() as u64,
939            mode: info.mode.bits(),
940            uid: info.uid,
941            gid: info.gid,
942            rdev: info.rdev.bits(),
943            casefold: info.casefold,
944            has,
945            ..Default::default()
946        };
947        if let Some(id) = info.wrapping_key_id {
948            mutable_node_attributes.wrapping_key_id = id;
949        }
950        self.zxio
951            .attr_set(&mutable_node_attributes)
952            .map_err(|status| from_status_like_fdio!(status))
953    }
954
955    fn unlink(
956        &self,
957        _locked: &mut Locked<FileOpsCore>,
958        _node: &FsNode,
959        _current_task: &CurrentTask,
960        name: &FsStr,
961        _child: &FsNodeHandle,
962    ) -> Result<(), Errno> {
963        // We don't care about the _child argument because 1. unlinking already takes the parent's
964        // children lock, so we don't have to worry about conflicts on this path, and 2. the remote
965        // filesystem tracks the link counts so we don't need to update them here.
966        let name = get_name_str(name)?;
967        self.zxio
968            .unlink(name, fio::UnlinkFlags::empty())
969            .map_err(|status| from_status_like_fdio!(status))
970    }
971
972    fn create_symlink(
973        &self,
974        _locked: &mut Locked<FileOpsCore>,
975        node: &FsNode,
976        current_task: &CurrentTask,
977        name: &FsStr,
978        target: &FsStr,
979        owner: FsCred,
980    ) -> Result<FsNodeHandle, Errno> {
981        node.fail_if_locked(current_task)?;
982
983        let name = get_name_str(name)?;
984        let zxio = self
985            .zxio
986            .create_symlink(name, target)
987            .map_err(|status| from_status_like_fdio!(status))?;
988
989        let fs = node.fs();
990        let fs_ops = RemoteFs::from_fs(&fs);
991
992        let node_id = if fs_ops.use_remote_ids {
993            let attrs = zxio
994                .attr_get(zxio_node_attr_has_t { id: true, ..Default::default() })
995                .map_err(|status| from_status_like_fdio!(status))?;
996            attrs.id
997        } else {
998            fs.allocate_ino()
999        };
1000        let symlink = fs.create_node(
1001            node_id,
1002            RemoteSymlink { zxio: Mutex::new(zxio) },
1003            FsNodeInfo {
1004                size: target.len(),
1005                ..FsNodeInfo::new(FileMode::IFLNK | FileMode::ALLOW_ALL, owner)
1006            },
1007        );
1008        Ok(symlink)
1009    }
1010
1011    fn create_tmpfile(
1012        &self,
1013        node: &FsNode,
1014        _current_task: &CurrentTask,
1015        mode: FileMode,
1016        owner: FsCred,
1017    ) -> Result<FsNodeHandle, Errno> {
1018        let fs = node.fs();
1019        let fs_ops = RemoteFs::from_fs(&fs);
1020
1021        let zxio;
1022        let mut node_id;
1023        if !mode.is_reg() {
1024            return error!(EINVAL);
1025        }
1026        let mut attrs = zxio_node_attributes_t {
1027            has: zxio_node_attr_has_t { id: true, ..Default::default() },
1028            ..Default::default()
1029        };
1030        // `create_tmpfile` is used by O_TMPFILE. Note that
1031        // <https://man7.org/linux/man-pages/man2/open.2.html> states that if O_EXCL is specified
1032        // with O_TMPFILE, the temporary file created cannot be linked into the filesystem. Although
1033        // there exist fuchsia flags `fio::FLAG_TEMPORARY_AS_NOT_LINKABLE`, the starnix vfs already
1034        // handles this case and makes sure that the created file is not linkable. There is also no
1035        // current way of passing the open flags to this function.
1036        zxio = self
1037            .zxio
1038            .open(
1039                ".",
1040                fio::Flags::PROTOCOL_FILE
1041                    | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
1042                    | self.rights,
1043                ZxioOpenOptions::new(
1044                    Some(&mut attrs),
1045                    Some(zxio_node_attributes_t {
1046                        mode: mode.bits(),
1047                        uid: owner.uid,
1048                        gid: owner.gid,
1049                        has: zxio_node_attr_has_t {
1050                            mode: true,
1051                            uid: true,
1052                            gid: true,
1053                            ..Default::default()
1054                        },
1055                        ..Default::default()
1056                    }),
1057                ),
1058            )
1059            .map_err(|status| from_status_like_fdio!(status))?;
1060        node_id = attrs.id;
1061
1062        let ops = Box::new(RemoteNode { zxio, rights: self.rights }) as Box<dyn FsNodeOps>;
1063
1064        if !fs_ops.use_remote_ids {
1065            node_id = fs.allocate_ino();
1066        }
1067        let child = fs.create_node(node_id, ops, FsNodeInfo::new(mode, owner));
1068
1069        Ok(child)
1070    }
1071
1072    fn link(
1073        &self,
1074        _locked: &mut Locked<FileOpsCore>,
1075        node: &FsNode,
1076        _current_task: &CurrentTask,
1077        name: &FsStr,
1078        child: &FsNodeHandle,
1079    ) -> Result<(), Errno> {
1080        if !RemoteFs::from_fs(&node.fs()).use_remote_ids {
1081            return error!(EPERM);
1082        }
1083        let name = get_name_str(name)?;
1084        let link_into = |zxio: &syncio::Zxio| {
1085            zxio.link_into(&self.zxio, name).map_err(|status| match status {
1086                zx::Status::BAD_STATE => errno!(EXDEV),
1087                zx::Status::ACCESS_DENIED => errno!(ENOKEY),
1088                s => from_status_like_fdio!(s),
1089            })
1090        };
1091        if let Some(child) = child.downcast_ops::<RemoteNode>() {
1092            link_into(&child.zxio)
1093        } else if let Some(child) = child.downcast_ops::<RemoteSymlink>() {
1094            link_into(&child.zxio())
1095        } else {
1096            error!(EXDEV)
1097        }
1098    }
1099
1100    fn forget(
1101        self: Box<Self>,
1102        _locked: &mut Locked<FileOpsCore>,
1103        _current_task: &CurrentTask,
1104        info: FsNodeInfo,
1105    ) -> Result<(), Errno> {
1106        // Before forgetting this node, update atime if we need to.
1107        if info.pending_time_access_update {
1108            self.zxio
1109                .close_and_update_access_time()
1110                .map_err(|status| from_status_like_fdio!(status))?;
1111        }
1112        Ok(())
1113    }
1114
1115    fn enable_fsverity(&self, descriptor: &fsverity_descriptor) -> Result<(), Errno> {
1116        let descr = zxio_fsverity_descriptor_t {
1117            hash_algorithm: descriptor.hash_algorithm,
1118            salt_size: descriptor.salt_size,
1119            salt: descriptor.salt,
1120        };
1121        self.zxio.enable_verity(&descr).map_err(|status| from_status_like_fdio!(status))
1122    }
1123
1124    fn get_fsverity_descriptor(&self, log_blocksize: u8) -> Result<fsverity_descriptor, Errno> {
1125        let mut root_hash = [0; ZXIO_ROOT_HASH_LENGTH];
1126        let attrs = self
1127            .zxio
1128            .attr_get_with_root_hash(
1129                zxio_node_attr_has_t {
1130                    content_size: true,
1131                    fsverity_options: true,
1132                    fsverity_root_hash: true,
1133                    ..Default::default()
1134                },
1135                &mut root_hash,
1136            )
1137            .map_err(|status| match status {
1138                zx::Status::INVALID_ARGS => errno!(ENODATA),
1139                _ => from_status_like_fdio!(status),
1140            })?;
1141        return Ok(fsverity_descriptor {
1142            version: 1,
1143            hash_algorithm: attrs.fsverity_options.hash_alg,
1144            log_blocksize,
1145            salt_size: attrs.fsverity_options.salt_size as u8,
1146            __reserved_0x04: 0u32,
1147            data_size: attrs.content_size,
1148            root_hash,
1149            salt: attrs.fsverity_options.salt,
1150            __reserved: [0u8; 144],
1151        });
1152    }
1153}
1154
1155struct RemoteSpecialNode {
1156    zxio: syncio::Zxio,
1157}
1158
1159impl FsNodeOps for RemoteSpecialNode {
1160    fs_node_impl_not_dir!();
1161    fs_node_impl_xattr_delegate!(self, self.zxio);
1162
1163    fn create_file_ops(
1164        &self,
1165        _locked: &mut Locked<FileOpsCore>,
1166        _node: &FsNode,
1167        _current_task: &CurrentTask,
1168        _flags: OpenFlags,
1169    ) -> Result<Box<dyn FileOps>, Errno> {
1170        unreachable!("Special nodes cannot be opened.");
1171    }
1172}
1173
1174fn zxio_read_write_inner_map_error(status: zx::Status) -> Errno {
1175    match status {
1176        // zx::Stream may return invalid args or not found error because of
1177        // invalid zx_iovec buffer pointers.
1178        zx::Status::INVALID_ARGS | zx::Status::NOT_FOUND => errno!(EFAULT, ""),
1179        status => from_status_like_fdio!(status),
1180    }
1181}
1182
1183fn zxio_read_inner(
1184    data: &mut dyn OutputBuffer,
1185    unified_read_fn: impl FnOnce(&[syncio::zxio::zx_iovec]) -> Result<usize, zx::Status>,
1186    vmo_read_fn: impl FnOnce(&mut [u8]) -> Result<usize, zx::Status>,
1187) -> Result<usize, Errno> {
1188    let read_bytes = with_iovec_segments(data, |iovecs| {
1189        unified_read_fn(&iovecs).map_err(zxio_read_write_inner_map_error)
1190    });
1191
1192    match read_bytes {
1193        Some(actual) => {
1194            let actual = actual?;
1195            // SAFETY: we successfully read `actual` bytes
1196            // directly to the user's buffer segments.
1197            unsafe { data.advance(actual) }?;
1198            Ok(actual)
1199        }
1200        None => {
1201            // Perform the (slower) operation by using an intermediate buffer.
1202            let total = data.available();
1203            let mut bytes = vec![0u8; total];
1204            let actual =
1205                vmo_read_fn(&mut bytes).map_err(|status| from_status_like_fdio!(status))?;
1206            data.write_all(&bytes[0..actual])
1207        }
1208    }
1209}
1210
1211fn zxio_read_at(zxio: &Zxio, offset: usize, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
1212    let offset = offset as u64;
1213    zxio_read_inner(
1214        data,
1215        |iovecs| {
1216            // SAFETY: `zxio_read_inner` maps the returned error to an appropriate
1217            // `Errno` for userspace to handle. `data` only points to memory that
1218            // is allowed to be written to (Linux user-mode aspace or a valid
1219            // Starnix owned buffer).
1220            unsafe { zxio.readv_at(offset, iovecs) }
1221        },
1222        |bytes| zxio.read_at(offset, bytes),
1223    )
1224}
1225
1226fn zxio_write_inner(
1227    data: &mut dyn InputBuffer,
1228    unified_write_fn: impl FnOnce(&[syncio::zxio::zx_iovec]) -> Result<usize, zx::Status>,
1229    vmo_write_fn: impl FnOnce(&[u8]) -> Result<usize, zx::Status>,
1230) -> Result<usize, Errno> {
1231    let write_bytes = with_iovec_segments(data, |iovecs| {
1232        unified_write_fn(&iovecs).map_err(zxio_read_write_inner_map_error)
1233    });
1234
1235    match write_bytes {
1236        Some(actual) => {
1237            let actual = actual?;
1238            data.advance(actual)?;
1239            Ok(actual)
1240        }
1241        None => {
1242            // Perform the (slower) operation by using an intermediate buffer.
1243            let bytes = data.peek_all()?;
1244            let actual = vmo_write_fn(&bytes).map_err(|status| from_status_like_fdio!(status))?;
1245            data.advance(actual)?;
1246            Ok(actual)
1247        }
1248    }
1249}
1250
1251fn zxio_write_at(
1252    zxio: &Zxio,
1253    _current_task: &CurrentTask,
1254    offset: usize,
1255    data: &mut dyn InputBuffer,
1256) -> Result<usize, Errno> {
1257    let offset = offset as u64;
1258    zxio_write_inner(
1259        data,
1260        |iovecs| {
1261            // SAFETY: `zxio_write_inner` maps the returned error to an appropriate
1262            // `Errno` for userspace to handle.
1263            unsafe { zxio.writev_at(offset, iovecs) }
1264        },
1265        |bytes| zxio.write_at(offset, bytes),
1266    )
1267}
1268
1269/// Helper struct to track the context necessary to iterate over dir entries.
1270#[derive(Default)]
1271struct RemoteDirectoryIterator<'a> {
1272    iterator: Option<DirentIterator<'a>>,
1273
1274    /// If the last attempt to write to the sink failed, this contains the entry that is pending to
1275    /// be added. This is also used to synthesize dot-dot.
1276    pending_entry: Entry,
1277}
1278
1279#[derive(Default)]
1280enum Entry {
1281    // Indicates no more entries.
1282    #[default]
1283    None,
1284
1285    Some(ZxioDirent),
1286
1287    // Indicates dot-dot should be synthesized.
1288    DotDot,
1289}
1290
1291impl Entry {
1292    fn take(&mut self) -> Entry {
1293        std::mem::replace(self, Entry::None)
1294    }
1295}
1296
1297impl From<Option<ZxioDirent>> for Entry {
1298    fn from(value: Option<ZxioDirent>) -> Self {
1299        match value {
1300            None => Entry::None,
1301            Some(x) => Entry::Some(x),
1302        }
1303    }
1304}
1305
1306impl<'a> RemoteDirectoryIterator<'a> {
1307    fn get_or_init_iterator(&mut self, zxio: &'a Zxio) -> Result<&mut DirentIterator<'a>, Errno> {
1308        if self.iterator.is_none() {
1309            let iterator =
1310                zxio.create_dirent_iterator().map_err(|status| from_status_like_fdio!(status))?;
1311            self.iterator = Some(iterator);
1312        }
1313        if let Some(iterator) = &mut self.iterator {
1314            return Ok(iterator);
1315        }
1316
1317        // Should be an impossible error, because we just created the iterator above.
1318        error!(EIO)
1319    }
1320
1321    /// Returns the next dir entry. If no more entries are found, returns None.  Returns an error if
1322    /// the iterator fails for other reasons described by the zxio library.
1323    pub fn next(&mut self, zxio: &'a Zxio) -> Result<Entry, Errno> {
1324        let mut next = self.pending_entry.take();
1325        if let Entry::None = next {
1326            next = self
1327                .get_or_init_iterator(zxio)?
1328                .next()
1329                .transpose()
1330                .map_err(|status| from_status_like_fdio!(status))?
1331                .into();
1332        }
1333        // We only want to synthesize .. if . exists because the . and .. entries get removed if the
1334        // directory is unlinked, so if the remote filesystem has removed ., we know to omit the
1335        // .. entry.
1336        match &next {
1337            Entry::Some(ZxioDirent { name, .. }) if name == "." => {
1338                self.pending_entry = Entry::DotDot;
1339            }
1340            _ => {}
1341        }
1342        Ok(next)
1343    }
1344}
1345
1346struct RemoteDirectoryObject {
1347    iterator: Mutex<RemoteDirectoryIterator<'static>>,
1348
1349    // The underlying Zircon I/O object.  This *must* be dropped after `iterator` above because the
1350    // iterator has references to this object.  We use some unsafe code below to erase the lifetime
1351    // (hence the 'static above).
1352    zxio: Zxio,
1353}
1354
1355impl RemoteDirectoryObject {
1356    pub fn new(zxio: Zxio) -> RemoteDirectoryObject {
1357        RemoteDirectoryObject { zxio, iterator: Mutex::new(RemoteDirectoryIterator::default()) }
1358    }
1359
1360    /// Returns a reference to Zxio with the lifetime erased.
1361    ///
1362    /// # Safety
1363    ///
1364    /// The caller must uphold the lifetime requirements, which will be the case if this is only
1365    /// used for the contained iterator (`iterator` is dropped before `zxio`).
1366    unsafe fn zxio(&self) -> &'static Zxio {
1367        #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1368        unsafe {
1369            &*(&self.zxio as *const Zxio)
1370        }
1371    }
1372}
1373
1374impl FileOps for RemoteDirectoryObject {
1375    fileops_impl_directory!();
1376
1377    fn seek(
1378        &self,
1379        _locked: &mut Locked<FileOpsCore>,
1380        _file: &FileObject,
1381        _current_task: &CurrentTask,
1382        current_offset: off_t,
1383        target: SeekTarget,
1384    ) -> Result<off_t, Errno> {
1385        let mut iterator = self.iterator.lock();
1386        let new_offset = default_seek(current_offset, target, || error!(EINVAL))?;
1387        let mut iterator_position = current_offset;
1388
1389        if new_offset < iterator_position {
1390            // Our iterator only goes forward, so reset it here.  Note: we *must* rewind it rather
1391            // than just create a new iterator because the remote end maintains the offset.
1392            if let Some(iterator) = &mut iterator.iterator {
1393                iterator.rewind().map_err(|status| from_status_like_fdio!(status))?;
1394            }
1395            iterator.pending_entry = Entry::None;
1396            iterator_position = 0;
1397        }
1398
1399        // Advance the iterator to catch up with the offset.
1400        for i in iterator_position..new_offset {
1401            // SAFETY: See the comment on the `zxio` function above.  The iterator outlives this
1402            // function and the zxio object must outlive the iterator.
1403            match iterator.next(unsafe { self.zxio() }) {
1404                Ok(Entry::Some(_) | Entry::DotDot) => {}
1405                Ok(Entry::None) => break, // No more entries.
1406                Err(_) => {
1407                    // In order to keep the offset and the iterator in sync, set the new offset
1408                    // to be as far as we could get.
1409                    // Note that failing the seek here would also cause the iterator and the
1410                    // offset to not be in sync, because the iterator has already moved from
1411                    // where it was.
1412                    return Ok(i);
1413                }
1414            }
1415        }
1416
1417        Ok(new_offset)
1418    }
1419
1420    fn readdir(
1421        &self,
1422        _locked: &mut Locked<FileOpsCore>,
1423        file: &FileObject,
1424        _current_task: &CurrentTask,
1425        sink: &mut dyn DirentSink,
1426    ) -> Result<(), Errno> {
1427        // It is important to acquire the lock to the offset before the context, to avoid a deadlock
1428        // where seek() tries to modify the context.
1429        let mut iterator = self.iterator.lock();
1430
1431        loop {
1432            // SAFETY: See the comment on the `zxio` function above.  The iterator outlives this
1433            // function and the zxio object must outlive the iterator.
1434            let entry = iterator.next(unsafe { self.zxio() })?;
1435            if let Err(e) = match &entry {
1436                Entry::Some(entry) => {
1437                    let inode_num: ino_t = entry.id.ok_or_else(|| errno!(EIO))?;
1438                    let entry_type = if entry.is_dir() {
1439                        DirectoryEntryType::DIR
1440                    } else if entry.is_file() {
1441                        DirectoryEntryType::REG
1442                    } else {
1443                        DirectoryEntryType::UNKNOWN
1444                    };
1445                    sink.add(inode_num, sink.offset() + 1, entry_type, entry.name.as_bstr())
1446                }
1447                Entry::DotDot => {
1448                    let inode_num = if let Some(parent) = file.name.parent_within_mount() {
1449                        parent.node.ino
1450                    } else {
1451                        // For the root .. should have the same inode number as .
1452                        file.name.entry.node.ino
1453                    };
1454                    sink.add(inode_num, sink.offset() + 1, DirectoryEntryType::DIR, "..".into())
1455                }
1456                Entry::None => break,
1457            } {
1458                iterator.pending_entry = entry;
1459                return Err(e);
1460            }
1461        }
1462        Ok(())
1463    }
1464
1465    fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1466        self.zxio.sync().map_err(|status| match status {
1467            zx::Status::NO_RESOURCES | zx::Status::NO_MEMORY | zx::Status::NO_SPACE => {
1468                errno!(ENOSPC)
1469            }
1470            zx::Status::INVALID_ARGS | zx::Status::NOT_FILE => errno!(EINVAL),
1471            zx::Status::BAD_HANDLE => errno!(EBADFD),
1472            zx::Status::NOT_SUPPORTED => errno!(ENOTSUP),
1473            zx::Status::INTERRUPTED_RETRY => errno!(EINTR),
1474            _ => errno!(EIO),
1475        })
1476    }
1477
1478    fn to_handle(
1479        &self,
1480        _file: &FileObject,
1481        _current_task: &CurrentTask,
1482    ) -> Result<Option<zx::NullableHandle>, Errno> {
1483        self.zxio
1484            .deep_clone()
1485            .and_then(Zxio::release)
1486            .map(Some)
1487            .map_err(|status| from_status_like_fdio!(status))
1488    }
1489}
1490
1491pub struct RemoteFileObject {
1492    /// The underlying Zircon I/O object.  This is shared, so we must take care not to use any
1493    /// stateful methods on the underlying object (reading and writing is fine).
1494    zxio: Zxio,
1495
1496    /// Cached read-only VMO handle.
1497    read_only_memory: OnceCell<Arc<MemoryObject>>,
1498
1499    /// Cached read/exec VMO handle.
1500    read_exec_memory: OnceCell<Arc<MemoryObject>>,
1501}
1502
1503impl RemoteFileObject {
1504    fn new(zxio: Zxio) -> RemoteFileObject {
1505        RemoteFileObject {
1506            zxio,
1507            read_only_memory: Default::default(),
1508            read_exec_memory: Default::default(),
1509        }
1510    }
1511
1512    fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno> {
1513        let without_exec = self
1514            .zxio
1515            .vmo_get(prot.to_vmar_flags() - zx::VmarFlags::PERM_EXECUTE)
1516            .map_err(|status| from_status_like_fdio!(status))?;
1517        let all_flags = if prot.contains(ProtectionFlags::EXEC) {
1518            without_exec.replace_as_executable(&VMEX_RESOURCE).map_err(impossible_error)?
1519        } else {
1520            without_exec
1521        };
1522        Ok(Arc::new(MemoryObject::from(all_flags)))
1523    }
1524}
1525
1526impl FileOps for RemoteFileObject {
1527    fileops_impl_seekable!();
1528
1529    fn read(
1530        &self,
1531        _locked: &mut Locked<FileOpsCore>,
1532        _file: &FileObject,
1533        _current_task: &CurrentTask,
1534        offset: usize,
1535        data: &mut dyn OutputBuffer,
1536    ) -> Result<usize, Errno> {
1537        zxio_read_at(&self.zxio, offset, data)
1538    }
1539
1540    fn write(
1541        &self,
1542        _locked: &mut Locked<FileOpsCore>,
1543        _file: &FileObject,
1544        current_task: &CurrentTask,
1545        offset: usize,
1546        data: &mut dyn InputBuffer,
1547    ) -> Result<usize, Errno> {
1548        zxio_write_at(&self.zxio, current_task, offset, data)
1549    }
1550
1551    fn get_memory(
1552        &self,
1553        _locked: &mut Locked<FileOpsCore>,
1554        _file: &FileObject,
1555        _current_task: &CurrentTask,
1556        _length: Option<usize>,
1557        prot: ProtectionFlags,
1558    ) -> Result<Arc<MemoryObject>, Errno> {
1559        trace_duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
1560        let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
1561            Some(&self.read_exec_memory)
1562        } else if prot == ProtectionFlags::READ {
1563            Some(&self.read_only_memory)
1564        } else {
1565            None
1566        };
1567
1568        memory_cache
1569            .map(|c| c.get_or_try_init(|| self.fetch_remote_memory(prot)).cloned())
1570            .unwrap_or_else(|| self.fetch_remote_memory(prot))
1571    }
1572
1573    fn to_handle(
1574        &self,
1575        _file: &FileObject,
1576        _current_task: &CurrentTask,
1577    ) -> Result<Option<zx::NullableHandle>, Errno> {
1578        self.zxio
1579            .deep_clone()
1580            .and_then(Zxio::release)
1581            .map(Some)
1582            .map_err(|status| from_status_like_fdio!(status))
1583    }
1584
1585    fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1586        self.zxio.sync().map_err(|status| match status {
1587            zx::Status::NO_RESOURCES | zx::Status::NO_MEMORY | zx::Status::NO_SPACE => {
1588                errno!(ENOSPC)
1589            }
1590            zx::Status::INVALID_ARGS | zx::Status::NOT_FILE => errno!(EINVAL),
1591            zx::Status::BAD_HANDLE => errno!(EBADFD),
1592            zx::Status::NOT_SUPPORTED => errno!(ENOTSUP),
1593            zx::Status::INTERRUPTED_RETRY => errno!(EINTR),
1594            _ => errno!(EIO),
1595        })
1596    }
1597
1598    fn ioctl(
1599        &self,
1600        locked: &mut Locked<Unlocked>,
1601        file: &FileObject,
1602        current_task: &CurrentTask,
1603        request: u32,
1604        arg: SyscallArg,
1605    ) -> Result<SyscallResult, Errno> {
1606        default_ioctl(file, locked, current_task, request, arg)
1607    }
1608}
1609
1610struct RemoteSymlink {
1611    zxio: Mutex<syncio::Zxio>,
1612}
1613
1614impl RemoteSymlink {
1615    fn zxio(&self) -> syncio::Zxio {
1616        self.zxio.lock().clone()
1617    }
1618}
1619
1620impl FsNodeOps for RemoteSymlink {
1621    fs_node_impl_symlink!();
1622    fs_node_impl_xattr_delegate!(self, self.zxio());
1623
1624    fn readlink(
1625        &self,
1626        _locked: &mut Locked<FileOpsCore>,
1627        _node: &FsNode,
1628        _current_task: &CurrentTask,
1629    ) -> Result<SymlinkTarget, Errno> {
1630        Ok(SymlinkTarget::Path(
1631            self.zxio().read_link().map_err(|status| from_status_like_fdio!(status))?.into(),
1632        ))
1633    }
1634
1635    fn fetch_and_refresh_info<'a>(
1636        &self,
1637        _locked: &mut Locked<FileOpsCore>,
1638        _node: &FsNode,
1639        _current_task: &CurrentTask,
1640        info: &'a RwLock<FsNodeInfo>,
1641    ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
1642        fetch_and_refresh_info_impl(&self.zxio(), info)
1643    }
1644
1645    fn forget(
1646        self: Box<Self>,
1647        _locked: &mut Locked<FileOpsCore>,
1648        _current_task: &CurrentTask,
1649        info: FsNodeInfo,
1650    ) -> Result<(), Errno> {
1651        // Before forgetting this node, update atime if we need to.
1652        if info.pending_time_access_update {
1653            self.zxio()
1654                .close_and_update_access_time()
1655                .map_err(|status| from_status_like_fdio!(status))?;
1656        }
1657        Ok(())
1658    }
1659}
1660
1661pub struct RemoteCounter {
1662    counter: Counter,
1663}
1664
1665impl RemoteCounter {
1666    fn new(counter: Counter) -> Self {
1667        Self { counter }
1668    }
1669
1670    pub fn duplicate_handle(&self) -> Result<Counter, Errno> {
1671        self.counter.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)
1672    }
1673}
1674
1675impl FileOps for RemoteCounter {
1676    fileops_impl_nonseekable!();
1677    fileops_impl_noop_sync!();
1678
1679    fn read(
1680        &self,
1681        _locked: &mut Locked<FileOpsCore>,
1682        _file: &FileObject,
1683        _current_task: &CurrentTask,
1684        _offset: usize,
1685        _data: &mut dyn OutputBuffer,
1686    ) -> Result<usize, Errno> {
1687        error!(ENOTSUP)
1688    }
1689
1690    fn write(
1691        &self,
1692        _locked: &mut Locked<FileOpsCore>,
1693        _file: &FileObject,
1694        _current_task: &CurrentTask,
1695        _offset: usize,
1696        _data: &mut dyn InputBuffer,
1697    ) -> Result<usize, Errno> {
1698        error!(ENOTSUP)
1699    }
1700
1701    fn ioctl(
1702        &self,
1703        locked: &mut Locked<Unlocked>,
1704        file: &FileObject,
1705        current_task: &CurrentTask,
1706        request: u32,
1707        arg: SyscallArg,
1708    ) -> Result<SyscallResult, Errno> {
1709        let ioctl_type = (request >> 8) as u8;
1710        let ioctl_number = request as u8;
1711        if ioctl_type == SYNC_IOC_MAGIC
1712            && (ioctl_number == SYNC_IOC_FILE_INFO || ioctl_number == SYNC_IOC_MERGE)
1713        {
1714            let mut sync_points: Vec<SyncPoint> = vec![];
1715            let counter = self.duplicate_handle()?;
1716            sync_points.push(SyncPoint::new(Timeline::Hwc, counter.into()));
1717            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";
1718            let sync_file = SyncFile::new(*sync_file_name, SyncFence { sync_points });
1719            return sync_file.ioctl(locked, file, current_task, request, arg);
1720        }
1721
1722        error!(EINVAL)
1723    }
1724}
1725
1726#[cfg(test)]
1727mod test {
1728    use super::*;
1729    use crate::mm::PAGE_SIZE;
1730    use crate::testing::*;
1731    use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
1732    use crate::vfs::socket::{SocketFile, SocketMessageFlags};
1733    use crate::vfs::{EpollFileObject, LookupContext, Namespace, SymlinkMode, TimeUpdateType};
1734    use assert_matches::assert_matches;
1735    use fidl_fuchsia_io as fio;
1736    use flyweights::FlyByteStr;
1737    use fxfs_testing::{TestFixture, TestFixtureOptions};
1738    use starnix_uapi::auth::Credentials;
1739    use starnix_uapi::errors::EINVAL;
1740    use starnix_uapi::file_mode::{AccessCheck, mode};
1741    use starnix_uapi::open_flags::OpenFlags;
1742    use starnix_uapi::vfs::{EpollEvent, FdEvents};
1743    use zx::HandleBased;
1744
1745    #[::fuchsia::test]
1746    async fn test_remote_uds() {
1747        spawn_kernel_and_run(async |locked, current_task| {
1748            let (s1, s2) = zx::Socket::create_datagram();
1749            s2.write(&vec![0]).expect("write");
1750            let file = new_remote_file(locked, &current_task, s1.into(), OpenFlags::RDWR)
1751                .expect("new_remote_file");
1752            assert!(file.node().is_sock());
1753            let socket_ops = file.downcast_file::<SocketFile>().unwrap();
1754            let flags = SocketMessageFlags::CTRUNC
1755                | SocketMessageFlags::TRUNC
1756                | SocketMessageFlags::NOSIGNAL
1757                | SocketMessageFlags::CMSG_CLOEXEC;
1758            let mut buffer = VecOutputBuffer::new(1024);
1759            let info = socket_ops
1760                .recvmsg(locked, &current_task, &file, &mut buffer, flags, None)
1761                .expect("recvmsg");
1762            assert!(info.ancillary_data.is_empty());
1763            assert_eq!(info.message_length, 1);
1764        })
1765        .await;
1766    }
1767
1768    #[::fuchsia::test]
1769    async fn test_tree() {
1770        spawn_kernel_and_run(async |locked, current_task| {
1771            let kernel = current_task.kernel();
1772            let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
1773            let (server, client) = zx::Channel::create();
1774            fdio::open("/pkg", rights, server).expect("failed to open /pkg");
1775            let fs = RemoteFs::new_fs(
1776                locked,
1777                &kernel,
1778                client,
1779                FileSystemOptions { source: FlyByteStr::new(b"/pkg"), ..Default::default() },
1780                rights,
1781            )
1782            .unwrap();
1783            let ns = Namespace::new(fs);
1784            let root = ns.root();
1785            let mut context = LookupContext::default();
1786            assert_eq!(
1787                root.lookup_child(locked, &current_task, &mut context, "nib".into()).err(),
1788                Some(errno!(ENOENT))
1789            );
1790            let mut context = LookupContext::default();
1791            root.lookup_child(locked, &current_task, &mut context, "lib".into()).unwrap();
1792
1793            let mut context = LookupContext::default();
1794            let _test_file = root
1795                .lookup_child(
1796                    locked,
1797                    &current_task,
1798                    &mut context,
1799                    "data/tests/hello_starnix".into(),
1800                )
1801                .unwrap()
1802                .open(locked, &current_task, OpenFlags::RDONLY, AccessCheck::default())
1803                .unwrap();
1804        })
1805        .await;
1806    }
1807
1808    #[::fuchsia::test]
1809    async fn test_blocking_io() {
1810        spawn_kernel_and_run(async |locked, current_task| {
1811            let (client, server) = zx::Socket::create_stream();
1812            let pipe = create_fuchsia_pipe(locked, &current_task, client, OpenFlags::RDWR).unwrap();
1813
1814            let bytes = [0u8; 64];
1815            assert_eq!(bytes.len(), server.write(&bytes).unwrap());
1816
1817            // Spawn a kthread to get the right lock context.
1818            let bytes_read =
1819                pipe.read(locked, &current_task, &mut VecOutputBuffer::new(64)).unwrap();
1820
1821            assert_eq!(bytes_read, bytes.len());
1822        })
1823        .await;
1824    }
1825
1826    #[::fuchsia::test]
1827    async fn test_poll() {
1828        spawn_kernel_and_run(async |locked, current_task| {
1829            let (client, server) = zx::Socket::create_stream();
1830            let pipe = create_fuchsia_pipe(locked, &current_task, client, OpenFlags::RDWR)
1831                .expect("create_fuchsia_pipe");
1832            let server_zxio = Zxio::create(server.into_handle()).expect("Zxio::create");
1833
1834            assert_eq!(
1835                pipe.query_events(locked, &current_task),
1836                Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
1837            );
1838
1839            let epoll_object = EpollFileObject::new_file(locked, &current_task);
1840            let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
1841            let event = EpollEvent::new(FdEvents::POLLIN, 0);
1842            epoll_file
1843                .add(locked, &current_task, &pipe, &epoll_object, event)
1844                .expect("poll_file.add");
1845
1846            let fds = epoll_file
1847                .wait(locked, &current_task, 1, zx::MonotonicInstant::ZERO)
1848                .expect("wait");
1849            assert!(fds.is_empty());
1850
1851            assert_eq!(server_zxio.write(&[0]).expect("write"), 1);
1852
1853            assert_eq!(
1854                pipe.query_events(locked, &current_task),
1855                Ok(FdEvents::POLLOUT
1856                    | FdEvents::POLLWRNORM
1857                    | FdEvents::POLLIN
1858                    | FdEvents::POLLRDNORM)
1859            );
1860            let fds = epoll_file
1861                .wait(locked, &current_task, 1, zx::MonotonicInstant::ZERO)
1862                .expect("wait");
1863            assert_eq!(fds.len(), 1);
1864
1865            assert_eq!(
1866                pipe.read(locked, &current_task, &mut VecOutputBuffer::new(64)).expect("read"),
1867                1
1868            );
1869
1870            assert_eq!(
1871                pipe.query_events(locked, &current_task),
1872                Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
1873            );
1874            let fds = epoll_file
1875                .wait(locked, &current_task, 1, zx::MonotonicInstant::ZERO)
1876                .expect("wait");
1877            assert!(fds.is_empty());
1878        })
1879        .await;
1880    }
1881
1882    #[::fuchsia::test]
1883    async fn test_new_remote_directory() {
1884        spawn_kernel_and_run(async |locked, current_task| {
1885            let (server, client) = zx::Channel::create();
1886            fdio::open("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE, server)
1887                .expect("failed to open /pkg");
1888
1889            let fd = new_remote_file(locked, &current_task, client.into(), OpenFlags::RDWR)
1890                .expect("new_remote_file");
1891            assert!(fd.node().is_dir());
1892            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
1893        })
1894        .await;
1895    }
1896
1897    #[::fuchsia::test]
1898    async fn test_new_remote_file() {
1899        spawn_kernel_and_run(async |locked, current_task| {
1900            let (server, client) = zx::Channel::create();
1901            fdio::open("/pkg/meta/contents", fio::PERM_READABLE, server)
1902                .expect("failed to open /pkg/meta/contents");
1903
1904            let fd = new_remote_file(locked, &current_task, client.into(), OpenFlags::RDONLY)
1905                .expect("new_remote_file");
1906            assert!(!fd.node().is_dir());
1907            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
1908        })
1909        .await;
1910    }
1911
1912    #[::fuchsia::test]
1913    async fn test_new_remote_counter() {
1914        spawn_kernel_and_run(async |locked, current_task| {
1915            let counter = zx::Counter::create();
1916
1917            let fd = new_remote_file(locked, &current_task, counter.into(), OpenFlags::RDONLY)
1918                .expect("new_remote_file");
1919            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
1920        })
1921        .await;
1922    }
1923
1924    #[::fuchsia::test]
1925    async fn test_new_remote_vmo() {
1926        spawn_kernel_and_run(async |locked, current_task| {
1927            let vmo = zx::Vmo::create(*PAGE_SIZE).expect("Vmo::create");
1928            let fd = new_remote_file(locked, &current_task, vmo.into(), OpenFlags::RDWR)
1929                .expect("new_remote_file");
1930            assert!(!fd.node().is_dir());
1931            assert!(fd.to_handle(&current_task).expect("to_handle").is_some());
1932        })
1933        .await;
1934    }
1935
1936    #[::fuchsia::test(threads = 2)]
1937    async fn test_symlink() {
1938        let fixture = TestFixture::new().await;
1939        let (server, client) = zx::Channel::create();
1940        fixture.root().clone(server.into()).expect("clone failed");
1941
1942        const LINK_PATH: &'static str = "symlink";
1943        const LINK_TARGET: &'static str = "私は「UTF8」です";
1944        // We expect the reported size of the symlink to be the length of the target, in bytes,
1945        // *without* a null terminator. Most Linux systems assume UTF-8 encoding.
1946        const LINK_SIZE: usize = 22;
1947        assert_eq!(LINK_SIZE, LINK_TARGET.len());
1948
1949        spawn_kernel_and_run(async move |locked, current_task| {
1950            let kernel = current_task.kernel();
1951            let fs = RemoteFs::new_fs(
1952                locked,
1953                &kernel,
1954                client,
1955                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
1956                fio::PERM_READABLE | fio::PERM_WRITABLE,
1957            )
1958            .expect("new_fs failed");
1959            let ns = Namespace::new(fs);
1960            let root = ns.root();
1961            let symlink_node = root
1962                .create_symlink(locked, &current_task, LINK_PATH.into(), LINK_TARGET.into())
1963                .expect("symlink failed");
1964            assert_matches!(&*symlink_node.entry.node.info(), FsNodeInfo { size: LINK_SIZE, .. });
1965
1966            let mut context = LookupContext::new(SymlinkMode::NoFollow);
1967            let child = root
1968                .lookup_child(locked, &current_task, &mut context, "symlink".into())
1969                .expect("lookup_child failed");
1970
1971            match child.readlink(locked, &current_task).expect("readlink failed") {
1972                SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
1973                SymlinkTarget::Node(_) => panic!("readlink returned SymlinkTarget::Node"),
1974            }
1975            // Ensure the size stat reports matches what is expected.
1976            let stat_result = child.entry.node.stat(locked, &current_task).expect("stat failed");
1977            assert_eq!(stat_result.st_size as usize, LINK_SIZE);
1978        })
1979        .await;
1980
1981        // Simulate a second run to ensure the symlink was persisted correctly.
1982        let fixture = TestFixture::open(
1983            fixture.close().await,
1984            TestFixtureOptions { format: false, ..Default::default() },
1985        )
1986        .await;
1987        let (server, client) = zx::Channel::create();
1988        fixture.root().clone(server.into()).expect("clone failed after remount");
1989
1990        spawn_kernel_and_run(async move |locked, current_task| {
1991            let kernel = current_task.kernel();
1992            let fs = RemoteFs::new_fs(
1993                locked,
1994                &kernel,
1995                client,
1996                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
1997                fio::PERM_READABLE | fio::PERM_WRITABLE,
1998            )
1999            .expect("new_fs failed after remount");
2000            let ns = Namespace::new(fs);
2001            let root = ns.root();
2002            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2003            let child = root
2004                .lookup_child(locked, &current_task, &mut context, "symlink".into())
2005                .expect("lookup_child failed after remount");
2006
2007            match child.readlink(locked, &current_task).expect("readlink failed after remount") {
2008                SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2009                SymlinkTarget::Node(_) => {
2010                    panic!("readlink returned SymlinkTarget::Node after remount")
2011                }
2012            }
2013            // Ensure the size stat reports matches what is expected.
2014            let stat_result =
2015                child.entry.node.stat(locked, &current_task).expect("stat failed after remount");
2016            assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2017        })
2018        .await;
2019
2020        fixture.close().await;
2021    }
2022
2023    #[::fuchsia::test]
2024    async fn test_mode_uid_gid_and_dev_persists() {
2025        const FILE_MODE: FileMode = mode!(IFREG, 0o467);
2026        const DIR_MODE: FileMode = mode!(IFDIR, 0o647);
2027        const BLK_MODE: FileMode = mode!(IFBLK, 0o746);
2028
2029        let fixture = TestFixture::new().await;
2030        let (server, client) = zx::Channel::create();
2031        fixture.root().clone(server.into()).expect("clone failed");
2032
2033        // Simulate a first run of starnix.
2034        spawn_kernel_and_run(async move |locked, current_task| {
2035            let kernel = current_task.kernel();
2036            current_task.set_creds(Credentials {
2037                euid: 1,
2038                fsuid: 1,
2039                egid: 2,
2040                fsgid: 2,
2041                ..current_task.current_creds()
2042            });
2043            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2044            let fs = RemoteFs::new_fs(
2045                locked,
2046                &kernel,
2047                client,
2048                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2049                rights,
2050            )
2051            .expect("new_fs failed");
2052            let ns = Namespace::new(fs);
2053            current_task.fs().set_umask(FileMode::from_bits(0));
2054            ns.root()
2055                .create_node(locked, &current_task, "file".into(), FILE_MODE, DeviceType::NONE)
2056                .expect("create_node failed");
2057            ns.root()
2058                .create_node(locked, &current_task, "dir".into(), DIR_MODE, DeviceType::NONE)
2059                .expect("create_node failed");
2060            ns.root()
2061                .create_node(locked, &current_task, "dev".into(), BLK_MODE, DeviceType::RANDOM)
2062                .expect("create_node failed");
2063        })
2064        .await;
2065
2066        // Simulate a second run.
2067        let fixture = TestFixture::open(
2068            fixture.close().await,
2069            TestFixtureOptions { format: false, ..Default::default() },
2070        )
2071        .await;
2072
2073        let (server, client) = zx::Channel::create();
2074        fixture.root().clone(server.into()).expect("clone failed");
2075
2076        spawn_kernel_and_run(async move |locked, current_task| {
2077            let kernel = current_task.kernel();
2078            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2079            let fs = RemoteFs::new_fs(
2080                locked,
2081                &kernel,
2082                client,
2083                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2084                rights,
2085            )
2086            .expect("new_fs failed");
2087            let ns = Namespace::new(fs);
2088            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2089            let child = ns
2090                .root()
2091                .lookup_child(locked, &current_task, &mut context, "file".into())
2092                .expect("lookup_child failed");
2093            assert_matches!(
2094                &*child.entry.node.info(),
2095                FsNodeInfo { mode: FILE_MODE, uid: 1, gid: 2, rdev: DeviceType::NONE, .. }
2096            );
2097            let child = ns
2098                .root()
2099                .lookup_child(locked, &current_task, &mut context, "dir".into())
2100                .expect("lookup_child failed");
2101            assert_matches!(
2102                &*child.entry.node.info(),
2103                FsNodeInfo { mode: DIR_MODE, uid: 1, gid: 2, rdev: DeviceType::NONE, .. }
2104            );
2105            let child = ns
2106                .root()
2107                .lookup_child(locked, &current_task, &mut context, "dev".into())
2108                .expect("lookup_child failed");
2109            assert_matches!(
2110                &*child.entry.node.info(),
2111                FsNodeInfo { mode: BLK_MODE, uid: 1, gid: 2, rdev: DeviceType::RANDOM, .. }
2112            );
2113        })
2114        .await;
2115        fixture.close().await;
2116    }
2117
2118    #[::fuchsia::test]
2119    async fn test_dot_dot_inode_numbers() {
2120        let fixture = TestFixture::new().await;
2121        let (server, client) = zx::Channel::create();
2122        fixture.root().clone(server.into()).expect("clone failed");
2123
2124        const MODE: FileMode = FileMode::from_bits(FileMode::IFDIR.bits() | 0o777);
2125
2126        spawn_kernel_and_run(async |locked, current_task| {
2127            let kernel = current_task.kernel();
2128            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2129            let fs = RemoteFs::new_fs(
2130                locked,
2131                &kernel,
2132                client,
2133                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2134                rights,
2135            )
2136            .expect("new_fs failed");
2137            let ns = Namespace::new(fs);
2138            current_task.fs().set_umask(FileMode::from_bits(0));
2139            let sub_dir1 = ns
2140                .root()
2141                .create_node(locked, &current_task, "dir".into(), MODE, DeviceType::NONE)
2142                .expect("create_node failed");
2143            let sub_dir2 = sub_dir1
2144                .create_node(locked, &current_task, "dir".into(), MODE, DeviceType::NONE)
2145                .expect("create_node failed");
2146
2147            let dir_handle = ns
2148                .root()
2149                .entry
2150                .open_anonymous(locked, &current_task, OpenFlags::RDONLY)
2151                .expect("open failed");
2152
2153            #[derive(Default)]
2154            struct Sink {
2155                offset: off_t,
2156                dot_dot_inode_num: u64,
2157            }
2158            impl DirentSink for Sink {
2159                fn add(
2160                    &mut self,
2161                    inode_num: ino_t,
2162                    offset: off_t,
2163                    entry_type: DirectoryEntryType,
2164                    name: &FsStr,
2165                ) -> Result<(), Errno> {
2166                    if name == ".." {
2167                        self.dot_dot_inode_num = inode_num;
2168                        assert_eq!(entry_type, DirectoryEntryType::DIR);
2169                    }
2170                    self.offset = offset;
2171                    Ok(())
2172                }
2173                fn offset(&self) -> off_t {
2174                    self.offset
2175                }
2176            }
2177            let mut sink = Sink::default();
2178            dir_handle.readdir(locked, &current_task, &mut sink).expect("readdir failed");
2179
2180            // inode_num for .. for the root should be the same as root.
2181            assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2182
2183            let dir_handle = sub_dir1
2184                .entry
2185                .open_anonymous(locked, &current_task, OpenFlags::RDONLY)
2186                .expect("open failed");
2187            let mut sink = Sink::default();
2188            dir_handle.readdir(locked, &current_task, &mut sink).expect("readdir failed");
2189
2190            // inode_num for .. for the first sub directory should be the same as root.
2191            assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2192
2193            let dir_handle = sub_dir2
2194                .entry
2195                .open_anonymous(locked, &current_task, OpenFlags::RDONLY)
2196                .expect("open failed");
2197            let mut sink = Sink::default();
2198            dir_handle.readdir(locked, &current_task, &mut sink).expect("readdir failed");
2199
2200            // inode_num for .. for the second subdir should be the first subdir.
2201            assert_eq!(sink.dot_dot_inode_num, sub_dir1.entry.node.ino);
2202        })
2203        .await;
2204        fixture.close().await;
2205    }
2206
2207    #[::fuchsia::test]
2208    async fn test_remote_special_node() {
2209        let fixture = TestFixture::new().await;
2210        let (server, client) = zx::Channel::create();
2211        fixture.root().clone(server.into()).expect("clone failed");
2212
2213        const FIFO_MODE: FileMode = FileMode::from_bits(FileMode::IFIFO.bits() | 0o777);
2214        const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2215
2216        spawn_kernel_and_run(async |locked, current_task| {
2217            let kernel = current_task.kernel();
2218            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2219            let fs = RemoteFs::new_fs(
2220                locked,
2221                &kernel,
2222                client,
2223                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2224                rights,
2225            )
2226            .expect("new_fs failed");
2227            let ns = Namespace::new(fs);
2228            current_task.fs().set_umask(FileMode::from_bits(0));
2229            let root = ns.root();
2230
2231            // Create RemoteSpecialNode (e.g. FIFO)
2232            root.create_node(locked, &current_task, "fifo".into(), FIFO_MODE, DeviceType::NONE)
2233                .expect("create_node failed");
2234            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2235            let fifo_node = root
2236                .lookup_child(locked, &current_task, &mut context, "fifo".into())
2237                .expect("lookup_child failed");
2238
2239            // Test that we get expected behaviour for RemoteSpecialNode operation, e.g.
2240            // test that truncate should return EINVAL
2241            match fifo_node.truncate(locked, &current_task, 0) {
2242                Ok(_) => {
2243                    panic!("truncate passed for special node")
2244                }
2245                Err(errno) if errno == EINVAL => {}
2246                Err(e) => {
2247                    panic!("truncate failed with error {:?}", e)
2248                }
2249            };
2250
2251            // Create regular RemoteNode
2252            root.create_node(locked, &current_task, "file".into(), REG_MODE, DeviceType::NONE)
2253                .expect("create_node failed");
2254            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2255            let reg_node = root
2256                .lookup_child(locked, &current_task, &mut context, "file".into())
2257                .expect("lookup_child failed");
2258
2259            // We should be able to perform truncate on regular files
2260            reg_node.truncate(locked, &current_task, 0).expect("truncate failed");
2261        })
2262        .await;
2263        fixture.close().await;
2264    }
2265
2266    #[::fuchsia::test]
2267    async fn test_hard_link() {
2268        let fixture = TestFixture::new().await;
2269        let (server, client) = zx::Channel::create();
2270        fixture.root().clone(server.into()).expect("clone failed");
2271
2272        spawn_kernel_and_run(async move |locked, current_task| {
2273            let kernel = current_task.kernel();
2274            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2275            let fs = RemoteFs::new_fs(
2276                locked,
2277                &kernel,
2278                client,
2279                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2280                rights,
2281            )
2282            .expect("new_fs failed");
2283            let ns = Namespace::new(fs);
2284            current_task.fs().set_umask(FileMode::from_bits(0));
2285            let node = ns
2286                .root()
2287                .create_node(
2288                    locked,
2289                    &current_task,
2290                    "file1".into(),
2291                    mode!(IFREG, 0o666),
2292                    DeviceType::NONE,
2293                )
2294                .expect("create_node failed");
2295            ns.root()
2296                .entry
2297                .node
2298                .link(locked, &current_task, &ns.root().mount, "file2".into(), &node.entry.node)
2299                .expect("link failed");
2300        })
2301        .await;
2302
2303        let fixture = TestFixture::open(
2304            fixture.close().await,
2305            TestFixtureOptions { format: false, ..Default::default() },
2306        )
2307        .await;
2308
2309        let (server, client) = zx::Channel::create();
2310        fixture.root().clone(server.into()).expect("clone failed");
2311
2312        spawn_kernel_and_run(async move |locked, current_task| {
2313            let kernel = current_task.kernel();
2314            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2315            let fs = RemoteFs::new_fs(
2316                locked,
2317                &kernel,
2318                client,
2319                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2320                rights,
2321            )
2322            .expect("new_fs failed");
2323            let ns = Namespace::new(fs);
2324            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2325            let child1 = ns
2326                .root()
2327                .lookup_child(locked, &current_task, &mut context, "file1".into())
2328                .expect("lookup_child failed");
2329            let child2 = ns
2330                .root()
2331                .lookup_child(locked, &current_task, &mut context, "file2".into())
2332                .expect("lookup_child failed");
2333            assert!(Arc::ptr_eq(&child1.entry.node, &child2.entry.node));
2334        })
2335        .await;
2336        fixture.close().await;
2337    }
2338
2339    #[::fuchsia::test]
2340    async fn test_lookup_on_fsverity_enabled_file() {
2341        let fixture = TestFixture::new().await;
2342        let (server, client) = zx::Channel::create();
2343        fixture.root().clone(server.into()).expect("clone failed");
2344
2345        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2346
2347        spawn_kernel_and_run(async move |locked, current_task| {
2348            let kernel = current_task.kernel();
2349            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2350            let fs = RemoteFs::new_fs(
2351                locked,
2352                &kernel,
2353                client,
2354                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2355                rights,
2356            )
2357            .expect("new_fs failed");
2358            let ns = Namespace::new(fs);
2359            current_task.fs().set_umask(FileMode::from_bits(0));
2360            let file = ns
2361                .root()
2362                .create_node(locked, &current_task, "file".into(), MODE, DeviceType::NONE)
2363                .expect("create_node failed");
2364            // Enable verity on the file.
2365            let desc = fsverity_descriptor {
2366                version: 1,
2367                hash_algorithm: 1,
2368                salt_size: 32,
2369                log_blocksize: 12,
2370                ..Default::default()
2371            };
2372            file.entry.node.ops().enable_fsverity(&desc).expect("enable fsverity failed");
2373        })
2374        .await;
2375
2376        // Tear down the kernel and open the file again. The file should no longer be cached.
2377        // Test that lookup works as expected for an fsverity-enabled file.
2378        let fixture = TestFixture::open(
2379            fixture.close().await,
2380            TestFixtureOptions { format: false, ..Default::default() },
2381        )
2382        .await;
2383        let (server, client) = zx::Channel::create();
2384        fixture.root().clone(server.into()).expect("clone failed");
2385
2386        spawn_kernel_and_run(async move |locked, current_task| {
2387            let kernel = current_task.kernel();
2388            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2389            let fs = RemoteFs::new_fs(
2390                locked,
2391                &kernel,
2392                client,
2393                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2394                rights,
2395            )
2396            .expect("new_fs failed");
2397            let ns = Namespace::new(fs);
2398            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2399            let _child = ns
2400                .root()
2401                .lookup_child(locked, &current_task, &mut context, "file".into())
2402                .expect("lookup_child failed");
2403        })
2404        .await;
2405        fixture.close().await;
2406    }
2407
2408    #[::fuchsia::test]
2409    async fn test_update_attributes_persists() {
2410        let fixture = TestFixture::new().await;
2411        let (server, client) = zx::Channel::create();
2412        fixture.root().clone(server.into()).expect("clone failed");
2413
2414        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2415
2416        spawn_kernel_and_run(async move |locked, current_task| {
2417            let kernel = current_task.kernel();
2418            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2419            let fs = RemoteFs::new_fs(
2420                locked,
2421                &kernel,
2422                client,
2423                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2424                rights,
2425            )
2426            .expect("new_fs failed");
2427            let ns = Namespace::new(fs);
2428            current_task.fs().set_umask(FileMode::from_bits(0));
2429            let file = ns
2430                .root()
2431                .create_node(locked, &current_task, "file".into(), MODE, DeviceType::NONE)
2432                .expect("create_node failed");
2433            // Change the mode, this change should persist
2434            file.entry
2435                .node
2436                .chmod(locked, &current_task, &file.mount, MODE | FileMode::ALLOW_ALL)
2437                .expect("chmod failed");
2438        })
2439        .await;
2440
2441        // Tear down the kernel and open the file again. Check that changes persisted.
2442        let fixture = TestFixture::open(
2443            fixture.close().await,
2444            TestFixtureOptions { format: false, ..Default::default() },
2445        )
2446        .await;
2447        let (server, client) = zx::Channel::create();
2448        fixture.root().clone(server.into()).expect("clone failed");
2449
2450        spawn_kernel_and_run(async move |locked, current_task| {
2451            let kernel = current_task.kernel();
2452            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2453            let fs = RemoteFs::new_fs(
2454                locked,
2455                &kernel,
2456                client,
2457                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2458                rights,
2459            )
2460            .expect("new_fs failed");
2461            let ns = Namespace::new(fs);
2462            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2463            let child = ns
2464                .root()
2465                .lookup_child(locked, &current_task, &mut context, "file".into())
2466                .expect("lookup_child failed");
2467            assert_eq!(child.entry.node.info().mode, MODE | FileMode::ALLOW_ALL);
2468        })
2469        .await;
2470        fixture.close().await;
2471    }
2472
2473    #[::fuchsia::test]
2474    async fn test_statfs() {
2475        let fixture = TestFixture::new().await;
2476        let (server, client) = zx::Channel::create();
2477        fixture.root().clone(server.into()).expect("clone failed");
2478
2479        spawn_kernel_and_run(async move |locked, current_task| {
2480            let kernel = current_task.kernel();
2481            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2482            let fs = RemoteFs::new_fs(
2483                locked,
2484                &kernel,
2485                client,
2486                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2487                rights,
2488            )
2489            .expect("new_fs failed");
2490
2491            let statfs = fs.statfs(locked, &current_task).expect("statfs failed");
2492            assert!(statfs.f_type != 0);
2493            assert!(statfs.f_bsize > 0);
2494            assert!(statfs.f_blocks > 0);
2495            assert!(statfs.f_bfree > 0 && statfs.f_bfree <= statfs.f_blocks);
2496            assert!(statfs.f_files > 0);
2497            assert!(statfs.f_ffree > 0 && statfs.f_ffree <= statfs.f_files);
2498            assert!(statfs.f_fsid.val[0] != 0 || statfs.f_fsid.val[1] != 0);
2499            assert!(statfs.f_namelen > 0);
2500            assert!(statfs.f_frsize > 0);
2501        })
2502        .await;
2503
2504        fixture.close().await;
2505    }
2506
2507    #[::fuchsia::test]
2508    async fn test_allocate() {
2509        let fixture = TestFixture::new().await;
2510        let (server, client) = zx::Channel::create();
2511        fixture.root().clone(server.into()).expect("clone failed");
2512
2513        spawn_kernel_and_run(async move |locked, current_task| {
2514            let kernel = current_task.kernel();
2515            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2516            let fs = RemoteFs::new_fs(
2517                locked,
2518                &kernel,
2519                client,
2520                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2521                rights,
2522            )
2523            .expect("new_fs failed");
2524            let ns = Namespace::new(fs);
2525            current_task.fs().set_umask(FileMode::from_bits(0));
2526            let root = ns.root();
2527
2528            const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2529            root.create_node(locked, &current_task, "file".into(), REG_MODE, DeviceType::NONE)
2530                .expect("create_node failed");
2531            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2532            let reg_node = root
2533                .lookup_child(locked, &current_task, &mut context, "file".into())
2534                .expect("lookup_child failed");
2535
2536            reg_node
2537                .entry
2538                .node
2539                .fallocate(locked, &current_task, FallocMode::Allocate { keep_size: false }, 0, 20)
2540                .expect("truncate failed");
2541        })
2542        .await;
2543        fixture.close().await;
2544    }
2545
2546    #[::fuchsia::test]
2547    async fn test_allocate_overflow() {
2548        let fixture = TestFixture::new().await;
2549        let (server, client) = zx::Channel::create();
2550        fixture.root().clone(server.into()).expect("clone failed");
2551
2552        spawn_kernel_and_run(async move |locked, current_task| {
2553            let kernel = current_task.kernel();
2554            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2555            let fs = RemoteFs::new_fs(
2556                locked,
2557                &kernel,
2558                client,
2559                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2560                rights,
2561            )
2562            .expect("new_fs failed");
2563            let ns = Namespace::new(fs);
2564            current_task.fs().set_umask(FileMode::from_bits(0));
2565            let root = ns.root();
2566
2567            const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2568            root.create_node(locked, &current_task, "file".into(), REG_MODE, DeviceType::NONE)
2569                .expect("create_node failed");
2570            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2571            let reg_node = root
2572                .lookup_child(locked, &current_task, &mut context, "file".into())
2573                .expect("lookup_child failed");
2574
2575            reg_node
2576                .entry
2577                .node
2578                .fallocate(
2579                    locked,
2580                    &current_task,
2581                    FallocMode::Allocate { keep_size: false },
2582                    1,
2583                    u64::MAX,
2584                )
2585                .expect_err("truncate unexpectedly passed");
2586        })
2587        .await;
2588        fixture.close().await;
2589    }
2590
2591    #[::fuchsia::test]
2592    async fn test_time_modify_persists() {
2593        let fixture = TestFixture::new().await;
2594        let (server, client) = zx::Channel::create();
2595        fixture.root().clone(server.into()).expect("clone failed");
2596
2597        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2598
2599        let last_modified = spawn_kernel_and_run(async move |locked, current_task| {
2600            let kernel = current_task.kernel();
2601            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2602            let fs = RemoteFs::new_fs(
2603                locked,
2604                &kernel,
2605                client,
2606                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2607                rights,
2608            )
2609            .expect("new_fs failed");
2610            let ns: Arc<Namespace> = Namespace::new(fs);
2611            current_task.fs().set_umask(FileMode::from_bits(0));
2612            let child = ns
2613                .root()
2614                .create_node(locked, &current_task, "file".into(), MODE, DeviceType::NONE)
2615                .expect("create_node failed");
2616            // Write to file (this should update mtime (time_modify))
2617            let file = child
2618                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
2619                .expect("open failed");
2620            // Call `fetch_and_refresh_info(..)` to refresh `time_modify` with the time managed by the
2621            // underlying filesystem
2622            let time_before_write = child
2623                .entry
2624                .node
2625                .fetch_and_refresh_info(locked, &current_task)
2626                .expect("fetch_and_refresh_info failed")
2627                .time_modify;
2628            let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
2629            let written = file
2630                .write(locked, &current_task, &mut VecInputBuffer::new(&write_bytes))
2631                .expect("write failed");
2632            assert_eq!(written, write_bytes.len());
2633            let last_modified = child
2634                .entry
2635                .node
2636                .fetch_and_refresh_info(locked, &current_task)
2637                .expect("fetch_and_refresh_info failed")
2638                .time_modify;
2639            assert!(last_modified > time_before_write);
2640            last_modified
2641        })
2642        .await;
2643
2644        // Tear down the kernel and open the file again. Check that modification time is when we
2645        // last modified the contents of the file
2646        let fixture = TestFixture::open(
2647            fixture.close().await,
2648            TestFixtureOptions { format: false, ..Default::default() },
2649        )
2650        .await;
2651        let (server, client) = zx::Channel::create();
2652        fixture.root().clone(server.into()).expect("clone failed");
2653        let refreshed_modified_time = spawn_kernel_and_run(async move |locked, current_task| {
2654            let kernel = current_task.kernel();
2655            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2656            let fs = RemoteFs::new_fs(
2657                locked,
2658                &kernel,
2659                client,
2660                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2661                rights,
2662            )
2663            .expect("new_fs failed");
2664            let ns = Namespace::new(fs);
2665            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2666            let child = ns
2667                .root()
2668                .lookup_child(locked, &current_task, &mut context, "file".into())
2669                .expect("lookup_child failed");
2670            let last_modified = child
2671                .entry
2672                .node
2673                .fetch_and_refresh_info(locked, &current_task)
2674                .expect("fetch_and_refresh_info failed")
2675                .time_modify;
2676            last_modified
2677        })
2678        .await;
2679        assert_eq!(last_modified, refreshed_modified_time);
2680
2681        fixture.close().await;
2682    }
2683
2684    #[::fuchsia::test]
2685    async fn test_update_atime_mtime() {
2686        let fixture = TestFixture::new().await;
2687        let (server, client) = zx::Channel::create();
2688        fixture.root().clone(server.into()).expect("clone failed");
2689
2690        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2691
2692        spawn_kernel_and_run(async move |locked, current_task| {
2693            let kernel = current_task.kernel();
2694            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2695            let fs = RemoteFs::new_fs(
2696                locked,
2697                &kernel,
2698                client,
2699                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2700                rights,
2701            )
2702            .expect("new_fs failed");
2703            let ns: Arc<Namespace> = Namespace::new(fs);
2704            current_task.fs().set_umask(FileMode::from_bits(0));
2705            let child = ns
2706                .root()
2707                .create_node(locked, &current_task, "file".into(), MODE, DeviceType::NONE)
2708                .expect("create_node failed");
2709
2710            let info_original = child
2711                .entry
2712                .node
2713                .fetch_and_refresh_info(locked, &current_task)
2714                .expect("fetch_and_refresh_info failed")
2715                .clone();
2716
2717            child
2718                .entry
2719                .node
2720                .update_atime_mtime(
2721                    locked,
2722                    &current_task,
2723                    &child.mount,
2724                    TimeUpdateType::Time(UtcInstant::from_nanos(30)),
2725                    TimeUpdateType::Omit,
2726                )
2727                .expect("update_atime_mtime failed");
2728            let info_after_update = child
2729                .entry
2730                .node
2731                .fetch_and_refresh_info(locked, &current_task)
2732                .expect("fetch_and_refresh_info failed")
2733                .clone();
2734            assert_eq!(info_after_update.time_modify, info_original.time_modify);
2735            assert_eq!(info_after_update.time_access, UtcInstant::from_nanos(30));
2736
2737            child
2738                .entry
2739                .node
2740                .update_atime_mtime(
2741                    locked,
2742                    &current_task,
2743                    &child.mount,
2744                    TimeUpdateType::Omit,
2745                    TimeUpdateType::Time(UtcInstant::from_nanos(50)),
2746                )
2747                .expect("update_atime_mtime failed");
2748            let info_after_update2 = child
2749                .entry
2750                .node
2751                .fetch_and_refresh_info(locked, &current_task)
2752                .expect("fetch_and_refresh_info failed")
2753                .clone();
2754            assert_eq!(info_after_update2.time_modify, UtcInstant::from_nanos(50));
2755            assert_eq!(info_after_update2.time_access, UtcInstant::from_nanos(30));
2756        })
2757        .await;
2758        fixture.close().await;
2759    }
2760
2761    #[::fuchsia::test]
2762    async fn test_write_updates_mtime_ctime() {
2763        let fixture = TestFixture::new().await;
2764        let (server, client) = zx::Channel::create();
2765        fixture.root().clone(server.into()).expect("clone failed");
2766
2767        const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2768
2769        spawn_kernel_and_run(async move |locked, current_task| {
2770            let kernel = current_task.kernel();
2771            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2772            let fs = RemoteFs::new_fs(
2773                locked,
2774                &kernel,
2775                client,
2776                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2777                rights,
2778            )
2779            .expect("new_fs failed");
2780            let ns: Arc<Namespace> = Namespace::new(fs);
2781            current_task.fs().set_umask(FileMode::from_bits(0));
2782            let child = ns
2783                .root()
2784                .create_node(locked, &current_task, "file".into(), MODE, DeviceType::NONE)
2785                .expect("create_node failed");
2786            let file = child
2787                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
2788                .expect("open failed");
2789            // Call `fetch_and_refresh_info(..)` to refresh ctime and mtime with the time managed by the
2790            // underlying filesystem
2791            let (ctime_before_write, mtime_before_write) = {
2792                let info = child
2793                    .entry
2794                    .node
2795                    .fetch_and_refresh_info(locked, &current_task)
2796                    .expect("fetch_and_refresh_info failed");
2797                (info.time_status_change, info.time_modify)
2798            };
2799
2800            // Writing to a file should update ctime and mtime
2801            let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
2802            let written = file
2803                .write(locked, &current_task, &mut VecInputBuffer::new(&write_bytes))
2804                .expect("write failed");
2805            assert_eq!(written, write_bytes.len());
2806
2807            // As Fxfs, the underlying filesystem in this test, can manage file timestamps,
2808            // we should not see an update in mtime and ctime without first refreshing the node with
2809            // the metadata from Fxfs.
2810            let (ctime_after_write_no_refresh, mtime_after_write_no_refresh) = {
2811                let info = child.entry.node.info();
2812                (info.time_status_change, info.time_modify)
2813            };
2814            assert_eq!(ctime_after_write_no_refresh, ctime_before_write);
2815            assert_eq!(mtime_after_write_no_refresh, mtime_before_write);
2816
2817            // Refresh information, we should see `info` with mtime and ctime from the remote
2818            // filesystem (assume this is true if the new timestamp values are greater than the ones
2819            // without the refresh).
2820            let (ctime_after_write_refresh, mtime_after_write_refresh) = {
2821                let info = child
2822                    .entry
2823                    .node
2824                    .fetch_and_refresh_info(locked, &current_task)
2825                    .expect("fetch_and_refresh_info failed");
2826                (info.time_status_change, info.time_modify)
2827            };
2828            assert_eq!(ctime_after_write_refresh, mtime_after_write_refresh);
2829            assert!(ctime_after_write_refresh > ctime_after_write_no_refresh);
2830        })
2831        .await;
2832        fixture.close().await;
2833    }
2834
2835    #[::fuchsia::test]
2836    async fn test_casefold_persists() {
2837        let fixture = TestFixture::new().await;
2838        let (server, client) = zx::Channel::create();
2839        fixture.root().clone(server.into()).expect("clone failed");
2840
2841        spawn_kernel_and_run(async move |locked, current_task| {
2842            let kernel = current_task.kernel();
2843            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2844            let fs = RemoteFs::new_fs(
2845                locked,
2846                &kernel,
2847                client,
2848                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2849                rights,
2850            )
2851            .expect("new_fs failed");
2852            let ns: Arc<Namespace> = Namespace::new(fs);
2853            let child = ns
2854                .root()
2855                .create_node(
2856                    locked,
2857                    &current_task,
2858                    "dir".into(),
2859                    FileMode::ALLOW_ALL.with_type(FileMode::IFDIR),
2860                    DeviceType::NONE,
2861                )
2862                .expect("create_node failed");
2863            child
2864                .entry
2865                .node
2866                .update_attributes(locked, &current_task, |info| {
2867                    info.casefold = true;
2868                    Ok(())
2869                })
2870                .expect("enable casefold")
2871        })
2872        .await;
2873
2874        // Tear down the kernel and open the dir again. Check that casefold is preserved.
2875        let fixture = TestFixture::open(
2876            fixture.close().await,
2877            TestFixtureOptions { format: false, ..Default::default() },
2878        )
2879        .await;
2880        let (server, client) = zx::Channel::create();
2881        fixture.root().clone(server.into()).expect("clone failed");
2882        let casefold = spawn_kernel_and_run(async move |locked, current_task| {
2883            let kernel = current_task.kernel();
2884            let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2885            let fs = RemoteFs::new_fs(
2886                locked,
2887                &kernel,
2888                client,
2889                FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2890                rights,
2891            )
2892            .expect("new_fs failed");
2893            let ns = Namespace::new(fs);
2894            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2895            let child = ns
2896                .root()
2897                .lookup_child(locked, &current_task, &mut context, "dir".into())
2898                .expect("lookup_child failed");
2899            let casefold = child
2900                .entry
2901                .node
2902                .fetch_and_refresh_info(locked, &current_task)
2903                .expect("fetch_and_refresh_info failed")
2904                .casefold;
2905            casefold
2906        })
2907        .await;
2908        assert!(casefold);
2909
2910        fixture.close().await;
2911    }
2912
2913    #[::fuchsia::test]
2914    async fn test_update_time_access_persists() {
2915        const TEST_FILE: &str = "test_file";
2916
2917        let fixture = TestFixture::new().await;
2918        let (server, client) = zx::Channel::create();
2919        fixture.root().clone(server.into()).expect("clone failed");
2920        // Set up file.
2921        let info_after_read = spawn_kernel_and_run(async move |locked, current_task| {
2922            let kernel = current_task.kernel();
2923            let fs = RemoteFs::new_fs(
2924                locked,
2925                &kernel,
2926                client,
2927                FileSystemOptions {
2928                    source: FlyByteStr::new(b"/"),
2929                    flags: MountFlags::RELATIME,
2930                    ..Default::default()
2931                },
2932                fio::PERM_READABLE | fio::PERM_WRITABLE,
2933            )
2934            .expect("new_fs failed");
2935            let ns = Namespace::new_with_flags(fs, MountFlags::RELATIME);
2936            let child = ns
2937                .root()
2938                .open_create_node(
2939                    locked,
2940                    &current_task,
2941                    TEST_FILE.into(),
2942                    FileMode::ALLOW_ALL.with_type(FileMode::IFREG),
2943                    DeviceType::NONE,
2944                    OpenFlags::empty(),
2945                )
2946                .expect("create_node failed");
2947
2948            let file_handle = child
2949                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
2950                .expect("open failed");
2951
2952            // Expect atime to be updated as this is the first file access since the
2953            // last file modification or status change.
2954            file_handle
2955                .read(locked, &current_task, &mut VecOutputBuffer::new(10))
2956                .expect("read failed");
2957
2958            // Call `fetch_and_refresh_info` to persist atime update.
2959            let info_after_read = child
2960                .entry
2961                .node
2962                .fetch_and_refresh_info(locked, &current_task)
2963                .expect("fetch_and_refresh_info failed")
2964                .clone();
2965
2966            info_after_read
2967        })
2968        .await;
2969
2970        // Tear down the kernel and open the file again. The file should no longer be cached.
2971        let fixture = TestFixture::open(
2972            fixture.close().await,
2973            TestFixtureOptions { format: false, ..Default::default() },
2974        )
2975        .await;
2976
2977        let (server, client) = zx::Channel::create();
2978        fixture.root().clone(server.into()).expect("clone failed");
2979
2980        spawn_kernel_and_run(async move |locked, current_task| {
2981            let kernel = current_task.kernel();
2982            let fs = RemoteFs::new_fs(
2983                locked,
2984                &kernel,
2985                client,
2986                FileSystemOptions {
2987                    source: FlyByteStr::new(b"/"),
2988                    flags: MountFlags::RELATIME,
2989                    ..Default::default()
2990                },
2991                fio::PERM_READABLE | fio::PERM_WRITABLE,
2992            )
2993            .expect("new_fs failed");
2994            let ns = Namespace::new_with_flags(fs, MountFlags::RELATIME);
2995            let mut context = LookupContext::new(SymlinkMode::NoFollow);
2996            let child = ns
2997                .root()
2998                .lookup_child(locked, &current_task, &mut context, TEST_FILE.into())
2999                .expect("lookup_child failed");
3000
3001            // Get info - this should be refreshed with info that was persisted before
3002            // we tore down the kernel.
3003            let persisted_info = child
3004                .entry
3005                .node
3006                .fetch_and_refresh_info(locked, &current_task)
3007                .expect("fetch_and_refresh_info failed")
3008                .clone();
3009            assert_eq!(info_after_read.time_access, persisted_info.time_access);
3010        })
3011        .await;
3012        fixture.close().await;
3013    }
3014
3015    #[::fuchsia::test]
3016    async fn test_pending_access_time_updates() {
3017        const TEST_FILE: &str = "test_file";
3018
3019        let fixture = TestFixture::new().await;
3020        let (server, client) = zx::Channel::create();
3021        fixture.root().clone(server.into()).expect("clone failed");
3022
3023        spawn_kernel_and_run(async move |locked, current_task| {
3024            let kernel = current_task.kernel.clone();
3025            let fs = RemoteFs::new_fs(
3026                locked,
3027                &kernel,
3028                client,
3029                FileSystemOptions {
3030                    source: FlyByteStr::new(b"/"),
3031                    flags: MountFlags::RELATIME,
3032                    ..Default::default()
3033                },
3034                fio::PERM_READABLE | fio::PERM_WRITABLE,
3035            )
3036            .expect("new_fs failed");
3037
3038            let ns = Namespace::new_with_flags(fs, MountFlags::RELATIME);
3039            let child = ns
3040                .root()
3041                .open_create_node(
3042                    locked,
3043                    &current_task,
3044                    TEST_FILE.into(),
3045                    FileMode::ALLOW_ALL.with_type(FileMode::IFREG),
3046                    DeviceType::NONE,
3047                    OpenFlags::empty(),
3048                )
3049                .expect("create_node failed");
3050
3051            let file_handle = child
3052                .open(locked, &current_task, OpenFlags::RDWR, AccessCheck::default())
3053                .expect("open failed");
3054
3055            // Expect atime to be updated as this is the first file access since the last
3056            // file modification or status change.
3057            file_handle
3058                .read(locked, &current_task, &mut VecOutputBuffer::new(10))
3059                .expect("read failed");
3060
3061            let atime_after_first_read = child
3062                .entry
3063                .node
3064                .fetch_and_refresh_info(locked, &current_task)
3065                .expect("fetch_and_refresh_info failed")
3066                .time_access;
3067
3068            // Read again (this read will not trigger a persistent atime update if
3069            // filesystem was mounted with atime)
3070            file_handle
3071                .read(locked, &current_task, &mut VecOutputBuffer::new(10))
3072                .expect("read failed");
3073
3074            let atime_after_second_read = child
3075                .entry
3076                .node
3077                .fetch_and_refresh_info(locked, &current_task)
3078                .expect("fetch_and_refresh_info failed")
3079                .time_access;
3080            assert_eq!(atime_after_first_read, atime_after_second_read);
3081
3082            // Do another operation that will update ctime and/or mtime but not atime.
3083            let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3084            let _written = file_handle
3085                .write(locked, &current_task, &mut VecInputBuffer::new(&write_bytes))
3086                .expect("write failed");
3087
3088            // Read again (atime should be updated).
3089            file_handle
3090                .read(locked, &current_task, &mut VecOutputBuffer::new(10))
3091                .expect("read failed");
3092
3093            assert!(
3094                atime_after_second_read
3095                    < child
3096                        .entry
3097                        .node
3098                        .fetch_and_refresh_info(locked, &current_task)
3099                        .expect("fetch_and_refresh_info failed")
3100                        .time_access
3101            );
3102        })
3103        .await;
3104        fixture.close().await;
3105    }
3106}