1use crate::security;
6use crate::task::CurrentTask;
7use crate::vfs::{
8 CheckAccessReason, DirectoryMode, FileHandle, FileObject, FsLockDepType, FsNodeHandle,
9 FsNodeLinkBehavior, FsStr, FsString, LookupVec, MountInfo, Mounts, NamespaceNode, PathBuilder,
10 UnlinkKind, inotify_hook, path,
11};
12use atomic_bitflags::atomic_bitflags;
13use bitflags::bitflags;
14use bstr::ByteSlice;
15use fuchsia_rcu::{RcuArc, RcuReadScope};
16use fuchsia_sync::ResetDependencies;
17use fxfs_unicode::{CasefoldStr, utf8_bytes};
18use smallvec::SmallVec;
19use starnix_rcu::RcuString;
20use starnix_sync::{
21 DirEntryChildrenLevel, DirEntryChildrenRecursiveLevel, DynamicLockDepRwLock,
22 FuseDirEntryChildrenLevel, LockDepWriteGuard, allow_subclass,
23};
24use starnix_uapi::auth::FsCred;
25use starnix_uapi::errors::{ENOENT, Errno};
26use starnix_uapi::file_mode::{Access, FileMode};
27use starnix_uapi::inotify_mask::InotifyMask;
28use starnix_uapi::open_flags::OpenFlags;
29use starnix_uapi::{NAME_MAX, RENAME_EXCHANGE, RENAME_NOREPLACE, RENAME_WHITEOUT, error};
30use std::collections::BTreeMap;
31use std::fmt;
32use std::ops::Deref;
33use std::sync::atomic::Ordering;
34use std::sync::{Arc, Weak};
35
36bitflags! {
37 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
38 pub struct RenameFlags: u32 {
39 const EXCHANGE = RENAME_EXCHANGE;
41
42 const NOREPLACE = RENAME_NOREPLACE;
44
45 const WHITEOUT = RENAME_WHITEOUT;
47
48 const REPLACE_ANY = 1 << 31;
51
52 const INTERNAL = Self::REPLACE_ANY.bits();
54 }
55}
56
57pub trait DirEntryOps: Send + Sync + 'static {
58 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 const IS_DEAD = 1 << 0;
84
85 const HAS_MOUNTS = 1 << 1;
87 }
88}
89
90pub struct DefaultDirEntryOps;
91
92impl DirEntryOps for DefaultDirEntryOps {}
93
94pub struct DirEntry {
106 pub node: FsNodeHandle,
111
112 ops: Box<dyn DirEntryOps>,
117
118 parent: RcuArc<DirEntry>,
125
126 flags: AtomicDirEntryFlags,
128
129 local_name: RcuString,
138
139 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 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 pub fn new_unrooted(node: FsNodeHandle) -> DirEntryHandle {
205 Self::new_uncached(node, None, FsString::default())
206 }
207
208 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 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 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 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 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 pub fn parent(&self) -> Option<DirEntryHandle> {
283 self.parent.upgrade()
284 }
285
286 pub fn set_parent(&self, parent: DirEntryHandle) {
288 self.parent.update(Some(parent));
289 }
290
291 pub fn parent_or_self(self: &DirEntryHandle) -> DirEntryHandle {
301 self.parent().unwrap_or_else(|| self.clone())
302 }
303
304 pub fn local_name<'a>(&self, scope: &'a RcuReadScope) -> &'a FsStr {
308 self.local_name.read(scope)
309 }
310
311 pub fn is_reserved_name(name: &FsStr) -> bool {
316 name.is_empty() || name == "." || name == ".."
317 }
318
319 pub fn flags(&self) -> DirEntryFlags {
321 self.flags.load(Ordering::Acquire)
322 }
323
324 pub fn raise_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
328 self.flags.fetch_or(flags, Ordering::AcqRel)
329 }
330
331 pub fn lower_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
335 self.flags.fetch_and(!flags, Ordering::AcqRel)
336 }
337
338 pub fn is_dead(&self) -> bool {
340 self.flags().contains(DirEntryFlags::IS_DEAD)
341 }
342
343 pub fn component_lookup(
346 self: &DirEntryHandle,
347 current_task: &CurrentTask,
348 mount: &MountInfo,
349 name: &FsStr,
350 ) -> Result<DirEntryHandle, Errno> {
351 let (node, _) =
352 self.get_or_create_child(current_task, mount, name, |d, _mount, name| {
353 Ok((d.node.ops().lookup(d, current_task, name)?, CreationStatus::Existed))
354 })?;
355 Ok(node)
356 }
357
358 pub fn get_children_pipelined(
359 self: &DirEntryHandle,
360 current_task: &CurrentTask,
361 mount: &MountInfo,
362 names: &[&FsStr],
363 ) -> LookupVec<Result<DirEntryHandle, Errno>> {
364 let mut nodes = LookupVec::new();
365 let mut results = LookupVec::new();
366 let mut current_parent = self.clone();
367 for i in 0..names.len() {
368 let next_node = nodes.pop();
369 match current_parent.get_or_create_child(
370 current_task,
371 mount,
372 names[i],
373 |d, _mount, _name| {
374 let node = if let Some(node) = next_node {
375 node?
376 } else {
377 nodes = d.node.ops().lookup_pipelined(d, current_task, &names[i..]);
378 nodes.reverse();
379 nodes.pop().expect("lookup_pipelined must return at least one result")?
380 };
381 Ok((node, CreationStatus::Existed))
382 },
383 ) {
384 Ok((entry, _)) => {
385 results.push(Ok(entry.clone()));
386 current_parent = entry;
387 }
388 Err(e) => {
389 results.push(Err(e));
390 break;
391 }
392 }
393 }
394 results
395 }
396
397 pub fn create_entry(
405 self: &DirEntryHandle,
406 current_task: &CurrentTask,
407 mount: &MountInfo,
408 name: &FsStr,
409 create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
410 ) -> Result<DirEntryHandle, Errno> {
411 let (entry, status) =
412 self.create_entry_internal(current_task, mount, name, create_node_fn)?;
413 if status == CreationStatus::Existed {
414 return error!(EEXIST);
415 }
416 Ok(entry)
417 }
418
419 pub fn get_or_create_entry(
422 self: &DirEntryHandle,
423 current_task: &CurrentTask,
424 mount: &MountInfo,
425 name: &FsStr,
426 create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
427 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
428 self.create_entry_internal(current_task, mount, name, create_node_fn)
429 }
430
431 fn create_entry_internal(
432 self: &DirEntryHandle,
433 current_task: &CurrentTask,
434 mount: &MountInfo,
435 name: &FsStr,
436 create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
437 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
438 if DirEntry::is_reserved_name(name) {
439 return error!(EEXIST);
440 }
441 if name.len() > NAME_MAX as usize {
443 return error!(ENAMETOOLONG);
444 }
445 if name.contains(&path::SEPARATOR) {
446 return error!(EINVAL);
447 }
448 let (entry, status) =
449 self.get_or_create_child(current_task, mount, name, |d, mount, name| {
450 match d.node.ops().lookup(d, current_task, name) {
451 Ok(node) => Ok((node, CreationStatus::Existed)),
452 Err(e) if e == ENOENT => {
453 Ok((create_node_fn(&d.node, mount, name)?, CreationStatus::Created))
454 }
455 Err(e) => Err(e),
456 }
457 })?;
458 if status == CreationStatus::Created {
459 self.node.update_ctime_mtime();
461 entry.notify_creation();
462 }
463 Ok((entry, status))
464 }
465
466 #[cfg(test)]
469 pub fn create_dir(
470 self: &DirEntryHandle,
471 current_task: &CurrentTask,
472 name: &FsStr,
473 ) -> Result<DirEntryHandle, Errno> {
474 self.create_dir_for_testing(current_task, name)
475 }
476
477 pub fn create_dir_for_testing(
480 self: &DirEntryHandle,
481 current_task: &CurrentTask,
482 name: &FsStr,
483 ) -> Result<DirEntryHandle, Errno> {
484 self.create_entry(current_task, &MountInfo::detached(), name, |dir, mount, name| {
486 dir.create_node(
487 current_task,
488 mount,
489 name,
490 starnix_uapi::file_mode::mode!(IFDIR, 0o777),
491 starnix_uapi::device_id::DeviceId::NONE,
492 FsCred::root(),
493 )
494 })
495 }
496
497 pub fn create_tmpfile(
503 self: &DirEntryHandle,
504 current_task: &CurrentTask,
505 mount: &MountInfo,
506 mode: FileMode,
507 owner: FsCred,
508 flags: OpenFlags,
509 ) -> Result<DirEntryHandle, Errno> {
510 if !self.node.is_dir() {
512 return error!(ENOTDIR);
513 }
514 assert!(mode.is_reg());
515
516 let link_behavior = if flags.contains(OpenFlags::EXCL) {
524 FsNodeLinkBehavior::Disallowed
525 } else {
526 FsNodeLinkBehavior::Allowed
527 };
528
529 let node = self.node.create_tmpfile(current_task, mount, mode, owner, link_behavior)?;
530 let local_name = format!("#{}", node.ino).into();
531 Ok(DirEntry::new_deleted(node, Some(self.clone()), local_name))
532 }
533
534 pub fn unlink(
535 self: &DirEntryHandle,
536 current_task: &CurrentTask,
537 mount: &MountInfo,
538 name: &FsStr,
539 kind: UnlinkKind,
540 directory_mode: DirectoryMode,
541 ) -> Result<(), Errno> {
542 assert!(!DirEntry::is_reserved_name(name));
543
544 let child_to_unlink;
546
547 let mut self_children = self.lock_children();
548 child_to_unlink = self_children.component_lookup(current_task, mount, name)?;
549 child_to_unlink.require_no_mounts(mount)?;
550
551 if directory_mode == DirectoryMode::MustBeDirectory && !child_to_unlink.node.is_dir() {
558 return error!(ENOTDIR);
559 }
560
561 match kind {
562 UnlinkKind::Directory => {
563 if !child_to_unlink.node.is_dir() {
564 return error!(ENOTDIR);
565 }
566 }
567 UnlinkKind::NonDirectory => {
568 if child_to_unlink.node.is_dir() {
569 return error!(EISDIR);
570 }
571 }
572 }
573
574 self.node.unlink(current_task, mount, name, &child_to_unlink.node)?;
575 self_children.children.remove(name);
576
577 std::mem::drop(self_children);
578 child_to_unlink.destroy(¤t_task.kernel().mounts);
579
580 Ok(())
581 }
582
583 fn destroy(self: DirEntryHandle, mounts: &Mounts) {
587 let was_already_dead =
588 self.raise_flags(DirEntryFlags::IS_DEAD).contains(DirEntryFlags::IS_DEAD);
589 if was_already_dead {
590 return;
591 }
592 let unmount =
593 self.lower_flags(DirEntryFlags::HAS_MOUNTS).contains(DirEntryFlags::HAS_MOUNTS);
594 self.node.fs().will_destroy_dir_entry(&self);
595 if unmount {
596 mounts.unmount(&self);
597 }
598 self.notify_deletion();
599 }
600
601 pub fn is_descendant_of(self: &DirEntryHandle, other: &DirEntryHandle) -> bool {
603 if Arc::ptr_eq(self, other) {
604 return true;
605 }
606 let mut current = self.parent();
607 while let Some(entry) = current {
608 if Arc::ptr_eq(&entry, other) {
609 return true;
611 }
612 current = entry.parent();
613 }
614 false
616 }
617
618 pub fn rename(
623 current_task: &CurrentTask,
624 old_parent: &DirEntryHandle,
625 old_mount: &MountInfo,
626 old_basename: &FsStr,
627 new_parent: &DirEntryHandle,
628 new_mount: &MountInfo,
629 new_basename: &FsStr,
630 flags: RenameFlags,
631 ) -> Result<(), Errno> {
632 if old_mount != new_mount {
634 return error!(EXDEV);
635 }
636
637 let mount = old_mount;
639
640 if DirEntry::is_reserved_name(old_basename) || DirEntry::is_reserved_name(new_basename) {
643 if flags.contains(RenameFlags::NOREPLACE) {
644 return error!(EEXIST);
645 }
646 return error!(EBUSY);
647 }
648
649 if Arc::ptr_eq(&old_parent.node, &new_parent.node) && old_basename == new_basename {
652 if flags.contains(RenameFlags::NOREPLACE) {
653 return error!(EEXIST);
654 }
655 return Ok(());
656 }
657
658 old_parent.node.check_access(
660 current_task,
661 mount,
662 Access::WRITE,
663 CheckAccessReason::InternalPermissionChecks,
664 old_parent,
665 )?;
666 new_parent.node.check_access(
667 current_task,
668 mount,
669 Access::WRITE,
670 CheckAccessReason::InternalPermissionChecks,
671 new_parent,
672 )?;
673
674 let fs = old_parent.node.fs();
677
678 let renamed;
681 let mut maybe_replaced = None;
682
683 {
684 let _lock = fs.rename_mutex.lock();
694
695 let mut state = RenameGuard::lock(old_parent, new_parent);
700
701 renamed =
704 state.old_parent_children().component_lookup(current_task, mount, old_basename)?;
705
706 let lookup_replaced =
711 state.new_parent_children().component_lookup(current_task, mount, new_basename);
712
713 if let Ok(replaced) = &lookup_replaced {
722 if old_parent.is_descendant_of(replaced) {
723 if flags.contains(RenameFlags::EXCHANGE) {
724 return error!(EINVAL);
725 } else {
726 return error!(ENOTEMPTY);
727 }
728 }
729 }
730
731 let mut state =
734 state.lock_info(old_parent, new_parent, &renamed, lookup_replaced.as_ref().ok());
735
736 if new_parent.is_descendant_of(&renamed) {
739 return error!(EINVAL);
740 }
741
742 {
745 let _token = allow_subclass();
749 old_parent.node.check_sticky_bit(
750 current_task,
751 &renamed.node,
752 state.old_parent_info(),
753 )?;
754 }
755
756 renamed.require_no_mounts(mount)?;
761
762 match &lookup_replaced {
767 Ok(replaced) => {
768 let replaced = maybe_replaced.insert(replaced.clone());
770
771 if flags.contains(RenameFlags::NOREPLACE) {
772 return error!(EEXIST);
778 }
779
780 if Arc::ptr_eq(&renamed.node, &replaced.node) {
781 if !Arc::ptr_eq(&renamed, &replaced) {
782 return Ok(());
788 }
789
790 if flags.contains(RenameFlags::EXCHANGE) {
795 return Ok(());
796 }
797 }
798
799 if state.replaced_is_dir() {
804 replaced.require_no_mounts(mount)?;
809 }
810
811 if !flags.intersects(RenameFlags::EXCHANGE | RenameFlags::REPLACE_ANY) {
812 let renamed_is_dir = state.renamed_is_dir();
813 let replaced_is_dir = state.replaced_is_dir();
814 if renamed_is_dir && !replaced_is_dir {
815 return error!(ENOTDIR);
816 } else if !renamed_is_dir && replaced_is_dir {
817 return error!(EISDIR);
818 }
819 }
820 }
821 Err(errno) if *errno == ENOENT => {
823 if flags.contains(RenameFlags::EXCHANGE) {
824 return error!(ENOENT);
825 }
826 }
827 Err(e) => return Err(e.clone()),
829 }
830
831 security::check_fs_node_rename_access(
832 current_task,
833 &old_parent.node,
834 &renamed.node,
835 &new_parent.node,
836 maybe_replaced.as_ref().map(|dir_entry| dir_entry.node.deref().as_ref()),
837 old_basename,
838 new_basename,
839 )?;
840
841 if let Some(replaced) = maybe_replaced.as_ref() {
842 let _token = allow_subclass();
846 new_parent.node.check_sticky_bit(
847 current_task,
848 &replaced.node,
849 state.new_parent_info().unwrap_or_else(|| state.old_parent_info()),
850 )?;
851 }
852
853 if state.replaced.is_some_and(|r| Arc::ptr_eq(&renamed, r)) {
858 state.replaced = None;
859 }
860
861 if flags.contains(RenameFlags::EXCHANGE) {
862 fs.exchange(current_task, &mut state, old_basename, new_basename)?;
863 } else {
864 fs.rename(current_task, &mut state, old_basename, new_basename)?;
865 }
866
867 if flags.contains(RenameFlags::EXCHANGE) {
868 let replaced =
870 maybe_replaced.as_ref().expect("replaced expected with RENAME_EXCHANGE");
871 replaced.set_parent(old_parent.clone());
872 replaced.local_name.update(old_basename.to_owned());
873 state.old_parent_children().children.insert(old_basename, Arc::downgrade(replaced));
874 } else {
875 state.old_parent_children().children.remove(old_basename);
880 }
881
882 renamed.set_parent(new_parent.clone());
885 renamed.local_name.update(new_basename.to_owned());
886
887 state.new_parent_children().children.insert(new_basename, Arc::downgrade(&renamed));
891
892 unsafe {
901 if let Some(replaced) =
902 maybe_replaced.as_ref().filter(|_| flags.contains(RenameFlags::EXCHANGE))
903 {
904 replaced.children.reset_dependencies();
905 replaced.node.info_lock().reset_dependencies();
906 }
907 renamed.children.reset_dependencies();
908 renamed.node.info_lock().reset_dependencies();
909 old_parent.children.reset_dependencies();
910 old_parent.node.info_lock().reset_dependencies();
911 new_parent.children.reset_dependencies();
912 new_parent.node.info_lock().reset_dependencies();
913 }
914 };
915
916 fs.purge_old_entries();
917
918 if let Some(replaced) = maybe_replaced {
919 if !flags.contains(RenameFlags::EXCHANGE) && !Arc::ptr_eq(&renamed, &replaced) {
920 replaced.destroy(¤t_task.kernel().mounts);
921 }
922 }
923
924 renamed.node.update_ctime();
926
927 let mode = renamed.node.info().mode;
928 if let Some(hook) =
929 current_task.kernel().expando.peek::<Arc<dyn inotify_hook::NotifyHook>>()
930 {
931 let cookie = hook.get_next_cookie();
932 old_parent.node.notify(InotifyMask::MOVE_FROM, cookie, old_basename, mode, false);
933 new_parent.node.notify(InotifyMask::MOVE_TO, cookie, new_basename, mode, false);
934 renamed.node.notify(InotifyMask::MOVE_SELF, 0, Default::default(), mode, false);
935 }
936
937 Ok(())
938 }
939
940 pub(crate) fn get_children<F, T>(&self, callback: F) -> T
941 where
942 F: FnOnce(&DirEntryChildren) -> T,
943 {
944 let children = self.children.read();
945 callback(&children)
946 }
947
948 pub fn remove_child(&self, name: &FsStr, mounts: &Mounts) {
951 let mut children = self.children.write();
952 let child = children.remove(name).and_then(|weak| weak.upgrade());
953 if let Some(child) = child {
954 std::mem::drop(children);
955 child.destroy(mounts);
956 }
957 }
958
959 fn check_search_access(
962 &self,
963 current_task: &CurrentTask,
964 mount: &MountInfo,
965 name: &FsStr,
966 ) -> Result<(), Errno> {
967 assert!(!DirEntry::is_reserved_name(name));
968 if !self.node.is_dir() {
970 return error!(ENOTDIR);
971 }
972 self.node.check_access(
974 current_task,
975 mount,
976 Access::EXEC,
977 CheckAccessReason::InternalPermissionChecks,
978 self,
979 )
980 }
981
982 fn get_or_create_child(
989 self: &DirEntryHandle,
990 current_task: &CurrentTask,
991 mount: &MountInfo,
992 name: &FsStr,
993 create_fn: impl FnOnce(
994 &DirEntryHandle,
995 &MountInfo,
996 &FsStr,
997 ) -> Result<(FsNodeHandle, CreationStatus), Errno>,
998 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
999 self.check_search_access(current_task, mount, name)?;
1000
1001 let child = self.children.read().get(name).and_then(Weak::upgrade);
1007 if let Some(child) = child {
1008 if child.ops.revalidate(current_task, &child)? {
1009 if self.node.fail_if_locked(current_task, &self.node.info()).is_ok() {
1011 child.node.fs().did_access_dir_entry(&child);
1012 }
1013 return Ok((child, CreationStatus::Existed));
1014 }
1015 self.internal_remove_child(&child);
1017 child.destroy(¤t_task.kernel().mounts);
1018 }
1019
1020 let (child, status) =
1023 self.lock_children().get_or_create_child(current_task, mount, name, create_fn)?;
1024 child.node.fs().purge_old_entries();
1025 Ok((child, status))
1026 }
1027
1028 #[cfg(test)]
1036 pub fn copy_child_names(&self) -> Vec<FsString> {
1037 let scope = RcuReadScope::new();
1038 self.children.read().copy_child_names(&scope)
1039 }
1040
1041 fn internal_remove_child(&self, child: &DirEntry) {
1042 let mut children = self.children.write();
1043 let scope = RcuReadScope::new();
1044 let local_name = child.local_name.read(&scope);
1045 children.remove_if_child_matches(local_name, child);
1046 }
1047
1048 pub fn notify(&self, event_mask: InotifyMask) {
1050 self.notify_watchers(event_mask, self.is_dead());
1051 }
1052
1053 pub fn notify_ignoring_excl_unlink(&self, event_mask: InotifyMask) {
1057 self.notify_watchers(event_mask, false);
1059 }
1060
1061 fn notify_watchers(&self, event_mask: InotifyMask, is_dead: bool) {
1062 let mode = self.node.info().mode;
1063 {
1064 let scope = RcuReadScope::new();
1065 if let Some(parent) = self.parent() {
1066 let local_name = self.local_name.read(&scope);
1067 parent.node.notify(event_mask, 0, local_name, mode, is_dead);
1068 }
1069 }
1070 self.node.notify(event_mask, 0, Default::default(), mode, is_dead);
1071 }
1072
1073 fn notify_creation(&self) {
1075 let mode = self.node.info().mode;
1076 if Arc::strong_count(&self.node) > 1 {
1077 self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1079 }
1080 let scope = RcuReadScope::new();
1081 if let Some(parent) = self.parent() {
1082 let local_name = self.local_name.read(&scope);
1083 parent.node.notify(InotifyMask::CREATE, 0, local_name, mode, false);
1084 }
1085 }
1086
1087 fn notify_deletion(&self) {
1091 let mode = self.node.info().mode;
1092 if !mode.is_dir() {
1093 self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1095 }
1096
1097 if Arc::strong_count(&self.node) == 1 {
1100 self.node.notify(InotifyMask::DELETE_SELF, 0, Default::default(), mode, false);
1101 }
1102
1103 let scope = RcuReadScope::new();
1104 if let Some(parent) = self.parent() {
1105 let local_name = self.local_name.read(&scope);
1106 parent.node.notify(InotifyMask::DELETE, 0, local_name, mode, false);
1107 }
1108 }
1109
1110 pub fn has_mounts(&self) -> bool {
1112 self.flags().contains(DirEntryFlags::HAS_MOUNTS)
1113 }
1114
1115 pub fn set_has_mounts(&self, v: bool) {
1117 if v {
1118 self.raise_flags(DirEntryFlags::HAS_MOUNTS);
1119 } else {
1120 self.lower_flags(DirEntryFlags::HAS_MOUNTS);
1121 }
1122 }
1123
1124 fn require_no_mounts(self: &Arc<Self>, parent_mount: &MountInfo) -> Result<(), Errno> {
1126 if self.has_mounts() {
1127 if let Some(mount) = parent_mount.as_ref() {
1128 if mount.has_submount(self) {
1129 return error!(EBUSY);
1130 }
1131 }
1132 }
1133 Ok(())
1134 }
1135}
1136
1137#[inline]
1140fn canonicalize_name(name: impl AsRef<[u8]>) -> SmallVec<[u8; NAME_MAX as usize]> {
1141 let name = name.as_ref();
1142 match std::str::from_utf8(name) {
1143 Ok(valid) => {
1144 CasefoldStr::new(valid).casefold_normalized_chars().flat_map(utf8_bytes).collect()
1145 }
1146 Err(_) => SmallVec::from_slice(name),
1147 }
1148}
1149
1150#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
1155pub(crate) struct DirEntryChildKey(FsString);
1156
1157impl DirEntryChildKey {
1158 pub fn new(name: &FsStr, casefold: bool) -> Self {
1159 if casefold {
1160 Self(canonicalize_name(name).into_vec().into())
1161 } else {
1162 Self(name.to_owned())
1163 }
1164 }
1165}
1166
1167impl std::borrow::Borrow<FsStr> for DirEntryChildKey {
1168 fn borrow(&self) -> &FsStr {
1169 self.0.as_bstr()
1170 }
1171}
1172
1173#[derive(Default, Debug)]
1180pub(crate) struct DirEntryChildren {
1181 entries: BTreeMap<DirEntryChildKey, Weak<DirEntry>>,
1182 casefold: bool,
1183}
1184
1185impl std::ops::Deref for DirEntryChildren {
1186 type Target = BTreeMap<DirEntryChildKey, Weak<DirEntry>>;
1187
1188 fn deref(&self) -> &Self::Target {
1189 &self.entries
1190 }
1191}
1192
1193impl DirEntryChildren {
1194 pub fn new(casefold: bool) -> Self {
1195 Self { entries: BTreeMap::new(), casefold }
1196 }
1197
1198 pub fn is_casefold(&self) -> bool {
1199 self.casefold
1200 }
1201
1202 pub fn set_casefold(&mut self, casefold: bool) {
1203 self.entries.clear();
1204 self.casefold = casefold;
1205 }
1206
1207 pub fn get(&self, name: &FsStr) -> Option<&Weak<DirEntry>> {
1209 if self.casefold {
1210 let key = canonicalize_name(name);
1211 self.entries.get(FsStr::new(&key))
1212 } else {
1213 self.entries.get(name)
1214 }
1215 }
1216
1217 pub fn insert(&mut self, name: &FsStr, child: Weak<DirEntry>) -> Option<Weak<DirEntry>> {
1220 self.entries.insert(DirEntryChildKey::new(name, self.casefold), child)
1221 }
1222
1223 pub fn remove(&mut self, name: &FsStr) -> Option<Weak<DirEntry>> {
1226 if self.casefold {
1227 let key = canonicalize_name(name);
1228 self.entries.remove(FsStr::new(&key))
1229 } else {
1230 self.entries.remove(name)
1231 }
1232 }
1233
1234 pub fn remove_if_child_matches(&mut self, name: &FsStr, expected_child: &DirEntry) -> bool {
1237 if let Some(weak_child) = self.get(name) {
1238 if std::ptr::eq(weak_child.as_ptr(), expected_child) {
1239 let _ = self.remove(name);
1240 return true;
1241 }
1242 }
1243 false
1244 }
1245
1246 #[cfg(test)]
1247 fn copy_child_names(&self, scope: &RcuReadScope) -> Vec<FsString> {
1250 self.entries
1251 .values()
1252 .filter_map(|child| Weak::upgrade(child).map(|c| c.local_name.read(scope).to_owned()))
1253 .collect()
1254 }
1255}
1256
1257struct DirEntryLockedChildren<'a> {
1258 entry: &'a DirEntryHandle,
1259 children: LockDepWriteGuard<'a, DirEntryChildren>,
1260}
1261
1262#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1265pub enum CreationStatus {
1266 Created,
1268 Existed,
1271}
1272
1273impl<'a> DirEntryLockedChildren<'a> {
1274 fn component_lookup(
1275 &mut self,
1276 current_task: &CurrentTask,
1277 mount: &MountInfo,
1278 name: &FsStr,
1279 ) -> Result<DirEntryHandle, Errno> {
1280 self.entry.check_search_access(current_task, mount, name)?;
1281 let (node, _) =
1282 self.get_or_create_child(current_task, mount, name, |d, _mount, name| {
1283 Ok((d.node.ops().lookup(d, current_task, name)?, CreationStatus::Existed))
1284 })?;
1285 Ok(node)
1286 }
1287
1288 fn get_or_create_child(
1289 &mut self,
1290 current_task: &CurrentTask,
1291 mount: &MountInfo,
1292 name: &FsStr,
1293 create_fn: impl FnOnce(
1294 &DirEntryHandle,
1295 &MountInfo,
1296 &FsStr,
1297 ) -> Result<(FsNodeHandle, CreationStatus), Errno>,
1298 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
1299 if let Some(child) = self.children.get(name).and_then(Weak::upgrade) {
1300 if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1302 child.node.fs().did_access_dir_entry(&child);
1303 }
1304 return Ok((child, CreationStatus::Existed));
1305 }
1306
1307 let (node, status) = create_fn(self.entry, mount, name)?;
1308
1309 assert!(
1310 node.info().mode & FileMode::IFMT != FileMode::EMPTY,
1311 "FsNode initialization did not populate the FileMode in FsNodeInfo."
1312 );
1313
1314 let child = DirEntry::new(node, Some(self.entry.clone()), name.to_owned());
1315
1316 if let Err(err) = security::fs_node_init_with_dentry(current_task, &child) {
1317 child.parent.update(None);
1320 return Err(err);
1321 }
1322
1323 if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1325 self.children.insert(name, Arc::downgrade(&child));
1326 }
1327
1328 Ok((child, status))
1329 }
1330}
1331
1332impl fmt::Debug for DirEntry {
1333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1334 let scope = RcuReadScope::new();
1335 let mut path = PathBuilder::new();
1336 let mut parent_holder;
1337 let mut current = self;
1338 loop {
1339 let name = current.local_name(&scope);
1341 if !name.is_empty() {
1342 path.prepend_element(name);
1343 }
1344 let Some(parent) = current.parent() else {
1345 break;
1346 };
1347 parent_holder = parent;
1348 current = &parent_holder;
1349 }
1350 f.debug_struct("DirEntry")
1351 .field("fs", &self.node.fs().name())
1352 .field("path", &path.build_absolute())
1353 .finish()
1354 }
1355}
1356
1357#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1358enum RenameRelationship {
1359 Same,
1360 NewIsDescendant,
1361 OldIsDescendant,
1362 Independent,
1363}
1364
1365struct RenameGuard<'a> {
1366 old_parent_guard: DirEntryLockedChildren<'a>,
1367 new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1368 relationship: RenameRelationship,
1369}
1370
1371impl<'a> RenameGuard<'a> {
1372 fn lock(old_parent: &'a DirEntryHandle, new_parent: &'a DirEntryHandle) -> Self {
1373 if Arc::ptr_eq(old_parent, new_parent) {
1374 let old_parent_guard = old_parent.lock_children();
1375 Self {
1376 old_parent_guard,
1377 new_parent_guard: None,
1378 relationship: RenameRelationship::Same,
1379 }
1380 } else if new_parent.is_descendant_of(old_parent) {
1381 let old_parent_guard = old_parent.lock_children();
1382 let _token = allow_subclass();
1383 let new_parent_guard = new_parent.lock_children();
1384 Self {
1385 old_parent_guard,
1386 new_parent_guard: Some(new_parent_guard),
1387 relationship: RenameRelationship::NewIsDescendant,
1388 }
1389 } else if old_parent.is_descendant_of(new_parent) {
1390 let new_parent_guard = new_parent.lock_children();
1391 let _token = allow_subclass();
1392 let old_parent_guard = old_parent.lock_children();
1393 Self {
1394 old_parent_guard,
1395 new_parent_guard: Some(new_parent_guard),
1396 relationship: RenameRelationship::OldIsDescendant,
1397 }
1398 } else {
1399 let (g1, g2) =
1401 starnix_sync::ordered_write_lock(&old_parent.children, &new_parent.children);
1402 let old_parent_guard = DirEntryLockedChildren { entry: old_parent, children: g1 };
1403 let new_parent_guard = DirEntryLockedChildren { entry: new_parent, children: g2 };
1404 Self {
1405 old_parent_guard,
1406 new_parent_guard: Some(new_parent_guard),
1407 relationship: RenameRelationship::Independent,
1408 }
1409 }
1410 }
1411
1412 fn lock_info(
1416 self,
1417 old_parent: &'a DirEntryHandle,
1418 new_parent: &'a DirEntryHandle,
1419 renamed: &'a DirEntryHandle,
1420 replaced: Option<&'a DirEntryHandle>,
1421 ) -> RenameContext<'a> {
1422 let (g1, g2) = match self.relationship {
1423 RenameRelationship::Same => (old_parent.node.info_lock().write(), None),
1424 RenameRelationship::NewIsDescendant => {
1425 let g1 = old_parent.node.info_lock().write();
1426 let _token = allow_subclass();
1427 let g2 = new_parent.node.info_lock().write();
1428 (g1, Some(g2))
1429 }
1430 RenameRelationship::OldIsDescendant => {
1431 let g2 = new_parent.node.info_lock().write();
1432 let _token = allow_subclass();
1433 let g1 = old_parent.node.info_lock().write();
1434 (g1, Some(g2))
1435 }
1436 RenameRelationship::Independent => {
1437 let (g1, g2) = starnix_sync::ordered_write_lock(
1438 old_parent.node.info_lock(),
1439 new_parent.node.info_lock(),
1440 );
1441 (g1, Some(g2))
1442 }
1443 };
1444
1445 RenameContext {
1446 renamed,
1447 replaced,
1448 old_parent_guard: self.old_parent_guard,
1449 new_parent_guard: self.new_parent_guard,
1450 old_parent_info_guard: g1,
1451 new_parent_info_guard: g2,
1452 }
1453 }
1454
1455 fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1456 &mut self.old_parent_guard
1457 }
1458
1459 fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1460 if let Some(new_guard) = self.new_parent_guard.as_mut() {
1461 new_guard
1462 } else {
1463 &mut self.old_parent_guard
1464 }
1465 }
1466}
1467
1468pub struct RenameContext<'a> {
1481 pub renamed: &'a DirEntryHandle,
1482 pub replaced: Option<&'a DirEntryHandle>,
1483 old_parent_guard: DirEntryLockedChildren<'a>,
1484 new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1485 old_parent_info_guard: LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>,
1486 new_parent_info_guard: Option<LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>>,
1487}
1488
1489impl<'a> RenameContext<'a> {
1490 pub fn renamed_is_dir(&self) -> bool {
1497 self.is_dir(&self.renamed.node)
1498 }
1499
1500 pub fn replaced_is_dir(&self) -> bool {
1509 self.replaced.map(|r| self.is_dir(&r.node)).unwrap_or(false)
1510 }
1511
1512 pub fn old_parent(&self) -> &DirEntryHandle {
1517 self.old_parent_guard.entry
1518 }
1519
1520 pub fn new_parent(&self) -> &DirEntryHandle {
1525 self.new_parent_guard.as_ref().map(|g| g.entry).unwrap_or(self.old_parent_guard.entry)
1526 }
1527
1528 pub fn parent_infos_mut(
1537 &mut self,
1538 ) -> (&mut crate::vfs::FsNodeInfo, Option<&mut crate::vfs::FsNodeInfo>) {
1539 let old = &mut *self.old_parent_info_guard;
1540 let new = self.new_parent_info_guard.as_mut().map(|g| &mut **g);
1541 (old, new)
1542 }
1543
1544 pub fn old_parent_info(&self) -> &crate::vfs::FsNodeInfo {
1547 &self.old_parent_info_guard
1548 }
1549 pub fn new_parent_info(&self) -> Option<&crate::vfs::FsNodeInfo> {
1554 self.new_parent_info_guard.as_deref()
1555 }
1556
1557 fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1558 self.new_parent_guard.as_mut().unwrap_or(&mut self.old_parent_guard)
1559 }
1560
1561 fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1562 &mut self.old_parent_guard
1563 }
1564
1565 fn is_dir(&self, node: &FsNodeHandle) -> bool {
1566 if Arc::ptr_eq(node, &self.old_parent().node) {
1567 self.old_parent_info_guard.mode.is_dir()
1568 } else if Arc::ptr_eq(node, &self.new_parent().node) {
1569 self.new_parent_info_guard
1570 .as_ref()
1571 .map(|g| g.mode.is_dir())
1572 .unwrap_or_else(|| self.old_parent_info_guard.mode.is_dir())
1573 } else {
1574 let _token = allow_subclass();
1575 node.is_dir()
1576 }
1577 }
1578}
1579
1580impl Drop for DirEntry {
1584 fn drop(&mut self) {
1585 let maybe_parent = self.parent();
1586 self.parent.update(None);
1587 if let Some(parent) = maybe_parent {
1588 parent.internal_remove_child(self);
1589 }
1590 }
1591}
1592
1593#[cfg(test)]
1594mod tests {
1595 use super::*;
1596 use crate::testing::{PanickingFile, spawn_kernel_and_run};
1597 use crate::vfs::pseudo::simple_directory::SimpleDirectory;
1598 use crate::vfs::{
1599 CacheConfig, CacheMode, FileOps, FileSystem, FileSystemHandle, FileSystemOps,
1600 FileSystemOptions, FsNode, FsNodeInfo, FsNodeOps, MemoryDirectoryFile, Namespace,
1601 SymlinkNode, WhatToMount, fs_node_impl_not_dir,
1602 };
1603 use starnix_sync::Mutex;
1604 use starnix_types::vfs::default_statfs;
1605 use starnix_uapi::device_id::DeviceId;
1606 use starnix_uapi::file_mode::mode;
1607 use starnix_uapi::mount_flags::MountpointFlags;
1608 use starnix_uapi::vfs::ResolveFlags;
1609 use starnix_uapi::{errno, statfs};
1610 use std::sync::atomic::AtomicUsize;
1611
1612 #[fuchsia::test]
1613 async fn test_dir_entry_debug() {
1614 spawn_kernel_and_run(async |current_task| {
1615 let fs = current_task.fs().root().entry.node.fs();
1616 let check = |parent: Option<&DirEntryHandle>, name: &str, expected_path: &str| {
1617 let node = SimpleDirectory::new().into_node(&fs, 0o755);
1618 let entry = DirEntry::new(node, parent.cloned(), name.into());
1619 assert_eq!(
1620 format!("{entry:?}"),
1621 format!("DirEntry {{ fs: {:?}, path: {expected_path:?} }}", fs.name())
1622 );
1623 entry
1624 };
1625
1626 let root = check(None, "", "/");
1627 let bus = check(Some(&root), "bus", "/bus");
1628 check(Some(&bus), "pci", "/bus/pci");
1629 check(None, "[eventfd]", "/[eventfd]");
1630 })
1631 .await;
1632 }
1633
1634 #[test]
1635 fn test_canonicalize_name() {
1636 let upper = canonicalize_name("FooBar.TXT");
1637 let lower = canonicalize_name("foobar.txt");
1638 let mixed = canonicalize_name("FOOBAR.TXT");
1639 let diff = canonicalize_name("other.txt");
1640
1641 assert_eq!(upper.as_slice(), lower.as_slice());
1642 assert_eq!(upper.as_slice(), mixed.as_slice());
1643 assert_ne!(upper.as_slice(), diff.as_slice());
1644
1645 let composed = canonicalize_name("\u{03AA}");
1647 let decomposed = canonicalize_name("\u{0399}\u{0308}");
1648 assert_eq!(composed.as_slice(), decomposed.as_slice());
1649
1650 let e_accent_lower = canonicalize_name("e\u{0301}");
1652 let e_accent_upper = canonicalize_name("\u{00c9}");
1653 assert_eq!(e_accent_lower.as_slice(), e_accent_upper.as_slice());
1654
1655 let strasse_lower = canonicalize_name("straße");
1657 let strasse_upper = canonicalize_name("STRASSE");
1658 assert_eq!(strasse_lower.as_slice(), strasse_upper.as_slice());
1659
1660 let non_utf8 = canonicalize_name(b"foo\x80bar");
1662 assert_eq!(non_utf8.as_slice(), b"foo\x80bar");
1663 }
1664
1665 #[test]
1666 fn test_dir_entry_children_casefold() {
1667 let mut children = DirEntryChildren::new(true);
1668 assert!(children.is_casefold());
1669
1670 children.insert("FooBar.TXT".into(), Weak::new());
1672 assert!(children.get("foobar.txt".into()).is_some());
1673 assert!(children.get("FOOBAR.TXT".into()).is_some());
1674 assert!(children.get("FooBar.TXT".into()).is_some());
1675 assert!(children.get("other.txt".into()).is_none());
1676
1677 children.insert("\u{03AA}".into(), Weak::new());
1679 assert!(children.get("\u{0399}\u{0308}".into()).is_some());
1680
1681 children.insert(b"foo\x80bar".into(), Weak::new());
1683 assert!(children.get(b"foo\x80bar".into()).is_some());
1684 assert!(children.get(b"FOO\x80bar".into()).is_none());
1685
1686 assert!(children.remove("FOOBAR.TXT".into()).is_some());
1688 assert!(children.get("foobar.txt".into()).is_none());
1689 assert!(children.remove("\u{0399}\u{0308}".into()).is_some());
1690 assert!(children.get("\u{03AA}".into()).is_none());
1691 assert!(children.remove(b"foo\x80bar".into()).is_some());
1692 assert!(children.get(b"foo\x80bar".into()).is_none());
1693 assert!(children.is_empty());
1694
1695 children.insert("Foo.TXT".into(), Weak::new());
1697 assert!(!children.is_empty());
1698 children.set_casefold(false);
1699 assert!(!children.is_casefold());
1700 assert!(children.is_empty());
1701 children.set_casefold(true);
1702 assert!(children.is_casefold());
1703 }
1704
1705 #[derive(Default)]
1706 struct CountingTestFs {
1707 existing_nodes: Mutex<BTreeMap<FsString, FsNodeHandle>>,
1710
1711 revalidate_count: AtomicUsize,
1713 }
1714
1715 impl FileSystemOps for CountingTestFs {
1716 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
1717 Ok(default_statfs(0))
1718 }
1719
1720 fn name(&self) -> &'static FsStr {
1721 "counting_test_fs".into()
1722 }
1723 }
1724
1725 fn counting_fs(fs: &FileSystem) -> &CountingTestFs {
1727 fs.downcast_ops::<CountingTestFs>().expect("not a CountingTestFs")
1728 }
1729
1730 struct CountingDirNode {
1731 lookup_count: Arc<AtomicUsize>,
1732 }
1733
1734 impl FsNodeOps for CountingDirNode {
1735 fn create_file_ops(
1736 &self,
1737 _node: &FsNode,
1738 _current_task: &CurrentTask,
1739 _flags: OpenFlags,
1740 ) -> Result<Box<dyn FileOps>, Errno> {
1741 Ok(Box::new(MemoryDirectoryFile::new()))
1742 }
1743
1744 fn lookup(
1745 &self,
1746 entry: &DirEntry,
1747 _current_task: &CurrentTask,
1748 name: &FsStr,
1749 ) -> Result<FsNodeHandle, Errno> {
1750 self.lookup_count.fetch_add(1, Ordering::SeqCst);
1751 let fs = entry.node.fs();
1752 let node = counting_fs(&fs).existing_nodes.lock().get(name).cloned();
1753 node.ok_or_else(|| errno!(ENOENT))
1754 }
1755
1756 fn mknod(
1757 &self,
1758 node: &FsNode,
1759 _current_task: &CurrentTask,
1760 _name: &FsStr,
1761 mode: FileMode,
1762 dev: DeviceId,
1763 owner: FsCred,
1764 ) -> Result<FsNodeHandle, Errno> {
1765 let mut info = FsNodeInfo::new(mode, owner);
1766 info.rdev = dev;
1767 Ok(node.fs().create_node_and_allocate_node_id(CountingFileNode, info))
1768 }
1769
1770 fn mkdir(
1771 &self,
1772 node: &FsNode,
1773 _current_task: &CurrentTask,
1774 _name: &FsStr,
1775 mode: FileMode,
1776 owner: FsCred,
1777 ) -> Result<FsNodeHandle, Errno> {
1778 Ok(node.fs().create_node_and_allocate_node_id(
1779 CountingDirNode { lookup_count: self.lookup_count.clone() },
1780 FsNodeInfo::new(mode, owner),
1781 ))
1782 }
1783
1784 fn unlink(
1785 &self,
1786 _node: &FsNode,
1787 _current_task: &CurrentTask,
1788 _name: &FsStr,
1789 _child: &FsNodeHandle,
1790 ) -> Result<(), Errno> {
1791 Ok(())
1792 }
1793
1794 fn create_symlink(
1795 &self,
1796 _node: &FsNode,
1797 _current_task: &CurrentTask,
1798 _name: &FsStr,
1799 _target: &FsStr,
1800 _owner: FsCred,
1801 ) -> Result<FsNodeHandle, Errno> {
1802 error!(ENOTSUP)
1803 }
1804 }
1805
1806 struct CountingFileNode;
1807 impl FsNodeOps for CountingFileNode {
1808 fs_node_impl_not_dir!();
1809
1810 fn create_file_ops(
1811 &self,
1812 _node: &FsNode,
1813 _current_task: &CurrentTask,
1814 _flags: OpenFlags,
1815 ) -> Result<Box<dyn FileOps>, Errno> {
1816 Ok(Box::new(PanickingFile))
1817 }
1818
1819 fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
1820 Box::new(CountingDirEntryOps)
1821 }
1822 }
1823
1824 struct CountingDirEntryOps;
1825 impl DirEntryOps for CountingDirEntryOps {
1826 fn revalidate(&self, _current_task: &CurrentTask, entry: &DirEntry) -> Result<bool, Errno> {
1827 counting_fs(&entry.node.fs()).revalidate_count.fetch_add(1, Ordering::SeqCst);
1828 Ok(true)
1829 }
1830 }
1831
1832 fn create_counting_fs(current_task: &CurrentTask) -> (FileSystemHandle, Arc<AtomicUsize>) {
1833 let lookup_count = Arc::new(AtomicUsize::new(0));
1834 let fs = FileSystem::new(
1835 current_task.kernel(),
1836 CacheMode::Cached(CacheConfig { capacity: 16 }),
1837 CountingTestFs::default(),
1838 FileSystemOptions::default(),
1839 )
1840 .expect("create filesystem");
1841 let root_ino = fs.allocate_ino();
1842 fs.create_root(root_ino, CountingDirNode { lookup_count: lookup_count.clone() });
1843 (fs, lookup_count)
1844 }
1845
1846 #[fuchsia::test]
1847 async fn test_open_create_looks_up_once() {
1848 spawn_kernel_and_run(async |current_task| {
1849 let (fs, lookup_count) = create_counting_fs(¤t_task);
1850 let dir = Namespace::new(fs.clone()).root();
1851 let open = |name: &str, flags: OpenFlags| {
1852 current_task.open_namespace_node_at(
1853 dir.clone(),
1854 name.into(),
1855 flags,
1856 mode!(IFREG, 0o644),
1857 ResolveFlags::empty(),
1858 )
1859 };
1860
1861 let created = open("file", OpenFlags::CREAT | OpenFlags::RDWR).expect("create file");
1863 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1864
1865 let opened = open("file", OpenFlags::CREAT | OpenFlags::RDWR).expect("open file");
1868 assert!(Arc::ptr_eq(&created.name.entry, &opened.name.entry));
1869 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1870 assert_eq!(counting_fs(&fs).revalidate_count.load(Ordering::SeqCst), 1);
1871
1872 assert_eq!(
1874 open("file", OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR).unwrap_err(),
1875 errno!(EEXIST)
1876 );
1877 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1878
1879 open("excl", OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR)
1881 .expect("create excl");
1882 assert_eq!(lookup_count.load(Ordering::SeqCst), 2);
1883 })
1884 .await;
1885 }
1886
1887 #[fuchsia::test]
1888 async fn test_open_create_existing() {
1889 spawn_kernel_and_run(async |current_task| {
1890 let (fs, lookup_count) = create_counting_fs(¤t_task);
1891 let ns = Namespace::new(fs.clone());
1892 let dir = ns.root();
1893 let open = |name: &str, resolve_flags: ResolveFlags| {
1894 current_task.open_namespace_node_at(
1895 dir.clone(),
1896 name.into(),
1897 OpenFlags::CREAT | OpenFlags::RDWR,
1898 mode!(IFREG, 0o644),
1899 resolve_flags,
1900 )
1901 };
1902 let add_existing_node = |name: &str, node: FsNodeHandle| {
1903 counting_fs(&fs).existing_nodes.lock().insert(name.into(), node);
1904 };
1905
1906 let file = fs.create_node_and_allocate_node_id(
1909 CountingFileNode,
1910 FsNodeInfo::new(mode!(IFREG, 0o644), FsCred::root()),
1911 );
1912 add_existing_node("file", file.clone());
1913 let opened = open("file", ResolveFlags::empty()).expect("open file");
1914 assert!(Arc::ptr_eq(&opened.name.entry.node, &file));
1915 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1916 assert_eq!(counting_fs(&fs).revalidate_count.load(Ordering::SeqCst), 0);
1917
1918 let (link, info) = SymlinkNode::new("file".into(), FsCred::root());
1920 add_existing_node("link", fs.create_node_and_allocate_node_id(link, info));
1921 let via_link = open("link", ResolveFlags::empty()).expect("open link");
1922 assert!(Arc::ptr_eq(&via_link.name.entry, &opened.name.entry));
1923 assert_eq!(lookup_count.load(Ordering::SeqCst), 2);
1924
1925 let mounted = open("mounted", ResolveFlags::empty()).expect("create mounted");
1928 opened
1929 .name
1930 .mount(WhatToMount::Bind(mounted.name.to_passive()), MountpointFlags::empty())
1931 .expect("bind mount");
1932 let over_mount = open("file", ResolveFlags::empty()).expect("open mount point");
1933 assert!(Arc::ptr_eq(&over_mount.name.entry, &mounted.name.entry));
1934 assert_eq!(open("file", ResolveFlags::NO_XDEV).unwrap_err(), errno!(EXDEV));
1935
1936 let open_excl = |name: &str, resolve_flags: ResolveFlags| {
1940 current_task.open_namespace_node_at(
1941 dir.clone(),
1942 name.into(),
1943 OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR,
1944 mode!(IFREG, 0o644),
1945 resolve_flags,
1946 )
1947 };
1948 assert_eq!(open_excl("file", ResolveFlags::empty()).unwrap_err(), errno!(EEXIST));
1949 assert_eq!(open_excl("file", ResolveFlags::NO_XDEV).unwrap_err(), errno!(EXDEV));
1950 })
1951 .await;
1952 }
1953}