操作系统的课程实验,最后要求用 C 来实现一个简单的 FAT 文件系统,虽然原理不复杂但是代码量比较大。这里分享一下我的代码,希望能给你一点参考。

代码肯定还存在很多缺陷,希望各位大佬轻喷

头文件中定义了用到的数据结构。

#ifndef OS_FILESYSTEM_HEADER_H
#define OS_FILESYSTEM_HEADER_H

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <time.h>

/**
 * 宏定义
 */
#define  TRUE               1           //TRUE 定义
#define  CMD_LENGTH         30          //命令行缓冲区大小
#define  BLOCK_SIZE         1024        //磁盘块大小
#define  SIZE               1024000     //虚拟磁盘空间大小
#define  END                65535       //FAT中的文件结束标志
#define  FREE               0           //FAT中盘块空闲标志
#define  ROOT_BLOCK_NUM     2           //根目录区所占盘块总数
#define  MAX_OPEN_FILE      10          //最多同时打开文件个数

/**
 * 私有时间类型定义
 */
typedef struct TIME{
    int hour;
    int minute;
    int second;
} my_time;

/**
 * 私有日期类型定义
 */
typedef struct DATE{
    int year;
    int month;
    int day;
} my_date;

/**
 * 仿照 FAT16 设置的文件控制块结构
 */
typedef struct FCB {
    char filename[8];                   //文件名
    char extname[3];                    //文件仿照 FAT16 设置的
    unsigned char attribute;            //文件属性字段,为简单起见,我们只为文件设置了两种属性:值为 0 时表示目录文件,值为 1 时表示数据文件
    my_time time;                       //文件创建时间
    my_date date;                       //文件创建日期
    unsigned short first;               //文件起始盘块号
    unsigned long length;               //文件长度(字节数)
    char free;                          //表示目录项是否为空,若值为 0,表示空,值为 1,表示已分配
} fcb;

/**
 * 文件分配表结构
 */
typedef struct FAT {
    unsigned short id;
} fat;

/**
 * 用户打开文件表结构
 */
typedef struct USER_OPEN {
    char filename[8];                   //文件名
    char extname[3];                    //文件扩展名
    unsigned char attribute;            //文件属性:值为 0 时表示目录文件,值为 1 时表示数据文件
    my_time time;                       //文件创建时间
    my_date date;                       //文件创建日期
    unsigned short first;               //文件起始盘块号
    unsigned long length;               //文件长度(对数据文件是字节数,对目录文件可以是目录项个数)
    char free;                          //表示目录项是否为空,若值为 0,表示空,值为 1,表示已分配
    int dir_no;                         //相应打开文件的目录项在父目录文件中的盘块号
    int dir_off;                        //相应打开文件的目录项在父目录文件的 dir_no 盘块中的目录项序号
    char dir[MAX_OPEN_FILE][80];        //相应打开文件所在的目录名,这样方便快速检查出指定文件是否已经打开
    int count;                          //读写指针在文件中的位置
    char fcb_state;                     //是否修改了文件的 FCB 的内容,如果修改了置为 1,否则为 0
    char t_openfile;                    //表示该用户打开表项是否为空,若值为0,表示为空,否则表示已被某打开文件占据
} user_open;

/**
 * 引导块结构
 */
typedef struct BLOCK0 {
    char magic_number[8];               //文件系统的魔数
    char information[200];              //存储一些描述信息,如磁盘块大小、磁盘块数量、最多打开文件数等
    unsigned short root;                //根目录文件的起始盘块号
    unsigned char *start_block;         //虚拟磁盘上数据区开始位置
} block0;

/**
 * 全局变量定义
 */
FILE *fptr;                             //文件指针
char general_buffer[SIZE];              //通用缓冲区
unsigned char *my_v_hard;               //指向虚拟磁盘的起始地址
user_open open_file_list[MAX_OPEN_FILE];//用户打开文件表数组
user_open *ptr_current_dir;             //指向用户打开文件表中的当前目录所在打开文件表项的位置
char current_dir[80];                   //记录当前目录的目录名(包括目录的路径)
unsigned char *start_position;          //记录虚拟磁盘上数据区开始位置

/**
 * 函数声明
 */
void start_sys();

void my_format();

void my_cd(char *dirname);

void my_mkdir(char *dirname);

void my_rmdir(char *dirname);

void my_ls(void);

int my_create(char *filename);

void my_rm(char *filename);

int my_open(char *filename);

void my_close(int fd);

int my_write(int fd);

int do_write(int fd, char *text, int len, char w_style);

int my_read(int fd, int len);

int do_read(int fd, int len, char *text);

void my_exit_sys();

void init_file_system();

void init_cmd();

#endif

下面是文件系统相关函数的具体实现:

#include "header.h"

/**
 * myfsys 文件系统实体文件路径名
 */
char *SYS_FILE = "myfsys";

/**
 * 文件系统初始化
 */
void start_sys() {
    unsigned char buffer[SIZE];
    unsigned char flag[8] = {'1', '0', '1', '0', '1', '0', '1', '0'};
    my_v_hard = (unsigned char *) malloc(SIZE);

    if (fopen_s(&fptr, SYS_FILE, "r") == 0) {
        fread(buffer, SIZE, 1, fptr);
        if (memcmp(flag, buffer, sizeof(flag)) == 0) {
            memcpy(my_v_hard, buffer, SIZE);
        } else {
            init_file_system();
        }
    } else {
        init_file_system();
    }

    fwrite(my_v_hard, SIZE, 1, fptr);
    fclose(fptr);

    fcb *root = (fcb *) (my_v_hard + BLOCK_SIZE * 5);
    user_open *current = &open_file_list[0];
    strcpy_s(current->filename, sizeof(root->filename), root->filename);
    strcpy_s(current->extname, sizeof(root->extname), root->extname);
    current->attribute = root->attribute;
    current->time = root->time;
    current->date = root->date;
    current->first = root->first;
    current->length = root->length;
    current->free = root->free;
    current->dir_no = 5;
    current->dir_off = 0;
    strcpy_s(current->dir[0], sizeof("/"), "/");
    current->count = 0;
    current->fcb_state = 0;
    current->t_openfile = 1;

    ptr_current_dir = current;
    start_position = ((block0 *) my_v_hard)->start_block;
    strcpy_s(current_dir, sizeof("/"), "/");
}

