基于C语言的简单文件系统的实现

BoyMeetsGirl

发布日期: 2018-11-13 08:39:25 浏览量: 6028
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1 题目介绍

通过具体的文件存储空间的管理、文件物理结构、目录结构和文件操作的实现,加深对文件系统内部的数据结构、功能以及实现过程的理解。

1.1 要求

  • 在内存中开辟一个虚拟磁盘空间作为文件存储分区,在其上实现一个简单的基于多级目录的单用户单任务系统中的文件系统。在推出该文件系统的使用时,应将虚拟磁盘上的内容以一个文件的方式保存到磁盘上,一遍下次可以将它恢复到内存的虚拟磁盘中

  • 文件物理结构可采用显式链接或其他结构

  • 空闲磁盘空间的管理可选择FAT表、位示图或其他办法

  • 文件目录结构采用多级目录结构。为简单起见,可以不使用索引结点,每个目录项应包含文件名、物理地址、长度等信息,还可以通过目录项实现对文件的读和写的保护

  • 需要提供一以下操作命令

    1. my_format
    2. my_mkdir
    3. my_rmdir
    4. my_ls
    5. my_cd
    6. my_create
    7. my_open
    8. my_close
    9. my_write
    10. my_read
    11. my_rm
    12. my_exitsys

2 实验思路

2.1 程序设计思想

  • 在该虚拟文件系统启动时,申请一块内存作为磁盘空间

  • 将这块内存空间进行格式化,本系统仿照的是FAT16文件系统,其结构如下

    | 1块 | 2块 | 2块 | 995块 |
    | :—: | :—: | :—: | :—: |
    | 引导块 | FAT1 | FAT2 | 数据区 |

  • 格式化时,主要包括引导块,FAT1,FAT2,的一些初始化工作。例如设置文件魔数,文件系统的信息,FAT1,FAT2的信息等等

  • 根据用户输入的命令,调用对应的my_函数

程序流程图如下:

2.2 关键数据结构设计

2.2.1 文件控制块

  1. typedef struct FCB
  2. {
  3. char filename[8]; // 文件名
  4. char exname[3]; // 文件扩展名
  5. unsigned char attribute; // 文件属性字段(目录or文件)
  6. unsigned short time; // 创建时间
  7. unsigned short date; // 创建日期
  8. unsigned short first; // 起始盘块号
  9. unsigned short length; // 文件长度
  10. char free; // 表示目录项是否为空
  11. } fcb;

2.2.2 文件分配表

  1. typedef struct FAT
  2. {
  3. unsigned short id;
  4. } fat;

2.2.3 用户打开文件表

当打开一个文件时,必须将文件的目录项中的所有内容全部复制到内存中,同时还要记录有关文件操作的动态信息,如读写指针的值等。在本实例中实现的是一个用于单用户单任务系统的文件系统,为简单起见,我们把用户文件描述符表和内存FCB表合在一起,称为用户打开文件表,表项数目为10,即一个用户最多可同时打开10个文件。然后用一个数组来描述,则数组下标即某个打开文件的描述符。另外,我们在用户打开文件表中还设置了一个字段“char dir[80]”,用来记录每个打开文件所在的目录名,以方便用户打开不同目录下具有相同文件名的不同文件。

  1. typedef struct USEROPEN
  2. {
  3. char filename[8];
  4. char exname[3];
  5. unsigned char attribute;
  6. unsigned short time;
  7. unsigned short date;
  8. unsigned short first;
  9. unsigned short length;
  10. char free;
  11. int dirno; // 父目录文件起始盘块号
  12. int diroff; // 该文件对应的 fcb 在父目录中的逻辑序号
  13. char dir[MAXOPENFILE][80]; // 全路径信息
  14. int count;
  15. char fcbstate; // 是否修改 1是 0否
  16. char topenfile; // 0: 空 openfile
  17. } useropen;

2.2.4 引导块BLOCK0

在引导块中主要存放逻辑磁盘的相关描述信息,比如磁盘块大小、磁盘块数量、文件分配表、根目录区、数据区在磁盘上的起始位置等。如果是引导盘,还要存放操作系统的引导信息。本实例是在内存的虚拟磁盘中创建一个文件系统,因此所包含的内容比较少,只有磁盘块大小、磁盘块数量、数据区开始位置、根目录文件开始位置等。

  1. typedef struct BLOCK {
  2. char magic_number[8];
  3. char information[200];
  4. unsigned short root;
  5. unsigned char* startblock;
  6. } block0;

