Skip to content
Open
15 changes: 13 additions & 2 deletions kernel/src/filesystem/fat/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,19 @@ impl FATFile {
return Ok(());
}

let zeroes: Vec<u8> = 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<u8> = 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(());
}
Expand Down
23 changes: 15 additions & 8 deletions kernel/src/filesystem/fat/fs.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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)?;
}

Expand All @@ -1767,16 +1776,14 @@ impl IndexNode for LockedFATInode {
}
Ordering::Greater => {
// 如果新的长度比旧的长度大,那么就在文件末尾添加空白
let mut buf: Vec<u8> = 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<u8> = 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;
}
Expand Down
28 changes: 25 additions & 3 deletions kernel/src/filesystem/vfs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)?;
Expand Down
12 changes: 8 additions & 4 deletions kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl Syscall for SysFtruncateHandle {
}
fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
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();
Expand All @@ -49,7 +49,7 @@ impl Syscall for SysFtruncateHandle {
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
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))),
]
}
}
Expand All @@ -59,8 +59,12 @@ impl SysFtruncateHandle {
args[0] as i32
}

fn len(args: &[usize]) -> usize {
args[1]
fn len(args: &[usize]) -> Result<usize, SystemError> {
let len = args[1];
if len > isize::MAX as usize {
return Err(SystemError::EINVAL);
}
Ok(len)
}
}

Expand Down
36 changes: 31 additions & 5 deletions kernel/src/filesystem/vfs/syscall/sys_truncate.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -33,8 +35,7 @@ impl Syscall for SysTruncateHandle {

fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
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)?;
Expand All @@ -50,6 +51,21 @@ impl Syscall for SysTruncateHandle {
let target: Arc<dyn IndexNode> = 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)
}
Expand All @@ -62,4 +78,14 @@ impl Syscall for SysTruncateHandle {
}
}

impl SysTruncateHandle {
fn len(args: &[usize]) -> Result<usize, SystemError> {
let len = args[1];
if len > isize::MAX as usize {
return Err(SystemError::EINVAL);
}
Ok(len)
}
}

syscall_table_macros::declare_syscall!(SYS_TRUNCATE, SysTruncateHandle);
1 change: 1 addition & 0 deletions user/apps/tests/syscall/gvisor/blocklists/truncate_test
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TruncateTest.FtruncateVirtualTmp
1 change: 1 addition & 0 deletions user/apps/tests/syscall/gvisor/whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ getdents_test
preadv_test
pwrite64_test
utimes_test
truncate_test
fadvise_test
open_test

Expand Down
Loading