From eda57666d548475401c0b0454f11b1e69e2af468 Mon Sep 17 00:00:00 2001 From: ZIYAN137 Date: Thu, 28 May 2026 01:56:20 +0800 Subject: [PATCH 1/5] refactor(mm): propagate typed paging errors --- os/src/arch/arch.rs | 13 ++- os/src/arch/arch_impl.rs | 43 +++++----- os/src/arch/loongarch/mm/page_table.rs | 51 +++++++----- os/src/arch/memory_impl.rs | 66 +++++++-------- os/src/arch/mock/arch.rs | 40 ++++++---- os/src/arch/mock/mm.rs | 26 +++--- os/src/arch/riscv/mm/page_table.rs | 57 +++++++------ os/src/arch/virtual_memory.rs | 28 ++++--- os/src/kernel/task/exec_loader.rs | 80 +++++++++---------- os/src/mm/memory_space/space/address_space.rs | 12 +-- os/src/mm/memory_space/space/elf_loader.rs | 2 +- os/src/mm/memory_space/space/kernel_space.rs | 10 +-- os/src/mm/memory_space/space/mod.rs | 2 +- os/src/mm/memory_space/space/tests.rs | 51 +++++++----- os/src/mm/mod.rs | 4 +- os/src/mm/page_table/inner.rs | 8 +- os/src/mm/page_table/mod.rs | 4 +- 17 files changed, 271 insertions(+), 226 deletions(-) diff --git a/os/src/arch/arch.rs b/os/src/arch/arch.rs index b0bf1950..8cc515cc 100644 --- a/os/src/arch/arch.rs +++ b/os/src/arch/arch.rs @@ -9,6 +9,7 @@ //! 确保架构层与内核其余部分的解耦。 use crate::arch::{address::UA, cpu_ops::CpuOps, virtual_memory::VirtualMemory}; +use crate::mm::page_table::PagingError; /// 顶层架构抽象 trait。 /// @@ -48,13 +49,13 @@ pub trait Arch: CpuOps + VirtualMemory { /// - `src` 必须是有效的用户空间虚拟地址 /// - `dst` 必须指向足够大的内核缓冲区 /// - `len` 字节必须在合法范围内 - unsafe fn copy_from_user(src: UA, dst: *mut u8, len: usize) -> Result<(), ()>; + unsafe fn copy_from_user(src: UA, dst: *mut u8, len: usize) -> Result<(), PagingError>; /// 尝试从用户空间复制数据(非阻塞版本,不处理缺页) /// /// # Safety /// 同上 - unsafe fn try_copy_from_user(src: UA, dst: *mut u8, len: usize) -> Result<(), ()>; + unsafe fn try_copy_from_user(src: UA, dst: *mut u8, len: usize) -> Result<(), PagingError>; /// 从内核空间复制数据到用户空间 /// @@ -63,13 +64,17 @@ pub trait Arch: CpuOps + VirtualMemory { /// - `dst` 必须是有效的用户空间虚拟地址 /// - `src` 必须指向有效内核数据 /// - `len` 字节必须在合法范围内 - unsafe fn copy_to_user(src: *const u8, dst: UA, len: usize) -> Result<(), ()>; + unsafe fn copy_to_user(src: *const u8, dst: UA, len: usize) -> Result<(), PagingError>; /// 从用户空间复制以 '\0' 结尾的字符串 /// /// # Safety /// 同上 - unsafe fn copy_strn_from_user(src: UA, dst: *mut u8, max_len: usize) -> Result; + unsafe fn copy_strn_from_user( + src: UA, + dst: *mut u8, + max_len: usize, + ) -> Result; // ---- 系统信息 ---- diff --git a/os/src/arch/arch_impl.rs b/os/src/arch/arch_impl.rs index bf1615a0..46924bc1 100644 --- a/os/src/arch/arch_impl.rs +++ b/os/src/arch/arch_impl.rs @@ -9,6 +9,7 @@ macro_rules! impl_arch { ($arch:ty, $process_space:ty, $kernel_space:ty) => { use $crate::arch::virtual_memory::VirtualMemory; use $crate::mm::address::Ppn; + use $crate::mm::page_table::PagingError; use $crate::sync::SpinLock; lazy_static::lazy_static! { @@ -46,11 +47,11 @@ macro_rules! impl_arch { src: $crate::arch::address::UA, dst: *mut u8, len: usize, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { let src = src.as_usize(); validate_user_copy_range(src, len, false)?; if len != 0 && dst.is_null() { - return Err(()); + return Err(PagingError::InvalidAddress); } let _guard = trap::SumGuard::new(); unsafe { core::ptr::copy_nonoverlapping(src as *const u8, dst, len) }; @@ -61,7 +62,7 @@ macro_rules! impl_arch { src: $crate::arch::address::UA, dst: *mut u8, len: usize, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { unsafe { Self::copy_from_user(src, dst, len) } } @@ -69,11 +70,11 @@ macro_rules! impl_arch { src: *const u8, dst: $crate::arch::address::UA, len: usize, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { let dst = dst.as_usize(); validate_user_copy_range(dst, len, true)?; if len != 0 && src.is_null() { - return Err(()); + return Err(PagingError::InvalidAddress); } let _guard = trap::SumGuard::new(); unsafe { core::ptr::copy_nonoverlapping(src, dst as *mut u8, len) }; @@ -84,18 +85,18 @@ macro_rules! impl_arch { src: $crate::arch::address::UA, dst: *mut u8, max_len: usize, - ) -> Result { + ) -> Result { let src = src.as_usize(); if !(constant::USER_BASE..=<$arch as VirtualMemory>::USER_TOP).contains(&src) { - return Err(()); + return Err(PagingError::InvalidAddress); } if max_len != 0 && dst.is_null() { - return Err(()); + return Err(PagingError::InvalidAddress); } let _guard = trap::SumGuard::new(); let mut i = 0; while i < max_len { - let cur = src.checked_add(i).ok_or(())?; + let cur = src.checked_add(i).ok_or(PagingError::InvalidAddress)?; validate_user_copy_range(cur, 1, false)?; let byte = unsafe { core::ptr::read_volatile(cur as *const u8) }; unsafe { *dst.add(i) = byte }; @@ -137,7 +138,11 @@ macro_rules! impl_arch { } } - fn validate_user_copy_range(start: usize, len: usize, write: bool) -> Result<(), ()> { + fn validate_user_copy_range( + start: usize, + len: usize, + write: bool, + ) -> Result<(), PagingError> { use $crate::mm::address::{PageNum, VA, Vpn}; use $crate::mm::page_table::{PageTableInner, UniversalPTEFlag}; @@ -145,12 +150,12 @@ macro_rules! impl_arch { return Ok(()); } if !(constant::USER_BASE..=<$arch as VirtualMemory>::USER_TOP).contains(&start) { - return Err(()); + return Err(PagingError::InvalidAddress); } - let end = start.checked_add(len).ok_or(())?; - let last = end.checked_sub(1).ok_or(())?; + let end = start.checked_add(len).ok_or(PagingError::InvalidAddress)?; + let last = end.checked_sub(1).ok_or(PagingError::InvalidAddress)?; if last > <$arch as VirtualMemory>::USER_TOP { - return Err(()); + return Err(PagingError::InvalidAddress); } let space = $crate::kernel::current_memory_space(); @@ -158,21 +163,21 @@ macro_rules! impl_arch { let mut cur = start; while cur < end { let vpn = Vpn::from_addr_floor(VA::from_usize(cur)); - let (_, _, flags) = guard.page_table().walk(vpn).map_err(|_| ())?; + let (_, _, flags) = guard.page_table().walk(vpn)?; let required = UniversalPTEFlag::VALID | UniversalPTEFlag::USER_ACCESSIBLE; if !flags.contains(required) { - return Err(()); + return Err(PagingError::PermissionDenied); } if write { if !flags.contains(UniversalPTEFlag::WRITEABLE) { - return Err(()); + return Err(PagingError::PermissionDenied); } } else if !flags.contains(UniversalPTEFlag::READABLE) { - return Err(()); + return Err(PagingError::PermissionDenied); } let next_page = (cur & !($crate::config::PAGE_SIZE - 1)) .checked_add($crate::config::PAGE_SIZE) - .ok_or(())?; + .ok_or(PagingError::InvalidAddress)?; cur = core::cmp::min(next_page, end); } Ok(()) diff --git a/os/src/arch/loongarch/mm/page_table.rs b/os/src/arch/loongarch/mm/page_table.rs index 54cb3702..ba0c8fe6 100644 --- a/os/src/arch/loongarch/mm/page_table.rs +++ b/os/src/arch/loongarch/mm/page_table.rs @@ -159,18 +159,18 @@ impl PageTableInnerTrait for PageTableInner { } /// 创建新的用户页表 - fn new() -> Self { - let frame = alloc_frame().expect("Failed to allocate root page table frame"); + fn new() -> PagingResult { + let frame = alloc_frame().ok_or(PagingError::FrameAllocFailed)?; let root_ppn = frame.ppn(); // 清零根页表 Self::clear_page_table(root_ppn); - Self { + Ok(Self { root_ppn, frames: alloc::vec![frame], is_user: true, - } + }) } /// 从已有的 PPN 创建页表(不拥有帧所有权) @@ -183,18 +183,18 @@ impl PageTableInnerTrait for PageTableInner { } /// 创建新的内核页表 - fn new_as_kernel_table() -> Self { - let frame = alloc_frame().expect("Failed to allocate kernel page table frame"); + fn new_as_kernel_table() -> PagingResult { + let frame = alloc_frame().ok_or(PagingError::FrameAllocFailed)?; let root_ppn = frame.ppn(); // 清零根页表 Self::clear_page_table(root_ppn); - Self { + Ok(Self { root_ppn, frames: alloc::vec![frame], is_user: false, - } + }) } fn root_ppn(&self) -> Ppn { @@ -251,7 +251,7 @@ impl PageTableInnerTrait for PageTableInner { ppn: Ppn, _page_size: PageSize, flags: UniversalPTEFlag, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { // 验证标志位:叶子节点必须至少设置可读或可执行 if !flags.intersects( UniversalPTEFlag::READABLE | UniversalPTEFlag::WRITEABLE | UniversalPTEFlag::EXECUTABLE, @@ -305,7 +305,7 @@ impl PageTableInnerTrait for PageTableInner { } /// 解除虚拟页的映射 - fn unmap(&mut self, vpn: Vpn) -> PagingResult<()> { + fn unmap(&mut self, vpn: Vpn) -> Result<(), PagingError> { let mut current_ppn = self.root_ppn; let vpn_value = vpn.as_usize(); @@ -337,13 +337,13 @@ impl PageTableInnerTrait for PageTableInner { target_ppn: Ppn, page_size: PageSize, flags: UniversalPTEFlag, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { self.unmap(vpn)?; self.map(vpn, target_ppn, page_size, flags) } /// 更新页表项标志位 - fn update_flags(&mut self, vpn: Vpn, flags: UniversalPTEFlag) -> PagingResult<()> { + fn update_flags(&mut self, vpn: Vpn, flags: UniversalPTEFlag) -> Result<(), PagingError> { let mut current_ppn = self.root_ppn; let vpn_value = vpn.as_usize(); @@ -402,7 +402,7 @@ impl PageTableInner { page_size: PageSize, flags: UniversalPTEFlag, _batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::map(self, vpn, ppn, page_size, flags)?; >::tlb_flush(vpn); Ok(()) @@ -413,7 +413,7 @@ impl PageTableInner { &mut self, vpn: Vpn, _batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::unmap(self, vpn)?; >::tlb_flush(vpn); Ok(()) @@ -425,7 +425,7 @@ impl PageTableInner { vpn: Vpn, flags: UniversalPTEFlag, _batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::update_flags(self, vpn, flags)?; >::tlb_flush(vpn); Ok(()) @@ -530,9 +530,16 @@ mod page_table_tests { use crate::mm::page_table::PageTableInner as PageTableInnerTrait; use crate::{kassert, test_case}; + fn new_page_table() -> PageTableInner { + match PageTableInner::new() { + Ok(pt) => pt, + Err(err) => panic!("failed to create test page table: {:?}", err), + } + } + // 1. 页表创建测试 test_case!(test_pt_create, { - let pt = PageTableInner::new(); + let pt = new_page_table(); // 根 PPN 应该有效 (大于 0) kassert!(pt.root_ppn().as_usize() > 0); // 默认创建为用户页表 @@ -541,7 +548,7 @@ mod page_table_tests { // 2. 映射与转换测试 test_case!(test_pt_map_translate, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); @@ -560,7 +567,7 @@ mod page_table_tests { // 3. 解除映射测试 test_case!(test_pt_unmap, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); @@ -580,7 +587,7 @@ mod page_table_tests { // 4. 错误测试:已映射 test_case!(test_pt_error_already_mapped, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); // 第一次映射成功 @@ -604,7 +611,7 @@ mod page_table_tests { // 5. 页表遍历 (Walk) 测试 test_case!(test_pt_walk, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); let original_flags = UniversalPTEFlag::kernel_rw(); @@ -629,7 +636,7 @@ mod page_table_tests { // 6. 更新标志位测试 test_case!(test_pt_update_flags, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); @@ -652,7 +659,7 @@ mod page_table_tests { // 7. 多重映射测试 test_case!(test_pt_multiple_mappings, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); // 映射多个 VPN for i in 0..10 { diff --git a/os/src/arch/memory_impl.rs b/os/src/arch/memory_impl.rs index ede40a25..0f2a89f5 100644 --- a/os/src/arch/memory_impl.rs +++ b/os/src/arch/memory_impl.rs @@ -21,7 +21,7 @@ macro_rules! impl_virtual_memory { }; use crate::mm::memory_space::{MemorySpace, with_kernel_space}; use crate::mm::page_table::PageTableInner; - use crate::mm::page_table::{PageSize, UniversalPTEFlag}; + use crate::mm::page_table::{PageSize, PagingError, UniversalPTEFlag}; pub struct $process_type { #[allow(dead_code)] @@ -30,10 +30,10 @@ macro_rules! impl_virtual_memory { #[allow(dead_code)] impl $process_type { - pub fn new() -> Self { - Self { - inner: MemorySpace::new(), - } + pub fn new() -> Result { + Ok(Self { + inner: MemorySpace::new()?, + }) } pub fn inner(&self) -> &MemorySpace { @@ -46,8 +46,8 @@ macro_rules! impl_virtual_memory { } impl UserAddressSpace for $process_type { - fn new() -> Result { - Ok(Self::new()) + fn new() -> Result { + Self::new() } fn activate(&self) { @@ -62,7 +62,7 @@ macro_rules! impl_virtual_memory { page: PageFrame, va: usize, perms: PtePermissions, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { let ppn = Ppn::from_usize(page.ppn); let vaddr = VA::from_usize(va); let vpn = Vpn::from_addr_floor(vaddr); @@ -70,14 +70,13 @@ macro_rules! impl_virtual_memory { self.inner .page_table_mut() .map(vpn, ppn, PageSize::Size4K, flags) - .map_err(|_| ()) } - fn unmap(&mut self, va: usize) -> Result { + fn unmap(&mut self, va: usize) -> Result { let vaddr = VA::from_usize(va); let vpn = Vpn::from_addr_floor(vaddr); - let (ppn, _size, _flags) = self.inner.page_table().walk(vpn).map_err(|_| ())?; - self.inner.page_table_mut().unmap(vpn).map_err(|_| ())?; + let (ppn, _size, _flags) = self.inner.page_table().walk(vpn)?; + self.inner.page_table_mut().unmap(vpn)?; Ok(PageFrame { ppn: ppn.as_usize(), }) @@ -88,7 +87,7 @@ macro_rules! impl_virtual_memory { va: usize, new_page: PageFrame, perms: PtePermissions, - ) -> Result { + ) -> Result { let old = self.unmap(va)?; self.map_page(new_page, va, perms)?; Ok(old) @@ -98,21 +97,21 @@ macro_rules! impl_virtual_memory { &mut self, region: VirtMemoryRegion, perms: PtePermissions, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { let start_vpn = Vpn::from_addr_floor(VA::from_usize(region.start_va)); let end_vpn = Vpn::from_addr_ceil(VA::from_usize(region.start_va + region.len)); let flags = UniversalPTEFlag::from_bits_truncate(perms.bits()); for vpn in VpnRange::new(start_vpn, end_vpn) { - self.inner - .page_table_mut() - .update_flags(vpn, flags) - .map_err(|_| ())?; + self.inner.page_table_mut().update_flags(vpn, flags)?; } Ok(()) } - fn unmap_range(&mut self, region: VirtMemoryRegion) -> Result, ()> { + fn unmap_range( + &mut self, + region: VirtMemoryRegion, + ) -> Result, PagingError> { let start_vpn = Vpn::from_addr_floor(VA::from_usize(region.start_va)); let end_vpn = Vpn::from_addr_ceil(VA::from_usize(region.start_va + region.len)); let range = VpnRange::new(start_vpn, end_vpn); @@ -120,7 +119,7 @@ macro_rules! impl_virtual_memory { let mut frames = Vec::new(); for vpn in range { if let Ok((ppn, _size, _flags)) = self.inner.page_table().walk(vpn) { - self.inner.page_table_mut().unmap(vpn).map_err(|_| ())?; + self.inner.page_table_mut().unmap(vpn)?; frames.push(PageFrame { ppn: ppn.as_usize(), }); @@ -147,7 +146,7 @@ macro_rules! impl_virtual_memory { region: VirtMemoryRegion, other: &mut Self, perms: PtePermissions, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { let start_va = region.start_va & !(PAGE_SIZE - 1); let end_va = (region.start_va + region.len + PAGE_SIZE - 1) & !(PAGE_SIZE - 1); let flags = UniversalPTEFlag::from_bits_truncate(perms.bits()); @@ -159,12 +158,8 @@ macro_rules! impl_virtual_memory { other .inner .page_table_mut() - .map(vpn, ppn, PageSize::Size4K, flags) - .map_err(|_| ())?; - self.inner - .page_table_mut() - .update_flags(vpn, flags) - .map_err(|_| ())?; + .map(vpn, ppn, PageSize::Size4K, flags)?; + self.inner.page_table_mut().update_flags(vpn, flags)?; } } Ok(()) @@ -181,7 +176,7 @@ macro_rules! impl_virtual_memory { } impl KernAddressSpace for $kernel_type { - fn map_mmio(&mut self, region: PhysMemoryRegion) -> Result { + fn map_mmio(&mut self, region: PhysMemoryRegion) -> Result { let pa = PA::from_usize(region.start_pa); let va = pa.to_va(); @@ -194,9 +189,12 @@ macro_rules! impl_virtual_memory { while cur_pa < end_pa { let ppn = Ppn::from_addr_floor(PA::from_usize(cur_pa)); let vpn = Vpn::from_addr_floor(VA::from_usize(cur_va)); - ks.page_table_mut() - .map(vpn, ppn, PageSize::Size4K, UniversalPTEFlag::kernel_rw()) - .map_err(|_| ())?; + ks.page_table_mut().map( + vpn, + ppn, + PageSize::Size4K, + UniversalPTEFlag::kernel_rw(), + )?; cur_pa += PAGE_SIZE; cur_va += PAGE_SIZE; } @@ -211,7 +209,7 @@ macro_rules! impl_virtual_memory { phys_range: PhysMemoryRegion, virt_range: VirtMemoryRegion, perms: PtePermissions, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { with_kernel_space(|ks| { let start_pa = phys_range.start_pa & !(PAGE_SIZE - 1); let end_pa = phys_range.start_pa + phys_range.len; @@ -222,9 +220,7 @@ macro_rules! impl_virtual_memory { let ppn = Ppn::from_addr_floor(PA::from_usize(start_pa + offset)); let vpn = Vpn::from_addr_floor(VA::from_usize(start_va + offset)); let flags = UniversalPTEFlag::from_bits_truncate(perms.bits()); - ks.page_table_mut() - .map(vpn, ppn, PageSize::Size4K, flags) - .map_err(|_| ())?; + ks.page_table_mut().map(vpn, ppn, PageSize::Size4K, flags)?; offset += PAGE_SIZE; } Ok(()) diff --git a/os/src/arch/mock/arch.rs b/os/src/arch/mock/arch.rs index 8d10658b..55c7ba66 100644 --- a/os/src/arch/mock/arch.rs +++ b/os/src/arch/mock/arch.rs @@ -12,6 +12,7 @@ use crate::arch::virtual_memory::{ KernAddressSpace, PageFrame, PageInfo, PhysMemoryRegion, PtePermissions, UserAddressSpace, VirtMemoryRegion, VirtualMemory, }; +use crate::mm::page_table::PagingError; use crate::sync::SpinLock; // 在非目标架构上,MockArch 的 UserContext 应等于 arch::kernel::context::Context, @@ -68,7 +69,7 @@ impl MockAddressSpace { } impl UserAddressSpace for MockAddressSpace { - fn new() -> Result { + fn new() -> Result { Ok(Self::new()) } @@ -76,17 +77,22 @@ impl UserAddressSpace for MockAddressSpace { fn deactivate(&self) {} - fn map_page(&mut self, page: PageFrame, va: usize, perms: PtePermissions) -> Result<(), ()> { + fn map_page( + &mut self, + page: PageFrame, + va: usize, + perms: PtePermissions, + ) -> Result<(), PagingError> { self.mappings.push((va, page, perms)); Ok(()) } - fn unmap(&mut self, va: usize) -> Result { + fn unmap(&mut self, va: usize) -> Result { if let Some(pos) = self.mappings.iter().position(|(v, _, _)| *v == va) { let (_, frame, _) = self.mappings.remove(pos); Ok(frame) } else { - Err(()) + Err(PagingError::NotMapped) } } @@ -95,7 +101,7 @@ impl UserAddressSpace for MockAddressSpace { va: usize, new_page: PageFrame, perms: PtePermissions, - ) -> Result { + ) -> Result { let old = self.unmap(va)?; self.map_page(new_page, va, perms)?; Ok(old) @@ -105,11 +111,11 @@ impl UserAddressSpace for MockAddressSpace { &mut self, _region: VirtMemoryRegion, _perms: PtePermissions, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { Ok(()) } - fn unmap_range(&mut self, region: VirtMemoryRegion) -> Result, ()> { + fn unmap_range(&mut self, region: VirtMemoryRegion) -> Result, PagingError> { let start = region.start_va; let end = start + region.len; let frames: Vec<_> = self @@ -137,13 +143,13 @@ impl UserAddressSpace for MockAddressSpace { region: VirtMemoryRegion, _other: &mut Self, _perms: PtePermissions, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { self.protect_range(region, _perms) } } impl KernAddressSpace for MockAddressSpace { - fn map_mmio(&mut self, _region: PhysMemoryRegion) -> Result { + fn map_mmio(&mut self, _region: PhysMemoryRegion) -> Result { Ok(0) } @@ -152,7 +158,7 @@ impl KernAddressSpace for MockAddressSpace { _phys_range: PhysMemoryRegion, _virt_range: VirtMemoryRegion, _perms: PtePermissions, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { Ok(()) } } @@ -217,10 +223,10 @@ mod mock_arch_impl { src: crate::arch::address::UA, dst: *mut u8, len: usize, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { let src = src.as_usize(); if len != 0 && (src == 0 || dst.is_null()) { - return Err(()); + return Err(PagingError::InvalidAddress); } unsafe { core::ptr::copy_nonoverlapping(src as *const u8, dst, len) }; Ok(()) @@ -230,7 +236,7 @@ mod mock_arch_impl { src: crate::arch::address::UA, dst: *mut u8, len: usize, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { unsafe { Self::copy_from_user(src, dst, len) } } @@ -238,10 +244,10 @@ mod mock_arch_impl { src: *const u8, dst: crate::arch::address::UA, len: usize, - ) -> Result<(), ()> { + ) -> Result<(), PagingError> { let dst = dst.as_usize(); if len != 0 && (src.is_null() || dst == 0) { - return Err(()); + return Err(PagingError::InvalidAddress); } unsafe { core::ptr::copy_nonoverlapping(src, dst as *mut u8, len) }; Ok(()) @@ -251,10 +257,10 @@ mod mock_arch_impl { src: crate::arch::address::UA, dst: *mut u8, max_len: usize, - ) -> Result { + ) -> Result { let src = src.as_usize(); if max_len != 0 && (src == 0 || dst.is_null()) { - return Err(()); + return Err(PagingError::InvalidAddress); } let mut i = 0; while i < max_len { diff --git a/os/src/arch/mock/mm.rs b/os/src/arch/mock/mm.rs index f565ebfe..723ac948 100644 --- a/os/src/arch/mock/mm.rs +++ b/os/src/arch/mock/mm.rs @@ -116,11 +116,11 @@ impl PageTableInnerTrait for PageTableInner { Ppn::from_usize(0) } - fn new() -> Self { - Self { + fn new() -> PagingResult { + Ok(Self { root: Ppn::from_usize(0x80000), is_user: true, - } + }) } fn from_ppn(ppn: Ppn) -> Self { @@ -130,11 +130,11 @@ impl PageTableInnerTrait for PageTableInner { } } - fn new_as_kernel_table() -> Self { - Self { + fn new_as_kernel_table() -> PagingResult { + Ok(Self { root: Ppn::from_usize(0x80000), is_user: false, - } + }) } fn root_ppn(&self) -> Ppn { @@ -155,11 +155,11 @@ impl PageTableInnerTrait for PageTableInner { _ppn: Ppn, _page_size: PageSize, _flags: UniversalPTEFlag, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { Ok(()) } - fn unmap(&mut self, _vpn: Vpn) -> PagingResult<()> { + fn unmap(&mut self, _vpn: Vpn) -> Result<(), PagingError> { Ok(()) } @@ -169,12 +169,12 @@ impl PageTableInnerTrait for PageTableInner { target_ppn: Ppn, _page_size: PageSize, _flags: UniversalPTEFlag, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { self.root = target_ppn; Ok(()) } - fn update_flags(&mut self, _vpn: Vpn, _flags: UniversalPTEFlag) -> PagingResult<()> { + fn update_flags(&mut self, _vpn: Vpn, _flags: UniversalPTEFlag) -> Result<(), PagingError> { Ok(()) } @@ -193,7 +193,7 @@ impl PageTableInner { page_size: PageSize, flags: UniversalPTEFlag, _batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::map(self, vpn, ppn, page_size, flags) } @@ -201,7 +201,7 @@ impl PageTableInner { &mut self, vpn: Vpn, _batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::unmap(self, vpn) } @@ -210,7 +210,7 @@ impl PageTableInner { vpn: Vpn, flags: UniversalPTEFlag, _batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::update_flags(self, vpn, flags) } } diff --git a/os/src/arch/riscv/mm/page_table.rs b/os/src/arch/riscv/mm/page_table.rs index 28fd0b03..554aba4e 100644 --- a/os/src/arch/riscv/mm/page_table.rs +++ b/os/src/arch/riscv/mm/page_table.rs @@ -83,13 +83,13 @@ impl PageTableInnerTrait for PageTableInner { } // 创建一个新的用户页表 - fn new() -> Self { - let frame = alloc_frame().unwrap(); // 分配根页表帧 - Self { + fn new() -> PagingResult { + let frame = alloc_frame().ok_or(PagingError::FrameAllocFailed)?; + Ok(Self { root: frame.ppn(), frames: alloc::vec![frame], // 存储根帧 is_user: true, - } + }) } // 从已有的 PPN 创建页表 (用于内核页表等,不拥有其帧) @@ -102,13 +102,13 @@ impl PageTableInnerTrait for PageTableInner { } // 创建一个新的内核页表 - fn new_as_kernel_table() -> Self { - let frame = alloc_frame().unwrap(); // 分配根页表帧 - Self { + fn new_as_kernel_table() -> PagingResult { + let frame = alloc_frame().ok_or(PagingError::FrameAllocFailed)?; + Ok(Self { root: frame.ppn(), frames: alloc::vec![frame], is_user: false, - } + }) } // 获取页表根 PPN @@ -180,7 +180,7 @@ impl PageTableInnerTrait for PageTableInner { ppn: Ppn, _page_size: PageSize, flags: UniversalPTEFlag, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { // 验证标志位:叶子节点必须至少有 R/W/X 之一被设置 if !flags.intersects( UniversalPTEFlag::READABLE | UniversalPTEFlag::WRITEABLE | UniversalPTEFlag::EXECUTABLE, @@ -248,7 +248,7 @@ impl PageTableInnerTrait for PageTableInner { } // 解除虚拟页号 (VPN) 映射 (Unmap) - fn unmap(&mut self, vpn: Vpn) -> PagingResult<()> { + fn unmap(&mut self, vpn: Vpn) -> Result<(), PagingError> { let mut current_ppn = self.root; let vpn_value = vpn.as_usize(); @@ -290,7 +290,7 @@ impl PageTableInnerTrait for PageTableInner { target_ppn: Ppn, page_size: PageSize, flags: UniversalPTEFlag, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { // 先解除旧映射 self.unmap(vpn)?; // 再映射到新的物理页 @@ -298,7 +298,7 @@ impl PageTableInnerTrait for PageTableInner { } // 更新指定 VPN 的页表项标志位 - fn update_flags(&mut self, vpn: Vpn, flags: UniversalPTEFlag) -> PagingResult<()> { + fn update_flags(&mut self, vpn: Vpn, flags: UniversalPTEFlag) -> Result<(), PagingError> { let mut current_ppn = self.root; let vpn_value = vpn.as_usize(); @@ -407,7 +407,7 @@ impl PageTableInner { page_size: PageSize, flags: UniversalPTEFlag, batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::map(self, vpn, ppn, page_size, flags)?; // 总是刷新本地 TLB >::tlb_flush(vpn); @@ -426,7 +426,7 @@ impl PageTableInner { &mut self, vpn: Vpn, batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::unmap(self, vpn)?; // 总是刷新本地 TLB >::tlb_flush(vpn); @@ -446,7 +446,7 @@ impl PageTableInner { vpn: Vpn, flags: UniversalPTEFlag, batch: Option<&mut TlbBatchContext>, - ) -> PagingResult<()> { + ) -> Result<(), PagingError> { >::update_flags(self, vpn, flags)?; // 总是刷新本地 TLB >::tlb_flush(vpn); @@ -521,9 +521,16 @@ mod page_table_tests { use crate::mm::page_table::PageTableInner as PageTableInnerTrait; use crate::{kassert, test_case}; + fn new_page_table() -> PageTableInner { + match PageTableInner::new() { + Ok(pt) => pt, + Err(err) => panic!("failed to create test page table: {:?}", err), + } + } + // 1. 页表创建测试 test_case!(test_pt_create, { - let pt = PageTableInner::new(); + let pt = new_page_table(); // 根 PPN 应该有效 (大于 0) kassert!(pt.root_ppn().as_usize() > 0); // 默认创建为用户页表 @@ -532,7 +539,7 @@ mod page_table_tests { // 2. 映射与转换测试 test_case!(test_pt_map_translate, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); @@ -551,7 +558,7 @@ mod page_table_tests { // 3. 解除映射测试 test_case!(test_pt_unmap, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); @@ -571,7 +578,7 @@ mod page_table_tests { // 4. 错误测试:已映射 test_case!(test_pt_error_already_mapped, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); // 第一次映射成功 @@ -595,7 +602,7 @@ mod page_table_tests { // 5. 页表遍历 (Walk) 测试 test_case!(test_pt_walk, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); let original_flags = UniversalPTEFlag::kernel_rw(); @@ -616,7 +623,7 @@ mod page_table_tests { // 6. 更新标志位测试 test_case!(test_pt_update_flags, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x1000); let ppn = Ppn::from_usize(0x80000); @@ -638,7 +645,7 @@ mod page_table_tests { // 7. 多重映射测试 test_case!(test_pt_multiple_mappings, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); // 映射多个 VPN for i in 0..10 { @@ -668,7 +675,7 @@ mod page_table_tests { /// 测试页表映射触发 TLB shootdown test_case!(test_page_table_map_with_tlb_flush, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x10000); let ppn = Ppn::from_usize(0x80000); @@ -683,7 +690,7 @@ mod page_table_tests { /// 测试页表解除映射触发 TLB shootdown test_case!(test_page_table_unmap_with_tlb_flush, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x20000); let ppn = Ppn::from_usize(0x81000); @@ -702,7 +709,7 @@ mod page_table_tests { /// 测试页表权限更新触发 TLB shootdown test_case!(test_page_table_update_flags_with_tlb_flush, { - let mut pt = PageTableInner::new(); + let mut pt = new_page_table(); let vpn = Vpn::from_usize(0x30000); let ppn = Ppn::from_usize(0x82000); diff --git a/os/src/arch/virtual_memory.rs b/os/src/arch/virtual_memory.rs index 8c8f70a1..2b46388f 100644 --- a/os/src/arch/virtual_memory.rs +++ b/os/src/arch/virtual_memory.rs @@ -5,6 +5,7 @@ use alloc::vec::Vec; use crate::arch::cpu_ops::CpuOps; +use crate::mm::page_table::PagingError; use crate::sync::SpinLock; // ============================================================================ @@ -84,7 +85,7 @@ pub struct PhysMemoryRegion { /// 这个 trait 完全解耦了进程地址空间的操作。架构相关实现只需填充方法。 pub trait UserAddressSpace: Send + Sync { /// 新建空页表 - fn new() -> Result + fn new() -> Result where Self: Sized; @@ -95,10 +96,15 @@ pub trait UserAddressSpace: Send + Sync { fn deactivate(&self); /// 映射一页 - fn map_page(&mut self, page: PageFrame, va: usize, perms: PtePermissions) -> Result<(), ()>; + fn map_page( + &mut self, + page: PageFrame, + va: usize, + perms: PtePermissions, + ) -> Result<(), PagingError>; /// 取消映射一页,返回被解除的页帧 - fn unmap(&mut self, va: usize) -> Result; + fn unmap(&mut self, va: usize) -> Result; /// 重新映射一页(替换) fn remap( @@ -106,13 +112,17 @@ pub trait UserAddressSpace: Send + Sync { va: usize, new_page: PageFrame, perms: PtePermissions, - ) -> Result; + ) -> Result; /// 保护一个内存范围 - fn protect_range(&mut self, region: VirtMemoryRegion, perms: PtePermissions) -> Result<(), ()>; + fn protect_range( + &mut self, + region: VirtMemoryRegion, + perms: PtePermissions, + ) -> Result<(), PagingError>; /// 取消映射一个内存范围,返回被解除的所有页帧 - fn unmap_range(&mut self, region: VirtMemoryRegion) -> Result, ()>; + fn unmap_range(&mut self, region: VirtMemoryRegion) -> Result, PagingError>; /// 翻译虚拟地址 → 页信息 fn translate(&self, va: usize) -> Option; @@ -123,7 +133,7 @@ pub trait UserAddressSpace: Send + Sync { region: VirtMemoryRegion, other: &mut Self, perms: PtePermissions, - ) -> Result<(), ()>; + ) -> Result<(), PagingError>; } // ============================================================================ @@ -133,7 +143,7 @@ pub trait UserAddressSpace: Send + Sync { /// 内核地址空间抽象。 pub trait KernAddressSpace: Send { /// 映射 MMIO 区域 - fn map_mmio(&mut self, region: PhysMemoryRegion) -> Result; + fn map_mmio(&mut self, region: PhysMemoryRegion) -> Result; /// 映射普通内存区域 fn map_normal( @@ -141,7 +151,7 @@ pub trait KernAddressSpace: Send { phys_range: PhysMemoryRegion, virt_range: VirtMemoryRegion, perms: PtePermissions, - ) -> Result<(), ()>; + ) -> Result<(), PagingError>; } // ============================================================================ diff --git a/os/src/kernel/task/exec_loader.rs b/os/src/kernel/task/exec_loader.rs index 884f0f53..18849639 100644 --- a/os/src/kernel/task/exec_loader.rs +++ b/os/src/kernel/task/exec_loader.rs @@ -16,6 +16,18 @@ pub enum ExecImageError { Paging(PagingError), } +impl From for ExecImageError { + fn from(err: FsError) -> Self { + Self::Fs(err) + } +} + +impl From for ExecImageError { + fn from(err: PagingError) -> Self { + Self::Paging(err) + } +} + pub struct PreparedExecImage { pub space: MemorySpace, /// 初始 PC:无动态链接器时为程序入口;有 PT_INTERP 时为动态链接器入口 @@ -86,7 +98,7 @@ fn read_exact_at(inode: &dyn Inode, offset: usize, buf: &mut [u8]) -> Result<(), while read_total < buf.len() { let n = inode .read_at(offset + read_total, &mut buf[read_total..]) - .map_err(ExecImageError::Fs)?; + .map_err(ExecImageError::from)?; if n == 0 { return Err(ExecImageError::InvalidElf); } @@ -223,7 +235,7 @@ fn load_segments_into_space( } else { let map_start = space .find_free_region(total_size, crate::config::PAGE_SIZE) - .ok_or(ExecImageError::Paging(PagingError::OutOfMemory))?; + .ok_or(PagingError::OutOfMemory)?; map_start.as_usize().saturating_sub(seg_start) } } else { @@ -249,7 +261,7 @@ fn load_segments_into_space( // Basic sanity: prevent mapping into user stack range if start_va >= crate::config::USER_STACK_TOP - crate::config::USER_STACK_SIZE { - return Err(ExecImageError::Paging(PagingError::InvalidAddress)); + return Err(PagingError::InvalidAddress.into()); } let vpn_range = crate::mm::address::VpnRange::new( @@ -278,9 +290,7 @@ fn load_segments_into_space( AreaType::UserRodata }; - space - .insert_framed_area(vpn_range, area_type, perm, None, None) - .map_err(ExecImageError::Paging)?; + space.insert_framed_area(vpn_range, area_type, perm, None, None)?; // Copy file bytes let mut remain = ph.p_filesz as usize; @@ -290,13 +300,11 @@ fn load_segments_into_space( let take = core::cmp::min(remain, tmp.len()); let n = inode .read_at(src_off, &mut tmp[..take]) - .map_err(ExecImageError::Fs)?; + .map_err(ExecImageError::from)?; if n == 0 { return Err(ExecImageError::InvalidElf); } - space - .write_bytes_at(dst_va, &tmp[..n]) - .map_err(ExecImageError::Paging)?; + space.write_bytes_at(dst_va, &tmp[..n])?; src_off += n; dst_va += n; remain -= n; @@ -307,9 +315,7 @@ fn load_segments_into_space( let mut zero_va = start_va + ph.p_filesz as usize; while zero_remain > 0 { let take = core::cmp::min(zero_remain, zero_page.len()); - space - .write_bytes_at(zero_va, &zero_page[..take]) - .map_err(ExecImageError::Paging)?; + space.write_bytes_at(zero_va, &zero_page[..take])?; zero_va += take; zero_remain -= take; } @@ -361,12 +367,8 @@ fn apply_static_pie_relocs( let dyn_end = dyn_addr + dyn_ph.p_memsz as usize; while dyn_addr + 16 <= dyn_end { - let tag = space - .read_i64_at(dyn_addr) - .map_err(ExecImageError::Paging)?; - let val = space - .read_u64_at(dyn_addr + 8) - .map_err(ExecImageError::Paging)? as usize; + let tag = space.read_i64_at(dyn_addr)?; + let val = space.read_u64_at(dyn_addr + 8)? as usize; dyn_addr += 16; match tag { DT_NULL => break, @@ -394,9 +396,9 @@ fn apply_static_pie_relocs( for i in 0..count { let r = rel_base + i * dt_relaent; - let r_offset = space.read_u64_at(r).map_err(ExecImageError::Paging)? as usize; - let r_info = space.read_u64_at(r + 8).map_err(ExecImageError::Paging)?; - let r_addend = space.read_i64_at(r + 16).map_err(ExecImageError::Paging)? as isize; + let r_offset = space.read_u64_at(r)? as usize; + let r_info = space.read_u64_at(r + 8)?; + let r_addend = space.read_i64_at(r + 16)? as isize; let r_type = (r_info & 0xffff_ffff) as u32; let r_sym = (r_info >> 32) as usize; @@ -410,26 +412,22 @@ fn apply_static_pie_relocs( return Err(ExecImageError::InvalidElf); } let sym_addr = load_bias + dt_symtab + r_sym * dt_syment; - let st_value = space - .read_u64_at(sym_addr + 8) - .map_err(ExecImageError::Paging)? as usize; + let st_value = space.read_u64_at(sym_addr + 8)? as usize; st_value } }; let value = resolve_relocation_value(kind, load_bias, symbol_value, r_addend); - space - .write_usize_at(target_va, value) - .map_err(ExecImageError::Paging)?; + space.write_usize_at(target_va, value)?; } Ok(()) } pub fn prepare_exec_image_from_path(path: &str) -> Result { - let dentry = crate::vfs::vfs_lookup(path).map_err(ExecImageError::Fs)?; + let dentry = crate::vfs::vfs_lookup(path).map_err(ExecImageError::from)?; let inode = dentry.inode.clone(); - let meta = inode.metadata().map_err(ExecImageError::Fs)?; + let meta = inode.metadata().map_err(ExecImageError::from)?; if meta.inode_type != InodeType::File { return Err(ExecImageError::Fs(FsError::IsDirectory)); } @@ -438,7 +436,7 @@ pub fn prepare_exec_image_from_path(path: &str) -> Result Result Self { - MemorySpace { - page_table: ActivePageTableInner::new(), + pub fn new() -> Result { + Ok(MemorySpace { + page_table: ActivePageTableInner::new()?, areas: Vec::new(), heap_start: None, - } + }) } /// 返回页表的引用 @@ -56,7 +56,7 @@ impl MemorySpace { let current_space = crate::kernel::current_memory_space(); let current_locked = current_space.lock(); - let mut space = MemorySpace::new(); + let mut space = MemorySpace::new()?; for area in current_locked.areas.iter() { let is_kernel = matches!( area.area_type(), @@ -317,7 +317,7 @@ impl MemorySpace { /// - 直接映射是共享的(不复制) /// - 帧映射是深层复制的 pub fn clone_for_fork(&self) -> Result { - let mut new_space = MemorySpace::new(); + let mut new_space = MemorySpace::new()?; new_space.heap_start = self.heap_start; for area in self.areas.iter() { diff --git a/os/src/mm/memory_space/space/elf_loader.rs b/os/src/mm/memory_space/space/elf_loader.rs index 1085e8d8..8531b026 100644 --- a/os/src/mm/memory_space/space/elf_loader.rs +++ b/os/src/mm/memory_space/space/elf_loader.rs @@ -57,7 +57,7 @@ impl MemorySpace { let current_space = crate::kernel::current_memory_space(); let current_locked = current_space.lock(); - let mut space = MemorySpace::new(); + let mut space = MemorySpace::new()?; // 只复制内核空间区域的元数据和映射 for area in current_locked.areas.iter() { diff --git a/os/src/mm/memory_space/space/kernel_space.rs b/os/src/mm/memory_space/space/kernel_space.rs index 853ca458..a69c3cf5 100644 --- a/os/src/mm/memory_space/space/kernel_space.rs +++ b/os/src/mm/memory_space/space/kernel_space.rs @@ -127,15 +127,13 @@ impl MemorySpace { /// /// 这将创建一个完整的内核地址空间,包括跳板页、内核段(.text、.rodata、.data、.bss、堆)以及直接映射的 /// 物理内存。供内核线程和系统初始化时使用。 - pub fn new_kernel() -> Self { - let mut space = MemorySpace::new(); + pub fn new_kernel() -> Result { + let mut space = MemorySpace::new()?; // 映射所有内核空间(包括带内核权限的跳板页) - space - .map_kernel_space() - .expect("Failed to map kernel space"); + space.map_kernel_space()?; - space + Ok(space) } /// 辅助函数:映射一个内核段 diff --git a/os/src/mm/memory_space/space/mod.rs b/os/src/mm/memory_space/space/mod.rs index 49ca5993..833d5549 100644 --- a/os/src/mm/memory_space/space/mod.rs +++ b/os/src/mm/memory_space/space/mod.rs @@ -30,7 +30,7 @@ unsafe extern "C" { lazy_static! { /// 全局内核内存空间(受 SpinLock 保护) static ref KERNEL_SPACE: SpinLock = { - SpinLock::new(MemorySpace::new_kernel()) + SpinLock::new(MemorySpace::new_kernel().expect("failed to create kernel memory space")) }; } diff --git a/os/src/mm/memory_space/space/tests.rs b/os/src/mm/memory_space/space/tests.rs index a429f48d..3f25ecc3 100644 --- a/os/src/mm/memory_space/space/tests.rs +++ b/os/src/mm/memory_space/space/tests.rs @@ -6,16 +6,23 @@ mod memory_space_tests { use crate::mm::page_table::{PagingError, UniversalPTEFlag}; use crate::{kassert, println, test_case}; + fn new_memory_space() -> MemorySpace { + match MemorySpace::new() { + Ok(space) => space, + Err(err) => panic!("failed to create test memory space: {:?}", err), + } + } + // 1. 创建内存空间 test_case!(test_memspace_create, { #[allow(unused)] - let ms = MemorySpace::new(); + let ms = new_memory_space(); // 应该已初始化页表 }); // 2. 直接映射:VA 必须 >= PAGE_OFFSET,从已知 PA 经 pa_to_va 计算 Vpn test_case!(test_direct_mapping, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); let va_base = crate::arch::pa_to_va(PA::from_usize(0x8000_0000)); let vpn_start = Vpn::from_addr_ceil(va_base); let vpn_range = VpnRange::new(vpn_start, Vpn::from_usize(vpn_start.as_usize() + 0x10)); @@ -33,7 +40,7 @@ mod memory_space_tests { // 3. 帧映射 test_case!(test_framed_mapping, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); let vpn_range = VpnRange::new(Vpn::from_usize(0x1000), Vpn::from_usize(0x1010)); let area = MappingArea::new( @@ -76,7 +83,7 @@ mod memory_space_tests { use crate::arch::ArchImpl; // 使用独立的 MemorySpace 实例,避免与其他测试或全局状态冲突 - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 使用一个不太可能被占用的测试地址 const TEST_MMIO_PADDR: usize = 0xE000_0000; @@ -174,7 +181,7 @@ mod memory_space_tests { test_case!(test_dynamic_mmio_mapping, { use crate::arch::ArchImpl; - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 尝试映射一个自定义的 MMIO 区域(使用未占用的地址) const CUSTOM_MMIO_PADDR: usize = 0x5000_0000; @@ -205,7 +212,7 @@ mod memory_space_tests { // 9. 测试 map_mmio 函数 - 新映射 test_case!(test_map_mmio_new_mapping, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 使用一个未占用的物理地址 const TEST_PADDR: usize = 0x6000_0000; @@ -237,7 +244,7 @@ mod memory_space_tests { // 10. 测试 map_mmio 函数 - 已存在的映射 test_case!(test_map_mmio_existing_mapping, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); const TEST_PADDR: usize = 0x7000_0000; const TEST_SIZE: usize = 0x1000; @@ -271,7 +278,7 @@ mod memory_space_tests { test_case!(test_map_mmio_conflict, { use crate::arch::ArchImpl; - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 使用一个合理的物理地址 const TEST_PADDR: usize = 0x8000_0000; @@ -320,7 +327,7 @@ mod memory_space_tests { // 12. 测试 unmap_mmio 函数 - 正常取消映射 test_case!(test_unmap_mmio_normal, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); const TEST_PADDR: usize = 0x9000_0000; const TEST_SIZE: usize = 0x1000; @@ -354,7 +361,7 @@ mod memory_space_tests { // 13. 测试 unmap_mmio 函数 - 取消映射不存在的区域 test_case!(test_unmap_mmio_not_mapped, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 尝试取消映射一个未映射的区域 let vaddr = VA::from_usize(0xffff_ffc0_a000_0000); @@ -371,7 +378,7 @@ mod memory_space_tests { // 14. 测试 unmap_mmio 函数 - 错误的区域类型 test_case!(test_unmap_mmio_wrong_type, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 映射一个非MMIO区域:Direct 映射的 VA 必须 >= PAGE_OFFSET let va_base = crate::arch::pa_to_va(PA::from_usize(0xb000_0000)); @@ -402,7 +409,7 @@ mod memory_space_tests { // 15. 测试 map_mmio 和 unmap_mmio 组合 - 多个区域 test_case!(test_mmio_multiple_regions, { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); println!("Testing multiple MMIO mappings and unmappings"); @@ -508,7 +515,7 @@ mod memory_space_tests { // 完整测试需要更复杂的设置 // 这里验证编译和结构正确性 - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); let vpn_range = VpnRange::new(Vpn::from_usize(0x2000), Vpn::from_usize(0x2002)); // 创建一个没有文件映射的区域 @@ -541,7 +548,7 @@ mod memory_space_tests { // 创建一个内存空间并添加一些区域 { - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); let vpn_range = VpnRange::new(Vpn::from_usize(0x3000), Vpn::from_usize(0x3002)); ms.insert_framed_area( @@ -564,7 +571,7 @@ mod memory_space_tests { test_case!(test_mprotect_basic, { println!("Testing mprotect basic functionality"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); let vpn_range = VpnRange::new(Vpn::from_usize(0x4000), Vpn::from_usize(0x4002)); // 创建一个可读写的区域 @@ -599,7 +606,7 @@ mod memory_space_tests { test_case!(test_mprotect_errors, { println!("Testing mprotect error handling"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 测试未对齐的地址 let result = ms.mprotect( @@ -631,7 +638,7 @@ mod memory_space_tests { test_case!(test_mprotect_multiple_areas, { println!("Testing mprotect across multiple areas"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 创建两个连续的区域 let vpn_range1 = VpnRange::new(Vpn::from_usize(0x6000), Vpn::from_usize(0x6002)); @@ -672,7 +679,7 @@ mod memory_space_tests { test_case!(test_mprotect_partial_front, { println!("Testing mprotect partial modification - front half"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 创建一个4页的区域 let vpn_range = VpnRange::new(Vpn::from_usize(0x7000), Vpn::from_usize(0x7004)); @@ -722,7 +729,7 @@ mod memory_space_tests { test_case!(test_mprotect_partial_back, { println!("Testing mprotect partial modification - back half"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 创建一个4页的区域 let vpn_range = VpnRange::new(Vpn::from_usize(0x8000), Vpn::from_usize(0x8004)); @@ -772,7 +779,7 @@ mod memory_space_tests { test_case!(test_mprotect_partial_middle, { println!("Testing mprotect partial modification - middle part (3-way split)"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 创建一个6页的区域 let vpn_range = VpnRange::new(Vpn::from_usize(0x9000), Vpn::from_usize(0x9006)); @@ -830,7 +837,7 @@ mod memory_space_tests { test_case!(test_mprotect_partial_pte_flags, { println!("Testing mprotect partial modification - verify PTE flags"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 创建一个4页的区域,并立即映射 let vpn_range = VpnRange::new(Vpn::from_usize(0xa000), Vpn::from_usize(0xa004)); @@ -885,7 +892,7 @@ mod memory_space_tests { test_case!(test_mprotect_partial_single_page, { println!("Testing mprotect partial modification - single page"); - let mut ms = MemorySpace::new(); + let mut ms = new_memory_space(); // 创建一个3页的区域 let vpn_range = VpnRange::new(Vpn::from_usize(0xb000), Vpn::from_usize(0xb003)); diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index d5b41227..6263f9b1 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -64,7 +64,9 @@ pub fn init() -> alloc::sync::Arc Ppn; - fn new() -> Self; + fn new() -> PagingResult + where + Self: Sized; fn from_ppn(ppn: Ppn) -> Self; - fn new_as_kernel_table() -> Self; + fn new_as_kernel_table() -> PagingResult + where + Self: Sized; fn root_ppn(&self) -> Ppn; diff --git a/os/src/mm/page_table/mod.rs b/os/src/mm/page_table/mod.rs index 7ac5b056..b3ceec6a 100644 --- a/os/src/mm/page_table/mod.rs +++ b/os/src/mm/page_table/mod.rs @@ -17,7 +17,7 @@ pub enum PageSize { } /// 分页操作中可能发生的错误 -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PagingError { /// 虚拟地址未被映射 NotMapped, @@ -27,6 +27,8 @@ pub enum PagingError { InvalidAddress, /// 提供了无效的标志(Flags) InvalidFlags, + /// 页表权限不允许该访问 + PermissionDenied, /// 帧(Frame)分配失败 FrameAllocFailed, /// 此操作不支持此映射类型 From a105b6356674b3a888c77673c9c318841ae65b52 Mon Sep 17 00:00:00 2001 From: ZIYAN137 Date: Thu, 28 May 2026 01:57:21 +0800 Subject: [PATCH 2/5] refactor(vfs): normalize filesystem errno mapping --- os/src/kernel/syscall/fs/metadata_ops.rs | 20 ++------ os/src/kernel/syscall/fs/mount_ops.rs | 16 ++---- os/src/kernel/syscall/fs/path_ops.rs | 38 ++++++++------ os/src/kernel/syscall/fs/rename_ops.rs | 8 +-- os/src/kernel/syscall/fs/stat_ops.rs | 28 +++-------- os/src/kernel/syscall/ioctl.rs | 23 ++------- os/src/kernel/syscall/task/exec_ops.rs | 62 +++++++++++++++-------- os/src/kernel/syscall/util.rs | 27 +++++----- os/src/vfs/error.rs | 63 +++++++++++++++--------- os/src/vfs/file.rs | 6 +-- os/src/vfs/impls/char_dev_file.rs | 8 +-- os/src/vfs/impls/pipe_file.rs | 2 +- os/src/vfs/impls/stdio_file.rs | 2 +- os/src/vfs/tests/pipe.rs | 2 +- os/src/vfs/tests/trait_file.rs | 2 +- 15 files changed, 148 insertions(+), 159 deletions(-) diff --git a/os/src/kernel/syscall/fs/metadata_ops.rs b/os/src/kernel/syscall/fs/metadata_ops.rs index e3523ee1..3f05d759 100644 --- a/os/src/kernel/syscall/fs/metadata_ops.rs +++ b/os/src/kernel/syscall/fs/metadata_ops.rs @@ -21,9 +21,7 @@ pub fn fchownat(dirfd: i32, pathname: *const c_char, owner: u32, group: u32, fla // 解析路径字符串 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 解析标志位 @@ -89,9 +87,7 @@ pub fn fchmodat(dirfd: i32, pathname: *const c_char, mode: u32, flags: u32) -> i // 解析路径字符串 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 解析标志位 @@ -133,9 +129,7 @@ pub fn mknodat(dirfd: i32, pathname: *const c_char, mode: u32, dev: u64) -> isiz // 安全地读取路径字符串 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; // 分割路径为目录和文件名 @@ -184,17 +178,13 @@ pub fn symlinkat(target: *const c_char, newdirfd: i32, linkpath: *const c_char) // 解析 target 路径 let target_str = match get_path_safe(target as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; // 解析 linkpath 路径 let link_str = match get_path_safe(linkpath as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; // 分割路径为目录和文件名 diff --git a/os/src/kernel/syscall/fs/mount_ops.rs b/os/src/kernel/syscall/fs/mount_ops.rs index 1f95e7a6..904d8754 100644 --- a/os/src/kernel/syscall/fs/mount_ops.rs +++ b/os/src/kernel/syscall/fs/mount_ops.rs @@ -27,18 +27,14 @@ pub fn mount( // 解析目标路径 let target_str = match get_path_safe(target as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; // 解析 source (可能为空) let source_str = if !source.is_null() { match get_path_safe(source as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), } } else { String::new() @@ -48,9 +44,7 @@ pub fn mount( let fstype_str = if !filesystemtype.is_null() { match get_path_safe(filesystemtype as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), } } else { String::new() @@ -206,9 +200,7 @@ pub fn umount2(target: *const c_char, _flags: i32) -> isize { // 解析目标路径 let target_str = match get_path_safe(target as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; crate::pr_debug!("[SYSCALL] umount2: unmounting '{}'", target_str); diff --git a/os/src/kernel/syscall/fs/path_ops.rs b/os/src/kernel/syscall/fs/path_ops.rs index 4398be47..aeaa46a1 100644 --- a/os/src/kernel/syscall/fs/path_ops.rs +++ b/os/src/kernel/syscall/fs/path_ops.rs @@ -5,9 +5,7 @@ pub fn openat(dirfd: i32, pathname: *const c_char, flags: u32, mode: u32) -> isi // 解析路径字符串 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; // 解析标志位 @@ -83,9 +81,7 @@ pub fn mkdirat(dirfd: i32, pathname: *const c_char, mode: u32) -> isize { // 解析路径 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; // 分割路径为目录和文件名 @@ -113,9 +109,7 @@ pub fn unlinkat(dirfd: i32, pathname: *const c_char, flags: u32) -> isize { // 解析路径 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; let is_rmdir = (flags & AT_REMOVEDIR) != 0; @@ -172,9 +166,7 @@ pub fn chdir(path: *const c_char) -> isize { // 解析路径 let path_str = match get_path_safe(path as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno(); - } + Err(e) => return e.to_errno(), }; // 查找目标目录 @@ -202,7 +194,7 @@ pub fn getcwd(buf: *mut u8, size: usize) -> isize { // 获取当前工作目录dentry let cwd_dentry = match current_task().lock().fs.lock().cwd.clone() { Some(d) => d, - None => return FsError::NotSupported.to_errno(), + None => return FsError::IoError.to_errno(), }; // 获取完整路径 @@ -215,14 +207,28 @@ pub fn getcwd(buf: *mut u8, size: usize) -> isize { } // 复制到用户态缓冲区 - unsafe { + if unsafe { crate::arch::ArchImpl::copy_to_user( path_bytes.as_ptr(), crate::arch::address::UA::from_usize(buf as usize), path_bytes.len(), ) - .ok(); - write_to_user(buf.add(path_bytes.len()), 0u8); + } + .is_err() + { + return FsError::BadAddress.to_errno(); + } + let nul = [0u8]; + if unsafe { + crate::arch::ArchImpl::copy_to_user( + nul.as_ptr(), + crate::arch::address::UA::from_usize(buf as usize + path_bytes.len()), + nul.len(), + ) + } + .is_err() + { + return FsError::BadAddress.to_errno(); } buf as isize diff --git a/os/src/kernel/syscall/fs/rename_ops.rs b/os/src/kernel/syscall/fs/rename_ops.rs index bd58c8e4..2a3923e5 100644 --- a/os/src/kernel/syscall/fs/rename_ops.rs +++ b/os/src/kernel/syscall/fs/rename_ops.rs @@ -27,17 +27,13 @@ pub fn renameat2( // 解析旧路径 let old_path_str = match get_path_safe(oldpath as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 解析新路径 let new_path_str = match get_path_safe(newpath as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 分割路径为 (父目录, 文件名) diff --git a/os/src/kernel/syscall/fs/stat_ops.rs b/os/src/kernel/syscall/fs/stat_ops.rs index 10d003bf..fc0e5569 100644 --- a/os/src/kernel/syscall/fs/stat_ops.rs +++ b/os/src/kernel/syscall/fs/stat_ops.rs @@ -137,14 +137,12 @@ pub fn statfs(path: *const c_char, buf: *mut LinuxStatFs) -> isize { // 解析路径字符串 let path_str = match get_path_safe(path as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 验证路径存在 - if vfs_lookup(&path_str).is_err() { - return -(EINVAL as isize); + if let Err(e) = vfs_lookup(&path_str) { + return e.to_errno(); } // 通过 MOUNT_TABLE 查找文件系统 @@ -191,9 +189,7 @@ pub fn faccessat(dirfd: i32, pathname: *const c_char, mode: i32, flags: u32) -> // 解析路径字符串 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 解析标志 @@ -272,9 +268,7 @@ pub fn readlinkat(dirfd: i32, pathname: *const c_char, buf: *mut u8, bufsiz: usi // 解析路径字符串 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 查找符号链接(不跟随最后一级的符号链接) @@ -322,9 +316,7 @@ pub fn newfstatat(dirfd: i32, pathname: *const c_char, statbuf: *mut Stat, flags // 解析路径 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 解析标志 @@ -378,9 +370,7 @@ pub fn statx( // 解析路径 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; let at_flags = AtFlags::from_bits_truncate(flags); @@ -430,9 +420,7 @@ pub fn utimensat(dirfd: i32, pathname: *const c_char, times: *const TimeSpec, fl // 解析路径 let path_str = match get_path_safe(pathname as usize) { Ok(s) => s, - Err(_) => { - return -(EINVAL as isize); - } + Err(e) => return e.to_errno(), }; // 解析标志 diff --git a/os/src/kernel/syscall/ioctl.rs b/os/src/kernel/syscall/ioctl.rs index b9127042..f962ee6d 100644 --- a/os/src/kernel/syscall/ioctl.rs +++ b/os/src/kernel/syscall/ioctl.rs @@ -81,7 +81,7 @@ pub fn ioctl(fd: i32, request: u32, arg: usize) -> isize { TIOCGWINSZ | TIOCSWINSZ | TCGETS | TCSETS | TCSETSW | TCSETSF => { match file.ioctl(request, arg) { Ok(ret) => ret, - Err(FsError::NotSupported) => { + Err(FsError::NotSupported | FsError::NotTty) => { pr_warn!( "ioctl: fd={}, terminal request {:#x} ({}) not supported by file type", fd, @@ -90,7 +90,7 @@ pub fn ioctl(fd: i32, request: u32, arg: usize) -> isize { ); -ENOTTY as isize } - Err(e) => fs_error_to_errno(e), + Err(e) => e.to_errno(), } } @@ -116,7 +116,7 @@ pub fn ioctl(fd: i32, request: u32, arg: usize) -> isize { pr_debug!("ioctl: delegating request {:#x} to file object", request); match file.ioctl(request, arg) { Ok(ret) => ret, - Err(FsError::NotSupported) => { + Err(FsError::NotSupported | FsError::NotTty) => { pr_warn!( "ioctl: unsupported request {:#x} (type={:#x}, nr={}, size={})", request, @@ -128,7 +128,7 @@ pub fn ioctl(fd: i32, request: u32, arg: usize) -> isize { } Err(e) => { pr_err!("ioctl: file ioctl failed: {:?}", e); - fs_error_to_errno(e) + e.to_errno() } } } @@ -318,18 +318,3 @@ fn handle_ifreq(_file: &alloc::sync::Arc, request: u32, ar _ => -EINVAL as isize, } } - -// 辅助函数 - -/// 将 VFS 错误转换为 errno -fn fs_error_to_errno(err: FsError) -> isize { - match err { - FsError::NotSupported => -EOPNOTSUPP as isize, - FsError::InvalidArgument => -EINVAL as isize, - FsError::NotFound => -crate::uapi::errno::ENOENT as isize, - FsError::PermissionDenied => -crate::uapi::errno::EACCES as isize, - FsError::AlreadyExists => -crate::uapi::errno::EEXIST as isize, - FsError::IoError => -crate::uapi::errno::EIO as isize, - _ => -crate::uapi::errno::EIO as isize, - } -} diff --git a/os/src/kernel/syscall/task/exec_ops.rs b/os/src/kernel/syscall/task/exec_ops.rs index 93d62714..c2fa7950 100644 --- a/os/src/kernel/syscall/task/exec_ops.rs +++ b/os/src/kernel/syscall/task/exec_ops.rs @@ -13,12 +13,16 @@ pub fn execve( ) -> c_int { let path_str = match get_path_safe(path as usize) { Ok(s) => s, - Err(_) => { - return FsError::InvalidArgument.to_errno() as i32; - } + Err(e) => return e.to_errno() as i32, + }; + let argv_strings = match get_args_safe(argv as usize, "argv") { + Ok(args) => args, + Err(e) => return e.to_errno() as i32, + }; + let envp_strings = match get_args_safe(envp as usize, "envp") { + Ok(envp) => envp, + Err(e) => return e.to_errno() as i32, }; - let argv_strings = get_args_safe(argv as usize, "argv").unwrap_or_else(|_| Vec::new()); - let envp_strings = get_args_safe(envp as usize, "envp").unwrap_or_else(|_| Vec::new()); let mut exec_path_str = path_str.clone(); let (argv_strings, envp_strings, exec_path_str) = { @@ -64,19 +68,20 @@ pub fn execve( prefix.truncate(read_total); if prefix.len() >= 2 && prefix[0] == b'#' && prefix[1] == b'!' { - if let Ok((path, args)) = parse_hashbang(&prefix) { - let mut new_argv = Vec::new(); - new_argv.push(path.to_string()); - // XXX: 目前仅支持单个参数 - if let Some(arg) = args { - new_argv.push(arg.to_string()); + match parse_hashbang(&prefix) { + Ok((path, args)) => { + let mut new_argv = Vec::new(); + new_argv.push(path.to_string()); + // XXX: 目前仅支持单个参数 + if let Some(arg) = args { + new_argv.push(arg.to_string()); + } + new_argv.push(path_str.clone()); + new_argv.extend(argv_strings.iter().skip(1).cloned()); + exec_path_str = path.to_string(); + (new_argv, envp_strings, exec_path_str) } - new_argv.push(path_str.clone()); - new_argv.extend(argv_strings.iter().skip(1).cloned()); - exec_path_str = path.to_string(); - (new_argv, envp_strings, exec_path_str) - } else { - return -EINVAL; + Err(e) => return e.to_errno(), } } else { (argv_strings, envp_strings, exec_path_str) @@ -118,8 +123,22 @@ pub fn execve( ) } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum HashbangError { + MissingInterpreter, + InvalidUtf8, +} + +impl HashbangError { + fn to_errno(self) -> c_int { + match self { + HashbangError::MissingInterpreter | HashbangError::InvalidUtf8 => -EINVAL, + } + } +} + /// 辅助函数:解析 Hashbang 行 -fn parse_hashbang(data: &[u8]) -> Result<(&str, Option<&str>), ()> { +fn parse_hashbang(data: &[u8]) -> Result<(&str, Option<&str>), HashbangError> { // 查找第一个换行符 ('\n'),只读取第一行 let line_end = data.iter().position(|&b| b == b'\n').unwrap_or(data.len()); // 跳过开头的空格和制表符 @@ -137,18 +156,19 @@ fn parse_hashbang(data: &[u8]) -> Result<(&str, Option<&str>), ()> { .collect(); if parts.is_empty() { - return Err(()); // 格式错误或只包含 #! + return Err(HashbangError::MissingInterpreter); } // 解释器路径 - let interpreter_path = core::str::from_utf8(parts[0]).map_err(|_| ())?; + let interpreter_path = + core::str::from_utf8(parts[0]).map_err(|_| HashbangError::InvalidUtf8)?; // 可选参数 let interpreter_arg = parts .get(1) .map(|p| core::str::from_utf8(p)) .transpose() - .map_err(|_| ())?; + .map_err(|_| HashbangError::InvalidUtf8)?; Ok((interpreter_path, interpreter_arg)) } diff --git a/os/src/kernel/syscall/util.rs b/os/src/kernel/syscall/util.rs index fdd19210..93ac4738 100644 --- a/os/src/kernel/syscall/util.rs +++ b/os/src/kernel/syscall/util.rs @@ -4,7 +4,6 @@ use core::ffi::CStr; use crate::arch::Arch; use alloc::{ - format, string::{String, ToString}, sync::Arc, vec::Vec, @@ -23,7 +22,7 @@ use crate::{ const PATH_MAX: usize = 4096; /// 从用户空间复制字符串到内核缓冲区 -fn copy_str_from_user(user_ptr: usize) -> Result { +fn copy_str_from_user(user_ptr: usize) -> Result { let mut buf = [0u8; PATH_MAX]; let len = unsafe { crate::arch::ArchImpl::copy_strn_from_user( @@ -31,18 +30,17 @@ fn copy_str_from_user(user_ptr: usize) -> Result { buf.as_mut_ptr(), PATH_MAX, ) - .map_err(|_| "Failed to read string from user space")? + .map_err(|_| FsError::BadAddress)? }; if len == PATH_MAX { - return Err("Path exceeds PATH_MAX"); + return Err(FsError::NameTooLong); } // copy_strn_from_user 在遇到 '\0' 时返回其索引(含 '\0') - let c_str = - CStr::from_bytes_until_nul(&buf[..=len]).map_err(|_| "String is not null-terminated")?; + let c_str = CStr::from_bytes_until_nul(&buf[..=len]).map_err(|_| FsError::InvalidArgument)?; c_str .to_str() .map(|s| s.to_string()) - .map_err(|_| "String is not valid UTF-8") + .map_err(|_| FsError::InvalidArgument) } /// 从用户空间获取路径字符串 @@ -50,10 +48,10 @@ fn copy_str_from_user(user_ptr: usize) -> Result { /// - `path`: 指向用户空间路径字符串的指针 /// # 返回值 /// - 成功时返回路径字符串 -/// - 失败时返回错误字符串 -pub fn get_path_safe(path: usize) -> Result { +/// - 失败时返回 [`FsError`] +pub fn get_path_safe(path: usize) -> Result { if path == 0 { - return Err("Path pointer is NULL"); + return Err(FsError::BadAddress); } copy_str_from_user(path) } @@ -65,7 +63,7 @@ pub fn get_path_safe(path: usize) -> Result { /// # 返回值 /// - 成功时返回包含参数字符串的 `Vec` /// - 失败时返回错误字符串 -pub fn get_args_safe(ptr_array: usize, name: &str) -> Result, String> { +pub fn get_args_safe(ptr_array: usize, _name: &str) -> Result, FsError> { let mut args = Vec::new(); if ptr_array == 0 { @@ -83,7 +81,7 @@ pub fn get_args_safe(ptr_array: usize, name: &str) -> Result, String (&mut val) as *mut usize as *mut u8, core::mem::size_of::(), ) - .map_err(|_| format!("Failed to read {} pointer array from user space", name))?; + .map_err(|_| FsError::BadAddress)?; } val }; @@ -92,8 +90,7 @@ pub fn get_args_safe(ptr_array: usize, name: &str) -> Result, String break; // NULL 终止 } - let s = - copy_str_from_user(ptr_val).map_err(|e| format!("{}[{}]: {}", name, args.len(), e))?; + let s = copy_str_from_user(ptr_val)?; args.push(s); offset += core::mem::size_of::(); @@ -115,7 +112,7 @@ pub fn resolve_at_path(dirfd: i32, path: &str) -> Result>, Fs .lock() .cwd .clone() - .ok_or(FsError::NotSupported)? + .ok_or(FsError::IoError)? } else { // 对于文件描述符,我们需要获取对应的 dentry let task = current_task(); diff --git a/os/src/vfs/error.rs b/os/src/vfs/error.rs index b6fd9117..3391ca41 100644 --- a/os/src/vfs/error.rs +++ b/os/src/vfs/error.rs @@ -23,6 +23,7 @@ pub enum FsError { // 参数相关 InvalidArgument, // -EINVAL(22): 无效参数 + BadAddress, // -EFAULT(14): 无效用户地址 NameTooLong, // -ENAMETOOLONG(36): 文件名过长 // 文件系统相关 @@ -30,6 +31,8 @@ pub enum FsError { NoSpace, // -ENOSPC(28): 设备空间不足 IoError, // -EIO(5): I/O 错误 NoDevice, // -ENODEV(19): 设备不存在 + NoMemory, // -ENOMEM(12): 内存不足 + Busy, // -EBUSY(16): 设备或资源忙 // 管道相关 (新增) BrokenPipe, // -EPIPE(32): 管道破裂 (读端已关闭) @@ -40,34 +43,43 @@ pub enum FsError { // 其他 NotSupported, // -ENOTSUP(95): 操作不支持 + NotTty, // -ENOTTY(25): 非 TTY 设备或不支持该 ioctl + NotSeekable, // -ESPIPE(29): 不支持 seek TooManyLinks, // -EMLINK(31): 硬链接过多 TooManySymlinks, // -ELOOP(40): 符号链接层级过多 } impl FsError { /// 转换为系统调用错误码(负数) - pub fn to_errno(&self) -> isize { + pub fn to_errno(self) -> isize { + use crate::uapi::errno::*; + match self { - FsError::NotFound => -2, - FsError::IoError => -5, - FsError::BadFileDescriptor => -9, - FsError::WouldBlock => -11, - FsError::PermissionDenied => -13, - FsError::AlreadyExists => -17, - FsError::NoDevice => -19, - FsError::NotDirectory => -20, - FsError::IsDirectory => -21, - FsError::InvalidArgument => -22, - FsError::TooManyOpenFiles => -24, - FsError::NoSpace => -28, - FsError::ReadOnlyFs => -30, - FsError::TooManyLinks => -31, - FsError::TooManySymlinks => -40, - FsError::BrokenPipe => -32, - FsError::NameTooLong => -36, - FsError::DirectoryNotEmpty => -39, - FsError::NotSupported => -95, - FsError::NotConnected => -107, + FsError::NotFound => -ENOENT as isize, + FsError::IoError => -EIO as isize, + FsError::BadFileDescriptor => -EBADF as isize, + FsError::WouldBlock => -EAGAIN as isize, + FsError::NoMemory => -ENOMEM as isize, + FsError::PermissionDenied => -EACCES as isize, + FsError::BadAddress => -EFAULT as isize, + FsError::Busy => -EBUSY as isize, + FsError::AlreadyExists => -EEXIST as isize, + FsError::NoDevice => -ENODEV as isize, + FsError::NotDirectory => -ENOTDIR as isize, + FsError::IsDirectory => -EISDIR as isize, + FsError::InvalidArgument => -EINVAL as isize, + FsError::TooManyOpenFiles => -EMFILE as isize, + FsError::NotTty => -ENOTTY as isize, + FsError::NoSpace => -ENOSPC as isize, + FsError::NotSeekable => -ESPIPE as isize, + FsError::ReadOnlyFs => -EROFS as isize, + FsError::TooManyLinks => -EMLINK as isize, + FsError::BrokenPipe => -EPIPE as isize, + FsError::NameTooLong => -ENAMETOOLONG as isize, + FsError::DirectoryNotEmpty => -ENOTEMPTY as isize, + FsError::TooManySymlinks => -ELOOP as isize, + FsError::NotSupported => -EOPNOTSUPP as isize, + FsError::NotConnected => -ENOTCONN as isize, } } } @@ -79,8 +91,11 @@ mod tests { use super::*; test_case!(test_error_codes, { - kassert!(FsError::NotFound.to_errno() == -2); - kassert!(FsError::PermissionDenied.to_errno() == -13); - kassert!(FsError::AlreadyExists.to_errno() == -17); + kassert!(FsError::NotFound.to_errno() == -crate::uapi::errno::ENOENT as isize); + kassert!(FsError::PermissionDenied.to_errno() == -crate::uapi::errno::EACCES as isize); + kassert!(FsError::AlreadyExists.to_errno() == -crate::uapi::errno::EEXIST as isize); + kassert!(FsError::BadAddress.to_errno() == -crate::uapi::errno::EFAULT as isize); + kassert!(FsError::NotSeekable.to_errno() == -crate::uapi::errno::ESPIPE as isize); + kassert!(FsError::NotTty.to_errno() == -crate::uapi::errno::ENOTTY as isize); }); } diff --git a/os/src/vfs/file.rs b/os/src/vfs/file.rs index 5fa7709a..bbc8ab21 100644 --- a/os/src/vfs/file.rs +++ b/os/src/vfs/file.rs @@ -94,7 +94,7 @@ use alloc::sync::Arc; /// /// - 方法不携带 offset 参数,由实现者内部维护 /// - 支持可 seek 文件(RegFile)和流式设备(PipeFile) -/// - 可选方法提供默认实现(如 `lseek` 默认返回 `NotSupported`) +/// - 可选方法提供默认实现(如 `lseek` 默认返回 `NotSeekable`) pub trait File: Send + Sync { /// 检查文件是否可读 fn readable(&self) -> bool; @@ -118,9 +118,9 @@ pub trait File: Send + Sync { /// 设置文件偏移量(可选方法) /// - /// 默认返回 `NotSupported`,适用于流式设备。 + /// 默认返回 `NotSeekable`,适用于流式设备。 fn lseek(&self, _offset: isize, _whence: SeekWhence) -> Result { - Err(FsError::NotSupported) + Err(FsError::NotSeekable) } /// 获取当前偏移量(可选方法) diff --git a/os/src/vfs/impls/char_dev_file.rs b/os/src/vfs/impls/char_dev_file.rs index 006e51e2..bc875fdb 100644 --- a/os/src/vfs/impls/char_dev_file.rs +++ b/os/src/vfs/impls/char_dev_file.rs @@ -288,7 +288,7 @@ impl File for CharDeviceFile { fn lseek(&self, _offset: isize, _whence: SeekWhence) -> Result { // 大多数字符设备不支持 seek // 但某些设备(如 /dev/mem)可能需要 - Err(FsError::NotSupported) + Err(FsError::NotSeekable) } fn offset(&self) -> usize { @@ -320,7 +320,7 @@ impl File for CharDeviceFile { // MISC 设备 ioctl (包括 RTC) self.misc_ioctl(request, arg) } - _ => Err(FsError::NotSupported), + _ => Err(FsError::NotTty), } } fn as_any(&self) -> &dyn core::any::Any { @@ -454,10 +454,10 @@ impl CharDeviceFile { } Err(FsError::NoDevice) } - _ => Err(FsError::NotSupported), + _ => Err(FsError::NotTty), } } else { - Err(FsError::NotSupported) + Err(FsError::NotTty) } } } diff --git a/os/src/vfs/impls/pipe_file.rs b/os/src/vfs/impls/pipe_file.rs index 5144df15..92e90d24 100644 --- a/os/src/vfs/impls/pipe_file.rs +++ b/os/src/vfs/impls/pipe_file.rs @@ -244,7 +244,7 @@ impl File for PipeFile { Ok(()) } - // lseek 使用默认实现 (返回 NotSupported) + // lseek 使用默认实现 (返回 NotSeekable) fn as_any(&self) -> &dyn core::any::Any { self } diff --git a/os/src/vfs/impls/stdio_file.rs b/os/src/vfs/impls/stdio_file.rs index 1d735eaa..16280a10 100644 --- a/os/src/vfs/impls/stdio_file.rs +++ b/os/src/vfs/impls/stdio_file.rs @@ -123,7 +123,7 @@ impl File for StdinFile { stdio_ioctl(request, arg) } - // lseek 使用默认实现 (返回 NotSupported) + // lseek 使用默认实现 (返回 NotSeekable) fn as_any(&self) -> &dyn core::any::Any { self } diff --git a/os/src/vfs/tests/pipe.rs b/os/src/vfs/tests/pipe.rs index 497b98c9..1038241c 100644 --- a/os/src/vfs/tests/pipe.rs +++ b/os/src/vfs/tests/pipe.rs @@ -65,7 +65,7 @@ test_case!(test_pipe_not_seekable, { // 管道不支持 lseek let result = file.lseek(0, SeekWhence::Set); kassert!(result.is_err()); - kassert!(matches!(result, Err(FsError::NotSupported))); + kassert!(matches!(result, Err(FsError::NotSeekable))); }); test_case!(test_pipe_readable_writable, { diff --git a/os/src/vfs/tests/trait_file.rs b/os/src/vfs/tests/trait_file.rs index a5e0e6f5..5aa8cca6 100644 --- a/os/src/vfs/tests/trait_file.rs +++ b/os/src/vfs/tests/trait_file.rs @@ -42,7 +42,7 @@ test_case!(test_disk_file_vs_pipe_file_lseek, { let result = pipe_file.lseek(0, SeekWhence::Set); kassert!(result.is_err()); - kassert!(matches!(result, Err(FsError::NotSupported))); + kassert!(matches!(result, Err(FsError::NotSeekable))); }); test_case!(test_disk_file_vs_pipe_file_offset, { From 566ed79047e8a3d69805a9d0a2863c5c1b061584 Mon Sep 17 00:00:00 2001 From: ZIYAN137 Date: Thu, 28 May 2026 01:58:28 +0800 Subject: [PATCH 3/5] refactor(net): introduce typed socket errors --- os/src/kernel/syscall/network/addr_ops.rs | 10 ++-- .../kernel/syscall/network/connection_ops.rs | 16 +++--- os/src/kernel/syscall/network/socket_ops.rs | 36 ++++++------- os/src/net/mod.rs | 31 +++++++++++ os/src/net/socket.rs | 52 ++++++++++++------- os/src/net/stack/mod.rs | 48 +++++++++++------ 6 files changed, 126 insertions(+), 67 deletions(-) diff --git a/os/src/kernel/syscall/network/addr_ops.rs b/os/src/kernel/syscall/network/addr_ops.rs index 6f4e88a4..8efce5dd 100644 --- a/os/src/kernel/syscall/network/addr_ops.rs +++ b/os/src/kernel/syscall/network/addr_ops.rs @@ -53,7 +53,7 @@ pub fn sendto( let endpoint = match parse_sockaddr_in(dest_addr, addrlen) { Ok(e) => e, - Err(_) => return -22, // EINVAL + Err(e) => return e.to_errno(), }; let task = current_task(); @@ -270,8 +270,8 @@ pub fn getsockname(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { } }; - if write_sockaddr_in(addr, addrlen, ep).is_err() { - return -22; // EINVAL + if let Err(e) = write_sockaddr_in(addr, addrlen, ep) { + return e.to_errno(); } 0 } @@ -300,8 +300,8 @@ pub fn getpeername(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { }; if let Some(ep) = remote_endpoint { - if write_sockaddr_in(addr, addrlen, ep).is_err() { - return -22; // EINVAL + if let Err(e) = write_sockaddr_in(addr, addrlen, ep) { + return e.to_errno(); } 0 } else { diff --git a/os/src/kernel/syscall/network/connection_ops.rs b/os/src/kernel/syscall/network/connection_ops.rs index e2bcf798..85c92ada 100644 --- a/os/src/kernel/syscall/network/connection_ops.rs +++ b/os/src/kernel/syscall/network/connection_ops.rs @@ -7,7 +7,7 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { pr_debug!("connect: sockfd={}, endpoint={}", sockfd, e); e } - Err(_) => return -22, // EINVAL + Err(e) => return e.to_errno(), }; let task = current_task(); @@ -26,7 +26,9 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { drop(task_lock); use crate::net::socket::set_socket_remote_endpoint; - set_socket_remote_endpoint(&file, endpoint).unwrap(); + if let Err(e) = set_socket_remote_endpoint(&file, endpoint) { + return e.to_errno(); + } let is_nonblock = file .flags() @@ -96,7 +98,7 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { use crate::net::socket::tcp_connect; if let Err(e) = tcp_connect(h, endpoint, local_endpoint) { pr_debug!("connect: tcp_connect failed: {:?}", e); - return -22; // EINVAL or connection error + return e.to_errno(); } // For blocking sockets, wait until connection is established @@ -184,17 +186,15 @@ pub fn connect(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { _ => None, }; - if crate::net::socket::udp_attach_fd_to_port( + if let Err(e) = crate::net::socket::udp_attach_fd_to_port( tid, sockfd as usize, &file, h, local_port, bind_addr, - ) - .is_err() - { - return -98; + ) { + return e.to_errno(); } pr_debug!("connect: sockfd={} UDP -> success", sockfd); } diff --git a/os/src/kernel/syscall/network/socket_ops.rs b/os/src/kernel/syscall/network/socket_ops.rs index d035cd1b..b605a18a 100644 --- a/os/src/kernel/syscall/network/socket_ops.rs +++ b/os/src/kernel/syscall/network/socket_ops.rs @@ -27,11 +27,11 @@ pub fn socket(domain: i32, socket_type: i32, _protocol: i32) -> isize { let handle = match base_type { SOCK_STREAM => match create_tcp_socket() { Ok(h) => h, - Err(_) => return -12, // ENOMEM + Err(e) => return e.to_errno(), }, SOCK_DGRAM => match create_udp_socket() { Ok(h) => h, - Err(_) => return -12, // ENOMEM + Err(e) => return e.to_errno(), }, _ => return -94, // ESOCKTNOSUPPORT }; @@ -66,7 +66,7 @@ pub fn socket(domain: i32, socket_type: i32, _protocol: i32) -> isize { pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { let endpoint = match parse_sockaddr_in(addr, addrlen) { Ok(e) => e, - Err(_) => return -22, // EINVAL + Err(e) => return e.to_errno(), }; let task = current_task(); @@ -111,8 +111,8 @@ pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { return -22; // EINVAL } - if set_socket_local_endpoint(&file, endpoint).is_err() { - return -22; // EINVAL + if let Err(e) = set_socket_local_endpoint(&file, endpoint) { + return e.to_errno(); } } SocketHandle::Udp(h) => { @@ -151,17 +151,15 @@ pub fn bind(sockfd: i32, addr: *const u8, addrlen: u32) -> isize { _ => None, }; - if crate::net::socket::udp_attach_fd_to_port( + if let Err(e) = crate::net::socket::udp_attach_fd_to_port( tid, sockfd as usize, &file, h, endpoint.port, bind_addr, - ) - .is_err() - { - return -98; // EADDRINUSE / bind error + ) { + return e.to_errno(); } } } @@ -247,10 +245,10 @@ pub fn listen(sockfd: i32, backlog: i32) -> isize { listen_endpoint.port ); - if network_stack().tcp_listen(h, listen_endpoint).is_err() { + if let Err(e) = network_stack().tcp_listen(h, listen_endpoint) { attempts_left = attempts_left.saturating_sub(1); if attempts_left == 0 { - return -98; // EADDRINUSE + return e.to_errno(); } endpoint.port = 0; continue; @@ -327,15 +325,13 @@ pub fn accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { // detach current listen socket immediately let new_listen_handle = match create_tcp_socket() { Ok(SocketHandle::Tcp(h)) => h, - _ => return -12, // ENOMEM + Err(e) => return e.to_errno(), + Ok(SocketHandle::Udp(_)) => return -(crate::uapi::errno::EINVAL as isize), }; - if network_stack() - .tcp_listen(new_listen_handle, listen_endpoint) - .is_err() - { + if let Err(e) = network_stack().tcp_listen(new_listen_handle, listen_endpoint) { network_stack().remove_tcp_socket(new_listen_handle); - return -12; // ENOMEM or other error + return e.to_errno(); } use crate::net::socket::{update_socket_file_handle, update_socket_handle}; @@ -344,7 +340,9 @@ pub fn accept(sockfd: i32, addr: *mut u8, addrlen: *mut u32) -> isize { sockfd as usize, SocketHandle::Tcp(new_listen_handle), ); - update_socket_file_handle(&file, SocketHandle::Tcp(new_listen_handle)).unwrap(); + if let Err(e) = update_socket_file_handle(&file, SocketHandle::Tcp(new_listen_handle)) { + return e.to_errno(); + } // Established / CloseWait: this handle is ready to return right away. if matches!( diff --git a/os/src/net/mod.rs b/os/src/net/mod.rs index 97a83c30..18ff8e0c 100644 --- a/os/src/net/mod.rs +++ b/os/src/net/mod.rs @@ -9,6 +9,37 @@ pub mod interface; pub mod socket; pub mod stack; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NetworkError { + NoMemory, + NotInitialized, + InvalidSocket, + InvalidAddress, + BadAddress, + BufferTooSmall, + AddressInUse, + NotSupported, + ConnectFailed, +} + +impl NetworkError { + pub fn to_errno(self) -> isize { + use crate::uapi::errno::*; + + match self { + NetworkError::NoMemory => -ENOMEM as isize, + NetworkError::NotInitialized => -ENETDOWN as isize, + NetworkError::InvalidSocket => -ENOTSOCK as isize, + NetworkError::InvalidAddress => -EINVAL as isize, + NetworkError::BadAddress => -EFAULT as isize, + NetworkError::BufferTooSmall => -EINVAL as isize, + NetworkError::AddressInUse => -EADDRINUSE as isize, + NetworkError::NotSupported => -EOPNOTSUPP as isize, + NetworkError::ConnectFailed => -EINVAL as isize, + } + } +} + /// Register a net device and create its current compatibility interface. /// /// This is the migration boundary between device drivers and the network diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs index 9a1f9054..57fb5693 100644 --- a/os/src/net/socket.rs +++ b/os/src/net/socket.rs @@ -1,6 +1,7 @@ //! Socket implementation using smoltcp use crate::arch::Arch; +use crate::net::NetworkError; use crate::sync::SpinLock; use crate::vfs::{File, FsError, InodeMetadata}; use alloc::collections::VecDeque; @@ -222,13 +223,13 @@ pub fn update_socket_handle(tid: usize, fd: usize, handle: SocketHandle) { pub fn set_socket_local_endpoint( file: &Arc, endpoint: IpEndpoint, -) -> Result<(), ()> { +) -> Result<(), NetworkError> { let any = file.as_any(); if let Some(socket_file) = any.downcast_ref::() { socket_file.set_local_endpoint(endpoint); Ok(()) } else { - Err(()) + Err(NetworkError::InvalidSocket) } } @@ -243,13 +244,13 @@ pub fn get_socket_local_endpoint(file: &Arc) -> Option, endpoint: IpEndpoint, -) -> Result<(), ()> { +) -> Result<(), NetworkError> { let any = file.as_any(); if let Some(socket_file) = any.downcast_ref::() { socket_file.set_remote_endpoint(endpoint); Ok(()) } else { - Err(()) + Err(NetworkError::InvalidSocket) } } @@ -278,13 +279,13 @@ pub fn socket_shutdown_write(file: &Arc) { pub fn update_socket_file_handle( file: &Arc, new_handle: SocketHandle, -) -> Result<(), ()> { +) -> Result<(), NetworkError> { let any = file.as_any(); if let Some(socket_file) = any.downcast_ref::() { socket_file.set_handle(new_handle); Ok(()) } else { - Err(()) + Err(NetworkError::InvalidSocket) } } @@ -339,9 +340,9 @@ const AF_INET: u16 = 2; const SOCKADDR_IN_SIZE: usize = 16; /// Parse sockaddr_in structure from user space -pub fn parse_sockaddr_in(addr: *const u8, addrlen: u32) -> Result { +pub fn parse_sockaddr_in(addr: *const u8, addrlen: u32) -> Result { if (addrlen as usize) < SOCKADDR_IN_SIZE { - return Err(()); + return Err(NetworkError::InvalidAddress); } let mut buf = [0u8; SOCKADDR_IN_SIZE]; @@ -352,11 +353,11 @@ pub fn parse_sockaddr_in(addr: *const u8, addrlen: u32) -> Result Result Result<(), ()> { +pub(crate) fn write_sockaddr_in_to_buf( + buf: &mut [u8], + endpoint: IpEndpoint, +) -> Result<(), NetworkError> { if buf.len() < SOCKADDR_IN_SIZE { - return Err(()); + return Err(NetworkError::BufferTooSmall); } // family @@ -387,11 +391,11 @@ pub(crate) fn write_sockaddr_in_to_buf(buf: &mut [u8], endpoint: IpEndpoint) -> } #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(_) => { - return Err(()); // IPv6 not supported in AF_INET + return Err(NetworkError::NotSupported); // IPv6 not supported in AF_INET } #[cfg(not(feature = "proto-ipv6"))] _ => { - return Err(()); // Unknown address type + return Err(NetworkError::NotSupported); // Unknown address type } } @@ -402,7 +406,11 @@ pub(crate) fn write_sockaddr_in_to_buf(buf: &mut [u8], endpoint: IpEndpoint) -> } /// Write sockaddr_in structure to user space -pub fn write_sockaddr_in(addr: *mut u8, addrlen: *mut u32, endpoint: IpEndpoint) -> Result<(), ()> { +pub fn write_sockaddr_in( + addr: *mut u8, + addrlen: *mut u32, + endpoint: IpEndpoint, +) -> Result<(), NetworkError> { if addr.is_null() || addrlen.is_null() { return Ok(()); } @@ -428,16 +436,16 @@ pub fn write_sockaddr_in(addr: *mut u8, addrlen: *mut u32, endpoint: IpEndpoint) n, ) } - .map_err(|_| ())?; + .map_err(|_| NetworkError::BadAddress)?; Ok(()) } -pub fn create_tcp_socket() -> Result { +pub fn create_tcp_socket() -> Result { crate::net::stack::network_stack().create_tcp_socket() } -pub fn create_udp_socket() -> Result { +pub fn create_udp_socket() -> Result { crate::net::stack::network_stack().create_udp_socket() } @@ -446,7 +454,11 @@ pub fn init_network(smoltcp_iface: crate::net::interface::SmoltcpInterface) { crate::net::stack::network_stack().init_network(smoltcp_iface); } -pub fn tcp_connect(handle: SmoltcpHandle, remote: IpEndpoint, local: IpEndpoint) -> Result<(), ()> { +pub fn tcp_connect( + handle: SmoltcpHandle, + remote: IpEndpoint, + local: IpEndpoint, +) -> Result<(), NetworkError> { crate::net::stack::network_stack().tcp_connect(handle, remote, local) } @@ -472,7 +484,7 @@ pub fn udp_attach_fd_to_port( old_handle: SmoltcpHandle, port: u16, bind_addr: Option, -) -> Result { +) -> Result { crate::net::stack::network_stack() .udp_attach_fd_to_port(tid, fd, file, old_handle, port, bind_addr) } diff --git a/os/src/net/stack/mod.rs b/os/src/net/stack/mod.rs index 5c5df36b..feefb716 100644 --- a/os/src/net/stack/mod.rs +++ b/os/src/net/stack/mod.rs @@ -12,6 +12,7 @@ use smoltcp::socket::{tcp, udp}; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpEndpoint, IpListenEndpoint, Ipv4Address}; +use super::NetworkError; use super::socket::{self, SocketFile, SocketHandle, UdpDatagram}; mod adapter; @@ -150,13 +151,17 @@ impl NetworkStack { } /// Create a TCP socket in the stack runtime. - pub fn create_tcp_socket(&self) -> Result { + pub fn create_tcp_socket(&self) -> Result { let mut rx_vec = alloc::vec::Vec::new(); - rx_vec.try_reserve(4096).map_err(|_| ())?; + rx_vec + .try_reserve(4096) + .map_err(|_| NetworkError::NoMemory)?; rx_vec.resize(4096, 0); let mut tx_vec = alloc::vec::Vec::new(); - tx_vec.try_reserve(4096).map_err(|_| ())?; + tx_vec + .try_reserve(4096) + .map_err(|_| NetworkError::NoMemory)?; tx_vec.resize(4096, 0); let rx_buffer = tcp::SocketBuffer::new(rx_vec); @@ -168,7 +173,7 @@ impl NetworkStack { } /// Create a UDP socket in the stack runtime. - pub fn create_udp_socket(&self) -> Result { + pub fn create_udp_socket(&self) -> Result { let mut sockets = self.socket_set.lock(); let handle = self.create_udp_socket_in_set(&mut sockets)?; Ok(SocketHandle::Udp(handle)) @@ -180,12 +185,12 @@ impl NetworkStack { handle: SmoltcpHandle, remote: IpEndpoint, local: IpEndpoint, - ) -> Result<(), ()> { + ) -> Result<(), NetworkError> { crate::pr_debug!("tcp_connect: start, handle={:?}", handle); let iface_guard = self.net_iface.lock(); crate::pr_debug!("tcp_connect: got net_iface lock"); - let wrapper = iface_guard.as_ref().ok_or(())?; + let wrapper = iface_guard.as_ref().ok_or(NetworkError::NotInitialized)?; let result = wrapper.with_context(|context| { crate::pr_debug!("tcp_connect: in with_context"); @@ -195,6 +200,7 @@ impl NetworkStack { crate::pr_debug!("tcp_connect: calling socket.connect"); let r = socket.connect(context, remote, local).map_err(|e| { crate::pr_debug!("tcp_connect error: {:?}", e); + NetworkError::ConnectFailed }); crate::pr_debug!("tcp_connect: socket.connect returned {:?}", r); r @@ -222,12 +228,16 @@ impl NetworkStack { } /// Start listening on a TCP socket. - pub fn tcp_listen(&self, handle: SmoltcpHandle, endpoint: IpListenEndpoint) -> Result<(), ()> { + pub fn tcp_listen( + &self, + handle: SmoltcpHandle, + endpoint: IpListenEndpoint, + ) -> Result<(), NetworkError> { let mut sockets = self.socket_set.lock(); sockets .get_mut::(handle) .listen(endpoint) - .map_err(|_| ()) + .map_err(|_| NetworkError::AddressInUse) } /// Query listener state and endpoint without exposing `SocketSet`. @@ -344,7 +354,7 @@ impl NetworkStack { old_handle: SmoltcpHandle, port: u16, bind_addr: Option, - ) -> Result { + ) -> Result { let shared_handle = { let mut sockets = self.socket_set.lock(); let mut ports = self.udp_ports.lock(); @@ -358,7 +368,7 @@ impl NetworkStack { }; if sockets.get_mut::(h).bind(listen).is_err() { sockets.remove(h); - return Err(()); + return Err(NetworkError::AddressInUse); } ports.insert( port, @@ -796,21 +806,29 @@ impl NetworkStack { fn create_udp_socket_in_set( &self, sockets: &mut SocketSet<'static>, - ) -> Result { + ) -> Result { let mut rx_meta_vec = alloc::vec::Vec::new(); - rx_meta_vec.try_reserve(4).map_err(|_| ())?; + rx_meta_vec + .try_reserve(4) + .map_err(|_| NetworkError::NoMemory)?; rx_meta_vec.resize(4, udp::PacketMetadata::EMPTY); let mut tx_meta_vec = alloc::vec::Vec::new(); - tx_meta_vec.try_reserve(4).map_err(|_| ())?; + tx_meta_vec + .try_reserve(4) + .map_err(|_| NetworkError::NoMemory)?; tx_meta_vec.resize(4, udp::PacketMetadata::EMPTY); let mut rx_data_vec = alloc::vec::Vec::new(); - rx_data_vec.try_reserve(4096).map_err(|_| ())?; + rx_data_vec + .try_reserve(4096) + .map_err(|_| NetworkError::NoMemory)?; rx_data_vec.resize(4096, 0); let mut tx_data_vec = alloc::vec::Vec::new(); - tx_data_vec.try_reserve(4096).map_err(|_| ())?; + tx_data_vec + .try_reserve(4096) + .map_err(|_| NetworkError::NoMemory)?; tx_data_vec.resize(4096, 0); let rx_buffer = udp::PacketBuffer::new(rx_meta_vec, rx_data_vec); From 85982e81f93ac6b16c069aeb10080482ed81d620 Mon Sep 17 00:00:00 2001 From: ZIYAN137 Date: Thu, 28 May 2026 01:59:36 +0800 Subject: [PATCH 4/5] refactor(util): replace remaining unit errors --- os/src/ipc/pipe.rs | 7 +++++-- os/src/kernel/syscall/fcntl.rs | 4 ++-- os/src/uapi/fcntl.rs | 21 +++++++++++++++++---- os/src/util/ring_buffer.rs | 9 +++++++-- os/src/util/str.rs | 18 +++++++++++++----- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/os/src/ipc/pipe.rs b/os/src/ipc/pipe.rs index b00d991a..dbdf15b5 100644 --- a/os/src/ipc/pipe.rs +++ b/os/src/ipc/pipe.rs @@ -7,7 +7,10 @@ use alloc::{ use crate::{ sync::Mutex, - util::{ring_buffer::RingBuffer, user_buffer::UserBuffer}, + util::{ + ring_buffer::{RingBuffer, RingBufferError}, + user_buffer::UserBuffer, + }, }; /// 创建一个管道,返回读端和写端 @@ -117,7 +120,7 @@ impl PipeRingBuffer { } /// 写入一个字节 - pub fn write_byte(&mut self, byte: u8) -> Result<(), ()> { + pub fn write_byte(&mut self, byte: u8) -> Result<(), RingBufferError> { self.buffer.write_byte(byte) } diff --git a/os/src/kernel/syscall/fcntl.rs b/os/src/kernel/syscall/fcntl.rs index d3ec5f07..d82ce31c 100644 --- a/os/src/kernel/syscall/fcntl.rs +++ b/os/src/kernel/syscall/fcntl.rs @@ -137,7 +137,7 @@ pub fn fcntl(fd: usize, cmd_raw: i32, arg: usize) -> isize { let file_size = metadata.size; let (start, len) = match flock.to_absolute_range(file_offset, file_size) { Ok(range) => range, - Err(_) => return -(EINVAL as isize), + Err(e) => return e.to_errno(), }; // 测试锁 @@ -201,7 +201,7 @@ pub fn fcntl(fd: usize, cmd_raw: i32, arg: usize) -> isize { let file_size = metadata.size; let (start, len) = match flock.to_absolute_range(file_offset, file_size) { Ok(range) => range, - Err(_) => return -(EINVAL as isize), + Err(e) => return e.to_errno(), }; // 获取当前进程 PID diff --git a/os/src/uapi/fcntl.rs b/os/src/uapi/fcntl.rs index fcc24511..5fbac6df 100644 --- a/os/src/uapi/fcntl.rs +++ b/os/src/uapi/fcntl.rs @@ -158,6 +158,19 @@ impl LockType { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FlockRangeError { + InvalidWhence, + NegativeStart, + NegativeLength, +} + +impl FlockRangeError { + pub fn to_errno(self) -> isize { + -(crate::uapi::errno::EINVAL as isize) + } +} + /// 文件锁结构(对应 POSIX struct flock) /// /// 用于 F_GETLK / F_SETLK / F_SETLKW 系统调用 @@ -205,16 +218,16 @@ impl Flock { &self, current_offset: usize, file_size: usize, - ) -> Result<(usize, usize), ()> { + ) -> Result<(usize, usize), FlockRangeError> { let start = match self.l_whence as i32 { SEEK_SET => self.l_start, SEEK_CUR => current_offset as i64 + self.l_start, SEEK_END => file_size as i64 + self.l_start, - _ => return Err(()), + _ => return Err(FlockRangeError::InvalidWhence), }; if start < 0 { - return Err(()); + return Err(FlockRangeError::NegativeStart); } let start = start as usize; @@ -224,7 +237,7 @@ impl Flock { } else if self.l_len > 0 { self.l_len as usize } else { - return Err(()); + return Err(FlockRangeError::NegativeLength); }; Ok((start, len)) diff --git a/os/src/util/ring_buffer.rs b/os/src/util/ring_buffer.rs index 45f7e011..8383c743 100644 --- a/os/src/util/ring_buffer.rs +++ b/os/src/util/ring_buffer.rs @@ -11,6 +11,11 @@ enum BufferStatus { NORMAL, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RingBufferError { + Full, +} + /// 环形缓冲区结构体 pub struct RingBuffer { arr: [u8; RING_BUFFER_SIZE], @@ -46,9 +51,9 @@ impl RingBuffer { } /// 向环形缓冲区写入一个字节 - pub fn write_byte(&mut self, byte: u8) -> Result<(), ()> { + pub fn write_byte(&mut self, byte: u8) -> Result<(), RingBufferError> { if self.status == BufferStatus::FULL { - return Err(()); + return Err(RingBufferError::Full); } self.arr[self.head] = byte; self.head = (self.head + 1) % RING_BUFFER_SIZE; diff --git a/os/src/util/str.rs b/os/src/util/str.rs index 2c34666d..35179c10 100644 --- a/os/src/util/str.rs +++ b/os/src/util/str.rs @@ -7,6 +7,12 @@ use alloc::{ use crate::config::MAX_ARGV; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StringCopyError { + InvalidUtf8, + TooLong, +} + /// 向指定地址写入内容 /// # 参数: /// * `addr` - 目标地址 @@ -32,7 +38,7 @@ pub fn read(addr: usize) -> T { /// 从以 NULL 结尾的 C 字符串指针拷贝并返回一个 owned String /// WARNING: 这个函数直接读取指针,调用者必须保证指针在内核可读 -pub unsafe fn copy_cstr_to_string(ptr: *const u8) -> Result { +pub unsafe fn copy_cstr_to_string(ptr: *const u8) -> Result { const MAX_PATH_LEN: usize = 4096; let mut buf: Vec = Vec::new(); let mut p = ptr; @@ -42,17 +48,19 @@ pub unsafe fn copy_cstr_to_string(ptr: *const u8) -> Result { if b == 0 { return core::str::from_utf8(&buf) .map(|s| s.to_string()) - .map_err(|_| ()); + .map_err(|_| StringCopyError::InvalidUtf8); } buf.push(b); p = unsafe { p.add(1) }; } - Err(()) + Err(StringCopyError::TooLong) } /// 把 NULL 终止的指针数组拷贝为 `Vec` /// WARNING: 这个函数直接读取指针,调用者必须保证指针在内核可读 -pub unsafe fn ptr_array_to_vec_strings(ptrs: *const *const u8) -> Result, ()> { +pub unsafe fn ptr_array_to_vec_strings( + ptrs: *const *const u8, +) -> Result, StringCopyError> { let mut out: Vec = Vec::new(); if ptrs.is_null() { return Ok(out); @@ -64,7 +72,7 @@ pub unsafe fn ptr_array_to_vec_strings(ptrs: *const *const u8) -> Result out.push(s), - Err(_) => return Err(()), + Err(e) => return Err(e), } } Ok(out) From 94ba810c69e18c1b67dd4c028ab488bc4c993bc7 Mon Sep 17 00:00:00 2001 From: ZIYAN137 Date: Thu, 28 May 2026 03:06:53 +0800 Subject: [PATCH 5/5] fix(syscall): limit copied exec argument count --- os/src/kernel/syscall/util.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/os/src/kernel/syscall/util.rs b/os/src/kernel/syscall/util.rs index 93ac4738..88143649 100644 --- a/os/src/kernel/syscall/util.rs +++ b/os/src/kernel/syscall/util.rs @@ -10,6 +10,7 @@ use alloc::{ }; use crate::{ + config::MAX_ARGV, kernel::current_task, uapi::{errno::EINVAL, log::SyslogAction}, vfs::{ @@ -90,6 +91,10 @@ pub fn get_args_safe(ptr_array: usize, _name: &str) -> Result, FsErr break; // NULL 终止 } + if args.len() >= MAX_ARGV { + return Err(FsError::InvalidArgument); + } + let s = copy_str_from_user(ptr_val)?; args.push(s);