操作系统真象还原[14章/九]-文件删除

        文件删除这一块在书中比较长,代码也比较多。不过其实际的原理还是比较简单的,文件在硬盘上就对应着一个inode结构以及为该inode结构所分配的数据块。所以,删除一个文件分以下两步操作进行即可:

        (一)、在父目录中删除该文件的目录项

        (二)、回收该文件在硬盘中占用的数据块(通过硬盘分区中的block_bitmap位图)以及inode结构(通过硬盘分区中的inode_bitmap位图)

        第一步对应的是书中第14.10.2节,梳理一下delete_dir_entry函数的流程:

        (1)、收集父目录对应的所有磁盘数据块

        (2)、从头开始逐个遍历数据块,查找是否有inode_no对应的目录项存在

        (3)、判断扇区中是否只有一个目录项,若只有一个目录项则需要对该数据块整体进行回收,否则将当前目录项清空后写入原数据块即可。

                (3.1)、进一步判断该块是直接块(block_idx <12)还是间接块(block_idx>=12)

                        (3.1.1)、直接块清空pdir->inode->i_sectors对应下标即可,最后要将inode同步回硬盘

                        (3.2.2)、间接块需要进一步判断间接表是否只有当前一个间接块

                                (3.2.2.1)、若只有当前一个间接块还需要回收索引表

                                (3.2.2.2)、若还有其他间接块仅在索引表中擦除当前这个间接块地址即可

        (4)、将父目录inode_size减去一个dir_entry大小,并将内存中inode信息同步回硬盘

        下面上代码(fs/dir.c中)

/* 把分区part目录pdir中编号为inode_no的目录项删除 */
bool delete_dir_entry(struct partition* part, struct dir* pdir, uint32_t inode_no, void* io_buf) {
    /* 从pdir中拿到inode */
    struct inode* dir_inode = pdir->inode;
    if(dir_inode == NULL) {
        printk("delete_dir_entry: parent dir's inode is empty, delete fail\n");
        return false;
    }
    /* 申请all_blocks */
    uint32_t* all_blocks = sys_malloc(48 + SECTOR_SIZE);
    if(all_blocks == NULL) {
        printk("delete_dir_entry: sys_malloc for all_blocks fail\n");
    }
    /* 填充all_blocks */
    uint32_t block_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(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
    }
    /* 逐个数据块读入数据,遍历每个dir_entry,对比inode_no */
    struct dir_entry* dir_e = (struct dir_entry*) io_buf;
    uint32_t dir_entry_size = part->sb->dir_entry_size;
    uint32_t dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size;
    uint32_t data_start_lba = part->sb->data_start_lba;
    block_idx = 0;
    while(block_idx < 140) {
        if(all_blocks[block_idx] != 0) {
            /* 在某个数据块中是否找到inode */
            memset(io_buf, 0, SECTOR_SIZE);
            ide_read(part->my_disk, all_blocks[block_idx], io_buf, 1);
            uint32_t dir_entry_idx = 0;
            uint32_t dir_entry_count = 0;
            uint32_t dir_found_idx = -1;
            bool is_first_block = false;
            bool found = false;
            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_first_block = true;
                    } else if (strcmp(".", (dir_e + dir_entry_idx)->filename) && strcmp("..", (dir_e + dir_entry_idx)->filename)) {
                        dir_entry_count++;
                        if((dir_e + dir_entry_idx)->i_no == inode_no) {
                            found = true;
                            dir_found_idx = dir_entry_idx;
                        }
                    }
                }
                dir_entry_idx++;
            }

            if(!found) {
                block_idx++;
                continue;
            }

            /* 找到对应inode_no的目录项则需要判断当前数据块是否只有一个目录项 */
            /* 只有一个目录需要删除整个数据块 */
            if(!is_first_block && dir_entry_count == 1) {
                bitmap_set(&part->block_bitmap, all_blocks[block_idx] - data_start_lba, 0);
                bitmap_sync(part, all_blocks[block_idx] - data_start_lba, BLOCK_BITMAP);
                all_blocks[block_idx] = 0;
                if(block_idx < 12) {
                    /* 如果是直接块内只有一个目录项回收直接块即可 */
                    dir_inode->i_sectors[block_idx] = 0;
                } else {
                    /* 如果是间接块内只有一个目录项回收间接块的同时,还要看间接块索引表中是否已经没有间接块,此时还需要回收间接块表 */
                    uint32_t indirect_block_count = 0;
                    int i;
                    for(i = 12; i < 140; i++) {
                        if(all_blocks[i] != 0) {
                            indirect_block_count++;
                        }
                    }
                    if(indirect_block_count == 0) {
                        bitmap_set(&part->block_bitmap, pdir->inode->i_sectors[12] - data_start_lba, 0);
                        bitmap_sync(part, pdir->inode->i_sectors[12] - data_start_lba, BLOCK_BITMAP);
                        pdir->inode->i_sectors[12] = 0;
                    } else {
                        ide_write(part->my_disk, pdir->inode->i_sectors[12], all_blocks + 12, 1);
                    }
                }
            } else {
                /* 不止一个目录项存在,不用删除数据块,把目录项删除写回磁盘即可 */
                memset(dir_e + dir_found_idx, 0, sizeof(struct dir_entry));
                ide_write(part->my_disk, all_blocks[block_idx], io_buf, 1);
            }
            ASSERT(pdir->inode->i_size >= dir_entry_size);
            pdir->inode->i_size -= dir_entry_size;
            memset(io_buf, 0, SECTOR_SIZE * 2);
            inode_sync(part, dir_inode, io_buf);
            sys_free(all_blocks);
            return true;
        }
        block_idx++;
    }
    sys_free(all_blocks);
    return false;
}

        复杂的说完了,下面说简单的,第二步回收inode数据块和inode本身,这一功能通过函数inode_release实现,梳理后的主要流程如下:

        (1)、收集inode对应的所有数据块lba地址存入all_blocks中

        (2)、将所有块回收,若块地址存在(!=0)则在block_bitmap中将对应位清0并写入硬盘即可

        (3)、将inode回收,即将inode_bitmap中对应位清0后写入硬盘即可

        (4)、将inode数组中对应的inode结构所有字节清0(这一步可有可无,因为将来的inode结构会覆盖旧数据,不清除没任何影响,这里清除通过inode_delete函数实现)

        下面上代码(fs/inode.c中):

