From a4157bb4a731f0ea3b4e2c1aa85231525abf65be Mon Sep 17 00:00:00 2001 From: fslongjin Date: Fri, 27 May 2022 13:41:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AE=8C=E6=88=90=E4=BA=86re?= =?UTF-8?q?addir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/filesystem/VFS/VFS.c | 9 ++ kernel/filesystem/VFS/VFS.h | 16 ++- kernel/filesystem/fat32/fat32.c | 177 +++++++++++++++++++++++++++++++- kernel/filesystem/fat32/fat32.h | 18 +++- kernel/syscall/syscall.c | 42 +++++++- kernel/syscall/syscall_num.h | 1 + user/libs/libc/Makefile | 5 +- user/libs/libc/dirent.c | 71 +++++++++++++ user/libs/libc/dirent.h | 57 ++++++++++ user/libs/libsystem/syscall.h | 1 + 10 files changed, 385 insertions(+), 12 deletions(-) create mode 100644 user/libs/libc/dirent.c create mode 100644 user/libs/libc/dirent.h diff --git a/kernel/filesystem/VFS/VFS.c b/kernel/filesystem/VFS/VFS.c index 7c3f3139..3039d31d 100644 --- a/kernel/filesystem/VFS/VFS.c +++ b/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) +{ + } \ No newline at end of file diff --git a/kernel/filesystem/VFS/VFS.h b/kernel/filesystem/VFS/VFS.h index 2a3376ad..5c0769ef 100644 --- a/kernel/filesystem/VFS/VFS.h +++ b/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); \ No newline at end of file +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); \ No newline at end of file diff --git a/kernel/filesystem/fat32/fat32.c b/kernel/filesystem/fat32/fat32.c index 65d06044..d9d3bc7b 100644 --- a/kernel/filesystem/fat32/fat32.c +++ b/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 = { diff --git a/kernel/filesystem/fat32/fat32.h b/kernel/filesystem/fat32/fat32.h index c96ffc29..f73fffde 100644 --- a/kernel/filesystem/fat32/fat32.h +++ b/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(); \ No newline at end of file +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); \ No newline at end of file diff --git a/kernel/syscall/syscall.c b/kernel/syscall/syscall.c index 7058de50..5d682ade 100644 --- a/kernel/syscall/syscall.c +++ b/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}; diff --git a/kernel/syscall/syscall_num.h b/kernel/syscall/syscall_num.h index 0fcbd670..b628dcf8 100644 --- a/kernel/syscall/syscall_num.h +++ b/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的系统调用 \ No newline at end of file diff --git a/user/libs/libc/Makefile b/user/libs/libc/Makefile index 85d40d2a..5c3a1aab 100644 --- a/user/libs/libc/Makefile +++ b/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 \ No newline at end of file + gcc $(CFLAGS) -c string.c -o string.o + +dirent.o: dirent.c + gcc $(CFLAGS) -c dirent.c -o dirent.o \ No newline at end of file diff --git a/user/libs/libc/dirent.c b/user/libs/libc/dirent.c new file mode 100644 index 00000000..60767780 --- /dev/null +++ b/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 + +/** + * @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; +} \ No newline at end of file diff --git a/user/libs/libc/dirent.h b/user/libs/libc/dirent.h new file mode 100644 index 00000000..1f13a379 --- /dev/null +++ b/user/libs/libc/dirent.h @@ -0,0 +1,57 @@ +#pragma once +#include + +#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); \ No newline at end of file diff --git a/user/libs/libsystem/syscall.h b/user/libs/libsystem/syscall.h index 1b003cf7..5ffd286e 100644 --- a/user/libs/libsystem/syscall.h +++ b/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 用户态系统调用函数