From cf05062f4d1277ffe79bc9998a76a2ff54051e27 Mon Sep 17 00:00:00 2001 From: sparkzky Date: Wed, 4 Feb 2026 23:17:34 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(filesystem):=20=E5=AE=9E=E7=8E=B0=20pi?= =?UTF-8?q?vot=5Froot=20=E7=B3=BB=E7=BB=9F=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现完整的 pivot_root(2) 系统调用,支持容器根文件系统切换。 主要功能: - 新增 sys_pivot_root.rs,实现 Linux 兼容的 pivot_root 语义 - MountFS 增加 bind_target_root 支持,正确处理 bind mount 场景 - 挂载命名空间增加旧根挂载点管理,支持 pivot_root 后卸载 - umount2 增加对当前目录卸载的特殊处理 Signed-off-by: sparkzky --- kernel/src/filesystem/vfs/mount.rs | 129 +++++- kernel/src/filesystem/vfs/syscall/mod.rs | 1 + .../src/filesystem/vfs/syscall/sys_mount.rs | 147 +++++- .../filesystem/vfs/syscall/sys_pivot_root.rs | 428 ++++++++++++++++++ .../src/filesystem/vfs/syscall/sys_umount2.rs | 61 +++ kernel/src/filesystem/vfs/utils.rs | 4 +- kernel/src/process/namespace/mnt.rs | 36 ++ 7 files changed, 794 insertions(+), 12 deletions(-) create mode 100644 kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 39eeee7480..2f95c556f9 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -237,6 +237,13 @@ pub struct MountFS { mount_id: MountId, mount_flags: MountFlags, + + /// 对于bind mount,存储bind target目录的inode。 + /// 当这个MountFS被用作根文件系统时,root_inode() 应该返回这个inode, + /// 而不是inner_filesystem的root。 + /// 这是为了支持container场景:bind mount /tmp/xxx/rootfs 后, + /// pivot_root到这个mount时,看到的应该是rootfs的内容,而不是底层tmpfs的根。 + bind_target_root: RwSem>>, } impl Debug for MountFS { @@ -290,6 +297,7 @@ impl MountFS { propagation, mount_id: MountId::alloc(), mount_flags, + bind_target_root: RwSem::new(None), }); if let Some(mnt_ns) = mnt_ns { @@ -312,6 +320,7 @@ impl MountFS { propagation: new_propagation, mount_id: MountId::alloc(), mount_flags: self.mount_flags, + bind_target_root: RwSem::new(None), }); return mountfs; @@ -321,6 +330,18 @@ impl MountFS { self.mount_flags } + /// 设置挂载标志 + /// + /// 用于 MS_REMOUNT | MS_BIND 场景,修改已存在挂载的标志(如只读状态) + pub fn set_mount_flags(&self, flags: MountFlags) { + // 使用 unsafe 来修改不可变字段 + // 这是安全的,因为我们只修改挂载标志,且在挂载命名空间的锁保护下 + let ptr = self as *const Self as *mut Self; + unsafe { + (*ptr).mount_flags = flags; + } + } + pub fn add_mount(&self, inode_id: InodeId, mount_fs: Arc) -> Result<(), SystemError> { // 检查是否已经存在同名的挂载点 if self.mountpoints.lock().contains_key(&inode_id) { @@ -350,6 +371,19 @@ impl MountFS { self.namespace.init(namespace); } + /// 设置 bind mount 的 target 目录 inode。 + /// + /// 当 bind mount 被用作根文件系统时(例如容器场景), + /// root_inode() 应该返回这个 inode,而不是底层文件系统的根。 + pub fn set_bind_target_root(&self, target_root: Arc) { + *self.bind_target_root.write() = Some(target_root); + } + + /// 获取 bind mount 的 target 目录 inode(如果设置了) + pub fn bind_target_root(&self) -> Option> { + self.bind_target_root.read().clone() + } + pub fn fs_type(&self) -> &str { self.inner_filesystem.name() } @@ -381,6 +415,15 @@ impl MountFS { /// @brief 获取挂载点的文件系统的root inode pub fn mountpoint_root_inode(&self) -> Arc { + // 如果设置了 bind_target_root(用于 bind mount 场景), + // 则返回 bind target 目录,而不是底层文件系统的根。 + // 这对于容器场景很关键:bind mount /tmp/xxx/rootfs 后, + // 作为根文件系统时应该看到 rootfs 的内容。 + if let Some(bind_target) = self.bind_target_root() { + return bind_target; + } + + // 默认行为:返回底层文件系统的根 return Arc::new_cyclic(|self_ref| MountFSInode { inner_inode: self.inner_filesystem.root_inode(), mount_fs: self.self_ref.upgrade().unwrap(), @@ -407,14 +450,47 @@ impl MountFS { unregister_peer(group_id, &self.self_ref()); } - let r = self - .self_mountpoint() - .ok_or(SystemError::EINVAL)? - .do_umount(); + // 获取 self_mountpoint + let self_mp = self.self_mountpoint(); - self.self_mountpoint.write().take(); + if let Some(mp) = self_mp { + // 正常情况:有父挂载点 + let r = mp.do_umount(); + self.self_mountpoint.write().take(); + return r; + } - return r; + // 特殊情况:self_mountpoint 为 None,说明这曾经是根文件系统 + // 需要检查是否仍然是当前 namespace 的根 + // 如果 pivot_root 已经切换到新的根,旧的根应该可以被卸载 + let current_ns = ProcessManager::current_mntns(); + let is_current_root = Arc::ptr_eq(current_ns.root_mntfs(), &self.self_ref()); + + if is_current_root { + // 仍然是当前根,不能卸载 + return Err(SystemError::EINVAL); + } + + // 不是当前根,说明是 pivot_root 后的旧根,可以卸载 + // 这种情况下,我们只需要清理挂载列表中的记录 + // 不需要调用 do_umount(因为没有父挂载点) + // log::debug!( + // "[MountFS::umount] unmounting old root mount id={:?}", + // self.mount_id() + // ); + + // 从当前 namespace 的挂载列表中移除 + // 首先需要找到这个挂载的路径 + let mount_list = current_ns.mount_list(); + if let Some(mount_path) = mount_list.get_mount_path_by_mountfs(&self.self_ref()) { + // log::debug!( + // "[MountFS::umount] removing old root from mount list: {:?}", + // mount_path + // ); + current_ns.remove_mount(mount_path.as_str()); + } + + Ok(self.self_ref()) } } @@ -428,6 +504,11 @@ impl Drop for MountFS { } impl MountFSInode { + /// 获取当前 inode 所在的 MountFS + pub fn mount_fs(&self) -> &Arc { + &self.mount_fs + } + /// @brief 用Arc指针包裹MountFSInode对象。 /// 本函数的主要功能为,初始化MountFSInode对象中的自引用Weak指针 /// 本函数只应在构造器中被调用 @@ -635,6 +716,19 @@ impl MountFSInode { self_ref: self_ref.clone(), }) } + + /// 创建一个新的 MountFSInode + /// + /// # 参数 + /// - inner_inode: 底层文件系统的 inode + /// - mount_fs: 所属的 MountFS + pub fn new(inner_inode: Arc, mount_fs: Arc) -> Arc { + Arc::new_cyclic(|self_ref| MountFSInode { + inner_inode, + mount_fs, + self_ref: self_ref.clone(), + }) + } } impl IndexNode for MountFSInode { @@ -1325,6 +1419,29 @@ impl MountList { .get(mountfs) .and_then(|ino| inner.ino2mp.get(ino).cloned()) } + + /// 根据文件系统查找对应的 MountFS + /// 用于 pivot_root 等场景,需要从底层 inode 找到其所在的 MountFS + pub fn find_mount_by_fs(&self, fs: &Arc) -> Option> { + let inner = self.inner.read(); + // 遍历所有挂载点,找到文件系统相同的 MountFS + for (_path, stack) in inner.mounts.iter() { + if let Some(record) = stack.last() { + // 比较 Arc 指针是否相同 + let inner_fs = record.fs.inner_filesystem(); + // 首先尝试直接比较 Arc 指针 + if Arc::ptr_eq(&inner_fs, fs) { + return Some(record.fs.clone()); + } + // 如果指针不同但文件系统类型相同,也返回 + // (处理 bind mount 等情况) + if inner_fs.name() == fs.name() { + return Some(record.fs.clone()); + } + } + } + None + } } impl Debug for MountList { diff --git a/kernel/src/filesystem/vfs/syscall/mod.rs b/kernel/src/filesystem/vfs/syscall/mod.rs index 4d02f5271f..a5631614b4 100644 --- a/kernel/src/filesystem/vfs/syscall/mod.rs +++ b/kernel/src/filesystem/vfs/syscall/mod.rs @@ -35,6 +35,7 @@ mod sys_lseek; mod sys_mkdirat; pub mod sys_mknodat; mod sys_openat; +mod sys_pivot_root; #[cfg(target_arch = "x86_64")] mod sys_poll; mod sys_ppoll; diff --git a/kernel/src/filesystem/vfs/syscall/sys_mount.rs b/kernel/src/filesystem/vfs/syscall/sys_mount.rs index abfda513be..62f0a4a1ff 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_mount.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_mount.rs @@ -4,7 +4,7 @@ use crate::{ arch::{interrupt::TrapFrame, syscall::nr::SYS_MOUNT}, filesystem::vfs::{ fcntl::AtFlags, - mount::{is_mountpoint_root, MountFlags}, + mount::{is_mountpoint_root, MountFSInode, MountFlags}, produce_fs, utils::user_path_at, FileType, IndexNode, InodeId, MountFS, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, @@ -22,10 +22,10 @@ use crate::{ }, }; use alloc::string::String; +use alloc::string::ToString; use alloc::sync::Arc; use alloc::vec::Vec; use system_error::SystemError; - /// #挂载文件系统 /// /// 用于挂载文件系统,目前仅支持ramfs挂载 @@ -162,6 +162,13 @@ pub fn do_mount( data: Option, mount_flags: MountFlags, ) -> Result<(), SystemError> { + // log::info!( + // "[do_mount] source={:?}, target={:?}, filesystemtype={:?}, flags={:?}", + // source, + // target, + // filesystemtype, + // mount_flags + // ); let (current_node, rest_path) = user_path_at( &ProcessManager::current_pcb(), AtFlags::AT_FDCWD.bits(), @@ -232,8 +239,8 @@ fn path_mount( if flags.intersection(MountFlags::REMOUNT | MountFlags::BIND) == (MountFlags::REMOUNT | MountFlags::BIND) { - log::warn!("todo: reconfigure mnt"); - return Err(SystemError::ENOSYS); + // MS_REMOUNT | MS_BIND: 修改已存在挂载的标志,不创建新挂载 + return do_reconfigure_mnt(target_inode, mnt_flags); } if flags.contains(MountFlags::REMOUNT) { @@ -258,6 +265,68 @@ fn path_mount( return do_new_mount(source, target_inode, filesystemtype, data, mnt_flags).map(|_| ()); } +/// 处理 MS_REMOUNT | MS_BIND 情况 +/// +/// 这用于修改已存在挂载的挂载标志(如只读状态),而不改变挂载本身。 +/// 参考 Linux 的 do_reconfigure_mnt 实现。 +fn do_reconfigure_mnt( + target_inode: Arc, + new_flags: MountFlags, +) -> Result<(), SystemError> { + use crate::filesystem::vfs::mount::MountFSInode; + + log::debug!("[do_reconfigure_mnt] new_flags={:?}", new_flags); + + // 获取目标 inode 对应的 MountFS + let mount_fs = + if let Some(mountfs_inode) = target_inode.as_any_ref().downcast_ref::() { + mountfs_inode.mount_fs().clone() + } else { + // 如果不是 MountFSInode,尝试通过文件系统查找 + let mnt_ns = ProcessManager::current_mntns(); + let inode_fs = target_inode.fs(); + + // 尝试通过文件系统查找对应的 MountFS + if let Some(mount_fs) = mnt_ns.mount_list().find_mount_by_fs(&inode_fs) { + mount_fs + } else { + // 作为最后的尝试,通过文件系统名称匹配 + let mount_list = mnt_ns.mount_list().clone_inner(); + let mut found = None; + for (_path, mnt_fs) in mount_list.iter() { + if mnt_fs.fs_type() == inode_fs.name() { + found = Some(mnt_fs.clone()); + break; + } + } + found.ok_or(SystemError::EINVAL)? + } + }; + + // 修改挂载标志 + // 注意:我们保留一些不应该被修改的标志(如 propagation 相关的) + let current_flags = mount_fs.mount_flags(); + + // 保留 propagation 标志(SHARED, PRIVATE, SLAVE, UNBINDABLE) + let propagation_flags = + MountFlags::SHARED | MountFlags::PRIVATE | MountFlags::SLAVE | MountFlags::UNBINDABLE; + let current_prop = current_flags & propagation_flags; + + // 合并新的标志和保留的 propagation 标志 + let merged_flags = new_flags | current_prop; + + log::debug!( + "[do_reconfigure_mnt] current_flags={:?}, new_flags={:?}, merged_flags={:?}", + current_flags, + new_flags, + merged_flags + ); + + mount_fs.set_mount_flags(merged_flags); + + Ok(()) +} + fn do_new_mount( source: Option, mut target_inode: Arc, @@ -265,8 +334,18 @@ fn do_new_mount( data: Option, mount_flags: MountFlags, ) -> Result, SystemError> { + let _target_path = target_inode + .absolute_path() + .unwrap_or_else(|_| "?".to_string()); let fs_type_str = filesystemtype.ok_or(SystemError::EINVAL)?; let source = source.ok_or(SystemError::EINVAL)?; + // log::info!( + // "[do_new_mount] source={}, fs_type={}, target={}, flags={:?}", + // source, + // fs_type_str, + // target_path, + // mount_flags + // ); let fs = produce_fs(&fs_type_str, data.as_deref(), &source).inspect_err(|e| { log::error!("Failed to produce filesystem: {:?}", e); })?; @@ -346,8 +425,17 @@ fn do_bind_mount( AtFlags::AT_FDCWD.bits(), &source_path, )?; + log::debug!( + "[do_bind_mount] current_node fs={}, rest_path={}", + current_node.fs().name(), + rest_path + ); let source_inode = current_node.lookup_follow_symlink(&rest_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + log::debug!( + "[do_bind_mount] source_inode fs={}", + source_inode.fs().name() + ); // Both source and target must be directories if source_inode.metadata()?.file_type != FileType::Dir { @@ -359,6 +447,9 @@ fn do_bind_mount( // Get the source's filesystem let source_fs = source_inode.fs(); + let _source_path_res = source_inode + .absolute_path() + .unwrap_or_else(|_| source_path.clone()); // Check if source is on a MountFS let source_mfs = source_fs.clone().downcast_arc::(); @@ -377,11 +468,57 @@ fn do_bind_mount( // Get the inner filesystem for mounting let inner_fs = source_mfs .map(|mfs| mfs.inner_filesystem()) - .unwrap_or(source_fs); + .unwrap_or(source_fs.clone()); // Use the target_inode.mount() method which handles all the mounting logic // This properly creates a new MountFS and registers it + // log::info!( + // "[do_bind_mount] source_path={:?}, source_fs={}, target_path={:?}", + // source_path_res, + // source_fs.name(), + // target_inode + // .absolute_path() + // .unwrap_or_else(|_| "?".to_string()) + // ); let target_mfs = target_inode.mount(inner_fs, MountFlags::empty())?; + // log::info!( + // "[do_bind_mount] created MountFS id={:?}, fs={}", + // target_mfs.mount_id(), + // target_mfs.fs_type() + // ); + + // 设置 bind_target_root + // DragonOS 的 bind mount 与 Linux 有差异: + // - Linux: bind mount 创建的挂载以 bind target 目录为根 + // - DragonOS: bind mount 包装整个底层文件系统,MountFS::root_inode() 返回底层文件系统的根 + // + // 为了支持容器场景,我们需要告诉 MountFS:当这个 mount 被用作根文件系统时, + // 应该返回 bind target 目录的内容,而不是底层文件系统的根。 + // + // 我们创建一个 MountFSInode 来包装 source_inode(bind target 目录), + // 并将其设置为 target_mfs 的 bind_target_root。 + let bind_target_root_inode = MountFSInode::new(source_inode.clone(), target_mfs.clone()); + target_mfs.set_bind_target_root(bind_target_root_inode); + // log::info!( + // "[do_bind_mount] set bind_target_root for MountFS id={:?}", + // target_mfs.mount_id() + // ); + + // 特殊处理:如果 bind 挂载到 "/",需要更新 namespace 的根 + let target_path = target_inode + .absolute_path() + .unwrap_or_else(|_| "?".to_string()); + if target_path == "/" { + // log::info!("[do_bind_mount] binding to root, updating namespace root"); + let mnt_ns = ProcessManager::current_mntns(); + unsafe { + // 这会替换 namespace 的根挂载点 + mnt_ns.force_change_root_mountfs(target_mfs.clone()); + } + // 同时更新当前进程的根目录 + let pcb = ProcessManager::current_pcb(); + pcb.fs_struct_mut().set_root(mnt_ns.root_inode()); + } // If MS_REC is set, recursively bind all submounts from source to target if flags.contains(MountFlags::REC) { diff --git a/kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs b/kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs new file mode 100644 index 0000000000..643dbee40f --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs @@ -0,0 +1,428 @@ +//! System call handler for pivot_root(2). +//! +//! Linux 语义要点: +//! - pivot_root() 将当前进程的根文件系统切换到 new_root,并将原根文件系统挂载到 put_old +//! - new_root 和 put_old 必须在不同的挂载点 +//! - put_old 必须在 new_root 之下 +//! - new_root 和 put_old 都必须是目录 +//! - 当前工作目录不能在 put_old 中 +//! - 需要 CAP_SYS_CHROOT 权限 + +use crate::arch::interrupt::TrapFrame; +use crate::arch::syscall::nr::SYS_PIVOT_ROOT; +use crate::filesystem::vfs::mount::{MountFS, MountFSInode}; +use crate::filesystem::vfs::permission::PermissionMask; +use crate::filesystem::vfs::{ + utils::user_path_at, FileType, IndexNode, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, +}; +use crate::process::cred::CAPFlags; +use crate::process::ProcessManager; +use crate::syscall::table::{FormattedSyscallParam, Syscall}; +use crate::syscall::user_access::vfs_check_and_clone_cstr; +use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::vec::Vec; +use system_error::SystemError; + +/// is_ancestor() 遍历父目录的最大深度 +/// +/// 用于防止在文件系统损坏或存在循环引用时出现无限循环。 +/// 1000 层对于正常使用场景来说绰绰有余(Linux 默认的链接数限制也远低于此)。 +const MAX_ANCESTOR_TRAVERSAL_DEPTH: u32 = 1000; + +pub struct SysPivotRootHandle; + +impl Syscall for SysPivotRootHandle { + fn num_args(&self) -> usize { + 2 + } + + fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result { + let new_root_ptr = Self::new_root(args); + let put_old_ptr = Self::put_old(args); + + if new_root_ptr.is_null() || put_old_ptr.is_null() { + return Err(SystemError::EFAULT); + } + + // 解析路径 + let new_root_path = vfs_check_and_clone_cstr(new_root_ptr, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let put_old_path = vfs_check_and_clone_cstr(put_old_ptr, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + + let new_root_path = new_root_path.trim(); + let put_old_path = put_old_path.trim(); + + // log::info!( + // "[pivot_root] called with new_root='{}', put_old='{}'", + // new_root_path, + // put_old_path + // ); + + if new_root_path.is_empty() || put_old_path.is_empty() { + log::error!("[pivot_root] empty path"); + return Err(SystemError::ENOENT); + } + + let pcb = ProcessManager::current_pcb(); + let cred = pcb.cred(); + + // 权限检查:需要 CAP_SYS_CHROOT + if !cred.has_capability(CAPFlags::CAP_SYS_CHROOT) { + log::error!("[pivot_root] permission denied: no CAP_SYS_CHROOT"); + return Err(SystemError::EPERM); + } + + // 解析 new_root 路径 + let (new_root_inode_begin, new_root_resolved) = user_path_at( + &pcb, + crate::filesystem::vfs::fcntl::AtFlags::AT_FDCWD.bits(), + new_root_path, + )?; + let new_root_inode = new_root_inode_begin + .lookup_follow_symlink(&new_root_resolved, VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + + // log::info!( + // "[pivot_root] new_root_inode type: {:?}, fs: {}", + // new_root_inode.type_id(), + // new_root_inode.fs().name() + // ); + + // 验证 new_root 是目录 + let new_root_meta = new_root_inode.metadata()?; + if new_root_meta.file_type != FileType::Dir { + log::error!( + "[pivot_root] new_root is not a directory: {:?}", + new_root_meta.file_type + ); + return Err(SystemError::ENOTDIR); + } + + // 目录搜索权限 + cred.inode_permission(&new_root_meta, PermissionMask::MAY_EXEC.bits())?; + + // 获取 new_root 对应的 MountFS + let new_root_mntfs = match Self::get_mountfs(&new_root_inode) { + Ok(mntfs) => { + log::info!("[pivot_root] new_root_mntfs: id={:?}", mntfs.mount_id()); + mntfs + } + Err(e) => { + log::error!("[pivot_root] failed to get new_root MountFS: {:?}", e); + return Err(e); + } + }; + + // 解析 put_old 路径(相对于 new_root) + let (put_old_inode_begin, put_old_resolved) = user_path_at( + &pcb, + crate::filesystem::vfs::fcntl::AtFlags::AT_FDCWD.bits(), + put_old_path, + )?; + let put_old_inode = put_old_inode_begin + .lookup_follow_symlink(&put_old_resolved, VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + + // log::info!( + // "[pivot_root] put_old_inode type: {:?}, fs: {}", + // put_old_inode.type_id(), + // put_old_inode.fs().name() + // ); + + // 验证 put_old 是目录 + let put_old_meta = put_old_inode.metadata()?; + if put_old_meta.file_type != FileType::Dir { + log::error!( + "[pivot_root] put_old is not a directory: {:?}", + put_old_meta.file_type + ); + return Err(SystemError::ENOTDIR); + } + + // 目录搜索权限 + cred.inode_permission(&put_old_meta, PermissionMask::MAY_EXEC.bits())?; + + // 获取 put_old 对应的 MountFS + let put_old_mntfs = match Self::get_mountfs(&put_old_inode) { + Ok(mntfs) => { + log::info!("[pivot_root] put_old_mntfs: id={:?}", mntfs.mount_id()); + mntfs + } + Err(e) => { + log::error!("[pivot_root] failed to get put_old MountFS: {:?}", e); + return Err(e); + } + }; + + // 验证 new_root 和 put_old 在不同的挂载点 + // 注意:如果 new_root 和 put_old 是同一个 inode(如 "." 和 "."),Linux 会特殊处理 + let is_same_inode = new_root_meta.inode_id == put_old_meta.inode_id + && Arc::ptr_eq(&new_root_inode.fs(), &put_old_inode.fs()); + + if is_same_inode { + // log::info!("[pivot_root] new_root and put_old are the same inode, special handling"); + + // Linux 的 pivot_root 允许 new_root 和 put_old 相同 + // 在这种情况下,我们需要: + // 1. 将 new_root 设置为新的根 + // 2. 旧根会自动被隐藏(不创建 put_old 挂载点) + // 简化实现:我们只需要改变根挂载点 + } else if Arc::ptr_eq(&new_root_mntfs, &put_old_mntfs) { + log::error!("[pivot_root] new_root and put_old are on the same mount point but different inodes"); + return Err(SystemError::EINVAL); + } else { + // 验证 put_old 是 new_root 的后代(即 put_old 在 new_root 之下) + let is_descendant = Self::is_ancestor(&new_root_inode, &put_old_inode)?; + if !is_descendant { + log::error!("[pivot_root] put_old is not a descendant of new_root"); + return Err(SystemError::EINVAL); + } + } + + // 验证当前工作目录不在 put_old 中 + // 只有当 new_root 和 put_old 不同时才需要检查 + if !is_same_inode { + let cwd = pcb.fs_struct().pwd(); + let cwd_in_putold = Self::is_ancestor(&put_old_inode, &cwd)?; + if cwd_in_putold { + log::error!("[pivot_root] current working directory is inside put_old"); + return Err(SystemError::EBUSY); + } + } + + // 执行 pivot_root + // 获取当前进程的挂载命名空间 + let mnt_ns = ProcessManager::current_mntns(); + + // 1. 保存旧的根 MountFS(用于后续设置当前目录) + let _old_root_mntfs = mnt_ns.root_mntfs().clone(); + let old_root_inode = mnt_ns.root_inode(); + + // log::info!( + // "[pivot_root] changing root mountfs from {:?} to {:?}", + // old_root_mntfs.mount_id(), + // new_root_mntfs.mount_id() + // ); + + // 2. 更新挂载命名空间的根 + // 使用 force_change_root_mountfs 来改变根挂载点 + // 注意:这是一个简化的实现,实际 Linux 的 pivot_root 更复杂 + unsafe { + mnt_ns.force_change_root_mountfs(new_root_mntfs.clone()); + } + + // 3. 更新进程的根目录 + // 关键修复:使用 new_root_inode 而不是 new_root_mntfs.root_inode() + // + // 原因:DragonOS 的 bind mount 实现与 Linux 有差异。 + // 在 Linux 中,bind mount 创建的挂载点以源目录为根,但在 DragonOS 中, + // MountFS 包装的是整个底层文件系统,所以 MountFS::root_inode() 返回的是 + // 底层文件系统的根目录,而不是 bind source 目录的内容。 + // + // 因此,我们需要直接使用 new_root_inode(bind source 目录的 inode)作为新的根目录。 + // 这样容器启动时能看到 rootfs 的内容(bin, lib 等),而不是底层文件系统的根目录。 + pcb.fs_struct_mut().set_root(new_root_inode.clone()); + + // 4. 关键步骤:当 new_root 和 put_old 相同时, + // 需要将当前工作目录设置为旧的根目录(以便后续 umount2 可以卸载它) + // 这样 `umount2(".")` 才能正确工作 + if is_same_inode { + // log::info!("[pivot_root] setting cwd to old root for umount2"); + pcb.fs_struct_mut().set_pwd(old_root_inode); + } + + // log::info!( + // "[pivot_root] SUCCESS: new_root='{}', put_old='{}', new_root_mntfs={:?}", + // new_root_path, + // put_old_path, + // new_root_mntfs.mount_id() + // ); + + Ok(0) + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("new_root", format!("{:#x}", Self::new_root(args) as usize)), + FormattedSyscallParam::new("put_old", format!("{:#x}", Self::put_old(args) as usize)), + ] + } +} + +impl SysPivotRootHandle { + fn new_root(args: &[usize]) -> *const u8 { + args[0] as *const u8 + } + + fn put_old(args: &[usize]) -> *const u8 { + args[1] as *const u8 + } + + /// 获取 inode 对应的 MountFS + /// 如果 inode 不是 MountFSInode,尝试通过其他方式获取其所在的 MountFS + fn get_mountfs(inode: &Arc) -> Result, SystemError> { + log::debug!("[pivot_root] get_mountfs: inode type={:?}", inode.type_id()); + + // 尝试将 inode 转换为 MountFSInode + if let Some(mountfs_inode) = inode.as_any_ref().downcast_ref::() { + log::debug!("[pivot_root] get_mountfs: found MountFSInode"); + return Ok(mountfs_inode.mount_fs().clone()); + } + + // 如果不是 MountFSInode,尝试从文件系统获取其挂载信息 + log::debug!( + "[pivot_root] get_mountfs: not a MountFSInode, trying to find containing mount" + ); + + // 获取当前进程的挂载命名空间 + let mnt_ns = ProcessManager::current_mntns(); + + // 获取 inode 所在的文件系统 + let inode_fs = inode.fs(); + + // 关键修复:遍历所有挂载点,找到最精确的匹配 + // 在容器场景中,我们需要找到 bind mount 创建的 MountFS + // + // 策略: + // 1. 优先选择设置了 bind_target_root 的 MountFS(这是 bind mount 的标记) + // 2. 在设置了 bind_target_root 的挂载点中,选择路径最短的(最上层的) + // 3. 如果没有找到,则选择路径最短的(最上层的)作为后备 + let mount_list = mnt_ns.mount_list().clone_inner(); + let mut candidates: Vec<(Arc, usize, String)> = Vec::new(); // (MountFS, 路径长度, 路径字符串) + + for (path, mnt_fs) in mount_list.iter() { + let inner_fs = mnt_fs.inner_filesystem(); + // 检查这个 MountFS 是否包装了 inode 所在的文件系统 + if Arc::ptr_eq(&inner_fs, &inode_fs) || inner_fs.name() == inode_fs.name() { + let path_str = path.as_str().to_string(); + let path_len = path_str.len(); + candidates.push((mnt_fs.clone(), path_len, path_str)); + log::debug!("[pivot_root] get_mountfs: found candidate MountFS for path={:?}, id={:?}, len={}", + path, mnt_fs.mount_id(), path_len); + } + } + + // 首先尝试找到设置了 bind_target_root 的挂载点 + // 这些是 bind mount 创建的,适合用作容器的根文件系统 + let mut bind_mount_candidates: Vec<(Arc, usize, String)> = Vec::new(); + + for (mnt_fs, path_len, path_str) in candidates.iter() { + if mnt_fs.bind_target_root().is_some() { + bind_mount_candidates.push((mnt_fs.clone(), *path_len, path_str.clone())); + log::debug!( + "[pivot_root] get_mountfs: found bind mount candidate id={:?}, path={:?}", + mnt_fs.mount_id(), + path_str + ); + } + } + + // 如果找到了设置了 bind_target_root 的挂载点,在其中选择路径最短的 + if !bind_mount_candidates.is_empty() { + bind_mount_candidates.sort_by(|a, b| a.1.cmp(&b.1)); + if let Some((mnt_fs, _, path_str)) = bind_mount_candidates.first() { + log::debug!( + "[pivot_root] get_mountfs: returning bind mount match id={:?}, path={:?}", + mnt_fs.mount_id(), + path_str + ); + return Ok(mnt_fs.clone()); + } + } + + // 如果没有找到 bind mount,选择路径最短的(最上层的)作为后备 + candidates.sort_by(|a, b| a.1.cmp(&b.1)); + if let Some((mnt_fs, _, path_str)) = candidates.first() { + log::debug!( + "[pivot_root] get_mountfs: returning shortest match id={:?}, path={:?}", + mnt_fs.mount_id(), + path_str + ); + return Ok(mnt_fs.clone()); + } + + // 如果找不到合适的,尝试通过文件系统查找对应的 MountFS(后备方案) + if let Some(mount_fs) = mnt_ns.mount_list().find_mount_by_fs(&inode_fs) { + log::debug!("[pivot_root] get_mountfs: found MountFS by filesystem lookup (fallback)"); + return Ok(mount_fs); + } + + // 如果找不到,尝试通过文件系统名称比较来查找 + let root_inode = mnt_ns.root_inode(); + let root_fs = root_inode.fs(); + + // 如果 inode 的文件系统和根文件系统类型相同(名称相同) + if inode_fs.name() == root_fs.name() { + log::debug!("[pivot_root] get_mountfs: fs name matches root, returning root_mntfs"); + return Ok(mnt_ns.root_mntfs().clone()); + } + + // 如果还是找不到,返回错误 + log::error!( + "[pivot_root] get_mountfs: cannot find MountFS for inode type={:?}, fs={}", + inode.type_id(), + inode_fs.name() + ); + Err(SystemError::EINVAL) + } + + /// 检查 target_inode 是否是 ancestor_inode 的后代 + /// + /// 通过向上遍历 target_inode 的父目录链,检查是否能到达 ancestor_inode + fn is_ancestor( + ancestor_inode: &Arc, + target_inode: &Arc, + ) -> Result { + // 获取 ancestor 的 inode id + let ancestor_id = ancestor_inode.metadata()?.inode_id; + let ancestor_fs = ancestor_inode.fs(); + + log::debug!( + "[pivot_root] is_ancestor: ancestor_id={:?}, ancestor_fs={}", + ancestor_id, + ancestor_fs.name() + ); + + // 从 target_inode 开始向上遍历 + let mut current = target_inode.clone(); + + // 最多向上遍历 MAX_ANCESTOR_TRAVERSAL_DEPTH 层,防止循环引用 + for i in 0..MAX_ANCESTOR_TRAVERSAL_DEPTH { + let current_meta = current.metadata()?; + + // 检查是否到达 ancestor + if current_meta.inode_id == ancestor_id && Arc::ptr_eq(¤t.fs(), &ancestor_fs) { + // 找到了 ancestor,说明 target 是 ancestor 的后代 + log::debug!("[pivot_root] is_ancestor: found ancestor after {} steps", i); + return Ok(true); + } + + // 尝试向上移动到父目录 + match current.parent() { + Ok(parent) => { + // 如果 parent 就是 current 本身,说明已经到达根目录 + if Arc::ptr_eq(&parent, ¤t) { + log::debug!("[pivot_root] is_ancestor: reached root (parent==self)"); + break; + } + current = parent; + } + Err(e) => { + // 没有父目录了,到达根目录 + log::debug!("[pivot_root] is_ancestor: no parent, err={:?}", e); + break; + } + } + } + + // 最后再检查一次根目录是否是 ancestor + let root_meta = current.metadata()?; + let result = root_meta.inode_id == ancestor_id && Arc::ptr_eq(¤t.fs(), &ancestor_fs); + log::debug!("[pivot_root] is_ancestor: final check result={}", result); + Ok(result) + } +} + +syscall_table_macros::declare_syscall!(SYS_PIVOT_ROOT, SysPivotRootHandle); diff --git a/kernel/src/filesystem/vfs/syscall/sys_umount2.rs b/kernel/src/filesystem/vfs/syscall/sys_umount2.rs index ce515fc886..db6234ce22 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_umount2.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_umount2.rs @@ -103,12 +103,73 @@ pub fn do_umount2( base }; + // log::info!("[umount2] target='{}', path='{}'", target, path); + + // 特殊处理:如果是卸载当前目录 "." + // 需要检查是否是 pivot_root 后的情况,此时需要卸载旧的根挂载点 + if target == "." { + let mnt_ns = ProcessManager::current_mntns(); + + // 首先检查是否有旧的根挂载点(pivot_root 保存的) + if let Some(old_root_mntfs) = mnt_ns.old_root_mntfs() { + // log::info!( + // "[umount2] found old_root_mntfs={:?}", + // old_root_mntfs.mount_id() + // ); + + // 直接调用 MountFS::umount(),它会处理所有必要的清理工作 + // 包括从挂载列表中移除 + // log::info!("[umount2] trying to umount old_root_mntfs directly"); + old_root_mntfs.umount()?; + + // 清除旧的根挂载点记录 + ProcessManager::current_mntns().clear_old_root_mntfs(); + return Ok(old_root_mntfs); + } + + // 如果没有旧的根挂载点,尝试常规方法 + // log::info!("[umount2] no old_root_mntfs, trying normal umount"); + let cwd = work; + // log::info!("[umount2] cwd inode_id={:?}", cwd.metadata()?.inode_id); + + // 尝试通过 inode_id 在挂载点列表中查找 + if let Some(mount_path) = mnt_ns + .mount_list() + .get_mount_path_by_ino(cwd.metadata()?.inode_id) + { + // log::info!("[umount2] found mount path by inode: {:?}", mount_path); + let result = mnt_ns.remove_mount(mount_path.as_str()); + if let Some(fs) = result { + fs.umount()?; + return Ok(fs); + } + } + + // 如果通过 inode_id 找不到,尝试通过文件系统查找 + let cwd_fs = cwd.fs(); + if let Some(mount_fs) = mnt_ns.mount_list().find_mount_by_fs(&cwd_fs) { + // log::info!("[umount2] found mount_fs by fs: {:?}", mount_fs.mount_id()); + // 通过 mount_fs 反向查找挂载路径 + if let Some(mount_path) = mnt_ns.mount_list().get_mount_path_by_mountfs(&mount_fs) { + // log::info!("[umount2] found mount path by mount_fs: {:?}", mount_path); + let result = mnt_ns.remove_mount(mount_path.as_str()); + if let Some(fs) = result { + fs.umount()?; + return Ok(fs); + } + } + } + + log::error!("[umount2] cannot find mount for current directory"); + } + let result = ProcessManager::current_mntns().remove_mount(&path); if let Some(fs) = result { // Todo: 占用检测 fs.umount()?; return Ok(fs); } + log::error!("[umount2] remove_mount failed for path='{}'", path); return Err(SystemError::EINVAL); } diff --git a/kernel/src/filesystem/vfs/utils.rs b/kernel/src/filesystem/vfs/utils.rs index 73c9b5583a..00ed340f4b 100644 --- a/kernel/src/filesystem/vfs/utils.rs +++ b/kernel/src/filesystem/vfs/utils.rs @@ -57,7 +57,9 @@ pub fn user_path_at( // 绝对路径:从进程 root 开始 if path.as_bytes()[0] == b'/' { - return Ok((pcb.fs_struct().root(), ret_path)); + let root = pcb.fs_struct().root(); + // log::debug!("[user_path_at] absolute path '{}', root fs={}", path, root.fs().name()); + return Ok((root, ret_path)); } // 相对路径:dirfd 优先,否则用 cwd diff --git a/kernel/src/process/namespace/mnt.rs b/kernel/src/process/namespace/mnt.rs index bdf0e14b3f..6574fab0fc 100644 --- a/kernel/src/process/namespace/mnt.rs +++ b/kernel/src/process/namespace/mnt.rs @@ -45,6 +45,8 @@ pub struct MntNamespace { _user_ns: Arc, root_mountfs: Arc, inner: SpinLock, + // 旧的根挂载点,用于 pivot_root 后的 umount + old_root_mntfs: SpinLock>>, } pub struct InnerMntNamespace { @@ -80,6 +82,7 @@ impl MntNamespace { mount_list, _dead: false, }), + old_root_mntfs: SpinLock::new(None), }); ramfs.set_namespace(Arc::downgrade(&result)); @@ -97,11 +100,18 @@ impl MntNamespace { let inner_guard = self.inner.lock(); let ptr = self as *const Self as *mut Self; let self_mut = (ptr).as_mut().unwrap(); + + // 保存旧的根挂载点,以便后续可能需要卸载它 + let old_root = self_mut.root_mountfs.clone(); self_mut.root_mountfs = new_root.clone(); let (path, _, _) = inner_guard.mount_list.get_mount_point("/").unwrap(); inner_guard.mount_list.insert(None, path, new_root); + // 将旧的根挂载点保存到一个特殊位置(使用特殊路径) + // 这样 umount2 可以通过这个路径找到并卸载它 + *self_mut.old_root_mntfs.lock() = Some(old_root); + // update mount list ino } @@ -118,6 +128,7 @@ impl MntNamespace { _dead: false, mount_list: MountList::new(), }), + old_root_mntfs: SpinLock::new(None), }); new_root.set_namespace(Arc::downgrade(&result)); @@ -154,9 +165,11 @@ impl MntNamespace { // Return the current mount namespace if CLONE_NEWNS is not set return Ok(self.self_ref.upgrade().unwrap()); } + // log::info!("[copy_mnt_ns] Creating new mount namespace, copying from current ns"); let inner = self.inner.lock(); let old_root_mntfs = self.root_mntfs().clone(); + // log::info!("[copy_mnt_ns] old_root: fs_type={}, fs={}", old_root_mntfs.fs_type(), old_root_mntfs.name()); let mut queue: Vec = Vec::new(); // 由于root mntfs比较特殊,因此单独复制。 @@ -188,6 +201,7 @@ impl MntNamespace { } } // 将root mntfs下的所有挂载点复制到新的mntns中 + // log::info!("[copy_mnt_ns] old_root has {} mountpoints", old_root_mntfs.mountpoints().len()); for (ino, mfs) in old_root_mntfs.mountpoints().iter() { let mount_path = inner .mount_list @@ -200,6 +214,13 @@ impl MntNamespace { ); }) .unwrap(); + // log::info!( + // "[copy_mnt_ns] copying mount: ino={:?}, path={:?}, fs={}, fs_type={}", + // ino, + // mount_path, + // mfs.name(), + // mfs.fs_type() + // ); queue.push(MountFSCopyInfo { old_mount_fs: mfs.clone(), @@ -282,6 +303,21 @@ impl MntNamespace { self.inner.lock().mount_list.clone() } + /// 获取旧的根挂载点(用于 pivot_root 后的 umount) + pub fn old_root_mntfs(&self) -> Option> { + self.old_root_mntfs.lock().clone() + } + + /// 设置旧的根挂载点(用于 pivot_root) + pub fn set_old_root_mntfs(&self, old_root: Arc) { + *self.old_root_mntfs.lock() = Some(old_root); + } + + /// 清除旧的根挂载点 + pub fn clear_old_root_mntfs(&self) { + *self.old_root_mntfs.lock() = None; + } + pub fn remove_mount(&self, mount_path: &str) -> Option> { self.inner.lock().mount_list.remove(mount_path) } From 51633a5ba7bb663f98b6961dc8becff7427a550a Mon Sep 17 00:00:00 2001 From: sparkzky Date: Wed, 11 Feb 2026 23:33:14 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor(pivot=5Froot):=20=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=20PR=20=E5=AE=A1=E6=9F=A5=E5=8F=8D=E9=A6=88=E6=94=B9?= =?UTF-8?q?=E8=BF=9B=E4=BB=A3=E7=A0=81=E8=B4=A8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 消除代码重复:使用 utils::is_ancestor_limited() 替代 sys_pivot_root.rs 中的本地实现 - 简化 get_mountfs():提取 find_bind_mount() 辅助函数 - 为 unsafe 操作添加详细的安全文档: MountFS::set_mount_flags() 和 MntNamespace::force_change_root_mountfs() - 更新 is_ancestor():同时检查文件系统指针和 inode ID - 导出 MAX_ANCESTOR_TRAVERSAL_DEPTH 常量以供复用 Signed-off-by: sparkzky --- kernel/src/filesystem/vfs/mount.rs | 20 +- .../filesystem/vfs/syscall/sys_pivot_root.rs | 194 +++++------------- kernel/src/filesystem/vfs/utils.rs | 89 +++++++- kernel/src/process/namespace/mnt.rs | 38 +++- 4 files changed, 196 insertions(+), 145 deletions(-) diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 2f95c556f9..393366e60b 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -332,7 +332,25 @@ impl MountFS { /// 设置挂载标志 /// - /// 用于 MS_REMOUNT | MS_BIND 场景,修改已存在挂载的标志(如只读状态) + /// 用于 MS_REMOUNT | MS_BIND 场景,修改已存在挂载的标志(如只读状态)。 + /// + /// # Safety + /// + /// 此函数使用 unsafe 代码来修改 `MountFS` 的不可变字段 `mount_flags`。 + /// 这样做是安全的,原因如下: + /// + /// 1. **并发安全**:调用者必须持有挂载命名空间的锁(如 `mount_list` 的锁), + /// 这确保了不会有多个线程同时修改同一个 MountFS 的标志。 + /// + /// 2. **不变量维护**:`mount_flags` 是一个简单的 `MountFlags` 位标志结构体, + /// 不包含任何引用或指针。修改它不会破坏其他字段的不变量。 + /// + /// 3. **原子性**:`MountFlags` 的赋值是原子操作(只是复制整数位标志), + /// 不会出现中间状态。 + /// + /// 4. **可见性**:由于锁的存在,修改对其他线程是可见的。 + /// + /// 注意:此函数应该只在挂载命名空间的锁保护下调用。 pub fn set_mount_flags(&self, flags: MountFlags) { // 使用 unsafe 来修改不可变字段 // 这是安全的,因为我们只修改挂载标志,且在挂载命名空间的锁保护下 diff --git a/kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs b/kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs index 643dbee40f..4d0ad1225f 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_pivot_root.rs @@ -7,29 +7,23 @@ //! - new_root 和 put_old 都必须是目录 //! - 当前工作目录不能在 put_old 中 //! - 需要 CAP_SYS_CHROOT 权限 - use crate::arch::interrupt::TrapFrame; use crate::arch::syscall::nr::SYS_PIVOT_ROOT; use crate::filesystem::vfs::mount::{MountFS, MountFSInode}; use crate::filesystem::vfs::permission::PermissionMask; use crate::filesystem::vfs::{ - utils::user_path_at, FileType, IndexNode, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, + utils::{is_ancestor_limited, user_path_at}, + FileType, IndexNode, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; use crate::process::cred::CAPFlags; +use crate::process::namespace::mnt::MntNamespace; use crate::process::ProcessManager; use crate::syscall::table::{FormattedSyscallParam, Syscall}; use crate::syscall::user_access::vfs_check_and_clone_cstr; -use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; use system_error::SystemError; -/// is_ancestor() 遍历父目录的最大深度 -/// -/// 用于防止在文件系统损坏或存在循环引用时出现无限循环。 -/// 1000 层对于正常使用场景来说绰绰有余(Linux 默认的链接数限制也远低于此)。 -const MAX_ANCESTOR_TRAVERSAL_DEPTH: u32 = 1000; - pub struct SysPivotRootHandle; impl Syscall for SysPivotRootHandle { @@ -174,7 +168,7 @@ impl Syscall for SysPivotRootHandle { return Err(SystemError::EINVAL); } else { // 验证 put_old 是 new_root 的后代(即 put_old 在 new_root 之下) - let is_descendant = Self::is_ancestor(&new_root_inode, &put_old_inode)?; + let is_descendant = is_ancestor_limited(&new_root_inode, &put_old_inode)?; if !is_descendant { log::error!("[pivot_root] put_old is not a descendant of new_root"); return Err(SystemError::EINVAL); @@ -185,7 +179,7 @@ impl Syscall for SysPivotRootHandle { // 只有当 new_root 和 put_old 不同时才需要检查 if !is_same_inode { let cwd = pcb.fs_struct().pwd(); - let cwd_in_putold = Self::is_ancestor(&put_old_inode, &cwd)?; + let cwd_in_putold = is_ancestor_limited(&put_old_inode, &cwd)?; if cwd_in_putold { log::error!("[pivot_root] current working directory is inside put_old"); return Err(SystemError::EBUSY); @@ -261,105 +255,42 @@ impl SysPivotRootHandle { } /// 获取 inode 对应的 MountFS - /// 如果 inode 不是 MountFSInode,尝试通过其他方式获取其所在的 MountFS + /// + /// 如果 inode 是 MountFSInode,直接返回其所在的 MountFS。 + /// 否则,通过挂载命名空间查找匹配的 MountFS。 + /// + /// 查找策略(按优先级排序): + /// 1. 直接从 MountFSInode 获取 + /// 2. 查找设置了 bind_target_root 的 MountFS(bind mount 标记) + /// 3. 通过文件系统指针查找 + /// 4. 通过文件系统名称查找(后备方案) fn get_mountfs(inode: &Arc) -> Result, SystemError> { - log::debug!("[pivot_root] get_mountfs: inode type={:?}", inode.type_id()); - - // 尝试将 inode 转换为 MountFSInode + // 1. 尝试直接从 MountFSInode 获取 if let Some(mountfs_inode) = inode.as_any_ref().downcast_ref::() { - log::debug!("[pivot_root] get_mountfs: found MountFSInode"); return Ok(mountfs_inode.mount_fs().clone()); } - // 如果不是 MountFSInode,尝试从文件系统获取其挂载信息 - log::debug!( - "[pivot_root] get_mountfs: not a MountFSInode, trying to find containing mount" - ); - - // 获取当前进程的挂载命名空间 let mnt_ns = ProcessManager::current_mntns(); - - // 获取 inode 所在的文件系统 let inode_fs = inode.fs(); - // 关键修复:遍历所有挂载点,找到最精确的匹配 - // 在容器场景中,我们需要找到 bind mount 创建的 MountFS - // - // 策略: - // 1. 优先选择设置了 bind_target_root 的 MountFS(这是 bind mount 的标记) - // 2. 在设置了 bind_target_root 的挂载点中,选择路径最短的(最上层的) - // 3. 如果没有找到,则选择路径最短的(最上层的)作为后备 - let mount_list = mnt_ns.mount_list().clone_inner(); - let mut candidates: Vec<(Arc, usize, String)> = Vec::new(); // (MountFS, 路径长度, 路径字符串) - - for (path, mnt_fs) in mount_list.iter() { - let inner_fs = mnt_fs.inner_filesystem(); - // 检查这个 MountFS 是否包装了 inode 所在的文件系统 - if Arc::ptr_eq(&inner_fs, &inode_fs) || inner_fs.name() == inode_fs.name() { - let path_str = path.as_str().to_string(); - let path_len = path_str.len(); - candidates.push((mnt_fs.clone(), path_len, path_str)); - log::debug!("[pivot_root] get_mountfs: found candidate MountFS for path={:?}, id={:?}, len={}", - path, mnt_fs.mount_id(), path_len); - } - } - - // 首先尝试找到设置了 bind_target_root 的挂载点 - // 这些是 bind mount 创建的,适合用作容器的根文件系统 - let mut bind_mount_candidates: Vec<(Arc, usize, String)> = Vec::new(); - - for (mnt_fs, path_len, path_str) in candidates.iter() { - if mnt_fs.bind_target_root().is_some() { - bind_mount_candidates.push((mnt_fs.clone(), *path_len, path_str.clone())); - log::debug!( - "[pivot_root] get_mountfs: found bind mount candidate id={:?}, path={:?}", - mnt_fs.mount_id(), - path_str - ); - } - } - - // 如果找到了设置了 bind_target_root 的挂载点,在其中选择路径最短的 - if !bind_mount_candidates.is_empty() { - bind_mount_candidates.sort_by(|a, b| a.1.cmp(&b.1)); - if let Some((mnt_fs, _, path_str)) = bind_mount_candidates.first() { - log::debug!( - "[pivot_root] get_mountfs: returning bind mount match id={:?}, path={:?}", - mnt_fs.mount_id(), - path_str - ); - return Ok(mnt_fs.clone()); - } - } - - // 如果没有找到 bind mount,选择路径最短的(最上层的)作为后备 - candidates.sort_by(|a, b| a.1.cmp(&b.1)); - if let Some((mnt_fs, _, path_str)) = candidates.first() { - log::debug!( - "[pivot_root] get_mountfs: returning shortest match id={:?}, path={:?}", - mnt_fs.mount_id(), - path_str - ); - return Ok(mnt_fs.clone()); + // 2. 查找 bind mount 候选 + if let Some(mount_fs) = Self::find_bind_mount(&mnt_ns, &inode_fs) { + return Ok(mount_fs); } - // 如果找不到合适的,尝试通过文件系统查找对应的 MountFS(后备方案) + // 3. 通过文件系统指针查找 if let Some(mount_fs) = mnt_ns.mount_list().find_mount_by_fs(&inode_fs) { - log::debug!("[pivot_root] get_mountfs: found MountFS by filesystem lookup (fallback)"); + log::debug!("[pivot_root] get_mountfs: found MountFS by filesystem lookup"); return Ok(mount_fs); } - // 如果找不到,尝试通过文件系统名称比较来查找 - let root_inode = mnt_ns.root_inode(); - let root_fs = root_inode.fs(); - - // 如果 inode 的文件系统和根文件系统类型相同(名称相同) + // 4. 通过文件系统名称查找(后备方案) + let root_fs = mnt_ns.root_inode().fs(); if inode_fs.name() == root_fs.name() { log::debug!("[pivot_root] get_mountfs: fs name matches root, returning root_mntfs"); return Ok(mnt_ns.root_mntfs().clone()); } - // 如果还是找不到,返回错误 log::error!( "[pivot_root] get_mountfs: cannot find MountFS for inode type={:?}, fs={}", inode.type_id(), @@ -368,60 +299,45 @@ impl SysPivotRootHandle { Err(SystemError::EINVAL) } - /// 检查 target_inode 是否是 ancestor_inode 的后代 + /// 查找 bind mount 创建的 MountFS /// - /// 通过向上遍历 target_inode 的父目录链,检查是否能到达 ancestor_inode - fn is_ancestor( - ancestor_inode: &Arc, - target_inode: &Arc, - ) -> Result { - // 获取 ancestor 的 inode id - let ancestor_id = ancestor_inode.metadata()?.inode_id; - let ancestor_fs = ancestor_inode.fs(); - - log::debug!( - "[pivot_root] is_ancestor: ancestor_id={:?}, ancestor_fs={}", - ancestor_id, - ancestor_fs.name() - ); - - // 从 target_inode 开始向上遍历 - let mut current = target_inode.clone(); - - // 最多向上遍历 MAX_ANCESTOR_TRAVERSAL_DEPTH 层,防止循环引用 - for i in 0..MAX_ANCESTOR_TRAVERSAL_DEPTH { - let current_meta = current.metadata()?; + /// 在挂载列表中查找包装了指定文件系统且设置了 bind_target_root 的 MountFS。 + /// 如果有多个候选,选择路径最短的(最上层的)。 + fn find_bind_mount( + mnt_ns: &Arc, + inode_fs: &Arc, + ) -> Option> { + let mount_list = mnt_ns.mount_list().clone_inner(); + let mut bind_mount_candidates: Vec<(Arc, usize)> = Vec::new(); - // 检查是否到达 ancestor - if current_meta.inode_id == ancestor_id && Arc::ptr_eq(¤t.fs(), &ancestor_fs) { - // 找到了 ancestor,说明 target 是 ancestor 的后代 - log::debug!("[pivot_root] is_ancestor: found ancestor after {} steps", i); - return Ok(true); + for (path, mnt_fs) in mount_list.iter() { + let inner_fs = mnt_fs.inner_filesystem(); + // 检查是否匹配文件系统且设置了 bind_target_root + if (Arc::ptr_eq(&inner_fs, inode_fs) || inner_fs.name() == inode_fs.name()) + && mnt_fs.bind_target_root().is_some() + { + let path_len = path.as_str().len(); + bind_mount_candidates.push((mnt_fs.clone(), path_len)); + log::debug!( + "[pivot_root] find_bind_mount: candidate id={:?}, path={:?}", + mnt_fs.mount_id(), + path.as_str() + ); } + } - // 尝试向上移动到父目录 - match current.parent() { - Ok(parent) => { - // 如果 parent 就是 current 本身,说明已经到达根目录 - if Arc::ptr_eq(&parent, ¤t) { - log::debug!("[pivot_root] is_ancestor: reached root (parent==self)"); - break; - } - current = parent; - } - Err(e) => { - // 没有父目录了,到达根目录 - log::debug!("[pivot_root] is_ancestor: no parent, err={:?}", e); - break; - } - } + // 选择路径最短的(最上层的) + if !bind_mount_candidates.is_empty() { + bind_mount_candidates.sort_by(|a, b| a.1.cmp(&b.1)); + let (mnt_fs, _) = bind_mount_candidates.into_iter().next()?; + log::debug!( + "[pivot_root] find_bind_mount: returning id={:?}", + mnt_fs.mount_id() + ); + return Some(mnt_fs); } - // 最后再检查一次根目录是否是 ancestor - let root_meta = current.metadata()?; - let result = root_meta.inode_id == ancestor_id && Arc::ptr_eq(¤t.fs(), &ancestor_fs); - log::debug!("[pivot_root] is_ancestor: final check result={}", result); - Ok(result) + None } } diff --git a/kernel/src/filesystem/vfs/utils.rs b/kernel/src/filesystem/vfs/utils.rs index 00ed340f4b..ce5c867ed7 100644 --- a/kernel/src/filesystem/vfs/utils.rs +++ b/kernel/src/filesystem/vfs/utils.rs @@ -83,6 +83,25 @@ pub fn user_path_at( Ok((pcb.pwd_inode(), ret_path)) } +/// 检查 ancestor 是否是 node 的祖先节点 +/// +/// 通过向上遍历 node 的父目录链,检查是否能到达 ancestor。 +/// 这用于验证一个目录是否在另一个目录之下(如 pivot_root 中检查 put_old 是否在 new_root 之下)。 +/// +/// # 参数 +/// +/// - `ancestor`: 可能的祖先节点 +/// - `node`: 要检查的节点 +/// +/// # 返回值 +/// +/// 如果 ancestor 是 node 的祖先(包括 ancestor == node 的情况),返回 true;否则返回 false。 +/// +/// # 注意 +/// +/// - 此函数通过 inode ID 和文件系统指针进行比较 +/// - 检查会在到达根目录或发生错误时停止 +/// - 不检查跨文件系统的边界(即需要同时在同一文件系统中) pub fn is_ancestor(ancestor: &Arc, node: &Arc) -> bool { let ancestor_id = match ancestor.metadata() { Ok(m) => m.inode_id, @@ -96,7 +115,8 @@ pub fn is_ancestor(ancestor: &Arc, node: &Arc) -> Err(_) => break, }; - if cur_id == ancestor_id { + // 同时检查 inode ID 和文件系统指针,确保是同一个文件系统中的同一个 inode + if cur_id == ancestor_id && Arc::ptr_eq(¤t.fs(), &ancestor.fs()) { return true; } @@ -110,6 +130,7 @@ pub fn is_ancestor(ancestor: &Arc, node: &Arc) -> Err(_) => break, }; + // 如果父节点的 inode ID 和当前节点相同,说明已经到达根目录 if parent_id == cur_id { break; } @@ -119,6 +140,72 @@ pub fn is_ancestor(ancestor: &Arc, node: &Arc) -> false } +/// is_ancestor() 遍历父目录的最大深度 +/// +/// 用于防止在文件系统损坏或存在循环引用时出现无限循环。 +/// 1000 层对于正常使用场景来说绰绰有余(Linux 默认的链接数限制也远低于此)。 +pub const MAX_ANCESTOR_TRAVERSAL_DEPTH: u32 = 1000; + +/// 检查 ancestor 是否是 node 的祖先节点(带深度限制的版本) +/// +/// 与 is_ancestor() 类似,但增加最大遍历深度限制以防止潜在的无限循环。 +/// 返回 Result 类型,可以传递错误信息。 +/// +/// # 参数 +/// +/// - `ancestor`: 可能的祖先节点 +/// - `node`: 要检查的节点 +/// +/// # 返回值 +/// +/// - `Ok(true)`: ancestor 是 node 的祖先 +/// - `Ok(false)`: ancestor 不是 node 的祖先 +/// - `Err(SystemError)`: 遍历过程中发生错误 +/// +/// # 注意 +/// +/// 此函数会限制遍历深度为 MAX_ANCESTOR_TRAVERSAL_DEPTH 层。 +/// 如果超过此深度仍未找到祖先,将返回 Ok(false)。 +pub fn is_ancestor_limited( + ancestor: &Arc, + node: &Arc, +) -> Result { + let ancestor_meta = ancestor.metadata()?; + let ancestor_id = ancestor_meta.inode_id; + let ancestor_fs = ancestor.fs(); + + let mut current = node.clone(); + + // 最多向上遍历 MAX_ANCESTOR_TRAVERSAL_DEPTH 层,防止循环引用 + for _i in 0..MAX_ANCESTOR_TRAVERSAL_DEPTH { + let current_meta = current.metadata()?; + + // 检查是否到达 ancestor(同时检查 inode ID 和文件系统) + if current_meta.inode_id == ancestor_id && Arc::ptr_eq(¤t.fs(), &ancestor_fs) { + return Ok(true); + } + + // 尝试向上移动到父目录 + match current.parent() { + Ok(parent) => { + // 如果 parent 就是 current 本身,说明已经到达根目录 + if Arc::ptr_eq(&parent, ¤t) { + break; + } + current = parent; + } + Err(_) => { + // 没有父目录了,到达根目录 + break; + } + } + } + + // 最后再检查一次根目录是否是 ancestor + let root_meta = current.metadata()?; + Ok(root_meta.inode_id == ancestor_id && Arc::ptr_eq(¤t.fs(), &ancestor_fs)) +} + /// Directory Name /// 可以用来作为原地提取目录名及比较的 /// Dentry的对标(x diff --git a/kernel/src/process/namespace/mnt.rs b/kernel/src/process/namespace/mnt.rs index 6574fab0fc..aebf3cf7fd 100644 --- a/kernel/src/process/namespace/mnt.rs +++ b/kernel/src/process/namespace/mnt.rs @@ -93,9 +93,41 @@ impl MntNamespace { return result; } - /// 强制替换本MountNamespace的根挂载文件系统 + /// 强制替换本 MountNamespace 的根挂载文件系统 /// - /// 本方法仅供dragonos初始化时使用 + /// 此方法用于 `pivot_root` 系统调用,将当前进程的根文件系统切换到新的挂载点。 + /// 它会更新命名空间的根挂载点、挂载列表,并保存旧的根挂载点以便后续卸载。 + /// + /// # Safety + /// + /// 此函数使用 unsafe 代码来修改 `MntNamespace` 的不可变字段。 + /// 这样做是安全的,原因如下: + /// + /// 1. **并发安全**:调用者必须持有 `self.inner.lock()`,这确保了不会有多个线程 + /// 同时修改同一个命名空间的状态。 + /// + /// 2. **不变量维护**: + /// - `root_mountfs` 被更新为新的挂载点 + /// - 挂载列表中的 "/" 路径也被更新为指向新的挂载点 + /// - 旧的根挂载点被保存到 `old_root_mntfs` 中 + /// 这些修改保持了一致性:命名空间始终有一个有效的根挂载点。 + /// + /// 3. **内存安全**: + /// - `new_root` 是一个 `Arc`,使用引用计数确保内存安全 + /// - `old_root` 被 clone 并保存,不会被提前释放 + /// - 所有 `Arc` 的 clone 操作都是原子且线程安全的 + /// + /// 4. **可见性**:由于 `inner` 锁的存在,所有修改对其他线程是可见的。 + /// + /// # 调用时机 + /// + /// 此函数应该在持有挂载命名空间锁的情况下调用,并且只在 `pivot_root` 系统调用 + /// 或类似的初始化场景中使用。 + /// + /// # 注意 + /// + /// 本方法需要被标记为 unsafe,因为它绕过了 Rust 的借用检查规则。 + /// 调用者必须确保上述安全条件得到满足。 pub unsafe fn force_change_root_mountfs(&self, new_root: Arc) { let inner_guard = self.inner.lock(); let ptr = self as *const Self as *mut Self; @@ -111,8 +143,6 @@ impl MntNamespace { // 将旧的根挂载点保存到一个特殊位置(使用特殊路径) // 这样 umount2 可以通过这个路径找到并卸载它 *self_mut.old_root_mntfs.lock() = Some(old_root); - - // update mount list ino } fn copy_with_mountfs(&self, new_root: Arc, _user_ns: Arc) -> Arc {