Skip to main content

starnix_core/vfs/
dir_entry.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::CurrentTask;
7use crate::vfs::{
8    CheckAccessReason, DirectoryMode, FileHandle, FileObject, FsLockDepType, FsNodeHandle,
9    FsNodeLinkBehavior, FsStr, FsString, LookupVec, MountInfo, Mounts, NamespaceNode, PathBuilder,
10    UnlinkKind, inotify_hook, path,
11};
12use atomic_bitflags::atomic_bitflags;
13use bitflags::bitflags;
14use bstr::ByteSlice;
15use fuchsia_rcu::{RcuArc, RcuReadScope};
16use fuchsia_sync::ResetDependencies;
17use fxfs_unicode::{CasefoldStr, utf8_bytes};
18use smallvec::SmallVec;
19use starnix_rcu::RcuString;
20use starnix_sync::{
21    DirEntryChildrenLevel, DirEntryChildrenRecursiveLevel, DynamicLockDepRwLock,
22    FuseDirEntryChildrenLevel, LockDepWriteGuard, allow_subclass,
23};
24use starnix_uapi::auth::FsCred;
25use starnix_uapi::errors::{ENOENT, Errno};
26use starnix_uapi::file_mode::{Access, FileMode};
27use starnix_uapi::inotify_mask::InotifyMask;
28use starnix_uapi::open_flags::OpenFlags;
29use starnix_uapi::{NAME_MAX, RENAME_EXCHANGE, RENAME_NOREPLACE, RENAME_WHITEOUT, error};
30use std::collections::BTreeMap;
31use std::fmt;
32use std::ops::Deref;
33use std::sync::atomic::Ordering;
34use std::sync::{Arc, Weak};
35
36bitflags! {
37    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
38    pub struct RenameFlags: u32 {
39        // Exchange the entries.
40        const EXCHANGE = RENAME_EXCHANGE;
41
42        // Don't overwrite an existing DirEntry.
43        const NOREPLACE = RENAME_NOREPLACE;
44
45        // Create a "whiteout" object to replace the file.
46        const WHITEOUT = RENAME_WHITEOUT;
47
48        // Allow replacing any file with a directory. This is an internal flag used only
49        // internally inside Starnix for OverlayFS.
50        const REPLACE_ANY = 1 << 31;
51
52        // Internal flags that cannot be passed to `sys_rename()`
53        const INTERNAL = Self::REPLACE_ANY.bits();
54    }
55}
56
57pub trait DirEntryOps: Send + Sync + 'static {
58    /// Revalidate the [`DirEntry`], if needed.
59    ///
60    /// Most filesystems don't need to do any revalidations because they are "local"
61    /// and all changes to nodes go through the kernel. However some filesystems
62    /// allow changes to happen through other means (e.g. NFS, FUSE) and these
63    /// filesystems need a way to let the kernel know it may need to refresh its
64    /// cached metadata. This method provides that hook for such filesystems.
65    ///
66    /// For more details, see:
67    ///  - https://www.halolinux.us/kernel-reference/the-dentry-cache.html
68    ///  - https://www.kernel.org/doc/html/latest/filesystems/path-lookup.html#revalidation-and-automounts
69    ///  - https://lwn.net/Articles/649115/
70    ///  - https://www.infradead.org/~mchehab/kernel_docs/filesystems/path-walking.html
71    ///
72    /// Returns `Ok(valid)` where `valid` indicates if the `DirEntry` is still valid,
73    /// or an error.
74    fn revalidate(&self, _: &CurrentTask, _: &DirEntry) -> Result<bool, Errno> {
75        Ok(true)
76    }
77}
78
79atomic_bitflags! {
80    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
81    pub struct DirEntryFlags: u8 {
82        /// Whether this directory entry has been removed from the tree.
83        const IS_DEAD = 1 << 0;
84
85        /// Whether the entry has filesystems mounted on top of it.
86        const HAS_MOUNTS = 1 << 1;
87    }
88}
89
90pub struct DefaultDirEntryOps;
91
92impl DirEntryOps for DefaultDirEntryOps {}
93
94/// An entry in a directory.
95///
96/// This structure assigns a name to an FsNode in a given file system. An
97/// FsNode might have multiple directory entries, for example if there are more
98/// than one hard link to the same FsNode. In those cases, each hard link will
99/// have a different parent and a different local_name because each hard link
100/// has its own DirEntry object.
101///
102/// A directory cannot have more than one hard link, which means there is a
103/// single DirEntry for each Directory FsNode. That invariant lets us store the
104/// children for a directory in the DirEntry rather than in the FsNode.
105pub struct DirEntry {
106    /// The FsNode referenced by this DirEntry.
107    ///
108    /// A given FsNode can be referenced by multiple DirEntry objects, for
109    /// example if there are multiple hard links to a given FsNode.
110    pub node: FsNodeHandle,
111
112    /// The [`DirEntryOps`] for this `DirEntry`.
113    ///
114    /// The `DirEntryOps` are implemented by the individual file systems to provide
115    /// specific behaviours for this `DirEntry`.
116    ops: Box<dyn DirEntryOps>,
117
118    /// The parent DirEntry.
119    ///
120    /// The DirEntry tree has strong references from child-to-parent and weak
121    /// references from parent-to-child. This design ensures that the parent
122    /// chain is always populated in the cache, but some children might be
123    /// missing from the cache.
124    parent: RcuArc<DirEntry>,
125
126    /// The [`DirEntryFlags`] for this `DirEntry`.
127    flags: AtomicDirEntryFlags,
128
129    /// The name that this parent calls this child.
130    ///
131    /// This name might not be reflected in the full path in the namespace that
132    /// contains this DirEntry. For example, this DirEntry might be the root of
133    /// a chroot.
134    ///
135    /// Most callers that want to work with names for DirEntries should use the
136    /// NamespaceNodes.
137    local_name: RcuString,
138
139    /// A partial cache of the children of this DirEntry.
140    ///
141    /// DirEntries are added to this cache when they are looked up and removed
142    /// when they are no longer referenced.
143    ///
144    // FIXME(b/379929394): The lock ordering here assumes parent-to-child lock acquisition, which
145    // a number of algorithms in the DirEntry operations also assume. This assumption can be broken
146    // by the rename operation, which can move nodes around the hierarchy. See the referenced bug
147    // for more details, the current mitigations, and potentials for long-term solutions.
148    children: DynamicLockDepRwLock<DirEntryChildren>,
149}
150
151pub type DirEntryHandle = Arc<DirEntry>;
152
153impl DirEntry {
154    #[allow(clippy::let_and_return)]
155    pub fn new_uncached(
156        node: FsNodeHandle,
157        parent: Option<DirEntryHandle>,
158        local_name: FsString,
159    ) -> DirEntryHandle {
160        let ops = node.create_dir_entry_ops();
161        let fs_lockdep_type = node.fs().fs_lockdep_type();
162        let casefold = node.info().casefold;
163        let initial_children = DirEntryChildren::new(casefold);
164        let result = Arc::new(DirEntry {
165            node,
166            ops,
167            parent: RcuArc::new(parent),
168            flags: Default::default(),
169            local_name: local_name.into(),
170            children: match fs_lockdep_type {
171                FsLockDepType::Normal => {
172                    DynamicLockDepRwLock::new::<DirEntryChildrenLevel>(initial_children)
173                }
174                FsLockDepType::Recursive => {
175                    DynamicLockDepRwLock::new::<DirEntryChildrenRecursiveLevel>(initial_children)
176                }
177                FsLockDepType::Fuse => {
178                    DynamicLockDepRwLock::new::<FuseDirEntryChildrenLevel>(initial_children)
179                }
180            },
181        });
182        #[cfg(any(test, debug_assertions))]
183        {
184            // Taking this lock tells the lock tracing system about the parent/child ordering
185            // relation.
186            let _token = allow_subclass();
187            let _l1 = result.children.read();
188        }
189        result
190    }
191
192    pub fn new(
193        node: FsNodeHandle,
194        parent: Option<DirEntryHandle>,
195        local_name: FsString,
196    ) -> DirEntryHandle {
197        let result = Self::new_uncached(node, parent, local_name);
198        result.node.fs().did_create_dir_entry(&result);
199        result
200    }
201
202    /// Returns a new DirEntry for the given `node` without parent. The entry has no local name and
203    /// is not cached.
204    pub fn new_unrooted(node: FsNodeHandle) -> DirEntryHandle {
205        Self::new_uncached(node, None, FsString::default())
206    }
207
208    /// Returns a new `DirEntry` that is ready marked as having been deleted.
209    pub fn new_deleted(
210        node: FsNodeHandle,
211        parent: Option<DirEntryHandle>,
212        local_name: FsString,
213    ) -> DirEntryHandle {
214        let entry = DirEntry::new_uncached(node, parent, local_name);
215        entry.raise_flags(DirEntryFlags::IS_DEAD);
216        entry
217    }
218
219    /// Returns a file handle to this entry, associated with an anonymous namespace.
220    pub fn open_anonymous(
221        self: &DirEntryHandle,
222        current_task: &CurrentTask,
223        flags: OpenFlags,
224    ) -> Result<FileHandle, Errno> {
225        let ops = self.node.create_file_ops(current_task, flags)?;
226        FileObject::new(current_task, ops, NamespaceNode::new_anonymous(self.clone()), flags)
227    }
228
229    /// Set the children of this DirEntry to the given `children`. This should only ever be called
230    /// when children is empty.
231    pub fn set_children(self: &DirEntryHandle, children: BTreeMap<FsString, DirEntryHandle>) {
232        let mut dir_entry_children = self.lock_children();
233        assert!(dir_entry_children.children.is_empty());
234        for (name, child) in children.into_iter() {
235            child.set_parent(self.clone());
236            dir_entry_children.children.insert(name.as_ref(), Arc::downgrade(&child));
237        }
238    }
239
240    fn lock_children<'a>(self: &'a DirEntryHandle) -> DirEntryLockedChildren<'a> {
241        DirEntryLockedChildren { entry: self, children: self.children.write() }
242    }
243
244    /// Sets whether this directory is casefolded.
245    ///
246    /// # Errors
247    ///
248    /// * [`ENOTDIR`]: If this entry is not a directory.
249    /// * [`ENOTSUP`]: If enabling casefolding on a filesystem without casefold support.
250    /// * [`ENOTEMPTY`]: If the directory is not empty.
251    /// * Propagates errors from [`FsNode::update_attributes`].
252    pub fn set_casefold(&self, current_task: &CurrentTask, casefold: bool) -> Result<(), Errno> {
253        if self.node.info().casefold == casefold {
254            return Ok(());
255        }
256        if !self.node.is_dir() {
257            return error!(ENOTDIR);
258        }
259        if casefold && !self.node.fs().has_casefold_support() {
260            return error!(ENOTSUP);
261        }
262
263        // It is important that the `children` write lock is held across the call to
264        // `update_attributes` below because it acts as the guard preventing the directory's
265        // case-sensitivity from changing concurrently. Elsewhere, we make decisions based on the
266        // state of `children` to determine whether a directory is case-folded or not.
267        let mut children = self.children.write();
268        if children.is_casefold() == casefold {
269            return Ok(());
270        }
271
272        self.node.update_attributes(current_task, |info| {
273            info.casefold = casefold;
274            Ok(())
275        })?;
276
277        children.set_casefold(casefold);
278        Ok(())
279    }
280
281    /// The parent DirEntry.
282    pub fn parent(&self) -> Option<DirEntryHandle> {
283        self.parent.upgrade()
284    }
285
286    /// Set the parent of this DirEntry.
287    pub fn set_parent(&self, parent: DirEntryHandle) {
288        self.parent.update(Some(parent));
289    }
290
291    /// The parent DirEntry object or this DirEntry if this entry is the root.
292    ///
293    /// Useful when traversing up the tree if you always want to find a parent
294    /// (e.g., for "..").
295    ///
296    /// Be aware that the root of one file system might be mounted as a child
297    /// in another file system. For that reason, consider walking the
298    /// NamespaceNode tree (which understands mounts) rather than the DirEntry
299    /// tree.
300    pub fn parent_or_self(self: &DirEntryHandle) -> DirEntryHandle {
301        self.parent().unwrap_or_else(|| self.clone())
302    }
303
304    /// The name that this parent calls this child.
305    ///
306    /// The reference is only valid for the duration of the RCU read scope.
307    pub fn local_name<'a>(&self, scope: &'a RcuReadScope) -> &'a FsStr {
308        self.local_name.read(scope)
309    }
310
311    /// Whether the given name has special semantics as a directory entry.
312    ///
313    /// Specifically, whether the name is empty (which means "self"), dot
314    /// (which also means "self"), or dot dot (which means "parent").
315    pub fn is_reserved_name(name: &FsStr) -> bool {
316        name.is_empty() || name == "." || name == ".."
317    }
318
319    /// Returns the flags of this DirEntry.
320    pub fn flags(&self) -> DirEntryFlags {
321        self.flags.load(Ordering::Acquire)
322    }
323
324    /// Raises the flags of this DirEntry.
325    ///
326    /// Returns the flags of this DirEntry before the flags were raised.
327    pub fn raise_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
328        self.flags.fetch_or(flags, Ordering::AcqRel)
329    }
330
331    /// Lowers the flags of this DirEntry.
332    ///
333    /// Returns the flags of this DirEntry before the flags were lowered.
334    pub fn lower_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
335        self.flags.fetch_and(!flags, Ordering::AcqRel)
336    }
337
338    /// Returns true if this DirEntry is dead.
339    pub fn is_dead(&self) -> bool {
340        self.flags().contains(DirEntryFlags::IS_DEAD)
341    }
342
343    /// Look up a directory entry with the given name as direct child of this
344    /// entry.
345    pub fn component_lookup(
346        self: &DirEntryHandle,
347        current_task: &CurrentTask,
348        mount: &MountInfo,
349        name: &FsStr,
350    ) -> Result<DirEntryHandle, Errno> {
351        let (node, _) =
352            self.get_or_create_child(current_task, mount, name, |d, _mount, name| {
353                Ok((d.node.ops().lookup(d, current_task, name)?, CreationStatus::Existed))
354            })?;
355        Ok(node)
356    }
357
358    pub fn get_children_pipelined(
359        self: &DirEntryHandle,
360        current_task: &CurrentTask,
361        mount: &MountInfo,
362        names: &[&FsStr],
363    ) -> LookupVec<Result<DirEntryHandle, Errno>> {
364        let mut nodes = LookupVec::new();
365        let mut results = LookupVec::new();
366        let mut current_parent = self.clone();
367        for i in 0..names.len() {
368            let next_node = nodes.pop();
369            match current_parent.get_or_create_child(
370                current_task,
371                mount,
372                names[i],
373                |d, _mount, _name| {
374                    let node = if let Some(node) = next_node {
375                        node?
376                    } else {
377                        nodes = d.node.ops().lookup_pipelined(d, current_task, &names[i..]);
378                        nodes.reverse();
379                        nodes.pop().expect("lookup_pipelined must return at least one result")?
380                    };
381                    Ok((node, CreationStatus::Existed))
382                },
383            ) {
384                Ok((entry, _)) => {
385                    results.push(Ok(entry.clone()));
386                    current_parent = entry;
387                }
388                Err(e) => {
389                    results.push(Err(e));
390                    break;
391                }
392            }
393        }
394        results
395    }
396
397    /// Creates a new DirEntry
398    ///
399    /// The create_node_fn function is called to create the underlying FsNode
400    /// for the DirEntry.
401    ///
402    /// If the entry already exists, create_node_fn is not called, and EEXIST is
403    /// returned.
404    pub fn create_entry(
405        self: &DirEntryHandle,
406        current_task: &CurrentTask,
407        mount: &MountInfo,
408        name: &FsStr,
409        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
410    ) -> Result<DirEntryHandle, Errno> {
411        let (entry, status) =
412            self.create_entry_internal(current_task, mount, name, create_node_fn)?;
413        if status == CreationStatus::Existed {
414            return error!(EEXIST);
415        }
416        Ok(entry)
417    }
418
419    /// Creates a new DirEntry. Works just like create_entry, except if the entry already exists,
420    /// it is returned. Also returns whether the entry was created.
421    pub fn get_or_create_entry(
422        self: &DirEntryHandle,
423        current_task: &CurrentTask,
424        mount: &MountInfo,
425        name: &FsStr,
426        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
427    ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
428        self.create_entry_internal(current_task, mount, name, create_node_fn)
429    }
430
431    fn create_entry_internal(
432        self: &DirEntryHandle,
433        current_task: &CurrentTask,
434        mount: &MountInfo,
435        name: &FsStr,
436        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
437    ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
438        if DirEntry::is_reserved_name(name) {
439            return error!(EEXIST);
440        }
441        // TODO: Do we need to check name for embedded NUL characters?
442        if name.len() > NAME_MAX as usize {
443            return error!(ENAMETOOLONG);
444        }
445        if name.contains(&path::SEPARATOR) {
446            return error!(EINVAL);
447        }
448        let (entry, status) =
449            self.get_or_create_child(current_task, mount, name, |d, mount, name| {
450                match d.node.ops().lookup(d, current_task, name) {
451                    Ok(node) => Ok((node, CreationStatus::Existed)),
452                    Err(e) if e == ENOENT => {
453                        Ok((create_node_fn(&d.node, mount, name)?, CreationStatus::Created))
454                    }
455                    Err(e) => Err(e),
456                }
457            })?;
458        if status == CreationStatus::Created {
459            // An entry was created. Update the ctime and mtime of this directory.
460            self.node.update_ctime_mtime();
461            entry.notify_creation();
462        }
463        Ok((entry, status))
464    }
465
466    // This is marked as test-only because it sets the owner/group to root instead of the current
467    // user to save a bit of typing in tests, but this shouldn't happen silently in production.
468    #[cfg(test)]
469    pub fn create_dir(
470        self: &DirEntryHandle,
471        current_task: &CurrentTask,
472        name: &FsStr,
473    ) -> Result<DirEntryHandle, Errno> {
474        self.create_dir_for_testing(current_task, name)
475    }
476
477    // This function is for testing because it sets the owner/group to root instead of the current
478    // user to save a bit of typing in tests, but this shouldn't happen silently in production.
479    pub fn create_dir_for_testing(
480        self: &DirEntryHandle,
481        current_task: &CurrentTask,
482        name: &FsStr,
483    ) -> Result<DirEntryHandle, Errno> {
484        // TODO: apply_umask
485        self.create_entry(current_task, &MountInfo::detached(), name, |dir, mount, name| {
486            dir.create_node(
487                current_task,
488                mount,
489                name,
490                starnix_uapi::file_mode::mode!(IFDIR, 0o777),
491                starnix_uapi::device_id::DeviceId::NONE,
492                FsCred::root(),
493            )
494        })
495    }
496
497    /// Creates an anonymous file.
498    ///
499    /// The FileMode::IFMT of the FileMode is always FileMode::IFREG.
500    ///
501    /// Used by O_TMPFILE.
502    pub fn create_tmpfile(
503        self: &DirEntryHandle,
504        current_task: &CurrentTask,
505        mount: &MountInfo,
506        mode: FileMode,
507        owner: FsCred,
508        flags: OpenFlags,
509    ) -> Result<DirEntryHandle, Errno> {
510        // Only directories can have children.
511        if !self.node.is_dir() {
512            return error!(ENOTDIR);
513        }
514        assert!(mode.is_reg());
515
516        // From <https://man7.org/linux/man-pages/man2/open.2.html>:
517        //
518        //   Specifying O_EXCL in conjunction with O_TMPFILE prevents a
519        //   temporary file from being linked into the filesystem in
520        //   the above manner.  (Note that the meaning of O_EXCL in
521        //   this case is different from the meaning of O_EXCL
522        //   otherwise.)
523        let link_behavior = if flags.contains(OpenFlags::EXCL) {
524            FsNodeLinkBehavior::Disallowed
525        } else {
526            FsNodeLinkBehavior::Allowed
527        };
528
529        let node = self.node.create_tmpfile(current_task, mount, mode, owner, link_behavior)?;
530        let local_name = format!("#{}", node.ino).into();
531        Ok(DirEntry::new_deleted(node, Some(self.clone()), local_name))
532    }
533
534    pub fn unlink(
535        self: &DirEntryHandle,
536        current_task: &CurrentTask,
537        mount: &MountInfo,
538        name: &FsStr,
539        kind: UnlinkKind,
540        directory_mode: DirectoryMode,
541    ) -> Result<(), Errno> {
542        assert!(!DirEntry::is_reserved_name(name));
543
544        // child_to_unlink *must* be dropped after self_children (even in the error paths).
545        let child_to_unlink;
546
547        let mut self_children = self.lock_children();
548        child_to_unlink = self_children.component_lookup(current_task, mount, name)?;
549        child_to_unlink.require_no_mounts(mount)?;
550
551        // Check that this filesystem entry must be a directory. This can
552        // happen if the path terminates with a trailing slash.
553        //
554        // Example: If we're unlinking a symlink `/foo/bar/`, this would
555        // result in `ENOTDIR` because of the trailing slash, even if
556        // `UnlinkKind::NonDirectory` was used.
557        if directory_mode == DirectoryMode::MustBeDirectory && !child_to_unlink.node.is_dir() {
558            return error!(ENOTDIR);
559        }
560
561        match kind {
562            UnlinkKind::Directory => {
563                if !child_to_unlink.node.is_dir() {
564                    return error!(ENOTDIR);
565                }
566            }
567            UnlinkKind::NonDirectory => {
568                if child_to_unlink.node.is_dir() {
569                    return error!(EISDIR);
570                }
571            }
572        }
573
574        self.node.unlink(current_task, mount, name, &child_to_unlink.node)?;
575        self_children.children.remove(name);
576
577        std::mem::drop(self_children);
578        child_to_unlink.destroy(&current_task.kernel().mounts);
579
580        Ok(())
581    }
582
583    /// Destroy this directory entry.
584    ///
585    /// Notice that this method takes `self` by value to destroy this reference.
586    fn destroy(self: DirEntryHandle, mounts: &Mounts) {
587        let was_already_dead =
588            self.raise_flags(DirEntryFlags::IS_DEAD).contains(DirEntryFlags::IS_DEAD);
589        if was_already_dead {
590            return;
591        }
592        let unmount =
593            self.lower_flags(DirEntryFlags::HAS_MOUNTS).contains(DirEntryFlags::HAS_MOUNTS);
594        self.node.fs().will_destroy_dir_entry(&self);
595        if unmount {
596            mounts.unmount(&self);
597        }
598        self.notify_deletion();
599    }
600
601    /// Returns whether this entry is a descendant of |other|.
602    pub fn is_descendant_of(self: &DirEntryHandle, other: &DirEntryHandle) -> bool {
603        if Arc::ptr_eq(self, other) {
604            return true;
605        }
606        let mut current = self.parent();
607        while let Some(entry) = current {
608            if Arc::ptr_eq(&entry, other) {
609                // We found |other|.
610                return true;
611            }
612            current = entry.parent();
613        }
614        // We reached the root of the file system.
615        false
616    }
617
618    /// Rename the file with old_basename in old_parent to new_basename in
619    /// new_parent.
620    ///
621    /// old_parent and new_parent must belong to the same file system.
622    pub fn rename(
623        current_task: &CurrentTask,
624        old_parent: &DirEntryHandle,
625        old_mount: &MountInfo,
626        old_basename: &FsStr,
627        new_parent: &DirEntryHandle,
628        new_mount: &MountInfo,
629        new_basename: &FsStr,
630        flags: RenameFlags,
631    ) -> Result<(), Errno> {
632        // The nodes we are touching must be part of the same mount.
633        if old_mount != new_mount {
634            return error!(EXDEV);
635        }
636
637        // The mounts are equals, choose one.
638        let mount = old_mount;
639
640        // If either the old_basename or the new_basename is a reserved name
641        // (e.g., "." or ".."), then we cannot do the rename.
642        if DirEntry::is_reserved_name(old_basename) || DirEntry::is_reserved_name(new_basename) {
643            if flags.contains(RenameFlags::NOREPLACE) {
644                return error!(EEXIST);
645            }
646            return error!(EBUSY);
647        }
648
649        // If the names and parents are the same, then there's nothing to do
650        // and we can report success.
651        if Arc::ptr_eq(&old_parent.node, &new_parent.node) && old_basename == new_basename {
652            if flags.contains(RenameFlags::NOREPLACE) {
653                return error!(EEXIST);
654            }
655            return Ok(());
656        }
657
658        // This task must have write access to the old and new parent nodes.
659        old_parent.node.check_access(
660            current_task,
661            mount,
662            Access::WRITE,
663            CheckAccessReason::InternalPermissionChecks,
664            old_parent,
665        )?;
666        new_parent.node.check_access(
667            current_task,
668            mount,
669            Access::WRITE,
670            CheckAccessReason::InternalPermissionChecks,
671            new_parent,
672        )?;
673
674        // The mount check ensures that the nodes we're touching are part of the
675        // same file system. It doesn't matter where we grab the FileSystem reference from.
676        let fs = old_parent.node.fs();
677
678        // We need to hold these DirEntryHandles until after we drop all the
679        // locks so that we do not deadlock when we drop them.
680        let renamed;
681        let mut maybe_replaced = None;
682
683        {
684            // Before we take any locks, we need to take the rename mutex on
685            // the file system. This lock ensures that no other rename
686            // operations are happening in this file system while we're
687            // analyzing this rename operation.
688            //
689            // For example, we grab writer locks on both old_parent and
690            // new_parent. If there was another rename operation in flight with
691            // old_parent and new_parent reversed, then we could deadlock while
692            // trying to acquire these locks.
693            let _lock = fs.rename_mutex.lock();
694
695            // We cannot simply grab the locks on old_parent and new_parent
696            // independently because old_parent and new_parent might be the
697            // same directory entry. Instead, we use the RenameGuard helper to
698            // grab the appropriate locks.
699            let mut state = RenameGuard::lock(old_parent, new_parent);
700
701            // Now that we know the old_parent child list cannot change, we
702            // establish the DirEntry that we are going to try to rename.
703            renamed =
704                state.old_parent_children().component_lookup(current_task, mount, old_basename)?;
705
706            // We need to check if there is already a DirEntry with
707            // new_basename in new_parent. If so, there are additional checks
708            // we need to perform.
709            // This must be done BEFORE locking info to avoid self-deadlock.
710            let lookup_replaced =
711                state.new_parent_children().component_lookup(current_task, mount, new_basename);
712
713            // If the target entry is an ancestor of the source parent, the
714            // rename would create a cycle (for EXCHANGE) or attempt to
715            // overwrite a non-empty directory.
716            // We check this before acquiring child locks to avoid
717            // deadlocks from bottom-up locking (locking ancestor after
718            // descendant).
719            // This is done early (before parent locks) as a fail-fast
720            // optimization since we already have the lookup result.
721            if let Ok(replaced) = &lookup_replaced {
722                if old_parent.is_descendant_of(replaced) {
723                    if flags.contains(RenameFlags::EXCHANGE) {
724                        return error!(EINVAL);
725                    } else {
726                        return error!(ENOTEMPTY);
727                    }
728                }
729            }
730
731            // Lock the info for the parents to ensure that subsequent checks on their
732            // state (e.g., sticky bit checks) and the actual rename operation are not racy.
733            let mut state =
734                state.lock_info(old_parent, new_parent, &renamed, lookup_replaced.as_ref().ok());
735
736            // If new_parent is a descendant of renamed, the operation would
737            // create a cycle. That's disallowed.
738            if new_parent.is_descendant_of(&renamed) {
739                return error!(EINVAL);
740            }
741
742            // Check whether the sticky bit on the old parent prevents us from
743            // removing this child.
744            {
745                // Safe because the parent is locked first, and then we check the
746                // sticky bit of the child. This parent -> child acquisition follows
747                // the hierarchical lock ordering.
748                let _token = allow_subclass();
749                old_parent.node.check_sticky_bit(
750                    current_task,
751                    &renamed.node,
752                    state.old_parent_info(),
753                )?;
754            }
755
756            // Check whether the renamed entry is a mountpoint.
757            // TODO: We should hold a read lock on the mount points for this
758            //       namespace to prevent the child from becoming a mount point
759            //       while this function is executing.
760            renamed.require_no_mounts(mount)?;
761
762            // We lookup the replaced entry before locking info to avoid deadlock, but we match
763            // on the result here under the parent info locks. This ensures that checks on the
764            // replaced entry (e.g., existence, directory status, identity) are consistent and
765            // do not race with concurrent operations.
766            match &lookup_replaced {
767                Ok(replaced) => {
768                    // Set `maybe_replaced` now to ensure it gets dropped in the right order.
769                    let replaced = maybe_replaced.insert(replaced.clone());
770
771                    if flags.contains(RenameFlags::NOREPLACE) {
772                        // From <https://man7.org/linux/man-pages/man2/rename.2.html>:
773                        //
774                        //   RENAME_NOREPLACE
775                        //          Don't overwrite newpath of the rename. Return an error
776                        //          if newpath already exists.
777                        return error!(EEXIST);
778                    }
779
780                    if Arc::ptr_eq(&renamed.node, &replaced.node) {
781                        if !Arc::ptr_eq(&renamed, &replaced) {
782                            // From <https://man7.org/linux/man-pages/man2/rename.2.html>:
783                            //
784                            // "If oldpath and newpath are existing hard links referring to the
785                            // same file, then rename() does nothing, and returns a success
786                            // status."
787                            return Ok(());
788                        }
789
790                        // Same DirEntry (e.g. case-only rename in a casefolded directory).
791                        // With EXCHANGE, swapping an entry with itself is a no-op that succeeds.
792                        // Without EXCHANGE, we must proceed to rename on the underlying filesystem
793                        // to update the on-disk casing.
794                        if flags.contains(RenameFlags::EXCHANGE) {
795                            return Ok(());
796                        }
797                    }
798
799                    // Sayeth https://man7.org/linux/man-pages/man2/rename.2.html:
800                    //
801                    // "oldpath can specify a directory.  In this case, newpath must"
802                    // either not exist, or it must specify an empty directory."
803                    if state.replaced_is_dir() {
804                        // Check whether the replaced entry is a mountpoint.
805                        // TODO: We should hold a read lock on the mount points for this
806                        //       namespace to prevent the child from becoming a mount point
807                        //       while this function is executing.
808                        replaced.require_no_mounts(mount)?;
809                    }
810
811                    if !flags.intersects(RenameFlags::EXCHANGE | RenameFlags::REPLACE_ANY) {
812                        let renamed_is_dir = state.renamed_is_dir();
813                        let replaced_is_dir = state.replaced_is_dir();
814                        if renamed_is_dir && !replaced_is_dir {
815                            return error!(ENOTDIR);
816                        } else if !renamed_is_dir && replaced_is_dir {
817                            return error!(EISDIR);
818                        }
819                    }
820                }
821                // It's fine for the lookup to fail to find a child.
822                Err(errno) if *errno == ENOENT => {
823                    if flags.contains(RenameFlags::EXCHANGE) {
824                        return error!(ENOENT);
825                    }
826                }
827                // However, other errors are fatal.
828                Err(e) => return Err(e.clone()),
829            }
830
831            security::check_fs_node_rename_access(
832                current_task,
833                &old_parent.node,
834                &renamed.node,
835                &new_parent.node,
836                maybe_replaced.as_ref().map(|dir_entry| dir_entry.node.deref().as_ref()),
837                old_basename,
838                new_basename,
839            )?;
840
841            if let Some(replaced) = maybe_replaced.as_ref() {
842                // Safe because the parent is locked first, and then we check the
843                // sticky bit of the child. This parent -> child acquisition
844                // follows the hierarchical lock ordering.
845                let _token = allow_subclass();
846                new_parent.node.check_sticky_bit(
847                    current_task,
848                    &replaced.node,
849                    state.new_parent_info().unwrap_or_else(|| state.old_parent_info()),
850                )?;
851            }
852
853            // In a casefolded directory, renaming an entry to a case variant of itself resolves
854            // `lookup_replaced` to the same DirEntry (`renamed`). At the filesystem level, no
855            // distinct directory entry is being overwritten or replaced, so clear `state.replaced`
856            // to `None` before invoking the filesystem operation.
857            if state.replaced.is_some_and(|r| Arc::ptr_eq(&renamed, r)) {
858                state.replaced = None;
859            }
860
861            if flags.contains(RenameFlags::EXCHANGE) {
862                fs.exchange(current_task, &mut state, old_basename, new_basename)?;
863            } else {
864                fs.rename(current_task, &mut state, old_basename, new_basename)?;
865            }
866
867            if flags.contains(RenameFlags::EXCHANGE) {
868                // Reparent `replaced` when exchanging.
869                let replaced =
870                    maybe_replaced.as_ref().expect("replaced expected with RENAME_EXCHANGE");
871                replaced.set_parent(old_parent.clone());
872                replaced.local_name.update(old_basename.to_owned());
873                state.old_parent_children().children.insert(old_basename, Arc::downgrade(replaced));
874            } else {
875                // Remove the renamed child from the old_parent's child list.
876                // In casefolded directories, removing old_basename before inserting new_basename
877                // prevents removal from clobbering the freshly inserted entry when old and new
878                // names canonicalize to the same key.
879                state.old_parent_children().children.remove(old_basename);
880            }
881
882            // We need to update the parent and local name for the DirEntry
883            // we are renaming to reflect its new parent and its new name.
884            renamed.set_parent(new_parent.clone());
885            renamed.local_name.update(new_basename.to_owned());
886
887            // Actually add the renamed child to the new_parent's child list.
888            // This operation implicitly removes the replaced child (if any)
889            // from the child list.
890            state.new_parent_children().children.insert(new_basename, Arc::downgrade(&renamed));
891
892            // Lock ordering is enforced from parent-to-child, and therefore we need to
893            // reset the lock ordering constraints when we reorder the tree nodes.
894            // SAFETY: We manually clear the dependency graph for these locks.
895            // This is safe because `fs.rename_mutex` is held during this operation, which
896            // prevents the tree topology from changing concurrently. This allows us to safely
897            // dynamically enforce a sound locking order (e.g. by memory address in `RenameGuard`)
898            // to avoid deadlocks. Clearing the graph prevents false-positive cycle panics from
899            // `tracing-mutex` after the node is reparented.
900            unsafe {
901                if let Some(replaced) =
902                    maybe_replaced.as_ref().filter(|_| flags.contains(RenameFlags::EXCHANGE))
903                {
904                    replaced.children.reset_dependencies();
905                    replaced.node.info_lock().reset_dependencies();
906                }
907                renamed.children.reset_dependencies();
908                renamed.node.info_lock().reset_dependencies();
909                old_parent.children.reset_dependencies();
910                old_parent.node.info_lock().reset_dependencies();
911                new_parent.children.reset_dependencies();
912                new_parent.node.info_lock().reset_dependencies();
913            }
914        };
915
916        fs.purge_old_entries();
917
918        if let Some(replaced) = maybe_replaced {
919            if !flags.contains(RenameFlags::EXCHANGE) && !Arc::ptr_eq(&renamed, &replaced) {
920                replaced.destroy(&current_task.kernel().mounts);
921            }
922        }
923
924        // Renaming a file updates its ctime.
925        renamed.node.update_ctime();
926
927        let mode = renamed.node.info().mode;
928        if let Some(hook) =
929            current_task.kernel().expando.peek::<Arc<dyn inotify_hook::NotifyHook>>()
930        {
931            let cookie = hook.get_next_cookie();
932            old_parent.node.notify(InotifyMask::MOVE_FROM, cookie, old_basename, mode, false);
933            new_parent.node.notify(InotifyMask::MOVE_TO, cookie, new_basename, mode, false);
934            renamed.node.notify(InotifyMask::MOVE_SELF, 0, Default::default(), mode, false);
935        }
936
937        Ok(())
938    }
939
940    pub(crate) fn get_children<F, T>(&self, callback: F) -> T
941    where
942        F: FnOnce(&DirEntryChildren) -> T,
943    {
944        let children = self.children.read();
945        callback(&children)
946    }
947
948    /// Remove the child with the given name from the children cache.  The child must not have any
949    /// mounts.
950    pub fn remove_child(&self, name: &FsStr, mounts: &Mounts) {
951        let mut children = self.children.write();
952        let child = children.remove(name).and_then(|weak| weak.upgrade());
953        if let Some(child) = child {
954            std::mem::drop(children);
955            child.destroy(mounts);
956        }
957    }
958
959    /// Verifies that `name` is not a reserved `.` or `..` entry, that this entry is a directory,
960    /// and that `current_task` has search (`EXEC`) permission on it.
961    fn check_search_access(
962        &self,
963        current_task: &CurrentTask,
964        mount: &MountInfo,
965        name: &FsStr,
966    ) -> Result<(), Errno> {
967        assert!(!DirEntry::is_reserved_name(name));
968        // Only directories can have children.
969        if !self.node.is_dir() {
970            return error!(ENOTDIR);
971        }
972        // The user must be able to search the directory (requires the EXEC permission).
973        self.node.check_access(
974            current_task,
975            mount,
976            Access::EXEC,
977            CheckAccessReason::InternalPermissionChecks,
978            self,
979        )
980    }
981
982    /// Retrieves a child `DirEntry` from the cache or obtains one via `create_fn`.
983    ///
984    /// Checks the in-memory child cache. A cached child is validated using `revalidate()` and
985    /// evicted if revalidation fails. On a cache miss, or after evicting a stale child, calls
986    /// `create_fn` to acquire the underlying `FsNode` and its `CreationStatus`. Nodes returned by
987    /// `create_fn` are not revalidated, and `create_fn` is called at most once.
988    fn get_or_create_child(
989        self: &DirEntryHandle,
990        current_task: &CurrentTask,
991        mount: &MountInfo,
992        name: &FsStr,
993        create_fn: impl FnOnce(
994            &DirEntryHandle,
995            &MountInfo,
996            &FsStr,
997        ) -> Result<(FsNodeHandle, CreationStatus), Errno>,
998    ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
999        self.check_search_access(current_task, mount, name)?;
1000
1001        // Check if the child is already in children. In that case, we can simply return the
1002        // child if it is still valid, and we do not need to call create_fn.
1003        //
1004        // The cache is queried in its own statement so that the children lock is released before
1005        // the child is revalidated or evicted.
1006        let child = self.children.read().get(name).and_then(Weak::upgrade);
1007        if let Some(child) = child {
1008            if child.ops.revalidate(current_task, &child)? {
1009                // Do not cache a child in a locked directory
1010                if self.node.fail_if_locked(current_task, &self.node.info()).is_ok() {
1011                    child.node.fs().did_access_dir_entry(&child);
1012                }
1013                return Ok((child, CreationStatus::Existed));
1014            }
1015            // The cached child is stale. Evict it so that create_fn provides a fresh node.
1016            self.internal_remove_child(&child);
1017            child.destroy(&current_task.kernel().mounts);
1018        }
1019
1020        // If another thread added the child since the check above, it is returned without being
1021        // revalidated because it was just added. Otherwise, create_fn provides the node.
1022        let (child, status) =
1023            self.lock_children().get_or_create_child(current_task, mount, name, create_fn)?;
1024        child.node.fs().purge_old_entries();
1025        Ok((child, status))
1026    }
1027
1028    // This function is only useful for tests and has some oddities.
1029    //
1030    // For example, not all the children might have been looked up yet, which
1031    // means the returned vector could be missing some names.
1032    //
1033    // Also, the vector might have "extra" names that are in the process of
1034    // being looked up. If the lookup fails, they'll be removed.
1035    #[cfg(test)]
1036    pub fn copy_child_names(&self) -> Vec<FsString> {
1037        let scope = RcuReadScope::new();
1038        self.children.read().copy_child_names(&scope)
1039    }
1040
1041    fn internal_remove_child(&self, child: &DirEntry) {
1042        let mut children = self.children.write();
1043        let scope = RcuReadScope::new();
1044        let local_name = child.local_name.read(&scope);
1045        children.remove_if_child_matches(local_name, child);
1046    }
1047
1048    /// Notifies watchers on the current node and its parent about an event.
1049    pub fn notify(&self, event_mask: InotifyMask) {
1050        self.notify_watchers(event_mask, self.is_dead());
1051    }
1052
1053    /// Notifies watchers on the current node and its parent about an event.
1054    ///
1055    /// Used for FSNOTIFY_EVENT_INODE events, which ignore IN_EXCL_UNLINK.
1056    pub fn notify_ignoring_excl_unlink(&self, event_mask: InotifyMask) {
1057        // We pretend that this directory entry is not dead to ignore IN_EXCL_UNLINK.
1058        self.notify_watchers(event_mask, false);
1059    }
1060
1061    fn notify_watchers(&self, event_mask: InotifyMask, is_dead: bool) {
1062        let mode = self.node.info().mode;
1063        {
1064            let scope = RcuReadScope::new();
1065            if let Some(parent) = self.parent() {
1066                let local_name = self.local_name.read(&scope);
1067                parent.node.notify(event_mask, 0, local_name, mode, is_dead);
1068            }
1069        }
1070        self.node.notify(event_mask, 0, Default::default(), mode, is_dead);
1071    }
1072
1073    /// Notifies parents about creation, and notifies current node about link_count change.
1074    fn notify_creation(&self) {
1075        let mode = self.node.info().mode;
1076        if Arc::strong_count(&self.node) > 1 {
1077            // Notify about link change only if there is already a hardlink.
1078            self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1079        }
1080        let scope = RcuReadScope::new();
1081        if let Some(parent) = self.parent() {
1082            let local_name = self.local_name.read(&scope);
1083            parent.node.notify(InotifyMask::CREATE, 0, local_name, mode, false);
1084        }
1085    }
1086
1087    /// Notifies watchers on the current node about deletion if this is the
1088    /// last hardlink, and drops the DirEntryHandle kept by Inotify.
1089    /// Parent is also notified about deletion.
1090    fn notify_deletion(&self) {
1091        let mode = self.node.info().mode;
1092        if !mode.is_dir() {
1093            // Linux notifies link count change for non-directories.
1094            self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1095        }
1096
1097        // This check is incorrect if there's another hard link to this FsNode that isn't in
1098        // memory at the moment.
1099        if Arc::strong_count(&self.node) == 1 {
1100            self.node.notify(InotifyMask::DELETE_SELF, 0, Default::default(), mode, false);
1101        }
1102
1103        let scope = RcuReadScope::new();
1104        if let Some(parent) = self.parent() {
1105            let local_name = self.local_name.read(&scope);
1106            parent.node.notify(InotifyMask::DELETE, 0, local_name, mode, false);
1107        }
1108    }
1109
1110    /// Returns true if this entry has mounts.
1111    pub fn has_mounts(&self) -> bool {
1112        self.flags().contains(DirEntryFlags::HAS_MOUNTS)
1113    }
1114
1115    /// Records whether or not the entry has mounts.
1116    pub fn set_has_mounts(&self, v: bool) {
1117        if v {
1118            self.raise_flags(DirEntryFlags::HAS_MOUNTS);
1119        } else {
1120            self.lower_flags(DirEntryFlags::HAS_MOUNTS);
1121        }
1122    }
1123
1124    /// Verifies this directory has nothing mounted on it.
1125    fn require_no_mounts(self: &Arc<Self>, parent_mount: &MountInfo) -> Result<(), Errno> {
1126        if self.has_mounts() {
1127            if let Some(mount) = parent_mount.as_ref() {
1128                if mount.has_submount(self) {
1129                    return error!(EBUSY);
1130                }
1131            }
1132        }
1133        Ok(())
1134    }
1135}
1136
1137/// Canonicalizes a filename into its casefolded, NFD-normalized byte representation on the stack.
1138/// Non-UTF-8 filenames are preserved as raw bytes.
1139#[inline]
1140fn canonicalize_name(name: impl AsRef<[u8]>) -> SmallVec<[u8; NAME_MAX as usize]> {
1141    let name = name.as_ref();
1142    match std::str::from_utf8(name) {
1143        Ok(valid) => {
1144            CasefoldStr::new(valid).casefold_normalized_chars().flat_map(utf8_bytes).collect()
1145        }
1146        Err(_) => SmallVec::from_slice(name),
1147    }
1148}
1149
1150/// A canonicalized key stored in the directory entry cache.
1151///
1152/// Stores pre-casefolded, NFD-normalized UTF-8 bytes (for casefolded directories) or raw bytes
1153/// (for case-sensitive directories and non-UTF-8 filenames).
1154#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
1155pub(crate) struct DirEntryChildKey(FsString);
1156
1157impl DirEntryChildKey {
1158    pub fn new(name: &FsStr, casefold: bool) -> Self {
1159        if casefold {
1160            Self(canonicalize_name(name).into_vec().into())
1161        } else {
1162            Self(name.to_owned())
1163        }
1164    }
1165}
1166
1167impl std::borrow::Borrow<FsStr> for DirEntryChildKey {
1168    fn borrow(&self) -> &FsStr {
1169        self.0.as_bstr()
1170    }
1171}
1172
1173/// The cached child directory entries of a directory.
1174///
1175/// Keys stored in this map are strictly internal to the VFS cache and are never exposed back to
1176/// userspace. Directory listings (`readdir`) query the underlying filesystem directly for canonical
1177/// on-disk names, while individual child entries track their associated lookup/creation name in
1178/// [`DirEntry::local_name`].
1179#[derive(Default, Debug)]
1180pub(crate) struct DirEntryChildren {
1181    entries: BTreeMap<DirEntryChildKey, Weak<DirEntry>>,
1182    casefold: bool,
1183}
1184
1185impl std::ops::Deref for DirEntryChildren {
1186    type Target = BTreeMap<DirEntryChildKey, Weak<DirEntry>>;
1187
1188    fn deref(&self) -> &Self::Target {
1189        &self.entries
1190    }
1191}
1192
1193impl DirEntryChildren {
1194    pub fn new(casefold: bool) -> Self {
1195        Self { entries: BTreeMap::new(), casefold }
1196    }
1197
1198    pub fn is_casefold(&self) -> bool {
1199        self.casefold
1200    }
1201
1202    pub fn set_casefold(&mut self, casefold: bool) {
1203        self.entries.clear();
1204        self.casefold = casefold;
1205    }
1206
1207    /// Looks up a child entry by name.
1208    pub fn get(&self, name: &FsStr) -> Option<&Weak<DirEntry>> {
1209        if self.casefold {
1210            let key = canonicalize_name(name);
1211            self.entries.get(FsStr::new(&key))
1212        } else {
1213            self.entries.get(name)
1214        }
1215    }
1216
1217    /// Inserts a child into the directory cache under `name`, returning the weak reference
1218    /// previously stored under this name, if any.
1219    pub fn insert(&mut self, name: &FsStr, child: Weak<DirEntry>) -> Option<Weak<DirEntry>> {
1220        self.entries.insert(DirEntryChildKey::new(name, self.casefold), child)
1221    }
1222
1223    /// Removes a child from the directory cache by name, returning the weak reference
1224    /// previously stored under this name, if any.
1225    pub fn remove(&mut self, name: &FsStr) -> Option<Weak<DirEntry>> {
1226        if self.casefold {
1227            let key = canonicalize_name(name);
1228            self.entries.remove(FsStr::new(&key))
1229        } else {
1230            self.entries.remove(name)
1231        }
1232    }
1233
1234    /// Removes a child entry if the cached weak reference matches `expected_child`.
1235    /// Returns `true` if the entry was found and removed.
1236    pub fn remove_if_child_matches(&mut self, name: &FsStr, expected_child: &DirEntry) -> bool {
1237        if let Some(weak_child) = self.get(name) {
1238            if std::ptr::eq(weak_child.as_ptr(), expected_child) {
1239                let _ = self.remove(name);
1240                return true;
1241            }
1242        }
1243        false
1244    }
1245
1246    #[cfg(test)]
1247    /// Extracts a snapshot of all active child names, retaining the exact casing currently
1248    /// stored on each child node and filtering out dropped entries.
1249    fn copy_child_names(&self, scope: &RcuReadScope) -> Vec<FsString> {
1250        self.entries
1251            .values()
1252            .filter_map(|child| Weak::upgrade(child).map(|c| c.local_name.read(scope).to_owned()))
1253            .collect()
1254    }
1255}
1256
1257struct DirEntryLockedChildren<'a> {
1258    entry: &'a DirEntryHandle,
1259    children: LockDepWriteGuard<'a, DirEntryChildren>,
1260}
1261
1262/// Represents whether a node was freshly created or already existed on the underlying
1263/// filesystem.
1264#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1265pub enum CreationStatus {
1266    /// The node was newly created. Updates directory ctime/mtime.
1267    Created,
1268    /// The node already existed (e.g., found in the cache, or resolved via lookup or pipelined
1269    /// lookup).
1270    Existed,
1271}
1272
1273impl<'a> DirEntryLockedChildren<'a> {
1274    fn component_lookup(
1275        &mut self,
1276        current_task: &CurrentTask,
1277        mount: &MountInfo,
1278        name: &FsStr,
1279    ) -> Result<DirEntryHandle, Errno> {
1280        self.entry.check_search_access(current_task, mount, name)?;
1281        let (node, _) =
1282            self.get_or_create_child(current_task, mount, name, |d, _mount, name| {
1283                Ok((d.node.ops().lookup(d, current_task, name)?, CreationStatus::Existed))
1284            })?;
1285        Ok(node)
1286    }
1287
1288    fn get_or_create_child(
1289        &mut self,
1290        current_task: &CurrentTask,
1291        mount: &MountInfo,
1292        name: &FsStr,
1293        create_fn: impl FnOnce(
1294            &DirEntryHandle,
1295            &MountInfo,
1296            &FsStr,
1297        ) -> Result<(FsNodeHandle, CreationStatus), Errno>,
1298    ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
1299        if let Some(child) = self.children.get(name).and_then(Weak::upgrade) {
1300            // Do not cache a child in a locked directory
1301            if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1302                child.node.fs().did_access_dir_entry(&child);
1303            }
1304            return Ok((child, CreationStatus::Existed));
1305        }
1306
1307        let (node, status) = create_fn(self.entry, mount, name)?;
1308
1309        assert!(
1310            node.info().mode & FileMode::IFMT != FileMode::EMPTY,
1311            "FsNode initialization did not populate the FileMode in FsNodeInfo."
1312        );
1313
1314        let child = DirEntry::new(node, Some(self.entry.clone()), name.to_owned());
1315
1316        if let Err(err) = security::fs_node_init_with_dentry(current_task, &child) {
1317            // Null out the `parent` reference from `child` otherwise dropping `child` will
1318            // attempt to remove itself from `parent`, triggering a deadlock with `self`.
1319            child.parent.update(None);
1320            return Err(err);
1321        }
1322
1323        // Do not cache a child in a locked directory
1324        if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1325            self.children.insert(name, Arc::downgrade(&child));
1326        }
1327
1328        Ok((child, status))
1329    }
1330}
1331
1332impl fmt::Debug for DirEntry {
1333    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1334        let scope = RcuReadScope::new();
1335        let mut path = PathBuilder::new();
1336        let mut parent_holder;
1337        let mut current = self;
1338        loop {
1339            // Root and unnamed unrooted entries have an empty `local_name`.
1340            let name = current.local_name(&scope);
1341            if !name.is_empty() {
1342                path.prepend_element(name);
1343            }
1344            let Some(parent) = current.parent() else {
1345                break;
1346            };
1347            parent_holder = parent;
1348            current = &parent_holder;
1349        }
1350        f.debug_struct("DirEntry")
1351            .field("fs", &self.node.fs().name())
1352            .field("path", &path.build_absolute())
1353            .finish()
1354    }
1355}
1356
1357#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1358enum RenameRelationship {
1359    Same,
1360    NewIsDescendant,
1361    OldIsDescendant,
1362    Independent,
1363}
1364
1365struct RenameGuard<'a> {
1366    old_parent_guard: DirEntryLockedChildren<'a>,
1367    new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1368    relationship: RenameRelationship,
1369}
1370
1371impl<'a> RenameGuard<'a> {
1372    fn lock(old_parent: &'a DirEntryHandle, new_parent: &'a DirEntryHandle) -> Self {
1373        if Arc::ptr_eq(old_parent, new_parent) {
1374            let old_parent_guard = old_parent.lock_children();
1375            Self {
1376                old_parent_guard,
1377                new_parent_guard: None,
1378                relationship: RenameRelationship::Same,
1379            }
1380        } else if new_parent.is_descendant_of(old_parent) {
1381            let old_parent_guard = old_parent.lock_children();
1382            let _token = allow_subclass();
1383            let new_parent_guard = new_parent.lock_children();
1384            Self {
1385                old_parent_guard,
1386                new_parent_guard: Some(new_parent_guard),
1387                relationship: RenameRelationship::NewIsDescendant,
1388            }
1389        } else if old_parent.is_descendant_of(new_parent) {
1390            let new_parent_guard = new_parent.lock_children();
1391            let _token = allow_subclass();
1392            let old_parent_guard = old_parent.lock_children();
1393            Self {
1394                old_parent_guard,
1395                new_parent_guard: Some(new_parent_guard),
1396                relationship: RenameRelationship::OldIsDescendant,
1397            }
1398        } else {
1399            // Independent directories can be locked in address order.
1400            let (g1, g2) =
1401                starnix_sync::ordered_write_lock(&old_parent.children, &new_parent.children);
1402            let old_parent_guard = DirEntryLockedChildren { entry: old_parent, children: g1 };
1403            let new_parent_guard = DirEntryLockedChildren { entry: new_parent, children: g2 };
1404            Self {
1405                old_parent_guard,
1406                new_parent_guard: Some(new_parent_guard),
1407                relationship: RenameRelationship::Independent,
1408            }
1409        }
1410    }
1411
1412    /// Consumes the `RenameGuard` (which only holds children locks) and locks the `info`
1413    /// of the parent directories in a safe order to prevent deadlocks. Returns a
1414    /// `RenameGuardLocked` which encapsulates all acquired locks (both children and info).
1415    fn lock_info(
1416        self,
1417        old_parent: &'a DirEntryHandle,
1418        new_parent: &'a DirEntryHandle,
1419        renamed: &'a DirEntryHandle,
1420        replaced: Option<&'a DirEntryHandle>,
1421    ) -> RenameContext<'a> {
1422        let (g1, g2) = match self.relationship {
1423            RenameRelationship::Same => (old_parent.node.info_lock().write(), None),
1424            RenameRelationship::NewIsDescendant => {
1425                let g1 = old_parent.node.info_lock().write();
1426                let _token = allow_subclass();
1427                let g2 = new_parent.node.info_lock().write();
1428                (g1, Some(g2))
1429            }
1430            RenameRelationship::OldIsDescendant => {
1431                let g2 = new_parent.node.info_lock().write();
1432                let _token = allow_subclass();
1433                let g1 = old_parent.node.info_lock().write();
1434                (g1, Some(g2))
1435            }
1436            RenameRelationship::Independent => {
1437                let (g1, g2) = starnix_sync::ordered_write_lock(
1438                    old_parent.node.info_lock(),
1439                    new_parent.node.info_lock(),
1440                );
1441                (g1, Some(g2))
1442            }
1443        };
1444
1445        RenameContext {
1446            renamed,
1447            replaced,
1448            old_parent_guard: self.old_parent_guard,
1449            new_parent_guard: self.new_parent_guard,
1450            old_parent_info_guard: g1,
1451            new_parent_info_guard: g2,
1452        }
1453    }
1454
1455    fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1456        &mut self.old_parent_guard
1457    }
1458
1459    fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1460        if let Some(new_guard) = self.new_parent_guard.as_mut() {
1461            new_guard
1462        } else {
1463            &mut self.old_parent_guard
1464        }
1465    }
1466}
1467
1468/// A context that holds the locked children and info of the parents during a
1469/// rename operation.
1470///
1471/// The context is constructed by locking the parent directories
1472/// (`old_parent`, `new_parent`) for write. These parent locks are held for
1473/// the duration of the context's lifetime, preventing concurrent
1474/// modifications to the parents.
1475///
1476/// Child nodes (`renamed` and `replaced`) are *not* locked upon
1477/// construction. To query properties of the children (like whether they are
1478/// directories) without risking deadlocks, use the provided helper methods
1479/// (`renamed_is_dir`, `replaced_is_dir`) instead of locking them directly.
1480pub struct RenameContext<'a> {
1481    pub renamed: &'a DirEntryHandle,
1482    pub replaced: Option<&'a DirEntryHandle>,
1483    old_parent_guard: DirEntryLockedChildren<'a>,
1484    new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1485    old_parent_info_guard: LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>,
1486    new_parent_info_guard: Option<LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>>,
1487}
1488
1489impl<'a> RenameContext<'a> {
1490    /// Returns whether the renamed child node is a directory.
1491    ///
1492    /// This method safely handles child info locking under the parent locks.
1493    /// If the renamed node is same as a parent (which is already locked),
1494    /// it uses the parent's guard to avoid self-deadlock. Otherwise, it
1495    /// locks the child info using `allow_subclass`.
1496    pub fn renamed_is_dir(&self) -> bool {
1497        self.is_dir(&self.renamed.node)
1498    }
1499
1500    /// Returns whether the replaced child node is a directory.
1501    ///
1502    /// Returns `false` if `replaced` is `None`.
1503    ///
1504    /// This method safely handles child info locking under the parent locks.
1505    /// If the replaced node is same as a parent (which is already locked),
1506    /// it uses the parent's guard to avoid self-deadlock. Otherwise, it
1507    /// locks the child info using `allow_subclass`.
1508    pub fn replaced_is_dir(&self) -> bool {
1509        self.replaced.map(|r| self.is_dir(&r.node)).unwrap_or(false)
1510    }
1511
1512    /// Returns the old parent directory entry handle.
1513    ///
1514    /// The old parent's child list is write-locked for the lifetime of the
1515    /// context.
1516    pub fn old_parent(&self) -> &DirEntryHandle {
1517        self.old_parent_guard.entry
1518    }
1519
1520    /// Returns the new parent directory entry handle.
1521    ///
1522    /// The new parent's child list is write-locked for the lifetime of the
1523    /// context.
1524    pub fn new_parent(&self) -> &DirEntryHandle {
1525        self.new_parent_guard.as_ref().map(|g| g.entry).unwrap_or(self.old_parent_guard.entry)
1526    }
1527
1528    /// Returns mutable references to the `FsNodeInfo` of both parent
1529    /// directories.
1530    ///
1531    /// The references are returned as a tuple to allow them to be borrowed
1532    /// mutably at the same time (e.g., to update link counts in both).
1533    ///
1534    /// If `new_parent` is the same as `old_parent`, the second element of the
1535    /// tuple will be `None` to prevent mutable aliasing of the same guard.
1536    pub fn parent_infos_mut(
1537        &mut self,
1538    ) -> (&mut crate::vfs::FsNodeInfo, Option<&mut crate::vfs::FsNodeInfo>) {
1539        let old = &mut *self.old_parent_info_guard;
1540        let new = self.new_parent_info_guard.as_mut().map(|g| &mut **g);
1541        (old, new)
1542    }
1543
1544    /// Returns a shared reference to the `FsNodeInfo` of the old parent
1545    /// directory.
1546    pub fn old_parent_info(&self) -> &crate::vfs::FsNodeInfo {
1547        &self.old_parent_info_guard
1548    }
1549    /// Returns a shared reference to the `FsNodeInfo` of the new parent
1550    /// directory.
1551    ///
1552    /// Returns `None` if `new_parent` is the same as `old_parent`.
1553    pub fn new_parent_info(&self) -> Option<&crate::vfs::FsNodeInfo> {
1554        self.new_parent_info_guard.as_deref()
1555    }
1556
1557    fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1558        self.new_parent_guard.as_mut().unwrap_or(&mut self.old_parent_guard)
1559    }
1560
1561    fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1562        &mut self.old_parent_guard
1563    }
1564
1565    fn is_dir(&self, node: &FsNodeHandle) -> bool {
1566        if Arc::ptr_eq(node, &self.old_parent().node) {
1567            self.old_parent_info_guard.mode.is_dir()
1568        } else if Arc::ptr_eq(node, &self.new_parent().node) {
1569            self.new_parent_info_guard
1570                .as_ref()
1571                .map(|g| g.mode.is_dir())
1572                .unwrap_or_else(|| self.old_parent_info_guard.mode.is_dir())
1573        } else {
1574            let _token = allow_subclass();
1575            node.is_dir()
1576        }
1577    }
1578}
1579
1580/// The Drop trait for DirEntry removes the entry from the child list of the
1581/// parent entry, which means we cannot drop DirEntry objects while holding a
1582/// lock on the parent's child list.
1583impl Drop for DirEntry {
1584    fn drop(&mut self) {
1585        let maybe_parent = self.parent();
1586        self.parent.update(None);
1587        if let Some(parent) = maybe_parent {
1588            parent.internal_remove_child(self);
1589        }
1590    }
1591}
1592
1593#[cfg(test)]
1594mod tests {
1595    use super::*;
1596    use crate::testing::{PanickingFile, spawn_kernel_and_run};
1597    use crate::vfs::pseudo::simple_directory::SimpleDirectory;
1598    use crate::vfs::{
1599        CacheConfig, CacheMode, FileOps, FileSystem, FileSystemHandle, FileSystemOps,
1600        FileSystemOptions, FsNode, FsNodeInfo, FsNodeOps, MemoryDirectoryFile, Namespace,
1601        SymlinkNode, WhatToMount, fs_node_impl_not_dir,
1602    };
1603    use starnix_sync::Mutex;
1604    use starnix_types::vfs::default_statfs;
1605    use starnix_uapi::device_id::DeviceId;
1606    use starnix_uapi::file_mode::mode;
1607    use starnix_uapi::mount_flags::MountpointFlags;
1608    use starnix_uapi::vfs::ResolveFlags;
1609    use starnix_uapi::{errno, statfs};
1610    use std::sync::atomic::AtomicUsize;
1611
1612    #[fuchsia::test]
1613    async fn test_dir_entry_debug() {
1614        spawn_kernel_and_run(async |current_task| {
1615            let fs = current_task.fs().root().entry.node.fs();
1616            let check = |parent: Option<&DirEntryHandle>, name: &str, expected_path: &str| {
1617                let node = SimpleDirectory::new().into_node(&fs, 0o755);
1618                let entry = DirEntry::new(node, parent.cloned(), name.into());
1619                assert_eq!(
1620                    format!("{entry:?}"),
1621                    format!("DirEntry {{ fs: {:?}, path: {expected_path:?} }}", fs.name())
1622                );
1623                entry
1624            };
1625
1626            let root = check(None, "", "/");
1627            let bus = check(Some(&root), "bus", "/bus");
1628            check(Some(&bus), "pci", "/bus/pci");
1629            check(None, "[eventfd]", "/[eventfd]");
1630        })
1631        .await;
1632    }
1633
1634    #[test]
1635    fn test_canonicalize_name() {
1636        let upper = canonicalize_name("FooBar.TXT");
1637        let lower = canonicalize_name("foobar.txt");
1638        let mixed = canonicalize_name("FOOBAR.TXT");
1639        let diff = canonicalize_name("other.txt");
1640
1641        assert_eq!(upper.as_slice(), lower.as_slice());
1642        assert_eq!(upper.as_slice(), mixed.as_slice());
1643        assert_ne!(upper.as_slice(), diff.as_slice());
1644
1645        // Unicode decomposed vs composed
1646        let composed = canonicalize_name("\u{03AA}");
1647        let decomposed = canonicalize_name("\u{0399}\u{0308}");
1648        assert_eq!(composed.as_slice(), decomposed.as_slice());
1649
1650        // Unicode accent folding (e\u{0301} vs \u{00c9})
1651        let e_accent_lower = canonicalize_name("e\u{0301}");
1652        let e_accent_upper = canonicalize_name("\u{00c9}");
1653        assert_eq!(e_accent_lower.as_slice(), e_accent_upper.as_slice());
1654
1655        // Unicode German sharp S (straße vs STRASSE)
1656        let strasse_lower = canonicalize_name("straße");
1657        let strasse_upper = canonicalize_name("STRASSE");
1658        assert_eq!(strasse_lower.as_slice(), strasse_upper.as_slice());
1659
1660        // Non-UTF-8 preserved as-is
1661        let non_utf8 = canonicalize_name(b"foo\x80bar");
1662        assert_eq!(non_utf8.as_slice(), b"foo\x80bar");
1663    }
1664
1665    #[test]
1666    fn test_dir_entry_children_casefold() {
1667        let mut children = DirEntryChildren::new(true);
1668        assert!(children.is_casefold());
1669
1670        // 1. Insert and lookup casefolded UTF-8
1671        children.insert("FooBar.TXT".into(), Weak::new());
1672        assert!(children.get("foobar.txt".into()).is_some());
1673        assert!(children.get("FOOBAR.TXT".into()).is_some());
1674        assert!(children.get("FooBar.TXT".into()).is_some());
1675        assert!(children.get("other.txt".into()).is_none());
1676
1677        // 2. Insert valid Unicode with decomposed/composed characters
1678        children.insert("\u{03AA}".into(), Weak::new());
1679        assert!(children.get("\u{0399}\u{0308}".into()).is_some());
1680
1681        // 3. Insert non-UTF-8
1682        children.insert(b"foo\x80bar".into(), Weak::new());
1683        assert!(children.get(b"foo\x80bar".into()).is_some());
1684        assert!(children.get(b"FOO\x80bar".into()).is_none());
1685
1686        // 4. Removal
1687        assert!(children.remove("FOOBAR.TXT".into()).is_some());
1688        assert!(children.get("foobar.txt".into()).is_none());
1689        assert!(children.remove("\u{0399}\u{0308}".into()).is_some());
1690        assert!(children.get("\u{03AA}".into()).is_none());
1691        assert!(children.remove(b"foo\x80bar".into()).is_some());
1692        assert!(children.get(b"foo\x80bar".into()).is_none());
1693        assert!(children.is_empty());
1694
1695        // 5. Dynamic casefold toggling clears the cache
1696        children.insert("Foo.TXT".into(), Weak::new());
1697        assert!(!children.is_empty());
1698        children.set_casefold(false);
1699        assert!(!children.is_casefold());
1700        assert!(children.is_empty());
1701        children.set_casefold(true);
1702        assert!(children.is_casefold());
1703    }
1704
1705    #[derive(Default)]
1706    struct CountingTestFs {
1707        /// The nodes that lookups find. Tests add nodes here to simulate files that exist in the
1708        /// filesystem but are not in the dcache.
1709        existing_nodes: Mutex<BTreeMap<FsString, FsNodeHandle>>,
1710
1711        /// The number of times that the entry of a `CountingFileNode` was revalidated.
1712        revalidate_count: AtomicUsize,
1713    }
1714
1715    impl FileSystemOps for CountingTestFs {
1716        fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
1717            Ok(default_statfs(0))
1718        }
1719
1720        fn name(&self) -> &'static FsStr {
1721            "counting_test_fs".into()
1722        }
1723    }
1724
1725    /// Returns the `CountingTestFs` of `fs`.
1726    fn counting_fs(fs: &FileSystem) -> &CountingTestFs {
1727        fs.downcast_ops::<CountingTestFs>().expect("not a CountingTestFs")
1728    }
1729
1730    struct CountingDirNode {
1731        lookup_count: Arc<AtomicUsize>,
1732    }
1733
1734    impl FsNodeOps for CountingDirNode {
1735        fn create_file_ops(
1736            &self,
1737            _node: &FsNode,
1738            _current_task: &CurrentTask,
1739            _flags: OpenFlags,
1740        ) -> Result<Box<dyn FileOps>, Errno> {
1741            Ok(Box::new(MemoryDirectoryFile::new()))
1742        }
1743
1744        fn lookup(
1745            &self,
1746            entry: &DirEntry,
1747            _current_task: &CurrentTask,
1748            name: &FsStr,
1749        ) -> Result<FsNodeHandle, Errno> {
1750            self.lookup_count.fetch_add(1, Ordering::SeqCst);
1751            let fs = entry.node.fs();
1752            let node = counting_fs(&fs).existing_nodes.lock().get(name).cloned();
1753            node.ok_or_else(|| errno!(ENOENT))
1754        }
1755
1756        fn mknod(
1757            &self,
1758            node: &FsNode,
1759            _current_task: &CurrentTask,
1760            _name: &FsStr,
1761            mode: FileMode,
1762            dev: DeviceId,
1763            owner: FsCred,
1764        ) -> Result<FsNodeHandle, Errno> {
1765            let mut info = FsNodeInfo::new(mode, owner);
1766            info.rdev = dev;
1767            Ok(node.fs().create_node_and_allocate_node_id(CountingFileNode, info))
1768        }
1769
1770        fn mkdir(
1771            &self,
1772            node: &FsNode,
1773            _current_task: &CurrentTask,
1774            _name: &FsStr,
1775            mode: FileMode,
1776            owner: FsCred,
1777        ) -> Result<FsNodeHandle, Errno> {
1778            Ok(node.fs().create_node_and_allocate_node_id(
1779                CountingDirNode { lookup_count: self.lookup_count.clone() },
1780                FsNodeInfo::new(mode, owner),
1781            ))
1782        }
1783
1784        fn unlink(
1785            &self,
1786            _node: &FsNode,
1787            _current_task: &CurrentTask,
1788            _name: &FsStr,
1789            _child: &FsNodeHandle,
1790        ) -> Result<(), Errno> {
1791            Ok(())
1792        }
1793
1794        fn create_symlink(
1795            &self,
1796            _node: &FsNode,
1797            _current_task: &CurrentTask,
1798            _name: &FsStr,
1799            _target: &FsStr,
1800            _owner: FsCred,
1801        ) -> Result<FsNodeHandle, Errno> {
1802            error!(ENOTSUP)
1803        }
1804    }
1805
1806    struct CountingFileNode;
1807    impl FsNodeOps for CountingFileNode {
1808        fs_node_impl_not_dir!();
1809
1810        fn create_file_ops(
1811            &self,
1812            _node: &FsNode,
1813            _current_task: &CurrentTask,
1814            _flags: OpenFlags,
1815        ) -> Result<Box<dyn FileOps>, Errno> {
1816            Ok(Box::new(PanickingFile))
1817        }
1818
1819        fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
1820            Box::new(CountingDirEntryOps)
1821        }
1822    }
1823
1824    struct CountingDirEntryOps;
1825    impl DirEntryOps for CountingDirEntryOps {
1826        fn revalidate(&self, _current_task: &CurrentTask, entry: &DirEntry) -> Result<bool, Errno> {
1827            counting_fs(&entry.node.fs()).revalidate_count.fetch_add(1, Ordering::SeqCst);
1828            Ok(true)
1829        }
1830    }
1831
1832    fn create_counting_fs(current_task: &CurrentTask) -> (FileSystemHandle, Arc<AtomicUsize>) {
1833        let lookup_count = Arc::new(AtomicUsize::new(0));
1834        let fs = FileSystem::new(
1835            current_task.kernel(),
1836            CacheMode::Cached(CacheConfig { capacity: 16 }),
1837            CountingTestFs::default(),
1838            FileSystemOptions::default(),
1839        )
1840        .expect("create filesystem");
1841        let root_ino = fs.allocate_ino();
1842        fs.create_root(root_ino, CountingDirNode { lookup_count: lookup_count.clone() });
1843        (fs, lookup_count)
1844    }
1845
1846    #[fuchsia::test]
1847    async fn test_open_create_looks_up_once() {
1848        spawn_kernel_and_run(async |current_task| {
1849            let (fs, lookup_count) = create_counting_fs(&current_task);
1850            let dir = Namespace::new(fs.clone()).root();
1851            let open = |name: &str, flags: OpenFlags| {
1852                current_task.open_namespace_node_at(
1853                    dir.clone(),
1854                    name.into(),
1855                    flags,
1856                    mode!(IFREG, 0o644),
1857                    ResolveFlags::empty(),
1858                )
1859            };
1860
1861            // Creating a new file looks up the name only once before creating it.
1862            let created = open("file", OpenFlags::CREAT | OpenFlags::RDWR).expect("create file");
1863            assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1864
1865            // Opening the existing file with O_CREAT is served from the cache, and revalidates the
1866            // cached entry only once.
1867            let opened = open("file", OpenFlags::CREAT | OpenFlags::RDWR).expect("open file");
1868            assert!(Arc::ptr_eq(&created.name.entry, &opened.name.entry));
1869            assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1870            assert_eq!(counting_fs(&fs).revalidate_count.load(Ordering::SeqCst), 1);
1871
1872            // O_EXCL still fails for an existing file.
1873            assert_eq!(
1874                open("file", OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR).unwrap_err(),
1875                errno!(EEXIST)
1876            );
1877            assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1878
1879            // Creating a new file with O_EXCL also looks up the name only once.
1880            open("excl", OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR)
1881                .expect("create excl");
1882            assert_eq!(lookup_count.load(Ordering::SeqCst), 2);
1883        })
1884        .await;
1885    }
1886
1887    #[fuchsia::test]
1888    async fn test_open_create_existing() {
1889        spawn_kernel_and_run(async |current_task| {
1890            let (fs, lookup_count) = create_counting_fs(&current_task);
1891            let ns = Namespace::new(fs.clone());
1892            let dir = ns.root();
1893            let open = |name: &str, resolve_flags: ResolveFlags| {
1894                current_task.open_namespace_node_at(
1895                    dir.clone(),
1896                    name.into(),
1897                    OpenFlags::CREAT | OpenFlags::RDWR,
1898                    mode!(IFREG, 0o644),
1899                    resolve_flags,
1900                )
1901            };
1902            let add_existing_node = |name: &str, node: FsNodeHandle| {
1903                counting_fs(&fs).existing_nodes.lock().insert(name.into(), node);
1904            };
1905
1906            // Opening a file that exists but is not in the dcache with O_CREAT looks it up only
1907            // once, and does not revalidate the entry that was just looked up.
1908            let file = fs.create_node_and_allocate_node_id(
1909                CountingFileNode,
1910                FsNodeInfo::new(mode!(IFREG, 0o644), FsCred::root()),
1911            );
1912            add_existing_node("file", file.clone());
1913            let opened = open("file", ResolveFlags::empty()).expect("open file");
1914            assert!(Arc::ptr_eq(&opened.name.entry.node, &file));
1915            assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1916            assert_eq!(counting_fs(&fs).revalidate_count.load(Ordering::SeqCst), 0);
1917
1918            // Opening a symlink with O_CREAT opens its target.
1919            let (link, info) = SymlinkNode::new("file".into(), FsCred::root());
1920            add_existing_node("link", fs.create_node_and_allocate_node_id(link, info));
1921            let via_link = open("link", ResolveFlags::empty()).expect("open link");
1922            assert!(Arc::ptr_eq(&via_link.name.entry, &opened.name.entry));
1923            assert_eq!(lookup_count.load(Ordering::SeqCst), 2);
1924
1925            // Opening a mount point with O_CREAT opens the root of the mount, unless the path must
1926            // not cross mount points.
1927            let mounted = open("mounted", ResolveFlags::empty()).expect("create mounted");
1928            opened
1929                .name
1930                .mount(WhatToMount::Bind(mounted.name.to_passive()), MountpointFlags::empty())
1931                .expect("bind mount");
1932            let over_mount = open("file", ResolveFlags::empty()).expect("open mount point");
1933            assert!(Arc::ptr_eq(&over_mount.name.entry, &mounted.name.entry));
1934            assert_eq!(open("file", ResolveFlags::NO_XDEV).unwrap_err(), errno!(EXDEV));
1935
1936            // With O_EXCL, opening a mount point fails with EEXIST, but only after the mount has
1937            // been entered, so a path that must not cross mount points fails with EXDEV instead,
1938            // as in Linux.
1939            let open_excl = |name: &str, resolve_flags: ResolveFlags| {
1940                current_task.open_namespace_node_at(
1941                    dir.clone(),
1942                    name.into(),
1943                    OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR,
1944                    mode!(IFREG, 0o644),
1945                    resolve_flags,
1946                )
1947            };
1948            assert_eq!(open_excl("file", ResolveFlags::empty()).unwrap_err(), errno!(EEXIST));
1949            assert_eq!(open_excl("file", ResolveFlags::NO_XDEV).unwrap_err(), errno!(EXDEV));
1950        })
1951        .await;
1952    }
1953}