2.3 全局变量定义

指向虚拟磁盘的起始地址

  1. unsigned char* myvhard;

用户打开文件表数组

  1. useropen openfilelist[MAXOPENFILE];

记录虚拟磁盘上的数据区开始位置

  1. unsigned char* startp;

2.4 虚拟磁盘空间布局

由于真正的磁盘操作需要涉及到设备的驱动程序,所以本实例是在内存中申请一块空间作为虚拟磁盘使用,我们的文件系统就建立在这个虚拟磁盘上。虚拟磁盘一共划分成1000个磁盘块,每个块1024个字节,其布局格式是模仿FAT文件系统设计的,其中引导块占一个盘块,两张FAT各占2个盘块,剩下的空间全部是数据区,在对虚拟磁盘进行格式化的时候,将把数据区第1块(即虚拟磁盘的第6块)分配给根目录文件,如下图所示:

3 核心代码与实验结果及实验结果分析

3.1 核心代码与实验结果

格式化磁盘

  1. void my_format()
  2. {
  3. /**
  4. * 初始化前五个磁盘块
  5. * 设定第六个磁盘块为根目录磁盘块
  6. * 初始化 root 目录: 创建 . 和 .. 目录
  7. * 写入 FILENAME 文件 (写入磁盘空间)
  8. */
  9. block0* boot = (block0*)myvhard;
  10. strcpy(boot->magic_number, "10101010");
  11. strcpy(boot->information, "fat file system");
  12. boot->root = 5;
  13. boot->startblock = myvhard + BLOCKSIZE * 5;
  14. fat* fat1 = (fat*)(myvhard + BLOCKSIZE);
  15. fat* fat2 = (fat*)(myvhard + BLOCKSIZE * 3);
  16. int i;
  17. for (i = 0; i < 6; i++) {
  18. fat1[i].id = END;
  19. fat2[i].id = END;
  20. }
  21. for (i = 6; i < 1000; i++) {
  22. fat1[i].id = FREE;
  23. fat2[i].id = FREE;
  24. }
  25. // 5th block is root
  26. fcb* root = (fcb*)(myvhard + BLOCKSIZE * 5);
  27. strcpy(root->filename, ".");
  28. strcpy(root->exname, "di");
  29. root->attribute = 0; // dir file
  30. time_t rawTime = time(NULL);
  31. struct tm* time = localtime(&rawTime);
  32. // 5 6 5 bits
  33. root->time = time->tm_hour * 2048 + time->tm_min * 32 + time->tm_sec / 2;
  34. // 7 4 5 bits; year from 2000
  35. root->date = (time->tm_year - 100) * 512 + (time->tm_mon + 1) * 32 + (time->tm_mday);
  36. root->first = 5;
  37. root->free = 1;
  38. root->length = 2 * sizeof(fcb);
  39. fcb* root2 = root + 1;
  40. memcpy(root2, root, sizeof(fcb));
  41. strcpy(root2->filename, "..");
  42. for (i = 2; i < (int)(BLOCKSIZE / sizeof(fcb)); i++) {
  43. root2++;
  44. strcpy(root2->filename, "");
  45. root2->free = 0;
  46. }
  47. FILE* fp = fopen(FILENAME, "w");
  48. fwrite(myvhard, SIZE, 1, fp);
  49. fclose(fp);
  50. }

