《操作系统真象还原》第14章(中)
本篇博客对应书本14.4~14.10节
步骤:
1.文件操作相关的基础函数
2.创建文件
3.文件打开与关闭
4.实现文件写入
5.读取文件
6.实现文件读写指针定位功能
7.实现文件删除功能
1.文件操作相关的基础函数+2.创建文件
几乎涉及fs/下的全部文件:
①fs/fs.h:

1 #ifndef __FS_FS_H
2 #define __FS_FS_H
3 #include "stdint.h"
4 #include "ide.h"
5
6 #define MAX_FILES_PER_PART 4096
7 #define BITS_PER_SECTOR 4096
8 #define SECTOR_SIZE 512
9 #define BLOCK_SIZE SECTOR_SIZE
10 #define MAX_PATH_LEN 512
11
12 /* 文件类型 */
13 enum file_types{
14 FT_UNKNOWN, // 不支持的文件类型
15 FT_REGULAR, // 普通文件
16 FT_DIRECTORY // 目录
17 };
18
19 /* 打开文件的选项 */
20 enum oflags{
21 O_RDONLY, // 只读
22 O_WRONLY, // 只写
23 O_RDWR, // 读写
24 O_CREAT=4 // 新建
25 };
26
27 /* 用来记录查找文件过程中已找到的上级路径,也就是查找文件过程中“走过的地方” */
28 struct path_search_record{
29 char searched_path[MAX_PATH_LEN]; // 查找过程中的父路径
30 struct dir* parent_dir; // 文件或目录所在的直接父目录
31 enum file_types file_type; // 找到的文件的类型
32 };
33
34 extern struct partition* cur_part;
35 void filesys_init(void);
36 int32_t path_depth_cnt(char* pathname);
37 int32_t sys_open(const char* pathname,uint8_t flags);
38 #endif
②fs/fs.c:

1 #include "fs.h"
2 #include "super_block.h"
3 #include "inode.h"
4 #include "dir.h"
5 #include "stdint.h"
6 #include "stdio-kernel.h"
7 #include "list.h"
8 #include "string.h"
9 #include "ide.h"
10 #include "global.h"
11 #include "debug.h"
12 #include "memory.h"
13 #include "file.h"
14
15 struct partition* cur_part; // 默认情况下操作的是哪个分区
16
17 /* 在分区中找到名为part_name的分区,并将其指针赋给cur_part */
18 static bool mount_partition(struct list_elem* pelem,int arg){
19 char* part_name=(char*)arg;
20 struct partition* part=elem2entry(struct partition,part_tag,pelem);
21 if (!strcmp(part->name,part_name)){
22 cur_part=part;
23 struct disk* hd=cur_part->my_disk;
24
25 /* sb_buf用来存储从硬盘上读入的超级块 */
26 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
27
28 /* 在内存中创建分区cur_part的超级块 */
29 cur_part->sb=(struct super_block*)sys_malloc(sizeof(struct super_block));
30 if (cur_part->sb==NULL){
31 PANIC("alloc memory failed!");
32 }
33
34 /* 读入超级块 */
35 memset(sb_buf,0,SECTOR_SIZE);
36 ide_read(hd,cur_part->start_lba+1,sb_buf,1);
37
38 /* 把sb_buf中超级块的信息复制到分区的超级块sb中 */
39 memcpy(cur_part->sb,sb_buf,sizeof(struct super_block));
40
41 /********** 将硬盘上的块位图读入到内存 **********/
42 cur_part->block_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->block_bitmap_sects*SECTOR_SIZE);
43 if (cur_part->block_bitmap.bits==NULL){
44 PANIC("alloc memory failed!");
45 }
46 cur_part->block_bitmap.btmp_bytes_len=sb_buf->block_bitmap_sects*SECTOR_SIZE;
47 /* 从硬盘上读入块位图到分区的block_bitmap.bits */
48 ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);
49 /***************************************************/
50
51 /********** 将硬盘上的inode位图读入到内存 **********/
52 cur_part->inode_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects*SECTOR_SIZE);
53 if (cur_part->inode_bitmap.bits==NULL){
54 PANIC("alloc memory failed!");
55 }
56 cur_part->inode_bitmap.btmp_bytes_len=sb_buf->inode_bitmap_sects*SECTOR_SIZE;
57 /* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
58 ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);
59 /**************************************************/
60
61 list_init(&cur_part->open_inodes);
62 printk("mount %s done!\n",part->name);
63
64 /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关
65 * 只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历 */
66 return true;
67 }
68 return false; // 使list_traversal继续遍历
69 }
70
71 /* 初始化分区元信息,创建文件系统 */
72 static void partition_format(struct partition* part){
73 /* 为方便,一个块大小是一扇区 */
74 uint32_t boot_sector_sects=1;
75 uint32_t super_block_sects=1;
76 uint32_t inode_bitmap_sects=DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); // inode位图所占用的扇区数
77 uint32_t inode_table_sects=DIV_ROUND_UP(((sizeof(struct inode)*MAX_FILES_PER_PART)),SECTOR_SIZE);
78 uint32_t used_sects=boot_sector_sects+super_block_sects+inode_bitmap_sects+inode_table_sects;
79 uint32_t free_sects=part->sec_cnt-used_sects;
80
81 /****************** 块位图占用的扇区数 ********************/
82 uint32_t block_bitmap_sects;
83 block_bitmap_sects=DIV_ROUND_UP(free_sects,BITS_PER_SECTOR);
84 /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
85 uint32_t block_bitmap_bit_len=free_sects-block_bitmap_sects;
86 block_bitmap_sects=DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);
87 /*********************************************************/
88
89 /* 超级块初始化 */
90 struct super_block sb;
91 sb.magic=0x19590318;
92 sb.sec_cnt=part->sec_cnt;
93 sb.inode_cnt=MAX_FILES_PER_PART;
94 sb.part_lba_base=part->start_lba;
95
96 sb.block_bitmap_lba=sb.part_lba_base+2; // 第0块为bootblock,第1块为superblock
97 sb.block_bitmap_sects=block_bitmap_sects;
98
99 sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sects;
100 sb.inode_bitmap_sects=inode_bitmap_sects;
101
102 sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sects;
103 sb.inode_table_sects=inode_table_sects;
104
105 sb.data_start_lba=sb.inode_table_lba+sb.inode_table_sects;
106 sb.root_inode_no=0;
107 sb.dir_entry_size=sizeof(struct dir_entry);
108
109 printk("%s info:\n",part->name);
110 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);
111
112 struct disk* hd=part->my_disk;
113 /****************************
114 * 1.将超级块写入本分区的1扇区
115 * *************************/
116 ide_write(hd,part->start_lba+1,&sb,1);
117 printk(" super_block_lba:0x%x\n",part->start_lba+1);
118
119 /* 找出数据量最大的元信息,用其尺寸做存储缓冲区 */
120 uint32_t buf_size=(sb.block_bitmap_sects>=sb.inode_bitmap_sects?sb.block_bitmap_sects:sb.inode_bitmap_sects);
121 buf_size=(buf_size>=sb.inode_table_sects?buf_size:sb.inode_table_sects)*SECTOR_SIZE;
122 uint8_t* buf=(uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回
123
124 /****************************
125 * 2.将块位图初始化并写入sb.block_bitmap_lba
126 * *************************/
127 /* 初始化块位图block_bitmap */
128 buf[0]|=0x01; // 第0个块预留给根目录,位图中先占位
129 uint32_t block_bitmap_last_byte=block_bitmap_bit_len/8;
130 uint8_t block_bitmap_last_bit=block_bitmap_bit_len%8;
131 uint32_t last_size=SECTOR_SIZE-(block_bitmap_last_byte%SECTOR_SIZE); // last_size是位图中的填充部分
132
133 /* 2.1先将位图最后一字节到其所在扇区结束全置为1,即超出实际块数部分直接置为已占用 */
134 memset(&buf[block_bitmap_last_byte],0xff,last_size);
135
136 /* 2.2再将上一步中覆盖的最后一字节内有效位重新置0 */
137 uint8_t bit_idx=0;
138 while (bit_idx<=block_bitmap_last_bit){
139 buf[block_bitmap_last_byte]&=~(1<<bit_idx++);
140 }
141 ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);
142
143 /***************************
144 * 3.将inode位图初始化并写入sb.inode_bitmap_lba
145 * ************************/
146 /* 先清空缓冲区 */
147 memset(buf,0,buf_size);
148 buf[0]|=0x1; // 第0个inode分给了根目录
149 /* 由于inode_table中共4096个inode,位图中inode_bitmap正好占用1扇区,
150 * 即inode_bitmap_sects等于1,所以位图中的位全都代表inode_table中的inode,
151 * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
152 * inode_bitmap所在的扇区中没有多余的无效位 */
153 ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects);
154
155 /*************************
156 * 4.将inode数组初始化并写入sb.inode_table_lba
157 * **********************/
158 /* 准备写inode_table中的第0项,即根目录所在的inode */
159 memset(buf,0,buf_size); // 先清空缓冲区buf
160 struct inode* i=(struct inode*)buf;
161 i->i_size=sb.dir_entry_size*2; // . & ..
162 i->i_no=0; // 根目录占inode数组中第0个inode
163 i->i_sectors[0]=sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
164 ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);
165
166 /*************************
167 * 5.将根目录初始化并写入sb.data_start_lba
168 * **********************/
169 /* 写入根目录的两个目录项 . & .. */
170 memset(buf,0,buf_size);
171 struct dir_entry* p_de=(struct dir_entry*)buf;
172
173 /* 初始化当前目录"." */
174 memcpy(p_de->filename,".",1);
175 p_de->i_no=0;
176 p_de->f_type=FT_DIRECTORY;
177 ++p_de;
178
179 /* 初始化当前目录父目录 ".." */
180 memcpy(p_de->filename,"..",2);
181 p_de->i_no=0; // 根目录的父目录是自己
182 p_de->f_type=FT_DIRECTORY;
183
184 /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
185 ide_write(hd,sb.data_start_lba,buf,1);
186
187 printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
188 printk("%s format done\n",part->name);
189 sys_free(buf);
190 }
191
192 /* 将最上层路径名称解析出来 */
193 static char* path_parse(char* pathname,char* name_store){
194 if (pathname[0]=='/'){ // 根目录不需要单独解析
195 /* 路径中出现1个或多个连续的字符 '/',将这些'/'跳过 */
196 while (*(++pathname)=='/');
197 }
198
199 /* 开始一般的路径解析 */
200 while (*pathname!='/' && *pathname!=0){
201 *name_store++=*pathname++;
202 }
203
204 if (pathname[0]==0){ // 路径字符串为空则返回NULL
205 return NULL;
206 }
207 return pathname;
208 }
209
210 /* 返回路径深度,比如/a/b/c,深度为3 */
211 int32_t path_depth_cnt(char* pathname){
212 ASSERT(pathname!=NULL);
213 char* p=pathname;
214 char name[MAX_FILE_NAME_LEN]; // 用于path_parse路径解析
215 uint32_t depth=0;
216
217 /* 解析路径,从中拆分出各级名称 */
218 p=path_parse(p,name);
219 while (name[0]){
220 ++depth;
221 memset(name,0,MAX_FILE_NAME_LEN);
222 if (p){ // 若p!=NULL
223 p=path_parse(p,name);
224 }
225 }
226 return depth;
227 }
228
229 /* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */
230 static int search_file(const char* pathname,struct path_search_record* searched_record){
231 /* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */
232 if (!strcmp(pathname,"/") || !strcmp(pathname,"/.") || !strcmp(pathname,"/..")){
233 searched_record->parent_dir=&root_dir;
234 searched_record->file_type=FT_DIRECTORY;
235 searched_record->searched_path[0]=0; // 搜索路径置空
236 return 0;
237 }
238
239 uint32_t path_len=strlen(pathname);
240 /* 保证pathname至少是这样的路径/x,且小于最大长度 */
241 ASSERT(pathname[0]=='/' && path_len>1 && path_len<MAX_PATH_LEN);
242 char* sub_path=(char*)pathname;
243 struct dir* parent_dir=&root_dir;
244 struct dir_entry dir_e;
245
246 /* 记录路径解析出来的各级名称,如路径"/a/b/c",
247 * 数组name每次的值是"a","b","c" */
248 char name[MAX_FILE_NAME_LEN]={0};
249
250 searched_record->parent_dir=parent_dir;
251 searched_record->file_type=FT_UNKNOWN;
252 uint32_t parent_inode_no=0; // 父目录的inode号
253
254 sub_path=path_parse(sub_path,name);
255 while (name[0]){ // 若第一个字符就是结束符,结束循环
256 /* 记录查找过的路径,但不能超过searched_path的长度512Byte */
257 ASSERT(strlen(searched_record->searched_path)<512);
258
259 /* 记录已存在的父目录 */
260 strcat(searched_record->searched_path,'/');
261 strcat(searched_record->searched_path,name);
262
263 /* 在所给的目录中查找文件 */
264 if (search_dir_entry(cur_part,parent_dir,name,&dir_e)){
265 memset(name,0,MAX_FILE_NAME_LEN);
266 /* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */
267 if (sub_path){
268 sub_path=path_parse(sub_path,name);
269 }
270
271 if (FT_DIRECTORY==dir_e.f_type){ // 如果被打开的是目录
272 parent_inode_no=parent_dir->inode->i_no;
273 dir_close(parent_dir);
274 parent_dir=dir_open(cur_part,dir_e.i_no); // 更新父目录
275 searched_record->parent_dir=parent_dir;
276 continue;
277 }else if (FT_REGULAR==dir_e.f_type){
278 searched_record->file_type=FT_REGULAR;
279 return dir_e.i_no;
280 }
281 }else { // 若找不到
282 /* 找不到目录项时,要留着parent_dir不要关闭,
283 * 若是创建新文件的话需要在parent_dir中创建 */
284 return -1;
285 }
286 }
287
288 /* 遍历了完整的路径并且查找的文件或目录只有同名目录存在 */
289 dir_close(searched_record->parent_dir);
290
291 /* 保存被查找目录的直接父目录 */
292 searched_record->parent_dir=dir_open(cur_part,parent_inode_no);
293 searched_record->file_type=FT_DIRECTORY;
294 return dir_e.i_no;
295 }
296
297 /* 打开或创建文件成功后,返回文件描述符,否则返回-1 */
298 int32_t sys_open(const char* pathname,uint8_t flags){
299 /* 对目录要用dir_open,这里只有open文件 */
300 if (pathname[strlen(pathname)-1]=='/'){
301 printk("can't open a directory %s\n",pathname);
302 return -1;
303 }
304 ASSERT(flags<=7);
305 int32_t fd=-1;
306
307 struct path_search_record searched_record;
308 memset(&searched_record,0,sizeof(struct path_search_record));
309
310 /* 记录目录深度,帮助判断中间某个目录不存在的情况 */
311 uint32_t pathname_depth=path_depth_cnt((char*)pathname);
312
313 /* 先检查文件是否存在 */
314 int inode_no=search_file(pathname,&searched_record);
315 bool found=inode_no!=-1?true:false;
316
317 if (searched_record.file_type==FT_DIRECTORY){
318 printk("can't open a directory with open(),use opendir() to instead\n");
319 dir_close(searched_record.parent_dir);
320 return -1;
321 }
322
323 uint32_t path_searched_depth=path_depth_cnt(searched_record.searched_path);
324
325 /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
326 if (pathname_depth!=path_searched_depth){ // 说明并没有访问到全部路径,某个中间目录是不存在的
327 printk("cannot access %s: Not a directory, subpath %s is't exist\n", \
328 pathname,searched_record.searched_path);
329 dir_close(searched_record.parent_dir);
330 return -1;
331 }
332
333 /* 若是最后一个路径上没找到,并且不是要创建文件,直接返回-1 */
334 if (!found && !(flags & O_CREAT)){
335 printk("in path %s, file %s is't exist\n", \
336 searched_record.searched_path,
337 (strrchr(searched_record.searched_path,'/')+1));
338 dir_close(searched_record.parent_dir);
339 return -1;
340 }else if (found && flags & O_CREAT){ // 若要创建的文件已存在
341 printk("%s has already exist!\n",pathname);
342 dir_close(searched_record.parent_dir);
343 return -1;
344 }
345
346 switch (flags & O_CREAT){
347 case O_CREAT:
348 printk("creating file\n");
349 fd=file_create(searched_record.parent_dir,(strrchr(pathname,'/')+1),flags);
350 dir_close(searched_record.parent_dir);
351 // 其余为打开文件
352 }
353
354 /* 此fd是指任务pcb->fd_table数组中的元素下标,
355 * 并不是指全局file_table中的下标 */
356 return fd;
357 }
358
359 /* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
360 void filesys_init(){
361 uint8_t channel_no=0,dev_no=0,part_idx=0;
362
363 /* sb_buf用来存储从硬盘上读入的超级块 */
364 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
365
366 if (sb_buf==NULL){
367 PANIC("alloc memory failed!");
368 }
369 printk("searching filesystem......\n");
370 while (channel_no<channel_cnt){
371 dev_no=0;
372 while (dev_no<2){
373 if (dev_no==0){
374 ++dev_no;
375 continue;
376 }
377 struct disk* hd=&channels[channel_no].devices[dev_no];
378 struct partition* part=hd->prim_parts;
379 while (part_idx<12){ // 四个主分区+八个逻辑分区
380 if (part_idx==4){ // 逻辑分区
381 part=hd->logic_parts;
382 }
383
384 /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
385 * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
386 * 若partition未初始化,则partition中的成员仍为0.
387 * 下面处理存在的分区. */
388 if (part->sec_cnt!=0){ // 如果分区存在
389 memset(sb_buf,0,SECTOR_SIZE);
390
391 /* 读出分区超级块,根据魔数是否正确来判断是否存在fs */
392 ide_read(hd,part->start_lba+1,sb_buf,1);
393
394 /* 只支持自己的文件系统,若磁盘上已经有fs就不再初始化了 */
395 if (sb_buf->magic==0x19590318){
396 printk("%s has filesystem\n",part->name);
397 }else{
398 printk("formatting %s's partition %s.....\n",hd->name,part->name);
399 partition_format(part);
400 }
401 }
402 ++part_idx;
403 ++part; // 下一分区
404 }
405 ++dev_no; //下一磁盘
406 }
407 ++channel_no; // 下一通道
408 }
409 sys_free(sb_buf);
410
411 /* 确定默认操作的分区 */
412 char default_part[8]="sdb1";
413 /* 挂载分区 */
414 list_traversal(&partition_list,mount_partition,(int)default_part);
415
416 /* 将当前分区的根目录打开 */
417 open_root_dir(cur_part);
418
419 /* 初始化文件表 */
420 uint32_t fd_idx=0;
421 while (fd_idx<MAX_FILE_OPEN){
422 file_table[fd_idx++].fd_inode=NULL;
423 }
424 }
③fs/file.h:

1 #ifndef __FS_FILE_H
2 #define __FS_FILE_H
3 #include "stdint.h"
4 #include "ide.h"
5 #include "dir.h"
6 #include "global.h"
7
8 /* 文件结构 */
9 struct file{
10 uint32_t fd_pos; // 记录当前文件操作的偏移地址,以0为起始,最大为文件大小-1
11 uint32_t fd_flag;
12 struct inode* fd_inode;
13 };
14
15 /* 标准输入输出描述符 */
16 enum std_fd{
17 stdin_no, // 0 标准输入
18 stdout_no, // 1 标准输出
19 stderr_no // 2 标准错误
20 };
21
22 /* 位图类型 */
23 enum bitmap_type{
24 INODE_BITMAP, // inode位图
25 BLOCK_BITMAP // 块位图
26 };
27
28 #define MAX_FILE_OPEN 32 // 系统可打开的最大文件数
29
30 extern struct file file_table[MAX_FILE_OPEN];
31 int32_t inode_bitmap_alloc(struct partition* part);
32 int32_t block_bitmap_alloc(struct partition* part);
33 int32_t file_create(struct dir* parent_dir,char* filename,uint8_t flag);
34 void bitmap_sync(struct partition* part,uint32_t bit_idx,uint8_t btmp);
35 int32_t get_free_slot_in_global(void);
36 int32_t pcb_fd_install(int32_t global_fd_idx);
37 #endif
④fs/file.c:

1 #include "file.h"
2 #include "fs.h"
3 #include "super_block.h"
4 #include "inode.h"
5 #include "stdio-kernel.h"
6 #include "memory.h"
7 #include "debug.h"
8 #include "interrupt.h"
9 #include "string.h"
10 #include "thread.h"
11 #include "global.h"
12
13 #define DEFAULT_SECT 1
14
15 /* 文件表 */
16 struct file file_table[MAX_FILE_OPEN];
17
18 /* 从文件表file_table中获取一个空闲位,成功返回下标,失败返回-1 */
19 int32_t get_free_slot_in_global(void){
20 uint32_t fd_idx=3;
21 while (fd_idx<MAX_FILE_OPEN){
22 if (file_table[fd_idx].fd_inode==NULL){
23 break;
24 }
25 ++fd_idx;
26 }
27 if (fd_idx==MAX_FILE_OPEN){
28 printk("exceed max open files\n");
29 return -1;
30 }
31 return fd_idx;
32 }
33
34 /* 将全局描述符表下标安装到进程或线程自己的文件描述符数组fd_table中,
35 * 成功则返回下标,失败则返回-1 */
36 int32_t pcb_fd_install(int32_t global_fd_idx){
37 struct task_struct* cur=running_thread();
38 uint8_t local_fd_idx=3; // 跳过stdin,stdout,stderr
39 while (local_fd_idx<MAX_FILES_OPEN_PER_PROC){
40 if (cur->fd_table[local_fd_idx]==-1){
41 cur->fd_table[local_fd_idx]=global_fd_idx;
42 break;
43 }
44 ++local_fd_idx;
45 }
46 if (local_fd_idx==MAX_FILES_OPEN_PER_PROC){
47 printk("exceed max open_per_proc\n");
48 return -1;
49 }
50 return local_fd_idx;
51 }
52
53 /* 分配一个inode,返回inode_no */
54 int32_t inode_bitmap_alloc(struct partition* part){
55 int32_t bit_idx=bitmap_scan(&part->inode_bitmap,1);
56 if (bit_idx==-1){
57 return -1;
58 }
59 bitmap_set(&part->inode_bitmap,bit_idx,1);
60 return bit_idx;
61 }
62
63 /* 分配一个扇区,返回其扇区地址 */
64 int32_t block_bitmap_alloc(struct partition* part){
65 int32_t bit_idx=bitmap_scan(&part->block_bitmap,1);
66 if (bit_idx==-1){
67 return -1;
68 }
69 bitmap_set(&part->block_bitmap,bit_idx,1);
70 /* 和inode_bitmap_malloc不同,此处返回的不是位图索引,而是具体可用的扇区地址 */
71 return (part->sb->data_start_lba+bit_idx);
72 }
73
74 /* 将内存bitmap第bit_idx位所在的512字节同步到硬盘 */
75 void bitmap_sync(struct partition* part,uint32_t bit_idx,uint8_t btmp_type){
76 uint32_t off_sec=bit_idx/4096; // 本inode相对于位图的扇区偏移量
77 uint32_t off_size=off_sec*BLOCK_SIZE; // 本inode相对于位图的字节偏移量
78 uint32_t sec_lba;
79 uint8_t* bitmap_off;
80
81 /* 需要被同步到硬盘的位图只有inode_bitmap和block_bitmap */
82 switch(btmp_type){
83 case INODE_BITMAP:
84 sec_lba=part->sb->inode_bitmap_lba+off_sec;
85 bitmap_off=part->inode_bitmap.bits+off_size;
86 break;
87
88 case BLOCK_BITMAP:
89 sec_lba=part->sb->block_bitmap_lba+off_sec;
90 bitmap_off=part->block_bitmap.bits+off_size;
91 break;
92 }
93 ide_write(part->my_disk,sec_lba,bitmap_off,1);
94 }
95
96 /* 创建文件,若成功则返回文件描述符,否则返回-1 */
97 int32_t file_create(struct dir* parent_dir,char* filename,uint8_t flag){
98 /* 后续操作的公共缓冲区 */
99 void* io_buf=sys_malloc(1024);
100 if (io_buf==NULL){
101 printk("in file_creat: sys_malloc for io_buf failed\n");
102 return -1;
103 }
104
105 uint8_t rollback_step=0; // 用于操作失败时回滚各资源
106
107 /* 为新文件分配inode */
108 int32_t inode_no=inode_bitmap_alloc(cur_part);
109 if (inode_no==-1){
110 printk("in file_create: allocate inode failed\n");
111 return -1;
112 }
113
114 /* 此inode要从堆中申请内存,不可生成局部变量(否则函数退出时会释放)
115 * 因为file_table数组中的文件描述符的inode指针要指向它 */
116 struct inode* new_file_inode=(struct inode*)sys_malloc(sizeof(struct inode));
117 if (new_file_inode==NULL){
118 printk("file_create: sys_malloc for inode failed\n");
119 rollback_step=1;
120 goto rollback;
121 }
122 inode_init(inode_no,new_file_inode); // 初始化inode
123
124 /* 返回的是file_table数组的下标 */
125 int fd_idx=get_free_slot_in_global();
126 if (fd_idx==-1){
127 printk("exceed max open files\n");
128 rollback_step=2;
129 goto rollback;
130 }
131
132 file_table[fd_idx].fd_inode=new_file_inode;
133 file_table[fd_idx].fd_pos=0;
134 file_table[fd_idx].fd_flag=flag;
135 file_table[fd_idx].fd_inode->write_deny=false;
136
137 struct dir_entry new_dir_entry;
138 memset(&new_dir_entry,0,sizeof(struct dir_entry));
139
140 create_dir_entry(filename,inode_no,FT_REGULAR,&new_dir_entry); // create_dir_entry只是内存操作
141
142 /* 同步内存数据到硬盘 */
143 /* 1.在目录parent_dir下安装目录项new_dir_entry,写入硬盘后返回true,否则返回false */
144 if (!sync_dir_entry(parent_dir,&new_dir_entry,io_buf)){
145 printk("sync dir_entry to disk failed\n");
146 rollback_step=3;
147 goto rollback;
148 }
149
150 memset(io_buf,0,1024);
151 /* 2.将父目录inode的内容同步到硬盘 */
152 inode_sync(cur_part,parent_dir->inode,io_buf);
153
154 memset(io_buf,0,1024);
155 /* 3.将新创建的文件的inode内容同步到硬盘 */
156 inode_sync(cur_part,new_file_inode,io_buf);
157
158 /* 4.将inode_bitmap位图同步到硬盘 */
159 bitmap_sync(cur_part,inode_no,INODE_BITMAP);
160
161 /* 5.将创建的文件inode添加到open_inodes链表 */
162 list_push(&cur_part->open_inodes,&new_file_inode->inode_tag);
163 new_file_inode->i_open_cnts=1;
164
165 sys_free(io_buf);
166 return pcb_fd_install(fd_idx);
167
168 /* 创建文件需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
169 rollback:
170 switch(rollback_step){
171 case 3:
172 /* 失败时,将file_table中的相应位清0 */
173 memset(&file_table[fd_idx],0,sizeof(struct file));
174 case 2:
175 sys_free(new_file_inode);
176 case 1:
177 /* 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复 */
178 bitmap_set(&cur_part->inode_bitmap,inode_no,0);
179 break;
180 }
181 sys_free(io_buf);
182 return -1;
183 }
⑤fs/inode.h:

1 #ifndef __FS_INODE_H
2 #define __FS_INODE_H
3 #include "stdint.h"
4 #include "list.h"
5 #include "ide.h"
6
7 /* inode结构 */
8 struct inode{
9 uint32_t i_no; // inode编号
10
11 /* 当此inode指向文件时,i_size为文件大小;
12 * 当此inode指向目录时,i_size为目录下所有目录项之和的大小 */
13 uint32_t i_size;
14
15 uint32_t i_open_cnts;//记录此文件被打开的次数
16 bool write_deny; // 写文件不能并行,进程写文件前检查此标识
17
18 /* i_sectors[0-11]是直接块,i_sectors[12]用来存储一级间接块指针 */
19 uint32_t i_sectors[13];
20 struct list_elem inode_tag; // 标识此inode,加入“已打开的inode列表”(内存缓存)中
21 };
22
23 struct inode* inode_open(struct partition* part,uint32_t inode_no);
24 void inode_sync(struct partition* part,struct inode* inode,void* io_buf);
25 void inode_init(uint32_t inode_no,struct inode* new_inode);
26 void inode_close(struct inode* inode);
27 #endif
⑥fs/inode.c:

