Skip to main content

starnix_core/vfs/
namespace.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::security;
6use crate::task::{
7    CurrentTask, EventHandler, Kernel, MountsWriteToken, Task, WaitCanceler, Waiter,
8};
9use crate::time::utc;
10use crate::vfs::fs_registry::FsRegistry;
11use crate::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource};
12use crate::vfs::pseudo::simple_file::SimpleFileNode;
13use crate::vfs::socket::{SocketAddress, SocketHandle, UnixSocket};
14use crate::vfs::{
15    CheckAccessReason, DirEntry, DirEntryHandle, FileHandle, FileObject, FileOps, FileSystemHandle,
16    FileSystemOptions, FileWriteGuardMode, FsContext, FsNode, FsNodeHandle, FsNodeOps, FsStr,
17    FsString, PathBuilder, RenameFlags, SymlinkTarget, UnlinkKind, fileops_impl_dataless,
18    fileops_impl_delegate_read_write_and_seek, fileops_impl_nonseekable, fileops_impl_noop_sync,
19    fs_node_impl_not_dir,
20};
21use fuchsia_rcu::{RcuBox, RcuReadScope};
22use fuchsia_rcu_collections::rcu_raw_hash_map::RcuRawHashMap;
23use ref_cast::RefCast;
24use starnix_logging::log_warn;
25use starnix_rcu::RcuHashMap;
26use starnix_sync::{
27    BeforeFsNodeAppend, FileOpsCore, LockDepMutex, LockEqualOrBefore, Locked, NamespaceFlagsLock,
28    Unlocked,
29};
30use starnix_uapi::arc_key::{ArcKey, PtrKey, WeakKey};
31use starnix_uapi::auth::Credentials;
32use starnix_uapi::device_id::DeviceId;
33use starnix_uapi::errors::Errno;
34use starnix_uapi::file_mode::{AccessCheck, FileMode};
35use starnix_uapi::inotify_mask::InotifyMask;
36use starnix_uapi::mount_flags::{
37    AtomicMountpointFlags, FileSystemFlags, MountFlags, MountpointFlags,
38};
39use starnix_uapi::open_flags::OpenFlags;
40use starnix_uapi::unmount_flags::UnmountFlags;
41use starnix_uapi::vfs::{FdEvents, ResolveFlags};
42use starnix_uapi::{NAME_MAX, errno, error};
43use std::borrow::Borrow;
44use std::collections::HashSet;
45use std::fmt;
46use std::hash::{Hash, Hasher};
47use std::ops::{Deref, DerefMut};
48use std::sync::atomic::Ordering;
49use std::sync::{Arc, Weak};
50
51/// A mount namespace.
52///
53/// The namespace records at which entries filesystems are mounted.
54#[derive(Debug)]
55pub struct Namespace {
56    root_mount: MountHandle,
57
58    // Unique ID of this namespace.
59    pub id: u64,
60}
61
62impl Namespace {
63    pub fn new(fs: FileSystemHandle) -> Arc<Namespace> {
64        Self::new_with_flags(fs, MountpointFlags::empty())
65    }
66
67    pub fn new_with_flags(fs: FileSystemHandle, flags: MountpointFlags) -> Arc<Namespace> {
68        let kernel = fs.kernel.upgrade().expect("can't create namespace without a kernel");
69        let mounts_guard = kernel.mounts_lock.lock();
70
71        let root_mount = Mount::new(&mounts_guard, WhatToMount::Fs(fs), flags);
72        Arc::new(Self { root_mount, id: kernel.get_next_namespace_id() })
73    }
74
75    pub fn root(&self) -> NamespaceNode {
76        self.root_mount.root()
77    }
78
79    pub fn kernel(&self) -> Arc<Kernel> {
80        self.root_mount.kernel()
81    }
82
83    pub fn clone_namespace(&self, mounts_guard: &MountsWriteToken) -> Arc<Namespace> {
84        Arc::new(Self {
85            root_mount: self.root_mount.clone_mount_recursive(mounts_guard),
86            id: self.kernel().get_next_namespace_id(),
87        })
88    }
89
90    /// Assuming new_ns is a clone of the namespace that node is from, return the equivalent of
91    /// node in new_ns. If this assumption is violated, returns None.
92    pub fn translate_node(
93        mut node: NamespaceNode,
94        new_ns: &Namespace,
95        _mounts_guard: &MountsWriteToken,
96    ) -> Option<NamespaceNode> {
97        // Collect the list of mountpoints that leads to this node's mount
98        let mut mountpoints = vec![];
99        let mut mount = node.mount;
100        while let Some(mountpoint) = mount.as_ref().and_then(|m| m.mountpoint()) {
101            mountpoints.push(mountpoint.entry);
102            mount = mountpoint.mount;
103        }
104
105        // Follow the same path in the new namespace
106        let scope = RcuReadScope::new();
107        let mut mount = &new_ns.root_mount;
108        for mountpoint in mountpoints.iter().rev() {
109            let next_mount =
110                &mount.relations.get_submount(&scope, ArcKey::ref_cast(mountpoint))?.mount;
111            mount = next_mount;
112        }
113        node.mount = Some(Arc::clone(mount)).into();
114        Some(node)
115    }
116}
117
118impl FsNodeOps for Arc<Namespace> {
119    fs_node_impl_not_dir!();
120
121    fn create_file_ops(
122        &self,
123        _locked: &mut Locked<FileOpsCore>,
124        _node: &FsNode,
125        _current_task: &CurrentTask,
126        _flags: OpenFlags,
127    ) -> Result<Box<dyn FileOps>, Errno> {
128        Ok(Box::new(MountNamespaceFile(self.clone())))
129    }
130}
131
132pub struct MountNamespaceFile(pub Arc<Namespace>);
133
134impl FileOps for MountNamespaceFile {
135    fileops_impl_nonseekable!();
136    fileops_impl_dataless!();
137    fileops_impl_noop_sync!();
138}
139
140/// An empty struct that we use to track the number of active clients for a mount.
141///
142/// Each active client takes a reference to this object. The unmount operation fails
143/// if there are any active clients of the mount.
144type MountClientMarker = Arc<()>;
145
146/// An instance of a filesystem mounted in a namespace.
147///
148/// At a mount, path traversal switches from one filesystem to another.
149/// The client sees a composed directory structure that glues together the
150/// directories from the underlying FsNodes from those filesystems.
151///
152/// The mounts in a namespace form a mount tree, with `mountpoint` pointing to the parent and
153/// `submounts` pointing to the children.
154pub struct Mount {
155    root: DirEntryHandle,
156    fs: FileSystemHandle,
157
158    /// Holds the flags specific to this mount of the underlying filesystem.
159    flags: AtomicMountpointFlags,
160
161    /// Lock used to serialize updates of `flags` to ensure consistency during remount operations.
162    flags_lock: LockDepMutex<(), NamespaceFlagsLock>,
163
164    /// A unique identifier for this mount reported in /proc/pid/mountinfo.
165    id: u64,
166
167    /// A count of the number of active clients.
168    active_client_counter: MountClientMarker,
169
170    /// The namespace node that this mount is mounted on. This is a tuple instead of a
171    /// NamespaceNode because the Mount pointer has to be weak because this is the pointer to the
172    /// parent mount, the parent has a pointer to the children too, and making both strong would be
173    /// a cycle.
174    /// Stores the relationships to other mounts (mountpoint and submounts).
175    /// Both require the `MountsWriteToken` to mutate.
176    relations: MountRelations,
177    // Mount used to contain a Weak<Namespace>. It no longer does because since the mount point
178    // hash was moved from Namespace to Mount, nothing actually uses it. Now that
179    // Namespace::clone_namespace() is implemented in terms of Mount::clone_mount_recursive, it
180    // won't be trivial to add it back. If you end up needing to find a Mount's Namespace, I
181    // recommend turning the mountpoint field into an enum of Mountpoint or Namespace, maybe called
182    // "parent", and then you can traverse up to the top of the tree.
183}
184type MountHandle = Arc<Mount>;
185
186/// Public representation of the mount options.
187#[derive(Clone, Debug)]
188pub struct MountInfo {
189    handle: Option<MountHandle>,
190}
191
192impl MountInfo {
193    /// `MountInfo` for a element that is not tied to a given mount. Mount flags will be considered
194    /// empty.
195    pub fn detached() -> Self {
196        None.into()
197    }
198
199    /// The mount flags of the represented mount.
200    pub fn flags(&self) -> MountFlags {
201        if let Some(handle) = &self.handle {
202            handle.flags()
203        } else {
204            // Consider not mounted node have the NOATIME flags.
205            MountFlags::NOATIME
206        }
207    }
208
209    /// Checks whether this `MountInfo` represents a writable file system mount.
210    pub fn check_readonly_filesystem(&self) -> Result<(), Errno> {
211        if self.flags().contains(MountFlags::RDONLY) {
212            return error!(EROFS);
213        }
214        Ok(())
215    }
216
217    /// Checks whether this `MountInfo` represents an executable file system mount.
218    pub fn check_noexec_filesystem(&self) -> Result<(), Errno> {
219        if self.flags().contains(MountFlags::NOEXEC) {
220            return error!(EACCES);
221        }
222        Ok(())
223    }
224}
225
226impl Deref for MountInfo {
227    type Target = Option<MountHandle>;
228
229    fn deref(&self) -> &Self::Target {
230        &self.handle
231    }
232}
233
234impl DerefMut for MountInfo {
235    fn deref_mut(&mut self) -> &mut Self::Target {
236        &mut self.handle
237    }
238}
239
240impl std::cmp::PartialEq for MountInfo {
241    fn eq(&self, other: &Self) -> bool {
242        self.handle.as_ref().map(Arc::as_ptr) == other.handle.as_ref().map(Arc::as_ptr)
243    }
244}
245
246impl std::cmp::Eq for MountInfo {}
247
248impl Into<MountInfo> for Option<MountHandle> {
249    fn into(self) -> MountInfo {
250        MountInfo { handle: self }
251    }
252}
253
254#[derive(Default)]
255struct MountRelations {
256    /// The parent mount and the directory entry in the parent where this mount is mounted.
257    mountpoint: RcuBox<Option<(Weak<Mount>, DirEntryHandle)>>,
258    /// The active submounts, keyed by the directory entry in this mount where they are mounted.
259    submounts: RcuRawHashMap<ArcKey<DirEntry>, Arc<Submount>>,
260    /// The membership of this mount in its peer group.
261    peer_group: RcuBox<Option<(Arc<PeerGroup>, PtrKey<Mount>)>>,
262    /// The membership of this mount in a PeerGroup's downstream.
263    upstream: RcuBox<Option<(Weak<PeerGroup>, PtrKey<Mount>)>>,
264}
265
266impl MountRelations {
267    fn get_submount<'a>(
268        &'a self,
269        scope: &'a starnix_rcu::RcuReadScope,
270        key: &ArcKey<DirEntry>,
271    ) -> Option<&'a Arc<Submount>> {
272        self.submounts.get(scope, key)
273    }
274
275    fn insert_submount(
276        &self,
277        _guard: &MountsWriteToken,
278        key: ArcKey<DirEntry>,
279        value: Arc<Submount>,
280    ) -> Option<Arc<Submount>> {
281        let scope = starnix_rcu::RcuReadScope::new();
282        // SAFETY: The MountsWriteToken proves we have exclusive write access.
283        let result = unsafe { self.submounts.insert(&scope, key, value) };
284        match result {
285            fuchsia_rcu_collections::rcu_raw_hash_map::InsertionResult::Inserted(_) => None,
286            fuchsia_rcu_collections::rcu_raw_hash_map::InsertionResult::Updated(old) => Some(old),
287        }
288    }
289
290    fn remove_submount(
291        &self,
292        _guard: &MountsWriteToken,
293        key: &ArcKey<DirEntry>,
294    ) -> Result<(), Errno> {
295        // SAFETY: The MountsWriteToken proves we have exclusive write access.
296        let submount = unsafe { self.submounts.remove(key) };
297        if submount.is_some() { Ok(()) } else { error!(EINVAL) }
298    }
299
300    fn iter_submounts<'a>(
301        &'a self,
302        scope: &'a starnix_rcu::RcuReadScope,
303    ) -> impl Iterator<Item = (&'a ArcKey<DirEntry>, &'a Arc<Submount>)> {
304        let mut cursor = self.submounts.cursor(scope);
305        std::iter::from_fn(move || {
306            let current = cursor.current();
307            if current.is_some() {
308                cursor.advance();
309            }
310            current
311        })
312    }
313
314    fn submounts_len(&self) -> usize {
315        self.submounts.len()
316    }
317
318    fn set_mountpoint(
319        &self,
320        _guard: &MountsWriteToken,
321        mountpoint: Option<(Weak<Mount>, DirEntryHandle)>,
322    ) {
323        self.mountpoint.update(mountpoint);
324    }
325
326    fn mountpoint<'a>(
327        &'a self,
328        scope: &'a starnix_rcu::RcuReadScope,
329    ) -> Option<&'a (Weak<Mount>, DirEntryHandle)> {
330        self.mountpoint.as_ref(scope).as_ref()
331    }
332
333    fn take_peer_group(
334        &self,
335        _guard: &MountsWriteToken,
336    ) -> Option<(Arc<PeerGroup>, PtrKey<Mount>)> {
337        let peer_group = self.peer_group.cloned();
338        self.peer_group.update(None);
339        peer_group
340    }
341
342    fn take_upstream(&self, _guard: &MountsWriteToken) -> Option<(Weak<PeerGroup>, PtrKey<Mount>)> {
343        let upstream = self.upstream.cloned();
344        self.upstream.update(None);
345        upstream
346    }
347}
348
349/// A group of mounts. Setting MS_SHARED on a mount puts it in its own peer group. Any bind mounts
350/// of a mount in the group are also added to the group. A mount created in any mount in a peer
351/// group will be automatically propagated (recreated) in every other mount in the group.
352#[derive(Default)]
353struct PeerGroup {
354    id: u64,
355    mounts: RcuRawHashMap<WeakKey<Mount>, ()>,
356    downstream: RcuRawHashMap<WeakKey<Mount>, ()>,
357}
358
359pub enum WhatToMount {
360    Fs(FileSystemHandle),
361    Bind(NamespaceNode),
362}
363
364enum WhatSubmount {
365    New(WhatToMount, MountpointFlags),
366    Existing(MountHandle),
367}
368
369impl Mount {
370    pub fn new(
371        mounts_guard: &MountsWriteToken,
372        what: WhatToMount,
373        mut flags: MountpointFlags,
374    ) -> MountHandle {
375        match what {
376            WhatToMount::Fs(fs) => {
377                // If `flags` does not explicitly specify an access-time flag then default to `RELATIME`.
378                flags.default_atime_from(MountpointFlags::RELATIME);
379                Self::new_with_root(fs.root().clone(), flags)
380            }
381            WhatToMount::Bind(node) => {
382                let mount = node.mount.as_ref().expect("can't bind mount from an anonymous node");
383                mount.clone_mount(mounts_guard, &node.entry, flags.into())
384            }
385        }
386    }
387
388    fn new_with_root(root: DirEntryHandle, flags: MountpointFlags) -> MountHandle {
389        let fs = root.node.fs();
390        let kernel = fs.kernel.upgrade().expect("can't create mount without kernel");
391        Arc::new(Self {
392            id: kernel.get_next_mount_id(),
393            flags: (flags & MountpointFlags::STORED_ON_MOUNT).into(),
394            flags_lock: Default::default(),
395            root,
396            active_client_counter: Default::default(),
397            fs,
398            relations: Default::default(),
399        })
400    }
401
402    /// A namespace node referring to the root of the mount.
403    pub fn root(self: &MountHandle) -> NamespaceNode {
404        NamespaceNode::new(Arc::clone(self), Arc::clone(&self.root))
405    }
406
407    fn kernel(&self) -> Arc<Kernel> {
408        self.fs.kernel.upgrade().expect("No Kernel")
409    }
410
411    /// Create the specified mount as a child. Also propagate it to the mount's peer group.
412    fn create_submount(
413        self: &MountHandle,
414        mounts_guard: &MountsWriteToken,
415        dir: &DirEntryHandle,
416        what: WhatSubmount,
417    ) {
418        // Necessary to make a copy to prevent excess replication, see the comment on the
419        // following Mount::new call.
420        let peers = self.peer_group().map(|g| g.copy_propagation_targets()).unwrap_or_default();
421
422        // Create the mount after copying the peer list, because in the case of creating a bind
423        // mount inside itself, the new mount would get added to our peer group during the
424        // Mount::new call, but we don't want to replicate into it already. For an example see
425        // MountTest.QuizBRecursion.
426        let mount = match what {
427            WhatSubmount::Existing(mount) => mount,
428            WhatSubmount::New(what, flags) => Mount::new(mounts_guard, what, flags),
429        };
430
431        if self.is_shared() {
432            mount.make_shared(mounts_guard);
433        }
434
435        for peer in peers {
436            if Arc::ptr_eq(self, &peer) {
437                continue;
438            }
439            let clone = mount.clone_mount_recursive(mounts_guard);
440            peer.add_submount_internal(mounts_guard, dir, clone);
441        }
442
443        self.add_submount_internal(mounts_guard, dir, mount)
444    }
445
446    fn remove_submount(
447        self: &MountHandle,
448        mounts_guard: &MountsWriteToken,
449        mount_hash_key: &ArcKey<DirEntry>,
450    ) -> Result<(), Errno> {
451        // create_submount explains why we need to make a copy of peers.
452        let peers = self.peer_group().map(|g| g.copy_propagation_targets()).unwrap_or_default();
453
454        for peer in peers {
455            if Arc::ptr_eq(self, &peer) {
456                continue;
457            }
458            // mount_namespaces(7): If B is shared, then all most-recently-mounted mounts at b on
459            // mounts that receive propagation from mount B and do not have submounts under them are
460            // unmounted.
461            let scope = RcuReadScope::new();
462            if let Some(submount) = peer.relations.submounts.get(&scope, mount_hash_key) {
463                if submount.mount.relations.submounts_len() != 0 {
464                    continue;
465                }
466            }
467            let _ = peer.remove_submount_internal(mounts_guard, mount_hash_key);
468        }
469
470        self.remove_submount_internal(mounts_guard, mount_hash_key)
471    }
472
473    pub fn move_mount(
474        source_mount: &MountHandle,
475        target_mount: &MountHandle,
476        target_dir: &DirEntryHandle,
477    ) -> Result<(), Errno> {
478        let kernel = target_mount.kernel();
479        let mounts_guard = kernel.mounts_lock.lock();
480
481        let source_mountpoint = source_mount.mountpoint().ok_or_else(|| errno!(EIO))?;
482        let source_parent =
483            source_mountpoint.mount.as_ref().expect("a mountpoint must be part of a mount");
484
485        // First, disconnect the mount from its parent.
486        {
487            if source_parent.peer_group().is_some() {
488                // Sayeth mount(2):
489                // EINVAL A move operation (MS_MOVE) was attempted, but the parent mount of source
490                //        mount has propagation type MS_SHARED.
491                return error!(EINVAL);
492            }
493            source_parent
494                .remove_submount_internal(&mounts_guard, source_mountpoint.mount_hash_key())?;
495            source_mount.relations.set_mountpoint(&mounts_guard, None);
496        }
497
498        target_mount.create_submount(
499            &mounts_guard,
500            target_dir,
501            WhatSubmount::Existing(Arc::clone(source_mount)),
502        );
503        Ok(())
504    }
505
506    /// Create a new mount with the same filesystem, flags, and peer group. Used to implement bind
507    /// mounts.
508    fn clone_mount(
509        self: &MountHandle,
510        mounts_guard: &MountsWriteToken,
511        new_root: &DirEntryHandle,
512        flags: MountFlags,
513    ) -> MountHandle {
514        assert!(new_root.is_descendant_of(&self.root));
515        // According to mount(2) on bind mounts, all flags other than MS_REC are ignored when doing
516        // a bind mount.
517        let clone = Self::new_with_root(Arc::clone(new_root), self.mount_flags());
518
519        if flags.contains(MountFlags::REC) {
520            for (dir, submount) in self.relations.iter_submounts(&RcuReadScope::new()) {
521                let submount = submount.mount.clone_mount_recursive(mounts_guard);
522                clone.add_submount_internal(mounts_guard, dir, submount);
523            }
524        }
525
526        // Put the clone in the same peer group
527        let peer_group = self.peer_group();
528        if let Some(peer_group) = peer_group {
529            clone.set_peer_group(mounts_guard, peer_group);
530        }
531
532        clone
533    }
534
535    /// Do a clone of the full mount hierarchy below this mount. Used for creating mount
536    /// namespaces and creating copies to use for propagation.
537    fn clone_mount_recursive(self: &MountHandle, mounts_guard: &MountsWriteToken) -> MountHandle {
538        self.clone_mount(mounts_guard, &self.root, MountFlags::REC)
539    }
540
541    pub fn change_propagation(
542        self: &MountHandle,
543        mounts_guard: &MountsWriteToken,
544        flag: MountFlags,
545        recursive: bool,
546    ) {
547        match flag {
548            MountFlags::SHARED => self.make_shared(mounts_guard),
549            MountFlags::PRIVATE => self.make_private(mounts_guard),
550            MountFlags::DOWNSTREAM => self.make_downstream(mounts_guard),
551            _ => {
552                log_warn!("mount propagation {:?}", flag);
553            }
554        }
555
556        if recursive {
557            for (_, submount) in self.relations.iter_submounts(&starnix_rcu::RcuReadScope::new()) {
558                submount.mount.change_propagation(mounts_guard, flag, recursive);
559            }
560        }
561    }
562
563    /// Returns the effective flags for the `Mount`, calculated as the union of the mount flags
564    /// associated with the `FileSystem`, and with the `Mount` itself.
565    fn flags(&self) -> MountFlags {
566        MountFlags::from(self.mount_flags()) | self.fs_flags().into()
567    }
568
569    /// Returns the mount flags stored unique to this `Mount`.
570    fn mount_flags(&self) -> MountpointFlags {
571        self.flags.load(Ordering::Relaxed)
572    }
573
574    /// Returns the mount flags for the `FileSystem` of this `Mount`.
575    fn fs_flags(&self) -> FileSystemFlags {
576        self.fs.options.flags.load(Ordering::Relaxed)
577    }
578
579    /// Updates the `Mount` with the per-mount flags specified in `flags`, while preserving the
580    /// existing access-time flag if no access-time flag is set in `flags`.
581    pub fn update_flags(self: &MountHandle, mut flags: MountpointFlags) {
582        let _lock = self.flags_lock.lock();
583        // Since Linux 3.17, if none of MS_NOATIME, MS_NODIRATIME,
584        // MS_RELATIME, or MS_STRICTATIME is specified in mountflags, then
585        // the remount operation preserves the existing values of these
586        // flags (rather than defaulting to MS_RELATIME).
587        flags.default_atime_from(self.flags.load(Ordering::Relaxed));
588        flags &= MountpointFlags::STORED_ON_MOUNT;
589        self.flags.store(flags, Ordering::Relaxed);
590    }
591
592    /// The number of active clients of this mount.
593    ///
594    /// The mount cannot be unmounted if there are any active clients.
595    fn active_clients(&self) -> usize {
596        // We need to subtract one for our own reference. We are not a real client.
597        Arc::strong_count(&self.active_client_counter) - 1
598    }
599
600    pub fn unmount(
601        &self,
602        mounts_guard: &MountsWriteToken,
603        flags: UnmountFlags,
604    ) -> Result<(), Errno> {
605        if !flags.contains(UnmountFlags::DETACH) {
606            if self.active_clients() > 0 || self.relations.submounts_len() != 0 {
607                return error!(EBUSY);
608            }
609        }
610
611        let mountpoint = self.mountpoint().ok_or_else(|| errno!(EINVAL))?;
612        let parent_mount = mountpoint.mount.as_ref().expect("a mountpoint must be part of a mount");
613        parent_mount.remove_submount(mounts_guard, mountpoint.mount_hash_key())
614    }
615
616    /// Returns the security state of the fs.
617    pub fn security_state(&self) -> &security::FileSystemState {
618        &self.fs.security_state
619    }
620
621    /// Returns the name of the fs.
622    pub fn fs_name(&self) -> &'static FsStr {
623        self.fs.name()
624    }
625
626    /// Reconfigures the flags for the `FileSystem` backing this mount point.
627    pub fn reconfigure_fs(
628        &self,
629        current_task: &CurrentTask,
630        flags: FileSystemFlags,
631    ) -> Result<(), Errno> {
632        self.fs.update_flags(current_task, flags)
633    }
634
635    /// Returns true if there is a submount on top of `dir_entry`.
636    pub fn has_submount(&self, dir_entry: &DirEntryHandle) -> bool {
637        let scope = RcuReadScope::new();
638        self.relations.get_submount(&scope, ArcKey::ref_cast(dir_entry)).is_some()
639    }
640
641    /// The NamespaceNode on which this Mount is mounted.
642    pub fn mountpoint(&self) -> Option<NamespaceNode> {
643        let scope = RcuReadScope::new();
644        let (mount, entry) = self.relations.mountpoint(&scope)?;
645        Some(NamespaceNode::new(mount.upgrade()?, entry.clone()))
646    }
647
648    /// Add a child mount *without propagating it to the peer group*. For internal use only.
649    pub fn add_submount_internal(
650        self: &MountHandle,
651        guard: &MountsWriteToken,
652        dir: &DirEntryHandle,
653        mount: MountHandle,
654    ) {
655        if !dir.is_descendant_of(&self.root) {
656            return;
657        }
658
659        let submount = mount.kernel().mounts.register_mount(dir, mount.clone());
660
661        let old_mountpoint = {
662            let scope = RcuReadScope::new();
663            mount.relations.mountpoint(&scope).map(|x| x.clone())
664        };
665        mount.relations.set_mountpoint(guard, Some((Arc::downgrade(self), Arc::clone(dir))));
666        assert!(old_mountpoint.is_none(), "add_submount can only take a newly created mount");
667
668        let old_mount = self.relations.insert_submount(
669            guard,
670            ArcKey::ref_cast(dir).clone(),
671            Arc::new(submount),
672        );
673
674        if let Some(old_mount) = old_mount {
675            old_mount
676                .mount
677                .relations
678                .set_mountpoint(guard, Some((Arc::downgrade(&mount), Arc::clone(dir))));
679            let new_old_submount =
680                mount.kernel().mounts.register_mount(&mount.root, old_mount.mount.clone());
681            mount.relations.insert_submount(
682                guard,
683                ArcKey(mount.root.clone()),
684                Arc::new(new_old_submount),
685            );
686        }
687    }
688
689    pub fn remove_submount_internal(
690        self: &MountHandle,
691        guard: &MountsWriteToken,
692        mount_hash_key: &ArcKey<DirEntry>,
693    ) -> Result<(), Errno> {
694        self.relations.remove_submount(guard, mount_hash_key)
695    }
696
697    /// Return this mount's current peer group.
698    fn peer_group(&self) -> Option<Arc<PeerGroup>> {
699        let scope = RcuReadScope::new();
700        self.relations.peer_group.as_ref(&scope).as_ref().map(|(g, _)| g.clone())
701    }
702
703    /// Handles unregistering from both peer group and upstream simultaneously.
704    /// This resolves forwarding the upstream to the next mount in the peer group if necessary.
705    fn unregister_from_peer_group_and_upstream(
706        guard: &MountsWriteToken,
707        peer_group: Option<(Arc<PeerGroup>, PtrKey<Mount>)>,
708        upstream: Option<(Weak<PeerGroup>, PtrKey<Mount>)>,
709    ) {
710        let upstream_group = match upstream {
711            Some((weak_group, mount)) => {
712                if let Some(group) = weak_group.upgrade() {
713                    group.remove_downstream(guard, mount);
714                    Some(group)
715                } else {
716                    None
717                }
718            }
719            None => None,
720        };
721
722        if let Some((group, mount)) = peer_group {
723            group.remove(guard, mount);
724
725            if let Some(upstream_group) = upstream_group {
726                let next_mount = {
727                    let scope = RcuReadScope::new();
728                    group.mounts.keys(&scope).next().map(|w| w.0.upgrade().unwrap())
729                };
730                if let Some(next_mount) = next_mount {
731                    next_mount.set_upstream(guard, upstream_group);
732                }
733            }
734        }
735    }
736
737    /// Remove this mount from its peer group.
738    fn take_from_peer_group(&self, guard: &MountsWriteToken) -> Option<Arc<PeerGroup>> {
739        let peer_group = self.relations.take_peer_group(guard);
740        if peer_group.is_none() {
741            return None;
742        }
743        let upstream = self.relations.take_upstream(guard);
744        let return_group = peer_group.as_ref().map(|(g, _)| g.clone());
745        Mount::unregister_from_peer_group_and_upstream(guard, peer_group, upstream);
746        return_group
747    }
748
749    fn upstream(&self) -> Option<Arc<PeerGroup>> {
750        let scope = RcuReadScope::new();
751        let (group, _) = self.relations.upstream.as_ref(&scope).as_ref()?;
752        group.upgrade()
753    }
754
755    fn remove_from_upstream(&self, guard: &MountsWriteToken) {
756        let upstream = self.relations.take_upstream(guard);
757        if let Some((weak_group, mount)) = upstream {
758            if let Some(group) = weak_group.upgrade() {
759                group.remove_downstream(guard, mount);
760            }
761        }
762    }
763
764    /// Set this mount's peer group.
765    fn set_peer_group(self: &Arc<Mount>, guard: &MountsWriteToken, group: Arc<PeerGroup>) {
766        self.take_from_peer_group(guard);
767        group.add(guard, self);
768        self.relations.peer_group.update(Some((group, Arc::as_ptr(self).into())));
769    }
770
771    fn set_upstream(self: &Arc<Mount>, guard: &MountsWriteToken, group: Arc<PeerGroup>) {
772        self.remove_from_upstream(guard);
773        group.add_downstream(guard, self);
774        self.relations.upstream.update(Some((Arc::downgrade(&group), Arc::as_ptr(self).into())));
775    }
776
777    /// Is the mount in a peer group? Corresponds to MS_SHARED.
778    pub fn is_shared(&self) -> bool {
779        self.peer_group().is_some()
780    }
781
782    /// Put the mount in a peer group. Implements MS_SHARED.
783    fn make_shared(self: &Arc<Mount>, guard: &MountsWriteToken) {
784        if self.is_shared() {
785            return;
786        }
787        let kernel = self.kernel();
788        self.set_peer_group(guard, PeerGroup::new(kernel.get_next_peer_group_id()))
789    }
790
791    /// Take the mount out of its peer group, also remove upstream if any. Implements MS_PRIVATE.
792    fn make_private(&self, guard: &MountsWriteToken) {
793        let peer_group = self.relations.take_peer_group(guard);
794        let upstream = self.relations.take_upstream(guard);
795        Mount::unregister_from_peer_group_and_upstream(guard, peer_group, upstream);
796    }
797
798    /// Take the mount out of its peer group and make it downstream instead. Implements
799    /// MountFlags::DOWNSTREAM (MS_SLAVE).
800    fn make_downstream(self: &Arc<Mount>, guard: &MountsWriteToken) {
801        if let Some(peer_group) = self.take_from_peer_group(guard) {
802            self.set_upstream(guard, peer_group);
803        }
804    }
805}
806
807impl PeerGroup {
808    fn new(id: u64) -> Arc<Self> {
809        Arc::new(Self {
810            id,
811            mounts: RcuRawHashMap::default(),
812            downstream: RcuRawHashMap::default(),
813        })
814    }
815
816    fn add(&self, _guard: &MountsWriteToken, mount: &Arc<Mount>) {
817        // SAFETY: The MountsWriteToken proves we have exclusive write access.
818        unsafe { self.mounts.insert(&starnix_rcu::RcuReadScope::new(), WeakKey::from(mount), ()) };
819    }
820
821    fn remove(&self, _guard: &MountsWriteToken, mount: PtrKey<Mount>) {
822        // SAFETY: The MountsWriteToken proves we have exclusive write access.
823        unsafe { self.mounts.remove(&mount) };
824    }
825
826    fn add_downstream(&self, _guard: &MountsWriteToken, mount: &Arc<Mount>) {
827        // SAFETY: The MountsWriteToken proves we have exclusive write access.
828        unsafe {
829            self.downstream.insert(&starnix_rcu::RcuReadScope::new(), WeakKey::from(mount), ())
830        };
831    }
832
833    fn remove_downstream(&self, _guard: &MountsWriteToken, mount: PtrKey<Mount>) {
834        // SAFETY: The MountsWriteToken proves we have exclusive write access.
835        unsafe { self.downstream.remove(&mount) };
836    }
837
838    fn copy_propagation_targets(&self) -> Vec<MountHandle> {
839        let mut buf = vec![];
840        self.collect_propagation_targets(&mut buf);
841        buf
842    }
843
844    fn collect_propagation_targets(&self, buf: &mut Vec<MountHandle>) {
845        let downstream_mounts: Vec<_> = {
846            let scope = RcuReadScope::new();
847            buf.extend(self.mounts.keys(&scope).filter_map(|m| m.0.upgrade()));
848            self.downstream.keys(&scope).filter_map(|m| m.0.upgrade()).collect()
849        };
850        for mount in downstream_mounts {
851            let peer_group = mount.peer_group();
852            match peer_group {
853                Some(group) => group.collect_propagation_targets(buf),
854                None => buf.push(mount),
855            }
856        }
857    }
858}
859
860impl Kernel {
861    pub fn get_next_mount_id(&self) -> u64 {
862        self.next_mount_id.next()
863    }
864
865    pub fn get_next_peer_group_id(&self) -> u64 {
866        self.next_peer_group_id.next()
867    }
868
869    pub fn get_next_namespace_id(&self) -> u64 {
870        self.next_namespace_id.next()
871    }
872}
873
874impl CurrentTask {
875    pub fn create_filesystem(
876        &self,
877        locked: &mut Locked<Unlocked>,
878        fs_type: &FsStr,
879        options: FileSystemOptions,
880    ) -> Result<FileSystemHandle, Errno> {
881        // Please register new file systems via //src/starnix/modules/lib.rs, even if the file
882        // system is implemented inside starnix_core.
883        //
884        // Most file systems should be implemented as modules. The VFS provides various traits that
885        // let starnix_core integrate file systems without needing to depend on the file systems
886        // directly.
887        self.kernel()
888            .expando
889            .get::<FsRegistry>()
890            .create(locked, self, fs_type, options)
891            .ok_or_else(|| errno!(ENODEV, fs_type))?
892    }
893}
894
895struct ProcMountsFileSource(Weak<Task>);
896
897impl DynamicFileSource for ProcMountsFileSource {
898    fn generate(
899        &self,
900        _current_task: &CurrentTask,
901        sink: &mut DynamicFileBuf,
902    ) -> Result<(), Errno> {
903        // TODO(tbodt): We should figure out a way to have a real iterator instead of grabbing the
904        // entire list in one go. Should we have a BTreeMap<u64, Weak<Mount>> in the Namespace?
905        // Also has the benefit of correct (i.e. chronological) ordering. But then we have to do
906        // extra work to maintain it.
907        let task = Task::from_weak(&self.0)?;
908        let task_fs = task.running_state()?.fs.read();
909        let root = task_fs.root();
910        let ns = task_fs.namespace();
911        for_each_mount(&ns.root_mount, &mut |mount| {
912            let mountpoint = mount.mountpoint().unwrap_or_else(|| mount.root());
913            if !mountpoint.is_descendant_of(&root) {
914                return Ok(());
915            }
916            write!(
917                sink,
918                "{} {} {} {}{}",
919                mount.fs.options.source_for_display(),
920                mountpoint.path(&task_fs),
921                mount.fs.name(),
922                // Report the union of the FileSystem and Mount flags, as well as any FileSystem-
923                // or LSM-specific options.
924                mount.flags(),
925                security::sb_show_options(&task.kernel(), &mount.fs)?,
926            )?;
927            writeln!(sink, " 0 0")?;
928            Ok(())
929        })?;
930        Ok(())
931    }
932}
933
934pub struct ProcMountsFile {
935    dynamic_file: DynamicFile<ProcMountsFileSource>,
936}
937
938impl ProcMountsFile {
939    pub fn new_node(task: Weak<Task>) -> impl FsNodeOps {
940        SimpleFileNode::new(move |_, _| {
941            Ok(Self { dynamic_file: DynamicFile::new(ProcMountsFileSource(task.clone())) })
942        })
943    }
944}
945
946impl FileOps for ProcMountsFile {
947    fileops_impl_delegate_read_write_and_seek!(self, self.dynamic_file);
948    fileops_impl_noop_sync!();
949
950    fn wait_async(
951        &self,
952        _locked: &mut Locked<FileOpsCore>,
953        _file: &FileObject,
954        _current_task: &CurrentTask,
955        waiter: &Waiter,
956        _events: FdEvents,
957        _handler: EventHandler,
958    ) -> Option<WaitCanceler> {
959        // Polling this file gives notifications when any change to mounts occurs. This is not
960        // implemented yet, but stubbed for Android init.
961        Some(waiter.fake_wait())
962    }
963
964    fn query_events(
965        &self,
966        _locked: &mut Locked<FileOpsCore>,
967        _file: &FileObject,
968        _current_task: &CurrentTask,
969    ) -> Result<FdEvents, Errno> {
970        Ok(FdEvents::empty())
971    }
972}
973
974#[derive(Clone)]
975pub struct ProcMountinfoFile(Weak<Task>);
976impl ProcMountinfoFile {
977    pub fn new_node(task: Weak<Task>) -> impl FsNodeOps {
978        DynamicFile::new_node(Self(task))
979    }
980}
981impl DynamicFileSource for ProcMountinfoFile {
982    fn generate(
983        &self,
984        _current_task: &CurrentTask,
985        sink: &mut DynamicFileBuf,
986    ) -> Result<(), Errno> {
987        // Returns path to the `dir` from the root of the file system.
988        fn path_from_fs_root(dir: &DirEntryHandle) -> FsString {
989            let mut path = PathBuilder::new();
990            if dir.is_dead() {
991                // Return `/foo/dir//deleted` if the dir was deleted.
992                path.prepend_element("/deleted".into());
993            }
994            let scope = RcuReadScope::new();
995            let mut current = dir.deref();
996            while let Some(parent) = current.parent_ref(&scope) {
997                path.prepend_element(current.local_name(&scope));
998                current = parent;
999            }
1000            path.build_absolute()
1001        }
1002
1003        // TODO(tbodt): We should figure out a way to have a real iterator instead of grabbing the
1004        // entire list in one go. Should we have a BTreeMap<u64, Weak<Mount>> in the Namespace?
1005        // Also has the benefit of correct (i.e. chronological) ordering. But then we have to do
1006        // extra work to maintain it.
1007        let task = Task::from_weak(&self.0)?;
1008        let task_fs = task.running_state()?.fs.read();
1009        let root = task_fs.root();
1010        let ns = task_fs.namespace();
1011        for_each_mount(&ns.root_mount, &mut |mount| {
1012            let mountpoint = mount.mountpoint().unwrap_or_else(|| mount.root());
1013            if !mountpoint.is_descendant_of(&root) {
1014                return Ok(());
1015            }
1016            // Can't fail, mountpoint() and root() can't return a NamespaceNode with no mount
1017            let parent = mountpoint.mount.as_ref().unwrap();
1018            write!(
1019                sink,
1020                "{} {} {} {} {} {}",
1021                mount.id,
1022                parent.id,
1023                mount.root.node.fs().dev_id,
1024                path_from_fs_root(&mount.root),
1025                mountpoint.path(&task_fs),
1026                mount.mount_flags(),
1027            )?;
1028            if let Some(peer_group) = mount.peer_group() {
1029                write!(sink, " shared:{}", peer_group.id)?;
1030            }
1031            if let Some(upstream) = mount.upstream() {
1032                write!(sink, " master:{}", upstream.id)?;
1033            }
1034            writeln!(
1035                sink,
1036                " - {} {} {}{}",
1037                mount.fs.name(),
1038                mount.fs.options.source_for_display(),
1039                mount.fs_flags(),
1040                // LSM options are associated with the FileSystem rather than the Mount.
1041                security::sb_show_options(&task.kernel(), &mount.fs)?
1042            )?;
1043            Ok(())
1044        })?;
1045        Ok(())
1046    }
1047}
1048
1049fn for_each_mount<E>(
1050    mount: &MountHandle,
1051    callback: &mut impl FnMut(&MountHandle) -> Result<(), E>,
1052) -> Result<(), E> {
1053    callback(mount)?;
1054    for (_, s) in mount.relations.iter_submounts(&RcuReadScope::new()) {
1055        for_each_mount(&s.mount, callback)?;
1056    }
1057    Ok(())
1058}
1059
1060/// The `SymlinkMode` enum encodes how symlinks are followed during path traversal.
1061#[derive(Default, PartialEq, Eq, Copy, Clone, Debug)]
1062pub enum SymlinkMode {
1063    /// Follow a symlink at the end of a path resolution.
1064    #[default]
1065    Follow,
1066
1067    /// Do not follow a symlink at the end of a path resolution.
1068    NoFollow,
1069}
1070
1071/// The maximum number of symlink traversals that can be made during path resolution.
1072pub const MAX_SYMLINK_FOLLOWS: u8 = 40;
1073
1074/// The context passed during namespace lookups.
1075///
1076/// Namespace lookups need to mutate a shared context in order to correctly
1077/// count the number of remaining symlink traversals.
1078pub struct LookupContext {
1079    /// The SymlinkMode for the lookup.
1080    ///
1081    /// As the lookup proceeds, the follow count is decremented each time the
1082    /// lookup traverses a symlink.
1083    pub symlink_mode: SymlinkMode,
1084
1085    /// The number of symlinks remaining the follow.
1086    ///
1087    /// Each time path resolution calls readlink, this value is decremented.
1088    pub remaining_follows: u8,
1089
1090    /// Whether the result of the lookup must be a directory.
1091    ///
1092    /// For example, if the path ends with a `/` or if userspace passes
1093    /// O_DIRECTORY. This flag can be set to true if the lookup encounters a
1094    /// symlink that ends with a `/`.
1095    pub must_be_directory: bool,
1096
1097    /// Resolve flags passed to `openat2`. Empty if the lookup originated in any other syscall.
1098    pub resolve_flags: ResolveFlags,
1099
1100    /// Base directory for the lookup. Set only when either `RESOLVE_BENEATH` or `RESOLVE_IN_ROOT`
1101    /// is passed to `openat2`.
1102    pub resolve_base: ResolveBase,
1103}
1104
1105/// Used to specify base directory in `LookupContext` for lookups originating in the `openat2`
1106/// syscall with either `RESOLVE_BENEATH` or `RESOLVE_IN_ROOT` flag.
1107#[derive(Clone, Eq, PartialEq)]
1108pub enum ResolveBase {
1109    None,
1110
1111    /// The lookup is not allowed to traverse any node that's not beneath the specified node.
1112    Beneath(NamespaceNode),
1113
1114    /// The lookup should be handled as if the root specified node is the file-system root.
1115    InRoot(NamespaceNode),
1116}
1117
1118impl LookupContext {
1119    pub fn new(symlink_mode: SymlinkMode) -> LookupContext {
1120        LookupContext {
1121            symlink_mode,
1122            remaining_follows: MAX_SYMLINK_FOLLOWS,
1123            must_be_directory: false,
1124            resolve_flags: ResolveFlags::empty(),
1125            resolve_base: ResolveBase::None,
1126        }
1127    }
1128
1129    pub fn with(&self, symlink_mode: SymlinkMode) -> LookupContext {
1130        LookupContext { symlink_mode, resolve_base: self.resolve_base.clone(), ..*self }
1131    }
1132
1133    pub fn update_for_path(&mut self, path: &FsStr) {
1134        if path.last() == Some(&b'/') {
1135            // The last path element must resolve to a directory. This is because a trailing slash
1136            // was found in the path.
1137            self.must_be_directory = true;
1138            // If the last path element is a symlink, we should follow it.
1139            // See https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_75
1140            self.symlink_mode = SymlinkMode::Follow;
1141        }
1142    }
1143}
1144
1145impl Default for LookupContext {
1146    fn default() -> Self {
1147        LookupContext::new(SymlinkMode::Follow)
1148    }
1149}
1150
1151/// Whether the path is reachable from the given root.
1152pub enum PathWithReachability {
1153    /// The path is reachable from the given root.
1154    Reachable(FsString),
1155
1156    /// The path is not reachable from the given root.
1157    Unreachable(FsString),
1158}
1159
1160impl PathWithReachability {
1161    pub fn into_path(self) -> FsString {
1162        match self {
1163            PathWithReachability::Reachable(path) => path,
1164            PathWithReachability::Unreachable(path) => path,
1165        }
1166    }
1167}
1168
1169/// A node in a mount namespace.
1170///
1171/// This tree is a composite of the mount tree and the FsNode tree.
1172///
1173/// These nodes are used when traversing paths in a namespace in order to
1174/// present the client the directory structure that includes the mounted
1175/// filesystems.
1176#[derive(Clone)]
1177pub struct NamespaceNode {
1178    /// The mount where this namespace node is mounted.
1179    ///
1180    /// A given FsNode can be mounted in multiple places in a namespace. This
1181    /// field distinguishes between them.
1182    pub mount: MountInfo,
1183
1184    /// The FsNode that corresponds to this namespace entry.
1185    pub entry: DirEntryHandle,
1186}
1187
1188impl NamespaceNode {
1189    pub fn new(mount: MountHandle, entry: DirEntryHandle) -> Self {
1190        Self { mount: Some(mount).into(), entry }
1191    }
1192
1193    /// Create a namespace node that is not mounted in a namespace.
1194    pub fn new_anonymous(entry: DirEntryHandle) -> Self {
1195        Self { mount: None.into(), entry }
1196    }
1197
1198    /// Create a namespace node that is not mounted in a namespace and that refers to a node that
1199    /// is not rooted in a hierarchy and has no name.
1200    pub fn new_anonymous_unrooted(current_task: &CurrentTask, node: FsNodeHandle) -> Self {
1201        let dir_entry = DirEntry::new_unrooted(node);
1202        let _ = security::fs_node_init_with_dentry_no_xattr(current_task, &dir_entry);
1203        Self::new_anonymous(dir_entry)
1204    }
1205
1206    /// Create a FileObject corresponding to this namespace node.
1207    ///
1208    /// This function is the primary way of instantiating FileObjects. Each
1209    /// FileObject records the NamespaceNode that created it in order to
1210    /// remember its path in the Namespace.
1211    pub fn open(
1212        &self,
1213        locked: &mut Locked<Unlocked>,
1214        current_task: &CurrentTask,
1215        flags: OpenFlags,
1216        access_check: AccessCheck,
1217    ) -> Result<FileHandle, Errno> {
1218        let ops = self.entry.node.open(locked, current_task, self, flags, access_check)?;
1219        FileObject::new(locked, current_task, ops, self.clone(), flags)
1220    }
1221
1222    /// Create or open a node in the file system.
1223    ///
1224    /// Works for any type of node other than a symlink.
1225    ///
1226    /// Will return an existing node unless `flags` contains `OpenFlags::EXCL`.
1227    pub fn open_create_node<L>(
1228        &self,
1229        locked: &mut Locked<L>,
1230        current_task: &CurrentTask,
1231        name: &FsStr,
1232        mode: FileMode,
1233        dev: DeviceId,
1234        flags: OpenFlags,
1235    ) -> Result<NamespaceNode, Errno>
1236    where
1237        L: LockEqualOrBefore<FileOpsCore>,
1238    {
1239        let owner = current_task.current_fscred();
1240        let mode = current_task.fs().apply_umask(mode);
1241        let create_fn =
1242            |locked: &mut Locked<L>, dir: &FsNodeHandle, mount: &MountInfo, name: &_| {
1243                dir.create_node(locked, current_task, mount, name, mode, dev, owner)
1244            };
1245        let entry = if flags.contains(OpenFlags::EXCL) {
1246            self.entry.create_entry(locked, current_task, &self.mount, name, create_fn)
1247        } else {
1248            self.entry.get_or_create_entry(locked, current_task, &self.mount, name, create_fn)
1249        }?;
1250        Ok(self.with_new_entry(entry))
1251    }
1252
1253    pub fn into_active(self) -> ActiveNamespaceNode {
1254        ActiveNamespaceNode::new(self)
1255    }
1256
1257    pub fn into_mapping(self, mode: Option<FileWriteGuardMode>) -> Result<Arc<FileMapping>, Errno> {
1258        self.into_active().into_mapping(mode)
1259    }
1260
1261    /// Create a node in the file system.
1262    ///
1263    /// Works for any type of node other than a symlink.
1264    ///
1265    /// Does not return an existing node.
1266    pub fn create_node<L>(
1267        &self,
1268        locked: &mut Locked<L>,
1269        current_task: &CurrentTask,
1270        name: &FsStr,
1271        mode: FileMode,
1272        dev: DeviceId,
1273    ) -> Result<NamespaceNode, Errno>
1274    where
1275        L: LockEqualOrBefore<FileOpsCore>,
1276    {
1277        let owner = current_task.current_fscred();
1278        let mode = current_task.fs().apply_umask(mode);
1279        let entry = self.entry.create_entry(
1280            locked,
1281            current_task,
1282            &self.mount,
1283            name,
1284            |locked, dir, mount, name| {
1285                dir.create_node(locked, current_task, mount, name, mode, dev, owner)
1286            },
1287        )?;
1288        Ok(self.with_new_entry(entry))
1289    }
1290
1291    /// Create a symlink in the file system.
1292    ///
1293    /// To create another type of node, use `create_node`.
1294    pub fn create_symlink<L>(
1295        &self,
1296        locked: &mut Locked<L>,
1297        current_task: &CurrentTask,
1298        name: &FsStr,
1299        target: &FsStr,
1300    ) -> Result<NamespaceNode, Errno>
1301    where
1302        L: LockEqualOrBefore<FileOpsCore>,
1303    {
1304        let owner = current_task.current_fscred();
1305        let entry = self.entry.create_entry(
1306            locked,
1307            current_task,
1308            &self.mount,
1309            name,
1310            |locked, dir, mount, name| {
1311                dir.create_symlink(locked, current_task, mount, name, target, owner)
1312            },
1313        )?;
1314        Ok(self.with_new_entry(entry))
1315    }
1316
1317    /// Creates an anonymous file.
1318    ///
1319    /// The FileMode::IFMT of the FileMode is always FileMode::IFREG.
1320    ///
1321    /// Used by O_TMPFILE.
1322    pub fn create_tmpfile<L>(
1323        &self,
1324        locked: &mut Locked<L>,
1325        current_task: &CurrentTask,
1326        mode: FileMode,
1327        flags: OpenFlags,
1328    ) -> Result<NamespaceNode, Errno>
1329    where
1330        L: LockEqualOrBefore<FileOpsCore>,
1331    {
1332        let owner = current_task.current_fscred();
1333        let mode = current_task.fs().apply_umask(mode);
1334        Ok(self.with_new_entry(self.entry.create_tmpfile(
1335            locked,
1336            current_task,
1337            &self.mount,
1338            mode,
1339            owner,
1340            flags,
1341        )?))
1342    }
1343
1344    pub fn link<L>(
1345        &self,
1346        locked: &mut Locked<L>,
1347        current_task: &CurrentTask,
1348        name: &FsStr,
1349        child: &FsNodeHandle,
1350    ) -> Result<NamespaceNode, Errno>
1351    where
1352        L: LockEqualOrBefore<FileOpsCore>,
1353    {
1354        let dir_entry = self.entry.create_entry(
1355            locked,
1356            current_task,
1357            &self.mount,
1358            name,
1359            |locked, dir, mount, name| dir.link(locked, current_task, mount, name, child),
1360        )?;
1361        Ok(self.with_new_entry(dir_entry))
1362    }
1363
1364    pub fn bind_socket<L>(
1365        &self,
1366        locked: &mut Locked<L>,
1367        current_task: &CurrentTask,
1368        name: &FsStr,
1369        socket: SocketHandle,
1370        socket_address: SocketAddress,
1371        mode: FileMode,
1372    ) -> Result<NamespaceNode, Errno>
1373    where
1374        L: LockEqualOrBefore<FileOpsCore>,
1375    {
1376        let dir_entry = self.entry.create_entry(
1377            locked,
1378            current_task,
1379            &self.mount,
1380            name,
1381            |locked, dir, mount, name| {
1382                let node = dir.create_node(
1383                    locked,
1384                    current_task,
1385                    mount,
1386                    name,
1387                    mode,
1388                    DeviceId::NONE,
1389                    current_task.current_fscred(),
1390                )?;
1391                if let Some(unix_socket) = socket.downcast_socket::<UnixSocket>() {
1392                    unix_socket.bind_socket_to_node(&socket, socket_address, &node)?;
1393                } else {
1394                    return error!(ENOTSUP);
1395                }
1396                Ok(node)
1397            },
1398        )?;
1399        Ok(self.with_new_entry(dir_entry))
1400    }
1401
1402    pub fn unlink<L>(
1403        &self,
1404        locked: &mut Locked<L>,
1405        current_task: &CurrentTask,
1406        name: &FsStr,
1407        kind: UnlinkKind,
1408        must_be_directory: bool,
1409    ) -> Result<(), Errno>
1410    where
1411        L: LockEqualOrBefore<FileOpsCore>,
1412    {
1413        if DirEntry::is_reserved_name(name) {
1414            match kind {
1415                UnlinkKind::Directory => {
1416                    if name == ".." {
1417                        error!(ENOTEMPTY)
1418                    } else if self.parent().is_none() {
1419                        // The client is attempting to remove the root.
1420                        error!(EBUSY)
1421                    } else {
1422                        error!(EINVAL)
1423                    }
1424                }
1425                UnlinkKind::NonDirectory => error!(ENOTDIR),
1426            }
1427        } else {
1428            self.entry.unlink(locked, current_task, &self.mount, name, kind, must_be_directory)
1429        }
1430    }
1431
1432    // Resolve the current node.
1433    //
1434    // Depending on context, this will resolve symlink and mount point.
1435    fn resolve<L>(
1436        self,
1437        locked: &mut Locked<L>,
1438        current_task: &CurrentTask,
1439        context: &mut LookupContext,
1440    ) -> Result<NamespaceNode, Errno>
1441    where
1442        L: LockEqualOrBefore<FileOpsCore>,
1443    {
1444        let mut node = self;
1445
1446        loop {
1447            if !node.entry.node.is_lnk() || context.symlink_mode == SymlinkMode::NoFollow {
1448                break;
1449            }
1450            if context.remaining_follows == 0
1451                || context.resolve_flags.contains(ResolveFlags::NO_SYMLINKS)
1452            {
1453                return error!(ELOOP);
1454            }
1455            context.remaining_follows -= 1;
1456            node = match node.readlink(locked, current_task)? {
1457                SymlinkTarget::Path(link_target) => {
1458                    let link_directory = if link_target[0] == b'/' {
1459                        // If the path is absolute, we'll resolve the root directory.
1460                        match &context.resolve_base {
1461                            ResolveBase::None => current_task.fs().root(),
1462                            ResolveBase::Beneath(_) => return error!(EXDEV),
1463                            ResolveBase::InRoot(root) => root.clone(),
1464                        }
1465                    } else {
1466                        // If the path is not absolute, it's a relative directory.
1467                        // Let's try to get the parent of the current node, or in the case that
1468                        // the node is the root we can just use that directly.
1469                        node.parent().unwrap_or(node)
1470                    };
1471                    current_task.lookup_path(
1472                        locked,
1473                        context,
1474                        link_directory,
1475                        link_target.as_ref(),
1476                    )?
1477                }
1478                SymlinkTarget::Node(node) => {
1479                    if context.resolve_flags.contains(ResolveFlags::NO_MAGICLINKS) {
1480                        return error!(ELOOP);
1481                    }
1482                    node
1483                }
1484            };
1485        }
1486        Ok(node.enter_mount())
1487    }
1488
1489    /// Traverse down a parent-to-child link in the namespace.
1490    pub fn lookup_child<L>(
1491        &self,
1492        locked: &mut Locked<L>,
1493        current_task: &CurrentTask,
1494        context: &mut LookupContext,
1495        basename: &FsStr,
1496    ) -> Result<NamespaceNode, Errno>
1497    where
1498        L: LockEqualOrBefore<FileOpsCore>,
1499    {
1500        self.lookup_children(locked, current_task, context, &[basename])
1501    }
1502
1503    /// Traverse down a parent-to-child link in the namespace.
1504    pub fn lookup_children<L>(
1505        &self,
1506        locked: &mut Locked<L>,
1507        current_task: &CurrentTask,
1508        context: &mut LookupContext,
1509        mut basenames: &[&FsStr],
1510    ) -> Result<NamespaceNode, Errno>
1511    where
1512        L: LockEqualOrBefore<FileOpsCore>,
1513    {
1514        for name in basenames {
1515            if name.len() > NAME_MAX as usize {
1516                return error!(ENAMETOOLONG);
1517            }
1518        }
1519
1520        let mut current_namespace_node = self.clone();
1521
1522        while basenames.len() > 0 {
1523            if !current_namespace_node.entry.node.is_dir() {
1524                return error!(ENOTDIR);
1525            }
1526
1527            let basename = basenames[0];
1528            if basename.is_empty() || basename == "." {
1529                basenames = &basenames[1..];
1530                continue;
1531            }
1532            if basename == ".." {
1533                let root = match &context.resolve_base {
1534                    ResolveBase::None => current_task.fs().root(),
1535                    ResolveBase::Beneath(node) => {
1536                        // Do not allow traversal out of the 'node'.
1537                        if current_namespace_node == *node {
1538                            return error!(EXDEV);
1539                        }
1540                        current_task.fs().root()
1541                    }
1542                    ResolveBase::InRoot(root) => root.clone(),
1543                };
1544
1545                // Make sure this can't escape a chroot.
1546                if current_namespace_node != root {
1547                    current_namespace_node =
1548                        current_namespace_node.parent().unwrap_or(current_namespace_node)
1549                }
1550                if context.resolve_flags.contains(ResolveFlags::NO_XDEV)
1551                    && current_namespace_node.mount != self.mount
1552                {
1553                    return error!(EXDEV);
1554                }
1555
1556                if context.must_be_directory && !current_namespace_node.entry.node.is_dir() {
1557                    return error!(ENOTDIR);
1558                }
1559                basenames = &basenames[1..];
1560                continue;
1561            }
1562            if basenames.len() == 1
1563                || !current_namespace_node.entry.node.ops().has_lookup_pipelined()
1564            {
1565                current_namespace_node = current_namespace_node.with_new_entry(
1566                    current_namespace_node.entry.component_lookup(
1567                        locked,
1568                        current_task,
1569                        &current_namespace_node.mount,
1570                        basename,
1571                    )?,
1572                );
1573
1574                current_namespace_node =
1575                    current_namespace_node.resolve(locked, current_task, context)?;
1576
1577                if context.resolve_flags.contains(ResolveFlags::NO_XDEV)
1578                    && current_namespace_node.mount != self.mount
1579                {
1580                    return error!(EXDEV);
1581                }
1582
1583                if context.must_be_directory && !current_namespace_node.entry.node.is_dir() {
1584                    return error!(ENOTDIR);
1585                }
1586
1587                basenames = &basenames[1..];
1588                continue;
1589            }
1590
1591            let pipelined_basenames = if let Some(pos) =
1592                basenames.iter().position(|&name| name.is_empty() || name == "." || name == "..")
1593            {
1594                &basenames[..pos]
1595            } else {
1596                basenames
1597            };
1598            let precomputed_entries = current_namespace_node.entry.get_children_pipelined(
1599                locked,
1600                current_task,
1601                &current_namespace_node.mount,
1602                pipelined_basenames,
1603            );
1604            for entry in precomputed_entries {
1605                basenames = &basenames[1..];
1606                let child = current_namespace_node.with_new_entry(entry?);
1607
1608                current_namespace_node = child.clone().resolve(locked, current_task, context)?;
1609
1610                if context.resolve_flags.contains(ResolveFlags::NO_XDEV)
1611                    && current_namespace_node.mount != self.mount
1612                {
1613                    return error!(EXDEV);
1614                }
1615
1616                if context.must_be_directory && !current_namespace_node.entry.node.is_dir() {
1617                    return error!(ENOTDIR);
1618                }
1619
1620                if current_namespace_node != child {
1621                    break;
1622                }
1623            }
1624        }
1625
1626        Ok(current_namespace_node)
1627    }
1628
1629    /// Traverse up a child-to-parent link in the namespace.
1630    ///
1631    /// This traversal matches the child-to-parent link in the underlying
1632    /// FsNode except at mountpoints, where the link switches from one
1633    /// filesystem to another.
1634    pub fn parent(&self) -> Option<NamespaceNode> {
1635        let mountpoint_or_self = self.escape_mount();
1636        let parent = mountpoint_or_self.entry.parent()?;
1637        Some(mountpoint_or_self.with_new_entry(parent))
1638    }
1639
1640    /// Returns the parent, but does not escape mounts i.e. returns None if this node
1641    /// is the root of a mount.
1642    pub fn parent_within_mount(&self) -> Option<DirEntryHandle> {
1643        if let Ok(_) = self.mount_if_root() {
1644            return None;
1645        }
1646        self.entry.parent()
1647    }
1648
1649    /// Whether this namespace node is a descendant of the given node.
1650    ///
1651    /// Walks up the namespace node tree looking for ancestor. If ancestor is
1652    /// found, returns true. Otherwise, returns false.
1653    pub fn is_descendant_of(&self, ancestor: &NamespaceNode) -> bool {
1654        let ancestor = ancestor.escape_mount();
1655        let mut current = self.escape_mount();
1656        while current != ancestor {
1657            if let Some(parent) = current.parent() {
1658                current = parent.escape_mount();
1659            } else {
1660                return false;
1661            }
1662        }
1663        true
1664    }
1665
1666    /// If this node is a mountpoint, returns the root of the submount.
1667    ///
1668    /// This only traverses one level of mount, even if the submount's root
1669    /// is itself a mountpoint for another mount.
1670    fn enter_one_mount(&self) -> Option<NamespaceNode> {
1671        if let Some(mount) = self.mount.deref() {
1672            if let Some(submount) =
1673                mount.relations.get_submount(&RcuReadScope::new(), ArcKey::ref_cast(&self.entry))
1674            {
1675                return Some(submount.mount.root());
1676            }
1677        }
1678        None
1679    }
1680
1681    /// If this is a mount point, return the root of the mount. Otherwise return self.
1682    ///
1683    /// This function traverses multiple mounts if there are mounts layered on top of each other.
1684    /// It handles its own synchronization by acquiring the mounts sequence lock for reading.
1685    fn enter_mount(&self) -> NamespaceNode {
1686        // While the child is a mountpoint, replace child with the mount's root.
1687        let kernel = self.entry.node.fs().kernel.upgrade().expect("kernel");
1688        kernel.mounts_lock.read_seq(|| {
1689            let mut inner = self.clone();
1690            while let Some(inner_root) = inner.enter_one_mount() {
1691                inner = inner_root;
1692            }
1693            inner
1694        })
1695    }
1696
1697    /// If this is a mount point, return the root of the mount. Otherwise return self.
1698    ///
1699    /// This function traverses multiple mounts if there are mounts layered on top of each other.
1700    /// It requires the caller to hold the mounts write lock (proved by `MountsWriteToken`),
1701    /// unlike `enter_mount` which uses a sequence lock for reading.
1702    fn enter_mount_locked(&self, _mounts_guard: &MountsWriteToken) -> NamespaceNode {
1703        // While the child is a mountpoint, replace child with the mount's root.
1704        let mut inner = self.clone();
1705        while let Some(inner_root) = inner.enter_one_mount() {
1706            inner = inner_root;
1707        }
1708        inner
1709    }
1710
1711    /// If this is the root of a mount, return the mount point. Otherwise return self.
1712    ///
1713    /// This is not exactly the same as parent(). If parent() is called on a root, it will escape
1714    /// the mount, but then return the parent of the mount point instead of the mount point.
1715    fn escape_mount(&self) -> NamespaceNode {
1716        let kernel = self.entry.node.fs().kernel.upgrade().expect("kernel");
1717        kernel.mounts_lock.read_seq(|| {
1718            let mut mountpoint_or_self = self.clone();
1719            while let Some(mountpoint) = mountpoint_or_self.mountpoint() {
1720                mountpoint_or_self = mountpoint;
1721            }
1722            mountpoint_or_self
1723        })
1724    }
1725
1726    /// If this node is the root of a mount, return it. Otherwise EINVAL.
1727    pub fn mount_if_root(&self) -> Result<&MountHandle, Errno> {
1728        if let Some(mount) = self.mount.deref() {
1729            if Arc::ptr_eq(&self.entry, &mount.root) {
1730                return Ok(mount);
1731            }
1732        }
1733        error!(EINVAL)
1734    }
1735
1736    /// Returns the mountpoint at this location in the namespace.
1737    ///
1738    /// If this node is mounted in another node, this function returns the node
1739    /// at which this node is mounted. Otherwise, returns None.
1740    fn mountpoint(&self) -> Option<NamespaceNode> {
1741        self.mount_if_root().ok()?.mountpoint()
1742    }
1743
1744    /// The path from the filesystem root to this node.
1745    pub fn path(&self, fs: &FsContext) -> FsString {
1746        self.path_from_root(Some(&fs.root())).into_path()
1747    }
1748
1749    /// The path from the root of the namespace to this node.
1750    pub fn path_escaping_chroot(&self) -> FsString {
1751        self.path_from_root(None).into_path()
1752    }
1753
1754    /// Returns the path to this node, accounting for a custom root.
1755    /// A task may have a custom root set by `chroot`.
1756    pub fn path_from_root(&self, root: Option<&NamespaceNode>) -> PathWithReachability {
1757        if self.mount.is_none() {
1758            return self.unrooted_path();
1759        }
1760
1761        let mut path = PathBuilder::new();
1762        let mut current = self.escape_mount();
1763        if let Some(root) = root {
1764            let scope = RcuReadScope::new();
1765            // The current node is expected to intersect with the custom root as we travel up the tree.
1766            let root = root.escape_mount();
1767            while current != root {
1768                if let Some(parent) = current.parent() {
1769                    path.prepend_element(current.entry.local_name(&scope));
1770                    current = parent.escape_mount();
1771                } else {
1772                    // This node hasn't intersected with the custom root and has reached the namespace root.
1773                    let mut absolute_path = path.build_absolute();
1774                    if self.entry.is_dead() {
1775                        absolute_path.extend_from_slice(b" (deleted)");
1776                    }
1777
1778                    return PathWithReachability::Unreachable(absolute_path);
1779                }
1780            }
1781        } else {
1782            // No custom root, so travel up the tree to the namespace root.
1783            let scope = RcuReadScope::new();
1784            while let Some(parent) = current.parent() {
1785                path.prepend_element(current.entry.local_name(&scope));
1786                current = parent.escape_mount();
1787            }
1788        }
1789
1790        let mut absolute_path = path.build_absolute();
1791        if self.entry.is_dead() {
1792            absolute_path.extend_from_slice(b" (deleted)");
1793        }
1794
1795        PathWithReachability::Reachable(absolute_path)
1796    }
1797
1798    fn unrooted_path(&self) -> PathWithReachability {
1799        let scope = RcuReadScope::new();
1800        let mode = self.entry.node.info().mode;
1801        let local_name = self.entry.local_name(&scope);
1802        let path = if !local_name.is_empty() {
1803            format!("anon_inode:{}", local_name)
1804        } else if mode.is_sock() {
1805            format!("socket:[{}]", self.entry.node.ino)
1806        } else if mode.is_fifo() {
1807            format!("pipe:[{}]", self.entry.node.ino)
1808        } else {
1809            format!("file:[{}]", self.entry.node.ino)
1810        };
1811        PathWithReachability::Reachable(path.into())
1812    }
1813
1814    pub fn mount(&self, what: WhatToMount, flags: MountpointFlags) -> Result<(), Errno> {
1815        let kernel = self.entry.node.fs().kernel.upgrade().expect("can't mount without a kernel");
1816        let mounts_guard = kernel.mounts_lock.lock();
1817
1818        let mountpoint = self.enter_mount_locked(&mounts_guard);
1819        let mount = mountpoint.mount.as_ref().expect("a mountpoint must be part of a mount");
1820        mount.create_submount(&mounts_guard, &mountpoint.entry, WhatSubmount::New(what, flags));
1821        Ok(())
1822    }
1823
1824    /// If this is the root of a filesystem, unmount. Otherwise return EINVAL.
1825    pub fn unmount(&self, flags: UnmountFlags) -> Result<(), Errno> {
1826        let kernel = self.entry.node.fs().kernel.upgrade().expect("can't mount without a kernel");
1827        let mounts_guard = kernel.mounts_lock.lock();
1828
1829        let mount = self.enter_mount_locked(&mounts_guard).mount_if_root()?.clone();
1830        mount.unmount(&mounts_guard, flags)
1831    }
1832
1833    pub fn rename<L>(
1834        locked: &mut Locked<L>,
1835        current_task: &CurrentTask,
1836        old_parent: &NamespaceNode,
1837        old_name: &FsStr,
1838        new_parent: &NamespaceNode,
1839        new_name: &FsStr,
1840        flags: RenameFlags,
1841    ) -> Result<(), Errno>
1842    where
1843        L: LockEqualOrBefore<FileOpsCore>,
1844    {
1845        DirEntry::rename(
1846            locked,
1847            current_task,
1848            &old_parent.entry,
1849            &old_parent.mount,
1850            old_name,
1851            &new_parent.entry,
1852            &new_parent.mount,
1853            new_name,
1854            flags,
1855        )
1856    }
1857
1858    fn with_new_entry(&self, entry: DirEntryHandle) -> NamespaceNode {
1859        Self { mount: self.mount.clone(), entry }
1860    }
1861
1862    fn mount_hash_key(&self) -> &ArcKey<DirEntry> {
1863        ArcKey::ref_cast(&self.entry)
1864    }
1865
1866    pub fn apply_suid_and_sgid(&self, creds: &mut Credentials) {
1867        // From <https://man7.org/linux/man-pages/man2/execve.2.html>:
1868        //
1869        //   The aforementioned transformations of the effective IDs are not
1870        //   performed ... if ... the underlying filesystem is mounted nosuid
1871        //   (the MS_NOSUID flag for mount(2)).
1872        if self.mount.flags().contains(MountFlags::NOSUID) {
1873            return;
1874        }
1875        self.entry.node.info().apply_suid_and_sgid(creds)
1876    }
1877
1878    pub fn update_atime(&self) {
1879        // Do not update the atime of this node if it is mounted with the NOATIME flag.
1880        if !self.mount.flags().contains(MountFlags::NOATIME) {
1881            self.entry.node.update_info(|info| {
1882                let now = utc::utc_now();
1883                info.time_access = now;
1884                info.pending_time_access_update = true;
1885            });
1886        }
1887    }
1888
1889    pub fn readlink<L>(
1890        &self,
1891        locked: &mut Locked<L>,
1892        current_task: &CurrentTask,
1893    ) -> Result<SymlinkTarget, Errno>
1894    where
1895        L: LockEqualOrBefore<FileOpsCore>,
1896    {
1897        self.update_atime();
1898        self.entry.node.readlink(locked, current_task)
1899    }
1900
1901    pub fn notify(&self, event_mask: InotifyMask) {
1902        if self.mount.is_some() {
1903            self.entry.notify(event_mask);
1904        }
1905    }
1906
1907    /// Check whether the node can be accessed in the current context with the specified access
1908    /// flags (read, write, or exec). Accounts for capabilities and whether the current user is the
1909    /// owner or is in the file's group.
1910    pub fn check_access<L>(
1911        &self,
1912        locked: &mut Locked<L>,
1913        current_task: &CurrentTask,
1914        permission_flags: impl Into<security::PermissionFlags>,
1915        reason: CheckAccessReason,
1916    ) -> Result<(), Errno>
1917    where
1918        L: LockEqualOrBefore<FileOpsCore>,
1919    {
1920        self.entry.node.check_access(
1921            locked,
1922            current_task,
1923            &self.mount,
1924            permission_flags,
1925            reason,
1926            self,
1927        )
1928    }
1929
1930    /// Checks if O_NOATIME is allowed,
1931    pub fn check_o_noatime_allowed(&self, current_task: &CurrentTask) -> Result<(), Errno> {
1932        self.entry.node.check_o_noatime_allowed(current_task)
1933    }
1934
1935    pub fn truncate<L>(
1936        &self,
1937        locked: &mut Locked<L>,
1938        current_task: &CurrentTask,
1939        length: u64,
1940    ) -> Result<(), Errno>
1941    where
1942        L: LockEqualOrBefore<BeforeFsNodeAppend>,
1943    {
1944        self.entry.node.truncate(locked, current_task, &self.mount, length)?;
1945        self.entry.notify_ignoring_excl_unlink(InotifyMask::MODIFY);
1946        Ok(())
1947    }
1948}
1949
1950impl fmt::Debug for NamespaceNode {
1951    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1952        f.debug_struct("NamespaceNode")
1953            .field("path", &self.path_escaping_chroot())
1954            .field("mount", &self.mount)
1955            .field("entry", &self.entry)
1956            .finish()
1957    }
1958}
1959
1960// Eq/Hash impls intended for the MOUNT_POINTS hash
1961impl PartialEq for NamespaceNode {
1962    fn eq(&self, other: &Self) -> bool {
1963        self.mount.as_ref().map(Arc::as_ptr).eq(&other.mount.as_ref().map(Arc::as_ptr))
1964            && Arc::ptr_eq(&self.entry, &other.entry)
1965    }
1966}
1967impl Eq for NamespaceNode {}
1968impl Hash for NamespaceNode {
1969    fn hash<H: Hasher>(&self, state: &mut H) {
1970        self.mount.as_ref().map(Arc::as_ptr).hash(state);
1971        Arc::as_ptr(&self.entry).hash(state);
1972    }
1973}
1974
1975/// A namespace node that keeps the underly mount busy.
1976#[derive(Debug, Clone)]
1977pub struct ActiveNamespaceNode {
1978    /// The underlying namespace node.
1979    name: NamespaceNode,
1980
1981    /// Adds a reference to the mount client marker to prevent the mount from
1982    /// being removed while the NamespaceNode is active. Is None iff mount is
1983    /// None.
1984    _marker: Option<MountClientMarker>,
1985}
1986
1987impl ActiveNamespaceNode {
1988    pub fn new(name: NamespaceNode) -> Self {
1989        let marker = name.mount.as_ref().map(|mount| mount.active_client_counter.clone());
1990        Self { name, _marker: marker }
1991    }
1992
1993    pub fn to_passive(&self) -> NamespaceNode {
1994        self.deref().clone()
1995    }
1996
1997    pub fn into_mapping(self, mode: Option<FileWriteGuardMode>) -> Result<Arc<FileMapping>, Errno> {
1998        if let Some(mode) = mode {
1999            self.entry.node.write_guard_state.lock().acquire(mode)?;
2000        }
2001        Ok(Arc::new(FileMapping { name: self, mode }))
2002    }
2003}
2004
2005impl Deref for ActiveNamespaceNode {
2006    type Target = NamespaceNode;
2007
2008    fn deref(&self) -> &Self::Target {
2009        &self.name
2010    }
2011}
2012
2013impl PartialEq for ActiveNamespaceNode {
2014    fn eq(&self, other: &Self) -> bool {
2015        self.deref().eq(other.deref())
2016    }
2017}
2018impl Eq for ActiveNamespaceNode {}
2019impl Hash for ActiveNamespaceNode {
2020    fn hash<H: Hasher>(&self, state: &mut H) {
2021        self.deref().hash(state)
2022    }
2023}
2024
2025#[derive(Debug, Clone, PartialEq, Eq)]
2026#[must_use]
2027pub struct FileMapping {
2028    pub name: ActiveNamespaceNode,
2029    mode: Option<FileWriteGuardMode>,
2030}
2031
2032impl Drop for FileMapping {
2033    fn drop(&mut self) {
2034        if let Some(mode) = self.mode {
2035            self.name.entry.node.write_guard_state.lock().release(mode);
2036        }
2037    }
2038}
2039
2040/// Tracks all mounts, keyed by mount point.
2041pub struct Mounts {
2042    mounts: RcuHashMap<WeakKey<DirEntry>, Vec<ArcKey<Mount>>>,
2043}
2044
2045impl Mounts {
2046    pub fn new() -> Self {
2047        Mounts { mounts: RcuHashMap::default() }
2048    }
2049
2050    /// Registers the mount in the global mounts map.
2051    fn register_mount(&self, dir_entry: &Arc<DirEntry>, mount: MountHandle) -> Submount {
2052        let mut mounts = self.mounts.lock();
2053        let key = WeakKey::from(dir_entry);
2054        let mut vec = mounts.get(&key).unwrap_or_else(|| {
2055            dir_entry.set_has_mounts(true);
2056            Vec::new()
2057        });
2058        vec.push(ArcKey(mount.clone()));
2059        mounts.insert(key, vec);
2060        Submount { dir: ArcKey(dir_entry.clone()), mount }
2061    }
2062
2063    /// Unregisters the mount. This is called by `Submount::drop`.
2064    fn unregister_mount(&self, dir_entry: &Arc<DirEntry>, mount: &MountHandle) {
2065        let mut mounts = self.mounts.lock();
2066        let key = WeakKey::from(dir_entry);
2067        if let Some(mut vec) = mounts.get(&key) {
2068            let index = vec.iter().position(|e| e == ArcKey::ref_cast(mount)).unwrap();
2069            if vec.len() == 1 {
2070                mounts.remove(&key);
2071                dir_entry.set_has_mounts(false);
2072            } else {
2073                vec.swap_remove(index);
2074                mounts.insert(key, vec);
2075            }
2076        }
2077    }
2078
2079    /// Unmounts all mounts associated with `dir_entry`. This is called when `dir_entry` is
2080    /// unlinked (which would normally result in EBUSY, but not if it isn't mounted in the local
2081    /// namespace).
2082    pub fn unmount(&self, dir_entry: &DirEntry) {
2083        let mounts = self.mounts.lock().remove(&PtrKey::from(dir_entry as *const _));
2084        if let Some(mounts) = mounts {
2085            if let Some(kernel) = mounts.get(0).map(|m| m.kernel()) {
2086                let mounts_guard = kernel.mounts_lock.lock();
2087                for mount in mounts {
2088                    // Ignore errors.
2089                    let _ = mount.unmount(&mounts_guard, UnmountFlags::DETACH);
2090                }
2091            }
2092        }
2093    }
2094
2095    /// Drain mounts. For each drained mount, force a FileSystem unmount.
2096    // TODO(https://fxbug.dev/295073633): Graceful shutdown should try to first unmount the mounts
2097    // and only force a FileSystem unmount on failure.
2098    pub fn clear(&self) {
2099        for (_dir_entry, mounts) in self.mounts.lock().drain() {
2100            for mount in mounts {
2101                mount.fs.force_unmount_ops();
2102            }
2103        }
2104    }
2105
2106    pub fn sync_all(
2107        &self,
2108        locked: &mut Locked<Unlocked>,
2109        current_task: &CurrentTask,
2110    ) -> Result<(), Errno> {
2111        let mut filesystems = Vec::new();
2112        {
2113            let scope = RcuReadScope::new();
2114            let mut seen = HashSet::new();
2115            for (_dir_entry, m_list) in self.mounts.iter(&scope) {
2116                for m in m_list {
2117                    if seen.insert(Arc::as_ptr(&m.fs)) {
2118                        filesystems.push(m.fs.clone());
2119                    }
2120                }
2121            }
2122        }
2123
2124        for fs in filesystems {
2125            if let Err(e) = fs.sync(locked, current_task) {
2126                log_warn!("sync failed for filesystem {:?}: {:?}", fs.name(), e);
2127            }
2128        }
2129        Ok(())
2130    }
2131}
2132
2133impl Drop for Mount {
2134    fn drop(&mut self) {
2135        // Updating the RCU object without lock is acceptable because this Mount is not available
2136        // anymore by anything.
2137        let kernel = self.kernel();
2138        let peer_group = self.relations.peer_group.cloned();
2139        let upstream = self.relations.upstream.cloned();
2140        self.relations.peer_group.update(None);
2141        self.relations.upstream.update(None);
2142        if peer_group.is_some() || upstream.is_some() {
2143            fuchsia_rcu::rcu_drop(scopeguard::guard(
2144                (kernel, peer_group, upstream),
2145                |(kernel, peer_group, upstream)| {
2146                    let guard = kernel.mounts_lock.lock();
2147                    Mount::unregister_from_peer_group_and_upstream(&guard, peer_group, upstream);
2148                },
2149            ));
2150        }
2151    }
2152}
2153
2154impl fmt::Debug for Mount {
2155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2156        let scope = RcuReadScope::new();
2157        f.debug_struct("Mount")
2158            .field("id", &(self as *const Mount))
2159            .field("root", &self.root)
2160            .field("mountpoint", &self.relations.mountpoint(&scope))
2161            .field("submounts", &self.relations.iter_submounts(&scope).collect::<Vec<_>>())
2162            .finish()
2163    }
2164}
2165
2166/// A RAII object that unregisters a mount when dropped.
2167#[derive(Debug)]
2168struct Submount {
2169    dir: ArcKey<DirEntry>,
2170    mount: MountHandle,
2171}
2172
2173impl Drop for Submount {
2174    fn drop(&mut self) {
2175        self.mount.kernel().mounts.unregister_mount(&self.dir, &self.mount)
2176    }
2177}
2178
2179/// Submount is stored in a mount's submounts hash set, which is keyed by the mountpoint.
2180impl Eq for Submount {}
2181impl PartialEq<Self> for Submount {
2182    fn eq(&self, other: &Self) -> bool {
2183        self.dir == other.dir
2184    }
2185}
2186impl Hash for Submount {
2187    fn hash<H: Hasher>(&self, state: &mut H) {
2188        self.dir.hash(state)
2189    }
2190}
2191
2192impl Borrow<ArcKey<DirEntry>> for Submount {
2193    fn borrow(&self) -> &ArcKey<DirEntry> {
2194        &self.dir
2195    }
2196}
2197
2198#[cfg(test)]
2199mod test {
2200    use crate::fs::tmpfs::TmpFs;
2201    use crate::testing::spawn_kernel_and_run;
2202    use crate::vfs::namespace::DeviceId;
2203    use crate::vfs::{
2204        CallbackSymlinkNode, FsNodeInfo, LookupContext, MountInfo, Namespace, NamespaceNode,
2205        RenameFlags, SymlinkMode, SymlinkTarget, UnlinkKind, WhatToMount,
2206    };
2207    use starnix_uapi::mount_flags::MountpointFlags;
2208    use starnix_uapi::{errno, mode};
2209    use std::sync::Arc;
2210
2211    #[::fuchsia::test]
2212    async fn test_namespace() {
2213        spawn_kernel_and_run(async |locked, current_task| {
2214            let kernel = current_task.kernel();
2215            let root_fs = TmpFs::new_fs(locked, &kernel);
2216            let root_node = Arc::clone(root_fs.root());
2217            let _dev_node = root_node
2218                .create_dir(locked, &current_task, "dev".into())
2219                .expect("failed to mkdir dev");
2220            let dev_fs = TmpFs::new_fs(locked, &kernel);
2221            let dev_root_node = Arc::clone(dev_fs.root());
2222            let _dev_pts_node = dev_root_node
2223                .create_dir(locked, &current_task, "pts".into())
2224                .expect("failed to mkdir pts");
2225
2226            let ns = Namespace::new(root_fs);
2227            let mut context = LookupContext::default();
2228            let dev = ns
2229                .root()
2230                .lookup_child(locked, &current_task, &mut context, "dev".into())
2231                .expect("failed to lookup dev");
2232            dev.mount(WhatToMount::Fs(dev_fs), MountpointFlags::empty())
2233                .expect("failed to mount dev root node");
2234
2235            let mut context = LookupContext::default();
2236            let dev = ns
2237                .root()
2238                .lookup_child(locked, &current_task, &mut context, "dev".into())
2239                .expect("failed to lookup dev");
2240            let mut context = LookupContext::default();
2241            let pts = dev
2242                .lookup_child(locked, &current_task, &mut context, "pts".into())
2243                .expect("failed to lookup pts");
2244            let pts_parent =
2245                pts.parent().ok_or_else(|| errno!(ENOENT)).expect("failed to get parent of pts");
2246            assert!(Arc::ptr_eq(&pts_parent.entry, &dev.entry));
2247
2248            let dev_parent =
2249                dev.parent().ok_or_else(|| errno!(ENOENT)).expect("failed to get parent of dev");
2250            assert!(Arc::ptr_eq(&dev_parent.entry, &ns.root().entry));
2251        })
2252        .await;
2253    }
2254
2255    #[::fuchsia::test]
2256    async fn test_mount_does_not_upgrade() {
2257        spawn_kernel_and_run(async |locked, current_task| {
2258            let kernel = current_task.kernel();
2259            let root_fs = TmpFs::new_fs(locked, &kernel);
2260            let root_node = Arc::clone(root_fs.root());
2261            let _dev_node = root_node
2262                .create_dir(locked, &current_task, "dev".into())
2263                .expect("failed to mkdir dev");
2264            let dev_fs = TmpFs::new_fs(locked, &kernel);
2265            let dev_root_node = Arc::clone(dev_fs.root());
2266            let _dev_pts_node = dev_root_node
2267                .create_dir(locked, &current_task, "pts".into())
2268                .expect("failed to mkdir pts");
2269
2270            let ns = Namespace::new(root_fs);
2271            let mut context = LookupContext::default();
2272            let dev = ns
2273                .root()
2274                .lookup_child(locked, &current_task, &mut context, "dev".into())
2275                .expect("failed to lookup dev");
2276            dev.mount(WhatToMount::Fs(dev_fs), MountpointFlags::empty())
2277                .expect("failed to mount dev root node");
2278            let mut context = LookupContext::default();
2279            let new_dev = ns
2280                .root()
2281                .lookup_child(locked, &current_task, &mut context, "dev".into())
2282                .expect("failed to lookup dev again");
2283            assert!(!Arc::ptr_eq(&dev.entry, &new_dev.entry));
2284            assert_ne!(&dev, &new_dev);
2285
2286            let mut context = LookupContext::default();
2287            let _new_pts = new_dev
2288                .lookup_child(locked, &current_task, &mut context, "pts".into())
2289                .expect("failed to lookup pts");
2290            let mut context = LookupContext::default();
2291            assert!(dev.lookup_child(locked, &current_task, &mut context, "pts".into()).is_err());
2292        })
2293        .await;
2294    }
2295
2296    #[::fuchsia::test]
2297    async fn test_path() {
2298        spawn_kernel_and_run(async |locked, current_task| {
2299            let kernel = current_task.kernel();
2300            let root_fs = TmpFs::new_fs(locked, &kernel);
2301            let root_node = Arc::clone(root_fs.root());
2302            let _dev_node = root_node
2303                .create_dir(locked, &current_task, "dev".into())
2304                .expect("failed to mkdir dev");
2305            let dev_fs = TmpFs::new_fs(locked, &kernel);
2306            let dev_root_node = Arc::clone(dev_fs.root());
2307            let _dev_pts_node = dev_root_node
2308                .create_dir(locked, &current_task, "pts".into())
2309                .expect("failed to mkdir pts");
2310
2311            let ns = Namespace::new(root_fs);
2312            let mut context = LookupContext::default();
2313            let dev = ns
2314                .root()
2315                .lookup_child(locked, &current_task, &mut context, "dev".into())
2316                .expect("failed to lookup dev");
2317            dev.mount(WhatToMount::Fs(dev_fs), MountpointFlags::empty())
2318                .expect("failed to mount dev root node");
2319
2320            let mut context = LookupContext::default();
2321            let dev = ns
2322                .root()
2323                .lookup_child(locked, &current_task, &mut context, "dev".into())
2324                .expect("failed to lookup dev");
2325            let mut context = LookupContext::default();
2326            let pts = dev
2327                .lookup_child(locked, &current_task, &mut context, "pts".into())
2328                .expect("failed to lookup pts");
2329
2330            assert_eq!("/", ns.root().path_escaping_chroot());
2331            assert_eq!("/dev", dev.path_escaping_chroot());
2332            assert_eq!("/dev/pts", pts.path_escaping_chroot());
2333        })
2334        .await;
2335    }
2336
2337    #[::fuchsia::test]
2338    async fn test_shadowing() {
2339        spawn_kernel_and_run(async |locked, current_task| {
2340            let kernel = current_task.kernel();
2341            let root_fs = TmpFs::new_fs(locked, &kernel);
2342            let ns = Namespace::new(root_fs.clone());
2343            let _foo_node = root_fs.root().create_dir(locked, &current_task, "foo".into()).unwrap();
2344            let mut context = LookupContext::default();
2345            let foo_dir =
2346                ns.root().lookup_child(locked, &current_task, &mut context, "foo".into()).unwrap();
2347
2348            let foofs1 = TmpFs::new_fs(locked, &kernel);
2349            foo_dir.mount(WhatToMount::Fs(foofs1.clone()), MountpointFlags::empty()).unwrap();
2350            let mut context = LookupContext::default();
2351            assert!(Arc::ptr_eq(
2352                &ns.root()
2353                    .lookup_child(locked, &current_task, &mut context, "foo".into())
2354                    .unwrap()
2355                    .entry,
2356                foofs1.root()
2357            ));
2358            let foo_dir =
2359                ns.root().lookup_child(locked, &current_task, &mut context, "foo".into()).unwrap();
2360
2361            let ns_clone = ns.clone_namespace(&kernel.mounts_lock.lock());
2362
2363            let foofs2 = TmpFs::new_fs(locked, &kernel);
2364            foo_dir.mount(WhatToMount::Fs(foofs2.clone()), MountpointFlags::empty()).unwrap();
2365            let mut context = LookupContext::default();
2366            assert!(Arc::ptr_eq(
2367                &ns.root()
2368                    .lookup_child(locked, &current_task, &mut context, "foo".into())
2369                    .unwrap()
2370                    .entry,
2371                foofs2.root()
2372            ));
2373
2374            assert!(Arc::ptr_eq(
2375                &ns_clone
2376                    .root()
2377                    .lookup_child(
2378                        locked,
2379                        &current_task,
2380                        &mut LookupContext::default(),
2381                        "foo".into()
2382                    )
2383                    .unwrap()
2384                    .entry,
2385                foofs1.root()
2386            ));
2387        })
2388        .await;
2389    }
2390
2391    #[::fuchsia::test]
2392    async fn test_unlink_mounted_directory() {
2393        spawn_kernel_and_run(async |locked, current_task| {
2394            let kernel = current_task.kernel();
2395            let root_fs = TmpFs::new_fs(locked, &kernel);
2396            let ns1 = Namespace::new(root_fs.clone());
2397            let ns2 = Namespace::new(root_fs.clone());
2398            let _foo_node = root_fs.root().create_dir(locked, &current_task, "foo".into()).unwrap();
2399            let mut context = LookupContext::default();
2400            let foo_dir =
2401                ns1.root().lookup_child(locked, &current_task, &mut context, "foo".into()).unwrap();
2402
2403            let foofs = TmpFs::new_fs(locked, &kernel);
2404            foo_dir.mount(WhatToMount::Fs(foofs), MountpointFlags::empty()).unwrap();
2405
2406            // Trying to unlink from ns1 should fail.
2407            assert_eq!(
2408                ns1.root()
2409                    .unlink(locked, &current_task, "foo".into(), UnlinkKind::Directory, false)
2410                    .unwrap_err(),
2411                errno!(EBUSY),
2412            );
2413
2414            // But unlinking from ns2 should succeed.
2415            ns2.root()
2416                .unlink(locked, &current_task, "foo".into(), UnlinkKind::Directory, false)
2417                .expect("unlink failed");
2418
2419            // And it should no longer show up in ns1.
2420            assert_eq!(
2421                ns1.root()
2422                    .unlink(locked, &current_task, "foo".into(), UnlinkKind::Directory, false)
2423                    .unwrap_err(),
2424                errno!(ENOENT),
2425            );
2426        })
2427        .await;
2428    }
2429
2430    #[::fuchsia::test]
2431    async fn test_rename_mounted_directory() {
2432        spawn_kernel_and_run(async |locked, current_task| {
2433            let kernel = current_task.kernel();
2434            let root_fs = TmpFs::new_fs(locked, &kernel);
2435            let ns1 = Namespace::new(root_fs.clone());
2436            let ns2 = Namespace::new(root_fs.clone());
2437            let _foo_node = root_fs.root().create_dir(locked, &current_task, "foo".into()).unwrap();
2438            let _bar_node = root_fs.root().create_dir(locked, &current_task, "bar".into()).unwrap();
2439            let _baz_node = root_fs.root().create_dir(locked, &current_task, "baz".into()).unwrap();
2440            let mut context = LookupContext::default();
2441            let foo_dir =
2442                ns1.root().lookup_child(locked, &current_task, &mut context, "foo".into()).unwrap();
2443
2444            let foofs = TmpFs::new_fs(locked, &kernel);
2445            foo_dir.mount(WhatToMount::Fs(foofs), MountpointFlags::empty()).unwrap();
2446
2447            // Trying to rename over foo from ns1 should fail.
2448            let root = ns1.root();
2449            assert_eq!(
2450                NamespaceNode::rename(
2451                    locked,
2452                    &current_task,
2453                    &root,
2454                    "bar".into(),
2455                    &root,
2456                    "foo".into(),
2457                    RenameFlags::empty()
2458                )
2459                .unwrap_err(),
2460                errno!(EBUSY),
2461            );
2462            // Likewise the other way.
2463            assert_eq!(
2464                NamespaceNode::rename(
2465                    locked,
2466                    &current_task,
2467                    &root,
2468                    "foo".into(),
2469                    &root,
2470                    "bar".into(),
2471                    RenameFlags::empty()
2472                )
2473                .unwrap_err(),
2474                errno!(EBUSY),
2475            );
2476
2477            // But renaming from ns2 should succeed.
2478            let root = ns2.root();
2479
2480            // First rename the directory with the mount.
2481            NamespaceNode::rename(
2482                locked,
2483                &current_task,
2484                &root,
2485                "foo".into(),
2486                &root,
2487                "bar".into(),
2488                RenameFlags::empty(),
2489            )
2490            .expect("rename failed");
2491
2492            // Renaming over a directory with a mount should also work.
2493            NamespaceNode::rename(
2494                locked,
2495                &current_task,
2496                &root,
2497                "baz".into(),
2498                &root,
2499                "bar".into(),
2500                RenameFlags::empty(),
2501            )
2502            .expect("rename failed");
2503
2504            // "foo" and "baz" should no longer show up in ns1.
2505            assert_eq!(
2506                ns1.root()
2507                    .lookup_child(locked, &current_task, &mut context, "foo".into())
2508                    .unwrap_err(),
2509                errno!(ENOENT)
2510            );
2511            assert_eq!(
2512                ns1.root()
2513                    .lookup_child(locked, &current_task, &mut context, "baz".into())
2514                    .unwrap_err(),
2515                errno!(ENOENT)
2516            );
2517        })
2518        .await;
2519    }
2520
2521    /// Symlinks which need to be traversed across types (nodes and paths), as well as across
2522    /// owning directories, can be tricky to get right.
2523    #[::fuchsia::test]
2524    async fn test_lookup_with_symlink_chain() {
2525        spawn_kernel_and_run(async |locked, current_task| {
2526            // Set up the root filesystem
2527            let kernel = current_task.kernel();
2528            let root_fs = TmpFs::new_fs(locked, &kernel);
2529            let root_node = Arc::clone(root_fs.root());
2530            let _first_subdir_node = root_node
2531                .create_dir(locked, &current_task, "first_subdir".into())
2532                .expect("failed to mkdir dev");
2533            let _second_subdir_node = root_node
2534                .create_dir(locked, &current_task, "second_subdir".into())
2535                .expect("failed to mkdir dev");
2536
2537            // Set up two subdirectories under the root filesystem
2538            let first_subdir_fs = TmpFs::new_fs(locked, &kernel);
2539            let second_subdir_fs = TmpFs::new_fs(locked, &kernel);
2540
2541            let ns = Namespace::new(root_fs);
2542            let mut context = LookupContext::default();
2543            let first_subdir = ns
2544                .root()
2545                .lookup_child(locked, &current_task, &mut context, "first_subdir".into())
2546                .expect("failed to lookup first_subdir");
2547            first_subdir
2548                .mount(WhatToMount::Fs(first_subdir_fs), MountpointFlags::empty())
2549                .expect("failed to mount first_subdir fs node");
2550            let second_subdir = ns
2551                .root()
2552                .lookup_child(locked, &current_task, &mut context, "second_subdir".into())
2553                .expect("failed to lookup second_subdir");
2554            second_subdir
2555                .mount(WhatToMount::Fs(second_subdir_fs), MountpointFlags::empty())
2556                .expect("failed to mount second_subdir fs node");
2557
2558            // Create the symlink structure. To trigger potential symlink traversal bugs, we're going
2559            // for the following directory structure:
2560            // / (root)
2561            //     + first_subdir/
2562            //         - real_file
2563            //         - path_symlink (-> real_file)
2564            //     + second_subdir/
2565            //         - node_symlink (-> path_symlink)
2566            let real_file_node = first_subdir
2567                .create_node(
2568                    locked,
2569                    &current_task,
2570                    "real_file".into(),
2571                    mode!(IFREG, 0o777),
2572                    DeviceId::NONE,
2573                )
2574                .expect("failed to create real_file");
2575            first_subdir
2576                .create_symlink(locked, &current_task, "path_symlink".into(), "real_file".into())
2577                .expect("failed to create path_symlink");
2578
2579            let mut no_follow_lookup_context = LookupContext::new(SymlinkMode::NoFollow);
2580            let path_symlink_node = first_subdir
2581                .lookup_child(
2582                    locked,
2583                    &current_task,
2584                    &mut no_follow_lookup_context,
2585                    "path_symlink".into(),
2586                )
2587                .expect("Failed to lookup path_symlink");
2588
2589            // The second symlink needs to be of type SymlinkTarget::Node in order to trip the sensitive
2590            // code path. There's no easy method for creating this type of symlink target, so we'll need
2591            // to construct a node from scratch and insert it into the directory manually.
2592            let node_symlink_node = second_subdir.entry.node.fs().create_node_and_allocate_node_id(
2593                CallbackSymlinkNode::new(move || {
2594                    let node = path_symlink_node.clone();
2595                    Ok(SymlinkTarget::Node(node))
2596                }),
2597                FsNodeInfo::new(mode!(IFLNK, 0o777), current_task.current_fscred()),
2598            );
2599            second_subdir
2600                .entry
2601                .create_entry(
2602                    locked,
2603                    &current_task,
2604                    &MountInfo::detached(),
2605                    "node_symlink".into(),
2606                    move |_locked, _dir, _mount, _name| Ok(node_symlink_node),
2607                )
2608                .expect("failed to create node_symlink entry");
2609
2610            // Finally, exercise the lookup under test.
2611            let mut follow_lookup_context = LookupContext::new(SymlinkMode::Follow);
2612            let node_symlink_resolution = second_subdir
2613                .lookup_child(
2614                    locked,
2615                    &current_task,
2616                    &mut follow_lookup_context,
2617                    "node_symlink".into(),
2618                )
2619                .expect("lookup with symlink chain failed");
2620
2621            // The lookup resolution should have correctly followed the symlinks to the real_file node.
2622            assert!(node_symlink_resolution.entry.node.ino == real_file_node.entry.node.ino);
2623        })
2624        .await;
2625    }
2626}