diff --git a/kernel/src/filesystem/fat/entry.rs b/kernel/src/filesystem/fat/entry.rs index 3a0b0d394..b5ee37869 100644 --- a/kernel/src/filesystem/fat/entry.rs +++ b/kernel/src/filesystem/fat/entry.rs @@ -1,8 +1,7 @@ #![allow(dead_code)] -use core::{cmp::min, intrinsics::unlikely}; -use log::{debug, warn}; -use system_error::SystemError; - +use crate::filesystem::fat::fs::LockedFATInode; +use crate::filesystem::vfs::IndexNode; +use crate::mm::truncate::truncate_inode_pages; use crate::{ driver::base::block::{block_device::LBA_SIZE, SeekFrom}, libs::vec_cursor::VecCursor, @@ -12,6 +11,9 @@ use alloc::{ sync::Arc, vec::Vec, }; +use core::{cmp::min, intrinsics::unlikely}; +use log::{debug, warn}; +use system_error::SystemError; use super::{ fs::{Cluster, FATFileSystem, MAX_FILE_SIZE}, @@ -789,7 +791,8 @@ impl FATDir { let e: FATDirEntry = self.find_entry(name, None, None, fs.clone())?; // 判断文件夹是否为空,如果空,则不删除,报错。 - if e.is_dir() && !(e.to_dir().unwrap().is_empty(fs.clone())) { + // remove_clusters 为 false 时(即重命名/移动操作),不再检查目录是否为空,从而允许非空目录被“搬运”到新位置。 + if e.is_dir() && remove_clusters && !(e.to_dir().unwrap().is_empty(fs.clone())) { return Err(SystemError::ENOTEMPTY); } @@ -852,6 +855,7 @@ impl FATDir { fs: Arc, old_name: &str, new_name: &str, + new_inode: Option>, ) -> Result { // 判断源目录项是否存在 let old_dentry = if let FATDirEntryOrShortName::DirEntry(dentry) = @@ -862,25 +866,26 @@ impl FATDir { // 如果目标目录项不存在,则返回错误 return Err(SystemError::ENOENT); }; - let short_name = match self.check_existence(new_name, None, fs.clone())? { FATDirEntryOrShortName::ShortName(s) => s, - // If newpath already exists, it will be atomically replaced, so that - // there is no point at which another process attempting to access - // newpath will find it missing. - // TODO: support other flags like RENAME_EXCHANGE + // 目标已存在:根据类型关系决定是否允许覆盖 FATDirEntryOrShortName::DirEntry(e) => { - // remove the existing entry + validate_rename_target(&old_dentry, &e, fs.clone())?; + + if let Some(new_inode) = new_inode { + if let Some(page_cache) = new_inode.page_cache().clone() { + truncate_inode_pages(page_cache, 0); + } + } + + // 允许覆盖:若为非空目录,remove 会返回 ENOTEMPTY(这里只处理空目录或文件) self.remove(fs.clone(), new_name, true)?; e.short_name_raw() } }; - let old_short_dentry = old_dentry.short_dir_entry(); if let Some(se) = old_short_dentry { - // 删除原来的目录项 self.remove(fs.clone(), old_dentry.name().as_str(), false)?; - // 创建新的目录项 let new_dentry = self.create_dir_entries( new_name, @@ -889,7 +894,6 @@ impl FATDir { se.attributes, fs.clone(), )?; - return Ok(new_dentry); } else { // 不允许对根目录项进行重命名 @@ -905,6 +909,7 @@ impl FATDir { target: &FATDir, old_name: &str, new_name: &str, + new_inode: Result, SystemError>, ) -> Result { // 判断源目录项是否存在 let old_dentry: FATDirEntry = if let FATDirEntryOrShortName::DirEntry(dentry) = @@ -916,20 +921,24 @@ impl FATDir { return Err(SystemError::ENOENT); }; - let short_name = if let FATDirEntryOrShortName::ShortName(s) = - target.check_existence(new_name, None, fs.clone())? - { - s - } else { - // 如果目标目录项存在,那么就返回错误 - return Err(SystemError::EEXIST); + let short_name = match target.check_existence(new_name, None, fs.clone())? { + FATDirEntryOrShortName::ShortName(s) => s, + // 目标已存在:根据类型关系决定是否允许覆盖 + FATDirEntryOrShortName::DirEntry(e) => { + validate_rename_target(&old_dentry, &e, fs.clone())?; + + if let Some(page_cache) = new_inode.unwrap().page_cache().clone() { + truncate_inode_pages(page_cache, 0); + } + // 覆盖前删除目标目录项(空目录或文件),不截断源内容 + target.remove(fs.clone(), new_name, true)?; + e.short_name_raw() + } }; let old_short_dentry: Option = old_dentry.short_dir_entry(); if let Some(se) = old_short_dentry { - // 删除原来的目录项 self.remove(fs.clone(), old_dentry.name().as_str(), false)?; - // 创建新的目录项 let new_dentry: FATDirEntry = target.create_dir_entries( new_name, @@ -1025,7 +1034,6 @@ pub struct LongDirEntry { /// 长文件名的12-13个字符,每个字符占2bytes name3: [u16; 2], } - impl LongDirEntry { /// 长目录项的字符串长度(单位:word) pub const LONG_NAME_STR_LEN: usize = 13; @@ -2464,3 +2472,24 @@ pub fn get_raw_dir_entry( } } } + +pub fn validate_rename_target( + old_entry: &FATDirEntry, + new_entry: &FATDirEntry, + fs: Arc, +) -> Result<(), SystemError> { + let old_is_dir = old_entry.is_dir(); + let new_is_dir = new_entry.is_dir(); + + if old_is_dir && !new_is_dir { + return Err(SystemError::ENOTDIR); + } + if !old_is_dir && new_is_dir { + return Err(SystemError::EISDIR); + } + // new_entry是目录,直接unwrap + if new_entry.is_dir() && !(new_entry.to_dir().unwrap().is_empty(fs)) { + return Err(SystemError::ENOTEMPTY); + } + Ok(()) +} diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 587d24b50..e8dad51aa 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -1,4 +1,10 @@ +use crate::filesystem::vfs::syscall::RenameFlags; use alloc::string::ToString; +use alloc::{ + string::String, + sync::{Arc, Weak}, + vec::Vec, +}; use core::cmp::Ordering; use core::intrinsics::unlikely; use core::{any::Any, fmt::Debug}; @@ -6,12 +12,6 @@ use hashbrown::HashMap; use log::error; use system_error::SystemError; -use alloc::{ - string::String, - sync::{Arc, Weak}, - vec::Vec, -}; - use crate::driver::base::block::gendisk::GenDisk; use crate::driver::base::device::device_number::DeviceNumber; use crate::filesystem::page_cache::PageCache; @@ -265,13 +265,29 @@ impl LockedFATInode { &self, old_name: &str, new_name: &str, + flags: RenameFlags, ) -> Result<(), SystemError> { + if old_name == new_name { + return Ok(()); + } let mut guard = self.0.lock(); let old_inode = guard.find(old_name)?; - // 对目标inode上锁,以防更改 - let old_inode_guard = old_inode.0.lock(); - let fs = old_inode_guard.fs.upgrade().unwrap(); + let new_inode = guard.find(new_name).ok(); + if flags.contains(RenameFlags::NOREPLACE) && new_inode.is_some() { + return Err(SystemError::EEXIST); + } + if flags.contains(RenameFlags::EXCHANGE) { + if new_inode.is_none() { + return Err(SystemError::ENOENT); + } + // TODO: Implement EXCHANGE logic + return Err(SystemError::EINVAL); + } + + // 对目标inode上锁,以防更改 + let mut old_inode_guard = old_inode.0.lock(); + let fs = old_inode_guard.fs.upgrade().unwrap(); let old_dir = match &guard.inode_type { FATDirEntry::File(_) | FATDirEntry::VolId(_) => { return Err(SystemError::ENOTDIR); @@ -282,10 +298,8 @@ impl LockedFATInode { return Err(SystemError::EROFS); } }; - // remove entries - old_dir.rename(fs, old_name, new_name)?; - + old_inode_guard.inode_type = old_dir.rename(fs, old_name, new_name, new_inode)?; let old_inode = guard.children.remove(&to_search_name(old_name)).unwrap(); // the new_name should refer to old_inode guard.children.insert(to_search_name(new_name), old_inode); @@ -299,18 +313,33 @@ impl LockedFATInode { old_name: &str, new_name: &str, target: &Arc, + flags: RenameFlags, ) -> Result<(), SystemError> { let mut old_guard = self.0.lock(); let other: &LockedFATInode = target .downcast_ref::() .ok_or(SystemError::EPERM)?; - let new_guard = other.0.lock(); + let mut new_guard = other.0.lock(); let old_inode: Arc = old_guard.find(old_name)?; - // 对目标inode上锁,以防更改 - let old_inode_guard: SpinLockGuard = old_inode.0.lock(); - let fs = old_inode_guard.fs.upgrade().unwrap(); + let new_inode = new_guard.find(new_name); + if flags.contains(RenameFlags::NOREPLACE) && new_inode.is_ok() { + return Err(SystemError::EEXIST); + } + + if flags.contains(RenameFlags::EXCHANGE) { + if new_inode.is_err() { + return Err(SystemError::ENOENT); + } + // TODO: Implement EXCHANGE logic + return Err(SystemError::EINVAL); + } + + // 对目标inode上锁,以防更改 + let mut old_inode_guard: SpinLockGuard = old_inode.0.lock(); + // let new_inode_guard = new_inode.0.lock(); + let fs = old_inode_guard.fs.upgrade().unwrap(); let old_dir = match &old_guard.inode_type { FATDirEntry::File(_) | FATDirEntry::VolId(_) => { return Err(SystemError::ENOTDIR); @@ -331,12 +360,17 @@ impl LockedFATInode { return Err(SystemError::EROFS); } }; - // 检查文件是否存在 - old_dir.check_existence(old_name, Some(false), old_guard.fs.upgrade().unwrap())?; - old_dir.rename_across(fs, new_dir, old_name, new_name)?; - // 从缓存删除 - let _nod = old_guard.children.remove(&to_search_name(old_name)); + old_inode_guard.inode_type = + old_dir.rename_across(fs, new_dir, old_name, new_name, new_inode)?; + // 将源节点从父目录中删除 + let old_inode = old_guard + .children + .remove(&to_search_name(old_name)) + .unwrap(); + new_guard + .children + .insert(to_search_name(new_name), old_inode); Ok(()) } } @@ -1900,14 +1934,15 @@ impl IndexNode for LockedFATInode { old_name: &str, target: &Arc, new_name: &str, + flags: RenameFlags, ) -> Result<(), SystemError> { let old_id = self.metadata().unwrap().inode_id; let new_id = target.metadata().unwrap().inode_id; // 若在同一父目录下 if old_id == new_id { - self.rename_file_in_current_dir(old_name, new_name)?; + self.rename_file_in_current_dir(old_name, new_name, flags)?; } else { - self.move_to_another_dir(old_name, new_name, target)?; + self.move_to_another_dir(old_name, new_name, target, flags)?; } return Ok(()); diff --git a/kernel/src/filesystem/kernfs/mod.rs b/kernel/src/filesystem/kernfs/mod.rs index 604eb3349..5a361f173 100644 --- a/kernel/src/filesystem/kernfs/mod.rs +++ b/kernel/src/filesystem/kernfs/mod.rs @@ -12,6 +12,7 @@ use system_error::SystemError; use crate::{ driver::base::device::device_number::DeviceNumber, + filesystem::vfs::syscall::RenameFlags, libs::{ casting::DowncastArc, rwlock::RwLock, @@ -227,6 +228,7 @@ impl IndexNode for KernFSInode { _old_name: &str, _target: &Arc, _new_name: &str, + _flags: RenameFlags, ) -> Result<(), SystemError> { // 应当通过kernfs的其它方法来操作文件,而不能从用户态直接调用此方法。 return Err(SystemError::ENOSYS); diff --git a/kernel/src/filesystem/procfs/mod.rs b/kernel/src/filesystem/procfs/mod.rs index 4e908b703..652c9e449 100644 --- a/kernel/src/filesystem/procfs/mod.rs +++ b/kernel/src/filesystem/procfs/mod.rs @@ -16,6 +16,7 @@ use crate::{ driver::base::device::device_number::DeviceNumber, filesystem::vfs::{ mount::{MountFlags, MountPath}, + syscall::RenameFlags, vcore::generate_inode_id, FileType, }, @@ -1078,6 +1079,7 @@ impl IndexNode for LockedProcFSInode { _old_name: &str, _target: &Arc, _new_name: &str, + _flag: RenameFlags, ) -> Result<(), SystemError> { return Err(SystemError::ENOSYS); } diff --git a/kernel/src/filesystem/ramfs/mod.rs b/kernel/src/filesystem/ramfs/mod.rs index feafb906b..0c8a0d44d 100644 --- a/kernel/src/filesystem/ramfs/mod.rs +++ b/kernel/src/filesystem/ramfs/mod.rs @@ -1,6 +1,7 @@ use core::any::Any; use core::intrinsics::unlikely; +use crate::filesystem::vfs::syscall::RenameFlags; use crate::filesystem::vfs::{FileSystemMakerData, FSMAKER}; use crate::libs::rwlock::RwLock; use crate::register_mountable_fs; @@ -440,6 +441,7 @@ impl IndexNode for LockedRamFSInode { old_name: &str, target: &Arc, new_name: &str, + flags: RenameFlags, ) -> Result<(), SystemError> { let inode_to_move = self .find(old_name)? @@ -455,6 +457,10 @@ impl IndexNode for LockedRamFSInode { let mut self_inode = self.0.lock(); // 判断是否在同一目录下, 是则进行重命名 if target_id == self_inode.metadata.inode_id { + if flags.contains(RenameFlags::NOREPLACE) && self_inode.children.contains_key(&new_name) + { + return Err(SystemError::EEXIST); + } self_inode.children.remove(&DName::from(old_name)); self_inode.children.insert(new_name, inode_to_move); return Ok(()); diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index f06f288d7..d0552a7ea 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -20,7 +20,10 @@ use crate::{ driver::base::{ block::block_device::BlockDevice, char::CharDevice, device::device_number::DeviceNumber, }, - filesystem::{epoll::EPollItem, vfs::permission::PermissionMask}, + filesystem::{ + epoll::EPollItem, + vfs::{permission::PermissionMask, syscall::RenameFlags}, + }, ipc::pipe::LockedPipeInode, libs::{ casting::DowncastArc, @@ -407,6 +410,7 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { _old_name: &str, _target: &Arc, _new_name: &str, + _flag: RenameFlags, ) -> Result<(), SystemError> { // 若文件系统没有实现此方法,则返回“不支持” return Err(SystemError::ENOSYS); diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index d324bce19..8fff829db 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -19,7 +19,7 @@ use crate::{ driver::base::device::device_number::DeviceNumber, filesystem::{ page_cache::PageCache, - vfs::{fcntl::AtFlags, vcore::do_mkdir_at}, + vfs::{fcntl::AtFlags, syscall::RenameFlags, vcore::do_mkdir_at}, }, libs::{ casting::DowncastArc, @@ -729,8 +729,9 @@ impl IndexNode for MountFSInode { old_name: &str, target: &Arc, new_name: &str, + flags: RenameFlags, ) -> Result<(), SystemError> { - return self.inner_inode.move_to(old_name, target, new_name); + return self.inner_inode.move_to(old_name, target, new_name, flags); } fn find(&self, name: &str) -> Result, SystemError> { diff --git a/kernel/src/filesystem/vfs/syscall/mod.rs b/kernel/src/filesystem/vfs/syscall/mod.rs index c457b48ea..6957be3a8 100644 --- a/kernel/src/filesystem/vfs/syscall/mod.rs +++ b/kernel/src/filesystem/vfs/syscall/mod.rs @@ -175,6 +175,19 @@ bitflags! { } } +bitflags! { + /// Flags used in the `renameat2` system call. + /// + /// Reference: . + /// + /// Reference: . + pub struct RenameFlags: u32 { + const NOREPLACE = 1 << 0; + const EXCHANGE = 1 << 1; + const WHITEOUT = 1 << 2; + } +} + #[repr(C)] #[derive(Clone, Copy)] /// # 文件信息结构体X diff --git a/kernel/src/filesystem/vfs/syscall/rename_utils.rs b/kernel/src/filesystem/vfs/syscall/rename_utils.rs index 397a8e553..aa6dd0ccd 100644 --- a/kernel/src/filesystem/vfs/syscall/rename_utils.rs +++ b/kernel/src/filesystem/vfs/syscall/rename_utils.rs @@ -1,3 +1,5 @@ +use crate::filesystem::vfs::syscall::RenameFlags; +use crate::filesystem::vfs::utils::is_ancestor; use crate::filesystem::vfs::utils::rsplit_path; use crate::filesystem::vfs::utils::user_path_at; use crate::filesystem::vfs::SystemError; @@ -21,6 +23,7 @@ use crate::syscall::user_access::check_and_clone_cstr; /// - Ok(返回值类型): 返回值的说明 /// - Err(错误值类型): 错误的说明 /// +/// references: https://code.dragonos.org.cn/xref/linux-6.6.21/fs/namei.c#4913 pub fn do_renameat2( oldfd: i32, filename_from: *const u8, @@ -41,10 +44,8 @@ pub fn do_renameat2( return Err(SystemError::ENAMETOOLONG); } - let flags = Flags::from_bits_truncate(flags); - if !flags.is_empty() { - log::warn!("renameat2 flags {flags:?} not supported yet"); - return Err(SystemError::EINVAL); + if filename_from == "/" || filename_to == "/" { + return Err(SystemError::EBUSY); } //获取pcb,文件节点 @@ -59,19 +60,28 @@ pub fn do_renameat2( let (new_filename, new_parent_path) = rsplit_path(&new_remain_path); let new_parent_inode = root_inode .lookup_follow_symlink(new_parent_path.unwrap_or("/"), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; - old_parent_inode.move_to(old_filename, &new_parent_inode, new_filename)?; + + let flags = RenameFlags::from_bits_truncate(flags); + if flags.contains(RenameFlags::NOREPLACE) && (new_filename == "." || new_filename == "..") { + return Err(SystemError::EEXIST); + } + + if old_filename == "." || old_filename == ".." || new_filename == "." || new_filename == ".." { + return Err(SystemError::EBUSY); + } + + let old_inode = old_parent_inode.lookup(old_filename)?; + if old_inode.metadata()?.file_type == crate::filesystem::vfs::FileType::Dir { + // 仅当把目录移动到其自身或其子树下时拦截 + if is_ancestor(&old_inode, &new_parent_inode) { + return Err(SystemError::EINVAL); + } + } + + // 不要在这里检查 new_parent 是否是 old 的祖先: + // 这会把同目录/向上移动的合法情况误判为 ENOTEMPTY。 + // 非空目录覆盖应由具体文件系统在 move_to/rename 实现中返回 ENOTEMPTY。 + + old_parent_inode.move_to(old_filename, &new_parent_inode, new_filename, flags)?; return Ok(0); } - -bitflags! { - /// Flags used in the `renameat2` system call. - /// - /// Reference: . - /// - /// Reference: . - struct Flags: u32 { - const NOREPLACE = 1 << 0; - const EXCHANGE = 1 << 1; - const WHITEOUT = 1 << 2; - } -} diff --git a/kernel/src/filesystem/vfs/utils.rs b/kernel/src/filesystem/vfs/utils.rs index 04b3f5f25..97d4b4c7f 100644 --- a/kernel/src/filesystem/vfs/utils.rs +++ b/kernel/src/filesystem/vfs/utils.rs @@ -78,6 +78,42 @@ pub fn user_path_at( return Ok((inode, ret_path)); } +pub fn is_ancestor(ancestor: &Arc, node: &Arc) -> bool { + let ancestor_id = match ancestor.metadata() { + Ok(m) => m.inode_id, + Err(_) => return false, + }; + + let mut next_node: Option> = Some(node.clone()); + while let Some(current) = next_node { + let cur_id = match current.metadata() { + Ok(m) => m.inode_id, + Err(_) => break, + }; + + if cur_id == ancestor_id { + return true; + } + + let parent = match current.parent() { + Ok(p) => p, + Err(_) => break, // 没有父节点,到达根或错误,停止循环 + }; + + let parent_id = match parent.metadata() { + Ok(m) => m.inode_id, + Err(_) => break, + }; + + if parent_id == cur_id { + break; + } + next_node = Some(parent); + } + + false +} + /// Directory Name /// 可以用来作为原地提取目录名及比较的 /// Dentry的对标(x diff --git a/user/apps/tests/syscall/gvisor/blocklists/rename_test b/user/apps/tests/syscall/gvisor/blocklists/rename_test new file mode 100644 index 000000000..65a75e1a2 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/blocklists/rename_test @@ -0,0 +1,9 @@ +RenameTest.DirectoryOverwritesDirectoryLinkCount +# 权限相关未实现 +RenameTest.FailsWhenOldParentNotWritable +RenameTest.FailsWhenNewParentNotWritable +RenameTest.OverwriteFailsWhenNewParentNotWritable +RenameTest.FileDoesNotExistWhenNewParentNotExecutable +# 相关系统调用未实现 +RenameTest.SysfsFileToSelf +RenameTest.SysfsDirectoryToSelf diff --git a/user/apps/tests/syscall/gvisor/whitelist.txt b/user/apps/tests/syscall/gvisor/whitelist.txt index 9ec513f04..bd5bca749 100644 --- a/user/apps/tests/syscall/gvisor/whitelist.txt +++ b/user/apps/tests/syscall/gvisor/whitelist.txt @@ -23,6 +23,7 @@ sync_test #chown_test chdir_test fchdir_test +rename_test getdents_test # 进程相关测试