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