/* 将硬盘分区上的inode清空 */
void inode_delete(struct partition* part, uint32_t inode_no, void* io_buf) {
    /* 定位inode */
    struct inode_position inode_pos;
    inode_locate(part, inode_no, &inode_pos);

    /* 读入inode所在扇区到buf */
    if(inode_pos.two_sec) {
        ide_read(part->my_disk, inode_pos.sec_lba, io_buf, 2);
        /* 把buf中的inode清空,写回inode数组到磁盘 */
        memset(io_buf + inode_pos.off_size, 0, sizeof(struct inode));
        ide_write(part->my_disk, inode_pos.sec_lba, io_buf, 2);
    } else {
        ide_read(part->my_disk, inode_pos.sec_lba, io_buf, 1);
        memset(io_buf + inode_pos.off_size, 0, sizeof(struct inode));
        ide_write(part->my_disk, inode_pos.sec_lba, io_buf, 1);
    }
}

/* 回收inode的数据块和inode本身 */
void inode_release(struct partition* part, uint32_t inode_no) {

    /* 打开inode */
    struct inode* inode_to_del = inode_open(part, inode_no);

    /* 分配all_blocks */
    uint32_t* all_blocks = (uint32_t*) sys_malloc(48 + SECTOR_SIZE);

    /* 读入所有数据块lba地址 */
    uint32_t block_idx = 0;
    while(block_idx < 12) {
        all_blocks[block_idx] = inode_to_del->i_sectors[block_idx];
        block_idx++;
    }
    if(inode_to_del->i_sectors[12] != 0) {
        ide_read(part, inode_to_del->i_sectors[12], all_blocks + 12, 1);
    }

    /* 逐个数据块在分区的inode位图中收回 */
    block_idx = 0;
    uint32_t data_start_lba = part->sb->data_start_lba;
    while (block_idx < 140) {
        if(all_blocks[block_idx] != 0) {
            bitmap_set(&part->block_bitmap, all_blocks[block_idx] - data_start_lba, 0);
            bitmap_sync(part, all_blocks[block_idx] - data_start_lba, BLOCK_BITMAP);
        }
        block_idx++;
    }

    /* 在inode位图中收回编号为inode_no的inode */
    bitmap_set(&part->inode_bitmap, inode_no, 0);
    bitmap_sync(part, inode_no, INODE_BITMAP);
    void* io_buf = sys_malloc(SECTOR_SIZE * 2);
    inode_delete(part, inode_no, io_buf);    //这里可有可无
    sys_free(io_buf);
    sys_free(all_blocks);
    inode_close(inode_to_del);
}

        最后在fs/fs.c中提供sys_unlink函数方便调用,该函数实际就是个包装函数,进行一些检查后调用delete_dir_entry以及inode_release来删除文件。

        需要进行的前置检查包括两项:

        (1)、检查待删除的文件是否存在

        (2)、检查待删除的文件是否正在被使用(即在已打开文件列表中有file结构的inode等于待删除文件的inode)

        下面上代码(fs/fs.c中)

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

    /* 先检查待删除的文件是否存在 */
    struct path_search_record search_record;
    memset(&search_record, 0, sizeof(struct path_search_record));
    int inode_no = search_file(pathname, &search_record);
    ASSERT(inode_no != 0);
    if(inode_no == -1) {
        printk("file %s not found!\n", pathname);
        dir_close(search_record.parent_dir);
        return -1;
    }
    if(search_record.file_type == FT_UNKNOWN) {
        printk("can't delete a directory with unlink(), use rmdir() to instead\n");
        dir_close(search_record.parent_dir);
        return -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(search_record.parent_dir);
        printk("file %s is in use, not allow to delete!\n", pathname);
        return -1;
    }
    ASSERT(file_idx == MAX_FILE_OPEN);

    /* 为delete_dir_entry申请缓冲区 */
    void* io_buf = sys_malloc(SECTOR_SIZE + SECTOR_SIZE);
    if(io_buf == NULL) {
        dir_close(search_record.parent_dir);
        printk("sys_unlink: malloc for io_buf failed\n");
        return -1;
    }

    struct dir* parent_dir = search_record.parent_dir;
    delete_dir_entry(cur_part, parent_dir, inode_no, io_buf);
    inode_release(cur_part, inode_no);
    sys_free(io_buf);
    dir_close(search_record.parent_dir);
    return 0;
}

        实验结果

#include "print.h"
#include "init.h"
#include "debug.h"
#include "memory.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"
#include "fs.h"

int main(void) {
    put_str("I am kernel\n");
    init_all();
    intr_enable();
	
    printf("/file1 delete %s!\n", sys_unlink("/file1") == 0 ? "done" : "fail");

    while (1);
	return 0;
}	

         一切正常,跟书上运行结果一模一样,下一节,目录开始!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值