Kaynağa Gözat

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

DoL 1 hafta önce
ebeveyn
işleme
8ff7cd5546

+ 42 - 52
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<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:?}");
-
-        // 已经读到末尾
-        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);
+        let mut current_pos = self.offset.load(Ordering::SeqCst);
+
+        // 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> {

+ 72 - 0
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<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(());
+    }
+}

+ 25 - 14
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不一致,需要修改!!!
-    ///
-    /// @param fd 文件描述符号
-    /// @param buf 输出缓冲区
+    /// ## 参数
+    /// - fd 文件描述符号
+    /// - 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 创建文件夹