上一次介绍了每个分区最终的结构图,这张结构图在本篇博客中至关重要,我们要围绕这张图来创建数据结构,并在内存中创建出各分区的各个结构初始的样子,将内存中初始化好的这些数据结构写入到磁盘当中去,这样每个分区就都按上图的样子被初始化好了。说到底,本篇要做的事情就是对照上图的各个结构在内存中创建好然后写入到磁盘中,这样就完成了文件系统的创建。
一、创建超级块、i节点、目录项数据结构
超级块是各个结构的元信息,其中主要记录了各结构的起始lba地址以及所占用的扇区数,具体信息如下:
struct super_block {
uint32_t magic; //标识文件系统类型
uint32_t sec_cnt; //本分区总扇区数
uint32_t inode_cnt; //本分区中inode数量
uint32_t part_lba_base; //本分区起始lba地址
uint32_t block_bitmap_lba; //块位图起始扇区lba地址
uint32_t block_bitmap_sects; //块位图占用的扇区数量
uint32_t inode_bitmap_lba; //i节点位图起始扇区地址
uint32_t inode_bitmap_sects; //i节点位图占用扇区数量
uint32_t inode_table_lba; //i节点数组起始扇区地址
uint32_t inode_table_sects; //i节点数组占用扇区数量
uint32_t data_start_lba; //空闲块起始lba地址
uint32_t root_inode_no; //根目录所在i节点号
uint32_t dir_entry_size; //目录项大小
uint8_t pad[460]; //加上460字节,凑够512字节1扇区大小
}__attribute__((packed));
i节点用于保存文件存储信息,结构如下(主要信息是i_no、i_size以及i_sectors,其余属性是在内存中操作时使用):
struct inode {
uint32_t i_no; //inode编号
uint32_t i_size; //当inode是文件时,i_size指文件大小;当inode是目录时,i_size指该目录下所有目录项大小之和
uint32_t i_open_cnts; //记录次文件被打开的次数
bool write_deny; //写文件不能并行,进程写文件前检查次标识
uint32_t i_sectors[13]; //0-11是直接块,12用来存储一级间接块指针
struct list_elem inode_tag; //用于加入已打开的inode队列
};
目录项是目录文件数据块中存储的内容,主要作用是绑定i_no与文件名以及对文件类别(文件、目录)进行标识。
#define MAX_FILE_NAME_LEN 16 //最大文件名长度
struct dir {
struct inode* inode;
uint32_t dir_pos;
uint8_t dir_buf[512];
};
/* 目录项结构 */
struct dir_entry {
char filename[MAX_FILE_NAME_LEN]; //普通文件或目录文件名称
uint32_t i_no; //普通文件或目录对应的inode编号
enum file_types f_type; //文件类型
};
下面还定义了一些硬盘操作常用的宏:
#define MAX_FILES_PER_PART 4096 //每个分区支持创建的最大文件数
#define BITS_PER_SECTOR 4096 //每个扇区的bit位数
#define SECTOR_SIZE 512 //扇区的字节大小
#define BLOCK_SIZE SECTOR_SIZE //块字节大小
enum file_types {
FT_UNKNOWN, //不支持的文件类型
FT_REGULAR, //普通文件
FT_DIRECTORY //目录
};
二、创建文件系统
创建文件系统实际就是将各数据结构填好,然后写入到硬盘中对应的扇区即可。
static void partition_format(struct disk* hd, struct partition* part) {
/* 各部分所占扇区数 */
uint32_t boot_sector_sects = 1;
uint32_t super_block_sects = 1;
uint32_t inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR);
uint32_t inode_table_sects = DIV_ROUND_UP((MAX_FILES_PER_PART * sizeof(struct inode)), SECTOR_SIZE);
uint32_t used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects;
uint32_t free_sects = part->sec_cnt - used_sects;
uint32_t block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);
uint32_t block_bitmap_bit_len = free_sects - block_bitmap_sects;
block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR);
/* 1、超级块初始化 */
struct super_block sb;
sb.magic = 0x19970814;
sb.sec_cnt = part->sec_cnt;
sb.inode_cnt = MAX_FILES_PER_PART;
sb.part_lba_base = part->start_lba;
sb.block_bitmap_lba = sb.part_lba_base + 2;
sb.block_bitmap_sects = block_bitmap_sects;
sb.inode_bitmap_lba = sb.block_bitmap_lba + sb.block_bitmap_sects;
sb.inode_bitmap_sects = inode_bitmap_sects;
sb.inode_table_lba = sb.inode_bitmap_lba + sb.inode_bitmap_sects;
sb.inode_table_sects = inode_table_sects;
sb.data_start_lba = sb.inode_table_lba + sb.inode_table_sects;
sb.root_inode_no = 0;
sb.dir_entry_size = sizeof(struct dir_entry);
printk("%s info:\n", part->name);
printk(" magic:0x%x\n part_lba_base:0x%x\n all_sectors:0x%x\n \
inode_cnt:0x%x\n block_bitmap_lba:0x%x\n block_bitmap_sectors:0x%x\n \
inode_bitmap_lba:0x%x\n inode_bitmap_sectors:0x%x\n \
inode_table_lba:0x%x\n inode_table_sectors:0x%x\n \
data_start_lba:0x%x\n", \
sb.magic,sb.part_lba_base,sb.sec_cnt,sb.inode_cnt,sb.block_bitmap_lba,sb.block_bitmap_sects,\
sb.inode_bitmap_lba,sb.inode_bitmap_sects,sb.inode_table_lba,\
sb.inode_table_sects,sb.data_start_lba);
/* 2、将超级块写入本分区的1扇区 */
ide_write(hd, part->start_lba + 1, &sb, 1);
printk(" super_block_lba:0x%x\n", part->start_lba + 1);
/* 3、将块位图初始化并写入sb.block_bitmap_lba */
uint32_t buf_size = (sb.block_bitmap_sects >= sb.inode_bitmap_sects ? sb.block_bitmap_sects : sb.inode_bitmap_sects);
buf_size = (buf_size >= sb.inode_table_sects ? buf_size : sb.inode_table_sects) * SECTOR_SIZE;
uint8_t* buf = (uint8_t*) sys_malloc(buf_size);
buf[0] |= 0x01;
uint32_t block_bitmap_last_byte = block_bitmap_bit_len / 8;
uint32_t block_bitmap_last_bit = block_bitmap_bit_len % 8;
uint32_t last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE);
memset(&buf[block_bitmap_last_byte], 0xff, last_size);
uint8_t bit_idx = 0;
while (bit_idx <= block_bitmap_last_bit) {
buf[block_bitmap_last_byte] &= ~(1 << bit_idx++);
}
ide_write(hd, sb.block_bitmap_lba, buf, sb.block_bitmap_sects);
/* 4、将inode位图初始化并写入sb.inode_bitmap_lba */
memset(buf, 0, buf_size);
buf[0] |= 0x1;
ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);
/* 5、将inode数组初始化并写入sb.inode_table_lba */
memset(buf, 0, buf_size);
struct inode* i = (struct inode*) buf;
i->i_size = sb.dir_entry_size * 2;
i->i_no = 0;
i->i_sectors[0] = sb.data_start_lba;
ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);
/* 6、将根目录写入sb.data_start_lba */
memset(buf, 0, buf_size);
struct dir_entry* p_de = (struct dir_entry*) buf;
memcpy(p_de->filename, ".", 1);
p_de->i_no = 0;
p_de->f_type = FT_DIRECTORY;
p_de++;
memcpy(p_de->filename, "..", 2);
p_de->i_no = 0;
p_de->f_type = FT_DIRECTORY;
ide_write(hd, sb.data_start_lba, buf, 1);
printk(" root_dir_lba:0x%x\n", sb.data_start_lba);
printk("%s format done\n", part->name);
sys_free(buf);
}
然后就是对每个硬盘的每个分区都用partition_format函数初始化成本文最开头图示的样子,这样硬盘上就初始化好了各个结构,完成了文件系统的创建。