《操作系统真象还原》第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
View Code
复制代码

②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 }
View Code
复制代码

③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
View Code
复制代码

④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 }
View Code
复制代码

⑤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
View Code
复制代码

⑥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 }
View Code
复制代码

⑦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
View Code
复制代码

⑧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 }
View Code
复制代码

⑨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
View Code
复制代码

奇怪的是,我发现当我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 }
View Code
复制代码

②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 }
View Code
复制代码

④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 }
View Code
复制代码

②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 }
View Code
复制代码

④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 }
View Code
复制代码

⑧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 }
View Code
复制代码

程序运行结果:

成功打开和关闭,并且写入的扇区是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 }
View Code
复制代码

②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 }
View Code
复制代码

④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 }
View Code
复制代码

运行结果如图:

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 }
View Code
复制代码

这个功能还是很简单的,看下运行结果:

和第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 }
View Code
复制代码

②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
View Code
复制代码

③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 }
View Code
复制代码

④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
View Code
复制代码

⑤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 }
View Code
复制代码

⑥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
View Code
复制代码

⑦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 }
View Code
复制代码

运行结果如下:

可以看到,在第一次运行时,会显示“/file1 delete done!”;而第二次运行时,就会显示“/file1 delete fail!”。

如果不放心的话,可以再用xxd.sh看看:

看吧,原先的file1已经没了。

块位图和inode位图也都由3变成了1。

嗯哼,文件操作就此结束。下一篇开启目录操作。


参考博客:

posted @   Hell0er  阅读(91)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示