/**
 * 磁盘格式化
 */
void my_format() {
    int i;
    block0 *boot = (block0 *) my_v_hard;
    time_t now;
    struct tm now_time;
    time(&now);
    localtime_s(&now_time, &now);
    char info[] = "189050712 过昊天 简单 FAT 文件系统";

    strcpy_s(boot->magic_number, sizeof("10101010"), "10101010");
    strcpy_s(boot->information, sizeof(info), info);
    boot->root = 5;
    boot->start_block = my_v_hard + BLOCK_SIZE * 5;

    fat *fat1 = (fat *) (my_v_hard + BLOCK_SIZE);
    fat *fat2 = (fat *) (my_v_hard + BLOCK_SIZE * 3);

    for (i = 0; i < 5; i++) {
        fat1[i].id = END;
        fat2[i].id = END;
    }
    fat1[5].id = 6;
    fat1[6].id = END;
    fat2[5].id = 6;
    fat2[6].id = END;
    for (i += ROOT_BLOCK_NUM; i < (SIZE / BLOCK_SIZE); i++) {
        fat1[i].id = FREE;
        fat2[i].id = FREE;
    }

    fcb *ptr_current_dir_fcb = (fcb *) (my_v_hard + BLOCK_SIZE * 5);
    strcpy_s(ptr_current_dir_fcb->filename, sizeof("."), ".");
    strcpy_s(ptr_current_dir_fcb->extname, sizeof(""), "");
    ptr_current_dir_fcb->attribute = 0;
    ptr_current_dir_fcb->time.hour = now_time.tm_hour;
    ptr_current_dir_fcb->time.minute = now_time.tm_min;
    ptr_current_dir_fcb->time.second = now_time.tm_sec;
    ptr_current_dir_fcb->date.year = now_time.tm_year;
    ptr_current_dir_fcb->date.month = now_time.tm_mon;
    ptr_current_dir_fcb->date.day = now_time.tm_mday;
    ptr_current_dir_fcb->first = 5;
    ptr_current_dir_fcb->length = 2 * sizeof(fcb);
    ptr_current_dir_fcb->free = 1;

    fcb *ptr_father_dir_fcb = ptr_current_dir_fcb + 1;
    memcpy(ptr_father_dir_fcb, ptr_current_dir_fcb, sizeof(fcb));
    strcpy_s(ptr_father_dir_fcb->filename, sizeof(".."), "..");

    for (i = ROOT_BLOCK_NUM; i < (int) (BLOCK_SIZE / sizeof(fcb)); i++) {
        ptr_father_dir_fcb++;
        strcpy_s(ptr_father_dir_fcb->filename, sizeof(""), "");
        ptr_father_dir_fcb->free = 0;
    }
}

/**
 * 更改当前目录
 *
 * @param dirname   新的当前目录的目录名
 */
void my_cd(char *dirname) {
    if (ptr_current_dir->attribute == 1) {
        puts("Error(my_cd): 当前正在文件中,请使用 'close' 命令关闭文件");
        return;
    }

    int i, fd = -1, tag = -1;
    static char buffer[SIZE];
    ptr_current_dir->count = 0;
    do_read(ptr_current_dir - open_file_list, (int) ptr_current_dir->length, buffer);

    fcb *fcb_ptr = (fcb *) buffer;
    for (i = 0; i < (int) (ptr_current_dir->length / sizeof(fcb)); i++, fcb_ptr++) {
        if (strcmp(fcb_ptr->filename, dirname) == 0 && fcb_ptr->attribute == 0) {
            tag = 1;
            break;
        }
    }
    if (tag != 1) {
        puts("Error(my_cd): 错误,目标路径不存在");
        return;
    } else {
        if (strcmp(fcb_ptr->filename, ".") == 0) {
            return;
        } else if (strcmp(fcb_ptr->filename, "..") == 0) {
            if (ptr_current_dir - open_file_list == 0) {
                return;
            } else {
                my_close(ptr_current_dir - open_file_list);
                return;
            }
        } else {
            for (int j = 0; j < MAX_OPEN_FILE; j++) {
                if (open_file_list[j].t_openfile == 0) {
                    open_file_list[j].t_openfile = 1;
                    fd = j;
                    break;
                }
            }
            if (fd == -1) {
                puts("Error(my_cd): 已超过允许的最多同时打开个数");
                return;
            }
            open_file_list[fd].attribute = fcb_ptr->attribute;
            open_file_list[fd].count = 0;
            open_file_list[fd].date = fcb_ptr->date;
            open_file_list[fd].time = fcb_ptr->time;
            strcpy_s(open_file_list[fd].filename, sizeof(fcb_ptr->filename) + 1, fcb_ptr->filename);
            strcpy_s(open_file_list[fd].extname, sizeof(fcb_ptr->extname), fcb_ptr->extname);
            open_file_list[fd].first = fcb_ptr->first;
            open_file_list[fd].free = fcb_ptr->free;
            open_file_list[fd].fcb_state = 0;
            open_file_list[fd].length = fcb_ptr->length;
            strcat(strcat(strcpy((char *) open_file_list[fd].dir, (char *) (ptr_current_dir->dir)), dirname), "/");
            open_file_list[fd].t_openfile = 1;
            open_file_list[fd].dir_no = ptr_current_dir->first;
            open_file_list[fd].dir_off = i;
            ptr_current_dir = open_file_list + fd;
        }
    }

}

