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, UnlinkKind,
10    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, _) = self.get_or_create_child(current_task, mount, name, |d, mount, name| {
352            d.lookup(current_task, mount, name)
353        })?;
354        Ok(node)
355    }
356
357    pub fn get_children_pipelined(
358        self: &DirEntryHandle,
359        current_task: &CurrentTask,
360        mount: &MountInfo,
361        names: &[&FsStr],
362    ) -> LookupVec<Result<DirEntryHandle, Errno>> {
363        let mut nodes = LookupVec::new();
364        let mut results = LookupVec::new();
365        let mut current_parent = self.clone();
366        for i in 0..names.len() {
367            let next_node = nodes.pop();
368            match current_parent.get_or_create_child(
369                current_task,
370                mount,
371                names[i],
372                |parent_node, _mount, _name| {
373                    if let Some(node) = next_node {
374                        return node;
375                    }
376                    nodes =
377                        parent_node.ops().lookup_pipelined(parent_node, current_task, &names[i..]);
378                    nodes.reverse();
379                    nodes.pop().unwrap()
380                },
381            ) {
382                Ok((entry, _)) => {
383                    results.push(Ok(entry.clone()));
384                    current_parent = entry;
385                }
386                Err(e) => {
387                    results.push(Err(e));
388                    break;
389                }
390            }
391        }
392        results
393    }
394
395    /// Creates a new DirEntry
396    ///
397    /// The create_node_fn function is called to create the underlying FsNode
398    /// for the DirEntry.
399    ///
400    /// If the entry already exists, create_node_fn is not called, and EEXIST is
401    /// returned.
402    pub fn create_entry(
403        self: &DirEntryHandle,
404        current_task: &CurrentTask,
405        mount: &MountInfo,
406        name: &FsStr,
407        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
408    ) -> Result<DirEntryHandle, Errno> {
409        let (entry, exists) =
410            self.create_entry_internal(current_task, mount, name, create_node_fn)?;
411        if exists {
412            return error!(EEXIST);
413        }
414        Ok(entry)
415    }
416
417    /// Creates a new DirEntry. Works just like create_entry, except if the entry already exists,
418    /// it is returned.
419    pub fn get_or_create_entry(
420        self: &DirEntryHandle,
421        current_task: &CurrentTask,
422        mount: &MountInfo,
423        name: &FsStr,
424        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
425    ) -> Result<DirEntryHandle, Errno> {
426        let (entry, _exists) =
427            self.create_entry_internal(current_task, mount, name, create_node_fn)?;
428        Ok(entry)
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, bool), 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, exists) =
449            self.get_or_create_child(current_task, mount, name, create_node_fn)?;
450        if !exists {
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, exists))
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        let mut current = self.clone();
596        loop {
597            if Arc::ptr_eq(&current, other) {
598                // We found |other|.
599                return true;
600            }
601            if let Some(parent) = current.parent() {
602                current = parent;
603            } else {
604                // We reached the root of the file system.
605                return false;
606            }
607        }
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    fn get_or_create_child(
952        self: &DirEntryHandle,
953        current_task: &CurrentTask,
954        mount: &MountInfo,
955        name: &FsStr,
956        create_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
957    ) -> Result<(DirEntryHandle, bool), Errno> {
958        assert!(!DirEntry::is_reserved_name(name));
959        // Only directories can have children.
960        if !self.node.is_dir() {
961            return error!(ENOTDIR);
962        }
963        // The user must be able to search the directory (requires the EXEC permission)
964        self.node.check_access(
965            current_task,
966            mount,
967            Access::EXEC,
968            CheckAccessReason::InternalPermissionChecks,
969            self,
970        )?;
971
972        // Check if the child is already in children. In that case, we can
973        // simply return the child and we do not need to call init_fn.
974        let child = self.children.read().get(name).and_then(Weak::upgrade);
975        let (child, create_result) = if let Some(child) = child {
976            // Do not cache a child in a locked directory
977            if self.node.fail_if_locked(current_task, &self.node.info()).is_ok() {
978                child.node.fs().did_access_dir_entry(&child);
979            }
980            (child, CreationResult::Existed { create_fn })
981        } else {
982            let (child, create_result) =
983                self.lock_children().get_or_create_child(current_task, mount, name, create_fn)?;
984            child.node.fs().purge_old_entries();
985            (child, create_result)
986        };
987
988        let (child, exists) = match create_result {
989            CreationResult::Created => (child, false),
990            CreationResult::Existed { create_fn } => {
991                if child.ops.revalidate(current_task, &child)? {
992                    (child, true)
993                } else {
994                    self.internal_remove_child(&child);
995                    child.destroy(&current_task.kernel().mounts);
996
997                    let (child, create_result) = self.lock_children().get_or_create_child(
998                        current_task,
999                        mount,
1000                        name,
1001                        create_fn,
1002                    )?;
1003                    child.node.fs().purge_old_entries();
1004                    (child, matches!(create_result, CreationResult::Existed { .. }))
1005                }
1006            }
1007        };
1008
1009        Ok((child, exists))
1010    }
1011
1012    // This function is only useful for tests and has some oddities.
1013    //
1014    // For example, not all the children might have been looked up yet, which
1015    // means the returned vector could be missing some names.
1016    //
1017    // Also, the vector might have "extra" names that are in the process of
1018    // being looked up. If the lookup fails, they'll be removed.
1019    #[cfg(test)]
1020    pub fn copy_child_names(&self) -> Vec<FsString> {
1021        let scope = RcuReadScope::new();
1022        self.children.read().copy_child_names(&scope)
1023    }
1024
1025    fn internal_remove_child(&self, child: &DirEntry) {
1026        let mut children = self.children.write();
1027        let scope = RcuReadScope::new();
1028        let local_name = child.local_name.read(&scope);
1029        children.remove_if_child_matches(local_name, child);
1030    }
1031
1032    /// Notifies watchers on the current node and its parent about an event.
1033    pub fn notify(&self, event_mask: InotifyMask) {
1034        self.notify_watchers(event_mask, self.is_dead());
1035    }
1036
1037    /// Notifies watchers on the current node and its parent about an event.
1038    ///
1039    /// Used for FSNOTIFY_EVENT_INODE events, which ignore IN_EXCL_UNLINK.
1040    pub fn notify_ignoring_excl_unlink(&self, event_mask: InotifyMask) {
1041        // We pretend that this directory entry is not dead to ignore IN_EXCL_UNLINK.
1042        self.notify_watchers(event_mask, false);
1043    }
1044
1045    fn notify_watchers(&self, event_mask: InotifyMask, is_dead: bool) {
1046        let mode = self.node.info().mode;
1047        {
1048            let scope = RcuReadScope::new();
1049            if let Some(parent) = self.parent() {
1050                let local_name = self.local_name.read(&scope);
1051                parent.node.notify(event_mask, 0, local_name, mode, is_dead);
1052            }
1053        }
1054        self.node.notify(event_mask, 0, Default::default(), mode, is_dead);
1055    }
1056
1057    /// Notifies parents about creation, and notifies current node about link_count change.
1058    fn notify_creation(&self) {
1059        let mode = self.node.info().mode;
1060        if Arc::strong_count(&self.node) > 1 {
1061            // Notify about link change only if there is already a hardlink.
1062            self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
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(InotifyMask::CREATE, 0, local_name, mode, false);
1068        }
1069    }
1070
1071    /// Notifies watchers on the current node about deletion if this is the
1072    /// last hardlink, and drops the DirEntryHandle kept by Inotify.
1073    /// Parent is also notified about deletion.
1074    fn notify_deletion(&self) {
1075        let mode = self.node.info().mode;
1076        if !mode.is_dir() {
1077            // Linux notifies link count change for non-directories.
1078            self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1079        }
1080
1081        // This check is incorrect if there's another hard link to this FsNode that isn't in
1082        // memory at the moment.
1083        if Arc::strong_count(&self.node) == 1 {
1084            self.node.notify(InotifyMask::DELETE_SELF, 0, Default::default(), mode, false);
1085        }
1086
1087        let scope = RcuReadScope::new();
1088        if let Some(parent) = self.parent() {
1089            let local_name = self.local_name.read(&scope);
1090            parent.node.notify(InotifyMask::DELETE, 0, local_name, mode, false);
1091        }
1092    }
1093
1094    /// Returns true if this entry has mounts.
1095    pub fn has_mounts(&self) -> bool {
1096        self.flags().contains(DirEntryFlags::HAS_MOUNTS)
1097    }
1098
1099    /// Records whether or not the entry has mounts.
1100    pub fn set_has_mounts(&self, v: bool) {
1101        if v {
1102            self.raise_flags(DirEntryFlags::HAS_MOUNTS);
1103        } else {
1104            self.lower_flags(DirEntryFlags::HAS_MOUNTS);
1105        }
1106    }
1107
1108    /// Verifies this directory has nothing mounted on it.
1109    fn require_no_mounts(self: &Arc<Self>, parent_mount: &MountInfo) -> Result<(), Errno> {
1110        if self.has_mounts() {
1111            if let Some(mount) = parent_mount.as_ref() {
1112                if mount.has_submount(self) {
1113                    return error!(EBUSY);
1114                }
1115            }
1116        }
1117        Ok(())
1118    }
1119}
1120
1121/// Canonicalizes a filename into its casefolded, NFD-normalized byte representation on the stack.
1122/// Non-UTF-8 filenames are preserved as raw bytes.
1123#[inline]
1124fn canonicalize_name(name: impl AsRef<[u8]>) -> SmallVec<[u8; NAME_MAX as usize]> {
1125    let name = name.as_ref();
1126    match std::str::from_utf8(name) {
1127        Ok(valid) => {
1128            CasefoldStr::new(valid).casefold_normalized_chars().flat_map(utf8_bytes).collect()
1129        }
1130        Err(_) => SmallVec::from_slice(name),
1131    }
1132}
1133
1134/// A canonicalized key stored in the directory entry cache.
1135///
1136/// Stores pre-casefolded, NFD-normalized UTF-8 bytes (for casefolded directories) or raw bytes
1137/// (for case-sensitive directories and non-UTF-8 filenames).
1138#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
1139pub(crate) struct DirEntryChildKey(FsString);
1140
1141impl DirEntryChildKey {
1142    pub fn new(name: &FsStr, casefold: bool) -> Self {
1143        if casefold {
1144            Self(canonicalize_name(name).into_vec().into())
1145        } else {
1146            Self(name.to_owned())
1147        }
1148    }
1149}
1150
1151impl std::borrow::Borrow<FsStr> for DirEntryChildKey {
1152    fn borrow(&self) -> &FsStr {
1153        self.0.as_bstr()
1154    }
1155}
1156
1157/// The cached child directory entries of a directory.
1158///
1159/// Keys stored in this map are strictly internal to the VFS cache and are never exposed back to
1160/// userspace. Directory listings (`readdir`) query the underlying filesystem directly for canonical
1161/// on-disk names, while individual child entries track their associated lookup/creation name in
1162/// [`DirEntry::local_name`].
1163#[derive(Default, Debug)]
1164pub(crate) struct DirEntryChildren {
1165    entries: BTreeMap<DirEntryChildKey, Weak<DirEntry>>,
1166    casefold: bool,
1167}
1168
1169impl std::ops::Deref for DirEntryChildren {
1170    type Target = BTreeMap<DirEntryChildKey, Weak<DirEntry>>;
1171
1172    fn deref(&self) -> &Self::Target {
1173        &self.entries
1174    }
1175}
1176
1177impl DirEntryChildren {
1178    pub fn new(casefold: bool) -> Self {
1179        Self { entries: BTreeMap::new(), casefold }
1180    }
1181
1182    pub fn is_casefold(&self) -> bool {
1183        self.casefold
1184    }
1185
1186    pub fn set_casefold(&mut self, casefold: bool) {
1187        self.entries.clear();
1188        self.casefold = casefold;
1189    }
1190
1191    /// Looks up a child entry by name.
1192    pub fn get(&self, name: &FsStr) -> Option<&Weak<DirEntry>> {
1193        if self.casefold {
1194            let key = canonicalize_name(name);
1195            self.entries.get(FsStr::new(&key))
1196        } else {
1197            self.entries.get(name)
1198        }
1199    }
1200
1201    /// Inserts a child into the directory cache under `name`, returning the weak reference
1202    /// previously stored under this name, if any.
1203    pub fn insert(&mut self, name: &FsStr, child: Weak<DirEntry>) -> Option<Weak<DirEntry>> {
1204        self.entries.insert(DirEntryChildKey::new(name, self.casefold), child)
1205    }
1206
1207    /// Removes a child from the directory cache by name, returning the weak reference
1208    /// previously stored under this name, if any.
1209    pub fn remove(&mut self, name: &FsStr) -> Option<Weak<DirEntry>> {
1210        if self.casefold {
1211            let key = canonicalize_name(name);
1212            self.entries.remove(FsStr::new(&key))
1213        } else {
1214            self.entries.remove(name)
1215        }
1216    }
1217
1218    /// Removes a child entry if the cached weak reference matches `expected_child`.
1219    /// Returns `true` if the entry was found and removed.
1220    pub fn remove_if_child_matches(&mut self, name: &FsStr, expected_child: &DirEntry) -> bool {
1221        if let Some(weak_child) = self.get(name) {
1222            if std::ptr::eq(weak_child.as_ptr(), expected_child) {
1223                let _ = self.remove(name);
1224                return true;
1225            }
1226        }
1227        false
1228    }
1229
1230    #[cfg(test)]
1231    /// Extracts a snapshot of all active child names, retaining the exact casing currently
1232    /// stored on each child node and filtering out dropped entries.
1233    fn copy_child_names(&self, scope: &RcuReadScope) -> Vec<FsString> {
1234        self.entries
1235            .values()
1236            .filter_map(|child| Weak::upgrade(child).map(|c| c.local_name.read(scope).to_owned()))
1237            .collect()
1238    }
1239}
1240
1241struct DirEntryLockedChildren<'a> {
1242    entry: &'a DirEntryHandle,
1243    children: LockDepWriteGuard<'a, DirEntryChildren>,
1244}
1245
1246enum CreationResult<F> {
1247    Created,
1248    Existed { create_fn: F },
1249}
1250
1251impl<'a> DirEntryLockedChildren<'a> {
1252    fn component_lookup(
1253        &mut self,
1254        current_task: &CurrentTask,
1255        mount: &MountInfo,
1256        name: &FsStr,
1257    ) -> Result<DirEntryHandle, Errno> {
1258        assert!(!DirEntry::is_reserved_name(name));
1259        let (node, _) =
1260            self.get_or_create_child(current_task, mount, name, |_, _, _| error!(ENOENT))?;
1261        Ok(node)
1262    }
1263
1264    fn get_or_create_child<
1265        F: FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
1266    >(
1267        &mut self,
1268        current_task: &CurrentTask,
1269        mount: &MountInfo,
1270        name: &FsStr,
1271        create_fn: F,
1272    ) -> Result<(DirEntryHandle, CreationResult<F>), Errno> {
1273        let create_child = |create_fn: F| {
1274            // Before creating the child, check for existence.
1275            let (node, create_result) = match self.entry.node.lookup(current_task, mount, name) {
1276                Ok(node) => (node, CreationResult::Existed { create_fn }),
1277                Err(e) if e == ENOENT => {
1278                    (create_fn(&self.entry.node, mount, name)?, CreationResult::Created)
1279                }
1280                Err(e) => return Err(e),
1281            };
1282
1283            assert!(
1284                node.info().mode & FileMode::IFMT != FileMode::EMPTY,
1285                "FsNode initialization did not populate the FileMode in FsNodeInfo."
1286            );
1287
1288            let entry = DirEntry::new(node, Some(self.entry.clone()), name.to_owned());
1289
1290            if let Err(err) = security::fs_node_init_with_dentry(current_task, &entry) {
1291                // Null out the `parent` reference from `entry` otherwise dropping `entry` will
1292                // attempt to remove itself from `parent`, triggering a deadlock with `self`.
1293                entry.parent.update(None);
1294                return Err(err);
1295            }
1296
1297            Ok((entry, create_result))
1298        };
1299
1300        if let Some(child) = self.children.get(name).and_then(Weak::upgrade) {
1301            // Do not cache a child in a locked directory
1302            if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1303                child.node.fs().did_access_dir_entry(&child);
1304            }
1305            return Ok((child, CreationResult::Existed { create_fn }));
1306        }
1307
1308        let (child, create_result) = create_child(create_fn)?;
1309        // Do not cache a child in a locked directory
1310        if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1311            self.children.insert(name, Arc::downgrade(&child));
1312        }
1313
1314        Ok((child, create_result))
1315    }
1316}
1317
1318impl fmt::Debug for DirEntry {
1319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1320        let scope = RcuReadScope::new();
1321        let mut parents = vec![];
1322        let mut maybe_parent = self.parent();
1323        while let Some(parent) = maybe_parent {
1324            parents.push(parent.local_name.read(&scope));
1325            maybe_parent = parent.parent();
1326        }
1327        let mut builder = f.debug_struct("DirEntry");
1328        builder.field("id", &(self as *const DirEntry));
1329        builder.field("local_name", &self.local_name.read(&scope).to_owned());
1330        if !parents.is_empty() {
1331            builder.field("parents", &parents);
1332        }
1333        builder.finish()
1334    }
1335}
1336
1337#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1338enum RenameRelationship {
1339    Same,
1340    NewIsDescendant,
1341    OldIsDescendant,
1342    Independent,
1343}
1344
1345struct RenameGuard<'a> {
1346    old_parent_guard: DirEntryLockedChildren<'a>,
1347    new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1348    relationship: RenameRelationship,
1349}
1350
1351impl<'a> RenameGuard<'a> {
1352    fn lock(old_parent: &'a DirEntryHandle, new_parent: &'a DirEntryHandle) -> Self {
1353        if Arc::ptr_eq(old_parent, new_parent) {
1354            let old_parent_guard = old_parent.lock_children();
1355            Self {
1356                old_parent_guard,
1357                new_parent_guard: None,
1358                relationship: RenameRelationship::Same,
1359            }
1360        } else if new_parent.is_descendant_of(old_parent) {
1361            let old_parent_guard = old_parent.lock_children();
1362            let _token = allow_subclass();
1363            let new_parent_guard = new_parent.lock_children();
1364            Self {
1365                old_parent_guard,
1366                new_parent_guard: Some(new_parent_guard),
1367                relationship: RenameRelationship::NewIsDescendant,
1368            }
1369        } else if old_parent.is_descendant_of(new_parent) {
1370            let new_parent_guard = new_parent.lock_children();
1371            let _token = allow_subclass();
1372            let old_parent_guard = old_parent.lock_children();
1373            Self {
1374                old_parent_guard,
1375                new_parent_guard: Some(new_parent_guard),
1376                relationship: RenameRelationship::OldIsDescendant,
1377            }
1378        } else {
1379            // Independent directories can be locked in address order.
1380            let (g1, g2) =
1381                starnix_sync::ordered_write_lock(&old_parent.children, &new_parent.children);
1382            let old_parent_guard = DirEntryLockedChildren { entry: old_parent, children: g1 };
1383            let new_parent_guard = DirEntryLockedChildren { entry: new_parent, children: g2 };
1384            Self {
1385                old_parent_guard,
1386                new_parent_guard: Some(new_parent_guard),
1387                relationship: RenameRelationship::Independent,
1388            }
1389        }
1390    }
1391
1392    /// Consumes the `RenameGuard` (which only holds children locks) and locks the `info`
1393    /// of the parent directories in a safe order to prevent deadlocks. Returns a
1394    /// `RenameGuardLocked` which encapsulates all acquired locks (both children and info).
1395    fn lock_info(
1396        self,
1397        old_parent: &'a DirEntryHandle,
1398        new_parent: &'a DirEntryHandle,
1399        renamed: &'a DirEntryHandle,
1400        replaced: Option<&'a DirEntryHandle>,
1401    ) -> RenameContext<'a> {
1402        let (g1, g2) = match self.relationship {
1403            RenameRelationship::Same => (old_parent.node.info_lock().write(), None),
1404            RenameRelationship::NewIsDescendant => {
1405                let g1 = old_parent.node.info_lock().write();
1406                let _token = allow_subclass();
1407                let g2 = new_parent.node.info_lock().write();
1408                (g1, Some(g2))
1409            }
1410            RenameRelationship::OldIsDescendant => {
1411                let g2 = new_parent.node.info_lock().write();
1412                let _token = allow_subclass();
1413                let g1 = old_parent.node.info_lock().write();
1414                (g1, Some(g2))
1415            }
1416            RenameRelationship::Independent => {
1417                let (g1, g2) = starnix_sync::ordered_write_lock(
1418                    old_parent.node.info_lock(),
1419                    new_parent.node.info_lock(),
1420                );
1421                (g1, Some(g2))
1422            }
1423        };
1424
1425        RenameContext {
1426            renamed,
1427            replaced,
1428            old_parent_guard: self.old_parent_guard,
1429            new_parent_guard: self.new_parent_guard,
1430            old_parent_info_guard: g1,
1431            new_parent_info_guard: g2,
1432        }
1433    }
1434
1435    fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1436        &mut self.old_parent_guard
1437    }
1438
1439    fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1440        if let Some(new_guard) = self.new_parent_guard.as_mut() {
1441            new_guard
1442        } else {
1443            &mut self.old_parent_guard
1444        }
1445    }
1446}
1447
1448/// A context that holds the locked children and info of the parents during a
1449/// rename operation.
1450///
1451/// The context is constructed by locking the parent directories
1452/// (`old_parent`, `new_parent`) for write. These parent locks are held for
1453/// the duration of the context's lifetime, preventing concurrent
1454/// modifications to the parents.
1455///
1456/// Child nodes (`renamed` and `replaced`) are *not* locked upon
1457/// construction. To query properties of the children (like whether they are
1458/// directories) without risking deadlocks, use the provided helper methods
1459/// (`renamed_is_dir`, `replaced_is_dir`) instead of locking them directly.
1460pub struct RenameContext<'a> {
1461    pub renamed: &'a DirEntryHandle,
1462    pub replaced: Option<&'a DirEntryHandle>,
1463    old_parent_guard: DirEntryLockedChildren<'a>,
1464    new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1465    old_parent_info_guard: LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>,
1466    new_parent_info_guard: Option<LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>>,
1467}
1468
1469impl<'a> RenameContext<'a> {
1470    /// Returns whether the renamed child node is a directory.
1471    ///
1472    /// This method safely handles child info locking under the parent locks.
1473    /// If the renamed node is same as a parent (which is already locked),
1474    /// it uses the parent's guard to avoid self-deadlock. Otherwise, it
1475    /// locks the child info using `allow_subclass`.
1476    pub fn renamed_is_dir(&self) -> bool {
1477        self.is_dir(&self.renamed.node)
1478    }
1479
1480    /// Returns whether the replaced child node is a directory.
1481    ///
1482    /// Returns `false` if `replaced` is `None`.
1483    ///
1484    /// This method safely handles child info locking under the parent locks.
1485    /// If the replaced 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 replaced_is_dir(&self) -> bool {
1489        self.replaced.map(|r| self.is_dir(&r.node)).unwrap_or(false)
1490    }
1491
1492    /// Returns the old parent directory entry handle.
1493    ///
1494    /// The old parent's child list is write-locked for the lifetime of the
1495    /// context.
1496    pub fn old_parent(&self) -> &DirEntryHandle {
1497        self.old_parent_guard.entry
1498    }
1499
1500    /// Returns the new parent directory entry handle.
1501    ///
1502    /// The new parent's child list is write-locked for the lifetime of the
1503    /// context.
1504    pub fn new_parent(&self) -> &DirEntryHandle {
1505        self.new_parent_guard.as_ref().map(|g| g.entry).unwrap_or(self.old_parent_guard.entry)
1506    }
1507
1508    /// Returns mutable references to the `FsNodeInfo` of both parent
1509    /// directories.
1510    ///
1511    /// The references are returned as a tuple to allow them to be borrowed
1512    /// mutably at the same time (e.g., to update link counts in both).
1513    ///
1514    /// If `new_parent` is the same as `old_parent`, the second element of the
1515    /// tuple will be `None` to prevent mutable aliasing of the same guard.
1516    pub fn parent_infos_mut(
1517        &mut self,
1518    ) -> (&mut crate::vfs::FsNodeInfo, Option<&mut crate::vfs::FsNodeInfo>) {
1519        let old = &mut *self.old_parent_info_guard;
1520        let new = self.new_parent_info_guard.as_mut().map(|g| &mut **g);
1521        (old, new)
1522    }
1523
1524    /// Returns a shared reference to the `FsNodeInfo` of the old parent
1525    /// directory.
1526    pub fn old_parent_info(&self) -> &crate::vfs::FsNodeInfo {
1527        &self.old_parent_info_guard
1528    }
1529    /// Returns a shared reference to the `FsNodeInfo` of the new parent
1530    /// directory.
1531    ///
1532    /// Returns `None` if `new_parent` is the same as `old_parent`.
1533    pub fn new_parent_info(&self) -> Option<&crate::vfs::FsNodeInfo> {
1534        self.new_parent_info_guard.as_deref()
1535    }
1536
1537    fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1538        self.new_parent_guard.as_mut().unwrap_or(&mut self.old_parent_guard)
1539    }
1540
1541    fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1542        &mut self.old_parent_guard
1543    }
1544
1545    fn is_dir(&self, node: &FsNodeHandle) -> bool {
1546        if Arc::ptr_eq(node, &self.old_parent().node) {
1547            self.old_parent_info_guard.mode.is_dir()
1548        } else if Arc::ptr_eq(node, &self.new_parent().node) {
1549            self.new_parent_info_guard
1550                .as_ref()
1551                .map(|g| g.mode.is_dir())
1552                .unwrap_or_else(|| self.old_parent_info_guard.mode.is_dir())
1553        } else {
1554            let _token = allow_subclass();
1555            node.is_dir()
1556        }
1557    }
1558}
1559
1560/// The Drop trait for DirEntry removes the entry from the child list of the
1561/// parent entry, which means we cannot drop DirEntry objects while holding a
1562/// lock on the parent's child list.
1563impl Drop for DirEntry {
1564    fn drop(&mut self) {
1565        let maybe_parent = self.parent();
1566        self.parent.update(None);
1567        if let Some(parent) = maybe_parent {
1568            parent.internal_remove_child(self);
1569        }
1570    }
1571}
1572
1573#[cfg(test)]
1574mod tests {
1575    use super::*;
1576
1577    #[test]
1578    fn test_canonicalize_name() {
1579        let upper = canonicalize_name("FooBar.TXT");
1580        let lower = canonicalize_name("foobar.txt");
1581        let mixed = canonicalize_name("FOOBAR.TXT");
1582        let diff = canonicalize_name("other.txt");
1583
1584        assert_eq!(upper.as_slice(), lower.as_slice());
1585        assert_eq!(upper.as_slice(), mixed.as_slice());
1586        assert_ne!(upper.as_slice(), diff.as_slice());
1587
1588        // Unicode decomposed vs composed
1589        let composed = canonicalize_name("\u{03AA}");
1590        let decomposed = canonicalize_name("\u{0399}\u{0308}");
1591        assert_eq!(composed.as_slice(), decomposed.as_slice());
1592
1593        // Unicode accent folding (e\u{0301} vs \u{00c9})
1594        let e_accent_lower = canonicalize_name("e\u{0301}");
1595        let e_accent_upper = canonicalize_name("\u{00c9}");
1596        assert_eq!(e_accent_lower.as_slice(), e_accent_upper.as_slice());
1597
1598        // Unicode German sharp S (straße vs STRASSE)
1599        let strasse_lower = canonicalize_name("straße");
1600        let strasse_upper = canonicalize_name("STRASSE");
1601        assert_eq!(strasse_lower.as_slice(), strasse_upper.as_slice());
1602
1603        // Non-UTF-8 preserved as-is
1604        let non_utf8 = canonicalize_name(b"foo\x80bar");
1605        assert_eq!(non_utf8.as_slice(), b"foo\x80bar");
1606    }
1607
1608    #[test]
1609    fn test_dir_entry_children_casefold() {
1610        let mut children = DirEntryChildren::new(true);
1611        assert!(children.is_casefold());
1612
1613        // 1. Insert and lookup casefolded UTF-8
1614        children.insert("FooBar.TXT".into(), Weak::new());
1615        assert!(children.get("foobar.txt".into()).is_some());
1616        assert!(children.get("FOOBAR.TXT".into()).is_some());
1617        assert!(children.get("FooBar.TXT".into()).is_some());
1618        assert!(children.get("other.txt".into()).is_none());
1619
1620        // 2. Insert valid Unicode with decomposed/composed characters
1621        children.insert("\u{03AA}".into(), Weak::new());
1622        assert!(children.get("\u{0399}\u{0308}".into()).is_some());
1623
1624        // 3. Insert non-UTF-8
1625        children.insert(b"foo\x80bar".into(), Weak::new());
1626        assert!(children.get(b"foo\x80bar".into()).is_some());
1627        assert!(children.get(b"FOO\x80bar".into()).is_none());
1628
1629        // 4. Removal
1630        assert!(children.remove("FOOBAR.TXT".into()).is_some());
1631        assert!(children.get("foobar.txt".into()).is_none());
1632        assert!(children.remove("\u{0399}\u{0308}".into()).is_some());
1633        assert!(children.get("\u{03AA}".into()).is_none());
1634        assert!(children.remove(b"foo\x80bar".into()).is_some());
1635        assert!(children.get(b"foo\x80bar".into()).is_none());
1636        assert!(children.is_empty());
1637
1638        // 5. Dynamic casefold toggling clears the cache
1639        children.insert("Foo.TXT".into(), Weak::new());
1640        assert!(!children.is_empty());
1641        children.set_casefold(false);
1642        assert!(!children.is_casefold());
1643        assert!(children.is_empty());
1644        children.set_casefold(true);
1645        assert!(children.is_casefold());
1646    }
1647}