创建目录

  • 调用do_read读入当前目录文件到内存,检查新建文件目录是否重名
  • 分配一个空闲的打开文件表项
  • 分配一个空闲的盘块
  • 在当前目录中问新建目录寻找一个空闲的目录项
  • 设置FCB,文件的属性信息
  • 创建特殊的两个目录项‘.’,‘..’
  • 返回
  1. void my_mkdir(char* dirname)
  2. {
  3. /**
  4. * 当前目录:当前打开目录项表示的目录
  5. * 该目录:以下指创建的目录
  6. * 父目录:指该目录的父目录
  7. * 如:
  8. * 我现在在 root 目录下, 输入命令 mkdir a/b/bb
  9. * 表示 在 root 目录下的 a 目录下的 b 目录中创建 bb 目录
  10. * 这时,父目录指 b,该目录指 bb,当前目录指 root
  11. * 以下都用这个表达,简单情况下,当前目录和父目录是一个目录
  12. * 来不及了,先讨论简单情况,即 mkdir bb
  13. */
  14. int i = 0;
  15. char text[MAX_TEXT_SIZE];
  16. char* fname = strtok(dirname, ".");
  17. char* exname = strtok(NULL, ".");
  18. if (exname != NULL) {
  19. printf("you can not use extension\n");
  20. return;
  21. }
  22. // 读取父目录信息
  23. openfilelist[currfd].count = 0;
  24. int filelen = do_read(currfd, openfilelist[currfd].length, text);
  25. fcb* fcbptr = (fcb*)text;
  26. // 查找是否重名
  27. for (i = 0; i < (int)(filelen / sizeof(fcb)); i++) {
  28. if (strcmp(dirname, fcbptr[i].filename) == 0 && fcbptr->attribute == 0) {
  29. printf("dir has existed\n");
  30. return;
  31. }
  32. }
  33. // 申请一个打开目录表项
  34. int fd = get_free_openfilelist();
  35. if (fd == -1) {
  36. printf("openfilelist is full\n");
  37. return;
  38. }
  39. // 申请一个磁盘块
  40. unsigned short int block_num = get_free_block();
  41. if (block_num == END) {
  42. printf("blocks are full\n");
  43. openfilelist[fd].topenfile = 0;
  44. return;
  45. }
  46. // 更新 fat 表
  47. fat* fat1 = (fat*)(myvhard + BLOCKSIZE);
  48. fat* fat2 = (fat*)(myvhard + BLOCKSIZE * 3);
  49. fat1[block_num].id = END;
  50. fat2[block_num].id = END;
  51. // 在父目录中找一个空的 fcb,分配给该目录 ??未考虑父目录满的情况??
  52. for (i = 0; i < (int)(filelen / sizeof(fcb)); i++) {
  53. if (fcbptr[i].free == 0) {
  54. break;
  55. }
  56. }
  57. openfilelist[currfd].count = i * sizeof(fcb);
  58. openfilelist[currfd].fcbstate = 1;
  59. // 初始化该 fcb
  60. fcb* fcbtmp = (fcb*)malloc(sizeof(fcb));
  61. fcbtmp->attribute = 0;
  62. time_t rawtime = time(NULL);
  63. struct tm* time = localtime(&rawtime);
  64. fcbtmp->date = (time->tm_year - 100) * 512 + (time->tm_mon + 1) * 32 + (time->tm_mday);
  65. fcbtmp->time = (time->tm_hour) * 2048 + (time->tm_min) * 32 + (time->tm_sec) / 2;
  66. strcpy(fcbtmp->filename, dirname);
  67. strcpy(fcbtmp->exname, "di");
  68. fcbtmp->first = block_num;
  69. fcbtmp->length = 2 * sizeof(fcb);
  70. fcbtmp->free = 1;
  71. do_write(currfd, (char*)fcbtmp, sizeof(fcb), 2);
  72. // 设置打开文件表项
  73. openfilelist[fd].attribute = 0;
  74. openfilelist[fd].count = 0;
  75. openfilelist[fd].date = fcbtmp->date;
  76. openfilelist[fd].time = fcbtmp->time;
  77. openfilelist[fd].dirno = openfilelist[currfd].first;
  78. openfilelist[fd].diroff = i;
  79. strcpy(openfilelist[fd].exname, "di");
  80. strcpy(openfilelist[fd].filename, dirname);
  81. openfilelist[fd].fcbstate = 0;
  82. openfilelist[fd].first = fcbtmp->first;
  83. openfilelist[fd].free = fcbtmp->free;
  84. openfilelist[fd].length = fcbtmp->length;
  85. openfilelist[fd].topenfile = 1;
  86. strcat(strcat(strcpy(openfilelist[fd].dir, (char*)(openfilelist[currfd].dir)), dirname), "/");
  87. // 设置 . 和 .. 目录
  88. fcbtmp->attribute = 0;
  89. fcbtmp->date = fcbtmp->date;
  90. fcbtmp->time = fcbtmp->time;
  91. strcpy(fcbtmp->filename, ".");
  92. strcpy(fcbtmp->exname, "di");
  93. fcbtmp->first = block_num;
  94. fcbtmp->length = 2 * sizeof(fcb);
  95. do_write(fd, (char*)fcbtmp, sizeof(fcb), 2);
  96. fcb* fcbtmp2 = (fcb*)malloc(sizeof(fcb));
  97. memcpy(fcbtmp2, fcbtmp, sizeof(fcb));
  98. strcpy(fcbtmp2->filename, "..");
  99. fcbtmp2->first = openfilelist[currfd].first;
  100. fcbtmp2->length = openfilelist[currfd].length;
  101. fcbtmp2->date = openfilelist[currfd].date;
  102. fcbtmp2->time = openfilelist[currfd].time;
  103. do_write(fd, (char*)fcbtmp2, sizeof(fcb), 2);
  104. // 关闭该目录的打开文件表项,close 会修改父目录中对应该目录的 fcb 信息
  105. /**
  106. * 这里注意,一个目录存在 2 个 fcb 信息,一个为该目录下的 . 目录文件,一个为父目录下的 fcb。
  107. * 因此,这俩个fcb均需要修改,前一个 fcb 由各个函数自己完成,后一个 fcb 修改由 close 完成。
  108. * 所以这里,需要打开文件表,再关闭文件表,实际上更新了后一个 fcb 信息。
  109. */
  110. my_close(fd);
  111. free(fcbtmp);
  112. free(fcbtmp2);
  113. // 修改父目录 fcb
  114. fcbptr = (fcb*)text;
  115. fcbptr->length = openfilelist[currfd].length;
  116. openfilelist[currfd].count = 0;
  117. do_write(currfd, (char*)fcbptr, sizeof(fcb), 2);
  118. openfilelist[currfd].fcbstate = 1;
  119. }