/**
 * 创建子目录
 *
 * @param dirname   新建目录的目录名
 */
void my_mkdir(char *dirname) {
    char *separator = ".", *tok_buffer, *filename = strtok_s(dirname, separator, &tok_buffer);
    static char buffer[SIZE];

    char *extname = strtok_s(NULL, separator, &tok_buffer);
    if (extname != NULL) {
        puts("Error(mkdir): 参数错误,目录项不允许使用扩展名");
        return;
    }

    ptr_current_dir->count = 0;
    int file_length = do_read(ptr_current_dir - open_file_list, (int) ptr_current_dir->length, buffer);
    fcb *fcb_ptr = (fcb *) buffer;

    for (int i = 0; i < (int) (file_length / sizeof(fcb)); i++) {
        if (strcmp(dirname, fcb_ptr[i].filename) == 0 && fcb_ptr->attribute == 0) {
            puts("Error(mkdir): 路径名已存在");
            return;
        }
    }

    int fd = -1;
    for (int i = 0; i < MAX_OPEN_FILE; i++) {
        if (open_file_list[i].t_openfile != 1) {
            open_file_list[i].t_openfile = 1;
            fd = i;
            break;
        }
    }
    if (fd == -1) {
        puts("Error(mkdir): 已超过允许的最多同时打开个数");
        return;
    }

    unsigned short int block_num = END;
    fat *fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE);
    for (int i = 0; i < (int) (SIZE / BLOCK_SIZE); i++) {
        if (fat_ptr[i].id == FREE) {
            block_num = i;
            break;
        }
    }
    if (block_num == END) {
        puts("Error(mkdir): 磁盘块已满");
        return;
    }

    // 更新 FAT 表
    fat *fat1 = (fat *) (my_v_hard + BLOCK_SIZE), *fat2 = (fat *) (my_v_hard + BLOCK_SIZE * 3);
    fat1[block_num].id = END;
    fat2[block_num].id = END;

    int no;
    for (no = 0; no < (int) (file_length / sizeof(fcb)); no++) {
        if (fcb_ptr[no].free == 0) {
            break;
        }
    }

    ptr_current_dir->count = no * (int) sizeof(fcb);
    ptr_current_dir->fcb_state = 1;

    // 初始化 FCB
    time_t now;
    struct tm now_time;
    time(&now);
    localtime_s(&now_time, &now);

    fcb *new_fcb = (fcb *) malloc(sizeof(fcb));
    new_fcb->attribute = 0;
    new_fcb->time.hour = now_time.tm_hour;
    new_fcb->time.minute = now_time.tm_min;
    new_fcb->time.second = now_time.tm_sec;
    new_fcb->date.year = now_time.tm_year;
    new_fcb->date.month = now_time.tm_mon;
    new_fcb->date.day = now_time.tm_mday;
    strcpy_s(new_fcb->filename, strlen(dirname) + 1, dirname);
    strcpy_s(new_fcb->extname, sizeof(""), "");
    new_fcb->first = block_num;
    new_fcb->length = 2 * sizeof(fcb);
    new_fcb->free = 1;
    do_write(ptr_current_dir - open_file_list, (char *) new_fcb, sizeof(fcb), 2);

    // 设置打开文件表项
    open_file_list[fd].attribute = 0;
    open_file_list[fd].count = 0;
    open_file_list[fd].date = new_fcb->date;
    open_file_list[fd].time = new_fcb->time;
    open_file_list[fd].dir_no = ptr_current_dir->first;
    open_file_list[fd].dir_off = no;
    strcpy_s(open_file_list[fd].filename, strlen(dirname) + 1, dirname);
    strcpy_s(open_file_list[fd].extname, sizeof(""), "");
    open_file_list[fd].fcb_state = 0;
    open_file_list[fd].first = new_fcb->first;
    open_file_list[fd].free = new_fcb->free;
    open_file_list[fd].length = new_fcb->length;
    open_file_list[fd].t_openfile = 1;
    strcat(strcat(strcpy(open_file_list[fd].dir, (char *) (ptr_current_dir->dir)), dirname), "/");


    // 设置目录项
    fcb *ptr_current_dir_fcb = (fcb *) malloc(sizeof(fcb));
    ptr_current_dir_fcb->attribute = 0;
    ptr_current_dir_fcb->date = new_fcb->date;
    ptr_current_dir_fcb->time = new_fcb->time;
    strcpy_s(ptr_current_dir_fcb->filename, sizeof("."), ".");
    strcpy_s(ptr_current_dir_fcb->extname, sizeof(""), "");
    ptr_current_dir_fcb->first = block_num;
    ptr_current_dir_fcb->length = 2 * sizeof(fcb);
    ptr_current_dir_fcb->free = 1;
    do_write(fd, (char *) ptr_current_dir_fcb, sizeof(fcb), 2);

    fcb *ptr_father_dir_fcb = (fcb *) malloc(sizeof(fcb));
    memcpy(ptr_father_dir_fcb, ptr_current_dir_fcb, sizeof(fcb));
    strcpy_s(ptr_father_dir_fcb->filename, sizeof(".."), "..");
    ptr_father_dir_fcb->first = ptr_current_dir->first;
    ptr_father_dir_fcb->length = ptr_current_dir->length;
    ptr_father_dir_fcb->date = ptr_current_dir->date;
    ptr_father_dir_fcb->time = ptr_current_dir->time;
    ptr_father_dir_fcb->free = 1;
    do_write(fd, (char *) ptr_father_dir_fcb, sizeof(fcb), 2);

    my_close(fd);
    free(new_fcb);
    free(ptr_current_dir_fcb);
    free(ptr_father_dir_fcb);


    // 修改父目录 FCB
    fcb_ptr = (fcb *) buffer;
    fcb_ptr->length = ptr_current_dir->length;
    ptr_current_dir->count = 0;
    do_write(ptr_current_dir - open_file_list, (char *) fcb_ptr, sizeof(fcb), 2);
    ptr_current_dir->fcb_state = 1;
}