1 #include "inode.h"
2 #include "fs.h"
3 #include "file.h"
4 #include "global.h"
5 #include "debug.h"
6 #include "memory.h"
7 #include "interrupt.h"
8 #include "list.h"
9 #include "stdio-kernel.h"
10 #include "string.h"
11 #include "super_block.h"
12
13 /* 用来存储inode位置 */
14 struct inode_position{
15 bool two_sec; // inode是否跨扇区
16 uint32_t sec_lba; // inode所在的扇区号
17 uint32_t off_size; // inode在扇区内的字节偏移量
18 };
19
20 /* 获取inode所在的扇区和偏移量 */
21 static void inode_locate(struct partition* part,uint32_t inode_no,struct inode_position* inode_pos){
22 /* inode_table在硬盘上是连续的 */
23 ASSERT(inode_no<4096);
24 uint32_t inode_table_lba=part->sb->inode_table_lba;
25
26 uint32_t inode_size=sizeof(struct inode);
27 uint32_t off_size=inode_no*inode_size; // inode_no号inode相对于inode_table_lba的字节偏移量
28 uint32_t off_sec=off_size/512; // 相对于inode_table_lba的扇区偏移量
29 uint32_t off_size_in_sec=off_size%512; // 扇区中的起始地址(扇区内偏移量)
30
31 /* 判断此结点是否跨越两个扇区 */
32 uint32_t left_in_sec=512-off_size_in_sec;
33 if (left_in_sec<inode_size){ // 剩下的空间不足以容纳一个inode
34 inode_pos->two_sec=true;
35 }else {
36 inode_pos->two_sec=false;
37 }
38 inode_pos->sec_lba=inode_table_lba+off_sec;
39 inode_pos->off_size=off_size_in_sec;
40 }
41
42 /* 将inode写入到分区part */
43 void inode_sync(struct partition* part,struct inode* inode,void* io_buf){ // io_buf是用于硬盘io的缓冲区
44 uint8_t inode_no=inode->i_no;
45 struct inode_position inode_pos;
46 inode_locate(part,inode_no,&inode_pos);
47 ASSERT(inode_pos.sec_lba<=(part->start_lba+part->sec_cnt));
48
49 /* 硬盘中的inode中的成员inode_tag和i_open_cnts是不需要的,
50 * 它们只在内存中记录链表位置和多少进程共享 */
51 struct inode pure_inode;
52 memcpy(&pure_inode,inode,sizeof(struct inode));
53
54 /* 以下inode的三个成员只存在于内存中,现在将inode同步到硬盘,清掉这三项即可 */
55 pure_inode.i_open_cnts=0;
56 pure_inode.write_deny=0;
57 pure_inode.inode_tag.prev=pure_inode.inode_tag.next=NULL;
58
59 char* inode_buf=(char*)io_buf;
60 if (inode_pos.two_sec){
61 /* 读写硬盘以扇区为单位,若写入的数据小于一扇区,则将原硬盘上的内容读出来再和新数据拼成一扇区后再写入 */
62 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,2); // 读取两个扇区
63
64 /* 开始将待写入的inode拼到这两个扇区的相应位置 */
65 memcpy((inode_buf+inode_pos.off_size),&pure_inode,sizeof(struct inode));
66
67 /* 将拼接好的数据再写入硬盘 */
68 ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,2);
69 }else {
70 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,1);
71 memcpy((inode_buf+inode_pos.off_size),&pure_inode,sizeof(struct inode));
72 ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,1);
73 }
74 }
75
76 /* 根据inode_no返回相应的inode */
77 struct inode* inode_open(struct partition* part,uint32_t inode_no){
78 /* 先在已打开inode链表中找inode,此链表是为了提速而创建的缓冲区 */
79 struct list_elem* elem=part->open_inodes.head.next;
80 struct inode* inode_found;
81 while (elem!=&part->open_inodes.tail){
82 inode_found=elem2entry(struct inode,inode_tag,elem);
83 if (inode_found->i_no==inode_no){
84 inode_found->i_open_cnts++;
85 return inode_found;
86 }
87 elem=elem->next;
88 }
89
90 /* 由于open_inodes链表中找不到,下面从硬盘上读入此inode并加入到此链表中 */
91 struct inode_position inode_pos;
92
93 /* inode位置信息会存入inode_pos,包括inode所在扇区地址和扇区内的字节偏移量 */
94 inode_locate(part,inode_no,&inode_pos);
95
96 /* 为使通过sys_malloc创建的新inode被所有任务共享,
97 * 需要将inode置于内核空间,故需要临时
98 * 将cur_pbc->pgdir置为NULL */
99 struct task_struct* cur=running_thread();
100 uint32_t* cur_pagedir_bak=cur->pgdir;
101 cur->pgdir=NULL;
102 /* 以上三行代码完成后下面分配的内存将位于内核区 */
103 inode_found=(struct inode*)sys_malloc(sizeof(struct inode));
104 /* 恢复pgdir */
105 cur->pgdir=cur_pagedir_bak;
106
107 char* inode_buf;
108 if (inode_pos.two_sec){
109 inode_buf=(char*)sys_malloc(1024);
110
111 /* inode是被partition_format函数连续写入扇区的,
112 * 所以下面可以连续读出来 */
113 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,2);
114 }else {
115 inode_buf=(char*)sys_malloc(512);
116 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,1);
117 }
118 memcpy(inode_found,inode_buf+inode_pos.off_size,sizeof(struct inode));
119
120 /* 因为一会很可能用到此inode,故将其插入到队首便于提前检索到 */
121 list_push(&part->open_inodes,&inode_found->inode_tag);
122 inode_found->i_open_cnts=1;
123
124 sys_free(inode_buf);
125 return inode_found;
126 }
127
128 /* 关闭inode或减少inode的打开数 */
129 void inode_close(struct inode* inode){
130 /* 若没有进程再打开此文件,将此inode去掉并释放空间 */
131 enum intr_status old_status=intr_disable();
132 if (--inode->i_open_cnts==0){
133 list_remove(&inode->inode_tag);
134 /* inode_open时为实现inode为所有进程共享,
135 * 已经在sys_malloc为inode分配了内核空间,
136 * 释放inode时也要确保释放的是内核内存池 */
137 struct task_struct* cur=running_thread();
138 uint32_t* cur_pagedir_bak=cur->pgdir;
139 cur->pgdir=NULL;
140 sys_free(inode);
141 cur->pgdir=cur_pagedir_bak;
142 }
143 intr_set_status(old_status);
144 }
145
146 /* 初始化new_inode */
147 void inode_init(uint32_t inode_no,struct inode* new_inode){
148 new_inode->i_no=inode_no;
149 new_inode->i_size=0;
150 new_inode->i_open_cnts=0;
151 new_inode->write_deny=false;
152
153 /* 初始化块索引数组i_sector */
154 uint8_t sec_idx=0;
155 while (sec_idx<13){
156 /* i_sectors[12]为一级间接块地址 */
157 new_inode->i_sectors[sec_idx]=0;
158 sec_idx++;
159 }
160 }
⑦fs/dir.h:

1 #ifndef __FS_DIR_H
2 #define __FS_DIR_H
3 #include "stdint.h"
4 #include "inode.h"
5 #include "fs.h"
6 #include "ide.h"
7 #include "global.h"
8
9 #define MAX_FILE_NAME_LEN 16
10
11 /* 目录结构 */
12 struct dir{
13 struct inode* inode;
14 uint32_t dir_pos; // 记录在目录内的偏移
15 uint8_t dir_buf[512]; // 目录的数据缓存
16 };
17
18 struct dir_entry{
19 char filename[MAX_FILE_NAME_LEN]; // 普通文件或目录
20 uint32_t i_no; // 普通文件或目录对应的inode
21 enum file_types f_type; // 文件类型
22 };
23
24 extern struct dir root_dir; // 根目录
25 void open_root_dir(struct partition* part);
26 struct dir* dir_open(struct partition* part,uint32_t inode_no);
27 void dir_close(struct dir* dir);
28 bool search_dir_entry(struct partition* part,struct dir* pdir,const char* name,struct dir_entry* dir_e);
29 void create_dir_entry(char* filename,uint32_t inode_no,uint8_t file_type,struct dir_entry* p_de);
30 bool sync_dir_entry(struct dir* parent_dir,struct dir_entry* p_de,void* io_buf);
31 #endif
⑧fs/dir.c:

1 #include "dir.h"
2 #include "stdint.h"
3 #include "inode.h"
4 #include "file.h"
5 #include "fs.h"
6 #include "stdio-kernel.h"
7 #include "global.h"
8 #include "debug.h"
9 #include "memory.h"
10 #include "string.h"
11 #include "interrupt.h"
12 #include "super_block.h"
13
14 struct dir root_dir; // 根目录
15
16 /* 打开根目录 */
17 void open_root_dir(struct partition* part){
18 root_dir.inode=inode_open(part,part->sb->root_inode_no);
19 root_dir.dir_pos=0;
20 }
21
22 /* 在分区part上打开inode_no的目录并返回目录指针 */
23 struct dir* dir_open(struct partition* part,uint32_t inode_no){
24 struct dir* pdir=(struct dir*)sys_malloc(sizeof(struct dir));
25 pdir->inode=inode_open(part,inode_no);
26 pdir->dir_pos=0;
27 return pdir;
28 }
29
30 /* 在part分区内的pdir目录内寻找名为name的文件或目录,
31 * 找到后返回true并将其目录项存入dir_e,否则返回false */
32 bool search_dir_entry(struct partition* part,struct dir* pdir,\
33 const char* name,struct dir_entry* dir_e){
34 uint32_t block_cnt=140; // 12个直接块+128个间接块=140块
35
36 /* 共560B */
37 uint32_t* all_blocks=(uint32_t*)sys_malloc(48+512);
38 if (all_blocks==NULL){
39 printk("search_dir_entry: sys_malloc for all_blocks failed");
40 return false;
41 }
42
43 uint32_t block_idx=0;
44 while (block_idx<12){
45 all_blocks[block_idx]=pdir->inode->i_sectors[block_idx];
46 ++block_idx;
47 }
48 block_idx=0;
49
50 if (pdir->inode->i_sectors[12]!=0){ // 若含有一级间接块表
51 ide_read(part->my_disk,pdir->inode->i_sectors[12],all_blocks+12,1);
52 }
53 /* 至此,all_blocks存储的是该文件或目录的所有扇区地址 */
54
55 /* 目录项不跨区
56 * 这样目录项容易处理,只申请容纳一个扇区的内存 */
57 uint8_t* buf=(uint8_t*)sys_malloc(SECTOR_SIZE);
58 struct dir_entry* p_de=(struct dir_entry*)buf; // p_de为指向目录项的指针
59 uint32_t dir_entry_size=part->sb->dir_entry_size;
60 uint32_t dir_entry_cnt=SECTOR_SIZE/dir_entry_size; // 一个扇区可容纳的目录项个数
61
62 /* 开始在所有块中查找目录项 */
63 while (block_idx<block_cnt){
64 /* 块地址为0时表示该块中无数据,继续在其它块中找 */
65 if (all_blocks[block_idx]==0){
66 ++block_idx;
67 continue;
68 }
69 ide_read(part->my_disk,all_blocks[block_idx],buf,1);
70
71 uint32_t dir_entry_idx=0;
72 /* 遍历扇区中所有目录项 */
73 while (dir_entry_idx<dir_entry_cnt){
74 /* 若找到,就直接复制整个目录项 */
75 if (!strcmp(p_de->filename,name)){
76 memcpy(dir_e,p_de,dir_entry_size);
77 sys_free(buf);
78 sys_free(all_blocks);
79 return true;
80 }
81 ++dir_entry_idx;
82 ++p_de;
83 }
84 ++block_idx;
85 p_de=(struct dir_entry*)buf; // 此时p_de已经指向扇区内最后一个完整目录项了,需要恢复p_de指向buf
86 memset(buf,0,SECTOR_SIZE); // buf清0
87 }
88 sys_free(buf);
89 sys_free(all_blocks);
90 return false;
91 }
92
93 /* 关闭目录 */
94 void dir_close(struct dir* dir){
95 /********** 根目录不能关闭 ***********
96 * 1.根目录自打开后就不能关闭,否则还需要再次open_root_dir(),
97 * 2.root_dir所在的内存是低端1M之内,并非在堆中,free()会出现问题 */
98 if (dir==&root_dir){
99 /* 不做任何处理直接返回 */
100 return;
101 }
102 inode_close(dir->inode);
103 sys_free(dir);
104 }
105
106 /* 在内存中初始化目录项p_de */
107 void create_dir_entry(char* filename,uint32_t inode_no,uint8_t file_type,struct dir_entry* p_de){
108 ASSERT(strlen(filename)<=MAX_FILE_NAME_LEN);
109
110 /* 初始化目录项 */
111 memcpy(p_de->filename,filename,strlen(filename));
112 p_de->i_no=inode_no;
113 p_de->f_type=file_type;
114 }
115
116 /* 将目录项p_de写入父目录parent_dir中,io_buf由主调函数提供 */
117 bool sync_dir_entry(struct dir* parent_dir,struct dir_entry* p_de,void* io_buf){
118 struct inode* dir_inode=parent_dir->inode;
119 uint32_t dir_size=dir_inode->i_size;
120 uint32_t dir_entry_size=cur_part->sb->dir_entry_size;
121
122 ASSERT(dir_size%dir_entry_size==0);
123
124 uint32_t dir_entrys_per_sec=(512/dir_entry_size); // 每扇区最大目录数
125 int32_t block_lba=-1;
126
127 /* 将该目录的所有扇区地址(12个直接块+128个间接块)存入all_blocks */
128 uint8_t block_idx=0;
129 uint32_t all_blocks[140]={0};
130
131 /* 将12个直接块存入all_blocks */
132 while (block_idx<12){
133 all_blocks[block_idx]=dir_inode->i_sectors[block_idx];
134 ++block_idx;
135 }
136
137 struct dir_entry* dir_e=(struct dir_entry*)io_buf; // dir_e用来遍历io_buf
138 int32_t block_bitmap_idx=-1;
139
140 /* 开始遍历所有块以寻找目录项空位,若已有扇区中没有空位,
141 * 在不超过文件大小的情况下上申请新扇区来存储新目录项 */
142 block_idx=0;
143 while (block_idx<140){ // 共140个块
144 block_bitmap_idx=-1;
145 if (all_blocks[block_idx]==0){
146 block_lba=block_bitmap_alloc(cur_part);
147 if (block_lba==-1){
148 printk("alloc block bitmap for sync_dir_entry failed\n");
149 return false;
150 }
151
152 /* 每分配一个块就同步一次block_bitmap */
153 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
154 ASSERT(block_bitmap_idx!=-1);
155 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
156
157 block_bitmap_idx=-1;
158 if (block_idx<12){ // 直接块
159 dir_inode->i_sectors[block_idx]=all_blocks[block_idx]=block_lba;
160 }else if (block_idx==12){ // 尚未分配一级间接块表
161 dir_inode->i_sectors[12]=block_lba;
162 block_lba=-1;
163 block_lba=block_bitmap_alloc(cur_part); // 在分配一个块作第0个间接块
164 if (block_lba==-1){
165 block_bitmap_idx=dir_inode->i_sectors[12]-cur_part->sb->data_start_lba;
166 bitmap_set(&cur_part->block_bitmap,block_bitmap_idx,0);
167 dir_inode->i_sectors[12]=0;
168 printk("alloc block bitmap for sync_dir_entry failed\n");
169 return false;
170 }
171
172 /* 每分配一个块就同步一次block_bitmap */
173 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
174 ASSERT(block_bitmap_idx!=-1);
175 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
176
177 all_blocks[12]=block_lba;
178 /* 把新分配的第0个间接块写入一级间接块表 */
179 ide_write(cur_part->my_disk,dir_inode->i_sectors[12],all_blocks+12,1);
180 }else { // 若是间接块未分配
181 all_blocks[block_idx]=block_lba;
182 /* 把新分配的第(block_idx-12)个间接块地址写入一级间接块表 */
183 ide_write(cur_part->my_disk,dir_inode->i_sectors[12],all_blocks+12,1);
184 }
185
186 /* 再将新目录项p_de写入新分配的间接块 */
187 memset(io_buf,0,512);
188 memcpy(io_buf,p_de,dir_entry_size);
189 ide_write(cur_part->my_disk,all_blocks[block_idx],io_buf,1);
190 dir_inode->i_size+=dir_entry_size;
191 return true;
192 }
193
194 /* 若第block_idx块已存在,将其读入内存,然后在该块中查找空目录项 */
195 ide_read(cur_part->my_disk,all_blocks[block_idx],io_buf,1);
196 /* 在扇区内查找空目录项 */
197 uint8_t dir_entry_idx=0;
198 while (dir_entry_idx<dir_entrys_per_sec){
199 if ((dir_e+dir_entry_idx)->f_type==FT_UNKNOWN){
200 memcpy(dir_e+dir_entry_idx,p_de,dir_entry_size);
201 ide_write(cur_part->my_disk,all_blocks[block_idx],io_buf,1);
202
203 dir_inode->i_size+=dir_entry_size;
204 return true;
205 }
206 ++dir_entry_idx;
207 }
208 ++block_idx;
209 }
210 printk("directory is full!\n");
211 return false;
212 }
⑨kernel/main.c:
添加头文件并创建文件。
注意:书中应当是有错误的,书中的main()并没有开中断,则程序始终运行在主线程下,进线程a、b无法切换,也就不会显示它们分配地址的情况。
⑩makefile:

1 BUILD_DIR = ./build
2 ENTRY_POINT = 0xc0001500
3 AS = nasm
4 CC = gcc
5 LD = ld
6 LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ -I userprog/ -I fs/
7 ASFLAGS = -f elf
8 CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes
9 LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map
10 OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \
11 $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \
12 $(BUILD_DIR)/switch.o $(BUILD_DIR)/debug.o $(BUILD_DIR)/string.o \
13 $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o $(BUILD_DIR)/thread.o \
14 $(BUILD_DIR)/list.o $(BUILD_DIR)/sync.o $(BUILD_DIR)/console.o \
15 $(BUILD_DIR)/keyboard.o $(BUILD_DIR)/ioqueue.o $(BUILD_DIR)/tss.o \
16 $(BUILD_DIR)/process.o $(BUILD_DIR)/syscall.o $(BUILD_DIR)/syscall-init.o \
17 $(BUILD_DIR)/stdio.o $(BUILD_DIR)/stdio-kernel.o $(BUILD_DIR)/ide.o \
18 $(BUILD_DIR)/fs.o $(BUILD_DIR)/inode.o $(BUILD_DIR)/file.o \
19 $(BUILD_DIR)/dir.o
20 ############## c代码编译 ###############
21 $(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \
22 lib/stdint.h kernel/init.h lib/string.h kernel/memory.h \
23 thread/thread.h kernel/interrupt.h device/console.h \
24 device/keyboard.h device/ioqueue.h userprog/process.h \
25 lib/user/syscall.h userprog/syscall-init.h lib/stdio.h \
26 lib/kernel/stdio-kernel.o fs/fs.o
27 $(CC) $(CFLAGS) $< -o $@
28
29 $(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \
30 lib/stdint.h kernel/interrupt.h device/timer.h kernel/memory.h \
31 thread/thread.h device/console.h device/keyboard.h userprog/tss.h \
32 userprog/syscall-init.h device/ide.h fs/fs.h
33 $(CC) $(CFLAGS) $< -o $@
34
35 $(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \
36 lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h
37 $(CC) $(CFLAGS) $< -o $@
38
39 $(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/kernel/io.h \
40 lib/kernel/print.h kernel/interrupt.h thread/thread.h kernel/debug.h
41 $(CC) $(CFLAGS) $< -o $@
42
43 $(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \
44 lib/kernel/print.h lib/stdint.h kernel/interrupt.h
45 $(CC) $(CFLAGS) $< -o $@
46
47 $(BUILD_DIR)/string.o: lib/string.c lib/string.h \
48 kernel/debug.h kernel/global.h
49 $(CC) $(CFLAGS) $< -o $@
50
51 $(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h \
52 lib/stdint.h lib/kernel/bitmap.h kernel/debug.h lib/string.h \
53 thread/sync.h thread/thread.h lib/kernel/list.h kernel/interrupt.h
54 $(CC) $(CFLAGS) $< -o $@
55
56 $(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h kernel/global.h \
57 lib/string.h kernel/interrupt.h lib/kernel/print.h kernel/debug.h
58 $(CC) $(CFLAGS) $< -o $@
59
60 $(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h \
61 lib/stdint.h lib/string.h kernel/global.h kernel/memory.h \
62 kernel/debug.h kernel/interrupt.h lib/kernel/print.h userprog/process.h
63 $(CC) $(CFLAGS) $< -o $@
64
65 $(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h \
66 kernel/interrupt.h lib/stdint.h kernel/debug.h
67 $(CC) $(CFLAGS) $< -o $@
68
69 $(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h \
70 lib/stdint.h thread/thread.h kernel/debug.h kernel/interrupt.h
71 $(CC) $(CFLAGS) $< -o $@
72
73 $(BUILD_DIR)/console.o: device/console.c device/console.h \
74 lib/kernel/print.h thread/sync.h
75 $(CC) $(CFLAGS) $< -o $@
76
77 $(BUILD_DIR)/keyboard.o: device/keyboard.c device/keyboard.h \
78 lib/kernel/print.h lib/kernel/io.h kernel/interrupt.h \
79 kernel/global.h lib/stdint.h device/ioqueue.h
80 $(CC) $(CFLAGS) $< -o $@
81
82 $(BUILD_DIR)/ioqueue.o: device/ioqueue.c device/ioqueue.h \
83 kernel/interrupt.h kernel/global.h kernel/debug.h
84 $(CC) $(CFLAGS) $< -o $@
85
86 $(BUILD_DIR)/tss.o: userprog/tss.c userprog/tss.h \
87 kernel/global.h thread/thread.h lib/kernel/print.h
88 $(CC) $(CFLAGS) $< -o $@
89
90 $(BUILD_DIR)/process.o: userprog/process.c userprog/process.h \
91 lib/string.h kernel/global.h kernel/memory.h lib/kernel/print.h \
92 thread/thread.h kernel/interrupt.h kernel/debug.h device/console.h
93 $(CC) $(CFLAGS) $< -o $@
94
95 $(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h \
96 lib/stdint.h
97 $(CC) $(CFLAGS) $< -o $@
98
99 $(BUILD_DIR)/syscall-init.o: userprog/syscall-init.c userprog/syscall-init.h \
100 lib/user/syscall.h lib/stdint.h lib/kernel/print.h device/console.h \
101 thread/thread.h lib/string.h
102 $(CC) $(CFLAGS) $< -o $@
103
104 $(BUILD_DIR)/stdio.o: lib/stdio.c lib/stdio.h lib/stdint.h lib/string.h \
105 lib/user/syscall.h
106 $(CC) $(CFLAGS) $< -o $@
107
108 $(BUILD_DIR)/stdio-kernel.o: lib/kernel/stdio-kernel.c lib/kernel/stdio-kernel.h \
109 lib/stdio.h device/console.h
110 $(CC) $(CFLAGS) $< -o $@
111
112 $(BUILD_DIR)/ide.o: device/ide.c device/ide.h lib/stdint.h thread/sync.h \
113 lib/kernel/list.h kernel/global.h thread/thread.h lib/kernel/bitmap.h \
114 kernel/memory.h lib/kernel/io.h lib/stdio.h lib/stdint.h \
115 lib/kernel/stdio-kernel.h kernel/interrupt.h kernel/debug.h \
116 device/console.h device/timer.h lib/string.h
117 $(CC) $(CFLAGS) $< -o $@
118
119 $(BUILD_DIR)/fs.o: fs/fs.c fs/fs.h fs/super_block.h fs/inode.h fs/dir.h \
120 lib/stdint.h lib/kernel/stdio-kernel.h lib/kernel/list.h lib/string.h \
121 device/ide.h kernel/global.h kernel/debug.h kernel/memory.h \
122 fs/file.h
123 $(CC) $(CFLAGS) $< -o $@
124
125 $(BUILD_DIR)/inode.o: fs/inode.c fs/inode.h lib/stdint.h lib/kernel/list.h \
126 kernel/global.h fs/fs.h device/ide.h thread/sync.h thread/thread.h \
127 lib/kernel/bitmap.h kernel/memory.h fs/file.h kernel/debug.h \
128 kernel/interrupt.h lib/kernel/stdio-kernel.h
129 $(CC) $(CFLAGS) $< -o $@
130
131 $(BUILD_DIR)/file.o: fs/file.c fs/file.h lib/stdint.h device/ide.h thread/sync.h \
132 lib/kernel/list.h kernel/global.h thread/thread.h lib/kernel/bitmap.h \
133 kernel/memory.h fs/fs.h fs/inode.h fs/dir.h lib/kernel/stdio-kernel.h \
134 kernel/debug.h kernel/interrupt.h
135 $(CC) $(CFLAGS) $< -o $@
136
137 $(BUILD_DIR)/dir.o: fs/dir.c fs/dir.h lib/stdint.h fs/inode.h lib/kernel/list.h \
138 kernel/global.h device/ide.h thread/sync.h thread/thread.h \
139 lib/kernel/bitmap.h kernel/memory.h fs/fs.h fs/file.h \
140 lib/kernel/stdio-kernel.h kernel/debug.h kernel/interrupt.h
141 $(CC) $(CFLAGS) $< -o $@
142
143 ############## 汇编代码编译 ###############
144 $(BUILD_DIR)/kernel.o: kernel/kernel.S
145 $(AS) $(ASFLAGS) $< -o $@
146
147 $(BUILD_DIR)/print.o: lib/kernel/print.S
148 $(AS) $(ASFLAGS) $< -o $@
149
150 $(BUILD_DIR)/switch.o: thread/switch.S
151 $(AS) $(ASFLAGS) $< -o $@
152
153 ############## 链接所有目标文件 #############
154 $(BUILD_DIR)/kernel.bin: $(OBJS)
155 $(LD) $(LDFLAGS) $^ -o $@
156
157 .PHONY : mk_dir hd clean all
158
159 mk_dir:
160 if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi
161
162 hd:
163 dd if=$(BUILD_DIR)/kernel.bin \
164 of=/home/zbb/bochs/hd60M.img \
165 bs=512 count=200 seek=9 conv=notrunc
166
167 clean:
168 cd $(BUILD_DIR) && rm -f ./*
169
170 build: $(BUILD_DIR)/kernel.bin
171
172 all: mk_dir build hd
奇怪的是,我发现当我make all后,fs/和lib/kernel/下出现了fs.o和stdio-kernel.o文件,也就是说makefile并没有把它俩删除掉,但是build/下也是有这两个.o文件的,虽然程序能正常运行但是强迫症表示看着很难受,不知道该怎么解决,希望哪位hxd告知一下解决方法,对此万分感谢。
看看运行结果:
这已经是我第二次运行了,所以直接显示了"/file1 has already exist!"。
下面我们用xxd命令显示一下二进制信息,由于我的sdb1的起始扇区号是0xA67即2663,2663*512=1363456得到起始字节,那就可以用命令“sh xxd.sh hd80M.img 1363456 512”查看起始扇区中的内容。
对了好像还没有vim xxd.sh,那就先编写一下,很简单:
xxd -u -a -g 1 -s $2 -l $3 $1
这样我们就有了xxd.sh脚本文件,可以更方便地查看二进制下的hd80M.img。
其中,2E和2E 2E对应 . 和 .. ,02为文件类型f_type;对于file1,可以看到是创建了的,其ASCII码为0x66 0x69 0x6C 0x65 0x31,并且inode=1,f_type=1。
3.文件打开与关闭
①fs/file.c:

1 #include "file.h"
2 #include "fs.h"
3 #include "super_block.h"
4 #include "inode.h"
5 #include "stdio-kernel.h"
6 #include "memory.h"
7 #include "debug.h"
8 #include "interrupt.h"
9 #include "string.h"
10 #include "thread.h"
11 #include "global.h"
12
13 #define DEFAULT_SECT 1
14
15 /* 文件表 */
16 struct file file_table[MAX_FILE_OPEN];
17
18 /* 从文件表file_table中获取一个空闲位,成功返回下标,失败返回-1 */
19 int32_t get_free_slot_in_global(void){
20 uint32_t fd_idx=3;
21 while (fd_idx<MAX_FILE_OPEN){
22 if (file_table[fd_idx].fd_inode==NULL){
23 break;
24 }
25 ++fd_idx;
26 }
27 if (fd_idx==MAX_FILE_OPEN){
28 printk("exceed max open files\n");
29 return -1;
30 }
31 return fd_idx;
32 }
33
34 /* 将全局描述符表下标安装到进程或线程自己的文件描述符数组fd_table中,
35 * 成功则返回下标,失败则返回-1 */
36 int32_t pcb_fd_install(int32_t global_fd_idx){
37 struct task_struct* cur=running_thread();
38 uint8_t local_fd_idx=3; // 跳过stdin,stdout,stderr
39 while (local_fd_idx<MAX_FILES_OPEN_PER_PROC){
40 if (cur->fd_table[local_fd_idx]==-1){
41 cur->fd_table[local_fd_idx]=global_fd_idx;
42 break;
43 }
44 ++local_fd_idx;
45 }
46 if (local_fd_idx==MAX_FILES_OPEN_PER_PROC){
47 printk("exceed max open_per_proc\n");
48 return -1;
49 }
50 return local_fd_idx;
51 }
52
53 /* 分配一个inode,返回inode_no */
54 int32_t inode_bitmap_alloc(struct partition* part){
55 int32_t bit_idx=bitmap_scan(&part->inode_bitmap,1);
56 if (bit_idx==-1){
57 return -1;
58 }
59 bitmap_set(&part->inode_bitmap,bit_idx,1);
60 return bit_idx;
61 }
62
63 /* 分配一个扇区,返回其扇区地址 */
64 int32_t block_bitmap_alloc(struct partition* part){
65 int32_t bit_idx=bitmap_scan(&part->block_bitmap,1);
66 if (bit_idx==-1){
67 return -1;
68 }
69 bitmap_set(&part->block_bitmap,bit_idx,1);
70 /* 和inode_bitmap_malloc不同,此处返回的不是位图索引,而是具体可用的扇区地址 */
71 return (part->sb->data_start_lba+bit_idx);
72 }
73
74 /* 将内存bitmap第bit_idx位所在的512字节同步到硬盘 */
75 void bitmap_sync(struct partition* part,uint32_t bit_idx,uint8_t btmp_type){
76 uint32_t off_sec=bit_idx/4096; // 本inode相对于位图的扇区偏移量
77 uint32_t off_size=off_sec*BLOCK_SIZE; // 本inode相对于位图的字节偏移量
78 uint32_t sec_lba;
79 uint8_t* bitmap_off;
80
81 /* 需要被同步到硬盘的位图只有inode_bitmap和block_bitmap */
82 switch(btmp_type){
83 case INODE_BITMAP:
84 sec_lba=part->sb->inode_bitmap_lba+off_sec;
85 bitmap_off=part->inode_bitmap.bits+off_size;
86 break;
87
88 case BLOCK_BITMAP:
89 sec_lba=part->sb->block_bitmap_lba+off_sec;
90 bitmap_off=part->block_bitmap.bits+off_size;
91 break;
92 }
93 ide_write(part->my_disk,sec_lba,bitmap_off,1);
94 }
95
96 /* 创建文件,若成功则返回文件描述符,否则返回-1 */
97 int32_t file_create(struct dir* parent_dir,char* filename,uint8_t flag){
98 /* 后续操作的公共缓冲区 */
99 void* io_buf=sys_malloc(1024);
100 if (io_buf==NULL){
101 printk("in file_creat: sys_malloc for io_buf failed\n");
102 return -1;
103 }
104
105 uint8_t rollback_step=0; // 用于操作失败时回滚各资源
106
107 /* 为新文件分配inode */
108 int32_t inode_no=inode_bitmap_alloc(cur_part);
109 if (inode_no==-1){
110 printk("in file_create: allocate inode failed\n");
111 return -1;
112 }
113
114 /* 此inode要从堆中申请内存,不可生成局部变量(否则函数退出时会释放)
115 * 因为file_table数组中的文件描述符的inode指针要指向它 */
116 struct inode* new_file_inode=(struct inode*)sys_malloc(sizeof(struct inode));
117 if (new_file_inode==NULL){
118 printk("file_create: sys_malloc for inode failed\n");
119 rollback_step=1;
120 goto rollback;
121 }
122 inode_init(inode_no,new_file_inode); // 初始化inode
123
124 /* 返回的是file_table数组的下标 */
125 int fd_idx=get_free_slot_in_global();
126 if (fd_idx==-1){
127 printk("exceed max open files\n");
128 rollback_step=2;
129 goto rollback;
130 }
131
132 file_table[fd_idx].fd_inode=new_file_inode;
133 file_table[fd_idx].fd_pos=0;
134 file_table[fd_idx].fd_flag=flag;
135 file_table[fd_idx].fd_inode->write_deny=false;
136
137 struct dir_entry new_dir_entry;
138 memset(&new_dir_entry,0,sizeof(struct dir_entry));
139
140 create_dir_entry(filename,inode_no,FT_REGULAR,&new_dir_entry); // create_dir_entry只是内存操作
141
142 /* 同步内存数据到硬盘 */
143 /* 1.在目录parent_dir下安装目录项new_dir_entry,写入硬盘后返回true,否则返回false */
144 if (!sync_dir_entry(parent_dir,&new_dir_entry,io_buf)){
145 printk("sync dir_entry to disk failed\n");
146 rollback_step=3;
147 goto rollback;
148 }
149
150 memset(io_buf,0,1024);
151 /* 2.将父目录inode的内容同步到硬盘 */
152 inode_sync(cur_part,parent_dir->inode,io_buf);
153
154 memset(io_buf,0,1024);
155 /* 3.将新创建的文件的inode内容同步到硬盘 */
156 inode_sync(cur_part,new_file_inode,io_buf);
157
158 /* 4.将inode_bitmap位图同步到硬盘 */
159 bitmap_sync(cur_part,inode_no,INODE_BITMAP);
160
161 /* 5.将创建的文件inode添加到open_inodes链表 */
162 list_push(&cur_part->open_inodes,&new_file_inode->inode_tag);
163 new_file_inode->i_open_cnts=1;
164
165 sys_free(io_buf);
166 return pcb_fd_install(fd_idx);
167
168 /* 创建文件需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
169 rollback:
170 switch(rollback_step){
171 case 3:
172 /* 失败时,将file_table中的相应位清0 */
173 memset(&file_table[fd_idx],0,sizeof(struct file));
174 case 2:
175 sys_free(new_file_inode);
176 case 1:
177 /* 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复 */
178 bitmap_set(&cur_part->inode_bitmap,inode_no,0);
179 break;
180 }
181 sys_free(io_buf);
182 return -1;
183 }
184
185 /* 打开编号为inode_no的inode对应的问价,若成功则返回文件描述符,否则返回-1 */
186 int32_t file_open(uint32_t inode_no,uint8_t flag){
187 int fd_idx=get_free_slot_in_global();
188 if (fd_idx==-1){
189 printk("exceed max open files\n");
190 return -1;
191 }
192 file_table[fd_idx].fd_inode=inode_open(cur_part,inode_no);
193 file_table[fd_idx].fd_pos=0; // 使文件指针指向开头
194 file_table[fd_idx].fd_flag=flag;
195 bool* write_deny=&file_table[fd_idx].fd_inode->write_deny;
196
197 if (flag & O_WRONLY || flag & O_RDWR){ // 可写
198
199 /* 一下进入临界区前先关中断 */
200 enum intr_status old_status=intr_disable();
201 if (!(*write_deny)){
202 *write_deny=true; // 置为true,避免多个进程同时写此文件
203 intr_set_status(old_status); // 恢复中断
204 }else {
205 intr_set_status(old_status);
206 printk("file can't be write now, try again later\n");
207 return -1;
208 }
209 } // 若是可读,则忽略write_deny
210 return pcb_fd_install(fd_idx);
211 }
212
213 /* 关闭文件 */
214 int32_t file_close(struct file* file){
215 if (file==NULL){
216 return -1;
217 }
218 file->fd_inode->write_deny=false;
219 inode_close(file->fd_inode);
220 file->fd_inode=NULL; // 使文件结构可用
221 return 0;
222 }
②fs/file.h增加两句函数声明:
int32_t file_open(uint32_t inode_no,uint8_t flag);
int32_t file_close(struct file* file);
③fs/fs.c:

1 #include "fs.h"
2 #include "super_block.h"
3 #include "inode.h"
4 #include "dir.h"
5 #include "stdint.h"
6 #include "stdio-kernel.h"
7 #include "list.h"
8 #include "string.h"
9 #include "ide.h"
10 #include "global.h"
11 #include "debug.h"
12 #include "memory.h"
13 #include "file.h"
14
15 struct partition* cur_part; // 默认情况下操作的是哪个分区
16
17 /* 在分区中找到名为part_name的分区,并将其指针赋给cur_part */
18 static bool mount_partition(struct list_elem* pelem,int arg){
19 char* part_name=(char*)arg;
20 struct partition* part=elem2entry(struct partition,part_tag,pelem);
21 if (!strcmp(part->name,part_name)){
22 cur_part=part;
23 struct disk* hd=cur_part->my_disk;
24
25 /* sb_buf用来存储从硬盘上读入的超级块 */
26 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
27
28 /* 在内存中创建分区cur_part的超级块 */
29 cur_part->sb=(struct super_block*)sys_malloc(sizeof(struct super_block));
30 if (cur_part->sb==NULL){
31 PANIC("alloc memory failed!");
32 }
33
34 /* 读入超级块 */
35 memset(sb_buf,0,SECTOR_SIZE);
36 ide_read(hd,cur_part->start_lba+1,sb_buf,1);
37
38 /* 把sb_buf中超级块的信息复制到分区的超级块sb中 */
39 memcpy(cur_part->sb,sb_buf,sizeof(struct super_block));
40
41 /********** 将硬盘上的块位图读入到内存 **********/
42 cur_part->block_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->block_bitmap_sects*SECTOR_SIZE);
43 if (cur_part->block_bitmap.bits==NULL){
44 PANIC("alloc memory failed!");
45 }
46 cur_part->block_bitmap.btmp_bytes_len=sb_buf->block_bitmap_sects*SECTOR_SIZE;
47 /* 从硬盘上读入块位图到分区的block_bitmap.bits */
48 ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);
49 /***************************************************/
50
51 /********** 将硬盘上的inode位图读入到内存 **********/
52 cur_part->inode_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects*SECTOR_SIZE);
53 if (cur_part->inode_bitmap.bits==NULL){
54 PANIC("alloc memory failed!");
55 }
56 cur_part->inode_bitmap.btmp_bytes_len=sb_buf->inode_bitmap_sects*SECTOR_SIZE;
57 /* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
58 ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);
59 /**************************************************/
60
61 list_init(&cur_part->open_inodes);
62 printk("mount %s done!\n",part->name);
63
64 /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关
65 * 只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历 */
66 return true;
67 }
68 return false; // 使list_traversal继续遍历
69 }
70
71 /* 初始化分区元信息,创建文件系统 */
72 static void partition_format(struct partition* part){
73 /* 为方便,一个块大小是一扇区 */
74 uint32_t boot_sector_sects=1;
75 uint32_t super_block_sects=1;
76 uint32_t inode_bitmap_sects=DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); // inode位图所占用的扇区数
77 uint32_t inode_table_sects=DIV_ROUND_UP(((sizeof(struct inode)*MAX_FILES_PER_PART)),SECTOR_SIZE);
78 uint32_t used_sects=boot_sector_sects+super_block_sects+inode_bitmap_sects+inode_table_sects;
79 uint32_t free_sects=part->sec_cnt-used_sects;
80
81 /****************** 块位图占用的扇区数 ********************/
82 uint32_t block_bitmap_sects;
83 block_bitmap_sects=DIV_ROUND_UP(free_sects,BITS_PER_SECTOR);
84 /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
85 uint32_t block_bitmap_bit_len=free_sects-block_bitmap_sects;
86 block_bitmap_sects=DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);
87 /*********************************************************/
88
89 /* 超级块初始化 */
90 struct super_block sb;
91 sb.magic=0x19590318;
92 sb.sec_cnt=part->sec_cnt;
93 sb.inode_cnt=MAX_FILES_PER_PART;
94 sb.part_lba_base=part->start_lba;
95
96 sb.block_bitmap_lba=sb.part_lba_base+2; // 第0块为bootblock,第1块为superblock
97 sb.block_bitmap_sects=block_bitmap_sects;
98
99 sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sects;
100 sb.inode_bitmap_sects=inode_bitmap_sects;
101
102 sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sects;
103 sb.inode_table_sects=inode_table_sects;
104
105 sb.data_start_lba=sb.inode_table_lba+sb.inode_table_sects;
106 sb.root_inode_no=0;
107 sb.dir_entry_size=sizeof(struct dir_entry);
108
109 printk("%s info:\n",part->name);
110 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);
111
112 struct disk* hd=part->my_disk;
113 /****************************
114 * 1.将超级块写入本分区的1扇区
115 * *************************/
116 ide_write(hd,part->start_lba+1,&sb,1);
117 printk(" super_block_lba:0x%x\n",part->start_lba+1);
118
119 /* 找出数据量最大的元信息,用其尺寸做存储缓冲区 */
120 uint32_t buf_size=(sb.block_bitmap_sects>=sb.inode_bitmap_sects?sb.block_bitmap_sects:sb.inode_bitmap_sects);
121 buf_size=(buf_size>=sb.inode_table_sects?buf_size:sb.inode_table_sects)*SECTOR_SIZE;
122 uint8_t* buf=(uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回
123
124 /****************************
125 * 2.将块位图初始化并写入sb.block_bitmap_lba
126 * *************************/
127 /* 初始化块位图block_bitmap */
128 buf[0]|=0x01; // 第0个块预留给根目录,位图中先占位
129 uint32_t block_bitmap_last_byte=block_bitmap_bit_len/8;
130 uint8_t block_bitmap_last_bit=block_bitmap_bit_len%8;
131 uint32_t last_size=SECTOR_SIZE-(block_bitmap_last_byte%SECTOR_SIZE); // last_size是位图中的填充部分
132
133 /* 2.1先将位图最后一字节到其所在扇区结束全置为1,即超出实际块数部分直接置为已占用 */
134 memset(&buf[block_bitmap_last_byte],0xff,last_size);
135
136 /* 2.2再将上一步中覆盖的最后一字节内有效位重新置0 */
137 uint8_t bit_idx=0;
138 while (bit_idx<=block_bitmap_last_bit){
139 buf[block_bitmap_last_byte]&=~(1<<bit_idx++);
140 }
141 ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);
142
143 /***************************
144 * 3.将inode位图初始化并写入sb.inode_bitmap_lba
145 * ************************/
146 /* 先清空缓冲区 */
147 memset(buf,0,buf_size);
148 buf[0]|=0x1; // 第0个inode分给了根目录
149 /* 由于inode_table中共4096个inode,位图中inode_bitmap正好占用1扇区,
150 * 即inode_bitmap_sects等于1,所以位图中的位全都代表inode_table中的inode,
151 * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
152 * inode_bitmap所在的扇区中没有多余的无效位 */
153 ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects);
154
155 /*************************
156 * 4.将inode数组初始化并写入sb.inode_table_lba
157 * **********************/
158 /* 准备写inode_table中的第0项,即根目录所在的inode */
159 memset(buf,0,buf_size); // 先清空缓冲区buf
160 struct inode* i=(struct inode*)buf;
161 i->i_size=sb.dir_entry_size*2; // . & ..
162 i->i_no=0; // 根目录占inode数组中第0个inode
163 i->i_sectors[0]=sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
164 ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);
165
166 /*************************
167 * 5.将根目录初始化并写入sb.data_start_lba
168 * **********************/
169 /* 写入根目录的两个目录项 . & .. */
170 memset(buf,0,buf_size);
171 struct dir_entry* p_de=(struct dir_entry*)buf;
172
173 /* 初始化当前目录"." */
174 memcpy(p_de->filename,".",1);
175 p_de->i_no=0;
176 p_de->f_type=FT_DIRECTORY;
177 ++p_de;
178
179 /* 初始化当前目录父目录 ".." */
180 memcpy(p_de->filename,"..",2);
181 p_de->i_no=0; // 根目录的父目录是自己
182 p_de->f_type=FT_DIRECTORY;
183
184 /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
185 ide_write(hd,sb.data_start_lba,buf,1);
186
187 printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
188 printk("%s format done\n",part->name);
189 sys_free(buf);
190 }
191
192 /* 将最上层路径名称解析出来 */
193 static char* path_parse(char* pathname,char* name_store){
194 if (pathname[0]=='/'){ // 根目录不需要单独解析
195 /* 路径中出现1个或多个连续的字符 '/',将这些'/'跳过 */
196 while (*(++pathname)=='/');
197 }
198
199 /* 开始一般的路径解析 */
200 while (*pathname!='/' && *pathname!=0){
201 *name_store++=*pathname++;
202 }
203
204 if (pathname[0]==0){ // 路径字符串为空则返回NULL
205 return NULL;
206 }
207 return pathname;
208 }
209
210 /* 返回路径深度,比如/a/b/c,深度为3 */
211 int32_t path_depth_cnt(char* pathname){
212 ASSERT(pathname!=NULL);
213 char* p=pathname;
214 char name[MAX_FILE_NAME_LEN]; // 用于path_parse路径解析
215 uint32_t depth=0;
216
217 /* 解析路径,从中拆分出各级名称 */
218 p=path_parse(p,name);
219 while (name[0]){
220 ++depth;
221 memset(name,0,MAX_FILE_NAME_LEN);
222 if (p){ // 若p!=NULL
223 p=path_parse(p,name);
224 }
225 }
226 return depth;
227 }
228
229 /* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */
230 static int search_file(const char* pathname,struct path_search_record* searched_record){
231 /* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */
232 if (!strcmp(pathname,"/") || !strcmp(pathname,"/.") || !strcmp(pathname,"/..")){
233 searched_record->parent_dir=&root_dir;
234 searched_record->file_type=FT_DIRECTORY;
235 searched_record->searched_path[0]=0; // 搜索路径置空
236 return 0;
237 }
238
239 uint32_t path_len=strlen(pathname);
240 /* 保证pathname至少是这样的路径/x,且小于最大长度 */
241 ASSERT(pathname[0]=='/' && path_len>1 && path_len<MAX_PATH_LEN);
242 char* sub_path=(char*)pathname;
243 struct dir* parent_dir=&root_dir;
244 struct dir_entry dir_e;
245
246 /* 记录路径解析出来的各级名称,如路径"/a/b/c",
247 * 数组name每次的值是"a","b","c" */
248 char name[MAX_FILE_NAME_LEN]={0};
249
250 searched_record->parent_dir=parent_dir;
251 searched_record->file_type=FT_UNKNOWN;
252 uint32_t parent_inode_no=0; // 父目录的inode号
253
254 sub_path=path_parse(sub_path,name);
255 while (name[0]){ // 若第一个字符就是结束符,结束循环
256 /* 记录查找过的路径,但不能超过searched_path的长度512Byte */
257 ASSERT(strlen(searched_record->searched_path)<512);
258
259 /* 记录已存在的父目录 */
260 strcat(searched_record->searched_path,'/');
261 strcat(searched_record->searched_path,name);
262
263 /* 在所给的目录中查找文件 */
264 if (search_dir_entry(cur_part,parent_dir,name,&dir_e)){
265 memset(name,0,MAX_FILE_NAME_LEN);
266 /* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */
267 if (sub_path){
268 sub_path=path_parse(sub_path,name);
269 }
270
271 if (FT_DIRECTORY==dir_e.f_type){ // 如果被打开的是目录
272 parent_inode_no=parent_dir->inode->i_no;
273 dir_close(parent_dir);
274 parent_dir=dir_open(cur_part,dir_e.i_no); // 更新父目录
275 searched_record->parent_dir=parent_dir;
276 continue;
277 }else if (FT_REGULAR==dir_e.f_type){
278 searched_record->file_type=FT_REGULAR;
279 return dir_e.i_no;
280 }
281 }else { // 若找不到
282 /* 找不到目录项时,要留着parent_dir不要关闭,
283 * 若是创建新文件的话需要在parent_dir中创建 */
284 return -1;
285 }
286 }
287
288 /* 遍历了完整的路径并且查找的文件或目录只有同名目录存在 */
289 dir_close(searched_record->parent_dir);
290
291 /* 保存被查找目录的直接父目录 */
292 searched_record->parent_dir=dir_open(cur_part,parent_inode_no);
293 searched_record->file_type=FT_DIRECTORY;
294 return dir_e.i_no;
295 }
296
297 /* 打开或创建文件成功后,返回文件描述符,否则返回-1 */
298 int32_t sys_open(const char* pathname,uint8_t flags){
299 /* 对目录要用dir_open,这里只有open文件 */
300 if (pathname[strlen(pathname)-1]=='/'){
301 printk("can't open a directory %s\n",pathname);
302 return -1;
303 }
304 ASSERT(flags<=7);
305 int32_t fd=-1;
306
307 struct path_search_record searched_record;
308 memset(&searched_record,0,sizeof(struct path_search_record));
309
310 /* 记录目录深度,帮助判断中间某个目录不存在的情况 */
311 uint32_t pathname_depth=path_depth_cnt((char*)pathname);
312
313 /* 先检查文件是否存在 */
314 int inode_no=search_file(pathname,&searched_record);
315 bool found=inode_no!=-1?true:false;
316
317 if (searched_record.file_type==FT_DIRECTORY){
318 printk("can't open a directory with open(),use opendir() to instead\n");
319 dir_close(searched_record.parent_dir);
320 return -1;
321 }
322
323 uint32_t path_searched_depth=path_depth_cnt(searched_record.searched_path);
324
325 /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
326 if (pathname_depth!=path_searched_depth){ // 说明并没有访问到全部路径,某个中间目录是不存在的
327 printk("cannot access %s: Not a directory, subpath %s is't exist\n", \
328 pathname,searched_record.searched_path);
329 dir_close(searched_record.parent_dir);
330 return -1;
331 }
332
333 /* 若是最后一个路径上没找到,并且不是要创建文件,直接返回-1 */
334 if (!found && !(flags & O_CREAT)){
335 printk("in path %s, file %s is't exist\n", \
336 searched_record.searched_path,
337 (strrchr(searched_record.searched_path,'/')+1));
338 dir_close(searched_record.parent_dir);
339 return -1;
340 }else if (found && flags & O_CREAT){ // 若要创建的文件已存在
341 printk("%s has already exist!\n",pathname);
342 dir_close(searched_record.parent_dir);
343 return -1;
344 }
345
346 switch (flags & O_CREAT){
347 case O_CREAT:
348 printk("creating file\n");
349 fd=file_create(searched_record.parent_dir,(strrchr(pathname,'/')+1),flags);
350 dir_close(searched_record.parent_dir);
351 break;
352 default:
353 /* 其余情况为打开已存在文件
354 * O_REONLY,O_WRONLY,O_REWR */
355 fd=file_open(inode_no,flags);
356 }
357
358 /* 此fd是指任务pcb->fd_table数组中的元素下标,
359 * 并不是指全局file_table中的下标 */
360 return fd;
361 }
362
363 /* 将文件描述符转化为文件表的下标 */
364 static uint32_t fd_local2global(uint32_t local_fd){
365 struct task_struct* cur=running_thread();
366 int32_t global_fd=cur->fd_table[local_fd];
367 ASSERT(global_fd>=0 && global_fd<MAX_FILE_OPEN);
368 return (uint32_t)global_fd;
369 }
370
371 /* 关闭文件描述符fd指向的文件,成功返回0,否则-1 */
372 int32_t sys_close(int32_t fd){
373 int32_t ret=-1;
374 if (fd>2){
375 uint32_t _fd=fd_local2global(fd);
376 ret=file_close(&file_table[_fd]);
377 running_thread()->fd_table[fd]=-1; // 使该文件描述符可用
378 }
379 return ret;
380 }
381
382 /* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
383 void filesys_init(){
384 uint8_t channel_no=0,dev_no=0,part_idx=0;
385
386 /* sb_buf用来存储从硬盘上读入的超级块 */
387 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
388
389 if (sb_buf==NULL){
390 PANIC("alloc memory failed!");
391 }
392 printk("searching filesystem......\n");
393 while (channel_no<channel_cnt){
394 dev_no=0;
395 while (dev_no<2){
396 if (dev_no==0){
397 ++dev_no;
398 continue;
399 }
400 struct disk* hd=&channels[channel_no].devices[dev_no];
401 struct partition* part=hd->prim_parts;
402 while (part_idx<12){ // 四个主分区+八个逻辑分区
403 if (part_idx==4){ // 逻辑分区
404 part=hd->logic_parts;
405 }
406
407 /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
408 * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
409 * 若partition未初始化,则partition中的成员仍为0.
410 * 下面处理存在的分区. */
411 if (part->sec_cnt!=0){ // 如果分区存在
412 memset(sb_buf,0,SECTOR_SIZE);
413
414 /* 读出分区超级块,根据魔数是否正确来判断是否存在fs */
415 ide_read(hd,part->start_lba+1,sb_buf,1);
416
417 /* 只支持自己的文件系统,若磁盘上已经有fs就不再初始化了 */
418 if (sb_buf->magic==0x19590318){
419 printk("%s has filesystem\n",part->name);
420 }else{
421 printk("formatting %s's partition %s.....\n",hd->name,part->name);
422 partition_format(part);
423 }
424 }
425 ++part_idx;
426 ++part; // 下一分区
427 }
428 ++dev_no; //下一磁盘
429 }
430 ++channel_no; // 下一通道
431 }
432 sys_free(sb_buf);
433
434 /* 确定默认操作的分区 */
435 char default_part[8]="sdb1";
436 /* 挂载分区 */
437 list_traversal(&partition_list,mount_partition,(int)default_part);
438
439 /* 将当前分区的根目录打开 */
440 open_root_dir(cur_part);
441
442 /* 初始化文件表 */
443 uint32_t fd_idx=0;
444 while (fd_idx<MAX_FILE_OPEN){
445 file_table[fd_idx++].fd_inode=NULL;
446 }
447 }
④fs/fs.h,增加一句函数声明:
int32_t sys_close(int32_t fd);
⑤kernel/mian.c:
看下运行结果吧:
成功输出了“fd:3”和“3 closed now”,应该是没啥问题的。
4.实现文件写入
①fs/file.c:

1 #include "file.h"
2 #include "fs.h"
3 #include "super_block.h"
4 #include "inode.h"
5 #include "stdio-kernel.h"
6 #include "memory.h"
7 #include "debug.h"
8 #include "interrupt.h"
9 #include "string.h"
10 #include "thread.h"
11 #include "global.h"
12
13 #define DEFAULT_SECT 1
14
15 /* 文件表 */
16 struct file file_table[MAX_FILE_OPEN];
17
18 /* 从文件表file_table中获取一个空闲位,成功返回下标,失败返回-1 */
19 int32_t get_free_slot_in_global(void){
20 uint32_t fd_idx=3;
21 while (fd_idx<MAX_FILE_OPEN){
22 if (file_table[fd_idx].fd_inode==NULL){
23 break;
24 }
25 ++fd_idx;
26 }
27 if (fd_idx==MAX_FILE_OPEN){
28 printk("exceed max open files\n");
29 return -1;
30 }
31 return fd_idx;
32 }
33
34 /* 将全局描述符表下标安装到进程或线程自己的文件描述符数组fd_table中,
35 * 成功则返回下标,失败则返回-1 */
36 int32_t pcb_fd_install(int32_t global_fd_idx){
37 struct task_struct* cur=running_thread();
38 uint8_t local_fd_idx=3; // 跳过stdin,stdout,stderr
39 while (local_fd_idx<MAX_FILES_OPEN_PER_PROC){
40 if (cur->fd_table[local_fd_idx]==-1){
41 cur->fd_table[local_fd_idx]=global_fd_idx;
42 break;
43 }
44 ++local_fd_idx;
45 }
46 if (local_fd_idx==MAX_FILES_OPEN_PER_PROC){
47 printk("exceed max open_per_proc\n");
48 return -1;
49 }
50 return local_fd_idx;
51 }
52
53 /* 分配一个inode,返回inode_no */
54 int32_t inode_bitmap_alloc(struct partition* part){
55 int32_t bit_idx=bitmap_scan(&part->inode_bitmap,1);
56 if (bit_idx==-1){
57 return -1;
58 }
59 bitmap_set(&part->inode_bitmap,bit_idx,1);
60 return bit_idx;
61 }
62
63 /* 分配一个扇区,返回其扇区地址 */
64 int32_t block_bitmap_alloc(struct partition* part){
65 int32_t bit_idx=bitmap_scan(&part->block_bitmap,1);
66 if (bit_idx==-1){
67 return -1;
68 }
69 bitmap_set(&part->block_bitmap,bit_idx,1);
70 /* 和inode_bitmap_malloc不同,此处返回的不是位图索引,而是具体可用的扇区地址 */
71 return (part->sb->data_start_lba+bit_idx);
72 }
73
74 /* 将内存bitmap第bit_idx位所在的512字节同步到硬盘 */
75 void bitmap_sync(struct partition* part,uint32_t bit_idx,uint8_t btmp_type){
76 uint32_t off_sec=bit_idx/4096; // 本inode相对于位图的扇区偏移量
77 uint32_t off_size=off_sec*BLOCK_SIZE; // 本inode相对于位图的字节偏移量
78 uint32_t sec_lba;
79 uint8_t* bitmap_off;
80
81 /* 需要被同步到硬盘的位图只有inode_bitmap和block_bitmap */
82 switch(btmp_type){
83 case INODE_BITMAP:
84 sec_lba=part->sb->inode_bitmap_lba+off_sec;
85 bitmap_off=part->inode_bitmap.bits+off_size;
86 break;
87
88 case BLOCK_BITMAP:
89 sec_lba=part->sb->block_bitmap_lba+off_sec;
90 bitmap_off=part->block_bitmap.bits+off_size;
91 break;
92 }
93 ide_write(part->my_disk,sec_lba,bitmap_off,1);
94 }
95
96 /* 创建文件,若成功则返回文件描述符,否则返回-1 */
97 int32_t file_create(struct dir* parent_dir,char* filename,uint8_t flag){
98 /* 后续操作的公共缓冲区 */
99 void* io_buf=sys_malloc(1024);
100 if (io_buf==NULL){
101 printk("in file_creat: sys_malloc for io_buf failed\n");
102 return -1;
103 }
104
105 uint8_t rollback_step=0; // 用于操作失败时回滚各资源
106
107 /* 为新文件分配inode */
108 int32_t inode_no=inode_bitmap_alloc(cur_part);
109 if (inode_no==-1){
110 printk("in file_create: allocate inode failed\n");
111 return -1;
112 }
113
114 /* 此inode要从堆中申请内存,不可生成局部变量(否则函数退出时会释放)
115 * 因为file_table数组中的文件描述符的inode指针要指向它 */
116 struct inode* new_file_inode=(struct inode*)sys_malloc(sizeof(struct inode));
117 if (new_file_inode==NULL){
118 printk("file_create: sys_malloc for inode failed\n");
119 rollback_step=1;
120 goto rollback;
121 }
122 inode_init(inode_no,new_file_inode); // 初始化inode
123
124 /* 返回的是file_table数组的下标 */
125 int fd_idx=get_free_slot_in_global();
126 if (fd_idx==-1){
127 printk("exceed max open files\n");
128 rollback_step=2;
129 goto rollback;
130 }
131
132 file_table[fd_idx].fd_inode=new_file_inode;
133 file_table[fd_idx].fd_pos=0;
134 file_table[fd_idx].fd_flag=flag;
135 file_table[fd_idx].fd_inode->write_deny=false;
136
137 struct dir_entry new_dir_entry;
138 memset(&new_dir_entry,0,sizeof(struct dir_entry));
139
140 create_dir_entry(filename,inode_no,FT_REGULAR,&new_dir_entry); // create_dir_entry只是内存操作
141
142 /* 同步内存数据到硬盘 */
143 /* 1.在目录parent_dir下安装目录项new_dir_entry,写入硬盘后返回true,否则返回false */
144 if (!sync_dir_entry(parent_dir,&new_dir_entry,io_buf)){
145 printk("sync dir_entry to disk failed\n");
146 rollback_step=3;
147 goto rollback;
148 }
149
150 memset(io_buf,0,1024);
151 /* 2.将父目录inode的内容同步到硬盘 */
152 inode_sync(cur_part,parent_dir->inode,io_buf);
153
154 memset(io_buf,0,1024);
155 /* 3.将新创建的文件的inode内容同步到硬盘 */
156 inode_sync(cur_part,new_file_inode,io_buf);
157
158 /* 4.将inode_bitmap位图同步到硬盘 */
159 bitmap_sync(cur_part,inode_no,INODE_BITMAP);
160
161 /* 5.将创建的文件inode添加到open_inodes链表 */
162 list_push(&cur_part->open_inodes,&new_file_inode->inode_tag);
163 new_file_inode->i_open_cnts=1;
164
165 sys_free(io_buf);
166 return pcb_fd_install(fd_idx);
167
168 /* 创建文件需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
169 rollback:
170 switch(rollback_step){
171 case 3:
172 /* 失败时,将file_table中的相应位清0 */
173 memset(&file_table[fd_idx],0,sizeof(struct file));
174 case 2:
175 sys_free(new_file_inode);
176 case 1:
177 /* 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复 */
178 bitmap_set(&cur_part->inode_bitmap,inode_no,0);
179 break;
180 }
181 sys_free(io_buf);
182 return -1;
183 }
184
185 /* 打开编号为inode_no的inode对应的问价,若成功则返回文件描述符,否则返回-1 */
186 int32_t file_open(uint32_t inode_no,uint8_t flag){
187 int fd_idx=get_free_slot_in_global();
188 if (fd_idx==-1){
189 printk("exceed max open files\n");
190 return -1;
191 }
192 file_table[fd_idx].fd_inode=inode_open(cur_part,inode_no);
193 file_table[fd_idx].fd_pos=0; // 使文件指针指向开头
194 file_table[fd_idx].fd_flag=flag;
195 bool* write_deny=&file_table[fd_idx].fd_inode->write_deny;
196
197 if (flag & O_WRONLY || flag & O_RDWR){ // 可写
198
199 /* 一下进入临界区前先关中断 */
200 enum intr_status old_status=intr_disable();
201 if (!(*write_deny)){
202 *write_deny=true; // 置为true,避免多个进程同时写此文件
203 intr_set_status(old_status); // 恢复中断
204 }else {
205 intr_set_status(old_status);
206 printk("file can't be write now, try again later\n");
207 return -1;
208 }
209 } // 若是可读,则忽略write_deny
210 return pcb_fd_install(fd_idx);
211 }
212
213 /* 关闭文件 */
214 int32_t file_close(struct file* file){
215 if (file==NULL){
216 return -1;
217 }
218 file->fd_inode->write_deny=false;
219 inode_close(file->fd_inode);
220 file->fd_inode=NULL; // 使文件结构可用
221 return 0;
222 }
223
224 /* 把buf中的count字节写入file,成功则返回写入的字节数,失败则返回-1 */
225 int32_t file_write(struct file* file,const void* buf,uint32_t count){
226 if ((file->fd_inode->i_size+count)>(BLOCK_SIZE*140)){
227 printk("exceed max file_size 71680B, write file failed\n");
228 return -1;
229 }
230 uint8_t* io_buf=sys_malloc(BLOCK_SIZE);
231 if (io_buf==NULL){
232 printk("file_write: sys_malloc for all_blocks failed\n");
233 return -1;
234 }
235 uint32_t* all_blocks=(uint32_t*)sys_malloc(BLOCK_SIZE+48); // 用来记录文件所有的块地址
236 if (all_blocks==NULL){
237 printk("file_write: sys_malloc for all_blocks failed\n");
238 return -1;
239 }
240
241 const uint8_t* src=buf; // 用src指向buf中待写入的数据
242 uint32_t bytes_written=0; // 用来记录已写入的数据大小
243 uint32_t size_left=count; // 用来记录未写入数据大小
244 int32_t block_lba=-1; // 块地址
245 uint32_t block_bitmap_idx=0;// 用来记录block对应于block_bitmap中的索引,作为参数传给bitmap_sync
246 uint32_t sec_idx; // 用来索引扇区
247 uint32_t sec_lba; // 扇区地址
248 uint32_t sec_off_bytes; // 扇区内字节偏移量
249 uint32_t sec_left_bytes; // 扇区内剩余字节量
250 uint32_t chunk_size; // 每次写入硬盘的数据块大小
251 int32_t indirect_block_table; // 用来获取一级间接表地址
252 uint32_t block_idx; // 块索引
253
254 /* 判断文件是否是第一次写,如果是则先为其分配一个块 */
255 if (file->fd_inode->i_sectors[0]==0){
256 block_lba=block_bitmap_alloc(cur_part);
257 if (block_lba==-1){
258 printk("file_write: block_bitmap_alloc failed\n");
259 return -1;
260 }
261 file->fd_inode->i_sectors[0]=block_lba;
262
263 /* 每分配一个块就将位图同步到硬盘 */
264 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
265 ASSERT(block_bitmap_idx!=0);
266 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
267 }
268
269 /* 写入count个字节前,该文件已经占用的块数 */
270 uint32_t file_has_used_blocks=file->fd_inode->i_size/BLOCK_SIZE+1;
271
272 /* 存储count字节后该文件将占用的块数 */
273 uint32_t file_will_use_blocks=(file->fd_inode->i_size+count)/BLOCK_SIZE+1;
274 ASSERT(file_will_use_blocks<=140);
275
276 /* 通过此增量判断是否需要分配扇区,如增量为0,表示原扇区够用 */
277 uint32_t add_blocks=file_will_use_blocks-file_has_used_blocks;
278
279 /* 开始将文件所以块地址收集到all_blocks,(系统中块大小等于扇区大小)
280 * 后面都统一在all_blocks中获取写入扇区地址 */
281 if (add_blocks==0){
282 /* 在同一扇区写入数据,不涉及到分配新扇区 */
283 if (file_has_used_blocks<=12){
284 block_idx=file_has_used_blocks-1;
285 all_blocks[block_idx]=file->fd_inode->i_sectors[block_idx];
286 }else {
287 /* 未写入新数据之前已经占用了间接块,需要将间接块地址读进来 */
288 ASSERT(file->fd_inode->i_sectors[12]!=0);
289 indirect_block_table=file->fd_inode->i_sectors[12];
290 ide_read(cur_part->my_disk,indirect_block_table,all_blocks+12,1);
291 }
292 }else {
293 /* 若有增量,便涉及到分配新扇区是否分配一级间接块表,下面分三种情况:
294 * 1. 12个直接块够用 */
295 if (file_will_use_blocks<=12){
296 /* 先将剩余空间可继续用的扇区地址写入all_blocks */
297 block_idx=file_has_used_blocks-1;
298 ASSERT(file->fd_inode->i_sectors[block_idx]!=0)
299 all_blocks[block_idx]=file->fd_inode->i_sectors[block_idx];
300
301 /* 再将未来要用的扇区分配号后写入all_blocks */
302 block_idx=file_has_used_blocks; // 指向第一个要分配的新扇区
303 while (block_idx<file_will_use_blocks){
304 block_lba=block_bitmap_alloc(cur_part);
305 if (block_lba==-1){
306 printk("file_write: block_bitmap_alloc for situation 1 failed\n");
307 return -1;
308 }
309
310 /* 写文件时,不应该存在块未使用但已分配扇区的情况,当文件删除时,就会把块地址清0 */
311 ASSERT(file->fd_inode->i_sectors[block_idx]==0); // 确保尚未分配扇区地址
312 file->fd_inode->i_sectors[block_idx]=all_blocks[block_idx]=block_lba;
313
314 /* 每分配一个块就将位图同步到硬盘 */
315 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
316 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
317
318 ++block_idx; // 下一个分配的新扇区
319 }
320 }else if (file_has_used_blocks<=12 && file_will_use_blocks>12){
321 /* 2. 旧数据在12个直接块内,新数据将使用间接块 */
322
323 /* 先将剩余空间的可继续用的扇区地址收集到all_blocks */
324 block_idx=file_has_used_blocks-1; // 指向旧数据所在的最后一个扇区
325 all_blocks[block_idx]=file->fd_inode->i_sectors[block_idx];
326
327 /* 创建一级间接块表 */
328 block_lba=block_bitmap_alloc(cur_part);
329 if (block_lba==-1){
330 printk("file_write: block_bitmap_alloc for situation 2 failed\n");
331 return -1;
332 }
333
334 ASSERT(file->fd_inode->i_sectors[12]==0); // 确保一级间接块表未分配
335 /* 分配一级间接块索引表 */
336 indirect_block_table=file->fd_inode->i_sectors[12]=block_lba;
337
338 block_idx=file_has_used_blocks; // 第一个未使用的块
339 while (block_idx<file_will_use_blocks){
340 block_lba=block_bitmap_alloc(cur_part);
341 if (block_lba==-1){
342 printk("file_write: block_bitmap_alloc for situation 2 failed\n");
343 return -1;
344 }
345
346 if (block_idx<12){ // 新创建的0~11块直接存入all_blocks数组
347 ASSERT(file->fd_inode->i_sectors[block_idx]==0);
348 file->fd_inode->i_sectors[block_idx]=all_blocks[block_idx]=block_lba;
349 }else { // 间接块只写入到all_block数组中,待全部分配完成后一次性同步到硬盘
350 all_blocks[block_idx]=block_lba;
351 }
352
353 /* 每分配一个块就将位图同步到硬盘 */
354 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
355 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
356
357 ++block_idx; // 下一个新扇区
358 }
359 ide_write(cur_part->my_disk,indirect_block_table,all_blocks+12,1);
360 }else if (file_has_used_blocks>12){
361 /* 3. 新数据占据间接块 */
362 ASSERT(file->fd_inode->i_sectors[12]!=0); // 已经具备了一级间接块表
363 indirect_block_table=file->fd_inode->i_sectors[12]; // 获取一级间接块表地址
364
365 /* 已使用的间接块也将被读入all_blocks,无须单独收录 */
366 ide_read(cur_part->my_disk,indirect_block_table,all_blocks+12,1); // 获取所有间接块地址
367
368 block_idx=file_has_used_blocks; // 第一个未使用的间接块,即已经使用的间接块的下一块
369 while (block_idx<file_will_use_blocks){
370 block_lba=block_bitmap_alloc(cur_part);
371 if (block_lba==-1){
372 printk("file_wirte: block_bitmap_alloc for situation 3 failed\n");
373 return -1;
374 }
375 all_blocks[block_idx++]=block_lba;
376
377 /* 每分配一个块就将位图同步到硬盘 */
378 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
379 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
380 }
381 ide_write(cur_part->my_disk,indirect_block_table,all_blocks+12,1);
382 }
383 }
384
385 bool first_write_block=true; // 含有剩余空间的扇区标识
386 /* 块地址已经收集到all_blocks中,下面开始写数据 */
387 file->fd_pos=file->fd_inode->i_size-1; // 置fd_pos为文件大小-1,下面在写数据时随时更新
388 while (bytes_written<count){ // 直到写完所有数据
389 memset(io_buf,0,BLOCK_SIZE);
390 sec_idx=file->fd_inode->i_size/BLOCK_SIZE;
391 sec_lba=all_blocks[sec_idx];
392 sec_off_bytes=file->fd_inode->i_size%BLOCK_SIZE;
393 sec_left_bytes=BLOCK_SIZE-sec_off_bytes;
394
395 /* 判断此次写入硬盘的数据大小 */
396 chunk_size=size_left<sec_left_bytes?size_left:sec_left_bytes;
397 if (first_write_block){
398 ide_read(cur_part->my_disk,sec_lba,io_buf,1);
399 first_write_block=false;
400 }
401 memcpy(io_buf+sec_off_bytes,src,chunk_size);
402 ide_write(cur_part->my_disk,sec_lba,io_buf,1);
403
404 src+=chunk_size;
405 file->fd_inode->i_size+=chunk_size; // 更新文件大小
406 file->fd_pos+=chunk_size;
407 bytes_written+=chunk_size;
408 size_left-=chunk_size;
409 }
410 inode_sync(cur_part,file->fd_inode,io_buf);
411 sys_free(all_blocks);
412 sys_free(io_buf);
413 return bytes_written;
414 }
②fs/file.h,增加一句函数声明:
int32_t file_write(struct file* file,const void* buf,uint32_t count);
③fs/fs.c:

1 #include "fs.h"
2 #include "super_block.h"
3 #include "inode.h"
4 #include "dir.h"
5 #include "stdint.h"
6 #include "stdio-kernel.h"
7 #include "list.h"
8 #include "string.h"
9 #include "ide.h"
10 #include "global.h"
11 #include "debug.h"
12 #include "memory.h"
13 #include "file.h"
14 #include "console.h"
15
16 struct partition* cur_part; // 默认情况下操作的是哪个分区
17
18 /* 在分区中找到名为part_name的分区,并将其指针赋给cur_part */
19 static bool mount_partition(struct list_elem* pelem,int arg){
20 char* part_name=(char*)arg;
21 struct partition* part=elem2entry(struct partition,part_tag,pelem);
22 if (!strcmp(part->name,part_name)){
23 cur_part=part;
24 struct disk* hd=cur_part->my_disk;
25
26 /* sb_buf用来存储从硬盘上读入的超级块 */
27 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
28
29 /* 在内存中创建分区cur_part的超级块 */
30 cur_part->sb=(struct super_block*)sys_malloc(sizeof(struct super_block));
31 if (cur_part->sb==NULL){
32 PANIC("alloc memory failed!");
33 }
34
35 /* 读入超级块 */
36 memset(sb_buf,0,SECTOR_SIZE);
37 ide_read(hd,cur_part->start_lba+1,sb_buf,1);
38
39 /* 把sb_buf中超级块的信息复制到分区的超级块sb中 */
40 memcpy(cur_part->sb,sb_buf,sizeof(struct super_block));
41
42 /********** 将硬盘上的块位图读入到内存 **********/
43 cur_part->block_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->block_bitmap_sects*SECTOR_SIZE);
44 if (cur_part->block_bitmap.bits==NULL){
45 PANIC("alloc memory failed!");
46 }
47 cur_part->block_bitmap.btmp_bytes_len=sb_buf->block_bitmap_sects*SECTOR_SIZE;
48 /* 从硬盘上读入块位图到分区的block_bitmap.bits */
49 ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);
50 /***************************************************/
51
52 /********** 将硬盘上的inode位图读入到内存 **********/
53 cur_part->inode_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects*SECTOR_SIZE);
54 if (cur_part->inode_bitmap.bits==NULL){
55 PANIC("alloc memory failed!");
56 }
57 cur_part->inode_bitmap.btmp_bytes_len=sb_buf->inode_bitmap_sects*SECTOR_SIZE;
58 /* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
59 ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);
60 /**************************************************/
61
62 list_init(&cur_part->open_inodes);
63 printk("mount %s done!\n",part->name);
64
65 /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关
66 * 只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历 */
67 return true;
68 }
69 return false; // 使list_traversal继续遍历
70 }
71
72 /* 初始化分区元信息,创建文件系统 */
73 static void partition_format(struct partition* part){
74 /* 为方便,一个块大小是一扇区 */
75 uint32_t boot_sector_sects=1;
76 uint32_t super_block_sects=1;
77 uint32_t inode_bitmap_sects=DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); // inode位图所占用的扇区数
78 uint32_t inode_table_sects=DIV_ROUND_UP(((sizeof(struct inode)*MAX_FILES_PER_PART)),SECTOR_SIZE);
79 uint32_t used_sects=boot_sector_sects+super_block_sects+inode_bitmap_sects+inode_table_sects;
80 uint32_t free_sects=part->sec_cnt-used_sects;
81
82 /****************** 块位图占用的扇区数 ********************/
83 uint32_t block_bitmap_sects;
84 block_bitmap_sects=DIV_ROUND_UP(free_sects,BITS_PER_SECTOR);
85 /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
86 uint32_t block_bitmap_bit_len=free_sects-block_bitmap_sects;
87 block_bitmap_sects=DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);
88 /*********************************************************/
89
90 /* 超级块初始化 */
91 struct super_block sb;
92 sb.magic=0x19590318;
93 sb.sec_cnt=part->sec_cnt;
94 sb.inode_cnt=MAX_FILES_PER_PART;
95 sb.part_lba_base=part->start_lba;
96
97 sb.block_bitmap_lba=sb.part_lba_base+2; // 第0块为bootblock,第1块为superblock
98 sb.block_bitmap_sects=block_bitmap_sects;
99
100 sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sects;
101 sb.inode_bitmap_sects=inode_bitmap_sects;
102
103 sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sects;
104 sb.inode_table_sects=inode_table_sects;
105
106 sb.data_start_lba=sb.inode_table_lba+sb.inode_table_sects;
107 sb.root_inode_no=0;
108 sb.dir_entry_size=sizeof(struct dir_entry);
109
110 printk("%s info:\n",part->name);
111 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);
112
113 struct disk* hd=part->my_disk;
114 /****************************
115 * 1.将超级块写入本分区的1扇区
116 * *************************/
117 ide_write(hd,part->start_lba+1,&sb,1);
118 printk(" super_block_lba:0x%x\n",part->start_lba+1);
119
120 /* 找出数据量最大的元信息,用其尺寸做存储缓冲区 */
121 uint32_t buf_size=(sb.block_bitmap_sects>=sb.inode_bitmap_sects?sb.block_bitmap_sects:sb.inode_bitmap_sects);
122 buf_size=(buf_size>=sb.inode_table_sects?buf_size:sb.inode_table_sects)*SECTOR_SIZE;
123 uint8_t* buf=(uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回
124
125 /****************************
126 * 2.将块位图初始化并写入sb.block_bitmap_lba
127 * *************************/
128 /* 初始化块位图block_bitmap */
129 buf[0]|=0x01; // 第0个块预留给根目录,位图中先占位
130 uint32_t block_bitmap_last_byte=block_bitmap_bit_len/8;
131 uint8_t block_bitmap_last_bit=block_bitmap_bit_len%8;
132 uint32_t last_size=SECTOR_SIZE-(block_bitmap_last_byte%SECTOR_SIZE); // last_size是位图中的填充部分
133
134 /* 2.1先将位图最后一字节到其所在扇区结束全置为1,即超出实际块数部分直接置为已占用 */
135 memset(&buf[block_bitmap_last_byte],0xff,last_size);
136
137 /* 2.2再将上一步中覆盖的最后一字节内有效位重新置0 */
138 uint8_t bit_idx=0;
139 while (bit_idx<=block_bitmap_last_bit){
140 buf[block_bitmap_last_byte]&=~(1<<bit_idx++);
141 }
142 ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);
143
144 /***************************
145 * 3.将inode位图初始化并写入sb.inode_bitmap_lba
146 * ************************/
147 /* 先清空缓冲区 */
148 memset(buf,0,buf_size);
149 buf[0]|=0x1; // 第0个inode分给了根目录
150 /* 由于inode_table中共4096个inode,位图中inode_bitmap正好占用1扇区,
151 * 即inode_bitmap_sects等于1,所以位图中的位全都代表inode_table中的inode,
152 * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
153 * inode_bitmap所在的扇区中没有多余的无效位 */
154 ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects);
155
156 /*************************
157 * 4.将inode数组初始化并写入sb.inode_table_lba
158 * **********************/
159 /* 准备写inode_table中的第0项,即根目录所在的inode */
160 memset(buf,0,buf_size); // 先清空缓冲区buf
161 struct inode* i=(struct inode*)buf;
162 i->i_size=sb.dir_entry_size*2; // . & ..
163 i->i_no=0; // 根目录占inode数组中第0个inode
164 i->i_sectors[0]=sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
165 ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);
166
167 /*************************
168 * 5.将根目录初始化并写入sb.data_start_lba
169 * **********************/
170 /* 写入根目录的两个目录项 . & .. */
171 memset(buf,0,buf_size);
172 struct dir_entry* p_de=(struct dir_entry*)buf;
173
174 /* 初始化当前目录"." */
175 memcpy(p_de->filename,".",1);
176 p_de->i_no=0;
177 p_de->f_type=FT_DIRECTORY;
178 ++p_de;
179
180 /* 初始化当前目录父目录 ".." */
181 memcpy(p_de->filename,"..",2);
182 p_de->i_no=0; // 根目录的父目录是自己
183 p_de->f_type=FT_DIRECTORY;
184
185 /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
186 ide_write(hd,sb.data_start_lba,buf,1);
187
188 printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
189 printk("%s format done\n",part->name);
190 sys_free(buf);
191 }
192
193 /* 将最上层路径名称解析出来 */
194 static char* path_parse(char* pathname,char* name_store){
195 if (pathname[0]=='/'){ // 根目录不需要单独解析
196 /* 路径中出现1个或多个连续的字符 '/',将这些'/'跳过 */
197 while (*(++pathname)=='/');
198 }
199
200 /* 开始一般的路径解析 */
201 while (*pathname!='/' && *pathname!=0){
202 *name_store++=*pathname++;
203 }
204
205 if (pathname[0]==0){ // 路径字符串为空则返回NULL
206 return NULL;
207 }
208 return pathname;
209 }
210
211 /* 返回路径深度,比如/a/b/c,深度为3 */
212 int32_t path_depth_cnt(char* pathname){
213 ASSERT(pathname!=NULL);
214 char* p=pathname;
215 char name[MAX_FILE_NAME_LEN]; // 用于path_parse路径解析
216 uint32_t depth=0;
217
218 /* 解析路径,从中拆分出各级名称 */
219 p=path_parse(p,name);
220 while (name[0]){
221 ++depth;
222 memset(name,0,MAX_FILE_NAME_LEN);
223 if (p){ // 若p!=NULL
224 p=path_parse(p,name);
225 }
226 }
227 return depth;
228 }
229
230 /* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */
231 static int search_file(const char* pathname,struct path_search_record* searched_record){
232 /* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */
233 if (!strcmp(pathname,"/") || !strcmp(pathname,"/.") || !strcmp(pathname,"/..")){
234 searched_record->parent_dir=&root_dir;
235 searched_record->file_type=FT_DIRECTORY;
236 searched_record->searched_path[0]=0; // 搜索路径置空
237 return 0;
238 }
239
240 uint32_t path_len=strlen(pathname);
241 /* 保证pathname至少是这样的路径/x,且小于最大长度 */
242 ASSERT(pathname[0]=='/' && path_len>1 && path_len<MAX_PATH_LEN);
243 char* sub_path=(char*)pathname;
244 struct dir* parent_dir=&root_dir;
245 struct dir_entry dir_e;
246
247 /* 记录路径解析出来的各级名称,如路径"/a/b/c",
248 * 数组name每次的值是"a","b","c" */
249 char name[MAX_FILE_NAME_LEN]={0};
250
251 searched_record->parent_dir=parent_dir;
252 searched_record->file_type=FT_UNKNOWN;
253 uint32_t parent_inode_no=0; // 父目录的inode号
254
255 sub_path=path_parse(sub_path,name);
256 while (name[0]){ // 若第一个字符就是结束符,结束循环
257 /* 记录查找过的路径,但不能超过searched_path的长度512Byte */
258 ASSERT(strlen(searched_record->searched_path)<512);
259
260 /* 记录已存在的父目录 */
261 strcat(searched_record->searched_path,'/');
262 strcat(searched_record->searched_path,name);
263
264 /* 在所给的目录中查找文件 */
265 if (search_dir_entry(cur_part,parent_dir,name,&dir_e)){
266 memset(name,0,MAX_FILE_NAME_LEN);
267 /* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */
268 if (sub_path){
269 sub_path=path_parse(sub_path,name);
270 }
271
272 if (FT_DIRECTORY==dir_e.f_type){ // 如果被打开的是目录
273 parent_inode_no=parent_dir->inode->i_no;
274 dir_close(parent_dir);
275 parent_dir=dir_open(cur_part,dir_e.i_no); // 更新父目录
276 searched_record->parent_dir=parent_dir;
277 continue;
278 }else if (FT_REGULAR==dir_e.f_type){
279 searched_record->file_type=FT_REGULAR;
280 return dir_e.i_no;
281 }
282 }else { // 若找不到
283 /* 找不到目录项时,要留着parent_dir不要关闭,
284 * 若是创建新文件的话需要在parent_dir中创建 */
285 return -1;
286 }
287 }
288
289 /* 遍历了完整的路径并且查找的文件或目录只有同名目录存在 */
290 dir_close(searched_record->parent_dir);
291
292 /* 保存被查找目录的直接父目录 */
293 searched_record->parent_dir=dir_open(cur_part,parent_inode_no);
294 searched_record->file_type=FT_DIRECTORY;
295 return dir_e.i_no;
296 }
297
298 /* 打开或创建文件成功后,返回文件描述符,否则返回-1 */
299 int32_t sys_open(const char* pathname,uint8_t flags){
300 /* 对目录要用dir_open,这里只有open文件 */
301 if (pathname[strlen(pathname)-1]=='/'){
302 printk("can't open a directory %s\n",pathname);
303 return -1;
304 }
305 ASSERT(flags<=7);
306 int32_t fd=-1;
307
308 struct path_search_record searched_record;
309 memset(&searched_record,0,sizeof(struct path_search_record));
310
311 /* 记录目录深度,帮助判断中间某个目录不存在的情况 */
312 uint32_t pathname_depth=path_depth_cnt((char*)pathname);
313
314 /* 先检查文件是否存在 */
315 int inode_no=search_file(pathname,&searched_record);
316 bool found=inode_no!=-1?true:false;
317
318 if (searched_record.file_type==FT_DIRECTORY){
319 printk("can't open a directory with open(),use opendir() to instead\n");
320 dir_close(searched_record.parent_dir);
321 return -1;
322 }
323
324 uint32_t path_searched_depth=path_depth_cnt(searched_record.searched_path);
325
326 /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
327 if (pathname_depth!=path_searched_depth){ // 说明并没有访问到全部路径,某个中间目录是不存在的
328 printk("cannot access %s: Not a directory, subpath %s is't exist\n", \
329 pathname,searched_record.searched_path);
330 dir_close(searched_record.parent_dir);
331 return -1;
332 }
333
334 /* 若是最后一个路径上没找到,并且不是要创建文件,直接返回-1 */
335 if (!found && !(flags & O_CREAT)){
336 printk("in path %s, file %s is't exist\n", \
337 searched_record.searched_path,
338 (strrchr(searched_record.searched_path,'/')+1));
339 dir_close(searched_record.parent_dir);
340 return -1;
341 }else if (found && flags & O_CREAT){ // 若要创建的文件已存在
342 printk("%s has already exist!\n",pathname);
343 dir_close(searched_record.parent_dir);
344 return -1;
345 }
346
347 switch (flags & O_CREAT){
348 case O_CREAT:
349 printk("creating file\n");
350 fd=file_create(searched_record.parent_dir,(strrchr(pathname,'/')+1),flags);
351 dir_close(searched_record.parent_dir);
352 break;
353 default:
354 /* 其余情况为打开已存在文件
355 * O_REONLY,O_WRONLY,O_REWR */
356 fd=file_open(inode_no,flags);
357 }
358
359 /* 此fd是指任务pcb->fd_table数组中的元素下标,
360 * 并不是指全局file_table中的下标 */
361 return fd;
362 }
363
364 /* 将文件描述符转化为文件表的下标 */
365 static uint32_t fd_local2global(uint32_t local_fd){
366 struct task_struct* cur=running_thread();
367 int32_t global_fd=cur->fd_table[local_fd];
368 ASSERT(global_fd>=0 && global_fd<MAX_FILE_OPEN);
369 return (uint32_t)global_fd;
370 }
371
372 /* 关闭文件描述符fd指向的文件,成功返回0,否则-1 */
373 int32_t sys_close(int32_t fd){
374 int32_t ret=-1;
375 if (fd>2){
376 uint32_t _fd=fd_local2global(fd);
377 ret=file_close(&file_table[_fd]);
378 running_thread()->fd_table[fd]=-1; // 使该文件描述符可用
379 }
380 return ret;
381 }
382
383 /* 将buf中连续count个字节写入文件描述符fd,成功则返回写入的字节数,失败则返回-1 */
384 int32_t sys_write(int32_t fd,const void* buf,uint32_t count){
385 if (fd<0){
386 printk("sys_write: fd error\n");
387 return -1;
388 }
389 if (fd==stdout_no){
390 char tmp_buf[1024]={0};
391 memcpy(tmp_buf,buf,count);
392 console_put_str(tmp_buf);
393 return count;
394 }
395 uint32_t _fd=fd_local2global(fd);
396 struct file* wr_file=&file_table[_fd];
397 if (wr_file->fd_flag & O_WRONLY || wr_file->fd_flag & O_RDWR){
398 uint32_t bytes_written=file_write(wr_file,buf,count);
399 return bytes_written;
400 }else {
401 console_put_str("sys_write: not allowed to write file without flag O_RDWR or O_WRONLY\n");
402 return -1;
403 }
404 }
405
406 /* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
407 void filesys_init(){
408 uint8_t channel_no=0,dev_no=0,part_idx=0;
409
410 /* sb_buf用来存储从硬盘上读入的超级块 */
411 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
412
413 if (sb_buf==NULL){
414 PANIC("alloc memory failed!");
415 }
416 printk("searching filesystem......\n");
417 while (channel_no<channel_cnt){
418 dev_no=0;
419 while (dev_no<2){
420 if (dev_no==0){
421 ++dev_no;
422 continue;
423 }
424 struct disk* hd=&channels[channel_no].devices[dev_no];
425 struct partition* part=hd->prim_parts;
426 while (part_idx<12){ // 四个主分区+八个逻辑分区
427 if (part_idx==4){ // 逻辑分区
428 part=hd->logic_parts;
429 }
430
431 /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
432 * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
433 * 若partition未初始化,则partition中的成员仍为0.
434 * 下面处理存在的分区. */
435 if (part->sec_cnt!=0){ // 如果分区存在
436 memset(sb_buf,0,SECTOR_SIZE);
437
438 /* 读出分区超级块,根据魔数是否正确来判断是否存在fs */
439 ide_read(hd,part->start_lba+1,sb_buf,1);
440
441 /* 只支持自己的文件系统,若磁盘上已经有fs就不再初始化了 */
442 if (sb_buf->magic==0x19590318){
443 printk("%s has filesystem\n",part->name);
444 }else{
445 printk("formatting %s's partition %s.....\n",hd->name,part->name);
446 partition_format(part);
447 }
448 }
449 ++part_idx;
450 ++part; // 下一分区
451 }
452 ++dev_no; //下一磁盘
453 }
454 ++channel_no; // 下一通道
455 }
456 sys_free(sb_buf);
457
458 /* 确定默认操作的分区 */
459 char default_part[8]="sdb1";
460 /* 挂载分区 */
461 list_traversal(&partition_list,mount_partition,(int)default_part);
462
463 /* 将当前分区的根目录打开 */
464 open_root_dir(cur_part);
465
466 /* 初始化文件表 */
467 uint32_t fd_idx=0;
468 while (fd_idx<MAX_FILE_OPEN){
469 file_table[fd_idx++].fd_inode=NULL;
470 }
471 }
④fs/fs.h,增加一句函数声明:
int32_t sys_write(int32_t fd,const void* buf,uint32_t count);
⑤lib/user/syscall.c,将write()函数改写成如下所示:
/* 把buf中count个字符写入文件描述符fd */
uint32_t write(int32_t fd,const void* buf,uint32_t count){
return _syscall3(SYS_WRITE,fd,buf,count);
}
⑥lib/user/syscall.h,改写write()的函数声明:
uint32_t write(int32_t fd,const void* buf,uint32_t count);
⑦lib/stdio.c,改写printf()函数:

1 /* 将字符串写到终端中 */
2 uint32_t printf(const char* format, ...)
3 {
4 va_list args;
5 va_start(args,format); // 使args指向format
6 char buf[1024]={0}; // 用于存储拼接后的字符串
7 vsprintf(buf,format,args);
8 va_end(args);
9 return write(1,buf,strlen(buf));
10 }
⑧userprog/syscall-init.c和userprog/syscall-init.h:
删除系统调用write对应的sys_write(及其声明)。
在syscall-init.h中增加头文件:
#include "memory.h"
#include "fs.h"
记得修改makefile哈。
⑨kernel/main.c,对于main():

1 int main(void){
2 put_str("Welcome,\nI am kernel!\n");
3 init_all();
4
5 intr_enable();
6 process_execute(u_prog_a,"u_prog_a");
7 process_execute(u_prog_b,"u_prog_b");
8 thread_start("k_thread_a",31,k_thread_a,"I'm thread_a ");
9 thread_start("k_thread_b",31,k_thread_b,"I'm thread_b ");
10
11 uint32_t fd=sys_open("/file1",O_RDWR);
12 printf("fd:%d\n",fd);
13 sys_write(fd,"hello world\n",12);
14 sys_close(fd);
15 printf("%d closed now\n",fd);
16 while(1);
17 return 0;
18 }
程序运行结果:
成功打开和关闭,并且写入的扇区是0XA68。
用xxd工具查看该地址处的数据:
由于这是第二次运行程序,所以会写入两次“hello world”,inode的i_size也变为了0x18。
应该没啥问题,下面开始读取文件。
5.读取文件
①fs/file.c:

1 #include "file.h"
2 #include "fs.h"
3 #include "super_block.h"
4 #include "inode.h"
5 #include "stdio-kernel.h"
6 #include "memory.h"
7 #include "debug.h"
8 #include "interrupt.h"
9 #include "string.h"
10 #include "thread.h"
11 #include "global.h"
12
13 #define DEFAULT_SECT 1
14
15 /* 文件表 */
16 struct file file_table[MAX_FILE_OPEN];
17
18 /* 从文件表file_table中获取一个空闲位,成功返回下标,失败返回-1 */
19 int32_t get_free_slot_in_global(void){
20 uint32_t fd_idx=3;
21 while (fd_idx<MAX_FILE_OPEN){
22 if (file_table[fd_idx].fd_inode==NULL){
23 break;
24 }
25 ++fd_idx;
26 }
27 if (fd_idx==MAX_FILE_OPEN){
28 printk("exceed max open files\n");
29 return -1;
30 }
31 return fd_idx;
32 }
33
34 /* 将全局描述符表下标安装到进程或线程自己的文件描述符数组fd_table中,
35 * 成功则返回下标,失败则返回-1 */
36 int32_t pcb_fd_install(int32_t global_fd_idx){
37 struct task_struct* cur=running_thread();
38 uint8_t local_fd_idx=3; // 跳过stdin,stdout,stderr
39 while (local_fd_idx<MAX_FILES_OPEN_PER_PROC){
40 if (cur->fd_table[local_fd_idx]==-1){
41 cur->fd_table[local_fd_idx]=global_fd_idx;
42 break;
43 }
44 ++local_fd_idx;
45 }
46 if (local_fd_idx==MAX_FILES_OPEN_PER_PROC){
47 printk("exceed max open_per_proc\n");
48 return -1;
49 }
50 return local_fd_idx;
51 }
52
53 /* 分配一个inode,返回inode_no */
54 int32_t inode_bitmap_alloc(struct partition* part){
55 int32_t bit_idx=bitmap_scan(&part->inode_bitmap,1);
56 if (bit_idx==-1){
57 return -1;
58 }
59 bitmap_set(&part->inode_bitmap,bit_idx,1);
60 return bit_idx;
61 }
62
63 /* 分配一个扇区,返回其扇区地址 */
64 int32_t block_bitmap_alloc(struct partition* part){
65 int32_t bit_idx=bitmap_scan(&part->block_bitmap,1);
66 if (bit_idx==-1){
67 return -1;
68 }
69 bitmap_set(&part->block_bitmap,bit_idx,1);
70 /* 和inode_bitmap_malloc不同,此处返回的不是位图索引,而是具体可用的扇区地址 */
71 return (part->sb->data_start_lba+bit_idx);
72 }
73
74 /* 将内存bitmap第bit_idx位所在的512字节同步到硬盘 */
75 void bitmap_sync(struct partition* part,uint32_t bit_idx,uint8_t btmp_type){
76 uint32_t off_sec=bit_idx/4096; // 本inode相对于位图的扇区偏移量
77 uint32_t off_size=off_sec*BLOCK_SIZE; // 本inode相对于位图的字节偏移量
78 uint32_t sec_lba;
79 uint8_t* bitmap_off;
80
81 /* 需要被同步到硬盘的位图只有inode_bitmap和block_bitmap */
82 switch(btmp_type){
83 case INODE_BITMAP:
84 sec_lba=part->sb->inode_bitmap_lba+off_sec;
85 bitmap_off=part->inode_bitmap.bits+off_size;
86 break;
87
88 case BLOCK_BITMAP:
89 sec_lba=part->sb->block_bitmap_lba+off_sec;
90 bitmap_off=part->block_bitmap.bits+off_size;
91 break;
92 }
93 ide_write(part->my_disk,sec_lba,bitmap_off,1);
94 }
95
96 /* 创建文件,若成功则返回文件描述符,否则返回-1 */
97 int32_t file_create(struct dir* parent_dir,char* filename,uint8_t flag){
98 /* 后续操作的公共缓冲区 */
99 void* io_buf=sys_malloc(1024);
100 if (io_buf==NULL){
101 printk("in file_creat: sys_malloc for io_buf failed\n");
102 return -1;
103 }
104
105 uint8_t rollback_step=0; // 用于操作失败时回滚各资源
106
107 /* 为新文件分配inode */
108 int32_t inode_no=inode_bitmap_alloc(cur_part);
109 if (inode_no==-1){
110 printk("in file_create: allocate inode failed\n");
111 return -1;
112 }
113
114 /* 此inode要从堆中申请内存,不可生成局部变量(否则函数退出时会释放)
115 * 因为file_table数组中的文件描述符的inode指针要指向它 */
116 struct inode* new_file_inode=(struct inode*)sys_malloc(sizeof(struct inode));
117 if (new_file_inode==NULL){
118 printk("file_create: sys_malloc for inode failed\n");
119 rollback_step=1;
120 goto rollback;
121 }
122 inode_init(inode_no,new_file_inode); // 初始化inode
123
124 /* 返回的是file_table数组的下标 */
125 int fd_idx=get_free_slot_in_global();
126 if (fd_idx==-1){
127 printk("exceed max open files\n");
128 rollback_step=2;
129 goto rollback;
130 }
131
132 file_table[fd_idx].fd_inode=new_file_inode;
133 file_table[fd_idx].fd_pos=0;
134 file_table[fd_idx].fd_flag=flag;
135 file_table[fd_idx].fd_inode->write_deny=false;
136
137 struct dir_entry new_dir_entry;
138 memset(&new_dir_entry,0,sizeof(struct dir_entry));
139
140 create_dir_entry(filename,inode_no,FT_REGULAR,&new_dir_entry); // create_dir_entry只是内存操作
141
142 /* 同步内存数据到硬盘 */
143 /* 1.在目录parent_dir下安装目录项new_dir_entry,写入硬盘后返回true,否则返回false */
144 if (!sync_dir_entry(parent_dir,&new_dir_entry,io_buf)){
145 printk("sync dir_entry to disk failed\n");
146 rollback_step=3;
147 goto rollback;
148 }
149
150 memset(io_buf,0,1024);
151 /* 2.将父目录inode的内容同步到硬盘 */
152 inode_sync(cur_part,parent_dir->inode,io_buf);
153
154 memset(io_buf,0,1024);
155 /* 3.将新创建的文件的inode内容同步到硬盘 */
156 inode_sync(cur_part,new_file_inode,io_buf);
157
158 /* 4.将inode_bitmap位图同步到硬盘 */
159 bitmap_sync(cur_part,inode_no,INODE_BITMAP);
160
161 /* 5.将创建的文件inode添加到open_inodes链表 */
162 list_push(&cur_part->open_inodes,&new_file_inode->inode_tag);
163 new_file_inode->i_open_cnts=1;
164
165 sys_free(io_buf);
166 return pcb_fd_install(fd_idx);
167
168 /* 创建文件需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
169 rollback:
170 switch(rollback_step){
171 case 3:
172 /* 失败时,将file_table中的相应位清0 */
173 memset(&file_table[fd_idx],0,sizeof(struct file));
174 case 2:
175 sys_free(new_file_inode);
176 case 1:
177 /* 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复 */
178 bitmap_set(&cur_part->inode_bitmap,inode_no,0);
179 break;
180 }
181 sys_free(io_buf);
182 return -1;
183 }
184
185 /* 打开编号为inode_no的inode对应的问价,若成功则返回文件描述符,否则返回-1 */
186 int32_t file_open(uint32_t inode_no,uint8_t flag){
187 int fd_idx=get_free_slot_in_global();
188 if (fd_idx==-1){
189 printk("exceed max open files\n");
190 return -1;
191 }
192 file_table[fd_idx].fd_inode=inode_open(cur_part,inode_no);
193 file_table[fd_idx].fd_pos=0; // 使文件指针指向开头
194 file_table[fd_idx].fd_flag=flag;
195 bool* write_deny=&file_table[fd_idx].fd_inode->write_deny;
196
197 if (flag & O_WRONLY || flag & O_RDWR){ // 可写
198
199 /* 一下进入临界区前先关中断 */
200 enum intr_status old_status=intr_disable();
201 if (!(*write_deny)){
202 *write_deny=true; // 置为true,避免多个进程同时写此文件
203 intr_set_status(old_status); // 恢复中断
204 }else {
205 intr_set_status(old_status);
206 printk("file can't be write now, try again later\n");
207 return -1;
208 }
209 } // 若是可读,则忽略write_deny
210 return pcb_fd_install(fd_idx);
211 }
212
213 /* 关闭文件 */
214 int32_t file_close(struct file* file){
215 if (file==NULL){
216 return -1;
217 }
218 file->fd_inode->write_deny=false;
219 inode_close(file->fd_inode);
220 file->fd_inode=NULL; // 使文件结构可用
221 return 0;
222 }
223
224 /* 把buf中的count字节写入file,成功则返回写入的字节数,失败则返回-1 */
225 int32_t file_write(struct file* file,const void* buf,uint32_t count){
226 if ((file->fd_inode->i_size+count)>(BLOCK_SIZE*140)){
227 printk("exceed max file_size 71680B, write file failed\n");
228 return -1;
229 }
230 uint8_t* io_buf=sys_malloc(BLOCK_SIZE);
231 if (io_buf==NULL){
232 printk("file_write: sys_malloc for all_blocks failed\n");
233 return -1;
234 }
235 uint32_t* all_blocks=(uint32_t*)sys_malloc(BLOCK_SIZE+48); // 用来记录文件所有的块地址
236 if (all_blocks==NULL){
237 printk("file_write: sys_malloc for all_blocks failed\n");
238 return -1;
239 }
240
241 const uint8_t* src=buf; // 用src指向buf中待写入的数据
242 uint32_t bytes_written=0; // 用来记录已写入的数据大小
243 uint32_t size_left=count; // 用来记录未写入数据大小
244 int32_t block_lba=-1; // 块地址
245 uint32_t block_bitmap_idx=0;// 用来记录block对应于block_bitmap中的索引,作为参数传给bitmap_sync
246 uint32_t sec_idx; // 用来索引扇区
247 uint32_t sec_lba; // 扇区地址
248 uint32_t sec_off_bytes; // 扇区内字节偏移量
249 uint32_t sec_left_bytes; // 扇区内剩余字节量
250 uint32_t chunk_size; // 每次写入硬盘的数据块大小
251 int32_t indirect_block_table; // 用来获取一级间接表地址
252 uint32_t block_idx; // 块索引
253
254 /* 判断文件是否是第一次写,如果是则先为其分配一个块 */
255 if (file->fd_inode->i_sectors[0]==0){
256 block_lba=block_bitmap_alloc(cur_part);
257 if (block_lba==-1){
258 printk("file_write: block_bitmap_alloc failed\n");
259 return -1;
260 }
261 file->fd_inode->i_sectors[0]=block_lba;
262
263 /* 每分配一个块就将位图同步到硬盘 */
264 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
265 ASSERT(block_bitmap_idx!=0);
266 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
267 }
268
269 /* 写入count个字节前,该文件已经占用的块数 */
270 uint32_t file_has_used_blocks=file->fd_inode->i_size/BLOCK_SIZE+1;
271
272 /* 存储count字节后该文件将占用的块数 */
273 uint32_t file_will_use_blocks=(file->fd_inode->i_size+count)/BLOCK_SIZE+1;
274 ASSERT(file_will_use_blocks<=140);
275
276 /* 通过此增量判断是否需要分配扇区,如增量为0,表示原扇区够用 */
277 uint32_t add_blocks=file_will_use_blocks-file_has_used_blocks;
278
279 /* 开始将文件所以块地址收集到all_blocks,(系统中块大小等于扇区大小)
280 * 后面都统一在all_blocks中获取写入扇区地址 */
281 if (add_blocks==0){
282 /* 在同一扇区写入数据,不涉及到分配新扇区 */
283 if (file_has_used_blocks<=12){
284 block_idx=file_has_used_blocks-1;
285 all_blocks[block_idx]=file->fd_inode->i_sectors[block_idx];
286 }else {
287 /* 未写入新数据之前已经占用了间接块,需要将间接块地址读进来 */
288 ASSERT(file->fd_inode->i_sectors[12]!=0);
289 indirect_block_table=file->fd_inode->i_sectors[12];
290 ide_read(cur_part->my_disk,indirect_block_table,all_blocks+12,1);
291 }
292 }else {
293 /* 若有增量,便涉及到分配新扇区是否分配一级间接块表,下面分三种情况:
294 * 1. 12个直接块够用 */
295 if (file_will_use_blocks<=12){
296 /* 先将剩余空间可继续用的扇区地址写入all_blocks */
297 block_idx=file_has_used_blocks-1;
298 ASSERT(file->fd_inode->i_sectors[block_idx]!=0)
299 all_blocks[block_idx]=file->fd_inode->i_sectors[block_idx];
300
301 /* 再将未来要用的扇区分配号后写入all_blocks */
302 block_idx=file_has_used_blocks; // 指向第一个要分配的新扇区
303 while (block_idx<file_will_use_blocks){
304 block_lba=block_bitmap_alloc(cur_part);
305 if (block_lba==-1){
306 printk("file_write: block_bitmap_alloc for situation 1 failed\n");
307 return -1;
308 }
309
310 /* 写文件时,不应该存在块未使用但已分配扇区的情况,当文件删除时,就会把块地址清0 */
311 ASSERT(file->fd_inode->i_sectors[block_idx]==0); // 确保尚未分配扇区地址
312 file->fd_inode->i_sectors[block_idx]=all_blocks[block_idx]=block_lba;
313
314 /* 每分配一个块就将位图同步到硬盘 */
315 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
316 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
317
318 ++block_idx; // 下一个分配的新扇区
319 }
320 }else if (file_has_used_blocks<=12 && file_will_use_blocks>12){
321 /* 2. 旧数据在12个直接块内,新数据将使用间接块 */
322
323 /* 先将剩余空间的可继续用的扇区地址收集到all_blocks */
324 block_idx=file_has_used_blocks-1; // 指向旧数据所在的最后一个扇区
325 all_blocks[block_idx]=file->fd_inode->i_sectors[block_idx];
326
327 /* 创建一级间接块表 */
328 block_lba=block_bitmap_alloc(cur_part);
329 if (block_lba==-1){
330 printk("file_write: block_bitmap_alloc for situation 2 failed\n");
331 return -1;
332 }
333
334 ASSERT(file->fd_inode->i_sectors[12]==0); // 确保一级间接块表未分配
335 /* 分配一级间接块索引表 */
336 indirect_block_table=file->fd_inode->i_sectors[12]=block_lba;
337
338 block_idx=file_has_used_blocks; // 第一个未使用的块
339 while (block_idx<file_will_use_blocks){
340 block_lba=block_bitmap_alloc(cur_part);
341 if (block_lba==-1){
342 printk("file_write: block_bitmap_alloc for situation 2 failed\n");
343 return -1;
344 }
345
346 if (block_idx<12){ // 新创建的0~11块直接存入all_blocks数组
347 ASSERT(file->fd_inode->i_sectors[block_idx]==0);
348 file->fd_inode->i_sectors[block_idx]=all_blocks[block_idx]=block_lba;
349 }else { // 间接块只写入到all_block数组中,待全部分配完成后一次性同步到硬盘
350 all_blocks[block_idx]=block_lba;
351 }
352
353 /* 每分配一个块就将位图同步到硬盘 */
354 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
355 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
356
357 ++block_idx; // 下一个新扇区
358 }
359 ide_write(cur_part->my_disk,indirect_block_table,all_blocks+12,1);
360 }else if (file_has_used_blocks>12){
361 /* 3. 新数据占据间接块 */
362 ASSERT(file->fd_inode->i_sectors[12]!=0); // 已经具备了一级间接块表
363 indirect_block_table=file->fd_inode->i_sectors[12]; // 获取一级间接块表地址
364
365 /* 已使用的间接块也将被读入all_blocks,无须单独收录 */
366 ide_read(cur_part->my_disk,indirect_block_table,all_blocks+12,1); // 获取所有间接块地址
367
368 block_idx=file_has_used_blocks; // 第一个未使用的间接块,即已经使用的间接块的下一块
369 while (block_idx<file_will_use_blocks){
370 block_lba=block_bitmap_alloc(cur_part);
371 if (block_lba==-1){
372 printk("file_wirte: block_bitmap_alloc for situation 3 failed\n");
373 return -1;
374 }
375 all_blocks[block_idx++]=block_lba;
376
377 /* 每分配一个块就将位图同步到硬盘 */
378 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
379 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
380 }
381 ide_write(cur_part->my_disk,indirect_block_table,all_blocks+12,1);
382 }
383 }
384
385 bool first_write_block=true; // 含有剩余空间的扇区标识
386 /* 块地址已经收集到all_blocks中,下面开始写数据 */
387 file->fd_pos=file->fd_inode->i_size-1; // 置fd_pos为文件大小-1,下面在写数据时随时更新
388 while (bytes_written<count){ // 直到写完所有数据
389 memset(io_buf,0,BLOCK_SIZE);
390 sec_idx=file->fd_inode->i_size/BLOCK_SIZE;
391 sec_lba=all_blocks[sec_idx];
392 sec_off_bytes=file->fd_inode->i_size%BLOCK_SIZE;
393 sec_left_bytes=BLOCK_SIZE-sec_off_bytes;
394
395 /* 判断此次写入硬盘的数据大小 */
396 chunk_size=size_left<sec_left_bytes?size_left:sec_left_bytes;
397 if (first_write_block){
398 ide_read(cur_part->my_disk,sec_lba,io_buf,1);
399 first_write_block=false;
400 }
401 memcpy(io_buf+sec_off_bytes,src,chunk_size);
402 ide_write(cur_part->my_disk,sec_lba,io_buf,1);
403
404 src+=chunk_size;
405 file->fd_inode->i_size+=chunk_size; // 更新文件大小
406 file->fd_pos+=chunk_size;
407 bytes_written+=chunk_size;
408 size_left-=chunk_size;
409 }
410 inode_sync(cur_part,file->fd_inode,io_buf);
411 sys_free(all_blocks);
412 sys_free(io_buf);
413 return bytes_written;
414 }
415
416 /* 从文件file中读取count个字节写入buf,返回读出的字节数,若到文件尾则返回-1 */
417 int32_t file_read(struct file* file,void* buf,uint32_t count){
418 int8_t* buf_dst=(uint8_t*)buf;
419 uint32_t size=count,size_left=size;
420
421 /* 若要读取的字节数超过了文件可读的剩余量,就用剩余量作为待读取的字节数 */
422 if ((file->fd_pos+count)>file->fd_inode->i_size){
423 size=file->fd_inode->i_size-file->fd_pos;
424 size_left=size;
425 if (size==0){ // 若到文件尾,则返回-1
426 return -1;
427 }
428 }
429
430 uint8_t* io_buf=sys_malloc(BLOCK_SIZE);
431 if (io_buf==NULL){
432 printk("file_read: sys_malloc for io_buf failed\n");
433 }
434 uint32_t* all_blocks=(uint32_t*)sys_malloc(BLOCK_SIZE+48); // 用来记录文件所有的块地址
435 if (all_blocks==NULL){
436 printk("file_read: sys_malloc for all_blocks failed\n");
437 return -1;
438 }
439
440 uint32_t block_read_start_idx=file->fd_pos/BLOCK_SIZE; // 数据所在块的起始地址
441 uint32_t block_read_end_idx=(file->fd_pos+size)/BLOCK_SIZE; // 终止地址
442 uint32_t read_blocks=block_read_start_idx-block_read_end_idx; // 增量为0,表示在同一扇区
443 ASSERT(block_read_start_idx<139 && block_read_end_idx<139);
444
445 int32_t indirect_block_table; // 用来获取一级间接表地址
446 uint32_t block_idx; // 获取待读的块地址
447
448 /* 以下开始构建all_blocks块地址数组,存储用到的块地址 */
449 if (read_blocks == 0) { // 在同一扇区内读数据,不涉及到跨扇区读取
450 ASSERT(block_read_end_idx == block_read_start_idx);
451 if (block_read_end_idx < 12 ) { // 待读的数据在12个直接块之内
452 block_idx = block_read_end_idx;
453 all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
454 }else { // 若用到了一级间接块表,需要将表中间接块读进来
455 indirect_block_table = file->fd_inode->i_sectors[12];
456 ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1);
457 }
458 }else { // 若要读多个块
459 /* 1. 起始块和终止块属于直接块 */
460 if (block_read_end_idx < 12 ) { // 数据结束所在的块属于直接块
461 block_idx = block_read_start_idx;
462 while (block_idx <= block_read_end_idx) {
463 all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
464 block_idx++;
465 }
466 }else if (block_read_start_idx < 12 && block_read_end_idx >= 12) {
467 /* 2.待读入的数据跨越直接块和间接块两类 */
468 /* 先将直接块地址写入all_blocks */
469 block_idx = block_read_start_idx;
470 while (block_idx < 12) {
471 all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
472 block_idx++;
473 }
474 ASSERT(file->fd_inode->i_sectors[12] != 0); // 确保已经分配了一级间接块表
475
476 /* 再将间接块地址写入all_blocks */
477 indirect_block_table = file->fd_inode->i_sectors[12];
478 ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 将一级间接块表读进来写入到第13个块的位置之后
479 }else {
480 /* 3.数据在间接块中*/
481 ASSERT(file->fd_inode->i_sectors[12] != 0); // 确保已经分配了一级间接块表
482 indirect_block_table = file->fd_inode->i_sectors[12]; // 获取一级间接表地址
483 ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 将一级间接块表读进来写入到第13个块的位置之后
484 }
485 }
486
487 /* 用到的块地址已经收集到all_blocks中,下面开始读数据 */
488 uint32_t sec_idx,sec_lba,sec_off_bytes,sec_left_bytes,chunk_size;
489 uint32_t bytes_read=0;
490 while (bytes_read<size){
491 sec_idx=file->fd_pos/BLOCK_SIZE;
492 sec_lba=all_blocks[sec_idx];
493 sec_off_bytes=file->fd_pos%BLOCK_SIZE;
494 sec_left_bytes=BLOCK_SIZE-sec_off_bytes;
495 chunk_size=size_left<sec_left_bytes?size_left:sec_left_bytes; // 待读入
496
497 memset(io_buf,0,BLOCK_SIZE);
498 ide_read(cur_part->my_disk,sec_lba,io_buf,1);
499 memcpy(buf_dst,io_buf+sec_off_bytes,chunk_size);
500
501 buf_dst+=chunk_size;
502 file->fd_pos+=chunk_size;
503 bytes_read+=chunk_size;
504 size_left-=chunk_size;
505 }
506 sys_free(all_blocks);
507 sys_free(io_buf);
508 return bytes_read;
509 }
②fs/file.c,添加一句函数声明:
int32_t file_read(struct file* file,void* buf,uint32_t count);
③fs/fs.c:

1 #include "fs.h"
2 #include "super_block.h"
3 #include "inode.h"
4 #include "dir.h"
5 #include "stdint.h"
6 #include "stdio-kernel.h"
7 #include "list.h"
8 #include "string.h"
9 #include "ide.h"
10 #include "global.h"
11 #include "debug.h"
12 #include "memory.h"
13 #include "file.h"
14 #include "console.h"
15
16 struct partition* cur_part; // 默认情况下操作的是哪个分区
17
18 /* 在分区中找到名为part_name的分区,并将其指针赋给cur_part */
19 static bool mount_partition(struct list_elem* pelem,int arg){
20 char* part_name=(char*)arg;
21 struct partition* part=elem2entry(struct partition,part_tag,pelem);
22 if (!strcmp(part->name,part_name)){
23 cur_part=part;
24 struct disk* hd=cur_part->my_disk;
25
26 /* sb_buf用来存储从硬盘上读入的超级块 */
27 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
28
29 /* 在内存中创建分区cur_part的超级块 */
30 cur_part->sb=(struct super_block*)sys_malloc(sizeof(struct super_block));
31 if (cur_part->sb==NULL){
32 PANIC("alloc memory failed!");
33 }
34
35 /* 读入超级块 */
36 memset(sb_buf,0,SECTOR_SIZE);
37 ide_read(hd,cur_part->start_lba+1,sb_buf,1);
38
39 /* 把sb_buf中超级块的信息复制到分区的超级块sb中 */
40 memcpy(cur_part->sb,sb_buf,sizeof(struct super_block));
41
42 /********** 将硬盘上的块位图读入到内存 **********/
43 cur_part->block_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->block_bitmap_sects*SECTOR_SIZE);
44 if (cur_part->block_bitmap.bits==NULL){
45 PANIC("alloc memory failed!");
46 }
47 cur_part->block_bitmap.btmp_bytes_len=sb_buf->block_bitmap_sects*SECTOR_SIZE;
48 /* 从硬盘上读入块位图到分区的block_bitmap.bits */
49 ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);
50 /***************************************************/
51
52 /********** 将硬盘上的inode位图读入到内存 **********/
53 cur_part->inode_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects*SECTOR_SIZE);
54 if (cur_part->inode_bitmap.bits==NULL){
55 PANIC("alloc memory failed!");
56 }
57 cur_part->inode_bitmap.btmp_bytes_len=sb_buf->inode_bitmap_sects*SECTOR_SIZE;
58 /* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
59 ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);
60 /**************************************************/
61
62 list_init(&cur_part->open_inodes);
63 printk("mount %s done!\n",part->name);
64
65 /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关
66 * 只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历 */
67 return true;
68 }
69 return false; // 使list_traversal继续遍历
70 }
71
72 /* 初始化分区元信息,创建文件系统 */
73 static void partition_format(struct partition* part){
74 /* 为方便,一个块大小是一扇区 */
75 uint32_t boot_sector_sects=1;
76 uint32_t super_block_sects=1;
77 uint32_t inode_bitmap_sects=DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); // inode位图所占用的扇区数
78 uint32_t inode_table_sects=DIV_ROUND_UP(((sizeof(struct inode)*MAX_FILES_PER_PART)),SECTOR_SIZE);
79 uint32_t used_sects=boot_sector_sects+super_block_sects+inode_bitmap_sects+inode_table_sects;
80 uint32_t free_sects=part->sec_cnt-used_sects;
81
82 /****************** 块位图占用的扇区数 ********************/
83 uint32_t block_bitmap_sects;
84 block_bitmap_sects=DIV_ROUND_UP(free_sects,BITS_PER_SECTOR);
85 /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
86 uint32_t block_bitmap_bit_len=free_sects-block_bitmap_sects;
87 block_bitmap_sects=DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);
88 /*********************************************************/
89
90 /* 超级块初始化 */
91 struct super_block sb;
92 sb.magic=0x19590318;
93 sb.sec_cnt=part->sec_cnt;
94 sb.inode_cnt=MAX_FILES_PER_PART;
95 sb.part_lba_base=part->start_lba;
96
97 sb.block_bitmap_lba=sb.part_lba_base+2; // 第0块为bootblock,第1块为superblock
98 sb.block_bitmap_sects=block_bitmap_sects;
99
100 sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sects;
101 sb.inode_bitmap_sects=inode_bitmap_sects;
102
103 sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sects;
104 sb.inode_table_sects=inode_table_sects;
105
106 sb.data_start_lba=sb.inode_table_lba+sb.inode_table_sects;
107 sb.root_inode_no=0;
108 sb.dir_entry_size=sizeof(struct dir_entry);
109
110 printk("%s info:\n",part->name);
111 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);
112
113 struct disk* hd=part->my_disk;
114 /****************************
115 * 1.将超级块写入本分区的1扇区
116 * *************************/
117 ide_write(hd,part->start_lba+1,&sb,1);
118 printk(" super_block_lba:0x%x\n",part->start_lba+1);
119
120 /* 找出数据量最大的元信息,用其尺寸做存储缓冲区 */
121 uint32_t buf_size=(sb.block_bitmap_sects>=sb.inode_bitmap_sects?sb.block_bitmap_sects:sb.inode_bitmap_sects);
122 buf_size=(buf_size>=sb.inode_table_sects?buf_size:sb.inode_table_sects)*SECTOR_SIZE;
123 uint8_t* buf=(uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回
124
125 /****************************
126 * 2.将块位图初始化并写入sb.block_bitmap_lba
127 * *************************/
128 /* 初始化块位图block_bitmap */
129 buf[0]|=0x01; // 第0个块预留给根目录,位图中先占位
130 uint32_t block_bitmap_last_byte=block_bitmap_bit_len/8;
131 uint8_t block_bitmap_last_bit=block_bitmap_bit_len%8;
132 uint32_t last_size=SECTOR_SIZE-(block_bitmap_last_byte%SECTOR_SIZE); // last_size是位图中的填充部分
133
134 /* 2.1先将位图最后一字节到其所在扇区结束全置为1,即超出实际块数部分直接置为已占用 */
135 memset(&buf[block_bitmap_last_byte],0xff,last_size);
136
137 /* 2.2再将上一步中覆盖的最后一字节内有效位重新置0 */
138 uint8_t bit_idx=0;
139 while (bit_idx<=block_bitmap_last_bit){
140 buf[block_bitmap_last_byte]&=~(1<<bit_idx++);
141 }
142 ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);
143
144 /***************************
145 * 3.将inode位图初始化并写入sb.inode_bitmap_lba
146 * ************************/
147 /* 先清空缓冲区 */
148 memset(buf,0,buf_size);
149 buf[0]|=0x1; // 第0个inode分给了根目录
150 /* 由于inode_table中共4096个inode,位图中inode_bitmap正好占用1扇区,
151 * 即inode_bitmap_sects等于1,所以位图中的位全都代表inode_table中的inode,
152 * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
153 * inode_bitmap所在的扇区中没有多余的无效位 */
154 ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects);
155
156 /*************************
157 * 4.将inode数组初始化并写入sb.inode_table_lba
158 * **********************/
159 /* 准备写inode_table中的第0项,即根目录所在的inode */
160 memset(buf,0,buf_size); // 先清空缓冲区buf
161 struct inode* i=(struct inode*)buf;
162 i->i_size=sb.dir_entry_size*2; // . & ..
163 i->i_no=0; // 根目录占inode数组中第0个inode
164 i->i_sectors[0]=sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
165 ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);
166
167 /*************************
168 * 5.将根目录初始化并写入sb.data_start_lba
169 * **********************/
170 /* 写入根目录的两个目录项 . & .. */
171 memset(buf,0,buf_size);
172 struct dir_entry* p_de=(struct dir_entry*)buf;
173
174 /* 初始化当前目录"." */
175 memcpy(p_de->filename,".",1);
176 p_de->i_no=0;
177 p_de->f_type=FT_DIRECTORY;
178 ++p_de;
179
180 /* 初始化当前目录父目录 ".." */
181 memcpy(p_de->filename,"..",2);
182 p_de->i_no=0; // 根目录的父目录是自己
183 p_de->f_type=FT_DIRECTORY;
184
185 /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
186 ide_write(hd,sb.data_start_lba,buf,1);
187
188 printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
189 printk("%s format done\n",part->name);
190 sys_free(buf);
191 }
192
193 /* 将最上层路径名称解析出来 */
194 static char* path_parse(char* pathname,char* name_store){
195 if (pathname[0]=='/'){ // 根目录不需要单独解析
196 /* 路径中出现1个或多个连续的字符 '/',将这些'/'跳过 */
197 while (*(++pathname)=='/');
198 }
199
200 /* 开始一般的路径解析 */
201 while (*pathname!='/' && *pathname!=0){
202 *name_store++=*pathname++;
203 }
204
205 if (pathname[0]==0){ // 路径字符串为空则返回NULL
206 return NULL;
207 }
208 return pathname;
209 }
210
211 /* 返回路径深度,比如/a/b/c,深度为3 */
212 int32_t path_depth_cnt(char* pathname){
213 ASSERT(pathname!=NULL);
214 char* p=pathname;
215 char name[MAX_FILE_NAME_LEN]; // 用于path_parse路径解析
216 uint32_t depth=0;
217
218 /* 解析路径,从中拆分出各级名称 */
219 p=path_parse(p,name);
220 while (name[0]){
221 ++depth;
222 memset(name,0,MAX_FILE_NAME_LEN);
223 if (p){ // 若p!=NULL
224 p=path_parse(p,name);
225 }
226 }
227 return depth;
228 }
229
230 /* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */
231 static int search_file(const char* pathname,struct path_search_record* searched_record){
232 /* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */
233 if (!strcmp(pathname,"/") || !strcmp(pathname,"/.") || !strcmp(pathname,"/..")){
234 searched_record->parent_dir=&root_dir;
235 searched_record->file_type=FT_DIRECTORY;
236 searched_record->searched_path[0]=0; // 搜索路径置空
237 return 0;
238 }
239
240 uint32_t path_len=strlen(pathname);
241 /* 保证pathname至少是这样的路径/x,且小于最大长度 */
242 ASSERT(pathname[0]=='/' && path_len>1 && path_len<MAX_PATH_LEN);
243 char* sub_path=(char*)pathname;
244 struct dir* parent_dir=&root_dir;
245 struct dir_entry dir_e;
246
247 /* 记录路径解析出来的各级名称,如路径"/a/b/c",
248 * 数组name每次的值是"a","b","c" */
249 char name[MAX_FILE_NAME_LEN]={0};
250
251 searched_record->parent_dir=parent_dir;
252 searched_record->file_type=FT_UNKNOWN;
253 uint32_t parent_inode_no=0; // 父目录的inode号
254
255 sub_path=path_parse(sub_path,name);
256 while (name[0]){ // 若第一个字符就是结束符,结束循环
257 /* 记录查找过的路径,但不能超过searched_path的长度512Byte */
258 ASSERT(strlen(searched_record->searched_path)<512);
259
260 /* 记录已存在的父目录 */
261 strcat(searched_record->searched_path,'/');
262 strcat(searched_record->searched_path,name);
263
264 /* 在所给的目录中查找文件 */
265 if (search_dir_entry(cur_part,parent_dir,name,&dir_e)){
266 memset(name,0,MAX_FILE_NAME_LEN);
267 /* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */
268 if (sub_path){
269 sub_path=path_parse(sub_path,name);
270 }
271
272 if (FT_DIRECTORY==dir_e.f_type){ // 如果被打开的是目录
273 parent_inode_no=parent_dir->inode->i_no;
274 dir_close(parent_dir);
275 parent_dir=dir_open(cur_part,dir_e.i_no); // 更新父目录
276 searched_record->parent_dir=parent_dir;
277 continue;
278 }else if (FT_REGULAR==dir_e.f_type){
279 searched_record->file_type=FT_REGULAR;
280 return dir_e.i_no;
281 }
282 }else { // 若找不到
283 /* 找不到目录项时,要留着parent_dir不要关闭,
284 * 若是创建新文件的话需要在parent_dir中创建 */
285 return -1;
286 }
287 }
288
289 /* 遍历了完整的路径并且查找的文件或目录只有同名目录存在 */
290 dir_close(searched_record->parent_dir);
291
292 /* 保存被查找目录的直接父目录 */
293 searched_record->parent_dir=dir_open(cur_part,parent_inode_no);
294 searched_record->file_type=FT_DIRECTORY;
295 return dir_e.i_no;
296 }
297
298 /* 打开或创建文件成功后,返回文件描述符,否则返回-1 */
299 int32_t sys_open(const char* pathname,uint8_t flags){
300 /* 对目录要用dir_open,这里只有open文件 */
301 if (pathname[strlen(pathname)-1]=='/'){
302 printk("can't open a directory %s\n",pathname);
303 return -1;
304 }
305 ASSERT(flags<=7);
306 int32_t fd=-1;
307
308 struct path_search_record searched_record;
309 memset(&searched_record,0,sizeof(struct path_search_record));
310
311 /* 记录目录深度,帮助判断中间某个目录不存在的情况 */
312 uint32_t pathname_depth=path_depth_cnt((char*)pathname);
313
314 /* 先检查文件是否存在 */
315 int inode_no=search_file(pathname,&searched_record);
316 bool found=inode_no!=-1?true:false;
317
318 if (searched_record.file_type==FT_DIRECTORY){
319 printk("can't open a directory with open(),use opendir() to instead\n");
320 dir_close(searched_record.parent_dir);
321 return -1;
322 }
323
324 uint32_t path_searched_depth=path_depth_cnt(searched_record.searched_path);
325
326 /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
327 if (pathname_depth!=path_searched_depth){ // 说明并没有访问到全部路径,某个中间目录是不存在的
328 printk("cannot access %s: Not a directory, subpath %s is't exist\n", \
329 pathname,searched_record.searched_path);
330 dir_close(searched_record.parent_dir);
331 return -1;
332 }
333
334 /* 若是最后一个路径上没找到,并且不是要创建文件,直接返回-1 */
335 if (!found && !(flags & O_CREAT)){
336 printk("in path %s, file %s is't exist\n", \
337 searched_record.searched_path,
338 (strrchr(searched_record.searched_path,'/')+1));
339 dir_close(searched_record.parent_dir);
340 return -1;
341 }else if (found && flags & O_CREAT){ // 若要创建的文件已存在
342 printk("%s has already exist!\n",pathname);
343 dir_close(searched_record.parent_dir);
344 return -1;
345 }
346
347 switch (flags & O_CREAT){
348 case O_CREAT:
349 printk("creating file\n");
350 fd=file_create(searched_record.parent_dir,(strrchr(pathname,'/')+1),flags);
351 dir_close(searched_record.parent_dir);
352 break;
353 default:
354 /* 其余情况为打开已存在文件
355 * O_REONLY,O_WRONLY,O_REWR */
356 fd=file_open(inode_no,flags);
357 }
358
359 /* 此fd是指任务pcb->fd_table数组中的元素下标,
360 * 并不是指全局file_table中的下标 */
361 return fd;
362 }
363
364 /* 将文件描述符转化为文件表的下标 */
365 static uint32_t fd_local2global(uint32_t local_fd){
366 struct task_struct* cur=running_thread();
367 int32_t global_fd=cur->fd_table[local_fd];
368 ASSERT(global_fd>=0 && global_fd<MAX_FILE_OPEN);
369 return (uint32_t)global_fd;
370 }
371
372 /* 关闭文件描述符fd指向的文件,成功返回0,否则-1 */
373 int32_t sys_close(int32_t fd){
374 int32_t ret=-1;
375 if (fd>2){
376 uint32_t _fd=fd_local2global(fd);
377 ret=file_close(&file_table[_fd]);
378 running_thread()->fd_table[fd]=-1; // 使该文件描述符可用
379 }
380 return ret;
381 }
382
383 /* 将buf中连续count个字节写入文件描述符fd,成功则返回写入的字节数,失败则返回-1 */
384 int32_t sys_write(int32_t fd,const void* buf,uint32_t count){
385 if (fd<0){
386 printk("sys_write: fd error\n");
387 return -1;
388 }
389 if (fd==stdout_no){
390 char tmp_buf[1024]={0};
391 memcpy(tmp_buf,buf,count);
392 console_put_str(tmp_buf);
393 return count;
394 }
395 uint32_t _fd=fd_local2global(fd);
396 struct file* wr_file=&file_table[_fd];
397 if (wr_file->fd_flag & O_WRONLY || wr_file->fd_flag & O_RDWR){
398 uint32_t bytes_written=file_write(wr_file,buf,count);
399 return bytes_written;
400 }else {
401 console_put_str("sys_write: not allowed to write file without flag O_RDWR or O_WRONLY\n");
402 return -1;
403 }
404 }
405
406 /* 从文件描述符fd指向的文件中读取count个字节到buf,若成功则返回读出的字节数,到文件尾则返回-1 */
407 int32_t sys_read(int32_t fd,void* buf,uint32_t count){
408 if (fd<0){
409 printk("sys_read: fd error\n");
410 return -1;
411 }
412 ASSERT(buf!=NULL);
413 uint32_t _fd=fd_local2global(fd);
414 return file_read(&file_table[_fd],buf,count);
415 }
416
417 /* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
418 void filesys_init(){
419 uint8_t channel_no=0,dev_no=0,part_idx=0;
420
421 /* sb_buf用来存储从硬盘上读入的超级块 */
422 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
423
424 if (sb_buf==NULL){
425 PANIC("alloc memory failed!");
426 }
427 printk("searching filesystem......\n");
428
429 while (channel_no<channel_cnt){
430 dev_no=0;
431 while (dev_no<2){
432 if (dev_no==0){
433 ++dev_no;
434 continue;
435 }
436 struct disk* hd=&channels[channel_no].devices[dev_no];
437 struct partition* part=hd->prim_parts;
438 while (part_idx<12){ // 四个主分区+八个逻辑分区
439 if (part_idx==4){ // 逻辑分区
440 part=hd->logic_parts;
441 }
442
443 /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
444 * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
445 * 若partition未初始化,则partition中的成员仍为0.
446 * 下面处理存在的分区. */
447 if (part->sec_cnt!=0){ // 如果分区存在
448 memset(sb_buf,0,SECTOR_SIZE);
449
450 /* 读出分区超级块,根据魔数是否正确来判断是否存在fs */
451 ide_read(hd,part->start_lba+1,sb_buf,1);
452
453 /* 只支持自己的文件系统,若磁盘上已经有fs就不再初始化了 */
454 if (sb_buf->magic==0x19590318){
455 printk("%s has filesystem\n",part->name);
456 }else{
457 printk("formatting %s's partition %s.....\n",hd->name,part->name);
458 partition_format(part);
459 }
460 }
461 ++part_idx;
462 ++part; // 下一分区
463 }
464 ++dev_no; //下一磁盘
465 }
466 ++channel_no; // 下一通道
467 }
468 sys_free(sb_buf);
469
470 /* 确定默认操作的分区 */
471 char default_part[8]="sdb1";
472 /* 挂载分区 */
473 list_traversal(&partition_list,mount_partition,(int)default_part);
474
475 /* 将当前分区的根目录打开 */
476 open_root_dir(cur_part);
477
478 /* 初始化文件表 */
479 uint32_t fd_idx=0;
480 while (fd_idx<MAX_FILE_OPEN){
481 file_table[fd_idx++].fd_inode=NULL;
482 }
483 }
④fs/fs.h,添加一句函数声明:
int32_t sys_read(int32_t fd,void* buf,uint32_t count);
⑤kernel/main.c,修改main()并添加一个头文件:

1 #include "string.h"
2
3 int main(void){
4 put_str("Welcome,\nI am kernel!\n");
5 init_all();
6
7 intr_enable();
8 process_execute(u_prog_a,"u_prog_a");
9 process_execute(u_prog_b,"u_prog_b");
10 thread_start("k_thread_a",31,k_thread_a,"I'm thread_a ");
11 thread_start("k_thread_b",31,k_thread_b,"I'm thread_b ");
12
13 uint32_t fd=sys_open("/file1",O_RDWR);
14 printf("open /file1, fd:%d\n",fd);
15 char buf[64]={0};
16 int read_bytes=sys_read(fd,buf,18);
17 printf("1_ read %d bytes: \n%s\n",read_bytes,buf);
18
19 memset(buf,0,64);
20 read_bytes=sys_read(fd,buf,6);
21 printf("2_ read %d bytes: \n%s\n",read_bytes,buf);
22
23 memset(buf,0,64);
24 read_bytes=sys_read(fd,buf,6);
25 printf("3_ read %d bytes: \n%s\n",read_bytes,buf);
26
27 printf("_____ close file1 and reopen _____\n");
28 sys_close(fd);
29 fd=sys_open("/file1",O_RDWR);
30 memset(buf,0,64);
31 read_bytes=sys_read(fd,buf,24);
32 printf("4_ read %d bytes: \n%s\n",read_bytes,buf);
33
34 sys_close(fd);
35 while(1);
36 return 0;
37 }
运行结果如图:
ok,“曲折地”读出了“hello world\n”。
6.实现文件读写指针定位功能
①fs/fs.h,增加一个枚举类型whence和一句函数声明:
/* 文件读写的偏移量 */
enum whence{
SEEK_SET=1,
SEEK_CUR,
SEEK_END
};
int32_t sys_lseek(int32_t fd,int32_t offset,uint8_t whence);
②fs/fs.c,编写sys_lseek()函数:
1 /* 重置用于文件读写操作的偏移指针。成功时返回新的偏移量,出错时返回-1 */
2 int32_t sys_lseek(int32_t fd,int32_t offset,uint8_t whence){
3 if (fd<0){
4 printk("sys_lseek: fd error\n");
5 return -1;
6 }
7 ASSERT(whence>0 && whence<4);
8 uint32_t _fd=fd_local2global(fd);
9 struct file* pf=&file_table[_fd];
10 int32_t new_pos=0; // 新的偏移量必须位于文件大小之内
11 int32_t file_size=(int32_t)pf->fd_inode->i_size;
12 switch(whence){
13 /* SEEK_SET 新的读写位置是相对于文件开头再增加offset个位移量 */
14 case SEEK_SET:
15 new_pos=offset;
16 break;
17
18 /* SEEK_CUR 新的读写位置是相对于当前位置增加offset个位移量 */
19 case SEEK_CUR:
20 new_pos=(int32_t)pf->fd_pos+offset;
21 break;
22
23 /* SEEK_END 新的读写位置是相对于文件尺寸增加offset个位移量 */
24 case SEEK_END:
25 new_pos=file_size+offset;
26 }
27 if (new_pos<0 || new_pos>(file_size-1)){
28 return -1;
29 }
30 pf->fd_pos=new_pos;
31 return pf->fd_pos;
32 }
③kernel/main.c,稍微修改一下main():

1 int main(void){
2 put_str("Welcome,\nI am kernel!\n");
3 init_all();
4
5 intr_enable();
6 process_execute(u_prog_a,"u_prog_a");
7 process_execute(u_prog_b,"u_prog_b");
8 thread_start("k_thread_a",31,k_thread_a,"I'm thread_a ");
9 thread_start("k_thread_b",31,k_thread_b,"I'm thread_b ");
10
11 uint32_t fd=sys_open("/file1",O_RDWR);
12 printf("open /file1, fd:%d\n",fd);
13 char buf[64]={0};
14 int read_bytes=sys_read(fd,buf,18);
15 printf("1_ read %d bytes: \n%s\n",read_bytes,buf);
16
17 memset(buf,0,64);
18 read_bytes=sys_read(fd,buf,6);
19 printf("2_ read %d bytes: \n%s\n",read_bytes,buf);
20
21 memset(buf,0,64);
22 read_bytes=sys_read(fd,buf,6);
23 printf("3_ read %d bytes: \n%s\n",read_bytes,buf);
24
25 printf("_____ SEEK_SET 0 _____\n");
26 sys_lseek(fd,0,SEEK_SET);
27 memset(buf,0,64);
28 read_bytes=sys_read(fd,buf,24);
29 printf("4_ read %d bytes: \n%s\n",read_bytes,buf);
30
31 sys_close(fd);
32 while(1);
33 return 0;
34 }
这个功能还是很简单的,看下运行结果:
和第5步的输出差不多,说明关闭后重打开文件的fd与直接SEEK_SET 0的作用是一致的,都会把偏移指针指向文件开头。
7.实现文件删除功能
(本篇博客的最后一个功能实现了,加油!)
作为本篇博客的最后一个功能实现,为了有个完整的认识,将有关代码完整放出来吧。
①fs/inode.c:

1 #include "inode.h"
2 #include "fs.h"
3 #include "file.h"
4 #include "global.h"
5 #include "debug.h"
6 #include "memory.h"
7 #include "interrupt.h"
8 #include "list.h"
9 #include "stdio-kernel.h"
10 #include "string.h"
11 #include "super_block.h"
12
13 /* 用来存储inode位置 */
14 struct inode_position{
15 bool two_sec; // inode是否跨扇区
16 uint32_t sec_lba; // inode所在的扇区号
17 uint32_t off_size; // inode在扇区内的字节偏移量
18 };
19
20 /* 获取inode所在的扇区和偏移量 */
21 static void inode_locate(struct partition* part,uint32_t inode_no,struct inode_position* inode_pos){
22 /* inode_table在硬盘上是连续的 */
23 ASSERT(inode_no<4096);
24 uint32_t inode_table_lba=part->sb->inode_table_lba;
25
26 uint32_t inode_size=sizeof(struct inode);
27 uint32_t off_size=inode_no*inode_size; // inode_no号inode相对于inode_table_lba的字节偏移量
28 uint32_t off_sec=off_size/512; // 相对于inode_table_lba的扇区偏移量
29 uint32_t off_size_in_sec=off_size%512; // 扇区中的起始地址(扇区内偏移量)
30
31 /* 判断此结点是否跨越两个扇区 */
32 uint32_t left_in_sec=512-off_size_in_sec;
33 if (left_in_sec<inode_size){ // 剩下的空间不足以容纳一个inode
34 inode_pos->two_sec=true;
35 }else {
36 inode_pos->two_sec=false;
37 }
38 inode_pos->sec_lba=inode_table_lba+off_sec;
39 inode_pos->off_size=off_size_in_sec;
40 }
41
42 /* 将inode写入到分区part */
43 void inode_sync(struct partition* part,struct inode* inode,void* io_buf){ // io_buf是用于硬盘io的缓冲区
44 uint8_t inode_no=inode->i_no;
45 struct inode_position inode_pos;
46 inode_locate(part,inode_no,&inode_pos);
47 ASSERT(inode_pos.sec_lba<=(part->start_lba+part->sec_cnt));
48
49 /* 硬盘中的inode中的成员inode_tag和i_open_cnts是不需要的,
50 * 它们只在内存中记录链表位置和多少进程共享 */
51 struct inode pure_inode;
52 memcpy(&pure_inode,inode,sizeof(struct inode));
53
54 /* 以下inode的三个成员只存在于内存中,现在将inode同步到硬盘,清掉这三项即可 */
55 pure_inode.i_open_cnts=0;
56 pure_inode.write_deny=0;
57 pure_inode.inode_tag.prev=pure_inode.inode_tag.next=NULL;
58
59 char* inode_buf=(char*)io_buf;
60 if (inode_pos.two_sec){
61 /* 读写硬盘以扇区为单位,若写入的数据小于一扇区,则将原硬盘上的内容读出来再和新数据拼成一扇区后再写入 */
62 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,2); // 读取两个扇区
63
64 /* 开始将待写入的inode拼到这两个扇区的相应位置 */
65 memcpy((inode_buf+inode_pos.off_size),&pure_inode,sizeof(struct inode));
66
67 /* 将拼接好的数据再写入硬盘 */
68 ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,2);
69 }else {
70 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,1);
71 memcpy((inode_buf+inode_pos.off_size),&pure_inode,sizeof(struct inode));
72 ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,1);
73 }
74 }
75
76 /* 根据inode_no返回相应的inode */
77 struct inode* inode_open(struct partition* part,uint32_t inode_no){
78 /* 先在已打开inode链表中找inode,此链表是为了提速而创建的缓冲区 */
79 struct list_elem* elem=part->open_inodes.head.next;
80 struct inode* inode_found;
81 while (elem!=&part->open_inodes.tail){
82 inode_found=elem2entry(struct inode,inode_tag,elem);
83 if (inode_found->i_no==inode_no){
84 inode_found->i_open_cnts++;
85 return inode_found;
86 }
87 elem=elem->next;
88 }
89
90 /* 由于open_inodes链表中找不到,下面从硬盘上读入此inode并加入到此链表中 */
91 struct inode_position inode_pos;
92
93 /* inode位置信息会存入inode_pos,包括inode所在扇区地址和扇区内的字节偏移量 */
94 inode_locate(part,inode_no,&inode_pos);
95
96 /* 为使通过sys_malloc创建的新inode被所有任务共享,
97 * 需要将inode置于内核空间,故需要临时
98 * 将cur_pbc->pgdir置为NULL */
99 struct task_struct* cur=running_thread();
100 uint32_t* cur_pagedir_bak=cur->pgdir;
101 cur->pgdir=NULL;
102 /* 以上三行代码完成后下面分配的内存将位于内核区 */
103 inode_found=(struct inode*)sys_malloc(sizeof(struct inode));
104 /* 恢复pgdir */
105 cur->pgdir=cur_pagedir_bak;
106
107 char* inode_buf;
108 if (inode_pos.two_sec){
109 inode_buf=(char*)sys_malloc(1024);
110
111 /* inode是被partition_format函数连续写入扇区的,
112 * 所以下面可以连续读出来 */
113 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,2);
114 }else {
115 inode_buf=(char*)sys_malloc(512);
116 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,1);
117 }
118 memcpy(inode_found,inode_buf+inode_pos.off_size,sizeof(struct inode));
119
120 /* 因为一会很可能用到此inode,故将其插入到队首便于提前检索到 */
121 list_push(&part->open_inodes,&inode_found->inode_tag);
122 inode_found->i_open_cnts=1;
123
124 sys_free(inode_buf);
125 return inode_found;
126 }
127
128 /* 关闭inode或减少inode的打开数 */
129 void inode_close(struct inode* inode){
130 /* 若没有进程再打开此文件,将此inode去掉并释放空间 */
131 enum intr_status old_status=intr_disable();
132 if (--inode->i_open_cnts==0){
133 list_remove(&inode->inode_tag);
134 /* inode_open时为实现inode为所有进程共享,
135 * 已经在sys_malloc为inode分配了内核空间,
136 * 释放inode时也要确保释放的是内核内存池 */
137 struct task_struct* cur=running_thread();
138 uint32_t* cur_pagedir_bak=cur->pgdir;
139 cur->pgdir=NULL;
140 sys_free(inode);
141 cur->pgdir=cur_pagedir_bak;
142 }
143 intr_set_status(old_status);
144 }
145
146 /* 将硬盘分区part上的inode清空 */
147 void inode_delete(struct partition* part,uint32_t inode_no,void* io_buf){
148 ASSERT(inode_no<4096);
149 struct inode_position inode_pos;
150 inode_locate(part,inode_no,&inode_pos); // inode位置信息会存入inode_pos
151 ASSERT(inode_pos.sec_lba<=(part->start_lba+part->sec_cnt));
152
153 char* inode_buf=(char*)io_buf;
154 if (inode_pos.two_sec){ // inode跨扇区,读入两个扇区
155 /* 将原硬盘上的内容先读出来 */
156 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,2);
157 /* 将inode_buf清0 */
158 memset((inode_buf+inode_pos.off_size),0,sizeof(struct inode));
159 /* 用清0的内存数据覆盖磁盘 */
160 ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,2);
161 }else { // 未跨扇区,只读入一个扇区
162 /* 将原硬盘上的内容先读出来 */
163 ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,1);
164 /* 将inode_buf清0 */
165 memset((inode_buf+inode_pos.off_size),0,sizeof(struct inode));
166 /* 用清0的内存数据覆盖磁盘 */
167 ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,1);
168 }
169 }
170
171 /* 回收inode的数据块和inode本身 */
172 void inode_release(struct partition* part,uint32_t inode_no){
173 struct inode* inode_to_del=inode_open(part,inode_no);
174 ASSERT(inode_to_del->i_no==inode_no);
175
176 /* 1.回收inode占用的所有块 */
177 uint8_t block_idx=0,block_cnt=12;
178 uint32_t block_bitmap_idx;
179 uint32_t all_blocks[140]={0}; // 12个直接块+128个间接块
180
181 /* 1.1 先将前12个直接块存入all_blocks */
182 while (block_idx<12){
183 all_blocks[block_idx]=inode_to_del->i_sectors[block_idx];
184 ++block_idx;
185 }
186
187 /* 1.2 如果一级间接块存在,将其128个间接块读到all_blocks[12~],并释放一级间接块表所占的扇区 */
188 if (inode_to_del->i_sectors[12]!=0){
189 ide_read(part->my_disk,inode_to_del->i_sectors[12],all_blocks+12,1);
190 block_cnt=140;
191
192 /* 回收一级间接块表占用的内存 */
193 block_bitmap_idx=inode_to_del->i_sectors[12]-part->sb->data_start_lba;
194 ASSERT(block_bitmap_idx>0);
195 bitmap_set(&part->block_bitmap,block_bitmap_idx,0);
196 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
197 }
198
199 /* 1.3 inode所有块地址已经收集到all_blocks中,下面逐个回收 */
200 block_idx=0;
201 while (block_idx<block_cnt){
202 if (all_blocks[block_idx]!=0){
203 block_bitmap_idx=0;
204 block_bitmap_idx=all_blocks[block_idx]-part->sb->data_start_lba;
205 ASSERT(block_bitmap_idx>0);
206 bitmap_set(&part->block_bitmap,block_bitmap_idx,0);
207 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
208 }
209 ++block_idx;
210 }
211
212 /* 2.回收该inode所占用的inode */
213 bitmap_set(&part->inode_bitmap,inode_no,0);
214 bitmap_sync(cur_part,inode_no,INODE_BITMAP);
215
216 /********** 调试 **********
217 * 在inode_table中将此inode清0,
218 * 但实际上不需要,indoe分配是由inode bitmap控制的
219 * 硬盘上的数据不需要清0,可以直接覆盖 */
220 void* io_buf=sys_malloc(1024);
221 inode_delete(part,inode_no,io_buf);
222 sys_free(io_buf);
223 /************************************************/
224
225 inode_close(inode_to_del);
226 }
227
228 /* 初始化new_inode */
229 void inode_init(uint32_t inode_no,struct inode* new_inode){
230 new_inode->i_no=inode_no;
231 new_inode->i_size=0;
232 new_inode->i_open_cnts=0;
233 new_inode->write_deny=false;
234
235 /* 初始化块索引数组i_sector */
236 uint8_t sec_idx=0;
237 while (sec_idx<13){
238 /* i_sectors[12]为一级间接块地址 */
239 new_inode->i_sectors[sec_idx]=0;
240 sec_idx++;
241 }
242 }
②fs/inode.h:

1 #ifndef __FS_INODE_H
2 #define __FS_INODE_H
3 #include "stdint.h"
4 #include "list.h"
5 #include "ide.h"
6
7 /* inode结构 */
8 struct inode{
9 uint32_t i_no; // inode编号
10
11 /* 当此inode指向文件时,i_size为文件大小;
12 * 当此inode指向目录时,i_size为目录下所有目录项之和的大小 */
13 uint32_t i_size;
14
15 uint32_t i_open_cnts;//记录此文件被打开的次数
16 bool write_deny; // 写文件不能并行,进程写文件前检查此标识
17
18 /* i_sectors[0-11]是直接块,i_sectors[12]用来存储一级间接块指针 */
19 uint32_t i_sectors[13];
20 struct list_elem inode_tag; // 标识此inode,加入“已打开的inode列表”(内存缓存)中
21 };
22
23 struct inode* inode_open(struct partition* part,uint32_t inode_no);
24 void inode_sync(struct partition* part,struct inode* inode,void* io_buf);
25 void inode_init(uint32_t inode_no,struct inode* new_inode);
26 void inode_close(struct inode* inode);
27 void inode_release(struct partition* part,uint32_t inode_no);
28 void inode_delete(struct partition* part,uint32_t inode_no,void* io_buf);
29 #endif
③fs/dir.c:

1 #include "dir.h"
2 #include "stdint.h"
3 #include "inode.h"
4 #include "file.h"
5 #include "fs.h"
6 #include "stdio-kernel.h"
7 #include "global.h"
8 #include "debug.h"
9 #include "memory.h"
10 #include "string.h"
11 #include "interrupt.h"
12 #include "super_block.h"
13
14 struct dir root_dir; // 根目录
15
16 /* 打开根目录 */
17 void open_root_dir(struct partition* part){
18 root_dir.inode=inode_open(part,part->sb->root_inode_no);
19 root_dir.dir_pos=0;
20 }
21
22 /* 在分区part上打开inode_no的目录并返回目录指针 */
23 struct dir* dir_open(struct partition* part,uint32_t inode_no){
24 struct dir* pdir=(struct dir*)sys_malloc(sizeof(struct dir));
25 pdir->inode=inode_open(part,inode_no);
26 pdir->dir_pos=0;
27 return pdir;
28 }
29
30 /* 在part分区内的pdir目录内寻找名为name的文件或目录,
31 * 找到后返回true并将其目录项存入dir_e,否则返回false */
32 bool search_dir_entry(struct partition* part,struct dir* pdir,\
33 const char* name,struct dir_entry* dir_e){
34 uint32_t block_cnt=140; // 12个直接块+128个间接块=140块
35
36 /* 共560B */
37 uint32_t* all_blocks=(uint32_t*)sys_malloc(48+512);
38 if (all_blocks==NULL){
39 printk("search_dir_entry: sys_malloc for all_blocks failed");
40 return false;
41 }
42
43 uint32_t block_idx=0;
44 while (block_idx<12){
45 all_blocks[block_idx]=pdir->inode->i_sectors[block_idx];
46 ++block_idx;
47 }
48 block_idx=0;
49
50 if (pdir->inode->i_sectors[12]!=0){ // 若含有一级间接块表
51 ide_read(part->my_disk,pdir->inode->i_sectors[12],all_blocks+12,1);
52 }
53 /* 至此,all_blocks存储的是该文件或目录的所有扇区地址 */
54
55 /* 目录项不跨区
56 * 这样目录项容易处理,只申请容纳一个扇区的内存 */
57 uint8_t* buf=(uint8_t*)sys_malloc(SECTOR_SIZE);
58 struct dir_entry* p_de=(struct dir_entry*)buf; // p_de为指向目录项的指针
59 uint32_t dir_entry_size=part->sb->dir_entry_size;
60 uint32_t dir_entry_cnt=SECTOR_SIZE/dir_entry_size; // 一个扇区可容纳的目录项个数
61
62 /* 开始在所有块中查找目录项 */
63 while (block_idx<block_cnt){
64 /* 块地址为0时表示该块中无数据,继续在其它块中找 */
65 if (all_blocks[block_idx]==0){
66 ++block_idx;
67 continue;
68 }
69 ide_read(part->my_disk,all_blocks[block_idx],buf,1);
70
71 uint32_t dir_entry_idx=0;
72 /* 遍历扇区中所有目录项 */
73 while (dir_entry_idx<dir_entry_cnt){
74 /* 若找到,就直接复制整个目录项 */
75 if (!strcmp(p_de->filename,name)){
76 memcpy(dir_e,p_de,dir_entry_size);
77 sys_free(buf);
78 sys_free(all_blocks);
79 return true;
80 }
81 ++dir_entry_idx;
82 ++p_de;
83 }
84 ++block_idx;
85 p_de=(struct dir_entry*)buf; // 此时p_de已经指向扇区内最后一个完整目录项了,需要恢复p_de指向buf
86 memset(buf,0,SECTOR_SIZE); // buf清0
87 }
88 sys_free(buf);
89 sys_free(all_blocks);
90 return false;
91 }
92
93 /* 关闭目录 */
94 void dir_close(struct dir* dir){
95 /********** 根目录不能关闭 ***********
96 * 1.根目录自打开后就不能关闭,否则还需要再次open_root_dir(),
97 * 2.root_dir所在的内存是低端1M之内,并非在堆中,free()会出现问题 */
98 if (dir==&root_dir){
99 /* 不做任何处理直接返回 */
100 return;
101 }
102 inode_close(dir->inode);
103 sys_free(dir);
104 }
105
106 /* 在内存中初始化目录项p_de */
107 void create_dir_entry(char* filename,uint32_t inode_no,uint8_t file_type,struct dir_entry* p_de){
108 ASSERT(strlen(filename)<=MAX_FILE_NAME_LEN);
109
110 /* 初始化目录项 */
111 memcpy(p_de->filename,filename,strlen(filename));
112 p_de->i_no=inode_no;
113 p_de->f_type=file_type;
114 }
115
116 /* 将目录项p_de写入父目录parent_dir中,io_buf由主调函数提供 */
117 bool sync_dir_entry(struct dir* parent_dir,struct dir_entry* p_de,void* io_buf){
118 struct inode* dir_inode=parent_dir->inode;
119 uint32_t dir_size=dir_inode->i_size;
120 uint32_t dir_entry_size=cur_part->sb->dir_entry_size;
121
122 ASSERT(dir_size%dir_entry_size==0);
123
124 uint32_t dir_entrys_per_sec=(512/dir_entry_size); // 每扇区最大目录数
125 int32_t block_lba=-1;
126
127 /* 将该目录的所有扇区地址(12个直接块+128个间接块)存入all_blocks */
128 uint8_t block_idx=0;
129 uint32_t all_blocks[140]={0};
130
131 /* 将12个直接块存入all_blocks */
132 while (block_idx<12){
133 all_blocks[block_idx]=dir_inode->i_sectors[block_idx];
134 ++block_idx;
135 }
136
137 struct dir_entry* dir_e=(struct dir_entry*)io_buf; // dir_e用来遍历io_buf
138 int32_t block_bitmap_idx=-1;
139
140 /* 开始遍历所有块以寻找目录项空位,若已有扇区中没有空位,
141 * 在不超过文件大小的情况下上申请新扇区来存储新目录项 */
142 block_idx=0;
143 while (block_idx<140){ // 共140个块
144 block_bitmap_idx=-1;
145 if (all_blocks[block_idx]==0){
146 block_lba=block_bitmap_alloc(cur_part);
147 if (block_lba==-1){
148 printk("alloc block bitmap for sync_dir_entry failed\n");
149 return false;
150 }
151
152 /* 每分配一个块就同步一次block_bitmap */
153 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
154 ASSERT(block_bitmap_idx!=-1);
155 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
156
157 block_bitmap_idx=-1;
158 if (block_idx<12){ // 直接块
159 dir_inode->i_sectors[block_idx]=all_blocks[block_idx]=block_lba;
160 }else if (block_idx==12){ // 尚未分配一级间接块表
161 dir_inode->i_sectors[12]=block_lba;
162 block_lba=-1;
163 block_lba=block_bitmap_alloc(cur_part); // 在分配一个块作第0个间接块
164 if (block_lba==-1){
165 block_bitmap_idx=dir_inode->i_sectors[12]-cur_part->sb->data_start_lba;
166 bitmap_set(&cur_part->block_bitmap,block_bitmap_idx,0);
167 dir_inode->i_sectors[12]=0;
168 printk("alloc block bitmap for sync_dir_entry failed\n");
169 return false;
170 }
171
172 /* 每分配一个块就同步一次block_bitmap */
173 block_bitmap_idx=block_lba-cur_part->sb->data_start_lba;
174 ASSERT(block_bitmap_idx!=-1);
175 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
176
177 all_blocks[12]=block_lba;
178 /* 把新分配的第0个间接块写入一级间接块表 */
179 ide_write(cur_part->my_disk,dir_inode->i_sectors[12],all_blocks+12,1);
180 }else { // 若是间接块未分配
181 all_blocks[block_idx]=block_lba;
182 /* 把新分配的第(block_idx-12)个间接块地址写入一级间接块表 */
183 ide_write(cur_part->my_disk,dir_inode->i_sectors[12],all_blocks+12,1);
184 }
185
186 /* 再将新目录项p_de写入新分配的间接块 */
187 memset(io_buf,0,512);
188 memcpy(io_buf,p_de,dir_entry_size);
189 ide_write(cur_part->my_disk,all_blocks[block_idx],io_buf,1);
190 dir_inode->i_size+=dir_entry_size;
191 return true;
192 }
193
194 /* 若第block_idx块已存在,将其读入内存,然后在该块中查找空目录项 */
195 ide_read(cur_part->my_disk,all_blocks[block_idx],io_buf,1);
196 /* 在扇区内查找空目录项 */
197 uint8_t dir_entry_idx=0;
198 while (dir_entry_idx<dir_entrys_per_sec){
199 if ((dir_e+dir_entry_idx)->f_type==FT_UNKNOWN){
200 memcpy(dir_e+dir_entry_idx,p_de,dir_entry_size);
201 ide_write(cur_part->my_disk,all_blocks[block_idx],io_buf,1);
202
203 dir_inode->i_size+=dir_entry_size;
204 return true;
205 }
206 ++dir_entry_idx;
207 }
208 ++block_idx;
209 }
210 printk("directory is full!\n");
211 return false;
212 }
213
214 /* 把分区part目录pdir中编号为inode_no的目录项删除 */
215 bool delete_dir_entry(struct partition* part,struct dir* pdir,uint32_t inode_no,void* io_buf){
216 struct inode* dir_inode=pdir->inode;
217 uint32_t block_idx=0,all_blocks[140]={0};
218 /* 收集目录全部地址 */
219 while (block_idx<12){
220 all_blocks[block_idx]=dir_inode->i_sectors[block_idx];
221 ++block_idx;
222 }
223 if (dir_inode->i_sectors[12]){
224 ide_read(part->my_disk,dir_inode->i_sectors[12],all_blocks+12,1);
225 }
226
227 /* 目录项在存储时保证不会跨扇区 */
228 uint32_t dir_entry_size=part->sb->dir_entry_size;
229 uint32_t dir_entrys_per_sec=(SECTOR_SIZE/dir_entry_size);
230 struct dir_entry* dir_e=(struct dir_entry*)io_buf;
231 struct dir_entry* dir_entry_found=NULL;
232 uint8_t dir_entry_idx,dir_entry_cnt;
233 bool is_dir_first_block=false; // 目录的第一个块
234
235 /* 遍历所有块,寻找目录项 */
236 block_idx=0;
237 while (block_idx<140){
238 is_dir_first_block=false;
239 if (all_blocks[block_idx]==0){
240 ++block_idx;
241 continue;
242 }
243 dir_entry_idx=dir_entry_cnt=0;
244 memset(io_buf,0,SECTOR_SIZE);
245 /* 读取扇区,获得目录项 */
246 ide_read(part->my_disk,all_blocks[block_idx],io_buf,1);
247
248 /* 遍历所有的目录项,统计该扇区的目录项数量及是否有待删除的目录项 */
249 while (dir_entry_idx<dir_entrys_per_sec){
250 if ((dir_e+dir_entry_idx)->f_type!=FT_UNKNOWN){
251 if (!strcmp((dir_e+dir_entry_idx)->filename,".")){
252 is_dir_first_block=true;
253 }else if (strcmp((dir_e+dir_entry_idx)->filename,".") &&
254 strcmp((dir_e+dir_entry_idx)->filename,"..")){
255 ++dir_entry_cnt; // 统计此扇区内的目录项个数,用来判断删除目录项后是否回收该扇区
256 if ((dir_e+dir_entry_idx)->i_no==inode_no){
257 ASSERT(dir_entry_found==NULL);
258 dir_entry_found=dir_e+dir_entry_idx;
259 /* 找到后也继续遍历,统计总共的目录项数 */
260 }
261 }
262 }
263 ++dir_entry_idx;
264 }
265
266 /* 若此扇区未找到该目录项,继续在下个扇区中找 */
267 if (dir_entry_found==NULL){
268 ++block_idx;
269 continue;
270 }
271
272 /* 在此扇区中找到目录项后,清除该目录项并判断是否回收扇区,随后退出循环 */
273 ASSERT(dir_entry_cnt>=1);
274 /* 除目录第一个扇区外,若该扇区上只有目录项自己,则将整个扇区回收 */
275 if (dir_entry_cnt==1 && !is_dir_first_block){
276 /* 1.在块位图中回收该块 */
277 uint32_t block_bitmap_idx=all_blocks[block_idx]-part->sb->data_start_lba;
278 bitmap_set(&part->block_bitmap,block_bitmap_idx,0);
279 bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
280
281 /* 2.将块地址从数组i_sectors或索引表中去掉 */
282 if (block_idx<12){
283 dir_inode->i_sectors[block_idx]=0;
284 }else { // 在一级 间接索引表中擦除该间接块地址
285 /* 先判断一级间接索引表中间接块的数量,如果仅有这一个间接块,连同间接索引表所在块一同回收 */
286 uint32_t indirect_blocks=0;
287 uint32_t indirect_block_idx=12;
288 while (indirect_block_idx<140){
289 if (all_blocks[indirect_block_idx]!=0){
290 ++indirect_blocks;
291 }
292 }
293 ASSERT(indirect_blocks>=1); // 包括当前块
294
295 if (indirect_blocks>1){ // 还有其它间接块
296 all_blocks[block_idx]=0;
297 ide_write(part->my_disk,dir_inode->i_sectors[12],all_blocks+12,1);
298 }else {
299 /* 回收间接索引表所在的块 */
300 block_bitmap_idx=dir_inode->i_sectors[12]-part->sb->data_start_lba;
301 bitmap_set(&part->block_bitmap,block_bitmap_idx,0);
302 bitmap_sync(&cur_part,block_bitmap_idx,BLOCK_BITMAP);
303
304 /* 将间接索引表地址清0 */
305 dir_inode->i_sectors[12]=0;
306 }
307 }
308 }else { // 仅将该目录清空
309 memset(dir_entry_found,0,dir_entry_size);
310 ide_write(part->my_disk,all_blocks[block_idx],io_buf,1);
311 }
312
313 /* 更新inode并同步到硬盘 */
314 ASSERT(dir_inode->i_size>=dir_entry_size);
315 dir_inode->i_size-=dir_entry_size;
316 memset(io_buf,0,SECTOR_SIZE*2);
317 inode_sync(part,dir_inode,io_buf);
318
319 return true;
320 }
321 /* 所有块中未找到则返回false,若出现这种情况应该是search_file出错 */
322 return false;
323 }
④fs/dir.h:

1 #ifndef __FS_DIR_H
2 #define __FS_DIR_H
3 #include "stdint.h"
4 #include "inode.h"
5 #include "fs.h"
6 #include "ide.h"
7 #include "global.h"
8
9 #define MAX_FILE_NAME_LEN 16
10
11 /* 目录结构 */
12 struct dir{
13 struct inode* inode;
14 uint32_t dir_pos; // 记录在目录内的偏移
15 uint8_t dir_buf[512]; // 目录的数据缓存
16 };
17
18 struct dir_entry{
19 char filename[MAX_FILE_NAME_LEN]; // 普通文件或目录
20 uint32_t i_no; // 普通文件或目录对应的inode
21 enum file_types f_type; // 文件类型
22 };
23
24 extern struct dir root_dir; // 根目录
25 void open_root_dir(struct partition* part);
26 struct dir* dir_open(struct partition* part,uint32_t inode_no);
27 void dir_close(struct dir* dir);
28 bool search_dir_entry(struct partition* part,struct dir* pdir,const char* name,struct dir_entry* dir_e);
29 void create_dir_entry(char* filename,uint32_t inode_no,uint8_t file_type,struct dir_entry* p_de);
30 bool sync_dir_entry(struct dir* parent_dir,struct dir_entry* p_de,void* io_buf);
31 bool delete_dir_entry(struct partition* part,struct dir* pdir,uint32_t inode_no,void* io_buf);
32 #endif
⑤fs/fs.c:

1 #include "fs.h"
2 #include "super_block.h"
3 #include "inode.h"
4 #include "dir.h"
5 #include "stdint.h"
6 #include "stdio-kernel.h"
7 #include "list.h"
8 #include "string.h"
9 #include "ide.h"
10 #include "global.h"
11 #include "debug.h"
12 #include "memory.h"
13 #include "file.h"
14 #include "console.h"
15
16 struct partition* cur_part; // 默认情况下操作的是哪个分区
17
18 /* 在分区中找到名为part_name的分区,并将其指针赋给cur_part */
19 static bool mount_partition(struct list_elem* pelem,int arg){
20 char* part_name=(char*)arg;
21 struct partition* part=elem2entry(struct partition,part_tag,pelem);
22 if (!strcmp(part->name,part_name)){
23 cur_part=part;
24 struct disk* hd=cur_part->my_disk;
25
26 /* sb_buf用来存储从硬盘上读入的超级块 */
27 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
28
29 /* 在内存中创建分区cur_part的超级块 */
30 cur_part->sb=(struct super_block*)sys_malloc(sizeof(struct super_block));
31 if (cur_part->sb==NULL){
32 PANIC("alloc memory failed!");
33 }
34
35 /* 读入超级块 */
36 memset(sb_buf,0,SECTOR_SIZE);
37 ide_read(hd,cur_part->start_lba+1,sb_buf,1);
38
39 /* 把sb_buf中超级块的信息复制到分区的超级块sb中 */
40 memcpy(cur_part->sb,sb_buf,sizeof(struct super_block));
41
42 /********** 将硬盘上的块位图读入到内存 **********/
43 cur_part->block_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->block_bitmap_sects*SECTOR_SIZE);
44 if (cur_part->block_bitmap.bits==NULL){
45 PANIC("alloc memory failed!");
46 }
47 cur_part->block_bitmap.btmp_bytes_len=sb_buf->block_bitmap_sects*SECTOR_SIZE;
48 /* 从硬盘上读入块位图到分区的block_bitmap.bits */
49 ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);
50 /***************************************************/
51
52 /********** 将硬盘上的inode位图读入到内存 **********/
53 cur_part->inode_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects*SECTOR_SIZE);
54 if (cur_part->inode_bitmap.bits==NULL){
55 PANIC("alloc memory failed!");
56 }
57 cur_part->inode_bitmap.btmp_bytes_len=sb_buf->inode_bitmap_sects*SECTOR_SIZE;
58 /* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
59 ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);
60 /**************************************************/
61
62 list_init(&cur_part->open_inodes);
63 printk("mount %s done!\n",part->name);
64
65 /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关
66 * 只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历 */
67 return true;
68 }
69 return false; // 使list_traversal继续遍历
70 }
71
72 /* 初始化分区元信息,创建文件系统 */
73 static void partition_format(struct partition* part){
74 /* 为方便,一个块大小是一扇区 */
75 uint32_t boot_sector_sects=1;
76 uint32_t super_block_sects=1;
77 uint32_t inode_bitmap_sects=DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); // inode位图所占用的扇区数
78 uint32_t inode_table_sects=DIV_ROUND_UP(((sizeof(struct inode)*MAX_FILES_PER_PART)),SECTOR_SIZE);
79 uint32_t used_sects=boot_sector_sects+super_block_sects+inode_bitmap_sects+inode_table_sects;
80 uint32_t free_sects=part->sec_cnt-used_sects;
81
82 /****************** 块位图占用的扇区数 ********************/
83 uint32_t block_bitmap_sects;
84 block_bitmap_sects=DIV_ROUND_UP(free_sects,BITS_PER_SECTOR);
85 /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
86 uint32_t block_bitmap_bit_len=free_sects-block_bitmap_sects;
87 block_bitmap_sects=DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);
88 /*********************************************************/
89
90 /* 超级块初始化 */
91 struct super_block sb;
92 sb.magic=0x19590318;
93 sb.sec_cnt=part->sec_cnt;
94 sb.inode_cnt=MAX_FILES_PER_PART;
95 sb.part_lba_base=part->start_lba;
96
97 sb.block_bitmap_lba=sb.part_lba_base+2; // 第0块为bootblock,第1块为superblock
98 sb.block_bitmap_sects=block_bitmap_sects;
99
100 sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sects;
101 sb.inode_bitmap_sects=inode_bitmap_sects;
102
103 sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sects;
104 sb.inode_table_sects=inode_table_sects;
105
106 sb.data_start_lba=sb.inode_table_lba+sb.inode_table_sects;
107 sb.root_inode_no=0;
108 sb.dir_entry_size=sizeof(struct dir_entry);
109
110 printk("%s info:\n",part->name);
111 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);
112
113 struct disk* hd=part->my_disk;
114 /****************************
115 * 1.将超级块写入本分区的1扇区
116 * *************************/
117 ide_write(hd,part->start_lba+1,&sb,1);
118 printk(" super_block_lba:0x%x\n",part->start_lba+1);
119
120 /* 找出数据量最大的元信息,用其尺寸做存储缓冲区 */
121 uint32_t buf_size=(sb.block_bitmap_sects>=sb.inode_bitmap_sects?sb.block_bitmap_sects:sb.inode_bitmap_sects);
122 buf_size=(buf_size>=sb.inode_table_sects?buf_size:sb.inode_table_sects)*SECTOR_SIZE;
123 uint8_t* buf=(uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回
124
125 /****************************
126 * 2.将块位图初始化并写入sb.block_bitmap_lba
127 * *************************/
128 /* 初始化块位图block_bitmap */
129 buf[0]|=0x01; // 第0个块预留给根目录,位图中先占位
130 uint32_t block_bitmap_last_byte=block_bitmap_bit_len/8;
131 uint8_t block_bitmap_last_bit=block_bitmap_bit_len%8;
132 uint32_t last_size=SECTOR_SIZE-(block_bitmap_last_byte%SECTOR_SIZE); // last_size是位图中的填充部分
133
134 /* 2.1先将位图最后一字节到其所在扇区结束全置为1,即超出实际块数部分直接置为已占用 */
135 memset(&buf[block_bitmap_last_byte],0xff,last_size);
136
137 /* 2.2再将上一步中覆盖的最后一字节内有效位重新置0 */
138 uint8_t bit_idx=0;
139 while (bit_idx<=block_bitmap_last_bit){
140 buf[block_bitmap_last_byte]&=~(1<<bit_idx++);
141 }
142 ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);
143
144 /***************************
145 * 3.将inode位图初始化并写入sb.inode_bitmap_lba
146 * ************************/
147 /* 先清空缓冲区 */
148 memset(buf,0,buf_size);
149 buf[0]|=0x1; // 第0个inode分给了根目录
150 /* 由于inode_table中共4096个inode,位图中inode_bitmap正好占用1扇区,
151 * 即inode_bitmap_sects等于1,所以位图中的位全都代表inode_table中的inode,
152 * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
153 * inode_bitmap所在的扇区中没有多余的无效位 */
154 ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects);
155
156 /*************************
157 * 4.将inode数组初始化并写入sb.inode_table_lba
158 * **********************/
159 /* 准备写inode_table中的第0项,即根目录所在的inode */
160 memset(buf,0,buf_size); // 先清空缓冲区buf
161 struct inode* i=(struct inode*)buf;
162 i->i_size=sb.dir_entry_size*2; // . & ..
163 i->i_no=0; // 根目录占inode数组中第0个inode
164 i->i_sectors[0]=sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
165 ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);
166
167 /*************************
168 * 5.将根目录初始化并写入sb.data_start_lba
169 * **********************/
170 /* 写入根目录的两个目录项 . & .. */
171 memset(buf,0,buf_size);
172 struct dir_entry* p_de=(struct dir_entry*)buf;
173
174 /* 初始化当前目录"." */
175 memcpy(p_de->filename,".",1);
176 p_de->i_no=0;
177 p_de->f_type=FT_DIRECTORY;
178 ++p_de;
179
180 /* 初始化当前目录父目录 ".." */
181 memcpy(p_de->filename,"..",2);
182 p_de->i_no=0; // 根目录的父目录是自己
183 p_de->f_type=FT_DIRECTORY;
184
185 /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
186 ide_write(hd,sb.data_start_lba,buf,1);
187
188 printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
189 printk("%s format done\n",part->name);
190 sys_free(buf);
191 }
192
193 /* 将最上层路径名称解析出来 */
194 static char* path_parse(char* pathname,char* name_store){
195 if (pathname[0]=='/'){ // 根目录不需要单独解析
196 /* 路径中出现1个或多个连续的字符 '/',将这些'/'跳过 */
197 while (*(++pathname)=='/');
198 }
199
200 /* 开始一般的路径解析 */
201 while (*pathname!='/' && *pathname!=0){
202 *name_store++=*pathname++;
203 }
204
205 if (pathname[0]==0){ // 路径字符串为空则返回NULL
206 return NULL;
207 }
208 return pathname;
209 }
210
211 /* 返回路径深度,比如/a/b/c,深度为3 */
212 int32_t path_depth_cnt(char* pathname){
213 ASSERT(pathname!=NULL);
214 char* p=pathname;
215 char name[MAX_FILE_NAME_LEN]; // 用于path_parse路径解析
216 uint32_t depth=0;
217
218 /* 解析路径,从中拆分出各级名称 */
219 p=path_parse(p,name);
220 while (name[0]){
221 ++depth;
222 memset(name,0,MAX_FILE_NAME_LEN);
223 if (p){ // 若p!=NULL
224 p=path_parse(p,name);
225 }
226 }
227 return depth;
228 }
229
230 /* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */
231 static int search_file(const char* pathname,struct path_search_record* searched_record){
232 /* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */
233 if (!strcmp(pathname,"/") || !strcmp(pathname,"/.") || !strcmp(pathname,"/..")){
234 searched_record->parent_dir=&root_dir;
235 searched_record->file_type=FT_DIRECTORY;
236 searched_record->searched_path[0]=0; // 搜索路径置空
237 return 0;
238 }
239
240 uint32_t path_len=strlen(pathname);
241 /* 保证pathname至少是这样的路径/x,且小于最大长度 */
242 ASSERT(pathname[0]=='/' && path_len>1 && path_len<MAX_PATH_LEN);
243 char* sub_path=(char*)pathname;
244 struct dir* parent_dir=&root_dir;
245 struct dir_entry dir_e;
246
247 /* 记录路径解析出来的各级名称,如路径"/a/b/c",
248 * 数组name每次的值是"a","b","c" */
249 char name[MAX_FILE_NAME_LEN]={0};
250
251 searched_record->parent_dir=parent_dir;
252 searched_record->file_type=FT_UNKNOWN;
253 uint32_t parent_inode_no=0; // 父目录的inode号
254
255 sub_path=path_parse(sub_path,name);
256 while (name[0]){ // 若第一个字符就是结束符,结束循环
257 /* 记录查找过的路径,但不能超过searched_path的长度512Byte */
258 ASSERT(strlen(searched_record->searched_path)<512);
259
260 /* 记录已存在的父目录 */
261 strcat(searched_record->searched_path,'/');
262 strcat(searched_record->searched_path,name);
263
264 /* 在所给的目录中查找文件 */
265 if (search_dir_entry(cur_part,parent_dir,name,&dir_e)){
266 memset(name,0,MAX_FILE_NAME_LEN);
267 /* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */
268 if (sub_path){
269 sub_path=path_parse(sub_path,name);
270 }
271
272 if (FT_DIRECTORY==dir_e.f_type){ // 如果被打开的是目录
273 parent_inode_no=parent_dir->inode->i_no;
274 dir_close(parent_dir);
275 parent_dir=dir_open(cur_part,dir_e.i_no); // 更新父目录
276 searched_record->parent_dir=parent_dir;
277 continue;
278 }else if (FT_REGULAR==dir_e.f_type){
279 searched_record->file_type=FT_REGULAR;
280 return dir_e.i_no;
281 }
282 }else { // 若找不到
283 /* 找不到目录项时,要留着parent_dir不要关闭,
284 * 若是创建新文件的话需要在parent_dir中创建 */
285 return -1;
286 }
287 }
288
289 /* 遍历了完整的路径并且查找的文件或目录只有同名目录存在 */
290 dir_close(searched_record->parent_dir);
291
292 /* 保存被查找目录的直接父目录 */
293 searched_record->parent_dir=dir_open(cur_part,parent_inode_no);
294 searched_record->file_type=FT_DIRECTORY;
295 return dir_e.i_no;
296 }
297
298 /* 打开或创建文件成功后,返回文件描述符,否则返回-1 */
299 int32_t sys_open(const char* pathname,uint8_t flags){
300 /* 对目录要用dir_open,这里只有open文件 */
301 if (pathname[strlen(pathname)-1]=='/'){
302 printk("can't open a directory %s\n",pathname);
303 return -1;
304 }
305 ASSERT(flags<=7);
306 int32_t fd=-1;
307
308 struct path_search_record searched_record;
309 memset(&searched_record,0,sizeof(struct path_search_record));
310
311 /* 记录目录深度,帮助判断中间某个目录不存在的情况 */
312 uint32_t pathname_depth=path_depth_cnt((char*)pathname);
313
314 /* 先检查文件是否存在 */
315 int inode_no=search_file(pathname,&searched_record);
316 bool found=inode_no!=-1?true:false;
317
318 if (searched_record.file_type==FT_DIRECTORY){
319 printk("can't open a directory with open(),use opendir() to instead\n");
320 dir_close(searched_record.parent_dir);
321 return -1;
322 }
323
324 uint32_t path_searched_depth=path_depth_cnt(searched_record.searched_path);
325
326 /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
327 if (pathname_depth!=path_searched_depth){ // 说明并没有访问到全部路径,某个中间目录是不存在的
328 printk("cannot access %s: Not a directory, subpath %s is't exist\n", \
329 pathname,searched_record.searched_path);
330 dir_close(searched_record.parent_dir);
331 return -1;
332 }
333
334 /* 若是最后一个路径上没找到,并且不是要创建文件,直接返回-1 */
335 if (!found && !(flags & O_CREAT)){
336 printk("in path %s, file %s is't exist\n", \
337 searched_record.searched_path,
338 (strrchr(searched_record.searched_path,'/')+1));
339 dir_close(searched_record.parent_dir);
340 return -1;
341 }else if (found && flags & O_CREAT){ // 若要创建的文件已存在
342 printk("%s has already exist!\n",pathname);
343 dir_close(searched_record.parent_dir);
344 return -1;
345 }
346
347 switch (flags & O_CREAT){
348 case O_CREAT:
349 printk("creating file\n");
350 fd=file_create(searched_record.parent_dir,(strrchr(pathname,'/')+1),flags);
351 dir_close(searched_record.parent_dir);
352 break;
353 default:
354 /* 其余情况为打开已存在文件
355 * O_REONLY,O_WRONLY,O_REWR */
356 fd=file_open(inode_no,flags);
357 }
358
359 /* 此fd是指任务pcb->fd_table数组中的元素下标,
360 * 并不是指全局file_table中的下标 */
361 return fd;
362 }
363
364 /* 将文件描述符转化为文件表的下标 */
365 static uint32_t fd_local2global(uint32_t local_fd){
366 struct task_struct* cur=running_thread();
367 int32_t global_fd=cur->fd_table[local_fd];
368 ASSERT(global_fd>=0 && global_fd<MAX_FILE_OPEN);
369 return (uint32_t)global_fd;
370 }
371
372 /* 关闭文件描述符fd指向的文件,成功返回0,否则-1 */
373 int32_t sys_close(int32_t fd){
374 int32_t ret=-1;
375 if (fd>2){
376 uint32_t _fd=fd_local2global(fd);
377 ret=file_close(&file_table[_fd]);
378 running_thread()->fd_table[fd]=-1; // 使该文件描述符可用
379 }
380 return ret;
381 }
382
383 /* 将buf中连续count个字节写入文件描述符fd,成功则返回写入的字节数,失败则返回-1 */
384 int32_t sys_write(int32_t fd,const void* buf,uint32_t count){
385 if (fd<0){
386 printk("sys_write: fd error\n");
387 return -1;
388 }
389 if (fd==stdout_no){
390 char tmp_buf[1024]={0};
391 memcpy(tmp_buf,buf,count);
392 console_put_str(tmp_buf);
393 return count;
394 }
395 uint32_t _fd=fd_local2global(fd);
396 struct file* wr_file=&file_table[_fd];
397 if (wr_file->fd_flag & O_WRONLY || wr_file->fd_flag & O_RDWR){
398 uint32_t bytes_written=file_write(wr_file,buf,count);
399 return bytes_written;
400 }else {
401 console_put_str("sys_write: not allowed to write file without flag O_RDWR or O_WRONLY\n");
402 return -1;
403 }
404 }
405
406 /* 从文件描述符fd指向的文件中读取count个字节到buf,若成功则返回读出的字节数,到文件尾则返回-1 */
407 int32_t sys_read(int32_t fd,void* buf,uint32_t count){
408 if (fd<0){
409 printk("sys_read: fd error\n");
410 return -1;
411 }
412 ASSERT(buf!=NULL);
413 uint32_t _fd=fd_local2global(fd);
414 return file_read(&file_table[_fd],buf,count);
415 }
416
417 /* 重置用于文件读写操作的偏移指针。成功时返回新的偏移量,出错时返回-1 */
418 int32_t sys_lseek(int32_t fd,int32_t offset,uint8_t whence){
419 if (fd<0){
420 printk("sys_lseek: fd error\n");
421 return -1;
422 }
423 ASSERT(whence>0 && whence<4);
424 uint32_t _fd=fd_local2global(fd);
425 struct file* pf=&file_table[_fd];
426 int32_t new_pos=0; // 新的偏移量必须位于文件大小之内
427 int32_t file_size=(int32_t)pf->fd_inode->i_size;
428 switch(whence){
429 /* SEEK_SET 新的读写位置是相对于文件开头再增加offset个位移量 */
430 case SEEK_SET:
431 new_pos=offset;
432 break;
433
434 /* SEEK_CUR 新的读写位置是相对于当前位置增加offset个位移量 */
435 case SEEK_CUR:
436 new_pos=(int32_t)pf->fd_pos+offset;
437 break;
438
439 /* SEEK_END 新的读写位置是相对于文件尺寸增加offset个位移量 */
440 case SEEK_END:
441 new_pos=file_size+offset;
442 }
443 if (new_pos<0 || new_pos>(file_size-1)){
444 return -1;
445 }
446 pf->fd_pos=new_pos;
447 return pf->fd_pos;
448 }
449
450 /* 删除文件(非目录),成果则返回0,失败返回-1 */
451 int32_t sys_unlink(const char* pathname){
452 ASSERT(strlen(pathname)<MAX_PATH_LEN);
453
454 /* 先检查待删除的文件是否存在 */
455 struct path_search_record searched_record;
456 memset(&searched_record,0,sizeof(struct path_search_record));
457 int inode_no=search_file(pathname,&searched_record);
458 ASSERT(inode_no!=0);
459 if (inode_no==-1){
460 printk("file %s not found!\n",pathname);
461 dir_close(searched_record.parent_dir);
462 return -1;
463 }
464 if (searched_record.file_type==FT_DIRECTORY){
465 printk("can't delete a directory with unlink(), use rmdir() to instead\n");
466 dir_close(searched_record.parent_dir);
467 return -1;
468 }
469
470 /* 先检查是否在已打开文件刘表(文件表)中 */
471 uint32_t file_idx=0;
472 while (file_idx<MAX_FILE_OPEN){
473 if (file_table[file_idx].fd_inode!=NULL && (uint32_t)inode_no==file_table[file_idx].fd_inode->i_no){
474 break;
475 }
476 ++file_idx;
477 }
478 if (file_idx<MAX_FILE_OPEN){
479 dir_close(searched_record.parent_dir);
480 printk("file %s is in use, not allow to delete!\n",pathname);
481 return -1;
482 }
483 ASSERT(file_idx==MAX_FILE_OPEN);
484
485 /* 为delete_dir_entry申请缓冲区 */
486 void* io_buf=sys_malloc(SECTOR_SIZE+SECTOR_SIZE);
487 if (io_buf==NULL){
488 dir_close(searched_record.parent_dir);
489 printk("sys_unlink: malloc for io_buf failed\n");
490 return -1;
491 }
492
493 struct dir* parent_dir=searched_record.parent_dir;
494 delete_dir_entry(cur_part,parent_dir,inode_no,io_buf);
495 inode_release(cur_part,inode_no);
496 sys_free(io_buf);
497 dir_close(searched_record.parent_dir);
498 return 0; // 成功删除
499 }
500
501 /* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
502 void filesys_init(){
503 uint8_t channel_no=0,dev_no=0,part_idx=0;
504
505 /* sb_buf用来存储从硬盘上读入的超级块 */
506 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
507
508 if (sb_buf==NULL){
509 PANIC("alloc memory failed!");
510 }
511 printk("searching filesystem......\n");
512
513 while (channel_no<channel_cnt){
514 dev_no=0;
515 while (dev_no<2){
516 if (dev_no==0){
517 ++dev_no;
518 continue;
519 }
520 struct disk* hd=&channels[channel_no].devices[dev_no];
521 struct partition* part=hd->prim_parts;
522 while (part_idx<12){ // 四个主分区+八个逻辑分区
523 if (part_idx==4){ // 逻辑分区
524 part=hd->logic_parts;
525 }
526
527 /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
528 * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
529 * 若partition未初始化,则partition中的成员仍为0.
530 * 下面处理存在的分区. */
531 if (part->sec_cnt!=0){ // 如果分区存在
532 memset(sb_buf,0,SECTOR_SIZE);
533
534 /* 读出分区超级块,根据魔数是否正确来判断是否存在fs */
535 ide_read(hd,part->start_lba+1,sb_buf,1);
536
537 /* 只支持自己的文件系统,若磁盘上已经有fs就不再初始化了 */
538 if (sb_buf->magic==0x19590318){
539 printk("%s has filesystem\n",part->name);
540 }else{
541 printk("formatting %s's partition %s.....\n",hd->name,part->name);
542 partition_format(part);
543 }
544 }
545 ++part_idx;
546 ++part; // 下一分区
547 }
548 ++dev_no; //下一磁盘
549 }
550 ++channel_no; // 下一通道
551 }
552 sys_free(sb_buf);
553
554 /* 确定默认操作的分区 */
555 char default_part[8]="sdb1";
556 /* 挂载分区 */
557 list_traversal(&partition_list,mount_partition,(int)default_part);
558
559 /* 将当前分区的根目录打开 */
560 open_root_dir(cur_part);
561
562 /* 初始化文件表 */
563 uint32_t fd_idx=0;
564 while (fd_idx<MAX_FILE_OPEN){
565 file_table[fd_idx++].fd_inode=NULL;
566 }
567 }
⑥fs/fs.h:

1 #ifndef __FS_FS_H
2 #define __FS_FS_H
3 #include "stdint.h"
4 #include "ide.h"
5
6 #define MAX_FILES_PER_PART 4096
7 #define BITS_PER_SECTOR 4096
8 #define SECTOR_SIZE 512
9 #define BLOCK_SIZE SECTOR_SIZE
10 #define MAX_PATH_LEN 512
11
12 /* 文件类型 */
13 enum file_types{
14 FT_UNKNOWN, // 不支持的文件类型
15 FT_REGULAR, // 普通文件
16 FT_DIRECTORY // 目录
17 };
18
19 /* 打开文件的选项 */
20 enum oflags{
21 O_RDONLY, // 只读
22 O_WRONLY, // 只写
23 O_RDWR, // 读写
24 O_CREAT=4 // 新建
25 };
26
27 /* 文件读写的偏移量 */
28 enum whence{
29 SEEK_SET=1,
30 SEEK_CUR,
31 SEEK_END
32 };
33
34 /* 用来记录查找文件过程中已找到的上级路径,也就是查找文件过程中“走过的地方” */
35 struct path_search_record{
36 char searched_path[MAX_PATH_LEN]; // 查找过程中的父路径
37 struct dir* parent_dir; // 文件或目录所在的直接父目录
38 enum file_types file_type; // 找到的文件的类型
39 };
40
41 extern struct partition* cur_part;
42 void filesys_init(void);
43 int32_t path_depth_cnt(char* pathname);
44 int32_t sys_open(const char* pathname,uint8_t flags);
45 int32_t sys_close(int32_t fd);
46 int32_t sys_write(int32_t fd,const void* buf,uint32_t count);
47 int32_t sys_read(int32_t fd,void* buf,uint32_t count);
48 int32_t sys_lseek(int32_t fd,int32_t offset,uint8_t whence);
49 int32_t sys_unlink(const char* pathname);
50 #endif
⑦kernel/main.c:

1 #include "print.h"
2 #include "init.h"
3 #include "thread.h"
4 #include "interrupt.h"
5 #include "console.h"
6 #include "ioqueue.h"
7 #include "process.h"
8 #include "syscall.h"
9 #include "syscall-init.h"
10 #include "stdio.h"
11 #include "memory.h"
12 #include "fs.h"
13 #include "string.h"
14
15 void k_thread_a(void*);
16 void k_thread_b(void*);
17 void u_prog_a(void);
18 void u_prog_b(void);
19
20 int main(void){
21 put_str("Welcome,\nI am kernel!\n");
22 init_all();
23
24 intr_enable();
25 process_execute(u_prog_a,"u_prog_a");
26 process_execute(u_prog_b,"u_prog_b");
27 thread_start("k_thread_a",31,k_thread_a,"I'm thread_a ");
28 thread_start("k_thread_b",31,k_thread_b,"I'm thread_b ");
29
30 printf("/file1 delete %s!\n",sys_unlink("/file1")==0?"done":"fail");
31 while(1);
32 return 0;
33 }
34
35 /* 在线程中运行的函数 */
36 void k_thread_a(void* arg){
37 void* addr1=sys_malloc(256);
38 void* addr2=sys_malloc(255);
39 void* addr3=sys_malloc(254);
40 console_put_str(" thread_a malloc addr:0x");
41 console_put_int((int)addr1);
42 console_put_char(',');
43 console_put_int((int)addr2);
44 console_put_char(',');
45 console_put_int((int)addr3);
46 console_put_char('\n');
47
48 int cpu_delay=100000;
49 while(cpu_delay-->0);
50 sys_free(addr1);
51 sys_free(addr2);
52 sys_free(addr3);
53 while(1);
54 }
55
56 /* 在线程中运行的函数 */
57 void k_thread_b(void* arg) {
58 void* addr1=sys_malloc(256);
59 void* addr2=sys_malloc(255);
60 void* addr3=sys_malloc(254);
61 console_put_str(" thread_b malloc addr:0x");
62 console_put_int((int)addr1);
63 console_put_char(',');
64 console_put_int((int)addr2);
65 console_put_char(',');
66 console_put_int((int)addr3);
67 console_put_char('\n');
68
69 int cpu_delay=100000;
70 while(cpu_delay-->0);
71 sys_free(addr1);
72 sys_free(addr2);
73 sys_free(addr3);
74 while(1);
75 }
76
77 /* 测试用户进程 */
78 void u_prog_a(void){
79 void* addr1=malloc(256);
80 void* addr2=malloc(255);
81 void* addr3=malloc(254);
82 printf(" prog_a malloc addr:0x%x,0x%x,0x%x\n",(int)addr1,(int)addr2,(int)addr3);
83
84 int cpu_delay=100000;
85 while(cpu_delay-->0);
86 free(addr1);
87 free(addr2);
88 free(addr3);
89 while(1);
90 }
91
92 /* 测试用户进程 */
93 void u_prog_b(void){
94 void* addr1=malloc(256);
95 void* addr2=malloc(255);
96 void* addr3=malloc(254);
97 printf(" prog_b malloc addr:0x%x,0x%x,0x%x\n",(int)addr1,(int)addr2,(int)addr3);
98 int cpu_delay=100000;
99 while(cpu_delay-->0);
100 free(addr1);
101 free(addr2);
102 free(addr3);
103 while(1);
104 }
运行结果如下:
可以看到,在第一次运行时,会显示“/file1 delete done!”;而第二次运行时,就会显示“/file1 delete fail!”。
如果不放心的话,可以再用xxd.sh看看:
看吧,原先的file1已经没了。
块位图和inode位图也都由3变成了1。
嗯哼,文件操作就此结束。下一篇开启目录操作。
参考博客:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库