mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 14:16:47 +00:00
fix(vfs):修复getdents系统调用 (#1195)
This commit is contained in:
parent
326cf3e0a3
commit
8ff7cd5546
@ -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<u64, SystemError> {
|
||||
/// # 读取目录项
|
||||
///
|
||||
/// ## 参数
|
||||
/// - ctx 填充目录项的上下文
|
||||
pub fn read_dir(&self, ctx: &mut FilldirContext) -> Result<(), SystemError> {
|
||||
let inode: &Arc<dyn IndexNode> = &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<dyn IndexNode> = 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<dyn IndexNode> = 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::<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);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn inode(&self) -> Arc<dyn IndexNode> {
|
||||
|
@ -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<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(());
|
||||
}
|
||||
}
|
||||
|
@ -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<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 {
|
||||
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 创建文件夹
|
||||
|
Loading…
x
Reference in New Issue
Block a user