/**
 * 删除子目录
 *
 * @param dirname   欲删除目录的目录名
 */
void my_rmdir(char *dirname) {
    int i, tag = 0;
    static char buffer[SIZE];

    if (strcmp(dirname, ".") == 0 || strcmp(dirname, "..") == 0) {
        puts("Error(My_Rmdir): 非法操作,无法对 '.' 和 '..' 目录执行 'rmdir'");
        return;
    }
    ptr_current_dir->count = 0;
    do_read(ptr_current_dir - open_file_list, (int) ptr_current_dir->length, buffer);

    // 查找要删除的目录
    fcb *fcb_ptr = (fcb *) buffer;
    for (i = 0; i < (int) (ptr_current_dir->length / sizeof(fcb)); i++, fcb_ptr++) {
        if (fcb_ptr->free == 0)
            continue;
        if (strcmp(fcb_ptr->filename, dirname) == 0 && fcb_ptr->attribute == 0) {
            tag = 1;
            break;
        }
    }
    if (tag != 1) {
        puts("Error(My_Rmdir): 找不到该目录");
        return;
    }
    // 无法删除非空目录
    if (fcb_ptr->length > 2 * sizeof(fcb)) {
        puts("Error(My_Rmdir): 禁止对非空目录执行 'rmdir'");
        return;
    }

    // 更新 FAT 表
    int block_num = fcb_ptr->first;
    fat *fat1 = (fat *) (my_v_hard + BLOCK_SIZE);
    int nxt_num;
    while (1) {
        nxt_num = fat1[block_num].id;
        fat1[block_num].id = FREE;
        if (nxt_num != END) {
            block_num = nxt_num;
        } else {
            break;
        }
    }
    fat1 = (fat *) (my_v_hard + BLOCK_SIZE);
    fat *fat2 = (fat *) (my_v_hard + BLOCK_SIZE * 3);
    memcpy(fat2, fat1, BLOCK_SIZE * 2);

    // 更新 FCB
    fcb_ptr->date.day = 0;
    fcb_ptr->date.month = 0;
    fcb_ptr->date.year = 0;
    fcb_ptr->time.second = 0;
    fcb_ptr->time.minute = 0;
    fcb_ptr->time.hour = 0;
    fcb_ptr->extname[0] = '\0';
    fcb_ptr->filename[0] = '\0';
    fcb_ptr->first = 0;
    fcb_ptr->free = 0;
    fcb_ptr->length = 0;

    ptr_current_dir->count = (int) (i * sizeof(fcb));
    do_write(ptr_current_dir - open_file_list, (char *) fcb_ptr, sizeof(fcb), 2);

    // 考虑修改父目录 length, 循环删除 FCB
    int log_num = i;
    if ((log_num + 1) * sizeof(fcb) == ptr_current_dir->length) {
        ptr_current_dir->length -= sizeof(fcb);
        log_num--;
        fcb_ptr = (fcb *) buffer + log_num;
        while (fcb_ptr->free == 0) {
            fcb_ptr--;
            ptr_current_dir->length -= sizeof(fcb);
        }
    }

    // 更新父目录 FCB
    fcb_ptr = (fcb *) buffer;
    fcb_ptr->length = ptr_current_dir->length;
    ptr_current_dir->count = 0;
    do_write(ptr_current_dir - open_file_list, (char *) fcb_ptr, sizeof(fcb), 2);

    ptr_current_dir->fcb_state = 1;
}

/**
 * 显示目录
 */
void my_ls(void) {
    if (ptr_current_dir->attribute == 1) {
        puts("Error(ls): 非法操作,不能对文件执行 ls 操作");
        return;
    }
    char text[SIZE];

    puts("类型    名称              大小   时间");
    ptr_current_dir->count = 0;
    do_read(ptr_current_dir - open_file_list, (int) ptr_current_dir->length, text);

    fcb *fcb_ptr = (fcb *) text;
    for (int i = 0; i < (int) (ptr_current_dir->length / sizeof(fcb)); i++, fcb_ptr++) {
        if (fcb_ptr->free == 1) {
            if (fcb_ptr->attribute == 0) {
                printf("<DIR>   %-15s  -     %d/%02d/%02d %02d:%02d:%02d\n",
                       fcb_ptr->filename,
                       fcb_ptr->date.year + 1900,
                       fcb_ptr->date.month,
                       fcb_ptr->date.day,
                       fcb_ptr->time.hour,
                       fcb_ptr->time.minute,
                       fcb_ptr->time.second);
            } else if (fcb_ptr->attribute == 1) {
                printf("<--->   %-15s  %-4lu  %d/%02d/%02d %02d:%02d:%02d\n",
                       fcb_ptr->filename,
                       fcb_ptr->length,
                       fcb_ptr->date.year + 1900,
                       fcb_ptr->date.month,
                       fcb_ptr->date.day,
                       fcb_ptr->time.hour,
                       fcb_ptr->time.minute,
                       fcb_ptr->time.second);
            } else {
                puts("Error(ls): 文件系统错误,遇到未知类型");
                return;
            }
        }
    }

}

