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)
This commit is contained in:
黄铭涛
2025-06-27 10:40:49 +08:00
committed by GitHub
parent 723ac05719
commit a69d1a93b6
8 changed files with 57 additions and 12 deletions

View File

@ -312,6 +312,10 @@ impl InnerPageCache {
Ok(())
}
pub fn pages_count(&self) -> usize {
return self.pages.len();
}
}
impl Drop for InnerPageCache {

View File

@ -13,7 +13,6 @@ use crate::{
driver::base::{block::SeekFrom, device::device_number::DeviceNumber},
filesystem::vfs::{file::FileDescriptorVec, vcore as Vcore},
libs::rwlock::RwLockWriteGuard,
mm::VirtAddr,
process::ProcessManager,
syscall::{
user_access::{self, check_and_clone_cstr, UserBufferWriter},
@ -633,7 +632,7 @@ impl Syscall {
///
/// @return 成功,返回的指针指向包含工作目录路径的字符串
/// @return 错误,没有足够的空间
pub fn getcwd(buf: &mut [u8]) -> Result<VirtAddr, SystemError> {
pub fn getcwd(buf: &mut [u8]) -> Result<usize, SystemError> {
let proc = ProcessManager::current_pcb();
let cwd = proc.basic().cwd();
@ -645,7 +644,7 @@ impl Syscall {
buf[..cwd_len].copy_from_slice(cwd_bytes);
buf[cwd_len] = 0;
return Ok(VirtAddr::new(buf.as_ptr() as usize));
return Ok(cwd_len + 1);
}
/// # 获取目录中的数据

View File

@ -2,6 +2,7 @@ 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;
@ -93,13 +94,16 @@ 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);
if file.is_none() {
return Err(SystemError::EBADF);
}
let file = fd_table_guard
.get_file_by_fd(fd)
.ok_or(SystemError::EBADF)?;
// drop guard 以避免无法调度的问题
drop(fd_table_guard);
let file = file.unwrap();
if file.mode().contains(FileMode::O_PATH) {
return Err(SystemError::EBADF);
}
return file.read(buf.len(), buf);
}

View File

@ -18,6 +18,7 @@ use crate::{
},
},
libs::spinlock::SpinLock,
mm::truncate::truncate_inode_pages,
process::ProcessManager,
syscall::user_access::check_and_clone_cstr,
};
@ -262,7 +263,7 @@ pub fn do_unlink_at(dirfd: i32, path: &str) -> Result<u64, SystemError> {
}
}
// 禁止在目录上unlink
if inode.unwrap().metadata()?.file_type == FileType::Dir {
if inode.as_ref().unwrap().metadata()?.file_type == FileType::Dir {
return Err(SystemError::EPERM);
}
@ -278,6 +279,10 @@ pub fn do_unlink_at(dirfd: i32, path: &str) -> Result<u64, SystemError> {
// 删除文件
parent_inode.unlink(filename)?;
if let Some(page_cache) = inode.unwrap().page_cache().clone() {
truncate_inode_pages(page_cache, 0);
}
return Ok(0);
}

View File

@ -32,6 +32,7 @@ pub mod no_init;
pub mod page;
pub mod percpu;
pub mod syscall;
pub mod truncate;
pub mod ucontext;
/// 内核INIT进程的用户地址空间结构体仅在process_init中初始化

32
kernel/src/mm/truncate.rs Normal file
View File

@ -0,0 +1,32 @@
use super::page::{Page, PageFlags};
use crate::filesystem::page_cache::PageCache;
use alloc::sync::Arc;
/// # 功能
///
/// 从指定偏移量开始截断与当前文件的所有页缓存目前仅是将文件相关的页缓存页的dirty位去除
///
/// # 参数
///
/// - page_cache: 与文件inode关联的页缓存
/// - start: 偏移量
pub fn truncate_inode_pages(page_cache: Arc<PageCache>, start: usize) {
let guard = page_cache.lock_irqsave();
let pages_count = guard.pages_count();
for i in start..pages_count {
let page = guard.get_page(i);
let page = if let Some(page) = page {
page
} else {
log::warn!("try to truncate page from different page cache");
return;
};
truncate_complete_page(page_cache.clone(), page.clone());
}
}
fn truncate_complete_page(_page_cache: Arc<PageCache>, page: Arc<Page>) {
let mut guard = page.write_irqsave();
guard.remove_flags(PageFlags::PG_DIRTY);
}

View File

@ -505,7 +505,7 @@ impl Syscall {
Err(e)
} else {
let buf = unsafe { core::slice::from_raw_parts_mut(buf, size) };
Self::getcwd(buf).map(|ptr| ptr.data())
Self::getcwd(buf)
}
}

View File

@ -334,11 +334,11 @@ impl<'a> UserBufferWriter<'a> {
}
fn convert_with_offset<T>(src: &mut [u8], offset: usize) -> Result<&mut [T], SystemError> {
if offset >= src.len() {
if offset > src.len() {
return Err(SystemError::EINVAL);
}
let byte_buffer: &mut [u8] = &mut src[offset..];
if byte_buffer.len() % core::mem::size_of::<T>() != 0 || byte_buffer.is_empty() {
if byte_buffer.len() % core::mem::size_of::<T>() != 0 {
return Err(SystemError::EINVAL);
}