手写操作系统(五十六)-实现目录相关功能

代码、内容参考来自于包括《操作系统真象还原》、《一个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:一个操作系统的实现

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