/**
 * 创建文件
 *
 * @param filename  新建文件的文件名,可能包含路径
 * @return          若创建成功,返回该文件的文件描述符(文件打开表中的数组下标);否则返回 -1
 */
int my_create(char *filename) {
    if (ptr_current_dir->attribute == 1) {
        puts("Error(My_Create): 非法操作,不能在数据块中执行 'create'");
        return -1;
    }

    static char buffer[SIZE];

    ptr_current_dir->count = 0;
    do_read(ptr_current_dir - open_file_list, (int) ptr_current_dir->length, buffer);

    int i;
    fcb *fcb_ptr = (fcb *) buffer;
    // 检查重名
    for (i = 0; i < (int) (ptr_current_dir->length / sizeof(fcb)); i++, fcb_ptr++) {
        if (fcb_ptr->free == 0) {
            continue;
        }
        if (strcmp(fcb_ptr->filename, filename) == 0 && fcb_ptr->attribute == 1) {
            puts("Error(My_Create): 文件已经存在");
            return -1;
        }
    }

    // 申请空 FCB
    fcb_ptr = (fcb *) buffer;
    for (i = 0; i < (int) (ptr_current_dir->length / sizeof(fcb)); i++, fcb_ptr++) {
        if (fcb_ptr->free == 0)
            break;
    }
    // 申请磁盘块并更新 FAT 表
    int block_num = -1;
    fat *fat1 = (fat *) (my_v_hard + BLOCK_SIZE);
    for (int j = 0; j < (int) (SIZE / BLOCK_SIZE); j++) {
        if (fat1[j].id == FREE) {
            block_num = j;
            break;
        }
    }
    if (block_num == -1) {
        return -1;
    }
    fat *fat11 = (fat *) (my_v_hard + BLOCK_SIZE);
    fat *fat2 = (fat *) (my_v_hard + BLOCK_SIZE * 3);
    fat11[block_num].id = END;
    memcpy(fat2, fat11, BLOCK_SIZE * 2);

    // 修改 FCB 信息
    strcpy_s(fcb_ptr->filename, strlen(filename) + 1, filename);
    time_t now;
    struct tm now_time;
    time(&now);
    localtime_s(&now_time, &now);
    fcb_ptr->date.year = now_time.tm_year;
    fcb_ptr->date.month = now_time.tm_mon;
    fcb_ptr->date.day = now_time.tm_mday;
    fcb_ptr->time.hour = now_time.tm_hour;
    fcb_ptr->time.minute = now_time.tm_min;
    fcb_ptr->time.second = now_time.tm_sec;
    fcb_ptr->first = block_num;
    fcb_ptr->free = 1;
    fcb_ptr->attribute = 1;
    fcb_ptr->length = 0;

    ptr_current_dir->count = (int) (i * sizeof(fcb));
    do_write(ptr_current_dir - open_file_list, (char *) fcb_ptr, sizeof(fcb), 2);

    // 修改父目录 FCB
    fcb_ptr = (fcb *) buffer;
    fcb_ptr->length = ptr_current_dir->length;
    ptr_current_dir->count = 0;
    do_write(ptr_current_dir - open_file_list, (char *) fcb_ptr, sizeof(fcb), 2);

    ptr_current_dir->fcb_state = 1;
}

/**
 * 删除文件
 *
 * @param filename 欲删除文件的文件名,可能还包含路径
 */
void my_rm(char *filename) {
    static char buffer[SIZE];
    ptr_current_dir->count = 0;
    do_read(ptr_current_dir - open_file_list, (int) ptr_current_dir->length, buffer);

    int i, flag = 0;
    fcb *fcb_ptr = (fcb *) buffer;

    for (i = 0; i < ((int) ptr_current_dir->length / sizeof(fcb)); i++, fcb_ptr++) {
        if (strcmp(fcb_ptr->filename, filename) == 0 && fcb_ptr->attribute == 1) {
            flag = 1;
            break;
        }
    }
    if (flag != 1) {
        puts("Error(my_rm): 无此文件");
        return;
    }

    // 更新 FAT 表
    int block_num = fcb_ptr->first;
    fat *fat1 = (fat *) (my_v_hard + BLOCK_SIZE);
    int nxt_num = 0;
    while (TRUE) {
        nxt_num = fat1[block_num].id;
        fat1[block_num].id = FREE;
        if (nxt_num != END)
            block_num = nxt_num;
        else
            break;
    }
    fat1 = (fat *) (my_v_hard + BLOCK_SIZE);
    fat *fat2 = (fat *) (my_v_hard + BLOCK_SIZE * 3);
    memcpy(fat2, fat1, BLOCK_SIZE * 2);

    // 清空 FCB
    fcb_ptr->date.day = 0;
    fcb_ptr->date.month = 0;
    fcb_ptr->date.year = 0;
    fcb_ptr->time.second = 0;
    fcb_ptr->time.minute = 0;
    fcb_ptr->time.hour = 0;
    fcb_ptr->extname[0] = '\0';
    fcb_ptr->filename[0] = '\0';
    fcb_ptr->first = 0;
    fcb_ptr->free = 0;
    fcb_ptr->length = 0;
    ptr_current_dir->count = (int) (i * sizeof(fcb));
    do_write(ptr_current_dir - open_file_list, (char *) fcb_ptr, sizeof(fcb), 2);
    //
    int log_num = i;
    if ((log_num + 1) * sizeof(fcb) == ptr_current_dir->length) {
        ptr_current_dir->length -= sizeof(fcb);
        log_num--;
        fcb_ptr = (fcb *) buffer + log_num;
        while (fcb_ptr->free == 0) {
            fcb_ptr--;
            ptr_current_dir->length -= sizeof(fcb);
        }
    }

    // 修改父目录 . 目录文件的 FCB
    fcb_ptr = (fcb *) buffer;
    fcb_ptr->length = ptr_current_dir->length;
    ptr_current_dir->count = 0;
    do_write(ptr_current_dir - open_file_list, (char *) fcb_ptr, sizeof(fcb), 2);

    ptr_current_dir->fcb_state = 1;

}

