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, FileHandle, FileObject, FsLockDepType, FsNodeHandle, FsNodeLinkBehavior,
9    FsStr, FsString, LookupVec, MountInfo, Mounts, NamespaceNode, UnlinkKind, inotify_hook, path,
10};
11use atomic_bitflags::atomic_bitflags;
12use bitflags::bitflags;
13use fuchsia_rcu::{RcuOptionArc, RcuReadScope};
14use fuchsia_sync::ResetDependencies;
15use starnix_rcu::RcuString;
16use starnix_sync::{
17    DirEntryChildrenLevel, DirEntryChildrenRecursiveLevel, DynamicLockDepRwLock,
18    FuseDirEntryChildrenLevel, LockDepWriteGuard, allow_subclass,
19};
20use starnix_uapi::auth::FsCred;
21use starnix_uapi::errors::{ENOENT, Errno};
22use starnix_uapi::file_mode::{Access, FileMode};
23use starnix_uapi::inotify_mask::InotifyMask;
24use starnix_uapi::open_flags::OpenFlags;
25use starnix_uapi::{NAME_MAX, RENAME_EXCHANGE, RENAME_NOREPLACE, RENAME_WHITEOUT, error};
26use std::collections::BTreeMap;
27use std::collections::btree_map::Entry;
28use std::fmt;
29use std::ops::Deref;
30use std::sync::atomic::Ordering;
31use std::sync::{Arc, Weak};
32
33bitflags! {
34    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35    pub struct RenameFlags: u32 {
36        // Exchange the entries.
37        const EXCHANGE = RENAME_EXCHANGE;
38
39        // Don't overwrite an existing DirEntry.
40        const NOREPLACE = RENAME_NOREPLACE;
41
42        // Create a "whiteout" object to replace the file.
43        const WHITEOUT = RENAME_WHITEOUT;
44
45        // Allow replacing any file with a directory. This is an internal flag used only
46        // internally inside Starnix for OverlayFS.
47        const REPLACE_ANY = 1 << 31;
48
49        // Internal flags that cannot be passed to `sys_rename()`
50        const INTERNAL = Self::REPLACE_ANY.bits();
51    }
52}
53
54pub trait DirEntryOps: Send + Sync + 'static {
55    /// Revalidate the [`DirEntry`], if needed.
56    ///
57    /// Most filesystems don't need to do any revalidations because they are "local"
58    /// and all changes to nodes go through the kernel. However some filesystems
59    /// allow changes to happen through other means (e.g. NFS, FUSE) and these
60    /// filesystems need a way to let the kernel know it may need to refresh its
61    /// cached metadata. This method provides that hook for such filesystems.
62    ///
63    /// For more details, see:
64    ///  - https://www.halolinux.us/kernel-reference/the-dentry-cache.html
65    ///  - https://www.kernel.org/doc/html/latest/filesystems/path-lookup.html#revalidation-and-automounts
66    ///  - https://lwn.net/Articles/649115/
67    ///  - https://www.infradead.org/~mchehab/kernel_docs/filesystems/path-walking.html
68    ///
69    /// Returns `Ok(valid)` where `valid` indicates if the `DirEntry` is still valid,
70    /// or an error.
71    fn revalidate(&self, _: &CurrentTask, _: &DirEntry) -> Result<bool, Errno> {
72        Ok(true)
73    }
74}
75
76atomic_bitflags! {
77    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
78    pub struct DirEntryFlags: u8 {
79        /// Whether this directory entry has been removed from the tree.
80        const IS_DEAD = 1 << 0;
81
82        /// Whether the entry has filesystems mounted on top of it.
83        const HAS_MOUNTS = 1 << 1;
84    }
85}
86
87pub struct DefaultDirEntryOps;
88
89impl DirEntryOps for DefaultDirEntryOps {}
90
91/// An entry in a directory.
92///
93/// This structure assigns a name to an FsNode in a given file system. An
94/// FsNode might have multiple directory entries, for example if there are more
95/// than one hard link to the same FsNode. In those cases, each hard link will
96/// have a different parent and a different local_name because each hard link
97/// has its own DirEntry object.
98///
99/// A directory cannot have more than one hard link, which means there is a
100/// single DirEntry for each Directory FsNode. That invariant lets us store the
101/// children for a directory in the DirEntry rather than in the FsNode.
102pub struct DirEntry {
103    /// The FsNode referenced by this DirEntry.
104    ///
105    /// A given FsNode can be referenced by multiple DirEntry objects, for
106    /// example if there are multiple hard links to a given FsNode.
107    pub node: FsNodeHandle,
108
109    /// The [`DirEntryOps`] for this `DirEntry`.
110    ///
111    /// The `DirEntryOps` are implemented by the individual file systems to provide
112    /// specific behaviours for this `DirEntry`.
113    ops: Box<dyn DirEntryOps>,
114
115    /// The parent DirEntry.
116    ///
117    /// The DirEntry tree has strong references from child-to-parent and weak
118    /// references from parent-to-child. This design ensures that the parent
119    /// chain is always populated in the cache, but some children might be
120    /// missing from the cache.
121    parent: RcuOptionArc<DirEntry>,
122
123    /// The [`DirEntryFlags`] for this `DirEntry`.
124    flags: AtomicDirEntryFlags,
125
126    /// The name that this parent calls this child.
127    ///
128    /// This name might not be reflected in the full path in the namespace that
129    /// contains this DirEntry. For example, this DirEntry might be the root of
130    /// a chroot.
131    ///
132    /// Most callers that want to work with names for DirEntries should use the
133    /// NamespaceNodes.
134    local_name: RcuString,
135
136    /// A partial cache of the children of this DirEntry.
137    ///
138    /// DirEntries are added to this cache when they are looked up and removed
139    /// when they are no longer referenced.
140    ///
141    // FIXME(b/379929394): The lock ordering here assumes parent-to-child lock acquisition, which
142    // a number of algorithms in the DirEntry operations also assume. This assumption can be broken
143    // by the rename operation, which can move nodes around the hierarchy. See the referenced bug
144    // for more details, the current mitigations, and potentials for long-term solutions.
145    children: DynamicLockDepRwLock<DirEntryChildren>,
146}
147type DirEntryChildren = BTreeMap<FsString, Weak<DirEntry>>;
148
149pub type DirEntryHandle = Arc<DirEntry>;
150
151impl DirEntry {
152    #[allow(clippy::let_and_return)]
153    pub fn new_uncached(
154        node: FsNodeHandle,
155        parent: Option<DirEntryHandle>,
156        local_name: FsString,
157    ) -> DirEntryHandle {
158        let ops = node.create_dir_entry_ops();
159        let fs_lockdep_type = node.fs().fs_lockdep_type();
160        let result = Arc::new(DirEntry {
161            node,
162            ops,
163            parent: RcuOptionArc::new(parent),
164            flags: Default::default(),
165            local_name: local_name.into(),
166            children: match fs_lockdep_type {
167                FsLockDepType::Normal => {
168                    DynamicLockDepRwLock::new::<DirEntryChildrenLevel>(Default::default())
169                }
170                FsLockDepType::Recursive => {
171                    DynamicLockDepRwLock::new::<DirEntryChildrenRecursiveLevel>(Default::default())
172                }
173                FsLockDepType::Fuse => {
174                    DynamicLockDepRwLock::new::<FuseDirEntryChildrenLevel>(Default::default())
175                }
176            },
177        });
178        #[cfg(any(test, debug_assertions))]
179        {
180            // Taking this lock tells the lock tracing system about the parent/child ordering
181            // relation.
182            let _token = allow_subclass();
183            let _l1 = result.children.read();
184        }
185        result
186    }
187
188    pub fn new(
189        node: FsNodeHandle,
190        parent: Option<DirEntryHandle>,
191        local_name: FsString,
192    ) -> DirEntryHandle {
193        let result = Self::new_uncached(node, parent, local_name);
194        result.node.fs().did_create_dir_entry(&result);
195        result
196    }
197
198    /// Returns a new DirEntry for the given `node` without parent. The entry has no local name and
199    /// is not cached.
200    pub fn new_unrooted(node: FsNodeHandle) -> DirEntryHandle {
201        Self::new_uncached(node, None, FsString::default())
202    }
203
204    /// Returns a new `DirEntry` that is ready marked as having been deleted.
205    pub fn new_deleted(
206        node: FsNodeHandle,
207        parent: Option<DirEntryHandle>,
208        local_name: FsString,
209    ) -> DirEntryHandle {
210        let entry = DirEntry::new_uncached(node, parent, local_name);
211        entry.raise_flags(DirEntryFlags::IS_DEAD);
212        entry
213    }
214
215    /// Returns a file handle to this entry, associated with an anonymous namespace.
216    pub fn open_anonymous(
217        self: &DirEntryHandle,
218        current_task: &CurrentTask,
219        flags: OpenFlags,
220    ) -> Result<FileHandle, Errno> {
221        let ops = self.node.create_file_ops(current_task, flags)?;
222        FileObject::new(current_task, ops, NamespaceNode::new_anonymous(self.clone()), flags)
223    }
224
225    /// Set the children of this DirEntry to the given `children`. This should only ever be called
226    /// when children is empty.
227    pub fn set_children(self: &DirEntryHandle, children: BTreeMap<FsString, DirEntryHandle>) {
228        let mut dir_entry_children = self.lock_children();
229        assert!(dir_entry_children.children.is_empty());
230        for (name, child) in children.into_iter() {
231            child.set_parent(self.clone());
232            dir_entry_children.children.insert(name, Arc::downgrade(&child));
233        }
234    }
235
236    fn lock_children<'a>(self: &'a DirEntryHandle) -> DirEntryLockedChildren<'a> {
237        DirEntryLockedChildren { entry: self, children: self.children.write() }
238    }
239
240    /// The parent DirEntry.
241    pub fn parent(&self) -> Option<DirEntryHandle> {
242        self.parent.to_option_arc()
243    }
244
245    /// Returns a reference to the parent DirEntry.
246    ///
247    /// The reference is only valid for the duration of the RCU read scope.
248    pub fn parent_ref<'a>(&'a self, scope: &'a RcuReadScope) -> Option<&'a DirEntry> {
249        self.parent.as_ref(scope)
250    }
251
252    /// Set the parent of this DirEntry.
253    pub fn set_parent(&self, parent: DirEntryHandle) {
254        self.parent.update(Some(parent));
255    }
256
257    /// The parent DirEntry object or this DirEntry if this entry is the root.
258    ///
259    /// Useful when traversing up the tree if you always want to find a parent
260    /// (e.g., for "..").
261    ///
262    /// Be aware that the root of one file system might be mounted as a child
263    /// in another file system. For that reason, consider walking the
264    /// NamespaceNode tree (which understands mounts) rather than the DirEntry
265    /// tree.
266    pub fn parent_or_self(self: &DirEntryHandle) -> DirEntryHandle {
267        self.parent().unwrap_or_else(|| self.clone())
268    }
269
270    /// The name that this parent calls this child.
271    ///
272    /// The reference is only valid for the duration of the RCU read scope.
273    pub fn local_name<'a>(&self, scope: &'a RcuReadScope) -> &'a FsStr {
274        self.local_name.read(scope)
275    }
276
277    /// Whether the given name has special semantics as a directory entry.
278    ///
279    /// Specifically, whether the name is empty (which means "self"), dot
280    /// (which also means "self"), or dot dot (which means "parent").
281    pub fn is_reserved_name(name: &FsStr) -> bool {
282        name.is_empty() || name == "." || name == ".."
283    }
284
285    /// Returns the flags of this DirEntry.
286    pub fn flags(&self) -> DirEntryFlags {
287        self.flags.load(Ordering::Acquire)
288    }
289
290    /// Raises the flags of this DirEntry.
291    ///
292    /// Returns the flags of this DirEntry before the flags were raised.
293    pub fn raise_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
294        self.flags.fetch_or(flags, Ordering::AcqRel)
295    }
296
297    /// Lowers the flags of this DirEntry.
298    ///
299    /// Returns the flags of this DirEntry before the flags were lowered.
300    pub fn lower_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
301        self.flags.fetch_and(!flags, Ordering::AcqRel)
302    }
303
304    /// Returns true if this DirEntry is dead.
305    pub fn is_dead(&self) -> bool {
306        self.flags().contains(DirEntryFlags::IS_DEAD)
307    }
308
309    /// Look up a directory entry with the given name as direct child of this
310    /// entry.
311    pub fn component_lookup(
312        self: &DirEntryHandle,
313        current_task: &CurrentTask,
314        mount: &MountInfo,
315        name: &FsStr,
316    ) -> Result<DirEntryHandle, Errno> {
317        let (node, _) = self.get_or_create_child(current_task, mount, name, |d, mount, name| {
318            d.lookup(current_task, mount, name)
319        })?;
320        Ok(node)
321    }
322
323    pub fn get_children_pipelined(
324        self: &DirEntryHandle,
325        current_task: &CurrentTask,
326        mount: &MountInfo,
327        names: &[&FsStr],
328    ) -> LookupVec<Result<DirEntryHandle, Errno>> {
329        let mut nodes = LookupVec::new();
330        let mut results = LookupVec::new();
331        let mut current_parent = self.clone();
332        for i in 0..names.len() {
333            let next_node = nodes.pop();
334            match current_parent.get_or_create_child(
335                current_task,
336                mount,
337                names[i],
338                |parent_node, _mount, _name| {
339                    if let Some(node) = next_node {
340                        return node;
341                    }
342                    nodes =
343                        parent_node.ops().lookup_pipelined(parent_node, current_task, &names[i..]);
344                    nodes.reverse();
345                    nodes.pop().unwrap()
346                },
347            ) {
348                Ok((entry, _)) => {
349                    results.push(Ok(entry.clone()));
350                    current_parent = entry;
351                }
352                Err(e) => {
353                    results.push(Err(e));
354                    break;
355                }
356            }
357        }
358        results
359    }
360
361    /// Creates a new DirEntry
362    ///
363    /// The create_node_fn function is called to create the underlying FsNode
364    /// for the DirEntry.
365    ///
366    /// If the entry already exists, create_node_fn is not called, and EEXIST is
367    /// returned.
368    pub fn create_entry(
369        self: &DirEntryHandle,
370        current_task: &CurrentTask,
371        mount: &MountInfo,
372        name: &FsStr,
373        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
374    ) -> Result<DirEntryHandle, Errno> {
375        let (entry, exists) =
376            self.create_entry_internal(current_task, mount, name, create_node_fn)?;
377        if exists {
378            return error!(EEXIST);
379        }
380        Ok(entry)
381    }
382
383    /// Creates a new DirEntry. Works just like create_entry, except if the entry already exists,
384    /// it is returned.
385    pub fn get_or_create_entry(
386        self: &DirEntryHandle,
387        current_task: &CurrentTask,
388        mount: &MountInfo,
389        name: &FsStr,
390        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
391    ) -> Result<DirEntryHandle, Errno> {
392        let (entry, _exists) =
393            self.create_entry_internal(current_task, mount, name, create_node_fn)?;
394        Ok(entry)
395    }
396
397    fn create_entry_internal(
398        self: &DirEntryHandle,
399        current_task: &CurrentTask,
400        mount: &MountInfo,
401        name: &FsStr,
402        create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
403    ) -> Result<(DirEntryHandle, bool), Errno> {
404        if DirEntry::is_reserved_name(name) {
405            return error!(EEXIST);
406        }
407        // TODO: Do we need to check name for embedded NUL characters?
408        if name.len() > NAME_MAX as usize {
409            return error!(ENAMETOOLONG);
410        }
411        if name.contains(&path::SEPARATOR) {
412            return error!(EINVAL);
413        }
414        let (entry, exists) =
415            self.get_or_create_child(current_task, mount, name, create_node_fn)?;
416        if !exists {
417            // An entry was created. Update the ctime and mtime of this directory.
418            self.node.update_ctime_mtime();
419            entry.notify_creation();
420        }
421        Ok((entry, exists))
422    }
423
424    // This is marked as test-only because it sets the owner/group to root instead of the current
425    // user to save a bit of typing in tests, but this shouldn't happen silently in production.
426    #[cfg(test)]
427    pub fn create_dir(
428        self: &DirEntryHandle,
429        current_task: &CurrentTask,
430        name: &FsStr,
431    ) -> Result<DirEntryHandle, Errno> {
432        self.create_dir_for_testing(current_task, name)
433    }
434
435    // This function is for testing because it sets the owner/group to root instead of the current
436    // user to save a bit of typing in tests, but this shouldn't happen silently in production.
437    pub fn create_dir_for_testing(
438        self: &DirEntryHandle,
439        current_task: &CurrentTask,
440        name: &FsStr,
441    ) -> Result<DirEntryHandle, Errno> {
442        // TODO: apply_umask
443        self.create_entry(current_task, &MountInfo::detached(), name, |dir, mount, name| {
444            dir.create_node(
445                current_task,
446                mount,
447                name,
448                starnix_uapi::file_mode::mode!(IFDIR, 0o777),
449                starnix_uapi::device_id::DeviceId::NONE,
450                FsCred::root(),
451            )
452        })
453    }
454
455    /// Creates an anonymous file.
456    ///
457    /// The FileMode::IFMT of the FileMode is always FileMode::IFREG.
458    ///
459    /// Used by O_TMPFILE.
460    pub fn create_tmpfile(
461        self: &DirEntryHandle,
462        current_task: &CurrentTask,
463        mount: &MountInfo,
464        mode: FileMode,
465        owner: FsCred,
466        flags: OpenFlags,
467    ) -> Result<DirEntryHandle, Errno> {
468        // Only directories can have children.
469        if !self.node.is_dir() {
470            return error!(ENOTDIR);
471        }
472        assert!(mode.is_reg());
473
474        // From <https://man7.org/linux/man-pages/man2/open.2.html>:
475        //
476        //   Specifying O_EXCL in conjunction with O_TMPFILE prevents a
477        //   temporary file from being linked into the filesystem in
478        //   the above manner.  (Note that the meaning of O_EXCL in
479        //   this case is different from the meaning of O_EXCL
480        //   otherwise.)
481        let link_behavior = if flags.contains(OpenFlags::EXCL) {
482            FsNodeLinkBehavior::Disallowed
483        } else {
484            FsNodeLinkBehavior::Allowed
485        };
486
487        let node = self.node.create_tmpfile(current_task, mount, mode, owner, link_behavior)?;
488        let local_name = format!("#{}", node.ino).into();
489        Ok(DirEntry::new_deleted(node, Some(self.clone()), local_name))
490    }
491
492    pub fn unlink(
493        self: &DirEntryHandle,
494        current_task: &CurrentTask,
495        mount: &MountInfo,
496        name: &FsStr,
497        kind: UnlinkKind,
498        must_be_directory: bool,
499    ) -> Result<(), Errno> {
500        assert!(!DirEntry::is_reserved_name(name));
501
502        // child_to_unlink *must* be dropped after self_children (even in the error paths).
503        let child_to_unlink;
504
505        let mut self_children = self.lock_children();
506        child_to_unlink = self_children.component_lookup(current_task, mount, name)?;
507        child_to_unlink.require_no_mounts(mount)?;
508
509        // Check that this filesystem entry must be a directory. This can
510        // happen if the path terminates with a trailing slash.
511        //
512        // Example: If we're unlinking a symlink `/foo/bar/`, this would
513        // result in `ENOTDIR` because of the trailing slash, even if
514        // `UnlinkKind::NonDirectory` was used.
515        if must_be_directory && !child_to_unlink.node.is_dir() {
516            return error!(ENOTDIR);
517        }
518
519        match kind {
520            UnlinkKind::Directory => {
521                if !child_to_unlink.node.is_dir() {
522                    return error!(ENOTDIR);
523                }
524            }
525            UnlinkKind::NonDirectory => {
526                if child_to_unlink.node.is_dir() {
527                    return error!(EISDIR);
528                }
529            }
530        }
531
532        self.node.unlink(current_task, mount, name, &child_to_unlink.node)?;
533        self_children.children.remove(name);
534
535        std::mem::drop(self_children);
536        child_to_unlink.destroy(&current_task.kernel().mounts);
537
538        Ok(())
539    }
540
541    /// Destroy this directory entry.
542    ///
543    /// Notice that this method takes `self` by value to destroy this reference.
544    fn destroy(self: DirEntryHandle, mounts: &Mounts) {
545        let was_already_dead =
546            self.raise_flags(DirEntryFlags::IS_DEAD).contains(DirEntryFlags::IS_DEAD);
547        if was_already_dead {
548            return;
549        }
550        let unmount =
551            self.lower_flags(DirEntryFlags::HAS_MOUNTS).contains(DirEntryFlags::HAS_MOUNTS);
552        self.node.fs().will_destroy_dir_entry(&self);
553        if unmount {
554            mounts.unmount(&self);
555        }
556        self.notify_deletion();
557    }
558
559    /// Returns whether this entry is a descendant of |other|.
560    pub fn is_descendant_of(self: &DirEntryHandle, other: &DirEntryHandle) -> bool {
561        let scope = RcuReadScope::new();
562        let mut current = self.deref();
563        loop {
564            if std::ptr::eq(current, other.deref()) {
565                // We found |other|.
566                return true;
567            }
568            if let Some(parent) = current.parent_ref(&scope) {
569                current = parent;
570            } else {
571                // We reached the root of the file system.
572                return false;
573            }
574        }
575    }
576
577    /// Rename the file with old_basename in old_parent to new_basename in
578    /// new_parent.
579    ///
580    /// old_parent and new_parent must belong to the same file system.
581    pub fn rename(
582        current_task: &CurrentTask,
583        old_parent: &DirEntryHandle,
584        old_mount: &MountInfo,
585        old_basename: &FsStr,
586        new_parent: &DirEntryHandle,
587        new_mount: &MountInfo,
588        new_basename: &FsStr,
589        flags: RenameFlags,
590    ) -> Result<(), Errno> {
591        // The nodes we are touching must be part of the same mount.
592        if old_mount != new_mount {
593            return error!(EXDEV);
594        }
595
596        // The mounts are equals, choose one.
597        let mount = old_mount;
598
599        // If either the old_basename or the new_basename is a reserved name
600        // (e.g., "." or ".."), then we cannot do the rename.
601        if DirEntry::is_reserved_name(old_basename) || DirEntry::is_reserved_name(new_basename) {
602            if flags.contains(RenameFlags::NOREPLACE) {
603                return error!(EEXIST);
604            }
605            return error!(EBUSY);
606        }
607
608        // If the names and parents are the same, then there's nothing to do
609        // and we can report success.
610        if Arc::ptr_eq(&old_parent.node, &new_parent.node) && old_basename == new_basename {
611            return Ok(());
612        }
613
614        // This task must have write access to the old and new parent nodes.
615        old_parent.node.check_access(
616            current_task,
617            mount,
618            Access::WRITE,
619            CheckAccessReason::InternalPermissionChecks,
620            old_parent,
621        )?;
622        new_parent.node.check_access(
623            current_task,
624            mount,
625            Access::WRITE,
626            CheckAccessReason::InternalPermissionChecks,
627            new_parent,
628        )?;
629
630        // The mount check ensures that the nodes we're touching are part of the
631        // same file system. It doesn't matter where we grab the FileSystem reference from.
632        let fs = old_parent.node.fs();
633
634        // We need to hold these DirEntryHandles until after we drop all the
635        // locks so that we do not deadlock when we drop them.
636        let renamed;
637        let mut maybe_replaced = None;
638
639        {
640            // Before we take any locks, we need to take the rename mutex on
641            // the file system. This lock ensures that no other rename
642            // operations are happening in this file system while we're
643            // analyzing this rename operation.
644            //
645            // For example, we grab writer locks on both old_parent and
646            // new_parent. If there was another rename operation in flight with
647            // old_parent and new_parent reversed, then we could deadlock while
648            // trying to acquire these locks.
649            let _lock = fs.rename_mutex.lock();
650
651            // We cannot simply grab the locks on old_parent and new_parent
652            // independently because old_parent and new_parent might be the
653            // same directory entry. Instead, we use the RenameGuard helper to
654            // grab the appropriate locks.
655            let mut state = RenameGuard::lock(old_parent, new_parent);
656
657            // Now that we know the old_parent child list cannot change, we
658            // establish the DirEntry that we are going to try to rename.
659            renamed =
660                state.old_parent_children().component_lookup(current_task, mount, old_basename)?;
661
662            // We need to check if there is already a DirEntry with
663            // new_basename in new_parent. If so, there are additional checks
664            // we need to perform.
665            // This must be done BEFORE locking info to avoid self-deadlock.
666            let lookup_replaced =
667                state.new_parent_children().component_lookup(current_task, mount, new_basename);
668
669            // If the target entry is an ancestor of the source parent, the
670            // rename would create a cycle (for EXCHANGE) or attempt to
671            // overwrite a non-empty directory.
672            // We check this before acquiring child locks to avoid
673            // deadlocks from bottom-up locking (locking ancestor after
674            // descendant).
675            // This is done early (before parent locks) as a fail-fast
676            // optimization since we already have the lookup result.
677            if let Ok(replaced) = &lookup_replaced {
678                if old_parent.is_descendant_of(replaced) {
679                    if flags.contains(RenameFlags::EXCHANGE) {
680                        return error!(EINVAL);
681                    } else {
682                        return error!(ENOTEMPTY);
683                    }
684                }
685            }
686
687            // Lock the info for the parents to ensure that subsequent checks on their
688            // state (e.g., sticky bit checks) and the actual rename operation are not racy.
689            let mut state =
690                state.lock_info(old_parent, new_parent, &renamed, lookup_replaced.as_ref().ok());
691
692            // If new_parent is a descendant of renamed, the operation would
693            // create a cycle. That's disallowed.
694            if new_parent.is_descendant_of(&renamed) {
695                return error!(EINVAL);
696            }
697
698            // Check whether the sticky bit on the old parent prevents us from
699            // removing this child.
700            {
701                // Safe because the parent is locked first, and then we check the
702                // sticky bit of the child. This parent -> child acquisition follows
703                // the hierarchical lock ordering.
704                let _token = allow_subclass();
705                old_parent.node.check_sticky_bit(
706                    current_task,
707                    &renamed.node,
708                    state.old_parent_info(),
709                )?;
710            }
711
712            // Check whether the renamed entry is a mountpoint.
713            // TODO: We should hold a read lock on the mount points for this
714            //       namespace to prevent the child from becoming a mount point
715            //       while this function is executing.
716            renamed.require_no_mounts(mount)?;
717
718            // We lookup the replaced entry before locking info to avoid deadlock, but we match
719            // on the result here under the parent info locks. This ensures that checks on the
720            // replaced entry (e.g., existence, directory status, identity) are consistent and
721            // do not race with concurrent operations.
722            match &lookup_replaced {
723                Ok(replaced) => {
724                    // Set `maybe_replaced` now to ensure it gets dropped in the right order.
725                    let replaced = maybe_replaced.insert(replaced.clone());
726
727                    if flags.contains(RenameFlags::NOREPLACE) {
728                        return error!(EEXIST);
729                    }
730
731                    // Sayeth https://man7.org/linux/man-pages/man2/rename.2.html:
732                    //
733                    // "If oldpath and newpath are existing hard links referring to the
734                    // same file, then rename() does nothing, and returns a success
735                    // status."
736                    if Arc::ptr_eq(&renamed.node, &replaced.node) {
737                        return Ok(());
738                    }
739
740                    // Sayeth https://man7.org/linux/man-pages/man2/rename.2.html:
741                    //
742                    // "oldpath can specify a directory.  In this case, newpath must"
743                    // either not exist, or it must specify an empty directory."
744                    if state.replaced_is_dir() {
745                        // Check whether the replaced entry is a mountpoint.
746                        // TODO: We should hold a read lock on the mount points for this
747                        //       namespace to prevent the child from becoming a mount point
748                        //       while this function is executing.
749                        replaced.require_no_mounts(mount)?;
750                    }
751
752                    if !flags.intersects(RenameFlags::EXCHANGE | RenameFlags::REPLACE_ANY) {
753                        let renamed_is_dir = state.renamed_is_dir();
754                        let replaced_is_dir = state.replaced_is_dir();
755                        if renamed_is_dir && !replaced_is_dir {
756                            return error!(ENOTDIR);
757                        } else if !renamed_is_dir && replaced_is_dir {
758                            return error!(EISDIR);
759                        }
760                    }
761                }
762                // It's fine for the lookup to fail to find a child.
763                Err(errno) if *errno == ENOENT => {
764                    if flags.contains(RenameFlags::EXCHANGE) {
765                        return error!(ENOENT);
766                    }
767                }
768                // However, other errors are fatal.
769                Err(e) => return Err(e.clone()),
770            }
771
772            security::check_fs_node_rename_access(
773                current_task,
774                &old_parent.node,
775                &renamed.node,
776                &new_parent.node,
777                maybe_replaced.as_ref().map(|dir_entry| dir_entry.node.deref().as_ref()),
778                old_basename,
779                new_basename,
780            )?;
781
782            if let Some(replaced) = maybe_replaced.as_ref() {
783                // Safe because the parent is locked first, and then we check the
784                // sticky bit of the child. This parent -> child acquisition
785                // follows the hierarchical lock ordering.
786                let _token = allow_subclass();
787                new_parent.node.check_sticky_bit(
788                    current_task,
789                    &replaced.node,
790                    state.new_parent_info().unwrap_or_else(|| state.old_parent_info()),
791                )?;
792            }
793
794            // We've found all the errors that we know how to find. Ask the
795            // file system to actually execute the rename operation. Once the
796            // file system has executed the rename, we are no longer allowed to
797            // fail because we will not be able to return the system to a
798            // consistent state.
799
800            if flags.contains(RenameFlags::EXCHANGE) {
801                fs.exchange(current_task, &mut state, old_basename, new_basename)?;
802            } else {
803                fs.rename(current_task, &mut state, old_basename, new_basename)?;
804            }
805
806            // We need to update the parent and local name for the DirEntry
807            // we are renaming to reflect its new parent and its new name.
808            renamed.set_parent(new_parent.clone());
809            renamed.local_name.update(new_basename.to_owned());
810
811            // Actually add the renamed child to the new_parent's child list.
812            // This operation implicitly removes the replaced child (if any)
813            // from the child list.
814            state
815                .new_parent_children()
816                .children
817                .insert(new_basename.into(), Arc::downgrade(&renamed));
818
819            // Lock ordering is enforced from parent-to-child, and therefore we need to
820            // reset the lock ordering constraints when we reorder the tree nodes.
821            // SAFETY: We manually clear the dependency graph for these locks.
822            // This is safe because `fs.rename_mutex` is held during this operation, which
823            // prevents the tree topology from changing concurrently. This allows us to safely
824            // dynamically enforce a sound locking order (e.g. by memory address in `RenameGuard`)
825            // to avoid deadlocks. Clearing the graph prevents false-positive cycle panics from `tracing-mutex`
826            // after the node is reparented.
827            unsafe {
828                renamed.children.reset_dependencies();
829                renamed.node.info_lock().reset_dependencies();
830                old_parent.children.reset_dependencies();
831                old_parent.node.info_lock().reset_dependencies();
832                new_parent.children.reset_dependencies();
833                new_parent.node.info_lock().reset_dependencies();
834            }
835
836            if flags.contains(RenameFlags::EXCHANGE) {
837                // Reparent `replaced` when exchanging.
838                let replaced =
839                    maybe_replaced.as_ref().expect("replaced expected with RENAME_EXCHANGE");
840                replaced.set_parent(old_parent.clone());
841                replaced.local_name.update(old_basename.to_owned());
842                state
843                    .old_parent_children()
844                    .children
845                    .insert(old_basename.into(), Arc::downgrade(replaced));
846
847                // Lock ordering is enforced from parent-to-child, and therefore we need to
848                // reset the lock ordering constraints when we reorder the tree nodes.
849                // SAFETY: See the comment above for `renamed` lock resetting.
850                unsafe {
851                    replaced.children.reset_dependencies();
852                    replaced.node.info_lock().reset_dependencies();
853                }
854            } else {
855                // Remove the renamed child from the old_parent's child list.
856                state.old_parent_children().children.remove(old_basename);
857            }
858        };
859
860        fs.purge_old_entries();
861
862        if let Some(replaced) = maybe_replaced {
863            if !flags.contains(RenameFlags::EXCHANGE) {
864                replaced.destroy(&current_task.kernel().mounts);
865            }
866        }
867
868        // Renaming a file updates its ctime.
869        renamed.node.update_ctime();
870
871        let mode = renamed.node.info().mode;
872        if let Some(hook) =
873            current_task.kernel().expando.peek::<Arc<dyn inotify_hook::NotifyHook>>()
874        {
875            let cookie = hook.get_next_cookie();
876            old_parent.node.notify(InotifyMask::MOVE_FROM, cookie, old_basename, mode, false);
877            new_parent.node.notify(InotifyMask::MOVE_TO, cookie, new_basename, mode, false);
878            renamed.node.notify(InotifyMask::MOVE_SELF, 0, Default::default(), mode, false);
879        }
880
881        Ok(())
882    }
883
884    pub fn get_children<F, T>(&self, callback: F) -> T
885    where
886        F: FnOnce(&DirEntryChildren) -> T,
887    {
888        let children = self.children.read();
889        callback(&children)
890    }
891
892    /// Remove the child with the given name from the children cache.  The child must not have any
893    /// mounts.
894    pub fn remove_child(&self, name: &FsStr, mounts: &Mounts) {
895        let mut children = self.children.write();
896        let child = children.get(name).and_then(Weak::upgrade);
897        if let Some(child) = child {
898            children.remove(name);
899            std::mem::drop(children);
900            child.destroy(mounts);
901        }
902    }
903
904    fn get_or_create_child(
905        self: &DirEntryHandle,
906        current_task: &CurrentTask,
907        mount: &MountInfo,
908        name: &FsStr,
909        create_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
910    ) -> Result<(DirEntryHandle, bool), Errno> {
911        assert!(!DirEntry::is_reserved_name(name));
912        // Only directories can have children.
913        if !self.node.is_dir() {
914            return error!(ENOTDIR);
915        }
916        // The user must be able to search the directory (requires the EXEC permission)
917        self.node.check_access(
918            current_task,
919            mount,
920            Access::EXEC,
921            CheckAccessReason::InternalPermissionChecks,
922            self,
923        )?;
924
925        // Check if the child is already in children. In that case, we can
926        // simply return the child and we do not need to call init_fn.
927        let child = self.children.read().get(name).and_then(Weak::upgrade);
928        let (child, create_result) = if let Some(child) = child {
929            // Do not cache a child in a locked directory
930            if self.node.fail_if_locked(current_task, &self.node.info()).is_ok() {
931                child.node.fs().did_access_dir_entry(&child);
932            }
933            (child, CreationResult::Existed { create_fn })
934        } else {
935            let (child, create_result) =
936                self.lock_children().get_or_create_child(current_task, mount, name, create_fn)?;
937            child.node.fs().purge_old_entries();
938            (child, create_result)
939        };
940
941        let (child, exists) = match create_result {
942            CreationResult::Created => (child, false),
943            CreationResult::Existed { create_fn } => {
944                if child.ops.revalidate(current_task, &child)? {
945                    (child, true)
946                } else {
947                    self.internal_remove_child(&child);
948                    child.destroy(&current_task.kernel().mounts);
949
950                    let (child, create_result) = self.lock_children().get_or_create_child(
951                        current_task,
952                        mount,
953                        name,
954                        create_fn,
955                    )?;
956                    child.node.fs().purge_old_entries();
957                    (child, matches!(create_result, CreationResult::Existed { .. }))
958                }
959            }
960        };
961
962        Ok((child, exists))
963    }
964
965    // This function is only useful for tests and has some oddities.
966    //
967    // For example, not all the children might have been looked up yet, which
968    // means the returned vector could be missing some names.
969    //
970    // Also, the vector might have "extra" names that are in the process of
971    // being looked up. If the lookup fails, they'll be removed.
972    #[cfg(test)]
973    pub fn copy_child_names(&self) -> Vec<FsString> {
974        let scope = RcuReadScope::new();
975        self.children
976            .read()
977            .values()
978            .filter_map(|child| Weak::upgrade(child).map(|c| c.local_name.read(&scope).to_owned()))
979            .collect()
980    }
981
982    fn internal_remove_child(&self, child: &DirEntry) {
983        let mut children = self.children.write();
984        let scope = RcuReadScope::new();
985        let local_name = child.local_name.read(&scope);
986        if let Some(weak_child) = children.get(local_name) {
987            // If this entry is occupied, we need to check whether child is
988            // the current occupant. If so, we should remove the entry
989            // because the child no longer exists.
990            if std::ptr::eq(weak_child.as_ptr(), child) {
991                children.remove(local_name);
992            }
993        }
994    }
995
996    /// Notifies watchers on the current node and its parent about an event.
997    pub fn notify(&self, event_mask: InotifyMask) {
998        self.notify_watchers(event_mask, self.is_dead());
999    }
1000
1001    /// Notifies watchers on the current node and its parent about an event.
1002    ///
1003    /// Used for FSNOTIFY_EVENT_INODE events, which ignore IN_EXCL_UNLINK.
1004    pub fn notify_ignoring_excl_unlink(&self, event_mask: InotifyMask) {
1005        // We pretend that this directory entry is not dead to ignore IN_EXCL_UNLINK.
1006        self.notify_watchers(event_mask, false);
1007    }
1008
1009    fn notify_watchers(&self, event_mask: InotifyMask, is_dead: bool) {
1010        let mode = self.node.info().mode;
1011        {
1012            let scope = RcuReadScope::new();
1013            if let Some(parent) = self.parent_ref(&scope) {
1014                let local_name = self.local_name.read(&scope);
1015                parent.node.notify(event_mask, 0, local_name, mode, is_dead);
1016            }
1017        }
1018        self.node.notify(event_mask, 0, Default::default(), mode, is_dead);
1019    }
1020
1021    /// Notifies parents about creation, and notifies current node about link_count change.
1022    fn notify_creation(&self) {
1023        let mode = self.node.info().mode;
1024        if Arc::strong_count(&self.node) > 1 {
1025            // Notify about link change only if there is already a hardlink.
1026            self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1027        }
1028        let scope = RcuReadScope::new();
1029        if let Some(parent) = self.parent_ref(&scope) {
1030            let local_name = self.local_name.read(&scope);
1031            parent.node.notify(InotifyMask::CREATE, 0, local_name, mode, false);
1032        }
1033    }
1034
1035    /// Notifies watchers on the current node about deletion if this is the
1036    /// last hardlink, and drops the DirEntryHandle kept by Inotify.
1037    /// Parent is also notified about deletion.
1038    fn notify_deletion(&self) {
1039        let mode = self.node.info().mode;
1040        if !mode.is_dir() {
1041            // Linux notifies link count change for non-directories.
1042            self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1043        }
1044
1045        // This check is incorrect if there's another hard link to this FsNode that isn't in
1046        // memory at the moment.
1047        if Arc::strong_count(&self.node) == 1 {
1048            self.node.notify(InotifyMask::DELETE_SELF, 0, Default::default(), mode, false);
1049        }
1050
1051        let scope = RcuReadScope::new();
1052        if let Some(parent) = self.parent_ref(&scope) {
1053            let local_name = self.local_name.read(&scope);
1054            parent.node.notify(InotifyMask::DELETE, 0, local_name, mode, false);
1055        }
1056    }
1057
1058    /// Returns true if this entry has mounts.
1059    pub fn has_mounts(&self) -> bool {
1060        self.flags().contains(DirEntryFlags::HAS_MOUNTS)
1061    }
1062
1063    /// Records whether or not the entry has mounts.
1064    pub fn set_has_mounts(&self, v: bool) {
1065        if v {
1066            self.raise_flags(DirEntryFlags::HAS_MOUNTS);
1067        } else {
1068            self.lower_flags(DirEntryFlags::HAS_MOUNTS);
1069        }
1070    }
1071
1072    /// Verifies this directory has nothing mounted on it.
1073    fn require_no_mounts(self: &Arc<Self>, parent_mount: &MountInfo) -> Result<(), Errno> {
1074        if self.has_mounts() {
1075            if let Some(mount) = parent_mount.as_ref() {
1076                if mount.has_submount(self) {
1077                    return error!(EBUSY);
1078                }
1079            }
1080        }
1081        Ok(())
1082    }
1083}
1084
1085struct DirEntryLockedChildren<'a> {
1086    entry: &'a DirEntryHandle,
1087    children: LockDepWriteGuard<'a, DirEntryChildren>,
1088}
1089
1090enum CreationResult<F> {
1091    Created,
1092    Existed { create_fn: F },
1093}
1094
1095impl<'a> DirEntryLockedChildren<'a> {
1096    fn component_lookup(
1097        &mut self,
1098        current_task: &CurrentTask,
1099        mount: &MountInfo,
1100        name: &FsStr,
1101    ) -> Result<DirEntryHandle, Errno> {
1102        assert!(!DirEntry::is_reserved_name(name));
1103        let (node, _) =
1104            self.get_or_create_child(current_task, mount, name, |_, _, _| error!(ENOENT))?;
1105        Ok(node)
1106    }
1107
1108    fn get_or_create_child<
1109        F: FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
1110    >(
1111        &mut self,
1112        current_task: &CurrentTask,
1113        mount: &MountInfo,
1114        name: &FsStr,
1115        create_fn: F,
1116    ) -> Result<(DirEntryHandle, CreationResult<F>), Errno> {
1117        let create_child = |create_fn: F| {
1118            // Before creating the child, check for existence.
1119            let (node, create_result) = match self.entry.node.lookup(current_task, mount, name) {
1120                Ok(node) => (node, CreationResult::Existed { create_fn }),
1121                Err(e) if e == ENOENT => {
1122                    (create_fn(&self.entry.node, mount, name)?, CreationResult::Created)
1123                }
1124                Err(e) => return Err(e),
1125            };
1126
1127            assert!(
1128                node.info().mode & FileMode::IFMT != FileMode::EMPTY,
1129                "FsNode initialization did not populate the FileMode in FsNodeInfo."
1130            );
1131
1132            let entry = DirEntry::new(node, Some(self.entry.clone()), name.to_owned());
1133
1134            if let Err(err) = security::fs_node_init_with_dentry(current_task, &entry) {
1135                // Null out the `parent` reference from `entry` otherwise dropping `entry` will
1136                // attempt to remove itself from `parent`, triggering a deadlock with `self`.
1137                entry.parent.update(None);
1138                return Err(err);
1139            }
1140
1141            Ok((entry, create_result))
1142        };
1143
1144        let (child, create_result) = match self.children.entry(name.to_owned()) {
1145            Entry::Vacant(entry) => {
1146                let (child, create_result) = create_child(create_fn)?;
1147                // Do not cache a child in a locked directory
1148                if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1149                    entry.insert(Arc::downgrade(&child));
1150                }
1151                (child, create_result)
1152            }
1153            Entry::Occupied(mut entry) => {
1154                // It's possible that the upgrade will succeed this time around because we dropped
1155                // the read lock before acquiring the write lock. Another thread might have
1156                // populated this entry while we were not holding any locks.
1157                if let Some(child) = Weak::upgrade(entry.get()) {
1158                    // Do not cache a child in a locked directory
1159                    if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok()
1160                    {
1161                        child.node.fs().did_access_dir_entry(&child);
1162                    }
1163                    return Ok((child, CreationResult::Existed { create_fn }));
1164                }
1165                let (child, create_result) = create_child(create_fn)?;
1166                // Do not cache a child in a locked directory
1167                if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1168                    entry.insert(Arc::downgrade(&child));
1169                }
1170                (child, create_result)
1171            }
1172        };
1173
1174        Ok((child, create_result))
1175    }
1176}
1177
1178impl fmt::Debug for DirEntry {
1179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1180        let scope = RcuReadScope::new();
1181        let mut parents = vec![];
1182        let mut maybe_parent = self.parent_ref(&scope);
1183        while let Some(parent) = maybe_parent {
1184            parents.push(parent.local_name.read(&scope));
1185            maybe_parent = parent.parent_ref(&scope);
1186        }
1187        let mut builder = f.debug_struct("DirEntry");
1188        builder.field("id", &(self as *const DirEntry));
1189        builder.field("local_name", &self.local_name.read(&scope).to_owned());
1190        if !parents.is_empty() {
1191            builder.field("parents", &parents);
1192        }
1193        builder.finish()
1194    }
1195}
1196
1197#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1198enum RenameRelationship {
1199    Same,
1200    NewIsDescendant,
1201    OldIsDescendant,
1202    Independent,
1203}
1204
1205struct RenameGuard<'a> {
1206    old_parent_guard: DirEntryLockedChildren<'a>,
1207    new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1208    relationship: RenameRelationship,
1209}
1210
1211impl<'a> RenameGuard<'a> {
1212    fn lock(old_parent: &'a DirEntryHandle, new_parent: &'a DirEntryHandle) -> Self {
1213        if Arc::ptr_eq(old_parent, new_parent) {
1214            let old_parent_guard = old_parent.lock_children();
1215            Self {
1216                old_parent_guard,
1217                new_parent_guard: None,
1218                relationship: RenameRelationship::Same,
1219            }
1220        } else if new_parent.is_descendant_of(old_parent) {
1221            let old_parent_guard = old_parent.lock_children();
1222            let _token = allow_subclass();
1223            let new_parent_guard = new_parent.lock_children();
1224            Self {
1225                old_parent_guard,
1226                new_parent_guard: Some(new_parent_guard),
1227                relationship: RenameRelationship::NewIsDescendant,
1228            }
1229        } else if old_parent.is_descendant_of(new_parent) {
1230            let new_parent_guard = new_parent.lock_children();
1231            let _token = allow_subclass();
1232            let old_parent_guard = old_parent.lock_children();
1233            Self {
1234                old_parent_guard,
1235                new_parent_guard: Some(new_parent_guard),
1236                relationship: RenameRelationship::OldIsDescendant,
1237            }
1238        } else {
1239            // Independent directories can be locked in address order.
1240            let (g1, g2) =
1241                starnix_sync::ordered_write_lock(&old_parent.children, &new_parent.children);
1242            let old_parent_guard = DirEntryLockedChildren { entry: old_parent, children: g1 };
1243            let new_parent_guard = DirEntryLockedChildren { entry: new_parent, children: g2 };
1244            Self {
1245                old_parent_guard,
1246                new_parent_guard: Some(new_parent_guard),
1247                relationship: RenameRelationship::Independent,
1248            }
1249        }
1250    }
1251
1252    /// Consumes the `RenameGuard` (which only holds children locks) and locks the `info`
1253    /// of the parent directories in a safe order to prevent deadlocks. Returns a
1254    /// `RenameGuardLocked` which encapsulates all acquired locks (both children and info).
1255    fn lock_info(
1256        self,
1257        old_parent: &'a DirEntryHandle,
1258        new_parent: &'a DirEntryHandle,
1259        renamed: &'a DirEntryHandle,
1260        replaced: Option<&'a DirEntryHandle>,
1261    ) -> RenameContext<'a> {
1262        let (g1, g2) = match self.relationship {
1263            RenameRelationship::Same => (old_parent.node.info_lock().write(), None),
1264            RenameRelationship::NewIsDescendant => {
1265                let g1 = old_parent.node.info_lock().write();
1266                let _token = allow_subclass();
1267                let g2 = new_parent.node.info_lock().write();
1268                (g1, Some(g2))
1269            }
1270            RenameRelationship::OldIsDescendant => {
1271                let g2 = new_parent.node.info_lock().write();
1272                let _token = allow_subclass();
1273                let g1 = old_parent.node.info_lock().write();
1274                (g1, Some(g2))
1275            }
1276            RenameRelationship::Independent => {
1277                let (g1, g2) = starnix_sync::ordered_write_lock(
1278                    old_parent.node.info_lock(),
1279                    new_parent.node.info_lock(),
1280                );
1281                (g1, Some(g2))
1282            }
1283        };
1284
1285        RenameContext {
1286            renamed,
1287            replaced,
1288            old_parent_guard: self.old_parent_guard,
1289            new_parent_guard: self.new_parent_guard,
1290            old_parent_info_guard: g1,
1291            new_parent_info_guard: g2,
1292        }
1293    }
1294
1295    fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1296        &mut self.old_parent_guard
1297    }
1298
1299    fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1300        if let Some(new_guard) = self.new_parent_guard.as_mut() {
1301            new_guard
1302        } else {
1303            &mut self.old_parent_guard
1304        }
1305    }
1306}
1307
1308/// A context that holds the locked children and info of the parents during a
1309/// rename operation.
1310///
1311/// The context is constructed by locking the parent directories
1312/// (`old_parent`, `new_parent`) for write. These parent locks are held for
1313/// the duration of the context's lifetime, preventing concurrent
1314/// modifications to the parents.
1315///
1316/// Child nodes (`renamed` and `replaced`) are *not* locked upon
1317/// construction. To query properties of the children (like whether they are
1318/// directories) without risking deadlocks, use the provided helper methods
1319/// (`renamed_is_dir`, `replaced_is_dir`) instead of locking them directly.
1320pub struct RenameContext<'a> {
1321    pub renamed: &'a DirEntryHandle,
1322    pub replaced: Option<&'a DirEntryHandle>,
1323    old_parent_guard: DirEntryLockedChildren<'a>,
1324    new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1325    old_parent_info_guard: LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>,
1326    new_parent_info_guard: Option<LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>>,
1327}
1328
1329impl<'a> RenameContext<'a> {
1330    /// Returns whether the renamed child node is a directory.
1331    ///
1332    /// This method safely handles child info locking under the parent locks.
1333    /// If the renamed node is same as a parent (which is already locked),
1334    /// it uses the parent's guard to avoid self-deadlock. Otherwise, it
1335    /// locks the child info using `allow_subclass`.
1336    pub fn renamed_is_dir(&self) -> bool {
1337        self.is_dir(&self.renamed.node)
1338    }
1339
1340    /// Returns whether the replaced child node is a directory.
1341    ///
1342    /// Returns `false` if `replaced` is `None`.
1343    ///
1344    /// This method safely handles child info locking under the parent locks.
1345    /// If the replaced node is same as a parent (which is already locked),
1346    /// it uses the parent's guard to avoid self-deadlock. Otherwise, it
1347    /// locks the child info using `allow_subclass`.
1348    pub fn replaced_is_dir(&self) -> bool {
1349        self.replaced.map(|r| self.is_dir(&r.node)).unwrap_or(false)
1350    }
1351
1352    /// Returns the old parent directory entry handle.
1353    ///
1354    /// The old parent's child list is write-locked for the lifetime of the
1355    /// context.
1356    pub fn old_parent(&self) -> &DirEntryHandle {
1357        self.old_parent_guard.entry
1358    }
1359
1360    /// Returns the new parent directory entry handle.
1361    ///
1362    /// The new parent's child list is write-locked for the lifetime of the
1363    /// context.
1364    pub fn new_parent(&self) -> &DirEntryHandle {
1365        self.new_parent_guard.as_ref().map(|g| g.entry).unwrap_or(self.old_parent_guard.entry)
1366    }
1367
1368    /// Returns mutable references to the `FsNodeInfo` of both parent
1369    /// directories.
1370    ///
1371    /// The references are returned as a tuple to allow them to be borrowed
1372    /// mutably at the same time (e.g., to update link counts in both).
1373    ///
1374    /// If `new_parent` is the same as `old_parent`, the second element of the
1375    /// tuple will be `None` to prevent mutable aliasing of the same guard.
1376    pub fn parent_infos_mut(
1377        &mut self,
1378    ) -> (&mut crate::vfs::FsNodeInfo, Option<&mut crate::vfs::FsNodeInfo>) {
1379        let old = &mut *self.old_parent_info_guard;
1380        let new = self.new_parent_info_guard.as_mut().map(|g| &mut **g);
1381        (old, new)
1382    }
1383
1384    /// Returns a shared reference to the `FsNodeInfo` of the old parent
1385    /// directory.
1386    pub fn old_parent_info(&self) -> &crate::vfs::FsNodeInfo {
1387        &self.old_parent_info_guard
1388    }
1389    /// Returns a shared reference to the `FsNodeInfo` of the new parent
1390    /// directory.
1391    ///
1392    /// Returns `None` if `new_parent` is the same as `old_parent`.
1393    pub fn new_parent_info(&self) -> Option<&crate::vfs::FsNodeInfo> {
1394        self.new_parent_info_guard.as_deref()
1395    }
1396
1397    fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1398        self.new_parent_guard.as_mut().unwrap_or(&mut self.old_parent_guard)
1399    }
1400
1401    fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1402        &mut self.old_parent_guard
1403    }
1404
1405    fn is_dir(&self, node: &FsNodeHandle) -> bool {
1406        if Arc::ptr_eq(node, &self.old_parent().node) {
1407            self.old_parent_info_guard.mode.is_dir()
1408        } else if Arc::ptr_eq(node, &self.new_parent().node) {
1409            self.new_parent_info_guard
1410                .as_ref()
1411                .map(|g| g.mode.is_dir())
1412                .unwrap_or_else(|| self.old_parent_info_guard.mode.is_dir())
1413        } else {
1414            let _token = allow_subclass();
1415            node.is_dir()
1416        }
1417    }
1418}
1419
1420/// The Drop trait for DirEntry removes the entry from the child list of the
1421/// parent entry, which means we cannot drop DirEntry objects while holding a
1422/// lock on the parent's child list.
1423impl Drop for DirEntry {
1424    fn drop(&mut self) {
1425        let maybe_parent = self.parent();
1426        self.parent.update(None);
1427        if let Some(parent) = maybe_parent {
1428            parent.internal_remove_child(self);
1429        }
1430    }
1431}