已完成实验
简介
实验 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;
}