进入目录

  • Open指定的目录名,调用read读入该父目录到内存
  • 检查新的当前目录名是否存在
  • 关闭原当前目录
  • 设置当前目录为该目录
  1. void my_cd(char* dirname)
  2. {
  3. int i = 0;
  4. int tag = -1;
  5. int fd;
  6. if (openfilelist[currfd].attribute == 1) {
  7. // if not a dir
  8. printf("you are in a data file, you could use 'close' to exit this file\n");
  9. return;
  10. }
  11. char* buf = (char*)malloc(10000);
  12. openfilelist[currfd].count = 0;
  13. do_read(currfd, openfilelist[currfd].length, buf);
  14. fcb* fcbptr = (fcb*)buf;
  15. // 查找目标 fcb
  16. for (i = 0; i < (int)(openfilelist[currfd].length / sizeof(fcb)); i++, fcbptr++) {
  17. if (strcmp(fcbptr->filename, dirname) == 0 && fcbptr->attribute == 0) {
  18. tag = 1;
  19. break;
  20. }
  21. }
  22. if (tag != 1) {
  23. printf("my_cd: no such dir\n");
  24. return;
  25. } else {
  26. // . 和 .. 检查
  27. if (strcmp(fcbptr->filename, ".") == 0) {
  28. return;
  29. } else if (strcmp(fcbptr->filename, "..") == 0) {
  30. if (currfd == 0) {
  31. // root
  32. return;
  33. } else {
  34. currfd = my_close(currfd);
  35. return;
  36. }
  37. } else {
  38. // 其他目录
  39. fd = get_free_openfilelist();
  40. if (fd == -1) {
  41. return;
  42. }
  43. openfilelist[fd].attribute = fcbptr->attribute;
  44. openfilelist[fd].count = 0;
  45. openfilelist[fd].date = fcbptr->date;
  46. openfilelist[fd].time = fcbptr->time;
  47. strcpy(openfilelist[fd].filename, fcbptr->filename);
  48. strcpy(openfilelist[fd].exname, fcbptr->exname);
  49. openfilelist[fd].first = fcbptr->first;
  50. openfilelist[fd].free = fcbptr->free;
  51. openfilelist[fd].fcbstate = 0;
  52. openfilelist[fd].length = fcbptr->length;
  53. strcat(strcat(strcpy(openfilelist[fd].dir, (char*)(openfilelist[currfd].dir)), dirname), "/");
  54. openfilelist[fd].topenfile = 1;
  55. openfilelist[fd].dirno = openfilelist[currfd].first;
  56. openfilelist[fd].diroff = i;
  57. currfd = fd;
  58. }
  59. }
  60. }

