14.12 遍历目录
这是一个网站有所有小节的代码实现,同时也包含了Bochs等文件
14.12.1 打开目录和关闭目录
遍历目录就是读取目录中所有的目录项,在遍历之前必须要先把目录打开,之后还需要把目录关闭。
Linux 中分别用函数 opendir 和 closedir 完成目录打开和关闭,原型分别是:
DIR *opendir(const char *name)
和int closedir(DIR *dirp)
所以我们先实现sys_opendir
和sys_closedir
。
c
/*目录打开成功后返回目录指针,失败则返回NULL*/
struct dir* sys_opendir(const char* name){
ASSERT(strlen(name) < MAX_PATH_LEN);
/*如果是根目录'/',则直接返回&root_dir*/
if(name[0]=='/'&&(name[1]==0 || name[0]=='.')) return &root_dir;
/*先检查待打开的目录是否存在*/
struct path_search_record search_record;
memset(&search_record,0,sizeof(struct path_search_record));
int inode_no = search_file(name,&search_record);
struct dir* ret = NULL;
if(inode_no==-1){ //如果没有找到目录,提示不存在的路径
printk("In %s, sub path %s not exist\n",name,search_record.searched_path);
}else{
if(search_record.file_type == FT_REGULAR){
printk("%s is regular file!\n",name);
}else if(search_record.file_type == FT_DIRECTORY){
ret = dir_open(cur_part,inode_no);
}
}
dir_close(search_record.parent_dir);
return ret;
}
/*成功关闭目录p_dir返回0,失败返回-1*/
int32_t sys_closedir(struct dir* dir){
int32_t ret = -1;
if(dir!=NULL){
dir_close(dir);
ret = 0;
}
return ret;
}
sys_opendir
:接受一个参数 name
,功能是打开目录 name
,成功后返回目录指针,失败返回 NULL 。 原理:调用search_file
将路径转换为inode
索引,然后调用dir_open
创建目录结构并将对应的inode
载入内存。
sys_closedir
:接受一个参数dir
,功能是关闭dir
目录。
14.12.2 读取1个目录项
读取目录实际上就是读取目录中的目录项。
c
/*读取目录,成功返回1个目录项,失败返回NULL*/
struct dir_entry* dir_read(struct dir* dir){
struct dir_entry* dir_e = (struct dir_entry*)dir->dirr_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_entrs_per_sec = SECTOR_SIZE / dir_entry_size; //1扇区可容纳的目录项个数
/*在目录大小内遍历*/
while(dir->dir_pos < dir_inode->i_size){
if(dir->dir_pos >= dir_inode->i_size)
return NULL;
if(all_blocks[block_idx]==0){ //如果此块地址为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_entrs_per_sec){
if((dir_e+dir_entry_idx)->f_type){ //如果f_type不等于0,即不等于FT_UNKNOWN
/*判断是不是最新的目录项,避免返回曾经已经返回过的目录项*/
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;
}
dir_read
:接受 1 个参数,目录指针曲,功能是读取目录dir
,成功返回 1 个目录项,失败返回 NULL
。 比如该目录文件下目录项分布:a,空,b,空,c,第一次调用返回a,第二次调用返回b,第三次调用返回c...原理:目录内有个指向自己inode
的指针,该inode
内有i_sectors[ ]
,所以能在磁盘中找到该目录文件,将其读出到缓冲区然后按需设定遍历规则即可。
代码中dir_pos
实际含义是已经返回过的目录项总大小,cur_dir_entry_pos
的含义是此次调用遍历过程中,扫描过的非空目录项总大小。当扫描到的非空目录项时,此时若cur_dir_entry_pos
与dir_pos
相等,自然就可以判断出这个非空目录项应该返回。dir_pos
在执行 sys_opendir
时就被置为0了。
14.12.3 实现sys_readdir及sys_rewinddir
再Linux
中读取目录的函数是readdir
,其原型是:"struct dirent *readdir(DIR *dirp)"
。
遍历目录的操作中,经常会用到目录回绕的功能,也就是使目录的"游标" dir_pos
回到 0,它与 lseek
功能类似,只不过是针对目录的,避免了将目录先关闭再重新打开的繁琐。在 Linux 中目录回绕是用函数 rewinddir
实现的,其原型是:"void rewinddir(DIR *dirp)"
我们也按照此形式实现。
c
/*读取目录dir的第一个目录项,成功后返回其目录项地址,到目录尾时或出错时返回NULL*/
struct dir_entry* sys_readdir(struct dir* dir){
ASSERT(dir!=NULL);
return dir_read(dir);
}
/*把目录dir的指针dir_pos置0*/
void sys_rewinddir(struct dir* dir){
dir->dir_pos = 0;
}
sys_readdir
,就是dir_read
的封装
sys_rewinddir
将目录的dir_pos
置为0