/**
 * 打开文件
 *
 * @param filename  欲打开文件的文件名
 * @return          若打开成功,返回该文件的描述符(在用户打开文件表中表项序号);否则返回 -1
 */
int my_open(char *filename) {
    static char buffer[SIZE];
    ptr_current_dir->count = 0;
    do_read(ptr_current_dir - open_file_list, (int) ptr_current_dir->length, buffer);

    int i, flag = 0;
    fcb *fcb_ptr = (fcb *) buffer;

    for (i = 0; i < (int) (ptr_current_dir->length / sizeof(fcb)); i++, fcb_ptr++) {
        if (strcmp(fcb_ptr->filename, filename) == 0 && fcb_ptr->attribute == 1) {
            flag = 1;
            break;
        }
    }
    if (flag != 1) {
        puts("Error(My_Open): 文件不存在");
        return -1;
    }

    int fd = -1;
    for (int j = 0; j < MAX_OPEN_FILE; j++) {
        if (open_file_list[j].t_openfile == 0) {
            open_file_list[j].t_openfile = 1;
            fd = j;
            break;
        }
    }
    if (fd == -1) {
        puts("Error(My_Open): 已超过允许的最多同时打开个数");
        return -1;
    }

    open_file_list[fd].attribute = 1;
    open_file_list[fd].count = 0;
    open_file_list[fd].date = fcb_ptr->date;
    open_file_list[fd].time = fcb_ptr->time;
    open_file_list[fd].length = fcb_ptr->length;
    open_file_list[fd].first = fcb_ptr->first;
    open_file_list[fd].free = 1;
    strcpy_s(open_file_list[fd].filename, strlen(fcb_ptr->filename) + 1, fcb_ptr->filename);
    strcat(strcpy((char *) open_file_list[fd].dir, (char *) (ptr_current_dir->dir)), filename);
    open_file_list[fd].dir_no = ptr_current_dir->first;
    open_file_list[fd].dir_off = i;
    open_file_list[fd].t_openfile = 1;

    open_file_list[fd].fcb_state = 0;

    ptr_current_dir = open_file_list + fd;
    return fd;
}

/**
 * 关闭文件
 *
 * @param fd 文件描述符
 */
void my_close(int fd) {
    if (fd > MAX_OPEN_FILE || fd < 0) {
        puts("Error(My_Close): 非法操作,文件描述符越界");
        return;
    }

    int i, father_fd = -1;
    fcb *fcb_ptr;

    for (i = 0; i < MAX_OPEN_FILE; i++) {
        if (open_file_list[i].first == open_file_list[fd].dir_no) {
            father_fd = i;
            break;
        }
    }
    if (father_fd == -1) {
        puts("Error(My_Close): 文件系统错误,父目录缺失");
        return;
    }

    static char buffer[SIZE];
    if (open_file_list[fd].fcb_state == 1) {
        do_read(father_fd, (int) open_file_list[father_fd].length, buffer);

        fcb_ptr = (fcb *) (buffer + sizeof(fcb) * open_file_list[fd].dir_off);
        strcpy_s(fcb_ptr->extname, sizeof(open_file_list[fd].extname), open_file_list[fd].extname);
        strcpy_s(fcb_ptr->filename, sizeof(open_file_list[fd].filename), open_file_list[fd].filename);
        fcb_ptr->first = open_file_list[fd].first;
        fcb_ptr->free = open_file_list[fd].free;
        fcb_ptr->length = open_file_list[fd].length;
        fcb_ptr->time = open_file_list[fd].time;
        fcb_ptr->date = open_file_list[fd].date;
        fcb_ptr->attribute = open_file_list[fd].attribute;
        open_file_list[father_fd].count = open_file_list[fd].dir_off * (int) sizeof(fcb);

        do_write(father_fd, (char *) fcb_ptr, sizeof(fcb), 2);
    }

    // 释放打开文件表
    memset(&open_file_list[fd], 0, sizeof(user_open));
    ptr_current_dir = open_file_list + father_fd;
}

/**
 * 写文件
 *
 * @param fd        open() 函数的返回值,文件的描述符
 * @return          实际写入的字节数
 */
int my_write(int fd) {
    if (fd < 0 || fd >= MAX_OPEN_FILE) {
        puts("Error(My_Write): 文件不存在");
        return -1;
    }

    int w_style;
    while (1) {
        puts("请选择写方式:");
        puts("1. 截断写: 清空全部内容,从头开始");
        puts("2. 覆盖写: 从文件指针处开始覆盖写入");
        puts("3. 追加写: 追加写入");
        scanf_s("%d", &w_style);
        if (w_style > 3 || w_style < 1) {
            printf("input error\n");
        } else {
            break;
        }
    }
    general_buffer[0] = '\0';
    char text_tmp[SIZE] = "\0";
    puts("Info(My_Write): 请输入,换行后输入 ':wq!' 结束输入");
    getchar();

    while (TRUE) {
        fgets(text_tmp, SIZE, stdin);
        if (strcmp(text_tmp, ":wq!\n") == 0) {
            break;
        }
        strcat_s(general_buffer, sizeof(general_buffer), text_tmp);
    }

    general_buffer[strlen(general_buffer)] = '\0';
    do_write(fd, general_buffer, (int) strlen(general_buffer) + 1, (char) w_style);
    open_file_list[fd].fcb_state = 1;

    return (int) strlen(general_buffer) + 1;
}