删除目录

  • Read读入当前目录文件内容到内存,检查要删除的文件目录是否存在
  • 检查该目录是否为空
  • 检查是否已经打开,打开用close则关闭
  • 回收给目录文件的磁盘块
  • 修改该目录文件的目录项
  • 修改用户打开表项的长度信息
  • 返回
  1. void my_rmdir(char* dirname)
  2. {
  3. int i, tag = 0;
  4. char buf[MAX_TEXT_SIZE];
  5. // 排除 . 和 .. 目录
  6. if (strcmp(dirname, ".") == 0 || strcmp(dirname, "..") == 0) {
  7. printf("can not remove . and .. special dir\n");
  8. return;
  9. }
  10. openfilelist[currfd].count = 0;
  11. do_read(currfd, openfilelist[currfd].length, buf);
  12. // 查找要删除的目录
  13. fcb* fcbptr = (fcb*)buf;
  14. for (i = 0; i < (int)(openfilelist[currfd].length / sizeof(fcb)); i++, fcbptr++) {
  15. if (fcbptr->free == 0)
  16. continue;
  17. if (strcmp(fcbptr->filename, dirname) == 0 && fcbptr->attribute == 0) {
  18. tag = 1;
  19. break;
  20. }
  21. }
  22. if (tag != 1) {
  23. printf("no such dir\n");
  24. return;
  25. }
  26. // 无法删除非空目录
  27. if (fcbptr->length > 2 * sizeof(fcb)) {
  28. printf("can not remove a non empty dir\n");
  29. return;
  30. }
  31. // 更新 fat 表
  32. int block_num = fcbptr->first;
  33. fat* fat1 = (fat*)(myvhard + BLOCKSIZE);
  34. int nxt_num = 0;
  35. while (1) {
  36. nxt_num = fat1[block_num].id;
  37. fat1[block_num].id = FREE;
  38. if (nxt_num != END) {
  39. block_num = nxt_num;
  40. } else {
  41. break;
  42. }
  43. }
  44. fat1 = (fat*)(myvhard + BLOCKSIZE);
  45. fat* fat2 = (fat*)(myvhard + BLOCKSIZE * 3);
  46. memcpy(fat2, fat1, BLOCKSIZE * 2);
  47. // 更新 fcb
  48. fcbptr->date = 0;
  49. fcbptr->time = 0;
  50. fcbptr->exname[0] = '\0';
  51. fcbptr->filename[0] = '\0';
  52. fcbptr->first = 0;
  53. fcbptr->free = 0;
  54. fcbptr->length = 0;
  55. openfilelist[currfd].count = i * sizeof(fcb);
  56. do_write(currfd, (char*)fcbptr, sizeof(fcb), 2);
  57. // 删除目录需要相应考虑可能删除 fcb,也就是修改父目录 length
  58. // 这里需要注意:因为删除中间的 fcb,目录有效长度不变,即 length 不变
  59. // 因此需要考虑特殊情况,即删除最后一个 fcb 时,极有可能之前的 fcb 都是空的,这是需要
  60. // 循环删除 fcb (以下代码完成),可能需要回收 block 修改 fat 表等过程(do_write 完成)
  61. int lognum = i;
  62. if ((lognum + 1) * sizeof(fcb) == openfilelist[currfd].length) {
  63. openfilelist[currfd].length -= sizeof(fcb);
  64. lognum--;
  65. fcbptr = (fcb *)buf + lognum;
  66. while (fcbptr->free == 0) {
  67. fcbptr--;
  68. openfilelist[currfd].length -= sizeof(fcb);
  69. }
  70. }
  71. // 更新父目录 fcb
  72. fcbptr = (fcb*)buf;
  73. fcbptr->length = openfilelist[currfd].length;
  74. openfilelist[currfd].count = 0;
  75. do_write(currfd, (char*)fcbptr, sizeof(fcb), 2);
  76. openfilelist[currfd].fcbstate = 1;
  77. }

列出目录和文件项命令

  • Read当前目录到内存
  • 读出目录文件的信息,显示到屏幕上
  • 返回
  1. void my_ls()
  2. {
  3. // 判断是否是目录
  4. if (openfilelist[currfd].attribute == 1) {
  5. printf("data file\n");
  6. return;
  7. }
  8. char buf[MAX_TEXT_SIZE];
  9. int i;
  10. // 读取当前目录文件信息(一个个fcb), 载入内存
  11. openfilelist[currfd].count = 0;
  12. do_read(currfd, openfilelist[currfd].length, buf);
  13. // 遍历当前目录 fcb
  14. fcb* fcbptr = (fcb*)buf;
  15. for (i = 0; i < (int)(openfilelist[currfd].length / sizeof(fcb)); i++, fcbptr++) {
  16. if (fcbptr->free == 1) {
  17. if (fcbptr->attribute == 0) {
  18. printf("<DIR> %-8s\t%d/%d/%d %d:%d\n",
  19. fcbptr->filename,
  20. (fcbptr->date >> 9) + 2000,
  21. (fcbptr->date >> 5) & 0x000f,
  22. (fcbptr->date) & 0x001f,
  23. (fcbptr->time >> 11),
  24. (fcbptr->time >> 5) & 0x003f);
  25. } else {
  26. printf("<---> %-8s\t%d/%d/%d %d:%d\t%d\n",
  27. fcbptr->filename,
  28. (fcbptr->date >> 9) + 2000,
  29. (fcbptr->date >> 5) & 0x000f,
  30. (fcbptr->date) & 0x001f,
  31. (fcbptr->time >> 11),
  32. (fcbptr->time >> 5) & 0x003f,
  33. fcbptr->length);
  34. }
  35. }
  36. }
  37. }

