文章目录
- 文件系统概念
- inode
- 目录项与目录
- 通过文件名查找文件的流程
- 超级块与文件系统布局
- 文件控制块 —— FCB
- 创建文件系统
- 创建相关结构
- 创建文件系统
- 挂载分区
- 文件描述符
- 文件描述符和文件表
- 文件描述符
- 文件表
- 文件描述符与 inode 关联关系
- 文件描述符的实现
- 文件操作相关的基础函数
- inode 操作有关的函数
- 文件相关的函数
- 目录相关的函数
- 路径解析的函数
- 实现文件检索功能
- 操作思路 —— 思维导图
- 创建文件
- fs/file.c —— file_create
- fs/fs.c —— sys_open
- fs/fs.c —— 初始化
- 文件的打开与关闭
- 文件的打开
- 文件的关闭
- 文件的写入
- fs/file.c —— file_write
- 改进 sys_write 和 write 系统调用
- 读取文件
- 实现文件读写指针定位
- 文件删除
- 回收 inode
- 删除目录项
- 实现 sys_unlink
- 创建目录
- 遍历目录
- 打开目录和关闭目录
- 读取一个目录项
- 实现 sys_readdir 及 sys_rewinddir
- 删除目录
- 删除目录与判断空目录
- 实现 sys_rmdir
- 任务的工作目录
- 基础函数
- sys_getcwd
- sys_chdir
- 文件属性
- 参考资料
文件系统概念
文件系统是对文件的管理方案。
硬盘是低速设备,其读写单位是扇区,为了避免频繁访问硬盘,操作系统不会有了一扇区数据就去读写一次磁盘,往往等数据积攒到足够大时,在一次性访问硬盘,那么到底是多大呢?我们以“块”为单位,一个块可能 4K 或 32K 等。
硬盘读写单位是扇区,一次一个块是由多个扇区组成,块大小是扇区的整数倍。
块是文件系统的读写单位,因此一个文件可能被分成多个块进行存储。
FAT 文件系统的文件链式组织结构:
缺点:当需要访问文件中的某一块时,需要从头开始遍历。
另一种文件组织结构:UINX 操作系统中的索引结构 —— inode。
inode
inode 提供了:文件的 inode 编号、权限、属组、时间、文件大小、基本的直接快(可以直接获取到块地址)、三个级别的间接块(若基本的直接块不够用,则启用这三级)。
关于三个间接块:
- 一级间接块,存储的是新建块的索引表的地址,该索引表可容纳 256 个块的地址,因此加上一级块总共可容纳 12+256=168 块。
- 二级间接块,存储的是新建的二级块索引表的地址,而二级块索引表中每项都是一级块索引表的地址,因此二级本身可容纳 256 × 256 256\times256 256×256 块,所以加上二级可总共可容纳 12 + 256 + 256 × 256 12+256+256\times256 12+256+256×256 块。
- 三级间接块,存储的是新建的三级块索引表的地址,而三级块索引表中每项都是二级块索引表的地址,二级块索引表中每项都是一级块索引表的地址,因此三级块本身可容纳 256 × 256 × 256 256\times256\times256 256×256×256 块,所以总共可容纳 12 + 256 + 256 × 256 + 256 × 256 × 256 12+256+256\times256+256\times256\times256 12+256+256×256+256×256×256 块。
inode 是文件在文件系统上的元信息(文件本身的元信息是文件本身的文件头),要想通过文件系统访问到文件,必须先找到文件的 inode,从这个意义上看,inode 也可以是文件。
每个文件都必须有一个 inode,有多少文件就有多少个 inode。
文件系统是针对各个分区来管理磁盘空间的,各个分区有所有文件的 inode 结构、文件的数据块、超级块、各种位图、目录项等,需要先确定下来一方,先划分 inode 结构和文件数据块,其它的暂时忽略,由于 inode 结构大小是确定的,因此可以先固定下来 inode 所占用的空间。
注:既然每个文件都必须有一个 inode,那么若 inode 所占空间固定下来,那么就说明该分区所允许创建的最大文件数量也是有限的。
当然,实际创建的文件数必然 <= 允许的最大文件数,因为还得考虑文件的大小,若空间为 10M,允许最多创建 10 个文件,即可创建 2 个 5M,也可创建 10 个 1M。
目录项与目录
我们是通过文件名来访问文件的,但文件系统是通过 inode 来访问文件的,因此对文件系统来说文件名并不重要。
我们需要把文件名和 inode 关联到一起。
我们找文件时,文件肯定存在于某个目录中,当然你可能不知道具体在哪个目录下,但可以肯定的是必然存在于根目录 /
之中。
在 Linux 中,目录和文件都用 inode 表示,因此目录也是文件,只是目录是包含文件的文件。我们通常称目录为目录文件,其它的称为普通文件。
既然都是文件,那么如何区分目录文件还是普通文件:根据数据块中描述的内容来确定,若数据块中的内容是数据项,那么该文件就是目录文件,反之为普通文件。
inode 编号 | 文件名 | 文件类型 |
---|---|---|
6165597 | . | 目录 |
6161422 | … | 目录 |
6165599 | image.img | 文件 |
6165600 | index.html | 文件 |
通过文件名查找文件的流程
- 根据
/
根目录的 inode 编号,找到对应的数据块。 - 因为
/
是目录文件,因此数据块中存储的都是目录项,根据文件名,在该数据块中找到etc
,判断其文件类型,发现etc
是个目录文件,最后拿到etc
的 inode 编号。 - 因为
etc
是目录文件,因此数据块中存储的都是目录项,根据文件名,在该数据块中找到fstab
,判断其文件类型是个普通文件,拿到其 inode 编号。 - 拿到
fstab
的 inode 编号后,去找到数据块,由于fstab
是普通文件,因此该数据块存储的是实际的内容。
图片来着:https://www.bilibili.com/video/BV1yV411r7Qb/?vd_source=9d985a804a1c8cf904ddafbc9c973491
超级块与文件系统布局
超级块(Super Block):存储文件系统的元信息的元信息。
保存 inode 数组的位置、block 数组的位置、inode 和 block 位图位置、以及容量大小等信息。
在 Linux 中根目录就只有一个,即 /
,但 Windows 中每个分区都各有不同的根目录。但我们需要考虑的问题是一样的,就是根目录要保存到哪儿,况且 Windows 中还需要保存多个根目录。
这个位置显然必须是固定的。
实际上超级块就是保存诸如上面的信息。
超级块可以看成是文件系统的配置文件。
超级块所存储的信息取决于文件系统的复杂程度。
超级块的逻辑结构:
超级块是在为分区创建文件系统时创建的,所有有关文件系统元信息的配置都在超级块中,因此超级块的位置和大小不能再被配置了,必须是固定的,它被固定存储在各分区的第二个扇区中,通常占用一个扇区,具体与实际文件系统类型为准。
文件系统布局:
注意:操作系统引导可能占用多个扇区,因此上图中写的是“块”,而不是“扇区”。
文件控制块 —— FCB
为了能对一个文件进行正确的存取,操作系统必须为文件设置用于描述和控制文件的数据结构,称之为“文件控制块(FCB)”。
创建文件系统
创建相关结构
fs/super_block.h
/* 超级块 */
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; // 块位图本身起始扇区地址
uint32_t block_bitmap_sects; // 扇区位图本身占用的扇区数量
uint32_t inode_bitmap_lba; // i结点位图起始扇区lba地址
uint32_t inode_bitmap_sects; // i结点位图占用的扇区数量
uint32_t inode_table_lba; // i结点表起始扇区lba地址
uint32_t inode_table_sects; // i结点表占用的扇区数量
uint32_t data_start_lba; // 数据区开始的第一个扇区号
uint32_t root_inode_no; // 根目录所在的I结点号
uint32_t dir_entry_size; // 目录项大小
uint8_t pad[460]; // 加上460字节,凑够512字节1扇区大小
} __attribute__ ((packed));
fs/inode.h
// inode 结构
struct inode {
uint32_t i_no; // inode 编号
uint32_t i_size; // 若 inode 为目录,则 i_size 便是该目录下所有目录项大小之和
// 若 inode 为文件,则 i_size 便是文件大小
uint32_t i_open_cnt; // 记录该文件被打开的次数之和
bool write_deny; // 写文件不能多个进程并行修改,进程写文件前会先检查此标识符
uint32_t i_sectors[13]; // [0~11] 是直接块,[12] 用来存储一级间接块指针
struct list_elem inode_tag;
};
fs/dir.h
#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; // 文件类型
};
fs/fs.h
#define MAX_FILES_PER_PART 4096 // 每个分区所支持的最大文件数
#define BITS_PER_SECTOR 4096 // 每个扇区的位数 512(byte) * 8(bit)
#define SECTOR_SIZE 512 // 一个扇区的字节大小
#define BLOCK_SIZE SECTOR_SIZE // 块字节大小
// 文件类型
enum file_types {
FT_UNKNOWN, // 其它类型文件
FT_REGULAR, // 普通文件
FT_DIRECTORY // 目录文件
};
创建文件系统
fs/fs.c —— 格式化分区
// 格式化分区,也就是初始化分区中的元信息,从而创建文件系统
static void partition_format(struct partition* part) {
// 为了方便,一个块大小 = 一个扇区
uint32_t boot_sector_sects = 1;
uint32_t super_block_sects = 1;
// inode 位图所占用的扇区数,最多支持 4096 个文件
// 目前我理解为:512byte = 4096bit, 即每个 bit 对应一个“块”, 每个块可以是 inode
uint32_t inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR);
// inode_table 所需要的扇区数
uint32_t inode_table_sects = DIV_ROUND_UP(((sizeof(struct inode) * MAX_FILES_PER_PART)), 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;
// 块的位图 所占用的扇区数
// 一个块 = 一个扇区, 4096 位 = 4096 个块, 直接除于 4096 可得到扇区数
uint32_t block_bitmap_sects;
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);
/*------------------------------------*/
// super_block 超级块的初始化
/*------------------------------------*/
struct super_block sb;
sb.magic = 0x19590318;
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; // 第 0 块是引导块,第 1 块是超级块
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);
/*-----------------------------------------*/
// 将超级块写入本分区的 1 扇区(0 扇区是引导扇区)
/*-----------------------------------------*/
struct disk* hd = part -> my_disk;
ide_write(hd, part -> start_lba + 1, &sb, 1);
printk(" super_block_lba:0x%x\n", part->start_lba + 1);
// 找出数据量最大的元信息,将其作为存储缓冲区
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);
/*-----------------------------------------*/
// 将块位图初始化并写入 sb.block_bitmap_lba
/*-----------------------------------------*/
buf[0] |= 0x01; // 第 0 块先预留给根目录,位图先占位
uint32_t block_bitmap_last_byte = block_bitmap_bit_len / 8; // 位图的长度,以字节为单位
uint8_t block_bitmap_last_bit = block_bitmap_bit_len % 8; // 最后不足一个字节的位的个数
// last_size 是位图所在扇区最后不足一扇区的部分
uint32_t last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE);
// 将位图最后一个字节所在扇区的有效位全部置为 1, 也就是说超出的那 last_size 全部设置为已占用状态
// 因为我们创建块位图大小是通过宏 DIV_ROUND_UP 把 free_sects 、 BITS_PER_SECTOR 向上取整得到的,因此位图中最后一个扇区的末尾在大多数情况下都会有多余的位,这些多余的位并不代表某些资源,也就是说这些资源即便存在也对我们毫无意义,并不是我们需要的,因此需要进行处理
memset(&buf[block_bitmap_last_byte], 0xFF, last_size);
// 将最后一个字节中其有效的部分将其置为 0,也就是没有超出的那部分
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);
/*--------------------------------------------*/
// 将 inode 位图初始化并写入 sb.inode_bitmap_lba
/*--------------------------------------------*/
// 先清空缓冲区
memset(buf, 0, buf_size);
buf[0] |= 0x01; // 第 0 个 inode 分给了根目录
/**
* 因为 inode_table 共 4096 个 inode,inode_bitmap 位图正好占用一个扇区
* 即 inode_bitmap_sects = 1
* 所以位图中的所有扇区均是有效位,无需再像 block_bimap 那样处理残余的扇区
*/
ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);
/*--------------------------------------------*/
// 将 inode 数组初始化并写入 sb.inode_table_lba
/*--------------------------------------------*/
memset(buf, 0, buf_size);
// 初始化第一个 inode 项,它表示根目录
struct inode* i = (struct inode*) buf;
i -> i_size = sb.dir_entry_size * 2; // . 和 ..
i -> i_no = 0; // inode 编号
i -> i_sectors[0] = sb.data_start_lba; // 第 0 个块指向 sb.data_start_lba
// 也就是将根目录安排在最开始的空闲块中
ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);
/*--------------------------------------------*/
// 将根目录初始化并写入 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);
}
fs/fs.c —— 初始化文件系统
struct partition* cur_part; // 默认情况下操作的是哪个分区
// 初始化文件系统
void filesys_init() {
uint8_t channel_no = 0, dev_no = 0, part_idx = 0;
// sb_buf 用来存储从硬盘上读入的超级块
struct super_block* sb_buf = (struct super_block*) sys_malloc(SECTOR_SIZE);
if(sb_buf == NULL)
PANIC("alloc memory failed!");
printk("searching filesystem...\n");
while(channel_no < channel_cnt) { // 遍历通道
dev_no = 0;
while(dev_no < 2) { //
if(dev_no == 0) {
dev_no++;
continue;
}
struct disk* hd = &channels[channel_no].devices[dev_no];
struct partition* part = hd -> prim_parts;
while(part_idx < 12) { // 4个主分区 + 8个逻辑分区
if(part_idx == 4) // 主分区遍历完了,开始切换到逻辑分区
part = hd -> logic_parts;
if(part -> sec_cnt != 0) { // 判断该分区是否存在
memset(sb_buf, 0, SECTOR_SIZE);
// 读取分区的超级块,根据魔数是否正确来判断是否存在文件系统
ide_read(hd, part -> start_lba + 1, sb_buf, 1);
// 若该分区已存在文件系统,则不再进行格式化
if(sb_buf -> magic == 0x19590318) {
printk("%s had filesystem.\n", part -> name);
} else {
printk("formatting %s's partition %s...\n", hd -> name, part -> name);
partition_format(part);
}
}
part_idx++;
part++; // 下一个分区
}
dev_no++; // 下一个硬盘
}
channel_no++; // 下一个通道
}
sys_free(sb_buf);
}
挂载分区
文件系统的主要工作是资源管理,跟踪资源的状态是通过位图来实现的,因此创建文件系统就是创建各种资源的位图,而位图肯定是在内存中先创建好,然后将其持久化到硬盘上,这些位图将来挂载分区时会用到。
当我们需要使用一个分区时,就需要使用到资源位图,而对资源位图的操作必然是在内存中的,但挂载分区前可没有位图,因此我们需要提取将位图写入到硬盘上,将来若需要使用到某个分区的数据时,就可以通过挂载分区再把硬盘上的位图重新加载到内存中。
fs/fs.c —— 文件挂载
// 在分区链表中找到名为 part_name 的分区,并将其指针赋值给 cur_part
static bool mount_partition(struct list_elem* pelem, int arg) {
char* part_name = (char*) arg;
struct partition* part = elem2entry(struct partition, part_tag, pelem);
if(!strcmp(part -> name, part_name)) {
cur_part = part;
struct disk* hd = cur_part -> my_disk;
// 准备 super_block 结构体准备用来存储从硬盘读入的超级块数据
struct super_block* sb_buf = (struct super_block*) sys_malloc(SECTOR_SIZE);
// 在内存中创建分区 cur_part 的超级块
cur_part -> sb = (struct super_block*) sys_malloc(sizeof(struct super_block));
if(cur_part -> sb == NULL)
PANIC("alloc memory failed!");
// 读入超级块
memset(sb_buf, 0, SECTOR_SIZE);
ide_read(hd, cur_part -> start_lba + 1, sb_buf, 1);
// 把 sb_buf 中的超级块信息复制到分区的超级块 sb 中
memcpy(cur_part -> sb, sb_buf, sizeof(struct super_block));
/*---------------------------------------*/
// 将硬盘上的块位图读入到内存
/*---------------------------------------*/
cur_part -> block_bitmap.bits = (uint8_t*) sys_malloc(sb_buf -> block_bitmap_sects * SECTOR_SIZE);
if(cur_part -> block_bitmap.bits == NULL)
PANIC("alloc memory failed!");
cur_part -> block_bitmap.btmp_bytes_len = sb_buf -> block_bitmap_sects * SECTOR_SIZE;
// 从硬盘上读入块位图到分区的 block_bitmap.bits
ide_read(hd, sb_buf -> block_bitmap_lba, cur_part -> block_bitmap.bits, sb_buf -> block_bitmap_sects);
/*---------------------------------------*/
// 将硬盘上的 inode 位图读入到内存
/*---------------------------------------*/
cur_part -> inode_bitmap.bits = (uint8_t*) sys_malloc(sb_buf -> inode_bitmap_sects * SECTOR_SIZE);
if(cur_part -> inode_bitmap.bits == NULL)
PANIC("alloc memroy failed!");
cur_part -> inode_bitmap.btmp_bytes_len = sb_buf -> inode_bitmap_sects * SECTOR_SIZE;
// 从硬盘上读入 inode 位图到分区的 inode_bitmap.bits
ide_read(hd, sb_buf -> inode_bitmap_lba, cur_part -> inode_bitmap.bits, sb_buf -> inode_bitmap_sects);
// ------------
list_init(&cur_part -> open_inodes);
printk("mount %s done!\n", part -> name);
/* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
return true;
}
return false; // 使 list_traversal 继续遍历
}
fs/fs.c —— 初始化文件系统
// 初始化文件系统
void filesys_init() {
...
// 确定默认操作的分区
char default_part[8] = "sdb1";
// 挂载分区
list_traversal(&partition_list, mount_partition, (int) default_part);
}
文件描述符
文件描述符和文件表
文件描述符
内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
习惯上,标准输入(standard input)的文件描述符是 0,标准输出(standard output)是 1,标准错误(standard error)是 2。尽管这种习惯并非Unix内核的特性,但是因为一些 shell 和很多应用程序都使用这种习惯,因此,如果内核不遵循这种习惯的话,很多应用程序将不能使用。
文件表
Linux 提供了称为“文件结构”的数据结构,专门用于记录与文件操作相关的信息,每次打开一个文件就会产生一个文件结构,多次打开就会产生多个文件结构。
Linux 把所有的“文件结构”组织到一起形成数组统一管理,该数组称为文件表。
文件描述符与 inode 关联关系
在 Linux 中文件通过先通过 open 打开文件以获得该文件的文件描述符,然后将文件描述符传给 read 或 write 等函数对该文件进行相关操作。
当用户进程打开文件时,文件系统给用户进程返回的是该 PCB 中文件描述符数组的下标值,也就是文件描述符。
上图是 Linux 通过文件描述符查找文件数据块(也就是查找文件)的过程,涉及的三个数据结构,它们都位于内存中,如下:
- PCB 中的文件描述符数组。
- 存储所有文件结构的文件表,也就是说这个数组是全局的。
- inode 队列,也就是 inode 缓存。
注:若要打开的文件所对应的 inode 不存在于 inode 缓存中,此时需要多做一个处理,就是文件系统会从硬盘上将该 inode 加载到 inode 缓存中,并使文件结构中的 fd_inode 指向它。
文件结构和文件表可查看:文件操作相关的基础函数 -> 文件相关的函数 -> file.h
文件描述符的实现
thread/thread.h —— 修改 PCB
// 进程或线程的 PCB,即程序控制块
struct task_struct {
...
int32_t fd_table[MAX_FILES_OPEN_PER_PROC]; // 文件描述符数组,存储的都是文件结构的地址
...
};
thread/thread.c —— 修改初始化线程
// 初始化线程基本信息
void init_thread(struct task_struct* pthread, char* name, int prio) {
...
// 预留标准输入输出
pthread -> fd_table[0] = 0;
pthread -> fd_table[1] = 1;
pthread -> fd_table[2] = 2;
// 其余全部设置为 -1,表示空位
uint8_t fd_idx = 3;
while(fd_idx < MAX_FILES_OPEN_PER_PROC) {
pthread -> fd_table[fd_idx] = -1;
fd_idx++;
}
...
}
文件操作相关的基础函数
inode 操作有关的函数
fs/inode.c
// 用来存储 inode 的位置信息
struct inode_position {
bool two_sec; // inode 是否跨扇区
uint32_t sec_lba; // inode 所在的扇区号
uint32_t off_size; // inode 所在扇区内的字节偏移量
};
// 根据 inode 编号获取 inode 所在扇区和扇区内的偏移量
static void inode_locate(struct partition* part, uint32_t inode_no, struct inode_position* inode_pos) {
// inode_table 在硬盘上是连续的
ASSERT(inode_no < 4096);
uint32_t inode_table_lba = part -> sb -> inode_table_lba;
uint32_t inode_size = sizeof(struct inode);
uint32_t off_size = inode_no * inode_size; // 第 inode_no 号 inode 相对于 inode_table_lba 偏移多少字节
uint32_t off_sec = off_size / 512; // 第 inode_no 号 inode 相对于 inode_table_lba 偏移多少扇区
uint32_t off_size_in_sec = off_size % 512; // 第 inode_no 号 inode 在扇区内的字节偏移
// 判断此 inode 是否跨越两个扇区
uint32_t left_in_sec = 512 - off_sec;
if(left_in_sec < inode_size) // 若扇区内剩余的空间不足以容纳一个 inode,则 inode 必然跨越了两个扇区
inode_pos -> two_sec = true;
else
inode_pos -> two_sec = false;
inode_pos -> sec_lba = inode_table_lba + off_sec;
inode_pos -> off_size = off_size_in_sec;
}
// 将 inode 写入到分区 part 中 —— 同步
void inode_sync(struct partition* part, struct inode* inode, void* io_buf) {
uint8_t inode_no = inode -> i_no;
struct inode_position inode_pos;
inode_locate(part, inode_no, &inode_pos); // 将 inode 的位置信息保存到 inode_pos
ASSERT(inode_pos.sec_lba <= (part -> start_lba + part -> sec_cnt));
/**
* 硬盘中 inode 的成员 inode_tag 和 i_open_cnts
* 它们只在内存中记录链表位置和被多少进程所共享
* 因此在写入硬盘前,需要对其清理
*/
// 不对现有的 inode 进行清理,而是复制一个 inode 进行清理,最后同步这个复制的 inode
struct inode pure_inode;
memcpy(&pure_inode, inode, sizeof(struct inode));
// 进行清理
pure_inode.i_open_cnt = 0;
pure_inode.write_deny = false;
pure_inode.inode_tag.prev = pure_inode.inode_tag.next = NULL;
char* inode_buf = (char*) io_buf;
if(inode_pos.two_sec) { // 处理两个扇区
// 先读取两个扇区
ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 2);
// 将 pure_inode 复制到 (inode_buf + inode_pos.off_size)
memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));
// 写入两个扇区
ide_write(part -> my_disk, inode_pos.sec_lba, inode_buf, 2);
} else { // 处理一个扇区
ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 1);
memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));
ide_write(part -> my_disk, inode_pos.sec_lba, inode_buf, 1);
}
}
// 根据 inode 编号返回相应的 inode 实体
struct inode* inode_open(struct partition* part, uint32_t inode_no) {
// 先在已打开的 inode 中查找,也就是 inode 缓存队列
struct list_elem* elem = part -> open_inodes.head.next;
struct inode* inode_found;
while(elem != &part -> open_inodes.tail) {
inode_found = elem2entry(struct inode, inode_tag, elem);
if(inode_found -> i_no == inode_no) {
inode_found -> i_open_cnt++;
return inode_found;
}
elem = elem -> next;
}
// --------------------------------------------------------
// 在已有的找不到,则从硬盘查找,并且加入到 part -> open_inodes 中
// --------------------------------------------------------
// 得到 inode 信息
struct inode_position inode_pos;
inode_locate(part, inode_no, &inode_pos);
/**
* 为了使 sys_malloc 创建的新 inode 被所有任务所共享
* 需要将 inode 置于内核空间
* 则需要临时将 cur_pbc -> pgdir = NULL
*/
struct task_struct* cur = running_thread();
uint32_t* cur_pagedir_bak = cur -> pgdir;
cur -> pgdir = NULL;
// cur -> pgdir = NULL 后,此时申请的就是内核空间
inode_found = (struct inode*) sys_malloc(sizeof(struct inode));
// 恢复 pgdir
cur -> pgdir = cur_pagedir_bak;
char* inode_buf;
if(inode_pos.two_sec) { // 跨两个扇区
inode_buf = (char*) sys_malloc(1024);
ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 2);
} else { // 没跨扇区
inode_buf = (char*) sys_malloc(512);
ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 1);
}
memcpy(inode_found, inode_buf + inode_pos.off_size, sizeof(struct inode));
list_push(&part -> open_inodes, &inode_found -> inode_tag);
inode_found -> i_open_cnt = 1; // 打开了一次
sys_free(inode_buf);
return inode_found;
}
// 关闭 inode 或减少 inode 的打开次数
void inode_close(struct inode* inode) {
enum intr_status old_status = intr_disable();
if(--inode -> i_open_cnt == 0) {
list_remove(&inode -> inode_tag);
/**
* inode_open 时为实现 inode 被所有进程所共享
* 已经为 inode 分配了内核空间的资源
* 所以此时释放也需要释放内核空间的资源
*/
struct task_struct* cur = running_thread();
uint32_t* cur_pagedir_bak = cur -> pgdir;
cur -> pgdir = NULL;
sys_free(inode);
cur -> pgdir = cur_pagedir_bak;
}
intr_set_status(old_status);
}
// 初始化 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_cnt = 0;
new_inode -> write_deny = false;
uint8_t sec_idx = 0;
while(sec_idx < 13) { // 初始化直接块(0~11),索引 >= 12 就是间接块
new_inode -> i_sectors[sec_idx] = 0;
sec_idx++;
}
}
文件相关的函数
fs/file.h
// 文件结构
struct file {
uint32_t fd_pos; // 记录当前文件操作的偏移量,文件中的偏移量,以0起始,最大为文件大小-1
uint32_t fd_flag;
struct inode* fd_inode;
};
// 标志输入输出描述符
enum std_fd {
stdin_no, // 0 标准输入
stdout_no, // 1 标准输出
stderr_no // 2 标准错误
};
// 位图类型
enum bitmap_type {
INODE_BITMAP, // inode 位图
BLOCK_BITMAP // block 位图
};
#define MAX_FILE_OPEN 32 // 系统可打开的最大文件数量
extern struct file file_table[MAX_FILE_OPEN]; // 全局的文件表
fs/file.c
#define DEFAULT_SECS 1
// 文件表
struct file file_table[MAX_FILE_OPEN];
// 从文件表 file_table 中获取一个空闲位,返回下标
int32_t get_free_slot_in_global(void) {
uint32_t fd_idx = 3; // 前面 0 1 2 是标准输入输出
while(fd_idx < MAX_FILE_OPEN) {
if(file_table[fd_idx].fd_inode == NULL) break;
fd_idx++;
}
if(fd_idx == MAX_FILE_OPEN) {
printk("exceed max open files.\n");
return -1;
}
return fd_idx;
}
// 将全局描述符下标安装到进程或线程自己的文件描述符数组 fd_table 中,返回其下标
int32_t pcb_fd_install(int32_t global_fd_idx) {
struct task_struct* cur = running_thread();
uint8_t local_fd_idx = 3; // 跨过标准输入输出
while(local_fd_idx < MAX_FILES_OPEN_PER_PROC) {
if(cur -> fd_table[local_fd_idx] == -1) { // 查找空闲位
cur -> fd_table[local_fd_idx] = global_fd_idx;
break;
}
local_fd_idx++;
}
if(local_fd_idx == MAX_FILES_OPEN_PER_PROC) {
printk("exceed max open files_per_proc\n");
return -1;
}
return local_fd_idx;
}
// 分配一个 inode,返回 inode 编号(位图索引)
int32_t inode_bitmap_alloc(struct partition* part) {
int32_t bit_idx = bitmap_scan(&part -> inode_bitmap, 1);
if(bit_idx == -1) return -1;
bitmap_set(&part -> inode_bitmap, bit_idx, 1);
return bit_idx;
}
// 分配一个扇区,返回其扇区地址
int32_t block_bitmap_alloc(struct partition* part) {
int32_t bit_idx = bitmap_scan(&part -> block_bitmap, 1);
if(bit_idx == -1) return -1;
bitmap_set(&part -> block_bitmap, bit_idx, 1);
return (part -> sb -> data_start_lba + bit_idx);
}
// 将内存中 bitmap 第 bit_idx 位所在的扇区同步到硬盘
void bitmap_sync(struct partition* part, uint32_t bit_idx, uint8_t btmp_type) {
/**
* Tips
* 4096 = 512字节 = 1扇区
* 但在位图(数组)中是以 0 为起始
* 因此第 4096 位占用两个扇区
* 0~4095 为一个扇区
*/
uint32_t off_sec = bit_idx / 4096; // 求得 bit_idx 所在扇区
uint32_t off_size = off_sec * BLOCK_SIZE; // 字节偏移量
uint32_t sec_lba; // 起始地址
uint8_t* bitmap_off; // 内容的起始地址
// 需要被同步到硬盘的位图只有 inode_bitmap 和 block_bitmap
switch(btmp_type) {
case INODE_BITMAP:
sec_lba = part -> sb -> inode_bitmap_lba + off_sec;
bitmap_off = part -> inode_bitmap.bits + off_size;
break;
case BLOCK_BITMAP:
sec_lba = part -> sb -> block_bitmap_lba + off_sec;
bitmap_off = part -> block_bitmap.bits + off_size;
break;
}
ide_write(part -> my_disk, sec_lba, bitmap_off, 1);
}
目录相关的函数
fs/dir.c
struct dir root_dir; // 根目录
// 打开根目录
void open_root_dir(struct partition* part) {
root_dir.inode = inode_open(part, part -> sb -> root_inode_no);
root_dir.dir_pos = 0;
}
// 在分区 Part 上打开 inode 编号为 inode_no 的目录,并返回目录指针
struct dir* dir_open(struct partition* part, uint32_t inode_no) {
struct dir* pdir = (struct dir*) sys_malloc(sizeof(struct dir));
pdir -> inode = inode_open(part, inode_no);
pdir -> dir_pos = 0;
return pdir;
}
/*在 part 分区内的 pdir 目录内寻找名为 name 的文件或目录
找到返回 true,并且将其目录项存入 dir_e,否则返回 false*/
bool search_dir_entry(struct partition* part, struct dir* pdir, const char* name, struct dir_entry* dir_e) {
uint32_t block_cnt = 140; // 12个直接块+128个一级间接块
// 12个直接块+128个一级间接块,总共 560 字节
// 存储的都是该文件或目录的所有扇区地址
uint32_t* all_blocks = (uint32_t*) sys_malloc(48 + 512);
if(all_blocks == NULL) {
printk("search_dir_entry: sys_malloc for all_blocks failed");
return false;
}
uint32_t block_idx = 0;
while(block_idx < 12) { // 处理直接块
all_blocks[block_idx] = pdir -> inode -> i_sectors[block_idx];
block_idx++;
}
block_idx = 0;
if(pdir -> inode -> i_sectors[12] != 0) { // 处理一级间接块
ide_read(part -> my_disk, pdir -> inode -> i_sectors[12], all_blocks + 12, 1);
}
/**
* 由于写目录项的时候已保证不跨越扇区
* 所以只需要申请一个扇区的内存
*/
uint8_t* buf = (uint8_t*) sys_malloc(SECTOR_SIZE);
struct dir_entry* p_de = (struct dir_entry*) buf;
uint32_t dir_entry_size = part -> sb -> dir_entry_size; // 目录项大小
uint32_t dir_entry_cnt = SECTOR_SIZE / dir_entry_size; // 一个扇区可容纳的目录项个数
// 开始在所有块中查找目录项
while(block_idx < block_cnt) {
if(all_blocks[block_idx] == 0) {
block_idx++;
continue;
}
ide_read(part -> my_disk, all_blocks[block_idx], buf, 1);
uint32_t dir_entry_idx = 0;
// 遍历扇区中所有目录项
while(dir_entry_idx < dir_entry_cnt) {
if(!strcmp(p_de -> filename, name)) {
memcpy(dir_e, p_de, dir_entry_size);
sys_free(buf);
sys_free(all_blocks);
return true;
}
dir_entry_idx++;
p_de++;
}
block_idx++;
// 还原
p_de = (struct dir_entry*) buf;
memset(buf, 0, SECTOR_SIZE);
}
sys_free(buf);
sys_free(all_blocks);
return false;
}
// 关闭目录
void dir_close(struct dir* dir) {
// ---------------------------------
// 根目录不能关闭
// :root_dir 所在内存是低端 1M 内,并不再堆中,free 会出问题
// ---------------------------------
if(dir == &root_dir) return; // 不对根目录做处理
inode_close(dir -> inode);
sys_free(dir);
}
// 在内存中初始化目录项 p_de
void create_dir_entry(char* filename, uint32_t inode_no, uint8_t file_type, struct dir_entry* p_de) {
ASSERT(strlen(filename) <= MAX_FILE_NAME_LEN);
// 初始化目录项
memcpy(p_de -> filename, filename, strlen(filename));
p_de -> i_no = inode_no;
p_de -> f_type = file_type;
}
// 将目录项 p_de 写入父目录中
bool sync_dir_entry(struct dir* parent_dir, struct dir_entry* p_de, void* io_buf) {
struct inode* dir_inode = parent_dir -> inode;
uint32_t dir_size = dir_inode -> i_size;
uint32_t dir_entry_size = cur_part -> sb -> dir_entry_size;
ASSERT(dir_size % dir_entry_size == 0); // dir_size 应该是 dir_entry_size 的整数倍
uint32_t dir_entry_per_sec = (512 / dir_entry_size); // 每扇区最大的目录项数目
int32_t block_lba = -1;
// 将该目录的所有扇区地址存入(12个直接块+128个一级间接块) all_blocks
uint8_t block_idx = 0;
uint32_t all_blocks[140] = {0};
// 将 12 个直接块存入 all_blocks
while(block_idx < 12) {
all_blocks[block_idx] = dir_inode -> i_sectors[block_idx];
block_idx++;
}
struct dir_entry* dir_e = (struct dir_entry*) io_buf;
int32_t block_bitmap_idx = -1;
/**
* 开始遍历所有块,寻找目录项空闲位,若已有扇区中没有空闲位
* 在不超过文件大小的情况下申请新的扇区来存储新目录项
*/
block_idx = 0;
while(block_idx < 140) {
block_bitmap_idx = -1;
if(all_blocks[block_idx] == 0) { // 该块未被分配
// 申请一个新的块
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1) {
printk("alloc block bitmap for sync_dir_entry failed\n");
return false;
}
// 同步进硬盘
block_bitmap_idx = block_lba - cur_part -> sb -> data_start_lba;
ASSERT(block_bitmap_idx != -1);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
block_bitmap_idx = -1;
if(block_idx < 12) { // 直接块
dir_inode -> i_sectors[block_idx] = all_blocks[block_idx] = block_lba;
} else if(block_idx == 12) { // 一级间接块
dir_inode -> i_sectors[12] = block_lba; // 将上面申请的这个 block_lba 块作为一级间接表
block_lba = -1;
block_lba = block_bitmap_alloc(cur_part); // 再分配一个块作为一级间接块的第 0 个间接块
if(block_lba == -1) {
block_bitmap_idx = dir_inode -> i_sectors[12] - cur_part -> sb -> data_start_lba;
bitmap_set(&cur_part -> block_bitmap, block_bitmap_idx, 0);
dir_inode -> i_sectors[12] = 0;
printk("alloc block bitmap for sync_dir_entry failed\n");
return false;
}
// 同步
block_bitmap_idx = block_lba - cur_part -> sb -> data_start_lba;
ASSERT(block_bitmap_idx != -1);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
// 将新分配的第0个间接块地址写入一级间接表
all_blocks[12] = block_lba;
ide_write(cur_part -> my_disk, dir_inode -> i_sectors[12], all_blocks + 12, 1);
} else { // 间接块未分配
all_blocks[block_idx] = block_lba;
/* 把新分配的第(block_idx-12)个间接块地址写入一级间接块表 */
ide_write(cur_part -> my_disk, dir_inode -> i_sectors[12], all_blocks + 12, 1);
}
// 将新目录项 p_de 写入新分配的间接块
memset(io_buf, 0, 512);
memcpy(io_buf, p_de, dir_entry_size);
ide_write(cur_part -> my_disk, all_blocks[block_idx], io_buf, 1);
dir_inode -> i_size += dir_entry_size;
return true;
}
// 该 block_idx 已经存在,将其读入内存,查询其空闲目录项
ide_read(cur_part -> my_disk, all_blocks[block_idx], io_buf, 1);
uint8_t dir_entry_idx = 0;
while(dir_entry_idx < dir_entry_per_sec) {
if((dir_e + dir_entry_idx) -> f_type == FT_UNKNOWN) { // FT_UNKNOWN为0,无论是初始化或是删除文件后,都会将f_type置为FT_UNKNOWN.
memcpy(dir_e + dir_entry_idx, p_de, dir_entry_size);
ide_write(cur_part -> my_disk, all_blocks[block_idx], io_buf, 1);
dir_inode -> i_size += dir_entry_size;
return true;
}
dir_entry_idx++;
}
block_idx++;
}
printk("directory is full!\n");
return false;
}
路径解析的函数
fs/fs.c
// 将最顶层的路径名称解析出来
static char* path_parse(char* pathname, char* name_store) {
if(pathname[0] == '/') { // 根目录不单独解析
/* 路径中出现1个或多个连续的字符'/',将这些'/'跳过,如"///a/b" */
while(*(++pathname) == '/');
}
// 一般路径解析
while(*pathname != '/' && *pathname != 0)
*name_store++ = *pathname++;
if(pathname[0] == 0)
return NULL;
return pathname;
}
// 返回路径深度
int32_t path_depth_cnt(char* pathname) {
ASSERT(pathname != NULL);
char* p = pathname;
char name[MAX_FILE_NAME_LEN];
uint32_t depth = 0;
// 解析路径,从中拆分出各级名称
p = path_parse(p, name);
while(name[0]) {
depth++;
memset(name, 0, MAX_FILE_NAME_LEN);
if(p) // 若 p != NULL,则继续解析
p = path_parse(p, name);
}
return depth;
}
实现文件检索功能
fs/fs.h
// 文件类型
enum file_types {
FT_UNKNOWN, // 其它类型文件
FT_REGULAR, // 普通文件
FT_DIRECTORY // 目录文件
};
/* 打开文件的选项 */
enum oflags {
O_RDONLY, // 只读
O_WRONLY, // 只写
O_RDWR, // 读写
O_CREAT = 4 // 创建
};
// 用来记录查找文件过程中已找到的上级路径,也就是查找文件过程中走过的地方
struct path_search_record {
char searched_path[MAX_PATH_LEN]; // 查找过程中的父路径
struct dir* parent_dir; // 文件或目录的直接父目录
enum file_types file_type; // 找到的文件所属类型
};
/* /a/b/c 假设 c 不存在,那么
searched_path=/a/b/c
parent_dir=b
/a/b/c 假设 b 不存在,那么
searched_path=/a/b
parent_dir=a
*/
enum oflag 结构中使用“位”作为值,例如 O_RDONLY=000b, O_WRONLY=001b, O_RDWR=010b, O_CREAT=100b, 这样的好处是当这几个标识通过位运算“|”叠加一起可作为复核参数,通过位运算“&”可单独解析出各位以反推标识位。
fs/fs.c
// 搜索文件
static int search_file(const char* pathname, struct path_search_record* searched_record) {
// 若待查找的是根目录,直接返回
if (!strcmp(pathname, "/") || !strcmp(pathname, "/.") || !strcmp(pathname, "/..")) {
searched_record->parent_dir = &root_dir;
searched_record->file_type = FT_DIRECTORY;
searched_record->searched_path[0] = 0; // 搜索路径置空
return 0;
}
uint32_t path_len = strlen(pathname);
/* 保证pathname至少是这样的路径/x且小于最大长度 */
ASSERT(pathname[0] == '/' && path_len > 1 && path_len < MAX_PATH_LEN);
char* sub_path = (char*) pathname;
struct dir* parent_dir = &root_dir;
struct dir_entry dir_e;
char name[MAX_FILE_NAME_LEN] = {0};
searched_record -> parent_dir = parent_dir;
searched_record -> file_type = FT_UNKNOWN;
uint32_t parent_inode_no = 0; // 父目录的 inode 编号
sub_path = path_parse(sub_path, name);
while(name[0]) { // 路径不许为空
/* 记录查找过的路径,但不能超过searched_path的长度512字节 */
ASSERT(strlen(searched_record->searched_path) < 512);
// 记录已存在的父目录
strcat(searched_record -> searched_path, "/");
strcat(searched_record -> searched_path, name);
if(search_dir_entry(cur_part, parent_dir, name, &dir_e)) { // 在所给的目录中查找文件
memset(name, 0, MAX_FILE_NAME_LEN);
if(sub_path) // 继续解析路径
sub_path = path_parse(sub_path, name);
if(FT_DIRECTORY == dir_e.f_type) { // 被打开的是目录
parent_inode_no = parent_dir -> inode -> i_no;
dir_close(parent_dir);
parent_dir = dir_open(cur_part, dir_e.i_no); // 更新父目录
searched_record -> parent_dir = parent_dir;
continue;
} else if(FT_REGULAR == dir_e.f_type) { // 被打开的是普通文件
searched_record -> file_type = FT_REGULAR;
return dir_e.i_no;
}
} else { // 找不到
/**
* 找不到目录项时,要留着 parent_dir 不要关闭
* 因为若需要创建文件的话,需要在 parent_dir 中创建
*/
return -1;
}
}
// ------------------------------------------
// 执行到这里,可说明:
// 1. 路径 pathname 已经被完整的解析过了,各级都存在
// 2. pathname 的最后一层路径不是普通文件,而是目录
// ------------------------------------------
/*
/a/b/c,它们都是目录,则此时:
parent_dir=c, 而不是 b
searched_path=/a/b/c
*/
dir_close(searched_record -> parent_dir); // 先关闭 c
searched_record -> parent_dir = dir_open(cur_part, parent_inode_no); // 重新打开 b
searched_record -> file_type = FT_DIRECTORY;
return dir_e.i_no;
}
注意:parent_dir 不可关闭,因为调用者或许需要使用父目录做操作,例如创建文件等,因此 parent_dir 最后需要由调用者关闭。
操作思路 —— 思维导图
创建文件
fs/file.c —— file_create
// 创建文件,成功则返回文件描述符
int32_t file_create(struct dir* parent_dir, char* filename, uint8_t flag) {
void* io_buf = sys_malloc(1024);
if(io_buf == NULL) {
printk("in file_creat: sys_malloc for io_buf failed\n");
return -1;
}
uint8_t rollback_step = 0; // 回滚点
// 为新文件分配 inode
int32_t inode_no = inode_bitmap_alloc(cur_part);
if(inode_no == -1) {
printk("in file_creat: allocate inode failed\n");
return -1;
}
/**
* 此 inode 要从堆中申请内存,不可申请局部变量,因为函数生命周期结束后会释放
* 因为 file_table 数组中的文件描述符的 inode 指针要指向它
*/
struct inode* new_file_inode = (struct inode*) sys_malloc(sizeof(struct inode));
if(new_file_inode == NULL) {
printk("file_create: sys_malloc for inode failded\n");
rollback_step = 1;
goto rollback;
}
inode_init(inode_no, new_file_inode); // 初始化 inode
// 得到全局的文件表空闲位
int fd_idx = get_free_slot_in_global();
if(fd_idx == -1) {
printk("exceed max open files\n");
rollback_step = 2;
goto rollback;
}
// 初始化文件表信息
file_table[fd_idx].fd_inode = new_file_inode;
file_table[fd_idx].fd_pos = 0;
file_table[fd_idx].fd_flag = flag;
file_table[fd_idx].fd_inode->write_deny = false;
struct dir_entry new_dir_entry;
memset(&new_dir_entry, 0, sizeof(struct dir_entry));
create_dir_entry(filename, inode_no, FT_REGULAR, &new_dir_entry); // create_dir_entry 只是内存操作不出意外,不会返回失败,因此不需要回滚
// 同步目录项
if(!sync_dir_entry(parent_dir, &new_dir_entry, io_buf)) {
printk("sync dir_entry to disk failed\n");
rollback_step = 3;
goto rollback;
}
memset(io_buf, 0, 1024);
// --------------------------------
// 因为 sync_dir_entry 会改变父目录 inode 中的信息,即 i_sectors 中的信息,因此需要同步父目录的 inode
inode_sync(cur_part, parent_dir -> inode, io_buf); // 同步父目录的 inode
// --------------------------------
memset(io_buf, 0, 1024);
inode_sync(cur_part, new_file_inode, io_buf); // 同步新文件的 inode
bitmap_sync(cur_part, inode_no, INODE_BITMAP); // 同步 inode_bitmap 位图
// 将新 inode 添加入已打开的 inode 链表中
list_push(&cur_part -> open_inodes, &new_file_inode -> inode_tag);
new_file_inode -> i_open_cnt = 1; // 打开次数设置为 1
sys_free(io_buf);
return pcb_fd_install(fd_idx); // 安装到当前进程的文件描述符数组中,并且返回下标
rollback:
switch(rollback_step) {
case 3:
memset(&file_table[fd_idx], 0, sizeof(struct file));
case 2:
sys_free(new_file_inode);
case 1:
bitmap_set(&cur_part -> inode_bitmap, inode_no, 0);
break;
}
sys_free(io_buf);
return -1;
}
fs/fs.c —— sys_open
// 打开或创建文件成功后,返回文件描述符
int32_t sys_open(const char* pathname, uint8_t flags) {
if(pathname[strlen(pathname)] == '/') {
printk("can`t open a directory %s\n",pathname);
return -1;
}
ASSERT(flags <= 7);
int32_t fd = -1; // 默认找不到
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
// 记录目录深度,便于判断中间某个目录不存在的情况
uint32_t pathname_depth = path_depth_cnt((char*) pathname);
// 先检查文件是否存在
int inode_no = search_file(pathname, &searched_record);
bool found = inode_no != -1 ? true : false;
if(searched_record.file_type == FT_DIRECTORY) {
printk("can`t open a direcotry with open(), use opendir() to instead\n");
dir_close(searched_record.parent_dir);
return -1;
}
uint32_t path_search_depth = path_depth_cnt(searched_record.searched_path);
// 判断是否把 pathname 各层目录都访问到了,即是否出现目录/文件找不到的情况
if(pathname_depth != path_search_depth) {
printk("cannot access %s: Not a directory, subpath %s is`t exist\n", \
pathname, searched_record.searched_path);
dir_close(searched_record.parent_dir);
return -1;
}
if(!found && !(flags & O_CREAT)) { // 找不到目标文件,且并不是要创建文件
printk("in path %s, file %s is`t exist\n", \
searched_record.searched_path, \
(strrchr(searched_record.searched_path, '/') + 1));
dir_close(searched_record.parent_dir);
return -1;
} else if(found && (flags & O_CREAT)) { // 找到目标文件,且是创建文件操作,表示:创建的文件已存在
printk("%s has already exist!\n", pathname);
dir_close(searched_record.parent_dir);
return -1;
}
switch(flags & O_CREAT) {
case O_CREAT:
printk("creating file.\n");
fd = file_create(searched_record.parent_dir, (strrchr(pathname, '/') + 1), flags);
dir_close(searched_record.parent_dir);
break;
// 其余为打开文件
default: // O_RDONLY, O_WRONLY, O_RDWR
fd = file_open(inode_no, flags);
}
return fd; // 此 fd 并非全局文件表 file_table 的下标,而是进程 PCB -> fd_table 的下标
}
fs/fs.c —— 初始化
// 初始化文件系统
void filesys_init() {
...
// 将当前分区的根目录打开
open_root_dir(cur_part);
// 初始化文件表
uint32_t fd_idx = 0;
while(fd_idx < MAX_FILE_OPEN) {
file_table[fd_idx++].fd_inode = NULL;
}
}
文件的打开与关闭
文件的打开
fs/file.c
// 打开编号为 inode_no 所对应的文件,成功返回文件描述符
int32_t file_open(uint32_t inode_no, uint8_t flag) {
// 先得到一个全局的文件表空闲位
int fd_idx = get_free_slot_in_global();
if(fd_idx == -1) {
printk("exceed max open files.\n");
return -1;
}
// 初始化文件表结构信息
file_table[fd_idx].fd_inode = inode_open(cur_part, inode_no);
file_table[fd_idx].fd_pos = 0; // 每次打开文件都要将偏移量指针重置为 0
file_table[fd_idx].fd_flag = flag;
// 文件是否可写,该变量指向 inode 是否可写的状态
bool* write_deny = &file_table[fd_idx].fd_inode -> write_deny;
if(flag & O_WRONLY || flag & O_RDWR) { // 只写 / 读写
enum intr_status old_status = intr_disable(); // 进入临界区前先关闭中断
if(!(*write_deny)) { // 写入
*write_deny = true; // 设置为 true,表示不可写,避免多个进程同时对此文件进行写入
intr_set_status(old_status);
} else { // 写入失败
intr_set_status(old_status);
printk("file can`t be write now, try again later\n");
return -1;
}
}
// 若是读文件或创建文件,则不用理会 write_deny,保持默认即可
return pcb_fd_install(fd_idx);
}
fs/fs.c
// 要修改的我已经在 创建文件 时就补全了
文件的关闭
fs/file.c
// 关闭文件
int32_t file_close(struct file* file) {
if(file == NULL) return -1;
file -> fd_inode -> write_deny = false;
inode_close(file -> fd_inode);
file -> fd_inode = NULL; // 重新使其文件结构可用
return 0;
}
fs/fs.c
// 将文件描述符转换为文件表下标
static uint32_t fd_local2global(uint32_t local_fd) {
struct task_struct* cur = running_thread();
int32_t global_fd = cur -> fd_table[local_fd];
ASSERT(global_fd >= 0 && global_fd < MAX_FILE_OPEN);
return (uint32_t) global_fd;
}
// 关闭文件描述符 fd 所指向的文件,成功返回0,失败返回-1
int32_t sys_close(int32_t fd) {
int32_t ret = -1;
if(fd > 2) { // 跨越标准输入输出
uint32_t _fd = fd_local2global(fd);
ret = file_close(&file_table[_fd]);
running_thread() -> fd_table[fd] = -1; // 将文件描述符重置为可用状态
}
return ret;
}
文件的写入
fs/file.c —— file_write
// 将 buf 中的 count 个字节写入到 file,成功则返回字节数,失败返回-1
int32_t file_write(struct file* file, const void* buf, uint32_t count) {
if((file -> fd_inode -> i_size + count) > (BLOCK_SIZE * 140)) { // 文件目前最大只支持 512*140 字节
printk("exceed max file_size 71680 bytes, write file failed\n");
return -1;
}
uint8_t* io_buf = sys_malloc(BLOCK_SIZE);
if(io_buf == NULL) {
printk("file_write: sys_malloc for io_buf failed\n");
return -1;
}
uint32_t* all_blocks = (uint32_t*) sys_malloc(BLOCK_SIZE + 48);
if(all_blocks == NULL) {
printk("file_write: sys_malloc for all_blocks failed\n");
return -1;
}
const uint8_t* src = buf; // 用src指向buf中待写入的数据
uint32_t bytes_written = 0; // 用来记录已写入数据大小
uint32_t size_left = count; // 用来记录未写入数据大小
int32_t block_lba = -1; // 块地址
uint32_t block_bitmap_idx = 0; // 用来记录block对应于block_bitmap中的索引,做为参数传给bitmap_sync
uint32_t sec_idx; // 用来索引扇区
uint32_t sec_lba; // 扇区地址
uint32_t sec_off_bytes; // 扇区内字节偏移量
uint32_t sec_left_bytes; // 扇区内剩余字节量
uint32_t chunk_size; // 每次写入硬盘的数据块大小
int32_t indirect_block_table; // 用来获取一级间接表地址
uint32_t block_idx; // 块索引
// 判断文件是否是第一次写,若是,则先分配一个块
if(file -> fd_inode -> i_sectors[0] == 0) {
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1) {
printk("file_write: block_bitmap_alloc failed\n");
return -1;
}
file -> fd_inode -> i_sectors[0] = block_lba;
// 同步
block_bitmap_idx = block_lba - cur_part -> sb -> data_start_lba;
ASSERT(block_bitmap_idx != 0);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
}
// 写入 count 个字节前,该文件已占的块数
uint32_t file_has_used_blocks = file -> fd_inode -> i_size / BLOCK_SIZE + 1;
// 存储 count 字节后,该文件将占用的块数
uint32_t file_will_use_blocks = (file -> fd_inode -> i_size + count) / BLOCK_SIZE + 1;
ASSERT(file_will_use_blocks <= 140);
// 通过此增量判断是否需要分配新的扇区,为0则不需要分配
uint32_t add_blocks = file_will_use_blocks - file_has_used_blocks;
// 我得说句实话,感觉这样判断完全没必要,它们都以块为单位,得到的差不就是需要分配的新的块数吗?不需要分配的话,那不就表示两者块数相同吗?
// -------------------------------------
// 将所有的数据块地址收集到 all_blocks 中
// -------------------------------------
if(add_blocks == 0) { // 不需要分配新的块
if(file_has_used_blocks <= 12) { // 都属于12个直接块
block_idx = file_has_used_blocks - 1; // 指向最后一个数据扇区(即数据块)
all_blocks[block_idx] = file -> fd_inode -> i_sectors[block_idx];
} else {
ASSERT(file -> fd_inode -> i_sectors[12] != 0);
indirect_block_table = file -> fd_inode -> i_sectors[12]; // 得到一级间接块索引表的地址
ide_read(cur_part -> my_disk, indirect_block_table, all_blocks + 12, 1);
}
} else { // 需要分配新的块
if(file_will_use_blocks <= 12) { // 所需要的块数直接块够用
// 先将有剩余空间的扇区,也就是最后那个块,先写入 all_blocks
block_idx = file_has_used_blocks - 1;
ASSERT(file -> fd_inode -> i_sectors[block_idx] != 0);
all_blocks[block_idx] = file -> fd_inode -> i_sectors[block_idx];
// 再将未来需要用到的块写入 all_blocks
block_idx = file_has_used_blocks;
while(block_idx < file_will_use_blocks) {
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1) {
printk("file_write: block_bitmap_alloc for situation 1 failed\n");
return -1;
}
// 写文件时,不应该出现块未使用,但已经分配扇区的情况,当文件删除时,就会把块地址请0
ASSERT(file -> fd_inode -> i_sectors[block_idx] == 0); // 确保尚未分配扇区地址
file -> fd_inode -> i_sectors[block_idx] = all_blocks[block_idx] = block_lba;
// 同步
block_bitmap_idx = block_lba - cur_part -> sb -> data_start_lba;
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
block_idx++; // 下一个分配的新块
}
} else if(file_has_used_blocks <= 12 && file_will_use_blocks > 12) { // 已经使用的块数在12块之内,新增若干后,超过了12块,需要分配一级间接块
// 道理同上,记录最后一个块,因为这个块数据未满
block_idx = file_has_used_blocks - 1;
all_blocks[block_idx] = file -> fd_inode -> i_sectors[block_idx];
// 创建一级间接块索引表
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1) {
printk("file_write: block_bitmap_alloc for situation 2 failed\n");
return -1;
}
ASSERT(file -> fd_inode -> i_sectors[12] == 0); // 确保一级间接块表未分配
indirect_block_table = file -> fd_inode -> i_sectors[12] = block_lba;
block_idx = file_has_used_blocks; // 第一个未使用的块,即本文件最后一个已使用的直接块的下一块
while(block_idx < file_will_use_blocks) {
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1) {
printk("file_write: block_bitmap_alloc for situation 2 failed\n");
return -1;
}
if(block_idx < 12) {
ASSERT(file -> fd_inode -> i_sectors[block_idx] == 0); // 确保尚未分配扇区地址
file -> fd_inode -> i_sectors[block_idx] = all_blocks[block_idx] = block_lba;
} else {
all_blocks[block_idx] = block_lba;
}
block_bitmap_idx = block_lba - cur_part -> sb -> data_start_lba;
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
block_idx++; // 下一个新扇区
}
// 同步一级间接表的所有间接块
ide_write(cur_part -> my_disk, indirect_block_table, all_blocks + 12, 1);
} else if(file_has_used_blocks > 12) { // 已经使用完了12个直接块,要创建一级间接块表中的间接块
ASSERT(file -> fd_inode -> i_sectors[12] != 0); // 确保一级间接块表存在
indirect_block_table = file -> fd_inode -> i_sectors[12]; // 获取一级间接块表
// 读取所有间接块
ide_read(cur_part -> my_disk, indirect_block_table, all_blocks + 12, 1);
block_idx = file_has_used_blocks; // 已使用的间接块的下一块
while(block_idx < file_will_use_blocks) {
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1) {
printk("file_write: block_bitmap_alloc for situation 3 failed\n");
return -1;
}
all_blocks[block_idx++] = block_lba;
// 同步
block_bitmap_idx = block_lba - cur_part -> sb -> data_start_lba;
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
}
// 同步所有间接块
ide_write(cur_part -> my_disk, indirect_block_table, all_blocks + 12, 1);
}
}
bool first_write_block = true; // 含有剩余空间的扇区标识
file -> fd_pos = file -> fd_inode -> i_size - 1;
while(bytes_written < count) { // 直到写完所有数据
memset(io_buf, 0, BLOCK_SIZE);
sec_idx = file -> fd_inode -> i_size / BLOCK_SIZE;
sec_lba = all_blocks[sec_idx];
sec_off_bytes = file -> fd_inode -> i_size % BLOCK_SIZE;
sec_left_bytes = BLOCK_SIZE - sec_off_bytes;
// 判断此次写入硬盘的数据大小
// 若剩余数据 < 扇区内剩余的内容 => size_left
// 否则 => sec_left_bytes
chunk_size = size_left < sec_left_bytes ? size_left : sec_left_bytes;
if(first_write_block) { // 只需要执行一次,只为处理最开始记录的 all_blocks 中的那个块
// 因为唯独这个扇区的数据可能本就不满一个扇区,也正是因为它有数据,所以要先读
ide_read(cur_part -> my_disk, sec_lba, io_buf, 1);
first_write_block = false; // 往后不再执行
}
memcpy(io_buf + sec_off_bytes, src, chunk_size);
ide_write(cur_part -> my_disk, sec_lba, io_buf, 1);
printk("file write at lba 0x%x\n", sec_lba); //调试,完成后去掉
src += chunk_size; // 对数据源偏移
file -> fd_inode -> i_size += chunk_size; // 更新文件大小
file -> fd_pos += chunk_size; // 文件位置指针更新
bytes_written += chunk_size;
size_left -= chunk_size;
}
inode_sync(cur_part, file -> fd_inode, io_buf);
sys_free(all_blocks);
sys_free(io_buf);
return bytes_written;
}
改进 sys_write 和 write 系统调用
fs/fs.c
// 将 buf 中连续 count 个字节写入文件描述符 fd,成功返回字节数,失败返回-1
int32_t sys_write(int32_t fd, const void* buf, uint32_t count) {
if(fd < 0) {
printk("sys_write: fd error.\n");
return -1;
}
if(fd == stdout_no) {
char tmp_buf[1024] = {0};
memcpy(tmp_buf, buf, count);
console_put_str(tmp_buf);
return count;
}
uint32_t _fd = fd_local2global(fd);
struct file* wr_file = &file_table[_fd];
if(wr_file -> fd_flag & O_WRONLY || wr_file -> fd_flag & O_RDWR) {
uint32_t bytes_written = file_write(wr_file, buf, count);
return bytes_written;
} else {
console_put_str("sys_write: not allowed to write file without flag O_RDWR or O_WRONLY.\n");
return -1;
}
}
lib/user/syscall.c
// 打印字符串
uint32_t write(int32_t fd, const void* buf, uint32_t count) {
return _syscall3(SYS_WRITE, fd, buf, count);
}
lib/stdio.c
// 格式化输出字符串 format
uint32_t printf(const char* format, ...) {
va_list args;
va_start(args, format); // 将 args 指向 format
char buf[1024] = {0}; // 用于存储拼接后的字符串
vsprintf(buf, format, args);
va_end(args);
return write(1, buf, strlen(buf));
}
读取文件
fs/file.c —— file_read
// 从文件 file 中读取 count 个字节到 buf,返回读取的字节数,若到末尾则返回-1
int32_t file_read(struct file* file, void* buf, uint32_t count) {
uint8_t* buf_dst = (uint8_t*) buf;
uint32_t size = count, size_left = size;
// 若要读取的字节数超过了文件本身的大小,则全部读取
if((file -> fd_pos + count) > file -> fd_inode -> i_size) {
size = file -> fd_inode -> i_size - file -> fd_pos;
size_left = size;
if(size == 0) return -1; // 到文件末尾了
}
uint8_t* io_buf = sys_malloc(BLOCK_SIZE);
if(io_buf == NULL) {
printk("file_read: sys_malloc for io_buf failed\n");
}
uint32_t* all_blocks = (uint32_t*) sys_malloc(BLOCK_SIZE + 48);
if(all_blocks == NULL) {
printk("file_read: sys_malloc for all_blocks failed\n");
return -1;
}
uint32_t block_read_start_idx = file -> fd_pos / BLOCK_SIZE; // 从哪个数据块开始读
uint32_t block_read_end_idx = (file -> fd_pos + size) / BLOCK_SIZE; // 最终要读取到哪儿,落在哪个块
uint32_t read_blocks = block_read_start_idx - block_read_end_idx; // 差为0,则表示要读取的起始和终止都在同一个块中,只读取一个扇区
ASSERT(block_read_start_idx < 139 && block_read_end_idx < 139);
int32_t indirect_block_table; // 一级间接块表的地址
uint32_t block_idx; // 待读取的块
// 开始构建 all_blocks
if(read_blocks == 0) { // 读取同一个扇区
ASSERT(block_read_start_idx == block_read_end_idx);
if(block_read_end_idx < 12) { // 直接块
block_idx = block_read_end_idx;
all_blocks[block_idx] = file -> fd_inode -> i_sectors[block_idx];
} else { // 间接块
indirect_block_table = file -> fd_inode -> i_sectors[block_idx];
ide_read(cur_part -> my_disk, indirect_block_table, all_blocks + 12, 1);
}
} else { // 读取多个扇区
if(block_read_end_idx < 12) { // 起始和终止块都在12个直接块内
block_idx = block_read_start_idx;
while(block_idx <= block_read_end_idx) {
all_blocks[block_idx] = file -> fd_inode -> i_sectors[block_idx];
block_idx++;
}
} else if(block_read_start_idx < 12 && block_read_end_idx >= 12) { // 起始块在直接块中,最终要落到间接块中
block_idx = block_read_start_idx;
while(block_idx < 12) {
all_blocks[block_idx] = file -> fd_inode -> i_sectors[block_idx];
block_idx++;
}
ASSERT(file -> fd_inode -> i_sectors[12] != 0);
indirect_block_table = file -> fd_inode -> i_sectors[12];
ide_read(cur_part -> my_disk, indirect_block_table, all_blocks + 12, 1);
} else { // 起始和终止都是间接块中
ASSERT(file -> fd_inode -> i_sectors[12] != 0);
indirect_block_table = file -> fd_inode -> i_sectors[12];
ide_read(cur_part -> my_disk, indirect_block_table, all_blocks + 12, 1);
}
}
// 读取数据
uint32_t sec_idx, sec_lba, sec_off_bytes, sec_left_bytes, chunk_size;
uint32_t bytes_read = 0;
while(bytes_read < size) {
sec_idx = file -> fd_pos / BLOCK_SIZE;
sec_lba = all_blocks[sec_idx];
sec_off_bytes = file -> fd_pos % BLOCK_SIZE;
sec_left_bytes = BLOCK_SIZE - sec_off_bytes;
chunk_size = size_left < sec_left_bytes ? size_left : sec_left_bytes;
memset(io_buf, 0, BLOCK_SIZE);
ide_read(cur_part -> my_disk, sec_lba, io_buf, 1);
memcpy(buf_dst, io_buf + sec_off_bytes, chunk_size);
buf_dst += chunk_size;
file -> fd_pos += chunk_size;
bytes_read += chunk_size;
size_left -= chunk_size;
}
sys_free(all_blocks);
sys_free(io_buf);
return bytes_read;
}
fs/fs.c
// 从文件描述符 fd 指向的文件中读取 count 个字节到 buf,成功返回读取的字节数,到文末返回-1
int32_t sys_read(int32_t fd, void* buf, uint32_t count) {
if(fd < 0) {
printk("sys_read: fd error\n");
return -1;
}
ASSERT(buf != NULL);
uint32_t _fd = fd_local2global(fd);
return file_read(&file_table[_fd], buf, count);
}
实现文件读写指针定位
fs/fs.h
/* 文件读写位置偏移量 */
enum whence {
SEEK_SET = 1,
SEEK_CUR,
SEEK_END
};
fs.fs.c
// 重置文件偏移指针,返回新的偏移量
int32_t sys_lseek(int32_t fd, int32_t offset, uint8_t whence) {
if(fd < 0) {
printk("sys_lseek: fd error\n");
return -1;
}
ASSERT(whence > 0 && whence < 4);
uint32_t _fd = fd_local2global(fd);
struct file* pf = &file_table[_fd];
int32_t new_pos = 0;
int32_t file_size = (int32_t) pf -> fd_inode -> i_size;
switch(whence) {
case SEEK_SET: // 相对于文件开头偏移 offset
new_pos = offset;
break;
case SEEK_CUR: // 相对于当前位置 fd_pos 偏移 offset
new_pos = (int32_t) pf -> fd_pos + offset;
break;
case SEEK_END: // 相对于文件结尾偏移 offset
new_pos = file_size + offset;
break;
}
if(new_pos < 0 || new_pos > (file_size - 1)) return -1;
pf -> fd_pos = new_pos;
return pf -> fd_pos;
}
文件删除
回收 inode
fs/inode.c
// 将硬盘分区 part 上的 inode 清空(该函数并没有存在的必要,因为这些内容后面会被新内容覆盖)
void inode_delete(struct partition* part, uint32_t inode_no, void* io_buf) {
ASSERT(inode_no < 4096);
struct inode_position inode_pos;
inode_locate(part, inode_no, &inode_pos);
ASSERT(inode_pos.sec_lba <= (part -> start_lba + part -> sec_cnt));
char* inode_buf = (char*) io_buf;
if(inode_pos.two_sec) {
ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 2);
memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
} else { // 未跨扇区,只读入1个扇区就好
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
}
}
// 回收 inode 的数据块和 inode 本身
void inode_release(struct partition* part, uint32_t inode_no) {
struct inode* inode_to_del = inode_open(part, inode_no);
ASSERT(inode_to_del -> i_no == inode_no);
uint8_t block_idx = 0, block_cnt = 12;
uint32_t block_bitmap_idx;
uint32_t all_blocks[140] = {0}; // 12个直接块+128个间接块
// 先收集12个直接块
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 -> my_disk, inode_to_del -> i_sectors[12], all_blocks + 12, 1);
block_cnt = 140;
// 回收一级间接块表
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);
}
// 开始逐渐回收
block_idx = 0;
while(block_idx < block_cnt) {
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);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
}
block_idx++;
}
// 回收 inode
// 注:inode_no 编号便是 inode 在位图中的位置
bitmap_set(&part -> inode_bitmap, inode_no, 0);
bitmap_sync(cur_part, inode_no, INODE_BITMAP);
/****** 以下inode_delete是调试用的 ******
* 此函数会在inode_table中将此inode清0,
* 但实际上是不需要的,inode分配是由inode位图控制的,
* 硬盘上的数据不需要清0,可以直接覆盖*/
void* io_buf = sys_malloc(1024);
inode_delete(part, inode_no, io_buf);
sys_free(io_buf);
/***********************************************/
inode_close(inode_to_del);
}
删除目录项
fs/dir.c
// 将分区 part 中目录 pdir 中编号为 inode_no 的目录项删除
bool delete_dir_entry(struct partition* part, struct dir* pdir, uint32_t inode_no, void* io_buf) {
struct inode* dir_inode = pdir -> inode;
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; // 将 dir_e 指向 io_buf
struct dir_entry* dir_entry_found = NULL; // 是否找到目录项
uint8_t dir_entry_idx, dir_entry_cnt; // 当前是哪个目录项,总目录项
bool is_dir_first_block = false; // 目录的第1个块
// 遍历所有块,寻找目录项
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);
// 获取目录项
ide_read(part -> my_disk, all_blocks[block_idx], io_buf, 1);
// 遍历所有目录项,统计该扇区的目录项数量以及是否有待删除的目录项
while(dir_entry_idx < dir_entrys_per_sec) {
if((dir_e + dir_entry_idx) -> f_type == FT_UNKNOWN) continue;
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) { // 找到了要删除的目录项
ASSERT(dir_entry_found == NULL); // 确保 inode_no 是唯一的
dir_entry_found = dir_e + dir_entry_idx; // 记录下来找到的这个目录项
} // 即使找到了要删除的目标,但此时还不能停止,而是需要继续统计总共的目录项数量
}
dir_entry_idx++;
}
if(dir_entry_found == NULL) { // 若未找到目标项,则继续去下一个扇区查找
block_idx++;
continue;
}
// 找到目标项后,清除该目标项并判断是否回收该扇区
ASSERT(dir_entry_cnt >= 1);
if(dir_entry_cnt == 1 && !is_dir_first_block) { // 除目录第一个扇区外,若该扇区上只有该目录项自己,则将整个扇区回收
// 回收该块
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);
// 将块地址从 i_sectors 或索引表(间接块表)中去掉
if(block_idx < 12) {
dir_inode -> i_sectors[block_idx] = 0;
} else {
uint32_t indirect_blocks = 0;
uint32_t indirect_blocks_idx = 12;
while(indirect_blocks_idx < 12) {
if(all_blocks[indirect_blocks_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 { // 间接块索引表中就当前这一个间接块,直接把间接索引表所在的块一并回收了,然后擦除表的地址
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);
// 擦除间接块索引表的地址
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);
}
// 更新 inode 结点信息并同步到硬盘
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;
}
实现 sys_unlink
fs/fs.c
// 删除文件(非目录),成功返回0,失败返回-1
int32_t sys_unlink(const char* pathname) {
ASSERT(strlen(pathname) < MAX_PATH_LEN);
// 检查待删除的文件是否存在
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;
}
// 检查是否被打开
// Tips:正在使用的文件不可删除
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);
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;
}
struct dir* parent_dir = searched_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(searched_record.parent_dir);
return 0;
}
创建目录
fs/fs.c
// 创建目录 pathname,成功返回0,失败返回-1
int32_t sys_mkdir(const char* pathname) {
uint8_t rollback_step = 0;
void* io_buf = sys_malloc(SECTOR_SIZE * 2);
if(io_buf == NULL) {
printk("sys_mkdir: sys_malloc for io_buf failed\n");
return -1;
}
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = -1;
inode_no = search_file(pathname, &searched_record);
if(inode_no != -1) { // 文件或目录已存在
printk("sys_mkdir: file or directory %s exist!\n", pathname);
rollback_step = 1;
goto rollback;
} else { // 中间目录是否缺失
uint32_t pathname_depth = path_depth_cnt((char*) pathname);
uint32_t path_searched_depth = path_depth_cnt(searched_record.searched_path);
// 判断中间目录是否缺失
if(pathname_depth != path_searched_depth) {
printk("sys_mkdir: can`t access %s, subpath %s is`t exist\n", pathname, searched_record.searched_path);
rollback_step = 1;
goto rollback;
}
}
struct dir* parent_dir = searched_record.parent_dir;
char* dirname = strrchr(searched_record.searched_path, '/') + 1;
inode_no = inode_bitmap_alloc(cur_part);
if(inode_no == -1) {
printk("sys_mkdir: allocate inode failed\n");
rollback_step = 1;
goto rollback;
}
struct inode new_dir_inode;
inode_init(inode_no, &new_dir_inode);
uint32_t block_bitmap_idx = 0;
int32_t block_lba = -1;
// 为目录分配一个块,用来写入目录项 . 和 ..
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1) {
printk("sys_mkdir: block_bitmap_alloc for create directory failed\n");
rollback_step = 2;
goto rollback;
}
new_dir_inode.i_sectors[0] = block_lba;
block_bitmap_idx = block_lba - cur_part -> sb -> data_start_lba;
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
// 写入目录项 . 和 ..
memset(io_buf, 0, SECTOR_SIZE * 2);
struct dir_entry* p_de = (struct dir_entry*) io_buf;
// 初始化当前目录 .
memcpy(p_de -> filename, ".", 1);
p_de -> i_no = inode_no;
p_de -> f_type = FT_DIRECTORY;
p_de++;
// 初始化目录项 ..
memcpy(p_de -> filename, "..", 2);
p_de -> i_no = parent_dir -> inode -> i_no;
p_de -> f_type = FT_DIRECTORY;
// 写入目录项
ide_write(cur_part -> my_disk, new_dir_inode.i_sectors[0], io_buf, 1);
new_dir_inode.i_size = 2 * cur_part -> sb -> dir_entry_size;
// 在父目录中添加新的目录项(也就是新创建的那个目录的目录项)
struct dir_entry new_dir_entry;
memset(&new_dir_entry, 0, sizeof(struct dir_entry));
create_dir_entry(dirname, inode_no, FT_DIRECTORY, &new_dir_entry);
memset(io_buf, 0, SECTOR_SIZE * 2);
// 父目录将新目录项写入
if(!sync_dir_entry(parent_dir, &new_dir_entry, io_buf)) {
printk("sys_mkdir: sync_dir_entry to disk failed!\n");
rollback_step = 2;
goto rollback;
}
// 同步父目录 inode
memset(io_buf, 0, SECTOR_SIZE * 2);
inode_sync(cur_part, parent_dir -> inode, io_buf);
// 同步新创建的目录 inode
memset(io_buf, 0, SECTOR_SIZE * 2);
inode_sync(cur_part, &new_dir_inode, io_buf);
// 将位图同步
bitmap_sync(cur_part, inode_no, INODE_BITMAP);
sys_free(io_buf);
dir_close(searched_record.parent_dir);
return 0;
rollback:
switch(rollback_step) {
case 2:
bitmap_set(&cur_part -> inode_bitmap, inode_no, 0);
case 1:
dir_close(searched_record.parent_dir);
}
sys_free(io_buf);
return -1;
}
遍历目录
打开目录和关闭目录
fs/fs.c
// 打开目录,返回目录指针,失败返回NULL
struct dir* sys_opendir(const char* name) {
ASSERT(strlen(name) < MAX_PATH_LEN);
// 若是根目录,直接返回
if(name[0] == '/' && (name[1] == 0 || name[0] == ".")) return &root_dir;
// 先检查待打开的目录是否存在
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = search_file(name, &searched_record);
struct dir* ret = NULL;
if(inode_no == -1) { // 找不到,提示路径不存在
printk("In %s, sub path %s not exist\n", name, searched_record.searched_path);
} else {
if(searched_record.file_type == FT_REGULAR) { // 目标是文件类型为普通文件,这不合法
printk("%s is regular file!\n", name);
} else if(searched_record.file_type == FT_DIRECTORY) { // 找到了目标目录
ret = dir_open(cur_part, inode_no);
}
}
dir_close(searched_record.parent_dir);
return ret;
}
// 关闭目录,返回0,失败-1
int32_t sys_closedir(struct dir* dir) {
int32_t ret = -1;
if(dir != NULL) {
dir_close(dir);
ret = 0;
}
return ret;
}
读取一个目录项
fs/dir.c
// 读取目录,返回一个目录项
struct dir_entry* dir_read(struct dir* dir) {
struct dir_entry* dir_e = (struct dir_entry*) dir -> dir_buf;
struct inode* dir_inode = dir -> inode;
uint32_t all_blocks[140] = {0}, block_cnt = 12;
uint32_t block_idx = 0, dir_entry_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(cur_part -> my_disk, dir_inode -> i_sectors[12], all_blocks + 12, 1);
block_cnt = 140;
}
block_idx = 0;
uint32_t cur_dir_entry_pos = 0; // 当前目录项的偏移,此项的作用是与 dir_pos 进行比较,判断是否之前已经返回了某些目录项
uint32_t dir_entry_size = cur_part -> sb -> dir_entry_size;
uint32_t dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size;
while(block_idx < block_cnt) {
if(dir -> dir_pos >= dir -> inode -> i_size) // 若指针位置超过了文件大小,则说明已经遍历完了该 inode 的所有目录项
return NULL;
if(all_blocks[block_idx] == 0) {
block_idx++;
continue;
}
memset(dir_e, 0, SECTOR_SIZE);
ide_read(cur_part -> my_disk, all_blocks[block_idx], dir_e, 1);
dir_entry_idx = 0;
// 遍历该扇区的所有目录项
while(dir_entry_idx < dir_entrys_per_sec) {
if((dir_e + dir_entry_idx) -> f_type == FT_UNKNOWN) { // 没意义的不要
dir_entry_idx++;
continue;
}
if(cur_dir_entry_pos < dir -> dir_pos) { // 遍历过的不要
cur_dir_entry_pos += dir_entry_size;
dir_entry_idx++;
continue;
}
ASSERT(cur_dir_entry_pos == dir -> dir_pos);
dir -> dir_pos += dir_entry_size; // 更新位置,即下一个返回的目录项地址
return dir_e + dir_entry_idx;
}
block_idx++;
}
return NULL;
}
实现 sys_readdir 及 sys_rewinddir
fs/fs.c
// 读取目录 dir 中的 1 个目录项,返回目录项地址,否则NULL
struct dir_entry* sys_readdir(struct dir* dir) {
ASSERT(dir != NULL);
return dir_read(dir);
}
// 把目录 dir 的指针 dir_pos 置为 0
void sys_rewinddir(struct dir* dir) {
dir -> dir_pos = 0;
}
删除目录
删除目录与判断空目录
fs/dir.c
// 判断目录是否为空
bool dir_is_empty(struct dir* dir) {
struct inode* dir_inode = dir -> inode;
return (dir_inode -> i_size == cur_part -> sb -> dir_entry_size * 2);
}
// 在父目录中删除子目录 child_dir
int32_t dir_remove(struct dir* parent_dir, struct dir* child_dir) {
struct inode* child_dir_inode = child_dir -> inode;
// .和..只存在 i_sectors[0] 中,因此其它删除应该都要为空
int32_t block_idx = 1; // 跨越 0
while(block_idx < 13) {
ASSERT(child_dir -> inode -> i_sectors[block_idx] == 0);
block_idx++;
}
void* io_buf = sys_malloc(SECTOR_SIZE * 2);
if(io_buf == NULL) {
printk("dir_remove: malloc for io_buf failed\n");
return -1;
}
// 在父目录中删除子目录所对应的目录项
delete_dir_entry(cur_part, parent_dir, child_dir_inode -> i_no, io_buf);
// 回收 inode 中 i_sectors 所占用的扇区,并同步
inode_release(cur_part, child_dir_inode -> i_no);
sys_free(io_buf);
return 0;
}
实现 sys_rmdir
fs/fs.c
// 删除空目录,成功0,失败-1
int32_t sys_rmdir(const char* pathname) {
// 先检查文件是否存在
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);
int retval = -1;
if(inode_no == -1) { // 已存在
printk("In %s, sub path %s not exist\n", pathname, searched_record.searched_path);
} else {
if(searched_record.file_type == FT_REGULAR) { // 是普通文件类型
printk("%s is regular file!\n", pathname);
} else {
struct dir* dir = dir_open(cur_part, inode_no);
if(!dir_is_empty(dir)) { // 判断目录是否为空
printk("dir %s is not empty, it is not allowed to delete a nonempty directory!\n", pathname);
} else { // 空目录,进行删除
if(!dir_remove(searched_record.parent_dir, dir)) retval = 0;
}
dir_close(dir);
}
}
dir_close(searched_record.parent_dir);
return retval;
}
任务的工作目录
基础函数
fs/fs.c
// 获得父目录的 inode 编号
static uint32_t get_parent_dir_inode_nr(uint32_t child_inode_nr, void* io_buf) {
struct inode* child_dir_inode = inode_open(cur_part, child_inode_nr);
uint32_t block_lba = child_dir_inode -> i_sectors[0];
ASSERT(block_lba >= cur_part -> sb -> data_start_lba);
inode_close(child_dir_inode);
ide_read(cur_part -> my_disk, block_lba, io_buf, 1);
struct dir_entry* dir_e = (struct dir_entry*) io_buf;
// 第0个目录项是., 第1个目录项是 ..
ASSERT(dir_e[1].i_no < 4096 && dir_e[1].f_type == FT_DIRECTORY);
return dir_e[1].i_no;
}
// 在 inode 编号为 p_inode_nr 的目录中查到 inode 编号为 c_inode_nr 的子目录名称,将名字存入 path
static int get_child_dir_name(uint32_t p_inode_nr, uint32_t c_inode_nr, char* path, void* io_buf) {
struct inode* parent_dir_inode = inode_open(cur_part, p_inode_nr);
uint8_t block_idx = 0;
uint32_t all_blocks[140] = {0}, block_cnt = 12;
while(block_idx < 12) {
all_blocks[block_idx] = parent_dir_inode -> i_sectors[block_idx];
block_idx++;
}
if(parent_dir_inode -> i_sectors[12]) {
ide_read(cur_part -> my_disk, parent_dir_inode -> i_sectors[12], all_blocks + 12, 1);
block_cnt = 140;
}
inode_close(parent_dir_inode);
struct dir_entry* dir_e = (struct dir_entry*) io_buf;
uint32_t dir_entry_size = cur_part -> sb -> dir_entry_size;
uint32_t dir_entrys_per_sec = 512 / dir_entry_size;
block_idx = 0;
// 遍历所有块
while(block_idx < block_cnt) {
if(all_blocks[block_idx] != 0) {
ide_read(cur_part -> my_disk, all_blocks[block_idx], io_buf, 1);
uint8_t dir_e_idx = 0;
// 遍历该扇区的所有目录项
while(dir_e_idx < dir_entrys_per_sec) {
if((dir_e + dir_e_idx) -> i_no == c_inode_nr) { // 找到目标
strcat(path, "/");
strcat(path, (dir_e + dir_e_idx) -> filename);
return 0;
}
dir_e_idx++;
}
block_idx++;
}
}
return -1;
}
sys_getcwd
fs/fs.c
thread/thread.h
// 进程或线程的 PCB,即程序控制块
struct task_struct {
...
uint32_t cwd_inode_nr; // 进程所在的工作目录的 inode 编号
...
};
thread/thread.c
// 初始化线程基本信息
void init_thread(struct task_struct* pthread, char* name, int prio) {
pthread -> cwd_inode_nr = 0; // 以根目录作为默认的工作路径
pthread -> stack_magic = 0x19870916; // 自定义的魔数,用于栈边界判断
}
fs/fs.c
// 将当前工作目录绝对路径写入 buf
char* sys_getcwd(char* buf, uint32_t size) {
ASSERT(buf != NULL);
void* io_buf = sys_malloc(SECTOR_SIZE);
if(io_buf == NULL) {
return NULL;
}
struct task_struct* cur_thread = running_thread();
int32_t parent_inode_nr = 0;
int32_t child_inode_nr = cur_thread -> cwd_inode_nr;
ASSERT(child_inode_nr >= 0 && child_inode_nr < 4096);
if(child_inode_nr == 0) {
buf[0] = '/';
buf[1] = 0;
return buf;
}
memset(buf, 0, size);
char full_path_reverse[MAX_PATH_LEN] = {0};
// 从下往上查找父目录,直到找到根目录为止
while(child_inode_nr) {
parent_inode_nr = get_parent_dir_inode_nr(child_inode_nr, io_buf);
if(get_child_dir_name(parent_inode_nr, child_inode_nr, full_path_reverse, io_buf) == -1) {
// 未找到对应的名字
sys_free(io_buf);;
return NULL;
}
child_inode_nr = parent_inode_nr;
}
ASSERT(strlen(full_path_reverse) <= size);
/**
* 至此 full_path_reverse 中的路径是反着的
* 即子目录在左边,父目录在右边
* 现在将其逆转
*/
char* last_slash; //
while((last_slash = strrchr(full_path_reverse, '/'))) {
uint16_t len = strlen(buf);
strcpy(buf + len, last_slash);
*last_slash = 0;
}
sys_free(io_buf);
return buf;
}
sys_chdir
fs/fs.c
// 更改当前工作目录为绝对路径 path,成功0,失败-1
int32_t sys_chdir(const char* path) {
int32_t ret = -1;
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = search_file(path, &searched_record);
if(inode_no != -1) {
if(searched_record.file_type == FT_DIRECTORY) {
running_thread() -> cwd_inode_nr = inode_no;
ret = 0;
} else {
printk("sys_chdir: %s is regular file or other!\n", path);
}
}
dir_close(searched_record.parent_dir);
return ret;
}
文件属性
fs/fs.h
// 文件属性结构
struct stat {
uint32_t st_ino; // inode 编号
uint32_t st_size; // 尺寸
enum file_types st_filetype; // 文件类型
};
fs/fs.c
// 获取文件属性信息,存入到 buf 中,成功0,失败-1
int32_t sys_stat(const char* path, struct stat* buf) {
// 找的是根目录
if(!strcmp(path, "/") || !strcmp(path, "/.") || !strcmp(path, "/..")) {
buf -> st_filetype = FT_DIRECTORY;
buf -> st_ino = 0;
buf -> st_size = root_dir.inode -> i_size;
return 0;
}
int ret = -1;
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = search_file(path, &searched_record);
if(inode_no != -1) { // 找到了
struct inode* obj_inode = inode_open(cur_part, inode_no);
buf -> st_filetype = searched_record.file_type;
buf -> st_ino = inode_no;
buf -> st_size = obj_inode -> i_size;
ret = 0;
inode_close(obj_inode);
} else { // 未找到
printk("sys_stat: %s not found.\n", path);
}
dir_close(searched_record.parent_dir);
return ret;
}
参考资料
- 文件描述符
- 文件系统inode详解