/**
 * 写文件动作
 *
 * @param fd        open() 函数的返回值,文件的描述符
 * @param text      指向要写入的内容的指针
 * @param len       本次要求写入字节数
 * @param w_style   写方式: 1-截断写 2-覆盖写 3-追加写
 * @return          实际写入的字节数
 */
int do_write(int fd, char *text, int len, char w_style) {
    unsigned char *buffer = (unsigned char *) malloc(BLOCK_SIZE);
    if (buffer == NULL) {
        puts("Error(Do_Write): 缓冲区空间申请失败");
        return -1;
    }

    int block_num = open_file_list[fd].first, read_count = 0;
    char *text_ptr = text;
    fat *fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;

    if (w_style == 1) {
        open_file_list[fd].count = 0;
        open_file_list[fd].length = 0;
    } else if (w_style == 3) {
        open_file_list[fd].count = (int) open_file_list[fd].length;
        if (open_file_list[fd].attribute == 1) {
            // 非空文件删除末尾 \0
            if (open_file_list[fd].length != 0) {
                open_file_list[fd].count = (int) open_file_list[fd].length - 1;
            }
        }
    }
    int offset = open_file_list[fd].count;

    // 定位磁盘块和块内偏移量
    while (offset >= BLOCK_SIZE) {
        block_num = fat_ptr->id;
        if (block_num == END) {
            puts("Error(Do_Write): 当前用户打开文件表的读写指针溢出");
            return -1;
        }
        fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;
        offset -= BLOCK_SIZE;
    }

    unsigned char *block_ptr = (unsigned char *) (my_v_hard + block_num * BLOCK_SIZE);
    while (len > read_count) {
        memcpy(buffer, block_ptr, BLOCK_SIZE);
        for (; offset < BLOCK_SIZE; offset++) {
            *(buffer + offset) = *text_ptr;
            text_ptr++;
            read_count++;
            if (len == read_count) {
                break;
            }
        }
        memcpy(block_ptr, buffer, BLOCK_SIZE);

        // 向下一个磁盘块写入,如果没有磁盘块则进行申请
        if (offset == BLOCK_SIZE && len != read_count) {
            offset = 0;
            block_num = fat_ptr->id;
            if (block_num == END) {
                fat *new_fat = (fat *) (my_v_hard + BLOCK_SIZE);
                for (int j = 0; j < (int) (SIZE / BLOCK_SIZE); j++) {
                    if (new_fat[j].id == FREE) {
                        block_num = j;
                    }
                }
                if (block_num == END) {
                    puts("Error(Do_Write): 磁盘块分配失败,空间已满");
                    return -1;
                }
                block_ptr = (unsigned char *) (my_v_hard + BLOCK_SIZE * block_num);
                fat_ptr->id = block_num;
                fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;
                fat_ptr->id = END;
            } else {
                block_ptr = (unsigned char *) (my_v_hard + BLOCK_SIZE * block_num);
                fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;
            }
        }
    }

    open_file_list[fd].count += len;
    if (open_file_list[fd].count > open_file_list[fd].length) {
        open_file_list[fd].length = open_file_list[fd].count;
    }

    // 定位磁盘块和块内偏移量
    if (w_style == 1 || (w_style == 2 && open_file_list[fd].attribute == 0)) {
        int i;
        offset = (int) open_file_list[fd].length;
        fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + open_file_list[fd].first;

        while (offset >= BLOCK_SIZE) {
            block_num = fat_ptr->id;
            offset -= BLOCK_SIZE;
            fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;
        }
        while (TRUE) {
            if (fat_ptr->id != END) {
                i = fat_ptr->id;
                fat_ptr->id = FREE;
                fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + i;
            } else {
                fat_ptr->id = FREE;
                break;
            }
        }

        fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;
        fat_ptr->id = END;
    }

    memcpy((fat *) (my_v_hard + BLOCK_SIZE * 3), (fat *) (my_v_hard + BLOCK_SIZE), BLOCK_SIZE * 2);
    return len;
}

/**
 * 读文件
 *
 * @param fd        open() 函数的返回值,文件的描述符
 * @param len       要从文件中读出的字节数
 * @return          实际读出的字节数
 */
int my_read(int fd, int len) {
    if (fd < 0 || fd >= MAX_OPEN_FILE) {
        puts("Error(My_Read): 文件不存在");
        return -1;
    }

    static char buffer[SIZE];
    ptr_current_dir->count = 0;
    int length = do_read(fd, len, buffer);
    puts(buffer);
    return length;
}

/**
 * 读文件动作
 *
 * @param fd        open() 函数的返回值,文件的描述符
 * @param len       要求从文件中读出的字节数
 * @param text      指向存放读出数据的用户区地址
 * @return          实际读出的字节数
 */
