fslongjin 2 лет назад
Родитель
Сommit
a4157bb4a7

+ 9 - 0
kernel/filesystem/VFS/VFS.c

@@ -134,4 +134,13 @@ struct vfs_dir_entry_t *vfs_path_walk(char *path, uint64_t flags)
 
         parent = dentry;
     }
+}
+
+/**
+ * @brief 填充dentry
+ * 
+ */
+int vfs_fill_dentry(void *buf, ino_t d_ino, char *name, int namelen, unsigned char type, off_t offset)
+{
+    
 }

+ 15 - 1
kernel/filesystem/VFS/VFS.h

@@ -124,6 +124,12 @@ struct vfs_dir_entry_operations_t
     long (*iput)(struct vfs_dir_entry_t *dEntry, struct vfs_index_node_t *inode);
 };
 
+/**
+ * @brief 填充dirent的函数指针的类型定义
+ *
+ */
+typedef int (*vfs_filldir_t)(void *buf, ino_t d_ino, char *name, int namelen, unsigned char type, off_t offset);
+
 struct vfs_file_operations_t
 {
     long (*open)(struct vfs_index_node_t *inode, struct vfs_file_t *file_ptr);
@@ -132,6 +138,8 @@ struct vfs_file_operations_t
     long (*write)(struct vfs_file_t *file_ptr, char *buf, int64_t count, long *position);
     long (*lseek)(struct vfs_file_t *file_ptr, long offset, long origin);
     long (*ioctl)(struct vfs_index_node_t *inode, struct vfs_file_t *file_ptr, uint64_t cmd, uint64_t arg);
+
+    long (*readdir)(struct vfs_file_t *file_ptr, void *dirent, vfs_filldir_t filler); // 读取文件夹
 };
 
 /**
@@ -161,4 +169,10 @@ struct vfs_superblock_t *vfs_mount_fs(char *name, void *DPTE, uint8_t DPT_type,
  * @param flags 1:返回父目录项, 0:返回结果目录项
  * @return struct vfs_dir_entry_t* 目录项
  */
-struct vfs_dir_entry_t *vfs_path_walk(char *path, uint64_t flags);
+struct vfs_dir_entry_t *vfs_path_walk(char *path, uint64_t flags);
+
+/**
+ * @brief 填充dentry
+ * 
+ */
+int vfs_fill_dentry(void *buf, ino_t d_ino, char *name, int namelen, unsigned char type, off_t offset);

+ 176 - 1
kernel/filesystem/fat32/fat32.c

@@ -347,7 +347,7 @@ find_lookup_success:; // 找到目标dentry
     // todo: 引入devfs后删除这段代码
     if ((tmp_dEntry->DIR_FstClusHI >> 12) && (p->attribute & VFS_ATTR_FILE))
         p->attribute |= VFS_ATTR_DEVICE;
-    
+
     dest_dentry->dir_inode = p;
     kfree(buf);
     return dest_dentry;
@@ -868,6 +868,7 @@ struct vfs_file_operations_t fat32_file_ops =
         .write = fat32_write,
         .lseek = fat32_lseek,
         .ioctl = fat32_ioctl,
+        .readdir = fat32_readdir,
 };
 
 // todo: create
@@ -899,6 +900,180 @@ int64_t fat32_getAttr(struct vfs_dir_entry_t *dEntry, uint64_t *attr)
 int64_t fat32_setAttr(struct vfs_dir_entry_t *dEntry, uint64_t *attr)
 {
 }
