fix(fs): 修复 sys_rename 逻辑并支持 RENAME_NOREPLACE (#1393)

逻辑修复:
· 修正覆盖现有文件/目录时的逻辑,确保目标被正确截断和删除。
· 增加文件类型检查,禁止文件与目录之间的非法覆盖。
· 增加祖先关系检查,防止将目录移动到其子目录下。
· 修复同名重命名及非空目录覆盖的边界情况。
· 拦截源或目标路径以 . 或 .. 结尾的非法操作。

功能增强:
引入RenameFlags支持,实现了 RENAME_NOREPLACE 语义。

相关 Commits:

· 添加 RenameFlags 支持 (NOREPLACE)
· 完善路径拦截 (. / ..)
·修复覆盖
截断及类型检查逻辑

· 增加祖先关系判断

---------

Signed-off-by: kaleidoscope416 <2448956191@qq.com>
This commit is contained in:
kaleidoscope416 2025-11-24 13:44:18 +08:00 committed by GitHub
parent 1e6ccecc17
commit 54c2a45d36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 217 additions and 69 deletions

View File

@ -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<FATFileSystem>,
old_name: &str,
new_name: &str,
new_inode: Option<Arc<LockedFATInode>>,
) -> Result<FATDirEntry, SystemError> {
// 判断源目录项是否存在
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<Arc<LockedFATInode>, SystemError>,
) -> Result<FATDirEntry, SystemError> {
// 判断源目录项是否存在
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<ShortDirEntry> = 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<FATFileSystem>,
) -> 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(())
}

View File

@ -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<dyn IndexNode>,
flags: RenameFlags,
) -> Result<(), SystemError> {
let mut old_guard = self.0.lock();
let other: &LockedFATInode = target
.downcast_ref::<LockedFATInode>()
.ok_or(SystemError::EPERM)?;
let new_guard = other.0.lock();
let mut new_guard = other.0.lock();
let old_inode: Arc<LockedFATInode> = old_guard.find(old_name)?;
// 对目标inode上锁以防更改
let old_inode_guard: SpinLockGuard<FATInode> = 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<FATInode> = 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<dyn IndexNode>,
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(());

View File

@ -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<dyn IndexNode>,
_new_name: &str,
_flags: RenameFlags,
) -> Result<(), SystemError> {
// 应当通过kernfs的其它方法来操作文件而不能从用户态直接调用此方法。
return Err(SystemError::ENOSYS);

View File

@ -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<dyn IndexNode>,
_new_name: &str,
_flag: RenameFlags,
) -> Result<(), SystemError> {
return Err(SystemError::ENOSYS);
}

View File

@ -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<dyn IndexNode>,
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(());

View File

@ -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<dyn IndexNode>,
_new_name: &str,
_flag: RenameFlags,
) -> Result<(), SystemError> {
// 若文件系统没有实现此方法,则返回“不支持”
return Err(SystemError::ENOSYS);

View File

@ -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<dyn IndexNode>,
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<Arc<dyn IndexNode>, SystemError> {

View File

@ -175,6 +175,19 @@ bitflags! {
}
}
bitflags! {
/// Flags used in the `renameat2` system call.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.16.3/source/include/uapi/linux/fcntl.h#L140-L143>.
///
/// Reference: <https://man7.org/linux/man-pages/man2/renameat.2.html>.
pub struct RenameFlags: u32 {
const NOREPLACE = 1 << 0;
const EXCHANGE = 1 << 1;
const WHITEOUT = 1 << 2;
}
}
#[repr(C)]
#[derive(Clone, Copy)]
/// # 文件信息结构体X

View File

@ -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: <https://elixir.bootlin.com/linux/v6.16.3/source/include/uapi/linux/fcntl.h#L140-L143>.
///
/// Reference: <https://man7.org/linux/man-pages/man2/renameat.2.html>.
struct Flags: u32 {
const NOREPLACE = 1 << 0;
const EXCHANGE = 1 << 1;
const WHITEOUT = 1 << 2;
}
}

View File

@ -78,6 +78,42 @@ pub fn user_path_at(
return Ok((inode, ret_path));
}
pub fn is_ancestor(ancestor: &Arc<dyn IndexNode>, node: &Arc<dyn IndexNode>) -> bool {
let ancestor_id = match ancestor.metadata() {
Ok(m) => m.inode_id,
Err(_) => return false,
};
let mut next_node: Option<Arc<dyn IndexNode>> = 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

View File

@ -0,0 +1,9 @@
RenameTest.DirectoryOverwritesDirectoryLinkCount
# 权限相关未实现
RenameTest.FailsWhenOldParentNotWritable
RenameTest.FailsWhenNewParentNotWritable
RenameTest.OverwriteFailsWhenNewParentNotWritable
RenameTest.FileDoesNotExistWhenNewParentNotExecutable
# 相关系统调用未实现
RenameTest.SysfsFileToSelf
RenameTest.SysfsDirectoryToSelf

View File

@ -23,6 +23,7 @@ sync_test
#chown_test
chdir_test
fchdir_test
rename_test
getdents_test
# 进程相关测试