From a69d1a93b67843d741c7808ad40cc87389e23e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E9=93=AD=E6=B6=9B?= <1037827920@qq.com> Date: Fri, 27 Jun 2025 10:40:49 +0800 Subject: [PATCH] =?UTF-8?q?fix(fs,=20mm):=20=E4=BF=AE=E5=A4=8Dfs=E3=80=81m?= =?UTF-8?q?m=E4=B8=8A=E6=9C=89=E5=85=B3=E7=B3=BB=E7=BB=9F=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E7=9A=84bug=20(#1208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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) --- kernel/src/filesystem/page_cache.rs | 4 +++ kernel/src/filesystem/vfs/syscall/mod.rs | 5 ++- kernel/src/filesystem/vfs/syscall/sys_read.rs | 14 +++++--- kernel/src/filesystem/vfs/vcore.rs | 7 +++- kernel/src/mm/mod.rs | 1 + kernel/src/mm/truncate.rs | 32 +++++++++++++++++++ kernel/src/syscall/mod.rs | 2 +- kernel/src/syscall/user_access.rs | 4 +-- 8 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 kernel/src/mm/truncate.rs diff --git a/kernel/src/filesystem/page_cache.rs b/kernel/src/filesystem/page_cache.rs index c5902a02..b77e3fdf 100644 --- a/kernel/src/filesystem/page_cache.rs +++ b/kernel/src/filesystem/page_cache.rs @@ -312,6 +312,10 @@ impl InnerPageCache { Ok(()) } + + pub fn pages_count(&self) -> usize { + return self.pages.len(); + } } impl Drop for InnerPageCache { diff --git a/kernel/src/filesystem/vfs/syscall/mod.rs b/kernel/src/filesystem/vfs/syscall/mod.rs index 813a757f..ee841abf 100644 --- a/kernel/src/filesystem/vfs/syscall/mod.rs +++ b/kernel/src/filesystem/vfs/syscall/mod.rs @@ -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 { + pub fn getcwd(buf: &mut [u8]) -> Result { 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); } /// # 获取目录中的数据 diff --git a/kernel/src/filesystem/vfs/syscall/sys_read.rs b/kernel/src/filesystem/vfs/syscall/sys_read.rs index 2d57c77c..b4cd26a0 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_read.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_read.rs @@ -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 { 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); } diff --git a/kernel/src/filesystem/vfs/vcore.rs b/kernel/src/filesystem/vfs/vcore.rs index b96796f0..5764652f 100644 --- a/kernel/src/filesystem/vfs/vcore.rs +++ b/kernel/src/filesystem/vfs/vcore.rs @@ -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 { } } // 禁止在目录上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 { // 删除文件 parent_inode.unlink(filename)?; + if let Some(page_cache) = inode.unwrap().page_cache().clone() { + truncate_inode_pages(page_cache, 0); + } + return Ok(0); } diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 4df64ee0..3944c995 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -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中初始化) diff --git a/kernel/src/mm/truncate.rs b/kernel/src/mm/truncate.rs new file mode 100644 index 00000000..bc3f1269 --- /dev/null +++ b/kernel/src/mm/truncate.rs @@ -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, 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, page: Arc) { + let mut guard = page.write_irqsave(); + guard.remove_flags(PageFlags::PG_DIRTY); +} diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 99057fd3..13a58529 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -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) } } diff --git a/kernel/src/syscall/user_access.rs b/kernel/src/syscall/user_access.rs index 36c3fe09..5fab8ba3 100644 --- a/kernel/src/syscall/user_access.rs +++ b/kernel/src/syscall/user_access.rs @@ -334,11 +334,11 @@ impl<'a> UserBufferWriter<'a> { } fn convert_with_offset(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::() != 0 || byte_buffer.is_empty() { + if byte_buffer.len() % core::mem::size_of::() != 0 { return Err(SystemError::EINVAL); }