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 = parent
161 .as_ref()
162 .and_then(|parent| parent.node.create_child_dir_entry_ops())
163 .unwrap_or_else(|| node.create_dir_entry_ops());
164 let fs_lockdep_type = node.fs().fs_lockdep_type();
165 let casefold = node.info().casefold;
166 let initial_children = DirEntryChildren::new(casefold);
167 let result = Arc::new(DirEntry {
168 node,
169 ops,
170 parent: RcuArc::new(parent),
171 flags: Default::default(),
172 local_name: local_name.into(),
173 children: match fs_lockdep_type {
174 FsLockDepType::Normal => {
175 DynamicLockDepRwLock::new::<DirEntryChildrenLevel>(initial_children)
176 }
177 FsLockDepType::Recursive => {
178 DynamicLockDepRwLock::new::<DirEntryChildrenRecursiveLevel>(initial_children)
179 }
180 FsLockDepType::Fuse => {
181 DynamicLockDepRwLock::new::<FuseDirEntryChildrenLevel>(initial_children)
182 }
183 },
184 });
185 #[cfg(any(test, debug_assertions))]
186 {
187 let _token = allow_subclass();
190 let _l1 = result.children.read();
191 }
192 result
193 }
194
195 pub fn new(
196 node: FsNodeHandle,
197 parent: Option<DirEntryHandle>,
198 local_name: FsString,
199 ) -> DirEntryHandle {
200 let result = Self::new_uncached(node, parent, local_name);
201 result.node.fs().did_create_dir_entry(&result);
202 result
203 }
204
205 pub fn new_unrooted(node: FsNodeHandle) -> DirEntryHandle {
208 Self::new_uncached(node, None, FsString::default())
209 }
210
211 pub fn new_deleted(
213 node: FsNodeHandle,
214 parent: Option<DirEntryHandle>,
215 local_name: FsString,
216 ) -> DirEntryHandle {
217 let entry = DirEntry::new_uncached(node, parent, local_name);
218 entry.raise_flags(DirEntryFlags::IS_DEAD);
219 entry
220 }
221
222 pub fn open_anonymous(
224 self: &DirEntryHandle,
225 current_task: &CurrentTask,
226 flags: OpenFlags,
227 ) -> Result<FileHandle, Errno> {
228 let ops = self.node.create_file_ops(current_task, flags)?;
229 FileObject::new(current_task, ops, NamespaceNode::new_anonymous(self.clone()), flags)
230 }
231
232 fn lock_children<'a>(self: &'a DirEntryHandle) -> DirEntryLockedChildren<'a> {
233 DirEntryLockedChildren { entry: self, children: self.children.write() }
234 }
235
236 pub fn set_casefold(&self, current_task: &CurrentTask, casefold: bool) -> Result<(), Errno> {
245 if self.node.info().casefold == casefold {
246 return Ok(());
247 }
248 if !self.node.is_dir() {
249 return error!(ENOTDIR);
250 }
251 if casefold && !self.node.fs().has_casefold_support() {
252 return error!(ENOTSUP);
253 }
254
255 let mut children = self.children.write();
260 if children.is_casefold() == casefold {
261 return Ok(());
262 }
263
264 self.node.update_attributes(current_task, |info| {
265 info.casefold = casefold;
266 Ok(())
267 })?;
268
269 children.set_casefold(casefold);
270 Ok(())
271 }
272
273 pub fn parent(&self) -> Option<DirEntryHandle> {
275 self.parent.upgrade()
276 }
277
278 pub fn set_parent(&self, parent: DirEntryHandle) {
280 self.parent.update(Some(parent));
281 }
282
283 pub fn parent_or_self(self: &DirEntryHandle) -> DirEntryHandle {
293 self.parent().unwrap_or_else(|| self.clone())
294 }
295
296 pub fn local_name<'a>(&self, scope: &'a RcuReadScope) -> &'a FsStr {
300 self.local_name.read(scope)
301 }
302
303 pub fn is_reserved_name(name: &FsStr) -> bool {
308 name.is_empty() || name == "." || name == ".."
309 }
310
311 pub fn flags(&self) -> DirEntryFlags {
313 self.flags.load(Ordering::Acquire)
314 }
315
316 pub fn raise_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
320 self.flags.fetch_or(flags, Ordering::AcqRel)
321 }
322
323 pub fn lower_flags(&self, flags: DirEntryFlags) -> DirEntryFlags {
327 self.flags.fetch_and(!flags, Ordering::AcqRel)
328 }
329
330 pub fn is_dead(&self) -> bool {
332 self.flags().contains(DirEntryFlags::IS_DEAD)
333 }
334
335 pub fn component_lookup(
338 self: &DirEntryHandle,
339 current_task: &CurrentTask,
340 mount: &MountInfo,
341 name: &FsStr,
342 ) -> Result<DirEntryHandle, Errno> {
343 let (node, _) =
344 self.get_or_create_child(current_task, mount, name, |d, _mount, name| {
345 Ok((d.node.ops().lookup(d, current_task, name)?, CreationStatus::Existed))
346 })?;
347 Ok(node)
348 }
349
350 pub fn get_children_pipelined(
351 self: &DirEntryHandle,
352 current_task: &CurrentTask,
353 mount: &MountInfo,
354 names: &[&FsStr],
355 ) -> LookupVec<Result<DirEntryHandle, Errno>> {
356 let mut nodes = LookupVec::new();
357 let mut results = LookupVec::new();
358 let mut current_parent = self.clone();
359 for i in 0..names.len() {
360 let next_node = nodes.pop();
361 match current_parent.get_or_create_child(
362 current_task,
363 mount,
364 names[i],
365 |d, _mount, _name| {
366 let node = if let Some(node) = next_node {
367 node?
368 } else {
369 nodes = d.node.ops().lookup_pipelined(d, current_task, &names[i..]);
370 nodes.reverse();
371 nodes.pop().expect("lookup_pipelined must return at least one result")?
372 };
373 Ok((node, CreationStatus::Existed))
374 },
375 ) {
376 Ok((entry, _)) => {
377 results.push(Ok(entry.clone()));
378 current_parent = entry;
379 }
380 Err(e) => {
381 results.push(Err(e));
382 break;
383 }
384 }
385 }
386 results
387 }
388
389 pub fn create_entry(
397 self: &DirEntryHandle,
398 current_task: &CurrentTask,
399 mount: &MountInfo,
400 name: &FsStr,
401 create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
402 ) -> Result<DirEntryHandle, Errno> {
403 let (entry, status) =
404 self.create_entry_internal(current_task, mount, name, create_node_fn)?;
405 if status == CreationStatus::Existed {
406 return error!(EEXIST);
407 }
408 Ok(entry)
409 }
410
411 pub fn get_or_create_entry(
414 self: &DirEntryHandle,
415 current_task: &CurrentTask,
416 mount: &MountInfo,
417 name: &FsStr,
418 create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
419 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
420 self.create_entry_internal(current_task, mount, name, create_node_fn)
421 }
422
423 fn create_entry_internal(
424 self: &DirEntryHandle,
425 current_task: &CurrentTask,
426 mount: &MountInfo,
427 name: &FsStr,
428 create_node_fn: impl FnOnce(&FsNodeHandle, &MountInfo, &FsStr) -> Result<FsNodeHandle, Errno>,
429 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
430 if DirEntry::is_reserved_name(name) {
431 return error!(EEXIST);
432 }
433 if name.len() > NAME_MAX as usize {
435 return error!(ENAMETOOLONG);
436 }
437 if name.contains(&path::SEPARATOR) {
438 return error!(EINVAL);
439 }
440 let (entry, status) =
441 self.get_or_create_child(current_task, mount, name, |d, mount, name| {
442 match d.node.ops().lookup(d, current_task, name) {
443 Ok(node) => Ok((node, CreationStatus::Existed)),
444 Err(e) if e == ENOENT => {
445 Ok((create_node_fn(&d.node, mount, name)?, CreationStatus::Created))
446 }
447 Err(e) => Err(e),
448 }
449 })?;
450 if status == CreationStatus::Created {
451 self.node.update_ctime_mtime();
453 entry.notify_creation();
454 }
455 Ok((entry, status))
456 }
457
458 #[cfg(test)]
461 pub fn create_dir(
462 self: &DirEntryHandle,
463 current_task: &CurrentTask,
464 name: &FsStr,
465 ) -> Result<DirEntryHandle, Errno> {
466 self.create_dir_for_testing(current_task, name)
467 }
468
469 pub fn create_dir_for_testing(
472 self: &DirEntryHandle,
473 current_task: &CurrentTask,
474 name: &FsStr,
475 ) -> Result<DirEntryHandle, Errno> {
476 self.create_entry(current_task, &MountInfo::detached(), name, |dir, mount, name| {
478 dir.create_node(
479 current_task,
480 mount,
481 name,
482 starnix_uapi::file_mode::mode!(IFDIR, 0o777),
483 starnix_uapi::device_id::DeviceId::NONE,
484 FsCred::root(),
485 )
486 })
487 }
488
489 pub fn create_tmpfile(
495 self: &DirEntryHandle,
496 current_task: &CurrentTask,
497 mount: &MountInfo,
498 mode: FileMode,
499 owner: FsCred,
500 flags: OpenFlags,
501 ) -> Result<DirEntryHandle, Errno> {
502 if !self.node.is_dir() {
504 return error!(ENOTDIR);
505 }
506 assert!(mode.is_reg());
507
508 let link_behavior = if flags.contains(OpenFlags::EXCL) {
516 FsNodeLinkBehavior::Disallowed
517 } else {
518 FsNodeLinkBehavior::Allowed
519 };
520
521 let node = self.node.create_tmpfile(current_task, mount, mode, owner, link_behavior)?;
522 let local_name = format!("#{}", node.ino).into();
523 Ok(DirEntry::new_deleted(node, Some(self.clone()), local_name))
524 }
525
526 pub fn unlink(
527 self: &DirEntryHandle,
528 current_task: &CurrentTask,
529 mount: &MountInfo,
530 name: &FsStr,
531 kind: UnlinkKind,
532 directory_mode: DirectoryMode,
533 ) -> Result<(), Errno> {
534 assert!(!DirEntry::is_reserved_name(name));
535
536 let child_to_unlink;
538
539 let mut self_children = self.lock_children();
540 child_to_unlink = self_children.component_lookup(current_task, mount, name)?;
541 child_to_unlink.require_no_mounts(mount)?;
542
543 if directory_mode == DirectoryMode::MustBeDirectory && !child_to_unlink.node.is_dir() {
550 return error!(ENOTDIR);
551 }
552
553 match kind {
554 UnlinkKind::Directory => {
555 if !child_to_unlink.node.is_dir() {
556 return error!(ENOTDIR);
557 }
558 }
559 UnlinkKind::NonDirectory => {
560 if child_to_unlink.node.is_dir() {
561 return error!(EISDIR);
562 }
563 }
564 }
565
566 self.node.unlink(current_task, mount, name, &child_to_unlink.node)?;
567 self_children.children.remove(name);
568
569 std::mem::drop(self_children);
570 child_to_unlink.destroy(¤t_task.kernel().mounts);
571
572 Ok(())
573 }
574
575 fn destroy(self: DirEntryHandle, mounts: &Mounts) {
579 let was_already_dead =
580 self.raise_flags(DirEntryFlags::IS_DEAD).contains(DirEntryFlags::IS_DEAD);
581 if was_already_dead {
582 return;
583 }
584 let unmount =
585 self.lower_flags(DirEntryFlags::HAS_MOUNTS).contains(DirEntryFlags::HAS_MOUNTS);
586 self.node.fs().will_destroy_dir_entry(&self);
587 if unmount {
588 mounts.unmount(&self);
589 }
590 self.notify_deletion();
591 }
592
593 pub fn is_descendant_of(self: &DirEntryHandle, other: &DirEntryHandle) -> bool {
595 if Arc::ptr_eq(self, other) {
596 return true;
597 }
598 let mut current = self.parent();
599 while let Some(entry) = current {
600 if Arc::ptr_eq(&entry, other) {
601 return true;
603 }
604 current = entry.parent();
605 }
606 false
608 }
609
610 pub fn rename(
615 current_task: &CurrentTask,
616 old_parent: &DirEntryHandle,
617 old_mount: &MountInfo,
618 old_basename: &FsStr,
619 new_parent: &DirEntryHandle,
620 new_mount: &MountInfo,
621 new_basename: &FsStr,
622 flags: RenameFlags,
623 ) -> Result<(), Errno> {
624 if old_mount != new_mount {
626 return error!(EXDEV);
627 }
628
629 let mount = old_mount;
631
632 if DirEntry::is_reserved_name(old_basename) || DirEntry::is_reserved_name(new_basename) {
635 if flags.contains(RenameFlags::NOREPLACE) {
636 return error!(EEXIST);
637 }
638 return error!(EBUSY);
639 }
640
641 if Arc::ptr_eq(&old_parent.node, &new_parent.node) && old_basename == new_basename {
644 if flags.contains(RenameFlags::NOREPLACE) {
645 return error!(EEXIST);
646 }
647 return Ok(());
648 }
649
650 old_parent.node.check_access(
652 current_task,
653 mount,
654 Access::WRITE,
655 CheckAccessReason::InternalPermissionChecks,
656 old_parent,
657 )?;
658 new_parent.node.check_access(
659 current_task,
660 mount,
661 Access::WRITE,
662 CheckAccessReason::InternalPermissionChecks,
663 new_parent,
664 )?;
665
666 let fs = old_parent.node.fs();
669
670 let renamed;
673 let mut maybe_replaced = None;
674
675 {
676 let _lock = fs.rename_mutex.lock();
686
687 let mut state = RenameGuard::lock(old_parent, new_parent);
692
693 renamed =
696 state.old_parent_children().component_lookup(current_task, mount, old_basename)?;
697
698 let lookup_replaced =
703 state.new_parent_children().component_lookup(current_task, mount, new_basename);
704
705 if let Ok(replaced) = &lookup_replaced {
714 if old_parent.is_descendant_of(replaced) {
715 if flags.contains(RenameFlags::EXCHANGE) {
716 return error!(EINVAL);
717 } else {
718 return error!(ENOTEMPTY);
719 }
720 }
721 }
722
723 let mut state =
726 state.lock_info(old_parent, new_parent, &renamed, lookup_replaced.as_ref().ok());
727
728 if new_parent.is_descendant_of(&renamed) {
731 return error!(EINVAL);
732 }
733
734 {
737 let _token = allow_subclass();
741 old_parent.node.check_sticky_bit(
742 current_task,
743 &renamed.node,
744 state.old_parent_info(),
745 )?;
746 }
747
748 renamed.require_no_mounts(mount)?;
753
754 match &lookup_replaced {
759 Ok(replaced) => {
760 let replaced = maybe_replaced.insert(replaced.clone());
762
763 if flags.contains(RenameFlags::NOREPLACE) {
764 return error!(EEXIST);
770 }
771
772 if Arc::ptr_eq(&renamed.node, &replaced.node) {
773 if !Arc::ptr_eq(&renamed, &replaced) {
774 return Ok(());
780 }
781
782 if flags.contains(RenameFlags::EXCHANGE) {
787 return Ok(());
788 }
789 }
790
791 if state.replaced_is_dir() {
796 replaced.require_no_mounts(mount)?;
801 }
802
803 if !flags.intersects(RenameFlags::EXCHANGE | RenameFlags::REPLACE_ANY) {
804 let renamed_is_dir = state.renamed_is_dir();
805 let replaced_is_dir = state.replaced_is_dir();
806 if renamed_is_dir && !replaced_is_dir {
807 return error!(ENOTDIR);
808 } else if !renamed_is_dir && replaced_is_dir {
809 return error!(EISDIR);
810 }
811 }
812 }
813 Err(errno) if *errno == ENOENT => {
815 if flags.contains(RenameFlags::EXCHANGE) {
816 return error!(ENOENT);
817 }
818 }
819 Err(e) => return Err(e.clone()),
821 }
822
823 security::check_fs_node_rename_access(
824 current_task,
825 &old_parent.node,
826 &renamed.node,
827 &new_parent.node,
828 maybe_replaced.as_ref().map(|dir_entry| dir_entry.node.deref().as_ref()),
829 old_basename,
830 new_basename,
831 )?;
832
833 if let Some(replaced) = maybe_replaced.as_ref() {
834 let _token = allow_subclass();
838 new_parent.node.check_sticky_bit(
839 current_task,
840 &replaced.node,
841 state.new_parent_info().unwrap_or_else(|| state.old_parent_info()),
842 )?;
843 }
844
845 if state.replaced.is_some_and(|r| Arc::ptr_eq(&renamed, r)) {
850 state.replaced = None;
851 }
852
853 if flags.contains(RenameFlags::EXCHANGE) {
854 fs.exchange(current_task, &mut state, old_basename, new_basename)?;
855 } else {
856 fs.rename(current_task, &mut state, old_basename, new_basename)?;
857 }
858
859 if flags.contains(RenameFlags::EXCHANGE) {
860 let replaced =
862 maybe_replaced.as_ref().expect("replaced expected with RENAME_EXCHANGE");
863 replaced.set_parent(old_parent.clone());
864 replaced.local_name.update(old_basename.to_owned());
865 state.old_parent_children().children.insert(old_basename, Arc::downgrade(replaced));
866 } else {
867 state.old_parent_children().children.remove(old_basename);
872 }
873
874 renamed.set_parent(new_parent.clone());
877 renamed.local_name.update(new_basename.to_owned());
878
879 state.new_parent_children().children.insert(new_basename, Arc::downgrade(&renamed));
883
884 unsafe {
893 if let Some(replaced) =
894 maybe_replaced.as_ref().filter(|_| flags.contains(RenameFlags::EXCHANGE))
895 {
896 replaced.children.reset_dependencies();
897 replaced.node.info_lock().reset_dependencies();
898 }
899 renamed.children.reset_dependencies();
900 renamed.node.info_lock().reset_dependencies();
901 old_parent.children.reset_dependencies();
902 old_parent.node.info_lock().reset_dependencies();
903 new_parent.children.reset_dependencies();
904 new_parent.node.info_lock().reset_dependencies();
905 }
906 };
907
908 fs.purge_old_entries();
909
910 if let Some(replaced) = maybe_replaced {
911 if !flags.contains(RenameFlags::EXCHANGE) && !Arc::ptr_eq(&renamed, &replaced) {
912 replaced.destroy(¤t_task.kernel().mounts);
913 }
914 }
915
916 renamed.node.update_ctime();
918
919 let mode = renamed.node.info().mode;
920 if let Some(hook) =
921 current_task.kernel().expando.peek::<Arc<dyn inotify_hook::NotifyHook>>()
922 {
923 let cookie = hook.get_next_cookie();
924 old_parent.node.notify(InotifyMask::MOVE_FROM, cookie, old_basename, mode, false);
925 new_parent.node.notify(InotifyMask::MOVE_TO, cookie, new_basename, mode, false);
926 renamed.node.notify(InotifyMask::MOVE_SELF, 0, Default::default(), mode, false);
927 }
928
929 Ok(())
930 }
931
932 pub(crate) fn get_children<F, T>(&self, callback: F) -> T
933 where
934 F: FnOnce(&DirEntryChildren) -> T,
935 {
936 let children = self.children.read();
937 callback(&children)
938 }
939
940 pub fn remove_child(&self, name: &FsStr, mounts: &Mounts) {
943 let mut children = self.children.write();
944 let child = children.remove(name).and_then(|weak| weak.upgrade());
945 if let Some(child) = child {
946 std::mem::drop(children);
947 child.destroy(mounts);
948 }
949 }
950
951 fn check_search_access(
954 &self,
955 current_task: &CurrentTask,
956 mount: &MountInfo,
957 name: &FsStr,
958 ) -> Result<(), Errno> {
959 assert!(!DirEntry::is_reserved_name(name));
960 if !self.node.is_dir() {
962 return error!(ENOTDIR);
963 }
964 self.node.check_access(
966 current_task,
967 mount,
968 Access::EXEC,
969 CheckAccessReason::InternalPermissionChecks,
970 self,
971 )
972 }
973
974 fn get_or_create_child(
981 self: &DirEntryHandle,
982 current_task: &CurrentTask,
983 mount: &MountInfo,
984 name: &FsStr,
985 create_fn: impl FnOnce(
986 &DirEntryHandle,
987 &MountInfo,
988 &FsStr,
989 ) -> Result<(FsNodeHandle, CreationStatus), Errno>,
990 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
991 self.check_search_access(current_task, mount, name)?;
992
993 let child = self.children.read().get(name).and_then(Weak::upgrade);
999 if let Some(child) = child {
1000 if child.ops.revalidate(current_task, &child)? {
1001 if self.node.fail_if_locked(current_task, &self.node.info()).is_ok() {
1003 child.node.fs().did_access_dir_entry(&child);
1004 }
1005 return Ok((child, CreationStatus::Existed));
1006 }
1007 self.internal_remove_child(&child);
1009 child.destroy(¤t_task.kernel().mounts);
1010 }
1011
1012 let (child, status) =
1015 self.lock_children().get_or_create_child(current_task, mount, name, create_fn)?;
1016 child.node.fs().purge_old_entries();
1017 Ok((child, status))
1018 }
1019
1020 #[cfg(test)]
1028 pub fn copy_child_names(&self) -> Vec<FsString> {
1029 let scope = RcuReadScope::new();
1030 self.children.read().copy_child_names(&scope)
1031 }
1032
1033 fn internal_remove_child(&self, child: &DirEntry) {
1034 let mut children = self.children.write();
1035 let scope = RcuReadScope::new();
1036 let local_name = child.local_name.read(&scope);
1037 children.remove_if_child_matches(local_name, child);
1038 }
1039
1040 pub fn notify(&self, event_mask: InotifyMask) {
1042 self.notify_watchers(event_mask, self.is_dead());
1043 }
1044
1045 pub fn notify_ignoring_excl_unlink(&self, event_mask: InotifyMask) {
1049 self.notify_watchers(event_mask, false);
1051 }
1052
1053 fn notify_watchers(&self, event_mask: InotifyMask, is_dead: bool) {
1054 let mode = self.node.info().mode;
1055 {
1056 let scope = RcuReadScope::new();
1057 if let Some(parent) = self.parent() {
1058 let local_name = self.local_name.read(&scope);
1059 parent.node.notify(event_mask, 0, local_name, mode, is_dead);
1060 }
1061 }
1062 self.node.notify(event_mask, 0, Default::default(), mode, is_dead);
1063 }
1064
1065 fn notify_creation(&self) {
1067 let mode = self.node.info().mode;
1068 if Arc::strong_count(&self.node) > 1 {
1069 self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1071 }
1072 let scope = RcuReadScope::new();
1073 if let Some(parent) = self.parent() {
1074 let local_name = self.local_name.read(&scope);
1075 parent.node.notify(InotifyMask::CREATE, 0, local_name, mode, false);
1076 }
1077 }
1078
1079 fn notify_deletion(&self) {
1083 let mode = self.node.info().mode;
1084 if !mode.is_dir() {
1085 self.node.notify(InotifyMask::ATTRIB, 0, Default::default(), mode, false);
1087 }
1088
1089 if Arc::strong_count(&self.node) == 1 {
1092 self.node.notify(InotifyMask::DELETE_SELF, 0, Default::default(), mode, false);
1093 }
1094
1095 let scope = RcuReadScope::new();
1096 if let Some(parent) = self.parent() {
1097 let local_name = self.local_name.read(&scope);
1098 parent.node.notify(InotifyMask::DELETE, 0, local_name, mode, false);
1099 }
1100 }
1101
1102 pub fn has_mounts(&self) -> bool {
1104 self.flags().contains(DirEntryFlags::HAS_MOUNTS)
1105 }
1106
1107 pub fn set_has_mounts(&self, v: bool) {
1109 if v {
1110 self.raise_flags(DirEntryFlags::HAS_MOUNTS);
1111 } else {
1112 self.lower_flags(DirEntryFlags::HAS_MOUNTS);
1113 }
1114 }
1115
1116 fn require_no_mounts(self: &Arc<Self>, parent_mount: &MountInfo) -> Result<(), Errno> {
1118 if self.has_mounts() {
1119 if let Some(mount) = parent_mount.as_ref() {
1120 if mount.has_submount(self) {
1121 return error!(EBUSY);
1122 }
1123 }
1124 }
1125 Ok(())
1126 }
1127}
1128
1129#[inline]
1132fn canonicalize_name(name: impl AsRef<[u8]>) -> SmallVec<[u8; NAME_MAX as usize]> {
1133 let name = name.as_ref();
1134 match std::str::from_utf8(name) {
1135 Ok(valid) => {
1136 CasefoldStr::new(valid).casefold_normalized_chars().flat_map(utf8_bytes).collect()
1137 }
1138 Err(_) => SmallVec::from_slice(name),
1139 }
1140}
1141
1142#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
1147pub(crate) struct DirEntryChildKey(FsString);
1148
1149impl DirEntryChildKey {
1150 pub fn new(name: &FsStr, casefold: bool) -> Self {
1151 if casefold {
1152 Self(canonicalize_name(name).into_vec().into())
1153 } else {
1154 Self(name.to_owned())
1155 }
1156 }
1157}
1158
1159impl std::borrow::Borrow<FsStr> for DirEntryChildKey {
1160 fn borrow(&self) -> &FsStr {
1161 self.0.as_bstr()
1162 }
1163}
1164
1165#[derive(Default, Debug)]
1172pub(crate) struct DirEntryChildren {
1173 entries: BTreeMap<DirEntryChildKey, Weak<DirEntry>>,
1174 casefold: bool,
1175}
1176
1177impl std::ops::Deref for DirEntryChildren {
1178 type Target = BTreeMap<DirEntryChildKey, Weak<DirEntry>>;
1179
1180 fn deref(&self) -> &Self::Target {
1181 &self.entries
1182 }
1183}
1184
1185impl DirEntryChildren {
1186 pub fn new(casefold: bool) -> Self {
1187 Self { entries: BTreeMap::new(), casefold }
1188 }
1189
1190 pub fn is_casefold(&self) -> bool {
1191 self.casefold
1192 }
1193
1194 pub fn set_casefold(&mut self, casefold: bool) {
1195 self.entries.clear();
1196 self.casefold = casefold;
1197 }
1198
1199 pub fn get(&self, name: &FsStr) -> Option<&Weak<DirEntry>> {
1201 if self.casefold {
1202 let key = canonicalize_name(name);
1203 self.entries.get(FsStr::new(&key))
1204 } else {
1205 self.entries.get(name)
1206 }
1207 }
1208
1209 pub fn insert(&mut self, name: &FsStr, child: Weak<DirEntry>) -> Option<Weak<DirEntry>> {
1212 self.entries.insert(DirEntryChildKey::new(name, self.casefold), child)
1213 }
1214
1215 pub fn remove(&mut self, name: &FsStr) -> Option<Weak<DirEntry>> {
1218 if self.casefold {
1219 let key = canonicalize_name(name);
1220 self.entries.remove(FsStr::new(&key))
1221 } else {
1222 self.entries.remove(name)
1223 }
1224 }
1225
1226 pub fn remove_if_child_matches(&mut self, name: &FsStr, expected_child: &DirEntry) -> bool {
1229 if let Some(weak_child) = self.get(name) {
1230 if std::ptr::eq(weak_child.as_ptr(), expected_child) {
1231 let _ = self.remove(name);
1232 return true;
1233 }
1234 }
1235 false
1236 }
1237
1238 #[cfg(test)]
1239 fn copy_child_names(&self, scope: &RcuReadScope) -> Vec<FsString> {
1242 self.entries
1243 .values()
1244 .filter_map(|child| Weak::upgrade(child).map(|c| c.local_name.read(scope).to_owned()))
1245 .collect()
1246 }
1247}
1248
1249struct DirEntryLockedChildren<'a> {
1250 entry: &'a DirEntryHandle,
1251 children: LockDepWriteGuard<'a, DirEntryChildren>,
1252}
1253
1254#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1257pub enum CreationStatus {
1258 Created,
1260 Existed,
1263}
1264
1265impl<'a> DirEntryLockedChildren<'a> {
1266 fn component_lookup(
1267 &mut self,
1268 current_task: &CurrentTask,
1269 mount: &MountInfo,
1270 name: &FsStr,
1271 ) -> Result<DirEntryHandle, Errno> {
1272 self.entry.check_search_access(current_task, mount, name)?;
1273 let (node, _) =
1274 self.get_or_create_child(current_task, mount, name, |d, _mount, name| {
1275 Ok((d.node.ops().lookup(d, current_task, name)?, CreationStatus::Existed))
1276 })?;
1277 Ok(node)
1278 }
1279
1280 fn get_or_create_child(
1281 &mut self,
1282 current_task: &CurrentTask,
1283 mount: &MountInfo,
1284 name: &FsStr,
1285 create_fn: impl FnOnce(
1286 &DirEntryHandle,
1287 &MountInfo,
1288 &FsStr,
1289 ) -> Result<(FsNodeHandle, CreationStatus), Errno>,
1290 ) -> Result<(DirEntryHandle, CreationStatus), Errno> {
1291 if let Some(child) = self.children.get(name).and_then(Weak::upgrade) {
1292 if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1294 child.node.fs().did_access_dir_entry(&child);
1295 }
1296 return Ok((child, CreationStatus::Existed));
1297 }
1298
1299 let (node, status) = create_fn(self.entry, mount, name)?;
1300
1301 assert!(
1302 node.info().mode & FileMode::IFMT != FileMode::EMPTY,
1303 "FsNode initialization did not populate the FileMode in FsNodeInfo."
1304 );
1305
1306 let child = DirEntry::new(node, Some(self.entry.clone()), name.to_owned());
1307
1308 if let Err(err) = security::fs_node_init_with_dentry(current_task, &child) {
1309 child.parent.update(None);
1312 return Err(err);
1313 }
1314
1315 if self.entry.node.fail_if_locked(current_task, &self.entry.node.info()).is_ok() {
1317 self.children.insert(name, Arc::downgrade(&child));
1318 }
1319
1320 Ok((child, status))
1321 }
1322}
1323
1324impl fmt::Debug for DirEntry {
1325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1326 let scope = RcuReadScope::new();
1327 let mut path = PathBuilder::new();
1328 let mut parent_holder;
1329 let mut current = self;
1330 loop {
1331 let name = current.local_name(&scope);
1333 if !name.is_empty() {
1334 path.prepend_element(name);
1335 }
1336 let Some(parent) = current.parent() else {
1337 break;
1338 };
1339 parent_holder = parent;
1340 current = &parent_holder;
1341 }
1342 f.debug_struct("DirEntry")
1343 .field("fs", &self.node.fs().name())
1344 .field("path", &path.build_absolute())
1345 .finish()
1346 }
1347}
1348
1349#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1350enum RenameRelationship {
1351 Same,
1352 NewIsDescendant,
1353 OldIsDescendant,
1354 Independent,
1355}
1356
1357struct RenameGuard<'a> {
1358 old_parent_guard: DirEntryLockedChildren<'a>,
1359 new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1360 relationship: RenameRelationship,
1361}
1362
1363impl<'a> RenameGuard<'a> {
1364 fn lock(old_parent: &'a DirEntryHandle, new_parent: &'a DirEntryHandle) -> Self {
1365 if Arc::ptr_eq(old_parent, new_parent) {
1366 let old_parent_guard = old_parent.lock_children();
1367 Self {
1368 old_parent_guard,
1369 new_parent_guard: None,
1370 relationship: RenameRelationship::Same,
1371 }
1372 } else if new_parent.is_descendant_of(old_parent) {
1373 let old_parent_guard = old_parent.lock_children();
1374 let _token = allow_subclass();
1375 let new_parent_guard = new_parent.lock_children();
1376 Self {
1377 old_parent_guard,
1378 new_parent_guard: Some(new_parent_guard),
1379 relationship: RenameRelationship::NewIsDescendant,
1380 }
1381 } else if old_parent.is_descendant_of(new_parent) {
1382 let new_parent_guard = new_parent.lock_children();
1383 let _token = allow_subclass();
1384 let old_parent_guard = old_parent.lock_children();
1385 Self {
1386 old_parent_guard,
1387 new_parent_guard: Some(new_parent_guard),
1388 relationship: RenameRelationship::OldIsDescendant,
1389 }
1390 } else {
1391 let (g1, g2) =
1393 starnix_sync::ordered_write_lock(&old_parent.children, &new_parent.children);
1394 let old_parent_guard = DirEntryLockedChildren { entry: old_parent, children: g1 };
1395 let new_parent_guard = DirEntryLockedChildren { entry: new_parent, children: g2 };
1396 Self {
1397 old_parent_guard,
1398 new_parent_guard: Some(new_parent_guard),
1399 relationship: RenameRelationship::Independent,
1400 }
1401 }
1402 }
1403
1404 fn lock_info(
1408 self,
1409 old_parent: &'a DirEntryHandle,
1410 new_parent: &'a DirEntryHandle,
1411 renamed: &'a DirEntryHandle,
1412 replaced: Option<&'a DirEntryHandle>,
1413 ) -> RenameContext<'a> {
1414 let (g1, g2) = match self.relationship {
1415 RenameRelationship::Same => (old_parent.node.info_lock().write(), None),
1416 RenameRelationship::NewIsDescendant => {
1417 let g1 = old_parent.node.info_lock().write();
1418 let _token = allow_subclass();
1419 let g2 = new_parent.node.info_lock().write();
1420 (g1, Some(g2))
1421 }
1422 RenameRelationship::OldIsDescendant => {
1423 let g2 = new_parent.node.info_lock().write();
1424 let _token = allow_subclass();
1425 let g1 = old_parent.node.info_lock().write();
1426 (g1, Some(g2))
1427 }
1428 RenameRelationship::Independent => {
1429 let (g1, g2) = starnix_sync::ordered_write_lock(
1430 old_parent.node.info_lock(),
1431 new_parent.node.info_lock(),
1432 );
1433 (g1, Some(g2))
1434 }
1435 };
1436
1437 RenameContext {
1438 renamed,
1439 replaced,
1440 old_parent_guard: self.old_parent_guard,
1441 new_parent_guard: self.new_parent_guard,
1442 old_parent_info_guard: g1,
1443 new_parent_info_guard: g2,
1444 }
1445 }
1446
1447 fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1448 &mut self.old_parent_guard
1449 }
1450
1451 fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1452 if let Some(new_guard) = self.new_parent_guard.as_mut() {
1453 new_guard
1454 } else {
1455 &mut self.old_parent_guard
1456 }
1457 }
1458}
1459
1460pub struct RenameContext<'a> {
1473 pub renamed: &'a DirEntryHandle,
1474 pub replaced: Option<&'a DirEntryHandle>,
1475 old_parent_guard: DirEntryLockedChildren<'a>,
1476 new_parent_guard: Option<DirEntryLockedChildren<'a>>,
1477 old_parent_info_guard: LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>,
1478 new_parent_info_guard: Option<LockDepWriteGuard<'a, crate::vfs::FsNodeInfo>>,
1479}
1480
1481impl<'a> RenameContext<'a> {
1482 pub fn renamed_is_dir(&self) -> bool {
1489 self.is_dir(&self.renamed.node)
1490 }
1491
1492 pub fn replaced_is_dir(&self) -> bool {
1501 self.replaced.map(|r| self.is_dir(&r.node)).unwrap_or(false)
1502 }
1503
1504 pub fn old_parent(&self) -> &DirEntryHandle {
1509 self.old_parent_guard.entry
1510 }
1511
1512 pub fn new_parent(&self) -> &DirEntryHandle {
1517 self.new_parent_guard.as_ref().map(|g| g.entry).unwrap_or(self.old_parent_guard.entry)
1518 }
1519
1520 pub fn parent_infos_mut(
1529 &mut self,
1530 ) -> (&mut crate::vfs::FsNodeInfo, Option<&mut crate::vfs::FsNodeInfo>) {
1531 let old = &mut *self.old_parent_info_guard;
1532 let new = self.new_parent_info_guard.as_mut().map(|g| &mut **g);
1533 (old, new)
1534 }
1535
1536 pub fn old_parent_info(&self) -> &crate::vfs::FsNodeInfo {
1539 &self.old_parent_info_guard
1540 }
1541 pub fn new_parent_info(&self) -> Option<&crate::vfs::FsNodeInfo> {
1546 self.new_parent_info_guard.as_deref()
1547 }
1548
1549 fn new_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1550 self.new_parent_guard.as_mut().unwrap_or(&mut self.old_parent_guard)
1551 }
1552
1553 fn old_parent_children(&mut self) -> &mut DirEntryLockedChildren<'a> {
1554 &mut self.old_parent_guard
1555 }
1556
1557 fn is_dir(&self, node: &FsNodeHandle) -> bool {
1558 if Arc::ptr_eq(node, &self.old_parent().node) {
1559 self.old_parent_info_guard.mode.is_dir()
1560 } else if Arc::ptr_eq(node, &self.new_parent().node) {
1561 self.new_parent_info_guard
1562 .as_ref()
1563 .map(|g| g.mode.is_dir())
1564 .unwrap_or_else(|| self.old_parent_info_guard.mode.is_dir())
1565 } else {
1566 let _token = allow_subclass();
1567 node.is_dir()
1568 }
1569 }
1570}
1571
1572impl Drop for DirEntry {
1576 fn drop(&mut self) {
1577 let maybe_parent = self.parent();
1578 self.parent.update(None);
1579 if let Some(parent) = maybe_parent {
1580 parent.internal_remove_child(self);
1581 }
1582 }
1583}
1584
1585#[cfg(test)]
1586mod tests {
1587 use super::*;
1588 use crate::testing::{PanickingFile, spawn_kernel_and_run};
1589 use crate::vfs::pseudo::simple_directory::SimpleDirectory;
1590 use crate::vfs::{
1591 CacheConfig, CacheMode, FileOps, FileSystem, FileSystemHandle, FileSystemOps,
1592 FileSystemOptions, FsNode, FsNodeInfo, FsNodeOps, MemoryDirectoryFile, Namespace,
1593 SymlinkNode, WhatToMount, fs_node_impl_not_dir,
1594 };
1595 use starnix_sync::Mutex;
1596 use starnix_types::vfs::default_statfs;
1597 use starnix_uapi::device_id::DeviceId;
1598 use starnix_uapi::file_mode::mode;
1599 use starnix_uapi::mount_flags::MountpointFlags;
1600 use starnix_uapi::vfs::ResolveFlags;
1601 use starnix_uapi::{errno, statfs};
1602 use std::sync::atomic::AtomicUsize;
1603
1604 #[fuchsia::test]
1605 async fn test_dir_entry_debug() {
1606 spawn_kernel_and_run(async |current_task| {
1607 let fs = current_task.fs().root().entry.node.fs();
1608 let check = |parent: Option<&DirEntryHandle>, name: &str, expected_path: &str| {
1609 let node = SimpleDirectory::new().into_node(&fs, 0o755);
1610 let entry = DirEntry::new(node, parent.cloned(), name.into());
1611 assert_eq!(
1612 format!("{entry:?}"),
1613 format!("DirEntry {{ fs: {:?}, path: {expected_path:?} }}", fs.name())
1614 );
1615 entry
1616 };
1617
1618 let root = check(None, "", "/");
1619 let bus = check(Some(&root), "bus", "/bus");
1620 check(Some(&bus), "pci", "/bus/pci");
1621 check(None, "[eventfd]", "/[eventfd]");
1622 })
1623 .await;
1624 }
1625
1626 #[test]
1627 fn test_canonicalize_name() {
1628 let upper = canonicalize_name("FooBar.TXT");
1629 let lower = canonicalize_name("foobar.txt");
1630 let mixed = canonicalize_name("FOOBAR.TXT");
1631 let diff = canonicalize_name("other.txt");
1632
1633 assert_eq!(upper.as_slice(), lower.as_slice());
1634 assert_eq!(upper.as_slice(), mixed.as_slice());
1635 assert_ne!(upper.as_slice(), diff.as_slice());
1636
1637 let composed = canonicalize_name("\u{03AA}");
1639 let decomposed = canonicalize_name("\u{0399}\u{0308}");
1640 assert_eq!(composed.as_slice(), decomposed.as_slice());
1641
1642 let e_accent_lower = canonicalize_name("e\u{0301}");
1644 let e_accent_upper = canonicalize_name("\u{00c9}");
1645 assert_eq!(e_accent_lower.as_slice(), e_accent_upper.as_slice());
1646
1647 let strasse_lower = canonicalize_name("straße");
1649 let strasse_upper = canonicalize_name("STRASSE");
1650 assert_eq!(strasse_lower.as_slice(), strasse_upper.as_slice());
1651
1652 let non_utf8 = canonicalize_name(b"foo\x80bar");
1654 assert_eq!(non_utf8.as_slice(), b"foo\x80bar");
1655 }
1656
1657 #[test]
1658 fn test_dir_entry_children_casefold() {
1659 let mut children = DirEntryChildren::new(true);
1660 assert!(children.is_casefold());
1661
1662 children.insert("FooBar.TXT".into(), Weak::new());
1664 assert!(children.get("foobar.txt".into()).is_some());
1665 assert!(children.get("FOOBAR.TXT".into()).is_some());
1666 assert!(children.get("FooBar.TXT".into()).is_some());
1667 assert!(children.get("other.txt".into()).is_none());
1668
1669 children.insert("\u{03AA}".into(), Weak::new());
1671 assert!(children.get("\u{0399}\u{0308}".into()).is_some());
1672
1673 children.insert(b"foo\x80bar".into(), Weak::new());
1675 assert!(children.get(b"foo\x80bar".into()).is_some());
1676 assert!(children.get(b"FOO\x80bar".into()).is_none());
1677
1678 assert!(children.remove("FOOBAR.TXT".into()).is_some());
1680 assert!(children.get("foobar.txt".into()).is_none());
1681 assert!(children.remove("\u{0399}\u{0308}".into()).is_some());
1682 assert!(children.get("\u{03AA}".into()).is_none());
1683 assert!(children.remove(b"foo\x80bar".into()).is_some());
1684 assert!(children.get(b"foo\x80bar".into()).is_none());
1685 assert!(children.is_empty());
1686
1687 children.insert("Foo.TXT".into(), Weak::new());
1689 assert!(!children.is_empty());
1690 children.set_casefold(false);
1691 assert!(!children.is_casefold());
1692 assert!(children.is_empty());
1693 children.set_casefold(true);
1694 assert!(children.is_casefold());
1695 }
1696
1697 #[derive(Default)]
1698 struct CountingTestFs {
1699 existing_nodes: Mutex<BTreeMap<FsString, FsNodeHandle>>,
1702
1703 revalidate_count: AtomicUsize,
1705 }
1706
1707 impl FileSystemOps for CountingTestFs {
1708 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
1709 Ok(default_statfs(0))
1710 }
1711
1712 fn name(&self) -> &'static FsStr {
1713 "counting_test_fs".into()
1714 }
1715 }
1716
1717 fn counting_fs(fs: &FileSystem) -> &CountingTestFs {
1719 fs.downcast_ops::<CountingTestFs>().expect("not a CountingTestFs")
1720 }
1721
1722 struct CountingDirNode {
1723 lookup_count: Arc<AtomicUsize>,
1724 }
1725
1726 impl FsNodeOps for CountingDirNode {
1727 fn create_file_ops(
1728 &self,
1729 _node: &FsNode,
1730 _current_task: &CurrentTask,
1731 _flags: OpenFlags,
1732 ) -> Result<Box<dyn FileOps>, Errno> {
1733 Ok(Box::new(MemoryDirectoryFile::new()))
1734 }
1735
1736 fn lookup(
1737 &self,
1738 entry: &DirEntry,
1739 _current_task: &CurrentTask,
1740 name: &FsStr,
1741 ) -> Result<FsNodeHandle, Errno> {
1742 self.lookup_count.fetch_add(1, Ordering::SeqCst);
1743 let fs = entry.node.fs();
1744 let node = counting_fs(&fs).existing_nodes.lock().get(name).cloned();
1745 node.ok_or_else(|| errno!(ENOENT))
1746 }
1747
1748 fn mknod(
1749 &self,
1750 node: &FsNode,
1751 _current_task: &CurrentTask,
1752 _name: &FsStr,
1753 mode: FileMode,
1754 dev: DeviceId,
1755 owner: FsCred,
1756 ) -> Result<FsNodeHandle, Errno> {
1757 let mut info = FsNodeInfo::new(mode, owner);
1758 info.rdev = dev;
1759 Ok(node.fs().create_node_and_allocate_node_id(CountingFileNode, info))
1760 }
1761
1762 fn mkdir(
1763 &self,
1764 node: &FsNode,
1765 _current_task: &CurrentTask,
1766 _name: &FsStr,
1767 mode: FileMode,
1768 owner: FsCred,
1769 ) -> Result<FsNodeHandle, Errno> {
1770 Ok(node.fs().create_node_and_allocate_node_id(
1771 CountingDirNode { lookup_count: self.lookup_count.clone() },
1772 FsNodeInfo::new(mode, owner),
1773 ))
1774 }
1775
1776 fn unlink(
1777 &self,
1778 _node: &FsNode,
1779 _current_task: &CurrentTask,
1780 _name: &FsStr,
1781 _child: &FsNodeHandle,
1782 ) -> Result<(), Errno> {
1783 Ok(())
1784 }
1785
1786 fn create_symlink(
1787 &self,
1788 _node: &FsNode,
1789 _current_task: &CurrentTask,
1790 _name: &FsStr,
1791 _target: &FsStr,
1792 _owner: FsCred,
1793 ) -> Result<FsNodeHandle, Errno> {
1794 error!(ENOTSUP)
1795 }
1796 }
1797
1798 struct CountingFileNode;
1799 impl FsNodeOps for CountingFileNode {
1800 fs_node_impl_not_dir!();
1801
1802 fn create_file_ops(
1803 &self,
1804 _node: &FsNode,
1805 _current_task: &CurrentTask,
1806 _flags: OpenFlags,
1807 ) -> Result<Box<dyn FileOps>, Errno> {
1808 Ok(Box::new(PanickingFile))
1809 }
1810
1811 fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
1812 Box::new(CountingDirEntryOps)
1813 }
1814 }
1815
1816 struct CountingDirEntryOps;
1817 impl DirEntryOps for CountingDirEntryOps {
1818 fn revalidate(&self, _current_task: &CurrentTask, entry: &DirEntry) -> Result<bool, Errno> {
1819 counting_fs(&entry.node.fs()).revalidate_count.fetch_add(1, Ordering::SeqCst);
1820 Ok(true)
1821 }
1822 }
1823
1824 fn create_counting_fs(current_task: &CurrentTask) -> (FileSystemHandle, Arc<AtomicUsize>) {
1825 let lookup_count = Arc::new(AtomicUsize::new(0));
1826 let fs = FileSystem::new(
1827 current_task.kernel(),
1828 CacheMode::Cached(CacheConfig { capacity: 16 }),
1829 CountingTestFs::default(),
1830 FileSystemOptions::default(),
1831 )
1832 .expect("create filesystem");
1833 let root_ino = fs.allocate_ino();
1834 fs.create_root(root_ino, CountingDirNode { lookup_count: lookup_count.clone() });
1835 (fs, lookup_count)
1836 }
1837
1838 #[fuchsia::test]
1839 async fn test_open_create_looks_up_once() {
1840 spawn_kernel_and_run(async |current_task| {
1841 let (fs, lookup_count) = create_counting_fs(¤t_task);
1842 let dir = Namespace::new(fs.clone()).root();
1843 let open = |name: &str, flags: OpenFlags| {
1844 current_task.open_namespace_node_at(
1845 dir.clone(),
1846 name.into(),
1847 flags,
1848 mode!(IFREG, 0o644),
1849 ResolveFlags::empty(),
1850 )
1851 };
1852
1853 let created = open("file", OpenFlags::CREAT | OpenFlags::RDWR).expect("create file");
1855 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1856
1857 let opened = open("file", OpenFlags::CREAT | OpenFlags::RDWR).expect("open file");
1860 assert!(Arc::ptr_eq(&created.name.entry, &opened.name.entry));
1861 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1862 assert_eq!(counting_fs(&fs).revalidate_count.load(Ordering::SeqCst), 1);
1863
1864 assert_eq!(
1866 open("file", OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR).unwrap_err(),
1867 errno!(EEXIST)
1868 );
1869 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1870
1871 open("excl", OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR)
1873 .expect("create excl");
1874 assert_eq!(lookup_count.load(Ordering::SeqCst), 2);
1875 })
1876 .await;
1877 }
1878
1879 #[fuchsia::test]
1880 async fn test_open_create_existing() {
1881 spawn_kernel_and_run(async |current_task| {
1882 let (fs, lookup_count) = create_counting_fs(¤t_task);
1883 let ns = Namespace::new(fs.clone());
1884 let dir = ns.root();
1885 let open = |name: &str, resolve_flags: ResolveFlags| {
1886 current_task.open_namespace_node_at(
1887 dir.clone(),
1888 name.into(),
1889 OpenFlags::CREAT | OpenFlags::RDWR,
1890 mode!(IFREG, 0o644),
1891 resolve_flags,
1892 )
1893 };
1894 let add_existing_node = |name: &str, node: FsNodeHandle| {
1895 counting_fs(&fs).existing_nodes.lock().insert(name.into(), node);
1896 };
1897
1898 let file = fs.create_node_and_allocate_node_id(
1901 CountingFileNode,
1902 FsNodeInfo::new(mode!(IFREG, 0o644), FsCred::root()),
1903 );
1904 add_existing_node("file", file.clone());
1905 let opened = open("file", ResolveFlags::empty()).expect("open file");
1906 assert!(Arc::ptr_eq(&opened.name.entry.node, &file));
1907 assert_eq!(lookup_count.load(Ordering::SeqCst), 1);
1908 assert_eq!(counting_fs(&fs).revalidate_count.load(Ordering::SeqCst), 0);
1909
1910 let (link, info) = SymlinkNode::new("file".into(), FsCred::root());
1912 add_existing_node("link", fs.create_node_and_allocate_node_id(link, info));
1913 let via_link = open("link", ResolveFlags::empty()).expect("open link");
1914 assert!(Arc::ptr_eq(&via_link.name.entry, &opened.name.entry));
1915 assert_eq!(lookup_count.load(Ordering::SeqCst), 2);
1916
1917 let mounted = open("mounted", ResolveFlags::empty()).expect("create mounted");
1920 opened
1921 .name
1922 .mount(WhatToMount::Bind(mounted.name.to_passive()), MountpointFlags::empty())
1923 .expect("bind mount");
1924 let over_mount = open("file", ResolveFlags::empty()).expect("open mount point");
1925 assert!(Arc::ptr_eq(&over_mount.name.entry, &mounted.name.entry));
1926 assert_eq!(open("file", ResolveFlags::NO_XDEV).unwrap_err(), errno!(EXDEV));
1927
1928 let open_excl = |name: &str, resolve_flags: ResolveFlags| {
1932 current_task.open_namespace_node_at(
1933 dir.clone(),
1934 name.into(),
1935 OpenFlags::CREAT | OpenFlags::EXCL | OpenFlags::RDWR,
1936 mode!(IFREG, 0o644),
1937 resolve_flags,
1938 )
1939 };
1940 assert_eq!(open_excl("file", ResolveFlags::empty()).unwrap_err(), errno!(EEXIST));
1941 assert_eq!(open_excl("file", ResolveFlags::NO_XDEV).unwrap_err(), errno!(EXDEV));
1942 })
1943 .await;
1944 }
1945}