实验28.文件删除 sys_unlink

已完成实验

《操作系统真相还原》部分实验记录

简介

实验 28. 文件删除 sys_unlink

总结

  • 删除文件: 1 删除目录项,2 释放 inode 的数据块和 inode 结构体

主要代码

  • 实现 inode_release 和 inode_delete (inode.c)

  • 实现 delete_dir_entry (dir.c)

  • 实现 sys_unlink (fs.c)

inode.c

/// @brief 释放inode
/// 在硬盘inode位图中标志该inode为空闲,并在块位图中标记他的所有块为空闲
/// @param part 分区
/// @param inode_no inode号
void inode_release(struct partition* part, uint32_t inode_no) {
    // 1. 打开inode
    struct inode* inode_to_del = inode_open(part, inode_no);
    ASSERT(inode_to_del->i_no == inode_no);

    // 2.回收inode占用的所有块
    uint32_t all_blocks[140] = {0};  // 12个直接块+128个间接块

    // 2.1 先复制前12个直接块
    uint8_t block_idx = 0;
    while (block_idx < 12) {
        all_blocks[block_idx] = inode_to_del->i_sectors[block_idx];
        block_idx++;
    }

    // 2.2 再复制128个间接块
    uint32_t block_bitmap_idx;
    if (inode_to_del->i_sectors[12] != 0) {
        // 2.2.1 从硬盘间接块
        ide_read(part->my_disk, inode_to_del->i_sectors[12], all_blocks + 12, 1);

        // 2.2.2 回收一级间接块表占用的扇区
        block_bitmap_idx = inode_to_del->i_sectors[12] - part->sb->data_start_lba;  // 计算块偏移
        ASSERT(block_bitmap_idx > 0);
        bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);   // 设置比特为空
        bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);  // 同步内存中的块位图到硬盘
    }

    // 2.3 inode所有的块地址已经收集到all_blocks中,下面逐个回收
    block_idx = 0;
    while (block_idx < 140) {
        if (all_blocks[block_idx] != 0) {
            block_bitmap_idx = 0;
            block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba;  // 计算块偏移
            ASSERT(block_bitmap_idx > 0);
            bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);   // 设置位图比特为0
            bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);  // 同步块位图
        }
        block_idx++;
    }

    // 3 回收该inode
    bitmap_set(&part->inode_bitmap, inode_no, 0);
    bitmap_sync(cur_part, inode_no, INODE_BITMAP);

    // 以下inode_delete是调试用的
    // 把inode表中的inode结构全部归0起始没有意义
    // 因为位图已经标记为空闲了,下次用时也会重新初始化
    void* io_buf = sys_malloc(1024);
    inode_delete(part, inode_no, io_buf);
    sys_free(io_buf);

    // 4 关闭inode
    inode_close(inode_to_del);
}


/// @brief 初始化inode
/// 除了inode号全部默认值
/// @param inode_no inode号
/// @param new_inode inode指针
void inode_init(uint32_t inode_no, struct inode* new_inode) {
    new_inode->i_no = inode_no;
    new_inode->i_size = 0;
    new_inode->i_open_cnts = 0;
    new_inode->write_deny = false;

    // 初始化块索引数组i_sector
    uint8_t sec_idx = 0;
    while (sec_idx < 13) {
        // i_sectors[12]为一级间接块地址
        new_inode->i_sectors[sec_idx] = 0;
        sec_idx++;
    }
}

dir.c