新建文件命令

  • 分配一个空闲的打开文件表项
  • 检查新文件的父目录是否打开
  • Read该父目录的文件到内存,并检测新建的文件名是否重名
  • 检查是否有空闲盘块
  • 寻找空闲的目录项
  • 准备好新文件的FCB
  • 调用close关闭打开的父目录文件
  • 返回
  1. int my_create(char* filename)
  2. {
  3. // 非法判断
  4. if (strcmp(filename, "") == 0) {
  5. printf("please input filename\n");
  6. return -1;
  7. }
  8. if (openfilelist[currfd].attribute == 1) {
  9. printf("you are in data file now\n");
  10. return -1;
  11. }
  12. openfilelist[currfd].count = 0;
  13. char buf[MAX_TEXT_SIZE];
  14. do_read(currfd, openfilelist[currfd].length, buf);
  15. int i;
  16. fcb* fcbptr = (fcb*)buf;
  17. // 检查重名
  18. for (i = 0; i < (int)(openfilelist[currfd].length / sizeof(fcb)); i++, fcbptr++) {
  19. if (fcbptr->free == 0) {
  20. continue;
  21. }
  22. if (strcmp(fcbptr->filename, filename) == 0 && fcbptr->attribute == 1) {
  23. printf("the same filename error\n");
  24. return -1;
  25. }
  26. }
  27. // 申请空 fcb;
  28. fcbptr = (fcb*)buf;
  29. for (i = 0; i < (int)(openfilelist[currfd].length / sizeof(fcb)); i++, fcbptr++) {
  30. if (fcbptr->free == 0)
  31. break;
  32. }
  33. // 申请磁盘块并更新 fat 表
  34. int block_num = get_free_block();
  35. if (block_num == -1) {
  36. return -1;
  37. }
  38. fat* fat1 = (fat*)(myvhard + BLOCKSIZE);
  39. fat* fat2 = (fat*)(myvhard + BLOCKSIZE * 3);
  40. fat1[block_num].id = END;
  41. memcpy(fat2, fat1, BLOCKSIZE * 2);
  42. // 修改 fcb 信息
  43. strcpy(fcbptr->filename, filename);
  44. time_t rawtime = time(NULL);
  45. struct tm* time = localtime(&rawtime);
  46. fcbptr->date = (time->tm_year - 100) * 512 + (time->tm_mon + 1) * 32 + (time->tm_mday);
  47. fcbptr->time = (time->tm_hour) * 2048 + (time->tm_min) * 32 + (time->tm_sec) / 2;
  48. fcbptr->first = block_num;
  49. fcbptr->free = 1;
  50. fcbptr->attribute = 1;
  51. fcbptr->length = 0;
  52. openfilelist[currfd].count = i * sizeof(fcb);
  53. do_write(currfd, (char*)fcbptr, sizeof(fcb), 2);
  54. // 修改父目录 fcb
  55. fcbptr = (fcb*)buf;
  56. fcbptr->length = openfilelist[currfd].length;
  57. openfilelist[currfd].count = 0;
  58. do_write(currfd, (char*)fcbptr, sizeof(fcb), 2);
  59. openfilelist[currfd].fcbstate = 1;
  60. }