+/**
+ * @brief 读取文件夹(在指定目录中找出有效目录项)
+ *
+ * @param file_ptr 文件结构体指针
+ * @param dirent 返回的dirent
+ * @param filler 填充dirent的函数
+ * @return int64_t
+ */
+int64_t fat32_readdir(struct vfs_file_t *file_ptr, void *dirent, vfs_filldir_t filler)
+{
+    struct fat32_inode_info_t *finode = (struct fat32_inode_info_t *)file_ptr->dEntry->dir_inode->private_inode_info;
+    fat32_sb_info_t *fsbi = (fat32_sb_info_t *)file_ptr->dEntry->dir_inode->sb->private_sb_info;
+
+    unsigned char *buf = (unsigned char *)kmalloc(fsbi->bytes_per_clus, 0);
+    uint32_t cluster = finode->first_clus;
+
+    // 当前文件指针所在位置的簇号(文件内偏移量)
+    int clus_num = file_ptr->position / fsbi->bytes_per_clus;
+
+    // 循环读取fat entry,直到读取到文件当前位置的所在簇号
+    for (int i = 0; i < clus_num; ++i)
+    {
+        cluster = fat32_read_FAT_entry(fsbi, cluster);
+        if (cluster > 0x0ffffff7) // 文件结尾
+        {
+            kerror("file position out of range! (cluster not exists)");
+            return NULL;
+        }
+    }
+
+    char *dir_name = NULL;
+    int name_len = 0;
+    // ==== 此时已经将文件夹的目录项起始簇的簇号读取到cluster变量中 ===
+    while (cluster <= 0x0ffffff7) // cluster在循环末尾更新(如果当前簇已经没有短目录项的话)
+    {
+        // 计算文件夹当前位置所在簇的起始扇区号
+        uint64_t sector = fsbi->first_data_sector + (cluster - 2) * fsbi->sec_per_clus;
+        // 读取文件夹目录项当前位置起始扇区的数据
+        if (AHCI_SUCCESS != ahci_operation.transfer(AHCI_CMD_READ_DMA_EXT, sector, fsbi->sec_per_clus, (uint64_t)buf, fsbi->ahci_ctrl_num, fsbi->ahci_port_num))
+        {
+            // 读取失败
+            kerror("Failed to read the file's first sector.");
+            kfree(buf);
+            return NULL;
+        }
+
+        struct fat32_Directory_t *dentry = NULL;
+        struct fat32_LongDirectory_t *long_dentry = NULL;
+
+        // 找到当前短目录项
+        dentry = (struct fat32_Directory_t *)(buf + file_ptr->position % fsbi->bytes_per_clus);
+
+        name_len = 0;
+        // 逐个查找短目录项
+        for (int i = file_ptr->position % fsbi->bytes_per_clus; i < fsbi->bytes_per_clus; i += 32, file_ptr->position += 32, ++dentry)
+        {
+            // 若是长目录项则跳过
+            if (dentry->DIR_Attr == ATTR_LONG_NAME)
+                continue;
+            // 跳过无效表项、空闲表项
+            if (dentry->DIR_Name[0] == 0xe5 || dentry->DIR_Name[0] == 0x00 || dentry->DIR_Name[0] == 0x05)
+                continue;
+
+            // 找到短目录项
+            // 该短目录项对应的第一个长目录项
+            long_dentry = (struct fat32_LongDirectory_t *)(dentry - 1);
+
+            // 如果长目录项有效,则读取长目录项
+            if (long_dentry->LDIR_Attr == ATTR_LONG_NAME && long_dentry->LDIR_Ord != 0xe5 && long_dentry->LDIR_Ord != 0x00 && long_dentry->LDIR_Ord != 0x05)
+            {
+                int count_long_dentry = 0;
+                // 统计长目录项的个数
+                while (long_dentry->LDIR_Attr == ATTR_LONG_NAME && long_dentry->LDIR_Ord != 0xe5 && long_dentry->LDIR_Ord != 0x00 && long_dentry->LDIR_Ord != 0x05)
+                {
+                    ++count_long_dentry;
+                    if (long_dentry->LDIR_Ord & 0x40) // 最后一个长目录项
+                        break;
+                    --long_dentry;
+                }
+                // 为目录名分配空间
+                dir_name = (char *)kmalloc(count_long_dentry * 26 + 1, 0);
+                memset(dir_name, 0, count_long_dentry * 26 + 1);
+
+                // 重新将长目录项指针指向第一个长目录项
+                long_dentry = (struct fat32_LongDirectory_t *)(dentry - 1);
+                name_len = 0;
+                // 逐个存储文件名
+                for (int j = 0; j < count_long_dentry; ++j, --long_dentry)
+                {
+                    // 存储name1
+                    for (int k = 0; k < 5; ++k)
+                    {
+                        if (long_dentry->LDIR_Name1[k] != 0xffff && long_dentry->LDIR_Name1[k] != 0x0000)
+                            dir_name[name_len++] = (char)long_dentry->LDIR_Name1[k];
+                    }
+
+                    // 存储name2
+                    for (int k = 0; k < 6; ++k)
+                    {
+                        if (long_dentry->LDIR_Name2[k] != 0xffff && long_dentry->LDIR_Name2[k] != 0x0000)
+                            dir_name[name_len++] = (char)long_dentry->LDIR_Name2[k];
+                    }
+
+                    // 存储name3
+                    for (int k = 0; k < 2; ++k)
+                    {
+                        if (long_dentry->LDIR_Name3[k] != 0xffff && long_dentry->LDIR_Name3[k] != 0x0000)
+                            dir_name[name_len++] = (char)long_dentry->LDIR_Name3[k];
+                    }
+                }
+
+                // 读取目录项成功,返回
+                goto find_dir_success;
+            }
+            else // 不存在长目录项
+            {
+                dir_name = (char *)kmalloc(15, 0);
+                memset(dir_name, 0, 15);
+
+                name_len = 0;
+                int total_len = 0;
+                // 读取基础名
+                for (int j = 0; j < 8; ++j, ++total_len)
+                {
+                    if (dentry->DIR_Name[j] == ' ')
+                        break;
+
+                    if (dentry->DIR_NTRes & LOWERCASE_BASE) // 如果标记了文件名小写,则转换为小写字符
+                        dir_name[name_len++] = dentry->DIR_Name[j] + 32;
+                    else
+                        dir_name[name_len++] = dentry->DIR_Name[j];
+                }
+
+                // 如果当前短目录项为文件夹,则直接返回,不需要读取扩展名
+                if (dentry->DIR_Attr & ATTR_DIRECTORY)
+                    goto find_dir_success;
+
+                // 是文件,增加  .
+                dir_name[name_len++] = '.';
+
+                // 读取扩展名
+                // 读取基础名
+                for (int j = 0; j < 3; ++j, ++total_len)
+                {
+                    if (dentry->DIR_Name[j] == ' ')
+                        break;
+
+                    if (dentry->DIR_NTRes & LOWERCASE_BASE) // 如果标记了文件名小写,则转换为小写字符
+                        dir_name[name_len++] = dentry->DIR_Name[j] + 32;
+                    else
+                        dir_name[name_len++] = dentry->DIR_Name[j];
+                }
+
+                if (total_len == 8) // 没有扩展名
+                    dir_name[--name_len] = '\0';
+
+                goto find_dir_success;
+            }
+        }
+
+        // 当前簇不存在目录项
+        cluster = fat32_read_FAT_entry(fsbi, cluster);
+    }
+
+    kfree(buf);
+    // 在上面的循环中读取到目录项结尾了,仍没有找到
+    return NULL;
+
+find_dir_success:;
+    // 将文件夹位置坐标加32(即指向下一个目录项)
+    file_ptr->position += 32;
+    // todo: 计算ino_t
+    return filler(dirent, 0, dir_name, name_len, 0, 0);
+}
 
 struct vfs_inode_operations_t fat32_inode_ops =
     {

+ 14 - 4
kernel/filesystem/fat32/fat32.h

@@ -155,9 +155,9 @@ typedef struct fat32_partition_info_t fat32_sb_info_t;
 
 struct fat32_inode_info_t
 {
-    uint64_t first_clus;
-    uint64_t dEntry_location_clus;        // dEntry struct in cluster (0 is root, 1 is invalid)
-    uint64_t dEntry_location_clus_offset; // dEntry struct offset in cluster
+    uint64_t first_clus;    // 文件的起始簇号
+    uint64_t dEntry_location_clus;        // fat entry的起始簇号 dEntry struct in cluster (0 is root, 1 is invalid)
+    uint64_t dEntry_location_clus_offset; // fat entry在起始簇中的偏移量(是第几个entry) dEntry struct offset in cluster
 
     uint16_t create_date;
     uint16_t create_time;
@@ -189,4 +189,14 @@ struct vfs_superblock_t *fat32_read_superblock(void *DPTE, uint8_t DPT_type, voi
 
 long fat32_create(struct vfs_index_node_t *inode, struct vfs_dir_entry_t *dentry, int mode);
 
-void fat32_init();
+void fat32_init();
+
+/**
+ * @brief 读取文件夹(在指定目录中找出有效目录项)
+ *
+ * @param file_ptr 文件结构体指针
+ * @param dirent 返回的dirent
+ * @param filler 填充dirent的函数
+ * @return int64_t
+ */
+int64_t fat32_readdir(struct vfs_file_t *file_ptr, void *dirent, vfs_filldir_t filler);

+ 37 - 5
kernel/syscall/syscall.c

@@ -143,8 +143,12 @@ uint64_t sys_open(struct pt_regs *regs)
     if (dentry == NULL)
         return -ENOENT;
 
-    // 暂时认为目标是目录是一种错误
-    if (dentry->dir_inode->attribute == VFS_ATTR_DIR)
+    // 要求打开文件夹而目标不是文件夹
+    if ((flags & O_DIRECTORY) && (dentry->dir_inode->attribute != VFS_ATTR_DIR))
+        return -ENOTDIR;
+
+    // 要找的目标是文件夹
+    if ((flags & O_DIRECTORY) && dentry->dir_inode->attribute == VFS_ATTR_DIR)
         return -EISDIR;
 
     // todo: 引入devfs后删除这段代码
@@ -485,7 +489,6 @@ uint64_t sys_chdir(struct pt_regs *regs)
     // 计算输入的路径长度
     int dest_path_len = strnlen_user(dest_path, PAGE_4K_SIZE);
 
-
     // 长度小于等于0
     if (dest_path_len <= 0)
         return -EFAULT;
@@ -503,7 +506,6 @@ uint64_t sys_chdir(struct pt_regs *regs)
     // 将字符串从用户空间拷贝进来, +1是为了拷贝结尾的\0
     strncpy_from_user(path, dest_path, dest_path_len + 1);
 
-
     struct vfs_dir_entry_t *dentry = vfs_path_walk(path, 0);
 
     kfree(path);
@@ -518,6 +520,35 @@ uint64_t sys_chdir(struct pt_regs *regs)
     return 0;
 }
 
+/**
+ * @brief 获取目录中的数据
+ *
+ * @param fd 文件描述符号
+ * @return uint64_t
+ */
+uint64_t sys_getdents(struct pt_regs *regs)
+{
+    int fd = (int)regs->r8;
+    void *dirent = (void *)regs->r9;
+    long count = (long)regs->r10;
+
+    if (fd < 0 || fd > PROC_MAX_FD_NUM)
+        return -EBADF;
+
+    if (count < 0)
+        return -EINVAL;
+
+    struct vfs_file_t *filp = current_pcb->fds[fd];
+    if (filp == NULL)
+        return -EBADF;
+
+    uint64_t retval = 0;
+    if (filp->file_ops && filp->file_ops->readdir)
+        retval = filp->file_ops->readdir(filp, dirent, &vfs_fill_dentry);
+
+    return retval;
+}
+
 ul sys_ahci_end_req(struct pt_regs *regs)
 {
     ahci_end_request();
@@ -547,5 +578,6 @@ system_call_t system_call_table[MAX_SYSTEM_CALL_NUM] =
         [10] = sys_sbrk,
         [11] = sys_reboot,
         [12] = sys_chdir,
-        [13 ... 254] = system_call_not_exists,
+        [13] = sys_getdents,
+        [14 ... 254] = system_call_not_exists,
         [255] = sys_ahci_end_req};

+ 1 - 0
kernel/syscall/syscall_num.h

@@ -22,5 +22,6 @@
 #define SYS_SBRK 10
 #define SYS_REBOOT 11   // 重启
 #define SYS_CHDIR 12    // 切换工作目录
+#define SYS_GET_DENTS 13 // 获取目录中的数据
 
 #define SYS_AHCI_END_REQ 255    // AHCI DMA请求结束end_request的系统调用

+ 4 - 1
user/libs/libc/Makefile

@@ -35,4 +35,7 @@ ctype.o: ctype.c
 	gcc $(CFLAGS) -c ctype.c -o ctype.o
 
 string.o: string.c
-	gcc $(CFLAGS) -c string.c -o string.o
+	gcc $(CFLAGS) -c string.c -o string.o
+
+dirent.o: dirent.c
+	gcc $(CFLAGS) -c dirent.c -o dirent.o

+ 71 - 0
user/libs/libc/dirent.c

@@ -0,0 +1,71 @@
+#include "dirent.h"
+#include "unistd.h"
+#include "stdio.h"
+#include "fcntl.h"
+#include "stddef.h"
+#include "stdlib.h"
+#include "string.h"
+#include <libsystem/syscall.h>
+
+/**
+ * @brief 打开文件夹
+ *
+ * @param dirname
+ * @return DIR*
+ */
+struct DIR *opendir(const char *path)
+{
+    int fd = open(path, O_DIRECTORY);
+    if (fd < 0) // 目录打开失败
+        return NULL;
+
+    // 分配DIR结构体
+    struct DIR *dirp = (struct DIR *)malloc(sizeof(struct DIR));
+    memset(dirp, 0, sizeof(struct DIR));
+    dirp->fd = fd;
+    dirp->buf_len = DIR_BUF_SIZE;
+    dirp->buf_pos = 0;
+
+    return dirp;
+}
+
+/**
+ * @brief 关闭文件夹
+ *
+ * @param dirp DIR结构体指针
+ * @return int 成功:0, 失败:-1
++--------+--------------------------------+
+| errno  |              描述               |
++--------+--------------------------------+
+|   0    |              成功               |
+| -EBADF | 当前dirp不指向一个打开了的目录      |
+| -EINTR |     函数执行期间被信号打断         |
++--------+--------------------------------+
+ */
+int closedir(struct DIR *dirp)
+{
+    int retval = close(dirp->fd);
+    free(dirp);
+    return retval;
+}
+
+int64_t getdents(int fd, struct dirent *dirent, long count)
+{
+    return syscall_invoke(SYS_GET_DENTS, fd, (uint64_t)dirent, count, 0, 0, 0, 0, 0);
+}
+/**
+ * @brief 从目录中读取数据
+ *
+ * @param dir
+ * @return struct dirent*
+ */
+struct dirent *reaaddir(struct DIR *dir)
+{
+    memset(dir, 0, DIR_BUF_SIZE);
+    int len = getdents(dir->fd, (struct dirent *)dir->buf, DIR_BUF_SIZE);
+
+    if (len > 0)
+        return (struct dirent *)dir->buf;
+    else
+        return NULL;
+}

+ 57 - 0
user/libs/libc/dirent.h

@@ -0,0 +1,57 @@
+#pragma once
+#include <libc/sys/types.h>
+
+#define DIR_BUF_SIZE 256
+/**
+ * @brief 文件夹结构体
+ *
+ */
+struct DIR
+{
+    int fd;
+    int buf_pos;
+    int buf_len;
+    char buf[DIR_BUF_SIZE];
+
+    // todo: 加一个指向dirent结构体的指针
+};
+
+struct dirent
+{
+    ino_t d_ino;    // 文件序列号
+    off_t d_off;    // dir偏移量
+    unsigned short d_reclen;    // 目录下的记录数
+    unsigned char d_type;   // entry的类型
+    char d_name[256];   // 文件entry的名字
+};
+
+/**
+ * @brief 打开文件夹
+ *
+ * @param dirname
+ * @return DIR*
+ */
+struct DIR *opendir(const char *dirname);
+
+/**
+ * @brief 关闭文件夹
+ *
+ * @param dirp DIR结构体指针
+ * @return int 成功:0, 失败:-1
++--------+--------------------------------+
+| errno  |              描述               |
++--------+--------------------------------+
+|   0    |              成功               |
+| -EBADF | 当前dirp不指向一个打开了的目录      |
+| -EINTR |     函数执行期间被信号打断         |
++--------+--------------------------------+
+ */
+int closedir(struct DIR *dirp);
+
+/**
+ * @brief 从目录中读取数据
+ * 
+ * @param dir 
+ * @return struct dirent* 
+ */
+struct dirent* reaaddir(struct DIR* dir);

+ 1 - 0
user/libs/libsystem/syscall.h

@@ -16,6 +16,7 @@
 #define SYS_SBRK 10
 #define SYS_REBOOT 11
 #define SYS_CHDIR 12    // 切换工作目录
+#define SYS_GET_DENTS 13 // 获取目录中的数据
 
 /**
  * @brief 用户态系统调用函数