Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions kernel/src/filesystem/fat/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use log::{debug, warn};
use system_error::SystemError;

use super::{
fs::{Cluster, FATFileSystem, MAX_FILE_SIZE},
fs::{Cluster, FATFileSystem, MAX_FILE_SIZE, ZERO_BUF_SIZE},
utils::decode_u8_ascii,
};

Expand Down Expand Up @@ -197,10 +197,13 @@ impl FATFile {

// 计算本次写入位置在分区上的偏移量
let offset = fs.cluster_bytes_offset(current_cluster) + in_cluster_bytes_offset;
// 写入磁盘
// 写入磁盘(防御性检查:若设备返回0字节写入视为IO错误)
let w = fs
.gendisk
.write_at_bytes(&buf[start..start + end_len], offset as usize)?;
if w == 0 {
return Err(SystemError::EIO);
}

// 更新偏移量数据
write_ok += w;
Expand Down Expand Up @@ -322,8 +325,22 @@ 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)?;
// 限制每次写入的缓冲区大小,避免大文件扩展时分配过大内存
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);
let w = fs
.gendisk
.write_at_bytes(&zeroes[..write_size], offset as usize)?;
if w == 0 {
return Err(SystemError::EIO);
}
offset += write_size as u64;
remain -= write_size;
}

return Ok(());
}
Expand Down
25 changes: 17 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 @@ -48,6 +51,9 @@ const FAT_MAX_NAMELEN: u64 = 255;
/// FAT32文件系统的最大的文件大小
pub const MAX_FILE_SIZE: u64 = 0xffff_ffff;

/// 每次清零写入时的缓冲区上限(避免分配过大内存)
pub const ZERO_BUF_SIZE: usize = 512 * 1024; // 512KB

/// @brief 表示当前簇和上一个簇的关系的结构体
/// 定义这样一个结构体的原因是,FAT文件系统的文件中,前后两个簇具有关联关系。
#[allow(dead_code)]
Expand Down Expand Up @@ -1749,8 +1755,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 +1779,13 @@ 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);
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);
5 changes: 5 additions & 0 deletions kernel/src/filesystem/vfs/vcore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,11 @@ pub(super) fn do_file_lookup_at(
pub fn vfs_truncate(inode: Arc<dyn IndexNode>, len: usize) -> Result<(), SystemError> {
let md = inode.metadata()?;

// 防御性检查:统一拒绝超出 isize::MAX 的长度,避免后续类型转换溢出
if len > isize::MAX as usize {
return Err(SystemError::EINVAL);
}

if md.file_type == FileType::Dir {
return Err(SystemError::EISDIR);
}
Expand Down
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 @@ -33,6 +33,7 @@ preadv_test
pwrite64_test
pwritev2_test
utimes_test
truncate_test
fadvise_test
open_test

Expand Down
Loading