代码、内容参考来自于包括《操作系统真象还原》、《一个64位操作系统的设计与实现》以及《ORANGE’S:一个操作系统的实现》。
接下来就是实现目录相关操作了
1.创建目录
Linux用mkdir函数创建目录。
mkdir的原型: “int mkdir(const char *pathname, mode_t mode)”,其中,pathname是待创建的目录名,mode 是所创建目录的权限,成功返回 0,失败返回-1。
创建目录所涉及的工作:
- 确认待创建的新目录在文件系统上不存在。
- 为新目录创建 inode。
- 为新目录分配1个块存储该目录中的目录项。
- 在新目录中创建两个目录项”.”和”..”,这是每个目录都必须存在的两个目录项。
- 在新目录的父目录中添加新目录的目录项。
- 将以上资源的变更同步到硬盘。
继续修改fs/fs.c函数
添加sys_mkdir函数
/* 创建目录pathname,成功返回0,失败返回-1 */
int32_t sys_mkdir(const char* pathname) {
uint8_t rollback_step = 0; // 用于操作失败时回滚各资源状态
void* io_buf = sys_malloc(SECTOR_SIZE * 2);
if (io_buf == NULL) {
printk("sys_mkdir: sys_malloc for io_buf failed\n");
return -1;
}
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = -1;
inode_no = search_file(pathname, &searched_record);
if (inode_no != -1) { // 如果找到了同名目录或文件,失败返回
printk("sys_mkdir: file or directory %s exist!\n", pathname);
rollback_step = 1;
goto rollback;
} else { // 若未找到,也要判断是在最终目录没找到还是某个中间目录不存在
uint32_t pathname_depth = path_depth_cnt((char*)pathname);
uint32_t path_searched_depth = path_depth_cnt(searched_record.searched_path);
/* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
if (pathname_depth != path_searched_depth) { // 说明并没有访问到全部的路径,某个中间目录是不存在的
printk("sys_mkdir: can`t access %s, subpath %s is`t exist\n", pathname, searched_record.searched_path);
rollback_step = 1;
goto rollback;
}
}
struct dir* parent_dir = searched_record.parent_dir;
/* 目录名称后可能会有字符'/',所以最好直接用searched_record.searched_path,无'/' */
char* dirname = strrchr(searched_record.searched_path, '/') + 1;
inode_no = inode_bitmap_alloc(cur_part);
if (inode_no == -1) {
printk("sys_mkdir: allocate inode failed\n");
rollback_step = 1;
goto rollback;
}
struct inode new_dir_inode;
inode_init(inode_no, &new_dir_inode); // 初始化i结点
uint32_t block_bitmap_idx = 0; // 用来记录block对应于block_bitmap中的索引
int32_t block_lba = -1;
/* 为目录分配一个块,用来写入目录.和.. */
block_lba = block_bitmap_alloc(cur_part);
if (block_lba == -1) {
printk("sys_mkdir: block_bitmap_alloc for create directory failed\n");
rollback_step = 2;
goto rollback;
}
new_dir_inode.i_sectors[0] = block_lba;
/* 每分配一个块就将位图同步到硬盘 */
block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
ASSERT(block_bitmap_idx != 0);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
/* 将当前目录的目录项'.'和'..'写入目录 */
memset(io_buf, 0, SECTOR_SIZE * 2); // 清空io_buf
struct dir_entry* p_de = (struct dir_entry*)io_buf;
/* 初始化当前目录"." */
memcpy(p_de->filename, ".", 1);
p_de->i_no = inode_no ;
p_de->f_type = FT_DIRECTORY;
p_de++;
/* 初始化当前目录".." */
memcpy(p_de->filename, "..", 2);
p_de->i_no = parent_dir->inode->i_no;
p_de->f_type = FT_DIRECTORY;
ide_write(cur_part->my_disk, new_dir_inode.i_sectors[0], io_buf, 1);
new_dir_inode.i_size = 2 * cur_part->sb->dir_entry_size;
/* 在父目录中添加自己的目录项 */
struct dir_entry new_dir_entry;
memset(&new_dir_entry, 0, sizeof(struct dir_entry));
create_dir_entry(dirname, inode_no, FT_DIRECTORY, &new_dir_entry);
memset(io_buf, 0, SECTOR_SIZE * 2); // 清空io_buf
if (!sync_dir_entry(parent_dir, &new_dir_entry, io_buf)) { // sync_dir_entry中将block_bitmap通过bitmap_sync同步到硬盘
printk("sys_mkdir: sync_dir_entry to disk failed!\n");
rollback_step = 2;
goto rollback;
}
/* 父目录的inode同步到硬盘 */
memset(io_buf, 0, SECTOR_SIZE * 2);
inode_sync(cur_part, parent_dir->inode, io_buf);
/* 将新创建目录的inode同步到硬盘 */
memset(io_buf, 0, SECTOR_SIZE * 2);
inode_sync(cur_part, &new_dir_inode, io_buf);
/* 将inode位图同步到硬盘 */
bitmap_sync(cur_part, inode_no, INODE_BITMAP);
sys_free(io_buf);
/* 关闭所创建目录的父目录 */
dir_close(searched_record.parent_dir);
return 0;
/*创建文件或目录需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
rollback: // 因为某步骤操作失败而回滚
switch (rollback_step) {
case 2:
bitmap_set(&cur_part->inode_bitmap, inode_no, 0); // 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复
case 1:
/* 关闭所创建目录的父目录 */
dir_close(searched_record.parent_dir);
break;
}
sys_free(io_buf);
return -1;
}sys_mkdir函数支持1个参数,路径名pathname,功能是创建目录pathname,成功返回0,失败返回-1。 创建目录也是由多个步骤完成的,因此创建目录的工作是个事务,具有原子性,即要么所有步骤都完成,要么一个都不做,若其中某个步骤失败,必须将之前完成的操作回滚到之前的状态。 在函数开头定义的rollback_step用于记录回滚的步骤。 接下来申请了2扇区大小的缓冲区给io_buf。
在创建目录之前要判断文件系统上是否已经有了同名的文件,无论是目录文件,还是普通文件,同一 目录下不允许存在同名的文件,调用search_file检索pathname,如果找到同名文件pathname,search_file会返回其inode编号,否则会返回-1。
if (inode_no != -1)判断search_file的返回值,若不等于-1表示同 名文件已存在,输出提示后将rollback_step置为1,跳转到rollback处执行回滚。 rollback是回滚操作,有case 2和case 1, case1是关闭待创建目录的父目录,case2在此基础上多了恢 复inode位图的操作。
如果未找到同名文件,进入else代码,这也不能贸然创建目录,因为待创建的目录有可能并不是在最后一级目录中不存在,很可能是某个中间路径就不存在,比如创建目录”/a/b”,有可能a目录就不存在。对于中间目录不存在的情况,我们就像Linux一样,给出提示后拒绝创建目录, 这里是用pathname的路径深度pathname_depth和已搜索过的路径深度path_searched_depth比较,若不相等(确切地说,若pathname_depth大于path_searched_depth)则表示某个中间目录不存在,该中间目录是 searched_record.searched_path。
struct dir* parent_dir = searched_record.parent_dir用指针parent_dir指向被创建目录所在的父目录,char* dirname = strrchr(searched_record.searched_path, ‘/’) + 1获取pathname的最后一级目录名,也就是最终创建的目录名,其中strrchr函数是在searched_record.searched_path中从后往前获取字符”/”第一次出现的地址(注意,是地址,不是字符下标),如创建目录”/a/b”,若按照char* dirname = strrchr(searched_record.searched_path, ‘/’) + 1的处理,目录名dirname指向b。 接着调用inode_bitmap_alloc在inode位图中分配inode,如果返回值为-1,将rollback_step置为1,跳转到rollback处执行回滚。
接下来为目录新建1个new_dir_inode并初始化inode_init(inode_no, &new_dir_inode)。 接下来为目录分配1个块并将块地址写入目录inode的i_sectors[0]中,此块用来存储目录中的目录项。 然后将块位图同步到硬盘。
然后在io_buf中新建目录项”.”和”..”并同步到硬盘,这样目录pathname中便有了两个目录项。 new_dir_inode.i_size = 2 * cur_part->sb->dir_entry_size初始化目录的尺寸,即new_dir_inode.i_size等于2个目录项大小。 接下来要在父目录中添加自己的目录项,即在parent_dir中添加dirname的目录项。 其中 create_dir_entry 只是初始化目录项的内容到 new_dir_entry 中,下面调用的函数”sync_dir_entry(parent_dir, &new_dir_entry, io_buf”才是真正把dirname的目录项new_dir_entry写入父目录 parent_dir 中。
最后是一些元信息持久化工作,同步父目录 inode 和新目录 inode 到硬盘,同步 inode 位图到硬盘。
在sys_free(io_buf)以后开始释放缓冲区io_buf,关闭父目录searched_record.parent_dir,随后返回0。
对应fs.h加入声明
int32_t sys_mkdir(const char* pathname);
修改main.c目录测试一下:
int main(void) {
put_str("I am kernel\n");
init_all();
process_execute(u_prog_a, "u_prog_a");
process_execute(u_prog_b, "u_prog_b");
thread_start("k_thread_a", 31, k_thread_a, "I am thread_a");
thread_start("k_thread_b", 31, k_thread_b, "I am thread_b");
printf("/dir1/subdir1 create %s!\n", sys_mkdir("/dir1/subdir1") == 0 ? "done" : "fail");
printf("/dir1 create %s!\n", sys_mkdir("/dir1") == 0 ? "done" : "fail");
printf("now, /dir1/subdir1 create %s!\n", sys_mkdir("/dir1/subdir1") == 0 ? "done" : "fail");
int fd = sys_open("/dir1/subdir1/file2", O_CREAT|O_RDWR);
if (fd != -1) {
printf("/dir1/subdir1/file2 create done!\n");
sys_write(fd, "Catch me if you can!\n", 21);
sys_lseek(fd, 0, SEEK_SET);
char buf[32] = {0};
sys_read(fd, buf, 21);
printf("/dir1/subdir1/file2 says:\n%s", buf);
sys_close(fd);
}
while(1);
return 0;
}想创建目录”/dirl/subdir1″,目前没有目录”/dir1″,因此直接创建”/dir1/subdir1″会失败。

2.遍历目录
创建目录成功后,接下来就是
遍历目录就是读取目录中所有的目录项,在遍历之前必须要先把目录打开,之后还需要把目录关闭。Linux中分别用函数 opendir 和 closedir完成目录打开和关闭
同样修改fs/fs.c,添加sys_opendir和sys_closedir
/* 目录打开成功后返回目录指针,失败返回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 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 if (searched_record.file_type == FT_DIRECTORY) {
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;
}sys_opendir接受一个参数name,功能是打开目录name,成功后返回目录指针,失败返回NULL。
根目录的形式有:”/”、”/.”、”/..”,当然按理说“/./..”、”/../..”等都能够表示根目录,不过简单点,暂不考虑它们。
if (name[0] == ‘/’ && (name[1] == 0 || name[0] == ‘.’))判断打开的目录是否是根目录,如果是就直接把根目录地址&root_dir返回,这里是简单处理”/.”和”/..”的情况。
打开目录之前要确认目录存在,否则失败返回,通过search_file在文件系统上查找目录name。用变量ret存储目录指针,默认为NULL。若if (inode_no == -1)找不到name,提示路径不存在。若找到了判断如果找到的是普通文件,就输出提示,否则若找到的确实是目录,就调用dir_open将name打开,目录指针存入ret中,随后在dir_close(searched_record.parent_dir)调用dir_close关闭name的父目录,最后返回ret指针。
接下来是sys_closedir函数,接受一个参数目录指针dir,功能是关闭目录dir,成功返回0,失败返回-1。关闭目录的具体工作是调用dir_close(dir)完成的。
对应fs.h
struct dir* sys_opendir(const char* pathname); int32_t sys_closedir(struct dir* dir);
修改main.c文件测试一下:
int main(void) {
put_str("I am kernel\n");
init_all();
process_execute(u_prog_a, "u_prog_a");
process_execute(u_prog_b, "u_prog_b");
thread_start("k_thread_a", 31, k_thread_a, "I am thread_a");
thread_start("k_thread_b", 31, k_thread_b, "I am thread_b");
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;
}
执行结果如下:

要遍历目录,在打开目录后就是要读取目录了
修改fs/dir.c
/* 读取目录,成功返回1个目录项,失败返回NULL */
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; // 1扇区内可容纳的目录项个数
/* 因为此目录内可能删除了某些文件或子目录,所以要遍历所有块 */
while (block_idx < block_cnt) {
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_entrys_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,功能是读取目录dir,成功返回1个目录项,失败返回NULL。 在目录结构中有个512字节的缓冲区dir_buf,作用是存储目录项。
首先在程序开头声明了目录项指针dir_e,使其指向目录缓冲区dir_buf。 为读取目录,必然要知道目录inode所有的块地址,然后将目录所有块地址收集到 all_blocks中,并使块索引 block_idx 恢复为0。
接下来遍历该目录所有的块,然后在每个块中遍历目录项。dir_read也是只返回1个目录项,dir_pos是目录的游标,作用同文件结构中的fd_pos一样,用于记录下一个读写对象的地址,dir_pos用于指向目录中某个目录项的地址。 我们每返回一个目录项后就使dir_pos的值增加一个目录项大小,这样就有可能知道该返回哪个目录项了。 由于目录中的目录项是单独的个体,它们可以被单独删除,这样就会使块中存在空洞,也就是目录中的目录项不连续存储,将它们读入到内存缓冲区中,这些空洞也存在,我们并没有在内存中整理目录项使其连续,因为这样会导致效率更加低下,也没必要。 所以仅凭dir_pos还不能满足要求,我们得知道哪些目录项已经被读取过了,在uint32_t cur_dir_entry_pos = 0定义变量cur_dir_entry_pos来表示当前目录项的地址,每找到一个目录项就将cur_dir_entry_pos加上一个目录项大小,直到cur_dir_entry_pos的值等于dir_pos,这才算找到该返回的目录项。
按着这种思路,在while (block_idx < block_cnt)开始寻找目录项,遍历所有块。 所有目录项必然是在文件大小之内,因此在if (dir->dir_pos >= dir_inode->i_size)对其判断,若如果dir_pos大于等于文件尺寸,这说明已经遍历了所有的目录项,直接返回NULL。dir_pos 在执行 sys_opendir 时就被置为 0了。
接着是将扇区读入到 dir_e中,遍历所有目录项,if ((dir_e + dir_entry_idx)->f_type) ,只要目录项有效,即目录项的f_type不等于FT_UNKNOWN (FT_UNKNOWN值为0),就用当前目录项地址cur_dir_entry_pos和dir->dir_pos比较,若cur_dir_entry_pos小于dir->dir_pos,这说明都是之前返回过的目录项,因此将cur_dir_entry_pos加上目录项大小,并使目录项索引dir_entry_idx加1后,跳过当前目录项,直到cur_dir_entry_pos等于dir->dir_pos,这才找到了该返回的目录项。
随后在dir->dir_pos += dir_entry_size把 dir->dir_pos 加上一个目录项大小,使其指向下一个待返回的目录项。
最后return dir_e + dir_entry_idx返回目录项地址dir_e+dir_entry_idx。
对应dir.h加入声明
struct dir_entry* dir_read(struct dir* dir);
在Linux中读取目录的函数是readdir,其原型是:”struct dirent*readdir(DIR *dirp)”。
遍历目录的操作中,经常会用到目录回绕的功能,也就是使目录的游标dir_pos 回到 0,它与 lseek 功能类似,只不过是针对目录的,避免了将目录先关闭再重新打开的繁琐。 在Linux中目录回绕是用函数rewinddir实现的,其原型是: “void rewinddir(DIR *dirp)”。
所以先实现这两个系统调用的内核部分sys_readdir和sys_rewinddir。
修改fs/fs.c
/* 读取目录dir的1个目录项,成功后返回其目录项地址,到目录尾时或出错时返回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;
}实际就是简单调用而已
对应fs/fs.h添加声明
struct dir_entry* sys_readdir(struct dir* dir); void sys_rewinddir(struct dir* dir);
修改main.c文件
int main(void) {
put_str("I am kernel\n");
init_all();
/******** 测试代码 ********/
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;
}执行结果

3.删除目录
接下来就是删除目录了
修改fs/dir.c
添加两个函数
/* 判断目录是否为空 */
bool dir_is_empty(struct dir* dir) {
struct inode* dir_inode = dir->inode;
/* 若目录下只有.和..这两个目录项则目录为空 */
return (dir_inode->i_size == cur_part->sb->dir_entry_size * 2);
}
/* 在父目录parent_dir中删除child_dir */
int32_t dir_remove(struct dir* parent_dir, struct dir* child_dir) {
struct inode* child_dir_inode = child_dir->inode;
/* 空目录只在inode->i_sectors[0]中有扇区,其它扇区都应该为空 */
int32_t block_idx = 1;
while (block_idx < 13) {
ASSERT(child_dir_inode->i_sectors[block_idx] == 0);
block_idx++;
}
void* io_buf = sys_malloc(SECTOR_SIZE * 2);
if (io_buf == NULL) {
printk("dir_remove: malloc for io_buf failed\n");
return -1;
}
/* 在父目录parent_dir中删除子目录child_dir对应的目录项 */
delete_dir_entry(cur_part, parent_dir, child_dir_inode->i_no, io_buf);
/* 回收inode中i_secotrs中所占用的扇区,并同步inode_bitmap和block_bitmap */
inode_release(cur_part, child_dir_inode->i_no);
sys_free(io_buf);
return 0;
}dir_is_empty函数接受1个参数,目录指针dir,功能是判断目录dir是否为空。 其原理很简单,任何目录中都有”.”和”..”这两个目录项,空目录中只剩下这两个目录项,因此若目录的大小等于2个目录项的大小,就表示该目录为空。 目录为空返回true,否则返回false。
函数dir_remove接受2个参数,父目录指针parent_dir和子目录指针child_dir,功能是在父目录parent_dir 中删除 child_dir。 成功删除则返回 0,否则返回-1。
代码开头通过ASSERT判断子目录只有一个块,我们要删除的child_dir肯定得是个空目录,空目录只有在其inode的i_sectors[0]中有扇区地址,因此while循环中是排查i_sectors[1~12],接下来为delete_dir_entry申请缓冲区io_buf,然后调用delete_dir_entry在父目录parent_dir中删除子目录child_dir对应的目录项,最后调用inode_release释放子目录的inode释放io_buf后,通过return返回0。
对应fs/dir.h
bool dir_is_empty(struct dir* dir); int32_t dir_remove(struct dir* parent_dir, struct dir* child_dir);
接着就是实现 sys_rmdir了
修改fs/fs.c
添加sys_rmdir函数
/* 删除空目录,成功时返回0,失败时返回-1*/
int32_t sys_rmdir(const char* pathname) {
/* 先检查待删除的文件是否存在 */
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = search_file(pathname, &searched_record);
ASSERT(inode_no != 0);
int retval = -1; // 默认返回值
if (inode_no == -1) {
printk("In %s, sub path %s not exist\n", pathname, searched_record.searched_path);
} else {
if (searched_record.file_type == FT_REGULAR) {
printk("%s is regular file!\n", pathname);
} else {
struct dir* dir = dir_open(cur_part, inode_no);
if (!dir_is_empty(dir)) { // 非空目录不可删除
printk("dir %s is not empty, it is not allowed to delete a nonempty directory!\n", pathname);
} else {
if (!dir_remove(searched_record.parent_dir, dir)) {
retval = 0;
}
}
dir_close(dir);
}
}
dir_close(searched_record.parent_dir);
return retval;
}sys_rmdir接受1个参数,待删除的目录pathname,功能是删除空目录pathname,成功时返回0,失败时返回-1,删除目录之前要确认目录在文件系统上存在,先判断目录是否存在。 int retval = -1定义了返回值retval初始化为-1,以下各步骤若失败将其作为返回值。
在执行search_file后,如果返回值inode_no为-1,这说明未找到该目录,输出提示信息,执行流跳到dir_close(searched_record.parent_dir)关闭目录searched_record.parent_dir。
如果inode_no不等于-1,这说明找到了pathname,接下来在else代码里判断pathname是目录,还是普通文件。如果searched_record. file_type的值是FT_REGULAR,这说明 pathname是同名的普通文件,输出提示后跳到关闭目录。 否则pathname是目录文件,这时候我们将pathname 打开,然后调用dir_is_empty判断其是否为空,如果非空就输出提示,不允许删除非空目录。 如果pathname 为空,就执行dir_remove将其在硬盘上删除,如果删除成功,就将retval置为0,流程跳到dir_close(dir),执 行 dir_close 关闭目录在内存中的资源,最后关闭父目录。
对应fs/fs.h加入声明
int32_t sys_rmdir(const char* pathname);
修改main.c测试
int main(void) {
put_str("I am kernel\n");
init_all();
/******** 测试代码 ********/
printf("/dir1 content before delete /dir1/subdir1:\n");
struct dir* dir = sys_opendir("/dir1/");
char* type = NULL;
struct dir_entry* dir_e = NULL;
while((dir_e = sys_readdir(dir))) {
if (dir_e->f_type == FT_REGULAR) {
type = "regular";
} else {
type = "directory";
}
printf(" %s %s\n", type, dir_e->filename);
}
printf("try to delete nonempty directory /dir1/subdir1\n");
if (sys_rmdir("/dir1/subdir1") == -1) {
printf("sys_rmdir: /dir1/subdir1 delete fail!\n");
}
printf("try to delete /dir1/subdir1/file2\n");
if (sys_rmdir("/dir1/subdir1/file2") == -1) {
printf("sys_rmdir: /dir1/subdir1/file2 delete fail!\n");
}
if (sys_unlink("/dir1/subdir1/file2") == 0 ) {
printf("sys_unlink: /dir1/subdir1/file2 delete done\n");
}
printf("try to delete directory /dir1/subdir1 again\n");
if (sys_rmdir("/dir1/subdir1") == 0) {
printf("/dir1/subdir1 delete done!\n");
}
printf("/dir1 content after delete /dir1/subdir1:\n");
sys_rewinddir(dir);
while((dir_e = sys_readdir(dir))) {
if (dir_e->f_type == FT_REGULAR) {
type = "regular";
} else {
type = "directory";
}
printf(" %s %s\n", type, dir_e->filename);
}
/******** 测试代码 ********/
while(1);
return 0;
}
执行结果如下:

4.参考
郑钢著操作系统真象还原
田宇著一个64位操作系统的设计与实现
丁渊著ORANGE’S:一个操作系统的实现


