一、打开/关闭目录
打开目录其实就是通过dir_open函数打开指定目录的inode,打开inode之前已经说过了就是把硬盘中的inode数组中的inode结构从硬盘中读入到内存中即可。
关闭目录其实就是通过dir_close函数关闭指定目录的inode,关闭inode也提到过就是把内存中的inode结构占用的内存通过sys_free函数释放掉。
这里再把inode_open、inode_close、dir_open、dir_close以及dir、inode的结构再贴一下强化记忆
struct dir {
struct inode* inode;
uint32_t dir_pos;
uint8_t dir_buf[512];
};
struct inode {
uint32_t i_no; //inode编号
uint32_t i_size; //当inode是文件时,i_size指文件大小;当inode是目录时,i_size指该目录下所有目录项大小之和
uint32_t i_open_cnts; //记录次文件被打开的次数
bool write_deny; //写文件不能并行,进程写文件前检查次标识
uint32_t i_sectors[13]; //0-11是直接块,12用来存储一级间接块指针
struct list_elem inode_tag; //用于加入已打开的inode队列
};
struct inode* inode_open(struct partition* part, uint32_t inode_no) {
/* 在已打开的inode链表中找是否存在编号为inode_no的inode */
struct list_elem* elem = part->open_inodes.head.next;
struct inode* inode_found;
while(elem != &part->open_inodes.tail) {
inode_found = elem2entry(struct inode, inode_tag, elem);
if(inode_found->i_no == inode_no) {
inode_found->i_open_cnts++;
return inode_found;
}
elem = elem->next;
}
/* 在内核空间创建inode,因为该inode需要被所有任务共享 */
struct task_struct* cur = running_thread();
uint32_t* cur_pagedir_bak = cur->pgdir;
cur->pgdir = NULL;
inode_found = (struct inode*) sys_malloc(sizeof(struct inode));
cur->pgdir = cur_pagedir_bak;
/* 定位inode */
struct inode_position inode_pos;
inode_locate(part, inode_no, &inode_pos);
/* 从硬盘中读出inode并拷贝到内核空间的inode中 */
char* inode_buf;
if(inode_pos.two_sec) {
inode_buf = (char*) sys_malloc(SECTOR_SIZE * 2);
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
} else {
inode_buf = (char*) sys_malloc(SECTOR_SIZE);
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
}
memcpy(inode_found, inode_buf + inode_pos.off_size, sizeof(struct inode));
sys_free(inode_buf);
/* 将内核空间的inode加入到已打开的inode队列中 */
list_push(&part->open_inodes, &inode_found->inode_tag);
inode_found->i_open_cnts = 1;
return inode_found;
}
void inode_close(struct inode* inode) {
enum intr_status old_status = intr_disable();
/* 将inode的i_open_cnts属性-1判断是否无任务引用该inode */
if(--inode->i_open_cnts == 0) {
/* 将inode移除出分区的已打开inode节点队列 */
list_remove(&inode->inode_tag);
/* 在内核内存池中释放inode */
struct task_struct* cur = running_thread();
uint32_t* cur_pagedir_bak = cur->pgdir;
cur->pgdir = NULL;
sys_free(inode);
cur->pgdir = cur_pagedir_bak;
}
intr_set_status(old_status);
}
/* 打开目录同打开根目录一致 */
struct dir* dir_open(struct partition* part, uint32_t inode_no) {
struct dir* pdir = (struct dir*) sys_malloc(sizeof(struct dir));
pdir->inode = inode_open(part, inode_no);
pdir->dir_pos = 0;
return pdir;
}
/* 关闭目录,实际上就是关闭目录代表的inode并释放目录结构在内存中所占的空间 */
void dir_close(struct dir* dir) {
if(dir == &root_dir) {
return;
}
inode_close(dir->inode);
sys_free(dir);
}
下面给出sys_opendir以及sys_closedir的代码:
/* 目录打开成功返回目录指针,失败返回NULL */
struct dir* sys_opendir(char* name) {
/* 判断是否是根目录 */
if(name[0] == '/' && (name[1] == 0 || name[1] == '.')) {
return &root_dir;
}
/* 检查待打开的目录是否存在 */
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = search_file(name, &searched_record);
struct dir* ret = NULL;
if(inode_no == -1) {
printk("In %s, sub path %s not exist\n", name, searched_record.searched_path);
} else {
if(searched_record.file_type == FT_REGULAR) {
printk("%s is regular file!\n", name);
} else {
ret = dir_open(cur_part, inode_no);
}
}
dir_close(searched_record.parent_dir);
return ret;
}
/* 成功关闭目录dir返回0,失败返回-1 */
int32_t sys_closedir(struct dir* dir) {
int32_t ret = -1;
if(dir != NULL) {
dir_close(dir);
ret = 0;
}
return ret;
}
实验结果
#include "print.h"
#include "init.h"
#include "debug.h"
#include "memory.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"
#include "fs.h"
int main(void)
{
put_str("I am kernel\n");
init_all();
intr_enable();
struct dir* p_dir = sys_opendir("/dir1/subdir1");
if(p_dir) {
printf("/dir1/subdir1 open done!\n");
if(sys_closedir(p_dir) == 0) {
printf("/dir1/subdir1 close done!\n");
} else {
printf("/dir1/subdir1 close fail!\n");
}
} else {
printf("/dir1/subdir1 open fail!\n");
}
while(1);
return 0;
}
二、遍历目录
遍历目录dir_read函数主要流程梳理:
(1)、将目录dir的所有磁盘数据块lba地址读入all_blocks中
(2)、逐个数据块读入到内存进行遍历
(2.1)、数据块中的逐个目录项进行遍历直到找到下一个要返回的目录项(dir->dir_pos代表已读入的目录项字节大小,下一个要返回的目录项位于距离数据块开始后的dir->dir_pos字节处)
这里再贴一下dir_entry的数据结构强化记忆,上代码(遍历目录的代码位于fs/dir.c中,包装函数sys_readdir位于fs/fs.c中)
/* 目录项结构 */
struct dir_entry {
char filename[MAX_FILE_NAME_LEN]; //普通文件或目录文件名称
uint32_t i_no; //普通文件或目录对应的inode编号
enum file_types f_type; //文件类型
};
struct dir_entry* dir_read(struct dir* dir) {
struct dir_entry* dir_e = (struct dir_entry*) dir->dir_buf;
struct inode* dir_inode = dir->inode;
uint32_t all_blocks[140] = {0}, block_cnt = 12;
uint32_t block_idx = 0, dir_entry_idx = 0;
while (block_idx < 12) {
all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
block_idx++;
}
if(dir_inode->i_sectors[12] != 0) {
ide_read(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
block_cnt = 140;
}
block_idx = 0;
uint32_t cur_dir_entry_pos = 0;
uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
uint32_t dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size;
while (dir->dir_pos < dir_inode->i_size) {
if(dir->dir_pos >= dir_inode->i_size) {
return NULL;
}
if(all_blocks[block_idx] == 0) {
block_idx++;
continue;
}
memset(dir_e, 0, SECTOR_SIZE);
ide_read(cur_part->my_disk, all_blocks[block_idx], dir_e, 1);
dir_entry_idx = 0;
while(dir_entry_idx < dir_entrys_per_sec) {
if((dir_e + dir_entry_idx)->f_type) {
if(cur_dir_entry_pos < dir->dir_pos) {
cur_dir_entry_pos += dir_entry_size;
dir_entry_idx++;
continue;
}
ASSERT(cur_dir_entry_pos == dir->dir_pos);
dir->dir_pos += dir_entry_size;
return dir_e + dir_entry_idx;
}
dir_entry_idx++;
}
block_idx++;
}
return NULL;
}
void sys_rewinddir(struct dir* dir) {
ASSERT(dir != NULL);
dir->dir_pos = 0;
}
struct dir_entry* sys_readdir(struct dir* dir) {
ASSERT(dir != NULL);
return dir_read(dir);
}
sys_rewinddir就是把目录已读字节数置0,太简单了就不单梳理流程了
实验结果
#include "print.h"
#include "init.h"
#include "debug.h"
#include "memory.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"
#include "fs.h"
#include "dir.h"
#include "inode.h"
int main(void)
{
put_str("I am kernel\n");
init_all();
intr_enable();
struct dir* p_dir = sys_opendir("/dir1/subdir1");
if(p_dir) {
printf("/dir1/subdir1 open done\ncontent:\n");
char* type = NULL;
struct dir_entry* dir_e = NULL;
while ((dir_e = sys_readdir(p_dir))) {
if(dir_e->f_type == FT_REGULAR) {
type = "regular";
} else {
type = "directory";
}
printf(" %s %s\n", type, dir_e->filename);
}
if(sys_closedir(p_dir) == 0) {
printf("/dir1/subdir1 close done!\n");
} else {
printf("/dir1/subdir1 close fail!\n");
}
} else {
printf("/dir1/subdir1 open fail!\n");
}
while(1);
return 0;
}