fix(vfs):修复getdents系统调用 (#1195)

This commit is contained in:
DoL 2025-06-05 15:09:43 +08:00 committed by GitHub
parent 326cf3e0a3
commit 8ff7cd5546
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 138 additions and 65 deletions

View File

@ -4,7 +4,7 @@ use alloc::{string::String, sync::Arc, vec::Vec};
use log::error; use log::error;
use system_error::SystemError; use system_error::SystemError;
use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; use super::{FileType, IndexNode, InodeId, Metadata, SpecialNodeData};
use crate::{ use crate::{
driver::{ driver::{
base::{block::SeekFrom, device::DevicePrivateData}, base::{block::SeekFrom, device::DevicePrivateData},
@ -13,6 +13,7 @@ use crate::{
filesystem::{ filesystem::{
epoll::{event_poll::EPollPrivateData, EPollItem}, epoll::{event_poll::EPollPrivateData, EPollItem},
procfs::ProcfsFilePrivateData, procfs::ProcfsFilePrivateData,
vfs::FilldirContext,
}, },
ipc::pipe::PipeFsPrivateData, ipc::pipe::PipeFsPrivateData,
libs::{rwlock::RwLock, spinlock::SpinLock}, libs::{rwlock::RwLock, spinlock::SpinLock},
@ -345,60 +346,49 @@ impl File {
return Ok(()); return Ok(());
} }
/// @biref 充填dirent结构体 /// # 读取目录项
/// @return 返回dirent结构体的大小 ///
pub fn readdir(&self, dirent: &mut Dirent) -> Result<u64, SystemError> { /// ## 参数
/// - ctx 填充目录项的上下文
pub fn read_dir(&self, ctx: &mut FilldirContext) -> Result<(), SystemError> {
let inode: &Arc<dyn IndexNode> = &self.inode; let inode: &Arc<dyn IndexNode> = &self.inode;
let mut readdir_subdirs_name = self.readdir_subdirs_name.lock(); let mut current_pos = self.offset.load(Ordering::SeqCst);
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:?}");
// 已经读到末尾 // POSIX 标准要求readdir应该返回. 和 ..
if offset == readdir_subdirs_name.len() { // 但是观察到在现有的子目录中已经包含,不做处理也能正常返回. 和 .. 这里先不做处理
self.offset.store(0, Ordering::SeqCst);
return Ok(0); // 迭代读取目录项
} let readdir_subdirs_name = inode.list()?;
let name = &readdir_subdirs_name[offset];
let sub_inode: Arc<dyn IndexNode> = match inode.find(name) { let subdirs_name_len = readdir_subdirs_name.len();
Ok(i) => i, while current_pos < subdirs_name_len {
Err(e) => { let name = &readdir_subdirs_name[current_pos];
error!( let sub_inode: Arc<dyn IndexNode> = match inode.find(name) {
"Readdir error: Failed to find sub inode:{name:?}, file={self:?}, error={e:?}" Ok(i) => i,
); Err(e) => {
return 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;
} }
return Ok(());
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::<Dirent>()
- ::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);
} }
pub fn inode(&self) -> Arc<dyn IndexNode> { pub fn inode(&self) -> Arc<dyn IndexNode> {

View File

@ -1073,3 +1073,75 @@ macro_rules! producefs {
} }
define_filesystem_maker_slice!(FSMAKER); 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<SystemError>,
}
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::<Dirent>() - ::core::mem::size_of::<u8>();
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::<usize>());
// 当前缓冲区空间已不足返回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(());
}
}

View File

@ -1,5 +1,5 @@
use crate::filesystem::overlayfs::OverlayMountData; use crate::filesystem::overlayfs::OverlayMountData;
use crate::filesystem::vfs::FileSystemMakerData; use crate::filesystem::vfs::{FileSystemMakerData, FilldirContext};
use core::mem::size_of; use core::mem::size_of;
use alloc::{string::String, sync::Arc, vec::Vec}; use alloc::{string::String, sync::Arc, vec::Vec};
@ -32,7 +32,7 @@ use super::{
}, },
utils::{rsplit_path, user_path_at}, utils::{rsplit_path, user_path_at},
vcore::{do_mkdir_at, do_remove_dir, do_unlink_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, VFS_MAX_FOLLOW_SYMLINK_TIMES,
}; };
@ -648,18 +648,16 @@ impl Syscall {
return Ok(VirtAddr::new(buf.as_ptr() as usize)); return Ok(VirtAddr::new(buf.as_ptr() as usize));
} }
/// @brief 获取目录中的数据 /// # 获取目录中的数据
/// ///
/// TODO: 这个函数的语义与Linux不一致需要修改 /// ## 参数
/// - fd 文件描述符号
/// - buf 输出缓冲区
/// ///
/// @param fd 文件描述符号 /// ## 返回值
/// @param buf 输出缓冲区 /// - Ok(ctx.current_pos) 填充缓冲区当前指针位置
/// /// - Err(ctx.error.unwrap()) 填充缓冲区时返回的错误
/// @return 成功返回读取的字节数,失败返回错误码
pub fn getdents(fd: i32, buf: &mut [u8]) -> Result<usize, SystemError> { pub fn getdents(fd: i32, buf: &mut [u8]) -> Result<usize, SystemError> {
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 { if fd < 0 || fd as usize > FileDescriptorVec::PROCESS_MAX_FD {
return Err(SystemError::EBADF); return Err(SystemError::EBADF);
} }
@ -674,9 +672,22 @@ impl Syscall {
// drop guard 以避免无法调度的问题 // drop guard 以避免无法调度的问题
drop(fd_table_guard); drop(fd_table_guard);
let res = file.readdir(dirent).map(|x| x as usize); let mut ctx = FilldirContext::new(buf);
match file.read_dir(&mut ctx) {
return res; 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 创建文件夹 /// @brief 创建文件夹