删除文件命令

  • 检查要删除的文件的父目录是否已打开
  • Read父目录到内存
  • 检查文件是否打开
  • 回收磁盘快
  • 清空该文件的目录项
  • 修改用户打开文件表项中的长度信息
  • 返回
  1. void my_rm(char* filename)
  2. {
  3. char buf[MAX_TEXT_SIZE];
  4. openfilelist[currfd].count = 0;
  5. do_read(currfd, openfilelist[currfd].length, buf);
  6. int i, flag = 0;
  7. fcb* fcbptr = (fcb*)buf;
  8. // 查询
  9. for (i = 0; i < (int)(openfilelist[currfd].length / sizeof(fcb)); i++, fcbptr++) {
  10. if (strcmp(fcbptr->filename, filename) == 0 && fcbptr->attribute == 1) {
  11. flag = 1;
  12. break;
  13. }
  14. }
  15. if (flag != 1) {
  16. printf("no such file\n");
  17. return;
  18. }
  19. // 更新 fat 表
  20. int block_num = fcbptr->first;
  21. fat* fat1 = (fat*)(myvhard + BLOCKSIZE);
  22. int nxt_num = 0;
  23. while (1) {
  24. nxt_num = fat1[block_num].id;
  25. fat1[block_num].id = FREE;
  26. if (nxt_num != END)
  27. block_num = nxt_num;
  28. else
  29. break;
  30. }
  31. fat1 = (fat*)(myvhard + BLOCKSIZE);
  32. fat* fat2 = (fat*)(myvhard + BLOCKSIZE * 3);
  33. memcpy(fat2, fat1, BLOCKSIZE * 2);
  34. // 清空 fcb
  35. fcbptr->date = 0;
  36. fcbptr->time = 0;
  37. fcbptr->exname[0] = '\0';
  38. fcbptr->filename[0] = '\0';
  39. fcbptr->first = 0;
  40. fcbptr->free = 0;
  41. fcbptr->length = 0;
  42. openfilelist[currfd].count = i * sizeof(fcb);
  43. do_write(currfd, (char*)fcbptr, sizeof(fcb), 2);
  44. //
  45. int lognum = i;
  46. if ((lognum + 1) * sizeof(fcb) == openfilelist[currfd].length) {
  47. openfilelist[currfd].length -= sizeof(fcb);
  48. lognum--;
  49. fcbptr = (fcb *)buf + lognum;
  50. while (fcbptr->free == 0) {
  51. fcbptr--;
  52. openfilelist[currfd].length -= sizeof(fcb);
  53. }
  54. }
  55. // 修改父目录 . 目录文件的 fcb
  56. fcbptr = (fcb*)buf;
  57. fcbptr->length = openfilelist[currfd].length;
  58. openfilelist[currfd].count = 0;
  59. do_write(currfd, (char*)fcbptr, sizeof(fcb), 2);
  60. openfilelist[currfd].fcbstate = 1;
  61. }

打开文件命令

  • 检查该文件名是否存在
  • Read该父目录到内存
  • 检查用户打开的文件表中是否有空闲表项
  • 为该文件填写空白用户打开文件表项内容
  • 返回
  1. int my_open(char* filename)
  2. {
  3. char buf[MAX_TEXT_SIZE];
  4. openfilelist[currfd].count = 0;
  5. do_read(currfd, openfilelist[currfd].length, buf);
  6. int i, flag = 0;
  7. fcb* fcbptr = (fcb*)buf;
  8. // 重名检查
  9. for (i = 0; i < (int)(openfilelist[currfd].length / sizeof(fcb)); i++, fcbptr++) {
  10. if (strcmp(fcbptr->filename, filename) == 0 && fcbptr->attribute == 1) {
  11. flag = 1;
  12. break;
  13. }
  14. }
  15. if (flag != 1) {
  16. printf("no such file\n");
  17. return -1;
  18. }
  19. // 申请新的打开目录项并初始化该目录项
  20. int fd = get_free_openfilelist();
  21. if (fd == -1) {
  22. printf("my_open: full openfilelist\n");
  23. return -1;
  24. }
  25. openfilelist[fd].attribute = 1;
  26. openfilelist[fd].count = 0;
  27. openfilelist[fd].date = fcbptr->date;
  28. openfilelist[fd].time = fcbptr->time;
  29. openfilelist[fd].length = fcbptr->length;
  30. openfilelist[fd].first = fcbptr->first;
  31. openfilelist[fd].free = 1;
  32. strcpy(openfilelist[fd].filename, fcbptr->filename);
  33. strcat(strcpy(openfilelist[fd].dir, (char*)(openfilelist[currfd].dir)), filename);
  34. openfilelist[fd].dirno = openfilelist[currfd].first;
  35. openfilelist[fd].diroff = i;
  36. openfilelist[fd].topenfile = 1;
  37. openfilelist[fd].fcbstate = 0;
  38. currfd = fd;
  39. return 1;
  40. }

