Files
DragonOS/kernel/src/filesystem/vfs/syscall/sys_read.rs
黄铭涛 a69d1a93b6 fix(fs, mm): 修复fs、mm上有关系统调用的bug (#1208)
* fix(fs, mm): 修复fs、mm上有关系统调用的bug

**filesystem:**

- 修改read系统调用:修复`O_PATH`文件模式该有的功能,使其能通过gvisor/syscall/read的测试。参考:[[file_table.c - fs/file_table.c - Linux source code v2.6.39 - Bootlin Elixir Cross Referencer](https://elixir.bootlin.com/linux/v2.6.39/source/fs/file_table.c#L331)](https://elixir.bootlin.com/linux/v2.6.39/source/fs/file_table.c#L331),在读取之前先进行检查文件模式是否为`O_PATH`
- 修改getcwd系统调用:修正成跟linux语义一样,返回目录长度而不是地址。因为gvisor用这个系统调用如果返回的是地址是会报错的,改成跟linux一样就不会报错了。参考:[[dcache.c - fs/dcache.c - Linux source code v2.6.39 - Bootlin Elixir Cross Referencer](https://elixir.bootlin.com/linux/v2.6.39/source/fs/dcache.c#L2774)](https://elixir.bootlin.com/linux/v2.6.39/source/fs/dcache.c#L2774)
- 修改unlink系统调用:在unlink删除inode之后,要将inode对应的pagecache的dirty标识去掉,否则在`flush_dirty_pages()`的时候,会将标记为dirty的pagecache进行`page_writeback()`,但是对应的pagecache的inode已经被释放了,这时候直接unwrap()就会导致panic。参考:[[namei.c - fs/namei.c - Linux source code v2.6.6 - Bootlin Elixir Cross Referencer](https://elixir.bootlin.com/linux/v2.6.6/source/fs/namei.c#L1714)](https://elixir.bootlin.com/linux/v2.6.6/source/fs/namei.c#L1714)

**mm:**

- 添加`truncate_inode_pages()`,用来截断文件从指定偏移量的页缓存,但目前该函数功能仅是将pagecache的dirty标识去掉。参考:[[truncate.c - mm/truncate.c - Linux source code v2.6.6 - Bootlin Elixir Cross Referencer](https://elixir.bootlin.com/linux/v2.6.6/source/mm/truncate.c#L112)](https://elixir.bootlin.com/linux/v2.6.6/source/mm/truncate.c#L112)

**syscall:**

- 修改了`convert_with_offset()`的判断逻辑,使其能够从用户空间读取0字节的数据,也是为了能够通过gvisor/syscall/read的测试

目前是能够跑通gvisor syscall测试`read_test`并通过所有测例
![image-20250614224227721](https://github.com/user-attachments/assets/b1c04720-da5c-464a-a7f3-c01f3e82783a)
2025-06-27 10:40:49 +08:00

110 lines
3.4 KiB
Rust

use system_error::SystemError;
use crate::arch::interrupt::TrapFrame;
use crate::arch::syscall::nr::SYS_READ;
use crate::filesystem::vfs::file::FileMode;
use crate::process::ProcessManager;
use crate::syscall::table::FormattedSyscallParam;
use crate::syscall::table::Syscall;
use crate::syscall::user_access::UserBufferWriter;
use alloc::string::ToString;
use alloc::vec::Vec;
/// System call handler for the `read` syscall
///
/// This handler implements the `Syscall` trait to provide functionality for reading data from a file descriptor.
pub struct SysReadHandle;
impl Syscall for SysReadHandle {
/// Returns the number of arguments expected by the `read` syscall
fn num_args(&self) -> usize {
3
}
/// Handles the `read` system call
///
/// Reads data from the specified file descriptor into a user buffer.
///
/// # Arguments
/// * `args` - Array containing:
/// - args[0]: File descriptor (i32)
/// - args[1]: Pointer to user buffer (*mut u8)
/// - args[2]: Length of data to read (usize)
/// * `from_user` - Indicates if the call originates from user space
///
/// # Returns
/// * `Ok(usize)` - Number of bytes successfully read
/// * `Err(SystemError)` - Error code if operation fails
fn handle(&self, args: &[usize], frame: &mut TrapFrame) -> Result<usize, SystemError> {
let fd = Self::fd(args);
let buf_vaddr = Self::buf(args);
let len = Self::len(args);
let mut user_buffer_writer = UserBufferWriter::new(buf_vaddr, len, frame.is_from_user())?;
let user_buf = user_buffer_writer.buffer(0)?;
do_read(fd, user_buf)
}
/// Formats the syscall parameters for display/debug purposes
///
/// # Arguments
/// * `args` - The raw syscall arguments
///
/// # Returns
/// Vector of formatted parameters with descriptive names
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
vec![
FormattedSyscallParam::new("fd", Self::fd(args).to_string()),
FormattedSyscallParam::new("buf", format!("{:#x}", Self::buf(args) as usize)),
FormattedSyscallParam::new("len", Self::len(args).to_string()),
]
}
}
impl SysReadHandle {
/// Extracts the file descriptor from syscall arguments
fn fd(args: &[usize]) -> i32 {
args[0] as i32
}
/// Extracts the buffer pointer from syscall arguments
fn buf(args: &[usize]) -> *mut u8 {
args[1] as *mut u8
}
/// Extracts the buffer length from syscall arguments
fn len(args: &[usize]) -> usize {
args[2]
}
}
syscall_table_macros::declare_syscall!(SYS_READ, SysReadHandle);
/// Internal implementation of the read operation
///
/// # Arguments
/// * `fd` - File descriptor to read from
/// * `buf` - Buffer to store read data
///
/// # Returns
/// * `Ok(usize)` - Number of bytes successfully read
/// * `Err(SystemError)` - Error code if operation fails
pub(super) fn do_read(fd: i32, buf: &mut [u8]) -> Result<usize, SystemError> {
let binding = ProcessManager::current_pcb().fd_table();
let fd_table_guard = binding.read();
let file = fd_table_guard
.get_file_by_fd(fd)
.ok_or(SystemError::EBADF)?;
// drop guard 以避免无法调度的问题
drop(fd_table_guard);
if file.mode().contains(FileMode::O_PATH) {
return Err(SystemError::EBADF);
}
return file.read(buf.len(), buf);
}