From 8ff7cd5546207351d44f55101b1b19a98d487755 Mon Sep 17 00:00:00 2001 From: DoL <1240800466@qq.com> Date: Thu, 5 Jun 2025 15:09:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(vfs)=EF=BC=9A=E4=BF=AE=E5=A4=8Dgetdents?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E8=B0=83=E7=94=A8=20(#1195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/filesystem/vfs/file.rs | 92 +++++++++++------------- kernel/src/filesystem/vfs/mod.rs | 72 +++++++++++++++++++ kernel/src/filesystem/vfs/syscall/mod.rs | 39 ++++++---- 3 files changed, 138 insertions(+), 65 deletions(-) diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index b226f806..4dbbb071 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -4,7 +4,7 @@ use alloc::{string::String, sync::Arc, vec::Vec}; use log::error; use system_error::SystemError; -use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; +use super::{FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; use crate::{ driver::{ base::{block::SeekFrom, device::DevicePrivateData}, @@ -13,6 +13,7 @@ use crate::{ filesystem::{ epoll::{event_poll::EPollPrivateData, EPollItem}, procfs::ProcfsFilePrivateData, + vfs::FilldirContext, }, ipc::pipe::PipeFsPrivateData, libs::{rwlock::RwLock, spinlock::SpinLock}, @@ -345,60 +346,49 @@ impl File { return Ok(()); } - /// @biref 充填dirent结构体 - /// @return 返回dirent结构体的大小 - pub fn readdir(&self, dirent: &mut Dirent) -> Result { + /// # 读取目录项 + /// + /// ## 参数 + /// - ctx 填充目录项的上下文 + pub fn read_dir(&self, ctx: &mut FilldirContext) -> Result<(), SystemError> { let inode: &Arc = &self.inode; - let mut readdir_subdirs_name = self.readdir_subdirs_name.lock(); - let offset = self.offset.load(Ordering::SeqCst); - // 如果偏移量为0 - if offset == 0 { - // 通过list更新readdir_subdirs_name - *readdir_subdirs_name = inode.list()?; - readdir_subdirs_name.sort(); - } - // debug!("sub_entries={sub_entries:?}"); + let mut current_pos = self.offset.load(Ordering::SeqCst); - // 已经读到末尾 - if offset == readdir_subdirs_name.len() { - self.offset.store(0, Ordering::SeqCst); - return Ok(0); - } - let name = &readdir_subdirs_name[offset]; - let sub_inode: Arc = match inode.find(name) { - Ok(i) => i, - Err(e) => { - error!( - "Readdir error: Failed to find sub inode:{name:?}, file={self:?}, error={e:?}" - ); - return Err(e); + // POSIX 标准要求readdir应该返回. 和 .. + // 但是观察到在现有的子目录中已经包含,不做处理也能正常返回. 和 .. 这里先不做处理 + + // 迭代读取目录项 + let readdir_subdirs_name = inode.list()?; + + let subdirs_name_len = readdir_subdirs_name.len(); + while current_pos < subdirs_name_len { + let name = &readdir_subdirs_name[current_pos]; + let sub_inode: Arc = match inode.find(name) { + Ok(i) => i, + Err(e) => { + error!("Readdir error: Failed to find sub inode"); + return Err(e); + } + }; + + let inode_metadata = sub_inode.metadata().unwrap(); + let entry_ino = inode_metadata.inode_id.into() as u64; + let entry_d_type = inode_metadata.file_type.get_file_type_num() as u8; + match ctx.fill_dir(name, current_pos, entry_ino, entry_d_type) { + Ok(_) => { + self.offset.fetch_add(1, Ordering::SeqCst); + current_pos += 1; + } + Err(SystemError::EINVAL) => { + return Ok(()); + } + Err(e) => { + ctx.error = Some(e.clone()); + return Err(e); + } } - }; - - let name_bytes: &[u8] = name.as_bytes(); - - // 根据posix的规定,dirent中的d_name是一个不定长的数组,因此需要unsafe来拷贝数据 - unsafe { - let ptr = &mut dirent.d_name as *mut u8; - - let buf: &mut [u8] = - ::core::slice::from_raw_parts_mut::<'static, u8>(ptr, name_bytes.len() + 1); - buf[0..name_bytes.len()].copy_from_slice(name_bytes); - buf[name_bytes.len()] = 0; } - - self.offset.fetch_add(1, Ordering::SeqCst); - dirent.d_ino = sub_inode.metadata().unwrap().inode_id.into() as u64; - dirent.d_type = sub_inode.metadata().unwrap().file_type.get_file_type_num() as u8; - - // 计算dirent结构体的大小 - let size = (name_bytes.len() + ::core::mem::size_of::() - - ::core::mem::size_of_val(&dirent.d_name)) as u64; - - dirent.d_reclen = size as u16; - dirent.d_off += dirent.d_reclen as i64; - - return Ok(size); + return Ok(()); } pub fn inode(&self) -> Arc { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 3a03513c..36bb7846 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -1073,3 +1073,75 @@ macro_rules! producefs { } define_filesystem_maker_slice!(FSMAKER); + +/// # 批量填充Dirent时的上下文Add commentMore actions +/// linux语义是通过getdents_callback *类型来实现类似链表的迭代填充,这里考虑通过填充传入的缓冲区来实现 +pub struct FilldirContext<'a> { + buf: &'a mut [u8], + current_pos: usize, + remain_size: usize, + error: Option, +} + +impl<'a> FilldirContext<'a> { + pub fn new(user_buf: &'a mut [u8]) -> Self { + Self { + remain_size: user_buf.len(), + buf: user_buf, + current_pos: 0, + error: None, + } + } + + /// # 填充单个dirent结构体 + /// + /// ## 参数 + /// - name 目录项名称 + /// - offset 当前目录项偏移量 + /// - ino 目录项的inode的inode_id + /// - d_type 目录项的inode的file_type_num + fn fill_dir( + &mut self, + name: &str, + offset: usize, + ino: u64, + d_type: u8, + ) -> Result<(), SystemError> { + let name_len = name.as_bytes().len(); + let dirent_size = ::core::mem::size_of::() - ::core::mem::size_of::(); + let reclen = name_len + dirent_size + 1; + + // 将reclen向上对齐usize大小 + let align_up = |len: usize, align: usize| -> usize { (len + align - 1) & !(align - 1) }; + let align_up_reclen = align_up(reclen, ::core::mem::size_of::()); + + // 当前缓冲区空间已不足,返回EINVAL + if align_up_reclen > self.remain_size { + self.error = Some(SystemError::EINVAL); + return Err(SystemError::EINVAL); + } + + // 获取剩余缓冲区 + let current_dirent_slice = &mut self.buf[self.current_pos..]; + let dirent = unsafe { (current_dirent_slice.as_mut_ptr() as *mut Dirent).as_mut() } + .ok_or(SystemError::EFAULT)?; + + // 填充dirent + dirent.d_ino = ino; + dirent.d_type = d_type; + dirent.d_reclen = align_up_reclen as u16; + dirent.d_off = offset as i64; + unsafe { + let ptr = &mut dirent.d_name as *mut u8; + let buf: &mut [u8] = + ::core::slice::from_raw_parts_mut::<'static, u8>(ptr, name_len + 1); + buf[0..name_len].copy_from_slice(name.as_bytes()); + buf[name_len] = 0; + } + + self.current_pos += align_up_reclen; + self.remain_size -= align_up_reclen; + + return Ok(()); + } +} diff --git a/kernel/src/filesystem/vfs/syscall/mod.rs b/kernel/src/filesystem/vfs/syscall/mod.rs index 675d29b4..813a757f 100644 --- a/kernel/src/filesystem/vfs/syscall/mod.rs +++ b/kernel/src/filesystem/vfs/syscall/mod.rs @@ -1,5 +1,5 @@ use crate::filesystem::overlayfs::OverlayMountData; -use crate::filesystem::vfs::FileSystemMakerData; +use crate::filesystem::vfs::{FileSystemMakerData, FilldirContext}; use core::mem::size_of; use alloc::{string::String, sync::Arc, vec::Vec}; @@ -32,7 +32,7 @@ use super::{ }, utils::{rsplit_path, user_path_at}, vcore::{do_mkdir_at, do_remove_dir, do_unlink_at}, - Dirent, FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE, + FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; @@ -648,18 +648,16 @@ impl Syscall { return Ok(VirtAddr::new(buf.as_ptr() as usize)); } - /// @brief 获取目录中的数据 + /// # 获取目录中的数据 /// - /// TODO: 这个函数的语义与Linux不一致,需要修改!!! + /// ## 参数 + /// - fd 文件描述符号 + /// - buf 输出缓冲区 /// - /// @param fd 文件描述符号 - /// @param buf 输出缓冲区 - /// - /// @return 成功返回读取的字节数,失败返回错误码 + /// ## 返回值 + /// - Ok(ctx.current_pos) 填充缓冲区当前指针位置 + /// - Err(ctx.error.unwrap()) 填充缓冲区时返回的错误 pub fn getdents(fd: i32, buf: &mut [u8]) -> Result { - let dirent = - unsafe { (buf.as_mut_ptr() as *mut Dirent).as_mut() }.ok_or(SystemError::EFAULT)?; - if fd < 0 || fd as usize > FileDescriptorVec::PROCESS_MAX_FD { return Err(SystemError::EBADF); } @@ -674,9 +672,22 @@ impl Syscall { // drop guard 以避免无法调度的问题 drop(fd_table_guard); - let res = file.readdir(dirent).map(|x| x as usize); - - return res; + let mut ctx = FilldirContext::new(buf); + match file.read_dir(&mut ctx) { + Ok(_) => { + if ctx.error.is_some() { + if ctx.error == Some(SystemError::EINVAL) { + return Ok(ctx.current_pos); + } else { + return Err(ctx.error.unwrap()); + } + } + return Ok(ctx.current_pos); + } + Err(e) => { + return Err(e); + } + } } /// @brief 创建文件夹