diff --git a/kernel/src/fs/path/dentry.rs b/kernel/src/fs/path/dentry.rs index 7615c8827..e71878746 100644 --- a/kernel/src/fs/path/dentry.rs +++ b/kernel/src/fs/path/dentry.rs @@ -101,16 +101,18 @@ impl Dentry { DentryFlags::from_bits(flags).unwrap() } - /// Checks if this dentry is a descendant (child, grandchild, or - /// great-grandchild, etc.) of another dentry. - pub(super) fn is_descendant_of(&self, ancestor: &Arc) -> bool { - let mut parent = self.parent(); - while let Some(p) = parent { - if Arc::ptr_eq(&p, ancestor) { + /// Checks if this dentry is a descendant of or the same as the given + /// ancestor dentry. + pub(super) fn is_equal_or_descendant_of(&self, ancestor: &Arc) -> bool { + let mut current = Some(self.this()); + + while let Some(node) = current { + if Arc::ptr_eq(&node, ancestor) { return true; } - parent = p.parent(); + current = node.parent(); } + false } diff --git a/kernel/src/fs/path/mount.rs b/kernel/src/fs/path/mount.rs index 1e32629f9..7cd21cfe2 100644 --- a/kernel/src/fs/path/mount.rs +++ b/kernel/src/fs/path/mount.rs @@ -326,7 +326,7 @@ impl Mount { let old_children = old_mount.children.read(); for old_child_mount in old_children.values() { let mountpoint = old_child_mount.mountpoint().unwrap(); - if !mountpoint.is_descendant_of(root_dentry) { + if !mountpoint.is_equal_or_descendant_of(old_mount.root_dentry()) { continue; } let new_child_mount = diff --git a/test/src/apps/namespace/mnt_ns.c b/test/src/apps/namespace/mnt_ns.c index a4f5830d4..d65a400bd 100644 --- a/test/src/apps/namespace/mnt_ns.c +++ b/test/src/apps/namespace/mnt_ns.c @@ -190,3 +190,71 @@ FN_TEST(setns_newns) TEST_SUCC(rmdir(SETNS_MNT)); } END_TEST() + +#define COMPLEX_BASE "/test0" +#define COMPLEX_CONTENT "/test0/content" +#define COMPLEX_FILE "/test0/content/hello.txt" + +FN_TEST(complex_mount_tree_unshare) +{ + // Setup: Create directory structure + CHECK_WITH(mkdir(COMPLEX_BASE, 0755), _ret >= 0 || errno == EEXIST); + + // Mount tmpfs on test0 (first layer) + TEST_SUCC(mount("none", COMPLEX_BASE, "tmpfs", 0, "")); + + // Mount tmpfs on test0 again (second layer, stacked mount) + TEST_SUCC(mount("none", COMPLEX_BASE, "tmpfs", 0, "")); + + // Recreate content directory on the new mount + TEST_SUCC(mkdir(COMPLEX_CONTENT, 0755)); + + // Mount tmpfs on test0/content (third layer) + TEST_SUCC(mount("none", COMPLEX_CONTENT, "tmpfs", 0, "")); + + // Create file with content + int fd = TEST_SUCC(open(COMPLEX_FILE, O_CREAT | O_WRONLY, 0644)); + TEST_SUCC(write(fd, "hello world\n", 12)); + TEST_SUCC(close(fd)); + + // Verify file exists in parent + TEST_SUCC(access(COMPLEX_FILE, F_OK)); + + // Fork and unshare in child + pid_t pid = TEST_SUCC(fork()); + + if (pid == 0) { + TEST_SUCC(unshare(CLONE_NEWNS)); + + // Try to read the file created in the parent's mount tree + int fd = CHECK(open(COMPLEX_FILE, O_RDONLY)); + + char buf[32] = { 0 }; + TEST_SUCC(read(fd, buf, sizeof(buf) - 1)); + TEST_SUCC(close(fd)); + + // Verify the content + CHECK_WITH(strcmp(buf, "hello world\n"), _ret == 0); + + // Verify we can still access the nested mount structure + TEST_SUCC(access(COMPLEX_CONTENT, F_OK)); + TEST_SUCC(access(COMPLEX_BASE, F_OK)); + + exit(0); + } else { + int status; + TEST_SUCC(waitpid(pid, &status, 0)); + TEST_RES(WIFEXITED(status) && WEXITSTATUS(status), _ret == 0); + + // Verify parent's mount tree is unchanged + TEST_SUCC(access(COMPLEX_FILE, F_OK)); + + // Teardown: Unmount in reverse order + TEST_SUCC(umount(COMPLEX_CONTENT)); + TEST_SUCC(rmdir(COMPLEX_CONTENT)); + TEST_SUCC(umount(COMPLEX_BASE)); + TEST_SUCC(umount(COMPLEX_BASE)); + TEST_SUCC(rmdir(COMPLEX_BASE)); + } +} +END_TEST() \ No newline at end of file