diff --git a/kernel/src/filesystem/fat/entry.rs b/kernel/src/filesystem/fat/entry.rs index b5ee37869..e13e9caaa 100644 --- a/kernel/src/filesystem/fat/entry.rs +++ b/kernel/src/filesystem/fat/entry.rs @@ -322,8 +322,19 @@ impl FATFile { return Ok(()); } - let zeroes: Vec = vec![0u8; (range_end - range_start) as usize]; - fs.gendisk.write_at_bytes(&zeroes, range_start as usize)?; + // 限制每次写入的缓冲区大小,避免大文件扩展时分配过大内存 + const ZERO_BUF_SIZE: usize = 512 * 1024; // 512KB + let zeroes: Vec = vec![0u8; ZERO_BUF_SIZE]; + let mut offset = range_start; + let mut remain = (range_end - range_start) as usize; + + while remain > 0 { + let write_size = core::cmp::min(remain, ZERO_BUF_SIZE); + fs.gendisk + .write_at_bytes(&zeroes[..write_size], offset as usize)?; + offset += write_size as u64; + remain -= write_size; + } return Ok(()); } diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index d98c886d8..f975a8829 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -1,4 +1,7 @@ +use crate::arch::MMArch; use crate::filesystem::vfs::syscall::RenameFlags; +use crate::mm::truncate::truncate_inode_pages; +use crate::mm::MemoryManagementArch; use alloc::string::ToString; use alloc::{ string::String, @@ -1749,8 +1752,14 @@ impl IndexNode for LockedFATInode { Ok(()) } fn resize(&self, len: usize) -> Result<(), SystemError> { - // 先调整页缓存大小,但不要提前返回;后续仍需同步到底层文件并更新元数据 + //检查是否超过fat支持的最大容量 + if (len as u64) > MAX_FILE_SIZE { + return Err(SystemError::EFBIG); + } + // 先调整页缓存:清除被截断区间的缓存页,再缩容缓存大小 if let Some(page_cache) = self.page_cache() { + let start_page = (len + MMArch::PAGE_SIZE - 1) >> MMArch::PAGE_SHIFT; + truncate_inode_pages(page_cache.clone(), start_page); page_cache.lock_irqsave().resize(len)?; } @@ -1767,16 +1776,14 @@ impl IndexNode for LockedFATInode { } Ordering::Greater => { // 如果新的长度比旧的长度大,那么就在文件末尾添加空白 - let mut buf: Vec = Vec::new(); - let mut remain_size = len - old_size; - let buf_size = remain_size; - // let buf_size = core::cmp::min(remain_size, 512 * 1024); - buf.resize(buf_size, 0); + const ZERO_BUF_SIZE: usize = 512 * 1024; // 与 zero_range 保持一致的上限 + let buf: Vec = vec![0u8; ZERO_BUF_SIZE]; + let mut remain_size = len - old_size; let mut offset = old_size; while remain_size > 0 { - let write_size = core::cmp::min(remain_size, buf_size); - file.write(fs, &buf[0..write_size], offset as u64)?; + let write_size = core::cmp::min(remain_size, ZERO_BUF_SIZE); + file.write(fs, &buf[..write_size], offset as u64)?; remain_size -= write_size; offset += write_size; } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 14a185344..25e106b8f 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -8,6 +8,7 @@ use log::error; use system_error::SystemError; use super::{FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; +use crate::{arch::ipc::signal::Signal, filesystem::vfs::InodeFlags, process::pid::PidPrivateData}; use crate::{ arch::MMArch, driver::{ @@ -37,7 +38,6 @@ use crate::{ ProcessControlBlock, ProcessManager, RawPid, }, }; -use crate::{filesystem::vfs::InodeFlags, process::pid::PidPrivateData}; const MAX_LFS_FILESIZE: i64 = i64::MAX; /// Namespace fd backing data, typically created from /proc/thread-self/ns/* files. @@ -988,8 +988,30 @@ impl File { /// @return 成功:Ok() /// 失败:Err(错误码) pub fn ftruncate(&self, len: usize) -> Result<(), SystemError> { - // 如果文件不可写,返回错误 - self.writeable()?; + // 类型必须是普通文件,否则 EINVAL + let md = self.inode.metadata()?; + if md.file_type != FileType::File { + return Err(SystemError::EINVAL); + } + + // O_PATH 直接返回 EBADF,保持与 open 时的行为一致 + let mode = *self.mode.read(); + if mode.contains(FileMode::FMODE_PATH) { + return Err(SystemError::EBADF); + } + + // 非可写打开返回 EINVAL(对齐 gVisor 预期) + if !mode.contains(FileMode::FMODE_WRITE) || !mode.can_write() { + return Err(SystemError::EINVAL); + } + + // RLIMIT_FSIZE 检查 + let current_pcb = ProcessManager::current_pcb(); + let fsize_limit = current_pcb.get_rlimit(RLimitID::Fsize); + if fsize_limit.rlim_cur != u64::MAX && len as u64 > fsize_limit.rlim_cur { + let _ = send_signal_to_pid(current_pcb.raw_pid(), Signal::SIGXFSZ); + return Err(SystemError::EFBIG); + } // 统一通过 VFS 封装,复用类型/只读检查 crate::filesystem::vfs::vcore::vfs_truncate(self.inode(), len)?; diff --git a/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs b/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs index 7d8f73acb..5f6d84c1c 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs @@ -31,7 +31,7 @@ impl Syscall for SysFtruncateHandle { } fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result { let fd = Self::fd(args); - let len = Self::len(args); + let len = Self::len(args)?; let binding = ProcessManager::current_pcb().fd_table(); let fd_table_guard = binding.read(); @@ -49,7 +49,7 @@ impl Syscall for SysFtruncateHandle { fn entry_format(&self, args: &[usize]) -> Vec { vec![ FormattedSyscallParam::new("fd", format!("{:#x}", Self::fd(args))), - FormattedSyscallParam::new("len", format!("{:#x}", Self::len(args))), + FormattedSyscallParam::new("len", format!("{:#x}", Self::len(args).unwrap_or(0))), ] } } @@ -59,8 +59,12 @@ impl SysFtruncateHandle { args[0] as i32 } - fn len(args: &[usize]) -> usize { - args[1] + fn len(args: &[usize]) -> Result { + let len = args[1]; + if len > isize::MAX as usize { + return Err(SystemError::EINVAL); + } + Ok(len) } } diff --git a/kernel/src/filesystem/vfs/syscall/sys_truncate.rs b/kernel/src/filesystem/vfs/syscall/sys_truncate.rs index 0e503a17e..f632af118 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_truncate.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_truncate.rs @@ -1,10 +1,12 @@ -use crate::arch::syscall::nr::SYS_TRUNCATE; +use crate::arch::{ipc::signal::Signal, syscall::nr::SYS_TRUNCATE}; use crate::{ arch::interrupt::TrapFrame, filesystem::vfs::{ - fcntl::AtFlags, utils::user_path_at, IndexNode, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, + fcntl::AtFlags, permission::PermissionMask, utils::user_path_at, FileType, IndexNode, + MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, }, - process::ProcessManager, + ipc::kill::send_signal_to_pid, + process::{resource::RLimitID, ProcessManager}, syscall::{ table::{FormattedSyscallParam, Syscall}, user_access::check_and_clone_cstr, @@ -33,8 +35,7 @@ impl Syscall for SysTruncateHandle { fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result { let path_ptr = args[0] as *const u8; - let length = args[1]; - + let length = Self::len(args)?; // 复制并校验用户态路径 let path = check_and_clone_cstr(path_ptr, Some(MAX_PATHLEN))?; let path = path.to_str().map_err(|_| SystemError::EINVAL)?; @@ -50,6 +51,21 @@ impl Syscall for SysTruncateHandle { let target: Arc = begin_inode .lookup_follow_symlink(remain_path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + let md = target.metadata()?; + // DAC write permission check + let cred = ProcessManager::current_pcb().cred(); + cred.inode_permission(&md, PermissionMask::MAY_WRITE.bits())?; + + // RLIMIT_FSIZE enforcement for regular files + if md.file_type == FileType::File { + let fsize_limit = ProcessManager::current_pcb().get_rlimit(RLimitID::Fsize); + if fsize_limit.rlim_cur != u64::MAX && length as u64 > fsize_limit.rlim_cur { + let _ = + send_signal_to_pid(ProcessManager::current_pcb().raw_pid(), Signal::SIGXFSZ); + return Err(SystemError::EFBIG); + } + } + vfs_truncate(target, length)?; Ok(0) } @@ -62,4 +78,14 @@ impl Syscall for SysTruncateHandle { } } +impl SysTruncateHandle { + fn len(args: &[usize]) -> Result { + let len = args[1]; + if len > isize::MAX as usize { + return Err(SystemError::EINVAL); + } + Ok(len) + } +} + syscall_table_macros::declare_syscall!(SYS_TRUNCATE, SysTruncateHandle); diff --git a/user/apps/tests/syscall/gvisor/blocklists/truncate_test b/user/apps/tests/syscall/gvisor/blocklists/truncate_test new file mode 100644 index 000000000..36f093ed0 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/blocklists/truncate_test @@ -0,0 +1 @@ +TruncateTest.FtruncateVirtualTmp diff --git a/user/apps/tests/syscall/gvisor/whitelist.txt b/user/apps/tests/syscall/gvisor/whitelist.txt index 4af4fc66c..bda2cfd62 100644 --- a/user/apps/tests/syscall/gvisor/whitelist.txt +++ b/user/apps/tests/syscall/gvisor/whitelist.txt @@ -33,6 +33,7 @@ preadv_test pwrite64_test pwritev2_test utimes_test +truncate_test fadvise_test open_test