实验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. 打开inodestruct 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); // 设置位图比特为0bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP); // 同步块位图}block_idx++;}// 3 回收该inodebitmap_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 关闭inodeinode_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_sectoruint8_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就不再是NULLASSERT(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 释放这个inodeinode_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;
}