关闭文件命令

  • 检查fd的有效性
  • 检查用户打开文件表表项的fcbstate字段
  • 回收该文件占据的用户打开文件表表项
  • 返回
  1. int my_close(int fd)
  2. {
  3. if (fd > MAXOPENFILE || fd < 0) {
  4. printf("my_close: fd error\n");
  5. return -1;
  6. }
  7. int i;
  8. char buf[MAX_TEXT_SIZE];
  9. int father_fd = -1;
  10. fcb* fcbptr;
  11. for (i = 0; i < MAXOPENFILE; i++) {
  12. if (openfilelist[i].first == openfilelist[fd].dirno) {
  13. father_fd = i;
  14. break;
  15. }
  16. }
  17. if (father_fd == -1) {
  18. printf("my_close: no father dir\n");
  19. return -1;
  20. }
  21. if (openfilelist[fd].fcbstate == 1) {
  22. do_read(father_fd, openfilelist[father_fd].length, buf);
  23. // update fcb
  24. fcbptr = (fcb*)(buf + sizeof(fcb) * openfilelist[fd].diroff);
  25. strcpy(fcbptr->exname, openfilelist[fd].exname);
  26. strcpy(fcbptr->filename, openfilelist[fd].filename);
  27. fcbptr->first = openfilelist[fd].first;
  28. fcbptr->free = openfilelist[fd].free;
  29. fcbptr->length = openfilelist[fd].length;
  30. fcbptr->time = openfilelist[fd].time;
  31. fcbptr->date = openfilelist[fd].date;
  32. fcbptr->attribute = openfilelist[fd].attribute;
  33. openfilelist[father_fd].count = openfilelist[fd].diroff * sizeof(fcb);
  34. do_write(father_fd, (char*)fcbptr, sizeof(fcb), 2);
  35. }
  36. // 释放打开文件表
  37. memset(&openfilelist[fd], 0, sizeof(useropen));
  38. currfd = father_fd;
  39. return father_fd;
  40. }

写文件命令

  • 检查fd的有效性
  • 提示用户输入写方式
  • 提示用户输入内容
  • 调用do_write()将键入的内容写入到文件中
  • 返回写入的字节数
  1. int my_write(int fd)
  2. {
  3. if (fd < 0 || fd >= MAXOPENFILE) {
  4. printf("my_write: no such file\n");
  5. return -1;
  6. }
  7. int wstyle;
  8. while (1) {
  9. // 1: 截断写,清空全部内容,从头开始写
  10. // 2. 覆盖写,从文件指针处开始写
  11. // 3. 追加写,字面意思
  12. printf("1:Truncation 2:Coverage 3:Addition\n");
  13. scanf("%d", &wstyle);
  14. if (wstyle > 3 || wstyle < 1) {
  15. printf("input error\n");
  16. } else {
  17. break;
  18. }
  19. }
  20. char text[MAX_TEXT_SIZE] = "\0";
  21. char texttmp[MAX_TEXT_SIZE] = "\0";
  22. printf("please input data, line feed + $$ to end file\n");
  23. getchar();
  24. while (gets(texttmp)) {
  25. if (strcmp(texttmp, "$$") == 0) {
  26. break;
  27. }
  28. texttmp[strlen(texttmp)] = '\n';
  29. strcat(text, texttmp);
  30. }
  31. text[strlen(text)] = '\0';
  32. do_write(fd, text, strlen(text) + 1, wstyle);
  33. openfilelist[fd].fcbstate = 1;
  34. return 1;
  35. }

3.2 实验结果分析

创建虚拟磁盘并格式化

显示工作目录的所有文件和文件夹

创建文件夹

进入文件夹并创建文件

打开文件并写入文件并读取文件

关闭文件

退出虚拟文件系统

上传的附件 cloud_download 简单文件系统的实现.7z ( 234.43kb, 638次下载 )
error_outline 下载需要9点积分

keyboard_arrow_left上一篇 : 基于JAVA EE和豆瓣API实现的电影评论网站 基于ASP.NET和SQL Server 2012实现的OnlineMart电商网站 : 下一篇keyboard_arrow_right



BoyMeetsGirl
2018-11-13 08:39:14
操作系统大作业,使用C语言实现的一个文件系统
Obsession
2021-04-18 20:36:13
兄弟,你的代码有问题啊 能解答一下吗
Obsession
2021-04-18 20:36:57
兄弟,你的代码有问题啊 能解答一下吗

发送私信

抓不住的东西,伸手都显得多余

10
文章数
13
评论数
最近文章
eject