/// @brief 删除目录项
/// 遍历目录pdir中的目录项,删除inode号为inode_no的目录项
/// @param part
/// @param pdir
/// @param inode_no
/// @param io_buf
/// @return 成功返回1, 失败返回0
bool delete_dir_entry(struct partition* part, struct dir* pdir, uint32_t inode_no, void* io_buf) {
    struct inode* dir_inode = pdir->inode;

    // 1.收集父目录全部块地址
    uint32_t block_idx = 0, all_blocks[140] = {0};
    while (block_idx < 12) {
        all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
        block_idx++;
    }
    if (dir_inode->i_sectors[12]) { ide_read(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1); }

    /* 目录项在存储时保证不会跨扇区 */
    uint32_t dir_entry_size = part->sb->dir_entry_size;
    uint32_t dir_entrys_per_sec = (SECTOR_SIZE / dir_entry_size);  // 每扇区最大的目录项数目
    struct dir_entry* dir_e = (struct dir_entry*)io_buf;
    struct dir_entry* dir_entry_found = NULL;
    uint8_t dir_entry_idx, dir_entry_cnt;
    bool is_dir_first_block = false;  // 目录的第1个块

    // 2 遍历所有块,寻找目录项
    block_idx = 0;
    while (block_idx < 140) {
        is_dir_first_block = false;
        if (all_blocks[block_idx] == 0) {
            block_idx++;
            continue;
        }
        dir_entry_idx = dir_entry_cnt = 0;
        memset(io_buf, 0, SECTOR_SIZE);
        // 2.1 读取扇区,获得目录项
        ide_read(part->my_disk, all_blocks[block_idx], io_buf, 1);

        // 2.2 遍历所有的目录项,统计该扇区的目录项数量及是否有待删除的目录项
        while (dir_entry_idx < dir_entrys_per_sec) {
            if ((dir_e + dir_entry_idx)->f_type != FT_UNKNOWN) {
                if (!strcmp((dir_e + dir_entry_idx)->filename, ".")) {
                    is_dir_first_block = true;
                } else if (strcmp((dir_e + dir_entry_idx)->filename, ".") &&
                           strcmp((dir_e + dir_entry_idx)->filename, "..")) {  // 是普通文件
                    dir_entry_cnt++;
                    if ((dir_e + dir_entry_idx)->i_no == inode_no) {  // 如果找到此i结点,就将其记录在dir_entry_found
                        // 确保目录中只有一个编号为inode_no的inode,找到一次后dir_entry_found就不再是NULL
                        ASSERT(dir_entry_found == NULL);
                        dir_entry_found = dir_e + dir_entry_idx;  // 找到后也继续遍历,统计总共的目录项数
                    }
                }
            }
            dir_entry_idx++;
        }

        // 2.3 若此扇区未找到该目录项,继续在下个扇区中找
        if (dir_entry_found == NULL) {
            block_idx++;
            continue;
        }

        // 2.4 在此扇区中找到目录项后,清除该目录项并判断是否回收扇区,随后退出循环直接返回
        ASSERT(dir_entry_cnt >= 1);
        // 2.4.1 除目录第1个扇区外,若该扇区上只有该目录项自己,则将整个扇区回收
        if (dir_entry_cnt == 1 && !is_dir_first_block) {
            /* a 在块位图中回收该块 */
            uint32_t block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba;
            bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
            bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

            /* b 将块地址从数组i_sectors或索引表中去掉 */
            if (block_idx < 12) {
                dir_inode->i_sectors[block_idx] = 0;
            } else {  // 在一级间接索引表中擦除该间接块地址
                /*先判断一级间接索引表中间接块的数量,如果仅有这1个间接块,连同间接索引表所在的块一同回收 */
                uint32_t indirect_blocks = 0;
                uint32_t indirect_block_idx = 12;
                while (indirect_block_idx < 140) {
                    if (all_blocks[indirect_block_idx] != 0) { indirect_blocks++; }
                }
                ASSERT(indirect_blocks >= 1);  // 包括当前间接块

                if (indirect_blocks > 1) {  // 间接索引表中还包括其它间接块,仅在索引表中擦除当前这个间接块地址
                    all_blocks[block_idx] = 0;
                    ide_write(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
                } else {  // 间接索引表中就当前这1个间接块,直接把间接索引表所在的块回收,然后擦除间接索引表块地址
                    /* 回收间接索引表所在的块 */
                    block_bitmap_idx = dir_inode->i_sectors[12] - part->sb->data_start_lba;
                    bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
                    bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

                    /* 将间接索引表地址清0 */
                    dir_inode->i_sectors[12] = 0;
                }
            }
        } else {  // 仅将该目录项清空
            memset(dir_entry_found, 0, dir_entry_size);
            ide_write(part->my_disk, all_blocks[block_idx], io_buf, 1);
        }

        //  更新i结点信息并同步到硬盘
        ASSERT(dir_inode->i_size >= dir_entry_size);
        dir_inode->i_size -= dir_entry_size;
        memset(io_buf, 0, SECTOR_SIZE * 2);
        inode_sync(part, dir_inode, io_buf);

        return true;
    }

    return false;
}

fs.c

/// @brief 删除文件(非目录)
/// @param pathname
/// @return 成功返回0,失败返回-1
int32_t sys_unlink(const char* pathname) {
    ASSERT(strlen(pathname) < MAX_PATH_LEN);

    // 0 先检查待删除的文件是否存在
    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);
    if (inode_no == -1) {
        printk("file %s not found!\n", pathname);
        dir_close(searched_record.parent_dir);
        return -1;
    }
    if (searched_record.file_type == FT_DIRECTORY) {
        printk("can`t delete a direcotry with unlink(), use rmdir() to instead\n");
        dir_close(searched_record.parent_dir);
        return -1;
    }

    // 1 检查是否在已打开文件列表(文件表)中
    uint32_t file_idx = 0;
    while (file_idx < MAX_FILE_OPEN) {
        if (file_table[file_idx].fd_inode != NULL && (uint32_t)inode_no == file_table[file_idx].fd_inode->i_no) {
            break;
        }
        file_idx++;
    }
    if (file_idx < MAX_FILE_OPEN) {
        dir_close(searched_record.parent_dir);
        printk("file %s is in use, not allow to delete!\n", pathname);
        return -1;
    }
    ASSERT(file_idx == MAX_FILE_OPEN);


    // 3 删除这个文件
    void* io_buf = sys_malloc(SECTOR_SIZE + SECTOR_SIZE);
    if (io_buf == NULL) {
        dir_close(searched_record.parent_dir);
        printk("sys_unlink: malloc for io_buf failed\n");
        return -1;
    }

    // 3.1 删除目录项
    struct dir* parent_dir = searched_record.parent_dir;
    delete_dir_entry(cur_part, parent_dir, inode_no, io_buf);
    // 3.2 释放这个inode
    inode_release(cur_part, inode_no);

    sys_free(io_buf);
    dir_close(searched_record.parent_dir);
    return 0;  // 成功删除文件
}

main.c

int main(void) {
    put_str("I am kernel\n");

    init_all();

    process_execute(u_prog_a, "user_prog_a");
    process_execute(u_prog_b, "user_prog_b");
    thread_start("k_thread_a", 31, k_thread_a, "argA ");
    thread_start("k_thread_b", 31, k_thread_b, "argB ");

    // 1 创建文件
    uint32_t fd = sys_open("/file1", O_CREAT);
    if (fd) printf("create /file1 %s\n", fd != -1 ? "success" : "failed");
    int r = sys_close(fd);
    if (fd) printf("close /file1 %s\n", r == 0 ? "success" : "failed");

    // 2 删除文件
    int ret = sys_unlink("/file1");
    printf("delete /file1 %s\n", ret == 0 ? "success" : "failed");

    while (1);
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值