int do_read(int fd, int len, char *text) {
    unsigned char *buffer = (unsigned char *) malloc(BLOCK_SIZE);
    if (buffer == NULL) {
        puts("Error(Do_Read): 缓冲区空间申请失败");
        return -1;
    }

    int required_len = len;
    char *text_ptr = text;
    int block_num = open_file_list[fd].first;
    int offset = open_file_list[fd].count;
    fat *fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;

    // 定位读取目标磁盘块和块内地址
    while (offset >= BLOCK_SIZE) {
        offset -= BLOCK_SIZE;
        block_num = fat_ptr->id;
        if (block_num == END) {
            puts("Error(Do_Read): 目标磁盘块不存在");
            return -1;
        }
        fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;
    }

    unsigned char *block_ptr = my_v_hard + block_num * BLOCK_SIZE;
    memcpy(buffer, block_ptr, BLOCK_SIZE);

    while (len > 0) {
        if (BLOCK_SIZE - offset > len) {
            memcpy(text_ptr, buffer + offset, len);
            text_ptr += len;
            offset += len;
            open_file_list[fd].count += len;
            len = 0;
        } else {
            memcpy(text_ptr, buffer + offset, BLOCK_SIZE - offset);
            text_ptr += BLOCK_SIZE - offset;
            len -= BLOCK_SIZE - offset;

            block_num = fat_ptr->id;
            if (block_num == END) {
                break;
            }
            fat_ptr = (fat *) (my_v_hard + BLOCK_SIZE) + block_num;
            block_ptr = my_v_hard + BLOCK_SIZE * block_num;
            memcpy(buffer, block_ptr, BLOCK_SIZE);
        }
    }

    free(buffer);
    return required_len - len;
}

/**
 * 退出文件系统
 */
void my_exit_sys() {
    while (ptr_current_dir - open_file_list) {
        my_close(ptr_current_dir - open_file_list);
    }

    if (fopen_s(&fptr, SYS_FILE, "w") != 0) {
        puts("Error(My_Exit): 文件指针操作失败");
        return;
    }
    fwrite(my_v_hard, SIZE, 1, fptr);
    fclose(fptr);
    puts("Info(My_Exit): 已保存并退出文件系统");
}

/**
 * 文件系统初始化
 */
void init_file_system() {
    if (fopen_s(&fptr, SYS_FILE, "w") != 0) {
        puts("Error(My_Format): 文件指针操作失败");
        return;
    }
    puts("Error(My_Format): myfsys 文件系统不存在,现在开始创建文件系统");
    my_format();
}

/**
 * 命令行初始化
 */
void init_cmd() {
    puts("------------------------------------命令行操作指南------------------------------------");
    puts("mkdir:创建子目录  rmdir:删除子目录  ls:显示目录中的内容  cd:更改当前目录  create:创建文件");
    puts("open:打开文件  close:关闭文件  write:写文件  read:读文件  rm:删除文件  exit:退出文件系统");
    puts("---------------------!危险操作!     format:格式化磁盘     !危险操作!--------------------");
    puts("------------------------------------------------------------------------------------");
}

主函数模拟一个命令行,用来进行文件系统操作或输入等:

#include "header.h"

int main() {
    start_sys();
    init_cmd();

    while (TRUE) {
        char input[CMD_LENGTH], *cmd, *param, *buffer, *separator = " ";

        printf("tony %s $", *ptr_current_dir->dir);
        gets_s(input, CMD_LENGTH);
        if (strcmp(input, "") == 0) {
            continue;
        }
        cmd = strtok_s(input, separator, &buffer);

        if (strcmp(cmd, "mkdir") == 0) {
            if ((param = strtok_s(NULL, separator, &buffer)) == NULL) {
                puts("Error(CMD): 无效参数");
                continue;
            }
            my_mkdir(param);
        } else if (strcmp(cmd, "rmdir") == 0) {
            if ((param = strtok_s(NULL, separator, &buffer)) == NULL) {
                puts("Error(CMD): 无效参数");
                continue;
            }
            my_rmdir(param);
        } else if (strcmp(cmd, "ls") == 0) {
            my_ls();
        } else if (strcmp(cmd, "cd") == 0) {
            if ((param = strtok_s(NULL, separator, &buffer)) == NULL) {
                puts("Error(CMD): 无效参数");
                continue;
            }
            my_cd(param);
        } else if (strcmp(cmd, "create") == 0) {
            if ((param = strtok_s(NULL, separator, &buffer)) == NULL) {
                puts("Error(CMD): 无效参数");
                continue;
            }
            my_create(param);
        } else if (strcmp(cmd, "open") == 0) {
            if ((param = strtok_s(NULL, separator, &buffer)) == NULL) {
                puts("Error(CMD): 无效参数");
                continue;
            }
            my_open(param);
        } else if (strcmp(cmd, "close") == 0) {
            if (ptr_current_dir->attribute == 0) {
                puts("Error(CMD): 非法操作,不能对目录执行 'close' 操作");
                continue;
            }
            my_close(ptr_current_dir - open_file_list);
        } else if (strcmp(cmd, "write") == 0) {
            if (ptr_current_dir->attribute == 1) {
                my_write(ptr_current_dir - open_file_list);
            }
        } else if (strcmp(cmd, "read") == 0) {
            if ((param = strtok_s(NULL, separator, &buffer)) == NULL) {
                puts("Error(CMD): 无效参数");
                continue;
            }
            char *ptr;
            my_read(ptr_current_dir - open_file_list, (int) strtol(param, &ptr, 10));
        } else if (strcmp(cmd, "rm") == 0) {
            if ((param = strtok_s(NULL, separator, &buffer)) == NULL) {
                puts("Error(CMD): 无效参数");
                continue;
            }
            my_rm(param);
        } else if (strcmp(cmd, "exit") == 0) {
            my_exit_sys();
            return 0;
        } else if (strcmp(cmd, "format") == 0) {
            my_format();
            puts("Info(CMD): 文件系统初始化完成");
        } else {
            printf("Error(CMD): 无效命令: %s\n", cmd);
        }
    }
}