杭电操作系统实验五

Intro

基本框架借鉴https://hub.fastgit.org/leslievan/Operator_System/tree/master/Operator_System_Lab5

写得非常好,有很多值得学习的地方,但bug有点多,不忍学弟学妹们再受我当时的苦,故将魔改版本公之于众。

我的主要修改在do_write,do_read,do_rmdir

Idea

  1. 初始化虚拟磁盘空间 or 从文件中读取虚拟磁盘内容
  2. 读取一行输入内容
  3. 将内容划分为tokens
  4. 根据tokens去执行相应的命令
  5. 循环2,3,4,直到输入退出命令

Conceive

  1. 普通目录占一个磁盘块,根目录占两个磁盘块

  2. 看一个文件有无fcb,看free的值即可

  3. 文件描述符fd的取值为从0到MAX_OPENFILE-1

  4. 用户打开文件表 USEROPEN

    • 进入目录即打开,退出目录不关闭
  5. 初始化还是很有必要将一个磁盘块的fcb的free字段的位置初始化为0,因为要遍历

  6. 关于用户打开文件表USEROPEN

    • 其中的fcb open_fcb字段是copy过来的
    • 导致修改文件后,有关open_fcb的字段的修改都应该改两个地方
  7. do_format🍂

    • Check argument count.

    • Check argument value.

    • Init the boot block(block0)

    • Init FAT0/1.

    • Allocate 5 blocks to one block0(1) and two fat(2).

    • init root directory.

    •     /**< init root directory. */
          fcb *root = (fcb *)ptr;
          first = get_free(ROOT_BLOCK_NUM);
          set_fat(first, ROOT_BLOCK_NUM, 0);
          set_fcb(root, ".", "di", 0, first, BLOCK_SIZE * 2, 1);
          root++;
          set_fcb(root, "..", "di", 0, first, BLOCK_SIZE * 2, 1);
          root++;
      
          for (i = 2; i < BLOCK_SIZE * 2 / sizeof(fcb); i++, root++)//置为0,代表这里没有fcb!
          {
              root->free = 0;
          }
      
  8. my_rmdir🎅(递归删除,并更新用户打开文件表)

    • 若目录文件打开(在USEROPEN中),则移除不了

    • 移除根目录 Permission denied

    • 调用do_rmdir,do_rmdir为强制删除

    • /**
       * Just do remove directory.
       * 强制删除,不管是否打开,调用前应该自己加上判断
       */
      void do_rmdir(fcb *dir)
      {
          int i;
          int first = dir->first;
          fcb *ergfcb = (fcb *)(fs_head + BLOCK_SIZE * first);
          //tuo
          for (i = 0; i < MAX_OPENFILE; i++)
          {
              if(openfile_list[i].free==1
              &&!strcmp(openfile_list[i].open_fcb.filename,dir->filename)
              &&(openfile_list[i].open_fcb.first==dir->first)){
                  openfile_list[i].free=0;
              }
              
          }
          
          dir->free = 0;
          ergfcb->free = 0;
          ergfcb++;
          ergfcb->free = 0;
          ergfcb++;
          set_fat(first, 1, 1);
          //遍历递归删除
          for (i = 2; i < BLOCK_SIZE / sizeof(fcb); i++)
          {
      
              if (ergfcb->free == 1)
              {
                  if (ergfcb->attribute == 0)//目录
                  {
                      do_rmdir(ergfcb);
                  }
                  else
                  {
                      do_rm(ergfcb);
                  }
              }
          }
      }
      
  9. my_cd💛

  • Check argument count.
  • Check argument value.
  • Check if the folder fcb exist in openfile_list.
    • yes->do_ChangeDirectory
    • NO->Folder is close, open it and change current directory.the open operation needs to be checked or the array will be out of bounds
  1. my_ls💦(能看见文件的拓展名,但看不到目录的拓展名)

    • Check argument count.
    • for (i = 1; args[i] != NULL; i++)
      • search params of ls
      • search the first block num of fcb corresponding to the path
    • traverse the corresponding disk block
  2. do_write🐋

    • int do_write(int fd, char *text, int len, int wstyle)
      {
      
          int off, logicNum, blockNum, i, j, index, size;
          char buf[BLOCK_SIZE];
          fat *fat1 = (fat *)(fs_head + BLOCK_SIZE);
          fat *fat2 = (fat *)(fs_head + 3 * BLOCK_SIZE);
          char *ptr = fs_head;
          char *varp, *varpText;
          fcb *fcb;
      
          if (wstyle == 'a')
          {
              openfile_list[fd].count = openfile_list[fd].open_fcb.length;
          }
      
          //将读写指针转化为逻辑块块号和块内偏移off
          logicNum = openfile_list[fd].count / BLOCK_SIZE;
          off = openfile_list[fd].count % BLOCK_SIZE;
      
          //进一步得到其磁盘块号(注:若logicNum过大,则需报错)
          index = openfile_list[fd].open_fcb.first;
          blockNum = index;
          for (j = 0; j < logicNum; j++)
          {
              index = fat1[index].id;
              if (index == END && j != logicNum)
              {
                  fprintf(stderr, "输入的读写指针太大,超过了正文内容\n");
                  return 0;
              }
              blockNum = fat1[index].id;
          }
      
          //申请空闲缓冲区,将临时存储区中的数据转储到缓冲区,将缓冲区的内容写到相应的磁盘块中,直到写完
          //这是文件系统,所以不能定位到盘块,然后就直接在盘块上写数据了!!
          //用一个buf, 把盘块内容读取进来, 在buf里面修改, 然后再把buf内容写回到盘块里去
          size = (off + len) / BLOCK_SIZE;
          ptr += BLOCK_SIZE * blockNum;
          strcpy(buf, ptr);
          varp = buf + off;
          if (size > 19)
          {
              return 0;
          }
          if (size == 0)
          {
              strcpy(varp, text);
      
              //写入虚拟磁盘(注意:写入磁盘的是buf)
              strcpy(ptr, buf);
          }
          else
          {
              strncpy(varp, ptr, BLOCK_SIZE - off);
              varpText = text + BLOCK_SIZE - off;
      
              while (size--)
              {
                  int n;
                  n = get_free(1);
                  //修改FAT
                  fat1[blockNum].id = n;
                  fat1[n].id = END;
                  fat2[blockNum].id = n;
                  fat2[n].id = END;
                  blockNum = n; //
                  //写入虚拟磁盘
                  ptr = fs_head + n * BLOCK_SIZE;
                  strncpy(buf, varpText, BLOCK_SIZE);
                  varpText += BLOCK_SIZE;
                  strcpy(ptr, buf);
              }
          }
      
          if (wstyle == 't')
          {
              find_fcb(openfile_list[fd].dir)->length = len;
          }
          else if (wstyle == 'c')
          {
              find_fcb(openfile_list[fd].dir)->length = openfile_list[fd].count + len;
          }
          else
          {
              find_fcb(openfile_list[fd].dir)->length += len;
          }
      
          fcb_cpy(&openfile_list[fd].open_fcb, find_fcb(openfile_list[fd].dir));
          openfile_list[fd].count = 0;
          openfile_list[fd].fcb_state = 1;
      
          return 1;
      }
      
  3. do_read🐇

    • int do_read(int fd, int len, char *text)
      {
          memset(text, '\0', BLOCK_SIZE * 20);
          
          if (len <= 0) //想要读取0个字符
          {
              return 0;
          }
      
          fat *fat1 = (fat *)(fs_head + BLOCK_SIZE); // FAT1表
          int location = 0;                          // text的写入位置
          int length;
          int count = openfile_list[fd].count; //读写指针位置
          //排除了id出现end的情况
          if ((openfile_list[fd].open_fcb.length - count) >= len) //可以读取的字符多于想要读取的字符
          {
              length = len; //想要读取的字符
          }
          else
          {
              length = openfile_list[fd].open_fcb.length - count; //只能读取这些字符
          }
      
      
          int off, logicNum, blockNum, i, j, index, size;
          char buf[BLOCK_SIZE];
      
          char *ptr = fs_head;
          char *varp, *varpText;
          fcb *fcb;
      
          
          
          //将读写指针转化为逻辑块块号和块内偏移off
          logicNum = openfile_list[fd].count / BLOCK_SIZE;
          off = openfile_list[fd].count % BLOCK_SIZE;
      
          //进一步得到其磁盘块号(注:若logicNum过大,则需报错)
          index = openfile_list[fd].open_fcb.first;
          blockNum = index;
          for (j = 0; j < logicNum; j++)
          {
              index = fat1[index].id;
              if (index == END && j != logicNum)
              {
                  fprintf(stderr, "输入的读写指针太大,超过了正文内容\n");
                  return 0;
              }
              blockNum = fat1[index].id;
          }
      
          //将该磁盘块整块内容读入缓冲区,再将从off开始的缓冲区中的内容复制到text[]中
      
          size = (off + length) / BLOCK_SIZE;
          ptr += BLOCK_SIZE * blockNum;
          strncpy(buf, ptr,BLOCK_SIZE);
          varp = buf + off;
      
          
          if (size == 0)
          {
              strcpy(text, varp);
          }
          else
          {
              strncpy(text, varp, BLOCK_SIZE - off);
      
              while (size--)
              {
                  
                  blockNum=fat1[blockNum].id;
                  ptr=fs_head+blockNum*BLOCK_SIZE;
                  strncat(text,ptr,BLOCK_SIZE);
              }
          }
      
          openfile_list[fd].count=0;
      
          return 1;
      }
      

关键函数(值得学习)

/**
 * Find fcb by abspath.
 * @param path File path.
 * @return File fcb pointer.
 */
fcb *find_fcb(const char *path)
{
    char abspath[PATHLENGTH];
    get_abspath(abspath, path);
    char *token = strtok(abspath, DELIM);
    if (token == NULL)
    { //根目录
        return (fcb *)(fs_head + BLOCK_SIZE * 5);
    }
    return find_fcb_r(token, 5); //永远是5,自底向上找
}

Attempt

  1. 把csh_launch去掉

原Bug

直接进入c/cc后,能删掉/c,但/c/cc没有删除!!
同时,USEROPEN中还保留这一项

解决办法:

修改cd,使得/c也添加到用户打开表

失败了。。。。

//错误代码,思路没问题,但是strtok在分隔循环中隐式调用了,操作手段欠缺

    /**< Folder is close, open it and change current directory.
     *  应该从根向上打开,因为判断过是否在USEROPEN中,所以肯定不可能是根目录
     */
    char str[PATHLENGTH];
    memset(str, '\0', PATHLENGTH);
    strcpy(str,abspath);
    char *token=strtok(str, DELIM);


    char flexPath[PATHLENGTH] = "/";
    fcb *f;
    int flag;

    
    while(token!=NULL){
        
        strcat(flexPath, token);
        
        f = find_fcb(flexPath);
        flag = 0;
        //判断是否在USEROPEN中,不在则加入
        for (i = 0; i < MAX_OPENFILE; i++)
        {
            if (openfile_list[i].free == 0)
            {
                continue;
            }

            if (!strcmp(f->filename, openfile_list[i].open_fcb.filename) &&
                f->first == openfile_list[i].open_fcb.first)
            {
                /**< Folder is open. */
                flag = 1;
            }
        }
        
        if (flag == 0)
        {
            fd = do_open(flexPath);
        }
        token=strtok(NULL,DELIM);
        strcat(flexPath, "/");
        
    }

    
    if (fd > 0)
    {
        do_chdir(fd);
    }

rmdir: failed to remove ‘…/…/c’: Directory not empty

实际的解决办法:直接改rmdir

  1. 能直接close掉/(也许不是bug)

  2. do_format能没有将USEROPEN清空(似乎)

  3. write不行,无法输入空行

Code

main.c

/**
 * @file    main.c
 * @brief   
 * @author  zssssssk
 * @date    2021-12-19 to 2022-1-3
 */

#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "simplefs.h"


/** List of builtin commands, followed by their corresponding functions. */
char *builtin_str[] = {
        "format",
        "cd",
        "mkdir",
        "rmdir",
        "ls",
        "create",
        "rm",
        "write",
        "read",
        "exit",
        "open",
        "close",
        "pwd",
        "see"
};

int (*builtin_func[])(char **) = {
        &my_format,
        &my_cd,
        &my_mkdir,
        &my_rmdir,
        &my_ls,
        &my_create,
        &my_rm,
        &my_write,
        &my_read,
        &my_exit_sys,
        &my_open,
        &my_close,
        &my_pwd,
        &seeUSEROPEN
};

int csh_num_builtins(void) {
    return sizeof(builtin_str) / sizeof(char*);
}
/*
 * @brief Launch a program and wait for it to terminate
 * @param args Null terminated list of arguments.
 * @return Always return 1, to continue executing.
 */
int csh_launch(char **args)
{
    pid_t pid, wpid;
    int status;

    pid = fork();
    if (pid == 0) {
        // Child process
        if (execvp(args[0], args) == -1) {
            perror("csh");
        }
        exit(EXIT_FAILURE);
    } else if (pid < 0) {
        // Error forking
        perror("csh");
    } else {
        // Parent process
        do {
            wpid = waitpid(pid, &status, WUNTRACED);
        } while (!WIFEXITED(status) && !WIFSIGNALED(status));
    }

    return 1;
}

/*
 * @brief Execute shell built-in or launch program.
 * @param args Null terminated list of arguments.
 * @return 1 if the shell should continue running, 0 if it should terminate.
 */
int csh_execute(char **args)
{
    int i;
    if (args[0] == NULL) {
        // An empty command was entered
        return 1;
    }

    for (i = 0; i < csh_num_builtins(); i++) {
        if (strcmp(args[0], builtin_str[i]) == 0) {
            return (*builtin_func[i])(args);
        }
    }

    return csh_launch(args);//执行shell中的命令
}

/*
 * @brief Read a line of input from stdin.
 * @return The line from stdin.
 */
char *csh_read_line(void)
{
    char *line = NULL;
    ssize_t bufsize = 0;
    getline(&line, &bufsize, stdin);
    return line;
}

#define CSH_TOK_BUFSIZE 64  //参数个数
#define CSH_TOK_DELIM " \t\r\n\a"
/*
 * @brief Split a line into tokens.
 * @param line The line.
 * @return Null-terminated array of tokens.
 */
char **csh_split_line(char *line)
{
    int bufsize = CSH_TOK_BUFSIZE, position = 0;
    char **tokens = malloc(bufsize * sizeof(char*));
    char *token;

    if (!tokens) {
        fprintf(stderr, "csh: allocation error\n");
        exit(EXIT_FAILURE);
    }

    token = strtok(line, CSH_TOK_DELIM);
    while (token != NULL) {
        tokens[position] = token;
        position++;

        if (position >= bufsize) {
            bufsize += CSH_TOK_BUFSIZE;
            tokens = realloc(tokens, bufsize * sizeof(char*));
            if (!tokens) {
                fprintf(stderr, "csh: allocation error\n");
                exit(EXIT_FAILURE);
            }
        }

        token = strtok(NULL, CSH_TOK_DELIM);
    }
    tokens[position] = NULL;
    return tokens;
}

/*
 * @brief Loop getting input and execting it.
 */
void csh_loop(void)
{
    char *line;
    char **args;
    int status;
    FILE *fp;

    do {
        printf("\n\e[1mOS\e[0m@ZS \e[1m%s\e[0m\n", current_dir);
        printf("> \e[032m$\e[0m ");
        line = csh_read_line();
        args = csh_split_line(line);
        status = csh_execute(args);
        //
        fp=fopen(SYS_PATH,"w");
        fwrite(fs_head,DISK_SIZE,1,fp);
        fclose(fp);
        free(line);
        free(args);
    } while (status);
}

/*
 * @brief Main entry point.
 * @param argc Argument count.
 * @param argv Argument vector.
 * @return status code.
 */
int main(int argc, char **argv)
{
    start_sys();
    csh_loop();

    return EXIT_SUCCESS;
}

simplefs.c

/**
 * @file    simplefs.c
 * @brief   Definition in FAT16 file system.
 * @details Macro definitions, structs such as FCB and FAT, and some global variable.
 * @author  zssssssk
 * @date    2021-12-19 to 2022-1-3
 */

#include "simplefs.h"

/* Definition of functions */
/**
 * Start file system and initial variable.
 * @author Leslie Van
 */
int start_sys(void)
{
    fs_head = (unsigned char *)malloc(DISK_SIZE);
    memset(fs_head, 0, DISK_SIZE);
    FILE *fp;
    int i;

    if ((fp = fopen(SYS_PATH, "r")) != NULL)
    {
        fread(fs_head, DISK_SIZE, 1, fp);
        fclose(fp);
    }
    else
    {
        printf("System is not initialized, now install it and create system file.\n");
        printf(".................................................................\n");
        printf("Initialed success!\n");
        do_format();
    }

    /**< Init the first openfile entry. */
    fcb_cpy(&openfile_list[0].open_fcb, ((fcb *)(fs_head + 5 * BLOCK_SIZE)));
    strcpy(openfile_list[0].dir, ROOT);
    openfile_list[0].count = 0;
    openfile_list[0].fcb_state = 0;
    openfile_list[0].free = 1;
    curdir = 0;

    /**< Init the other openfile entry. */
    fcb *empty = (fcb *)malloc(sizeof(fcb));
    set_fcb(empty, "\0", "\0", 0, 0, 0, 0);
    for (i = 1; i < MAX_OPENFILE; i++)
    {
        fcb_cpy(&openfile_list[i].open_fcb, empty);
        strcpy(openfile_list[i].dir, "\0");
        openfile_list[i].free = 0;
        openfile_list[i].count = 0;
        openfile_list[i].fcb_state = 0;
    }

    /**< Init global variables. */
    strcpy(current_dir, openfile_list[curdir].dir);
    start = ((block0 *)fs_head)->start_block;
    free(empty);
    return 0;
}

/**
 * Entry for command "format".
 * @author Leslie Van
 */
int my_format(char **args)
{
    unsigned char *ptr;
    FILE *fp;
    int i;

    /**< Check argument count. */
    for (i = 0; args[i] != NULL; i++)
        ;
    if (i > 2)
    {
        fprintf(stderr, "format: expected argument to \"format\"\n");
        return 1;
    }

    /**< Check argument value. */
    if (args[1] != NULL)
    {
        /**< Fill with 0. */
        if (!strcmp(args[1], "-x"))
        {
            ptr = (unsigned char *)malloc(DISK_SIZE);
            memset(ptr, 0, DISK_SIZE);
            fp = fopen(SYS_PATH, "w");
            fwrite(ptr, DISK_SIZE, 1, fp);
            free(ptr);
            fclose(fp);
        }
        else
        {
            fprintf(stderr, "format: expected argument to \"format\"\n");
            return 1;
        }
    }
    do_format();

    return 1;
}

/**
 * Fast format file system.
 * Create boot block, file allocation tables and root directory.
 * @author Leslie Van
 */
int do_format(void)
{
    unsigned char *ptr = fs_head;
    int i;
    int first, second;
    FILE *fp;

    /**< Init the boot block(block0). */
    block0 *init_block = (block0 *)ptr;
    strcpy(init_block->information,
           "Disk Size = 1MB, Block Size = 1KB, Block0 in 0, FAT0/1 in 1/3, Root Directory in 5");
    init_block->root = 5;
    init_block->start_block = (unsigned char *)(init_block + BLOCK_SIZE * 7);
    ptr += BLOCK_SIZE;

    /**< Init FAT0/1. */
    set_fat(0, 0, 2);

    /**< Allocate 5 blocks to one block0(1) and two fat(2). */
    set_fat(get_free(1), 1, 0);
    set_fat(get_free(2), 2, 0);
    set_fat(get_free(2), 2, 0);

    ptr += BLOCK_SIZE * 4;

    /**< 2 blocks to root directory. */
    fcb *root = (fcb *)ptr;
    first = get_free(ROOT_BLOCK_NUM);
    set_fat(first, ROOT_BLOCK_NUM, 0);
    set_fcb(root, ".", "di", 0, first, BLOCK_SIZE * 2, 1);
    root++;
    set_fcb(root, "..", "di", 0, first, BLOCK_SIZE * 2, 1);
    root++;

    for (i = 2; i < BLOCK_SIZE * 2 / sizeof(fcb); i++, root++) //置为0,代表这里没有fcb!
    {
        root->free = 0;
    }

    /**< Write back. */
    fp = fopen(SYS_PATH, "w");
    fwrite(fs_head, DISK_SIZE, 1, fp);
    fclose(fp);
}

/**
 * Change current directory.
 * @param args
 * @return Always 1.
 */
int my_cd(char **args)
{
    int i;
    int fd;
    char abspath[PATHLENGTH];
    fcb *dir;

    /**< Check argument count. */
    for (i = 0; args[i] != NULL; i++)
        ;
    if (i != 2)
    {
        fprintf(stderr, "cd: expected argument to \"format\"\n");
        return 1;
    }

    /**< Check argument value. */
    memset(abspath, '\0', PATHLENGTH);
    get_abspath(abspath, args[1]);
    dir = find_fcb(abspath);
    if (dir == NULL || dir->attribute == 1)
    {
        fprintf(stderr, "cd: No such folder\n");
        return 1;
    }

    /**< Check if the folder fcb exist in openfile_list. */
    for (i = 0; i < MAX_OPENFILE; i++)
    {
        if (openfile_list[i].free == 0)
        {
            continue;
        }

        if (!strcmp(dir->filename, openfile_list[i].open_fcb.filename) &&
            dir->first == openfile_list[i].open_fcb.first)
        {
            /**< Folder is open. */
            do_chdir(i);
            return 1;
        }
    }

    /**< Folder is close, open it and change current directory. */
    if ((fd = do_open(abspath)) > 0)
    {
        do_chdir(fd);
    }

    return 1;
}

/**
 * Just do change directory action.
 * @param fd File descriptor of directory.
 */
void do_chdir(int fd)
{
    curdir = fd;
    memset(current_dir, '\0', sizeof(current_dir));
    strcpy(current_dir, openfile_list[curdir].dir);
}

/**
 * Display current directory.
 * @param args No argument.
 * @return Always 1.
 */
int my_pwd(char **args)
{
    /**< Check argument count. */
    if (args[1] != NULL)
    {
        fprintf(stderr, "pwd: too many arguments\n");
        return 1;
    }

    printf("%s\n", current_dir);
    return 1;
}

/**
 * Create one or many directory once.
 * Provide function to create two or more directory once.
 * If par folder not exists, print error, others will continue.
 * @param args Folder names.
 * @return Always 1.
 */
int my_mkdir(char **args)
{
    int i;
    char path[PATHLENGTH];
    char parpath[PATHLENGTH], dirname[NAMELENGTH]; // parentpath
    char *end;

    /**< Check argument count. */
    if (args[1] == NULL)
    {
        fprintf(stderr, "mkdir: missing operand\n");
        return 1;
    }

    /**< Do mkdir. */
    for (i = 1; args[i] != NULL; i++)
    {
        /**< Split path into parent folder and current child folder. */
        get_abspath(path, args[i]);
        end = strrchr(path, '/');
        if (end == path)
        {
            strcpy(parpath, "/");
            strcpy(dirname, path + 1);
        }
        else
        { //类似于 /a/b/c
            strncpy(parpath, path, end - path);
            strcpy(dirname, end + 1);
        }

        if (find_fcb(parpath) == NULL)
        {
            fprintf(stderr, "create: cannot create \'%s\': Parent folder not exists\n", parpath);
            continue;
        }
        if (find_fcb(path) != NULL)
        {
            fprintf(stderr, "create: cannot create \'%s\': Folder  exists\n", args[i]);
            continue;
        }

        do_mkdir(parpath, dirname);
    }

    return 1;
}

/**
 * Just do create directory.
 * @param parpath Par folder of the folder you want to create.
 * @param dirname Folder name you want to create.
 * @return Error with -1, else return 0.
 */
int do_mkdir(const char *parpath, const char *dirname)
{
    int second = get_free(1);
    int i, flag = 0, first = find_fcb(parpath)->first;
    fcb *dir = (fcb *)(fs_head + BLOCK_SIZE * first); //?为什么要转成fcb指针--父目录文件,里面是fcb

    /**< Check for free fcb. */
    for (i = 0; i < BLOCK_SIZE / sizeof(fcb); i++, dir++)
    {
        if (dir->free == 0)
        {
            flag = 1;
            break;
        }
    }
    if (!flag)
    {
        fprintf(stderr, "mkdir: Cannot create more file in %s\n", parpath);
        return -1;
    }

    /**< Check for free space. */
    if (second == -1)
    {
        fprintf(stderr, "mkdir: No more space\n");
        return -1;
    }
    set_fat(second, 1, 0);

    /**< Set fcb and init folder. */
    set_fcb(dir, dirname, "di", 0, second, BLOCK_SIZE, 1); //目录文件,存放fcb
    init_folder(first, second);
    return 0;
}

/**
 * Remove folder one or more once.
 * @param args Folders name you want remove.
 */
int my_rmdir(char **args)
{
    int i, j;
    fcb *dir;

    /**< Check argument count. */
    if (args[1] == NULL)
    {
        fprintf(stderr, "rmdir: missing operand\n");
        return 1;
    }

    /**< Do remove. */
    for (i = 1; args[i] != NULL; i++)
    {
        if (!strcmp(args[i], ".") || !strcmp(args[i], ".."))
        {
            fprintf(stderr, "rmdir: failed to remove %s: Invalid argument \n", args[i]);
            return 1;
        }

        if (!strcmp(args[i], "/"))
        {
            fprintf(stderr, "rmdir:  Permission denied\n");
            return 1;
        }

        dir = find_fcb(args[i]);
        if (dir == NULL)
        {
            fprintf(stderr, "rmdir: cannot remove %s: No such folder\n", args[i]);
            return 1;
        }

        if (dir->attribute == 1)
        {
            fprintf(stderr, "rmdir: cannot remove %s: It's a file\n", args[i]);
            return 1;
        }

        /**< Check if the folder fcb exist in openfile_list. */
        for (j = 0; j < MAX_OPENFILE; j++)
        {
            if (openfile_list[j].free == 0)
            {
                continue;
            }
            //缺一不可,光文件名对应上不够
            if (!strcmp(dir->filename, openfile_list[j].open_fcb.filename) &&
                dir->first == openfile_list[j].open_fcb.first)
            {
                /**< Folder is open. */
                fprintf(stderr, "rmdir: cannot remove %s: File is open\n", args[i]);
                return 1;
            }
        }

        do_rmdir(dir);
        if (openfile_list[curdir].free==0)
        {
            do_chdir(0);
        }
        
    }
    return 1;
}

/**
 * Just do remove directory.
 * 强制删除,不管是否打开,调用前应该自己加上判断
 */
void do_rmdir(fcb *dir)
{
    int i;
    int first = dir->first;
    fcb *ergfcb = (fcb *)(fs_head + BLOCK_SIZE * first);
    //tuo
    for (i = 0; i < MAX_OPENFILE; i++)
    {
        if(openfile_list[i].free==1
        &&!strcmp(openfile_list[i].open_fcb.filename,dir->filename)
        &&(openfile_list[i].open_fcb.first==dir->first)){
            openfile_list[i].free=0;
        }
        
    }
    
    dir->free = 0;
    ergfcb->free = 0;
    ergfcb++;
    ergfcb->free = 0;
    ergfcb++;
    set_fat(first, 1, 1);
    //遍历递归删除
    for (i = 2; i < BLOCK_SIZE / sizeof(fcb); i++)
    {

        if (ergfcb->free == 1)
        {
            if (ergfcb->attribute == 0)//目录
            {
                do_rmdir(ergfcb);
            }
            else
            {
                do_rm(ergfcb);
            }
        }
    }
}

/**
 * Show all thing in folder.
 * @param args Empty to show current folder. '-l' to show by a long format. 'path' to show a specific folder.
 * @return Always 1.
 */
int my_ls(char **args)
{
    //当前目录所在的第一个磁盘块
    int first = openfile_list[curdir].open_fcb.first;
    int i, mode = 'n';
    int flag[3];
    fcb *dir;

    /**< Check argument count. */
    for (i = 0; args[i] != NULL; i++)
    {
        flag[i] = 0;
    }
    if (i > 3)
    {
        fprintf(stderr, "ls: expected argument\n");
        return 1;
    }

    flag[0] = 1; // so这有啥用
    for (i = 1; args[i] != NULL; i++)
    {
        //"ls -l /"或者"ls / -l"都可以
        if (args[i][0] == '-')
        {
            flag[i] = 1;
            if (!strcmp(args[i], "-l"))
            {
                mode = 'l';
                break;
            }
            else
            {
                fprintf(stderr, "ls: wrong operand\n");
                return 1;
            }
        }
    }

    for (i = 1; args[i] != NULL; i++)
    {
        //目录
        if (flag[i] == 0)
        {
            dir = find_fcb(args[i]);
            if (dir != NULL && dir->attribute == 0)
            {
                first = dir->first;
            }
            else
            {
                fprintf(stderr, "ls: cannot access '%s': No such directory\n", args[i]);
                return 1;
            }
            break;
        }
    }

    do_ls(first, mode);

    return 1;
}

/**
 * Just do ls.
 * @param first First block of folder you want to show.
 * @param mode 'n' to normal format, and 'l' to long format.
 */
void do_ls(int first, char mode)
{
    int i, count, length = BLOCK_SIZE;
    char fullname[NAMELENGTH], date[16], time[16];
    fcb *root = (fcb *)(fs_head + BLOCK_SIZE * first);
    block0 *init_block = (block0 *)fs_head;
    if (first == init_block->root)
    {
        length = ROOT_BLOCK_NUM * BLOCK_SIZE;
    }

    if (mode == 'n')
    {
        for (i = 0, count = 1; i < length / sizeof(fcb); i++, root++)
        {
            /**< Check if the fcb is used. */
            if (root->free == 0)
            {
                continue;
            }

            if (root->attribute == 0)
            {
                printf("%s", FOLDER_COLOR);
                printf("%s\t", root->filename);
                printf("%s", DEFAULT_COLOR);
            }
            else
            {
                get_fullname(fullname, root);
                printf("%s\t", fullname);
            }
            if (count % 5 == 0)
            {
                printf("\n");
            }
            count++;
        }
    }
    else if (mode == 'l')
    {
        for (i = 0, count = 1; i < length / sizeof(fcb); i++, root++)
        {
            /**< Check if the fcb is used. */
            if (root->free == 0)
            {
                continue;
            }

            trans_date(date, root->date);
            trans_time(time, root->time);
            get_fullname(fullname, root);
            printf("%d\t%6d\t%6ld\t%s\t%s\t", root->attribute, root->first, root->length, date, time);
            if (root->attribute == 0)
            {
                printf("%s", FOLDER_COLOR);
                printf("%s\n", fullname);
                printf("%s", DEFAULT_COLOR);
            }
            else
            {
                printf("%s\n", fullname);
            }
            count++;
        }
    }
    printf("\n");
}

/**
 * Create one or more files once.
 * @param args Filename you want to create.
 * @return Always 1.
 */
int my_create(char **args)
{
    int i;
    char path[PATHLENGTH];
    char parpath[PATHLENGTH], filename[NAMELENGTH];
    char *end;

    /**< Check argument count. */
    if (args[1] == NULL)
    {
        fprintf(stderr, "create: missing operand\n");
        return 1;
    }

    memset(parpath, '\0', PATHLENGTH);
    memset(filename, '\0', NAMELENGTH);

    /**< Do create */
    for (i = 1; args[i] != NULL; i++)
    {
        /**< Split parent folder and filename. */
        get_abspath(path, args[i]);
        end = strrchr(path, '/');
        if (end == path)
        {
            strcpy(parpath, "/");
            strcpy(filename, path + 1); //地址加一,指向后一位,即path[1]
        }
        else
        {
            strncpy(parpath, path, end - path);
            strcpy(filename, end + 1);
        }

        if (find_fcb(parpath) == NULL)
        {
            fprintf(stderr, "create: cannot create \'%s\': Parent folder not exists\n", parpath);
            continue;
        }
        if (find_fcb(path) != NULL)
        {
            fprintf(stderr, "create: cannot create \'%s\': Folder or file exists\n", args[i]);
            continue;
        }

        do_create(parpath, filename);
    }

    return 1;
}

/**
 * Just do create file.
 * @param parpath File par folder.
 * @param filename File name.
 * @return Error with -1, else return 0.
 */
int do_create(const char *parpath, const char *filename)
{
    char fullname[NAMELENGTH], fname[16], exname[8];
    char *token;
    int first = get_free(1);
    int i, flag = 0;
    fcb *dir = (fcb *)(fs_head + BLOCK_SIZE * find_fcb(parpath)->first);

    /**< Check for free fcb. */
    for (i = 0; i < BLOCK_SIZE / sizeof(fcb); i++, dir++)
    {
        if (dir->free == 0)
        {
            flag = 1;
            break;
        }
    }
    if (!flag)
    {
        fprintf(stderr, "create: Cannot create more file in %s\n", parpath);
        return -1;
    }

    /**< Check for free space. */
    if (first == -1)
    {
        fprintf(stderr, "create: No more space\n");
        return -1;
    }
    set_fat(first, 1, 0);

    /**< Split name and initial variables. */
    memset(fullname, '\0', NAMELENGTH);
    memset(fname, '\0', 8);
    memset(exname, '\0', 3);
    strcpy(fullname, filename);

    token = strtok(fullname, ".");
    strncpy(fname, token, 8);
    token = strtok(NULL, ".");
    if (token != NULL)
    {
        strncpy(exname, token, 3);
    }
    /**< Set fcb. */
    set_fcb(dir, fname, exname, 1, first, 0, 1);

    return 0;
}

/**
 * Remove files.
 * @param args Filename you want to remove.
 * @return Always return 1.
 */
int my_rm(char **args)
{
    int i, j;
    fcb *file;

    /**< Check argument count. */
    if (args[1] == NULL)
    {
        fprintf(stderr, "rm: missing operand\n");
        return 1;
    }

    /**< Do remove. */
    for (i = 1; args[i] != NULL; i++)
    {
        file = find_fcb(args[i]);
        if (file == NULL)
        {
            fprintf(stderr, "rm: cannot remove %s: No such file\n", args[i]);
            return 1;
        }

        if (file->attribute == 0)
        {
            fprintf(stderr, "rm: cannot remove %s: Is a directory\n", args[i]);
            return 1;
        }

        /**< Check if the file exist in openfile_list. */
        for (j = 0; j < MAX_OPENFILE; j++)
        {
            if (openfile_list[j].free == 0)
            {
                continue;
            }

            if (!strcmp(file->filename, openfile_list[j].open_fcb.filename) &&
                file->first == openfile_list[j].open_fcb.first)
            {
                /**< Folder is open. 但是排除不掉子文件有打开的*/
                fprintf(stderr, "rm: cannot remove %s: File is open\n", args[i]);
                return 1;
            }
        }

        do_rm(file);
    }

    return 1;
}

/**
 * Just do remove file.(强制删除,若不强制,应在函数调用之前做好判断)
 * @param file FCB pointer which file you want to remove.
 */
void do_rm(fcb *file)
{
    for(int i=0;i<MAX_OPENFILE;i++){
        if (openfile_list[i].free==1&& 
            !strcmp(file->filename, openfile_list[i].open_fcb.filename)&&
            file->first == openfile_list[i].open_fcb.first)
        {
            openfile_list[i].free=0;
        }
        
    }
    int first = file->first;

    file->free = 0;
    set_fat(first, 0, 1);
}

/**
 * Open file.
 * @param args '-l' to show all files opened. 'path' to open file.
 * @return Always 1.
 */
int my_open(char **args)
{
    int i, j;
    fcb *file;
    char path[PATHLENGTH];

    /**< Check argument count. */
    if (args[1] == NULL)
    {
        fprintf(stderr, "open: missing operand\n");
        return 1;
    }
    if (args[1][0] == '-')
    {
        if (!strcmp(args[1], "-l"))
        {
            printf("fd filename exname fcb_state path\n");
            for (i = 0; i < MAX_OPENFILE; i++)
            {
                if (openfile_list[i].free == 0)
                {
                    continue;
                }

                printf("%2d %8s %6s %9d %s\n", i, openfile_list[i].open_fcb.filename,
                       openfile_list[i].open_fcb.exname,
                       openfile_list[i].fcb_state, openfile_list[i].dir);
            }
            return 1;
        }
        else
        {
            fprintf(stderr, "open: wrong argument\n");
            return 1;
        }
    }

    /**< Do open. */
    for (i = 1; args[i] != NULL; i++)
    {
        file = find_fcb(args[i]);
        if (file == NULL)
        {
            fprintf(stderr, "open: cannot open %s: No such file or folder\n", args[i]);
            return 1;
        }

        /**< Check if the file exist in openfile_list. */
        for (j = 0; j < MAX_OPENFILE; j++)
        {
            if (openfile_list[j].free == 0)
            {
                continue;
            }

            if (!strcmp(file->filename, openfile_list[j].open_fcb.filename) &&
                file->first == openfile_list[j].open_fcb.first)
            {
                /**< file is open. */
                fprintf(stderr, "open: cannot open %s: File or folder is open\n", args[i]);
                continue;
            }
        }

        do_open(get_abspath(path, args[i]));
    }
    return 1;
}

/**
 * Just do open file.
 * @param path Abspath of file you want to open..
 * @return Error with -1, else return fd;
 */
int do_open(char *path)
{
    char *str = path;
    char *end;
    char parpath[PATHLENGTH];
    int fd = get_useropen();
    if (fd == -1)
    {
        fprintf(stderr, "open: cannot open file, no more useropen entry\n");
        return -1;
    }

    fcb *file = find_fcb(path);

    fcb_cpy(&openfile_list[fd].open_fcb, file);
    openfile_list[fd].free = 1;
    openfile_list[fd].count = 0;
    memset(openfile_list[fd].dir, '\0', 80);
    strcpy(openfile_list[fd].dir, path);

    end = strrchr(path, '/');
    if (end == path)
    {
        strcpy(parpath, "/");
    }
    else
    { //类似于 /a/b/c
        strncpy(parpath, path, end - path);
    }

    return fd;
}

/**
 * Close file and save it.
 * @param args '-a' to close all file. 'path' to close file.
 * @return Always 1.
 */
int my_close(char **args)
{
    int i, j;
    fcb *file;

    /**< Check argument count. */
    if (args[1] == NULL)
    {
        fprintf(stderr, "close: missing operand\n");
        return 1;
    }
    if (args[1][0] == '-')
    {
        if (!strcmp(args[1], "-a"))
        {
            for (i = 0; i < MAX_OPENFILE; i++)
            {
                if (i == curdir)
                {
                    continue;
                }
                openfile_list[i].free = 0;
            }
            return 1;
        }
        else
        {
            fprintf(stderr, "close: wrong argument\n");
            return 1;
        }
    }

    /**< Do close. */
    for (i = 1; args[i] != NULL; i++)
    {
        file = find_fcb(args[i]);
        if (file == NULL)
        {
            fprintf(stderr, "close: cannot close %s: No such file or folder\n", args[i]);
            return 1;
        }

        /**< Check if the file exist in openfile_list. */
        for (j = 0; j < MAX_OPENFILE; j++)
        {
            if (openfile_list[j].free == 0)
            {
                continue;
            }

            if (!strcmp(file->filename, openfile_list[j].open_fcb.filename) &&
                file->first == openfile_list[j].open_fcb.first)
            {
                /**< File is open. */
                do_close(j);
            }
        }
    }
    return 1;
}

/**
 * Just do close file.
 * @param fd File descriptor.
 */
void do_close(int fd)
{
    if (openfile_list[fd].fcb_state == 1)
    {
        fcb_cpy(find_fcb(openfile_list[fd].dir), &openfile_list[fd].open_fcb); //
    }
    openfile_list[fd].free = 0;
}

/**
 * Write file.一个文件最多占20个磁盘块
 * @param args [-t|-c|-a] truncate|cover|append, 'path' path of file.
 * @return
 */
int my_write(char **args)
{
    int i, j = 0, flag = 0, len;
    int mode = 't';
    char c;
    char path[PATHLENGTH];
    char buf[BLOCK_SIZE];
    char text[WRITE_SIZE];
    fcb *file;

    /**< Check for arguments count. */
    for (i = 1; args[i] != NULL; i++)
    {
        if (args[i][0] == '-')
        {
            if (!strcmp(args[i], "-t"))
            {
                mode = 't';
            }
            else if (!strcmp(args[i], "-c"))
            {
                mode = 'c';
            }
            else if (!strcmp(args[i], "-a"))
            {
                mode = 'a';
            }
            else
            {
                fprintf(stderr, "write: wrong argument\n");
                return 1;
            }
        }
        else
        {
            flag += i;
        }
    }
    // flag为1或2
    //采用常规手段行吗?显然不行。那就必然得整花活
    if ((flag == 0) || (flag > 2) || i > 3)
    {
        fprintf(stderr, "write: wrong argument\n");
        return 1;
    }

    /**< Check if it's a file or folder. */
    strcpy(path, args[flag]);
    if ((file = find_fcb(path)) == NULL)
    {
        fprintf(stderr, "write: File not exists\n");
        return 1;
    }
    if (file->attribute == 0)
    {
        fprintf(stderr, "write: cannot access a folder\n");
        return 1;
    }

    memset(text, '\0', WRITE_SIZE);
    /**< Check if it's open. */
    for (i = 0; i < MAX_OPENFILE; i++)
    {
        if (openfile_list[i].free == 0)
        {
            continue;
        }

        if (!strcmp(file->filename, openfile_list[i].open_fcb.filename) &&
            file->first == openfile_list[i].open_fcb.first)
        {
            /**< File is open. */

            do
            {
                if (mode == 'c')
                {
                    printf("Please input location: ");
                    scanf("%d", &openfile_list[i].count);
                    getchar();
                }
                if (mode == 'a')
                {
                    openfile_list[i].count = openfile_list[i].open_fcb.length;
                }

                while (fgets(buf, WRITE_SIZE, stdin))
                {
                    if (strcmp(buf, "EOF\n") == 0)
                    {
                        break;
                    }
                    else
                    {
                        strcat(text, buf);
                    }
                }

                for (len = 0; text[len] != '\0'; len++)
                    ;

            } while (do_write(i, text, len, mode) == 0);

            return 1;
        }
    }

    fprintf(stderr, "write: file is not open\n");
    return 1;
}

/**
 * @param fd File descriptor.
 * @param wstyle Write style.
 * @return 0 for fail,1 for success
 */
int do_write(int fd, char *text, int len, int wstyle)
{

    int off, logicNum, blockNum, i, j, index, size;
    char buf[BLOCK_SIZE];
    fat *fat1 = (fat *)(fs_head + BLOCK_SIZE);
    fat *fat2 = (fat *)(fs_head + 3 * BLOCK_SIZE);
    char *ptr = fs_head;
    char *varp, *varpText;
    fcb *fcb;

    if (wstyle == 'a')
    {
        openfile_list[fd].count = openfile_list[fd].open_fcb.length;
    }

    //将读写指针转化为逻辑块块号和块内偏移off
    logicNum = openfile_list[fd].count / BLOCK_SIZE;
    off = openfile_list[fd].count % BLOCK_SIZE;

    //进一步得到其磁盘块号(注:若logicNum过大,则需报错)
    index = openfile_list[fd].open_fcb.first;
    blockNum = index;
    for (j = 0; j < logicNum; j++)
    {
        index = fat1[index].id;
        if (index == END && j != logicNum)
        {
            fprintf(stderr, "输入的读写指针太大,超过了正文内容\n");
            return 0;
        }
        blockNum = fat1[index].id;
    }

    //申请空闲缓冲区,将临时存储区中的数据转储到缓冲区,将缓冲区的内容写到相应的磁盘块中,直到写完
    //这是文件系统,所以不能定位到盘块,然后就直接在盘块上写数据了!!
    //用一个buf, 把盘块内容读取进来, 在buf里面修改, 然后再把buf内容写回到盘块里去
    size = (off + len) / BLOCK_SIZE;
    ptr += BLOCK_SIZE * blockNum;
    strcpy(buf, ptr);
    varp = buf + off;
    if (size > 19)
    {
        return 0;
    }
    if (size == 0)
    {
        strcpy(varp, text);

        //写入虚拟磁盘(注意:写入磁盘的是buf)
        strcpy(ptr, buf);
    }
    else
    {
        strncpy(varp, ptr, BLOCK_SIZE - off);
        varpText = text + BLOCK_SIZE - off;

        while (size--)
        {
            int n;
            n = get_free(1);
            //修改FAT
            fat1[blockNum].id = n;
            fat1[n].id = END;
            fat2[blockNum].id = n;
            fat2[n].id = END;
            blockNum = n; //
            //写入虚拟磁盘
            ptr = fs_head + n * BLOCK_SIZE;
            strncpy(buf, varpText, BLOCK_SIZE);
            varpText += BLOCK_SIZE;
            strcpy(ptr, buf);
        }
    }

    if (wstyle == 't')
    {
        find_fcb(openfile_list[fd].dir)->length = len;
    }
    else if (wstyle == 'c')
    {
        find_fcb(openfile_list[fd].dir)->length = openfile_list[fd].count + len;
    }
    else
    {
        find_fcb(openfile_list[fd].dir)->length += len;
    }

    fcb_cpy(&openfile_list[fd].open_fcb, find_fcb(openfile_list[fd].dir));
    openfile_list[fd].count = 0;
    openfile_list[fd].fcb_state = 1;

    return 1;
}

/**
 * Read file.
 * @param args [-s|-a] select|all, 'path' path of file.
 * @return Bytes read.
 */
int my_read(char **args)
{
    int i, flag = 0;
    int length;
    int mode = 'a';
    char path[PATHLENGTH];
    char str[WRITE_SIZE];
    fcb *file;

    /**< Check for arguments count. */
    for (i = 1; args[i] != NULL; i++)
    {
        if (args[i][0] == '-')
        {
            if (!strcmp(args[i], "-s"))
            {
                mode = 's';
            }
            else if (!strcmp(args[i], "-a"))
            {
                mode = 'a';
            }
            else
            {
                fprintf(stderr, "read: wrong argument\n");
                return 1;
            }
        }
        else
        {
            flag += 1 << i;
        }
    }
    if ((flag == 0) || (flag > 4) || i > 3)
    {
        fprintf(stderr, "read: wrong argument\n");
        return 1;
    }

    /**< Check if it's a file or folder. */
    strcpy(path, args[flag >> 1]);
    if ((file = find_fcb(path)) == NULL)
    {
        fprintf(stderr, "read: File not exists\n");
        return 1;
    }
    if (file->attribute == 0)
    {
        fprintf(stderr, "read: cannot access a folder\n");
        return 1;
    }

    memset(str, '\0', WRITE_SIZE);
    /**< Check if it's open. */
    for (i = 0; i < MAX_OPENFILE; i++)
    {
        if (openfile_list[i].free == 0)
        {
            continue;
        }

        if (!strcmp(file->filename, openfile_list[i].open_fcb.filename) &&
            file->first == openfile_list[i].open_fcb.first)
        {
            /**< File is open. */
            if (mode == 'a')
            {
                openfile_list[i].count = 0;
                length = UINT16_MAX;
            }
            if (mode == 's')
            {
                printf("Please input location: ");
                scanf("%d", &openfile_list[i].count);
                printf("Please input length: ");
                scanf("%d", &length);
                printf("-----------------------\n");
            }
            do_read(i, length, str);

            fputs(str, stdout);
            return 1;
        }
    }

    fprintf(stderr, "read: file is not open\n");
    return 1;
}

/**
 * @param fd File descriptor.
 * @param len Length of text.
 * @param text Read file into text.
 * @return
 */
int do_read(int fd, int len, char *text)
{
    memset(text, '\0', BLOCK_SIZE * 20);

    if (len <= 0) //想要读取0个字符
    {
        return 0;
    }

    fat *fat1 = (fat *)(fs_head + BLOCK_SIZE); // FAT1表
    int location = 0;                          // text的写入位置
    int length;
    int count = openfile_list[fd].count; //读写指针位置
    //排除了id出现end的情况
    if ((openfile_list[fd].open_fcb.length - count) >= len) //可以读取的字符多于想要读取的字符
    {
        length = len; //想要读取的字符
    }
    else
    {
        length = openfile_list[fd].open_fcb.length - count; //只能读取这些字符
    }

    int off, logicNum, blockNum, i, j, index, size;
    char buf[BLOCK_SIZE];

    char *ptr = fs_head;
    char *varp, *varpText;
    fcb *fcb;

    //将读写指针转化为逻辑块块号和块内偏移off
    logicNum = openfile_list[fd].count / BLOCK_SIZE;
    off = openfile_list[fd].count % BLOCK_SIZE;

    //进一步得到其磁盘块号(注:若logicNum过大,则需报错)
    index = openfile_list[fd].open_fcb.first;
    blockNum = index;
    for (j = 0; j < logicNum; j++)
    {
        index = fat1[index].id;
        if (index == END && j != logicNum)
        {
            fprintf(stderr, "输入的读写指针太大,超过了正文内容\n");
            return 0;
        }
        blockNum = fat1[index].id;
    }

    //将该磁盘块整块内容读入缓冲区,再将从off开始的缓冲区中的内容复制到text[]中

    size = (off + length) / BLOCK_SIZE;
    ptr += BLOCK_SIZE * blockNum;
    strncpy(buf, ptr, BLOCK_SIZE);
    varp = buf + off;

    if (size == 0)
    {
        strcpy(text, varp);
    }
    else
    {
        strncpy(text, varp, BLOCK_SIZE - off);

        while (size--)
        {

            blockNum = fat1[blockNum].id;
            ptr = fs_head + blockNum * BLOCK_SIZE;
            strncat(text, ptr, BLOCK_SIZE);
        }
    }

    openfile_list[fd].count = 0;

    return 1;
}

/**
 * Exit system, save changes.
 * @author
 */
int my_exit_sys(void)
{
    int i;
    FILE *fp;

    for (i = 0; i < MAX_OPENFILE; i++)
    {
        do_close(i);
    }

    fp = fopen(SYS_PATH, "w");
    fwrite(fs_head, DISK_SIZE, 1, fp);
    free(fs_head);
    fclose(fp);
    return 0;
}

/**
 * Detect free blocks in FAT.
 * @param count Count of needed blocks.
 * @return 0 without enough space, else return the first block number.
 * @author Leslie Van
 */
int get_free(int count)
{
    unsigned char *ptr = fs_head;
    fat *fat0 = (fat *)(ptr + BLOCK_SIZE);
    int i, j, flag = 0;
    int fat[BLOCK_NUM];

    /** Copy FAT. */
    for (i = 0; i < BLOCK_NUM; i++, fat0++)
    {
        fat[i] = fat0->id;
    }

    /** Find a continuous space. */
    for (i = 0; i < BLOCK_NUM - count; i++)
    {
        for (j = i; j < i + count; j++)
        {
            if (fat[j] > 0)
            {
                flag = 1;
                break;
            }
        }
        if (flag)
        {
            flag = 0;
            i = j;
        }
        else
        {
            return i;
        }
    }

    return -1;
}

/**
 * Change value of FAT.
 * @param first The starting block number.
 * @param length The blocks count.
 * @param mode 0 to allocate, 1 to reclaim and 2 to format.
 * @author Leslie Van
 */
int set_fat(unsigned short first, unsigned short length, int mode)
{
    fat *flag = (fat *)(fs_head + BLOCK_SIZE);
    fat *fat0 = (fat *)(fs_head + BLOCK_SIZE);
    fat *fat1 = (fat *)(fs_head + BLOCK_SIZE * 3);
    int i;
    int offset;

    for (i = 0; i < first; i++, fat0++, fat1++)
        ;
    // fat0为当前磁盘块所在的fat的地方
    if (mode == 1)
    {
        /**< Reclaim space. */
        while (fat0->id != END)
        {
            offset = fat0->id - (fat0 - flag) / sizeof(fat);
            fat0->id = FREE;
            fat1->id = FREE;
            fat0 += offset;
            fat1 += offset;
        }
        fat0->id = FREE;
        fat1->id = FREE;
    }
    else if (mode == 2)
    {
        /**< Format FAT */
        for (i = 0; i < BLOCK_NUM; i++, fat0++, fat1++)
        {
            fat0->id = FREE;
            fat1->id = FREE;
        }
    }
    else
    {
        /**< Allocate consecutive space. */
        for (; i < first + length - 1; i++, fat0++, fat1++)
        {
            fat0->id = first + 1;
            fat1->id = first + 1;
        }
        fat0->id = END;
        fat1->id = END;
    }

    return 0;
}

/**
 * Set fcb attribute.
 * @param f The pointer of fcb.
 * @param filename FCB filename.
 * @param exname FCB file extensions name.
 * @param attr FCB file attribute.
 * @param first FCB starting block number.
 * @param length FCB file length.
 * @param ffree 1 when file occupied, else 0.
 * @author Leslie Van
 */
int set_fcb(fcb *f, const char *filename, const char *exname, unsigned char attr, unsigned short first,
            unsigned long length, char ffree)
{
    time_t *now = (time_t *)malloc(sizeof(time_t));
    struct tm *timeinfo;
    time(now);
    timeinfo = localtime(now);

    memset(f->filename, 0, 8);
    memset(f->exname, 0, 3);
    strncpy(f->filename, filename, 7);
    strncpy(f->exname, exname, 2);
    f->attribute = attr;
    f->time = get_time(timeinfo);
    f->date = get_date(timeinfo);
    f->first = first;
    f->length = length;
    f->free = ffree;

    free(now);
    return 0;
}

/**
 * Translate ISO time to short time.
 * @param timeinfo Current time structure.
 * @return Time number after translation.
 */
unsigned short get_time(struct tm *timeinfo)
{
    int hour, min, sec;
    unsigned short result;

    hour = timeinfo->tm_hour;
    min = timeinfo->tm_min;
    sec = timeinfo->tm_sec;
    result = (hour << 11) + (min << 5) + (sec >> 1);

    return result;
}

/**
 * Translate ISO date to short date.
 * @param timeinfo local
 * @return Date number after translation.
 */
unsigned short get_date(struct tm *timeinfo)
{
    int year, mon, day;
    unsigned short result;

    year = timeinfo->tm_year;
    mon = timeinfo->tm_mon;
    day = timeinfo->tm_mday;
    result = (year << 9) + (mon << 5) + day;

    return result;
}

/**
 * Copy a fcb.
 * @param dest Destination fcb.
 * @param src Source fcb.
 * @return Destination fcb pointer.
 */
fcb *fcb_cpy(fcb *dest, fcb *src)
{
    memset(dest->filename, '\0', 8);
    memset(dest->exname, '\0', 3);

    strcpy(dest->filename, src->filename);
    strcpy(dest->exname, src->exname);
    dest->attribute = src->attribute;
    dest->time = src->time;
    dest->date = src->date;
    dest->first = src->first;
    dest->length = src->length;
    dest->free = src->free;

    return dest;
}

/**
 * Translate relative path to absolute path
 * @param abspath Absolute path.
 * @param relpath Relative path.
 * @return Absolute path.
 */
char *get_abspath(char *abspath, const char *relpath)
{
    /**< If relpath is abspath. */
    if (!strcmp(relpath, DELIM) || relpath[0] == '/')
    {
        strcpy(abspath, relpath);
        return abspath;
    }

    char str[PATHLENGTH];
    char *token, *end;

    memset(abspath, '\0', PATHLENGTH);
    abspath[0] = '/';
    strcpy(abspath, current_dir);

    strcpy(str, relpath);
    token = strtok(str, DELIM);

    do
    {
        if (!strcmp(token, "."))
        {
            continue;
        }
        if (!strcmp(token, ".."))
        {
            if (!strcmp(abspath, ROOT))
            {
                continue;
            }
            else
            {
                end = strrchr(abspath, '/');
                if (end == abspath)
                {
                    strcpy(abspath, ROOT);
                    continue;
                }
                memset(end, '\0', 1);
                continue;
            }
        }
        if (strcmp(abspath, "/"))
        {
            strcat(abspath, DELIM);
        }
        strcat(abspath, token);
    } while ((token = strtok(NULL, DELIM)) != NULL);

    return abspath;
}

/**
 * Find fcb by abspath.
 * @param path File path.
 * @return File fcb pointer.
 */
fcb *find_fcb(const char *path)
{
    char abspath[PATHLENGTH];
    get_abspath(abspath, path);
    char *token = strtok(abspath, DELIM);
    if (token == NULL)
    { //根目录
        return (fcb *)(fs_head + BLOCK_SIZE * 5);
    }
    return find_fcb_r(token, 5); //永远是5
}

/**
 * A procedure to find fcb recursively.
 * @param token File name in (ptr).
 * @param first Par fcb pointer.
 * @return FCB pointer of token.
 */
fcb *find_fcb_r(char *token, int first)
{
    int i, length = BLOCK_SIZE; //普通目录最多BLOCK_SIZE个字节
    char fullname[NAMELENGTH] = "\0";
    fcb *root = (fcb *)(BLOCK_SIZE * first + fs_head);
    fcb *dir;
    block0 *init_block = (block0 *)fs_head;
    if (first == init_block->root)
    {                                         // init_block->root==5
        length = ROOT_BLOCK_NUM * BLOCK_SIZE; //根目录有BLOCK_SIZE*ROOT_BLOCK_NUM个字节
    }

    for (i = 0, dir = root; i < length / sizeof(fcb); i++, dir++)
    {
        if (dir->free == 0)
        {
            continue;
        }
        //找到一个已分配的目录项且该目录项的名字与之对应(缺陷:不能递归创建目录)
        get_fullname(fullname, dir);
        if (!strcmp(token, fullname))
        {
            token = strtok(NULL, DELIM);
            if (token == NULL)
            {
                return dir;
            }
            return find_fcb_r(token, dir->first); //到下一个块上找下一级目录
        }
    }
    return NULL;
}

/**
 * Get a empty useropen entry.
 * @return If empty useropen exist return entry index, else return -1;
 */
int get_useropen()
{
    int i;

    for (i = 0; i < MAX_OPENFILE; i++)
    {
        if (openfile_list[i].free == 0)
        {
            return i;
        }
    }

    return -1;
}

/**
 * Init a folder.
 * @param first Parent folder block num.
 * @param second Current folder block num.
 */
void init_folder(int first, int second)
{
    int i;
    fcb *par = (fcb *)(fs_head + BLOCK_SIZE * first);
    fcb *cur = (fcb *)(fs_head + BLOCK_SIZE * second);

    set_fcb(cur, ".", "di", 0, second, BLOCK_SIZE, 1);
    cur++;
    set_fcb(cur, "..", "di", 0, first, par->length, 1);
    cur++;
    for (i = 2; i < BLOCK_SIZE / sizeof(fcb); i++, cur++)
    { //感觉没什么必要
        cur->free = 0;
    }
}

/**
 * Get file full name.
 * @param fullname A char array[NAMELENGTH].
 * @param fcb1 Source fcb pointer.
 */
void get_fullname(char *fullname, fcb *fcb1)
{
    memset(fullname, '\0', NAMELENGTH);

    strcat(fullname, fcb1->filename);
    if (fcb1->attribute == 1 && fcb1->exname[0] != '\0')
    {
        strncat(fullname, ".", 2);
        strncat(fullname, fcb1->exname, 3);
    }
}

/**
 * Translate unsigned short number to date string.
 * @param sdate Date to string.
 * @param date A number to represent date.
 * @return sdate.
 */
char *trans_date(char *sdate, unsigned short date)
{
    int year, month, day;
    memset(sdate, '\0', 16);

    year = date & 0xfe00;
    month = date & 0x01e0;
    day = date & 0x001f;
    sprintf(sdate, "%04d-%02d-%02d", (year >> 9) + 1900, (month >> 5) + 1, day);
    return sdate;
}

/**
 * Translate unsigned short number to time string.
 * @param stime Time to string.
 * @param time A number to represent time.
 * @return stime.
 */
char *trans_time(char *stime, unsigned short time)
{
    int hour, min, sec;
    memset(stime, '\0', 16);

    hour = time & 0xfc1f;
    min = time & 0x03e0;
    sec = time & 0x001f;
    sprintf(stime, "%02d:%02d:%02d", hour >> 11, min >> 5, sec << 1);
    return stime;
}

int seeUSEROPEN()
{
    for (size_t i = 0; i < MAX_OPENFILE; i++)
    {
        printf("%s  %d  %ld\n", openfile_list[i].dir, openfile_list[i].free, openfile_list[i].open_fcb.length);
    }
    return 1;
}

simplefs.h

/**
 * @file    simplefs.h
 * @brief   Setup in FAT16 file system.
 * @details Macro definitions, structs such as FCB and FAT, and some global variable.
 * @author  zssssssk
 * @date    2021-12-19 to 2022-1-3
 */

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


#ifndef OPERATOR_SYSTEM_EXP4_SIMPLEFS_H
#define OPERATOR_SYSTEM_EXP4_SIMPLEFS_H
#define BLOCK_SIZE      1024
#define BLOCK_NUM       1024
#define DISK_SIZE       1048576
#define SYS_PATH        "./fsfile"
#define END             0xffff  /**< End of the block, a flag in FAT. */
#define FREE            0x0000  /**< Unused block, a flag in FAT. */
#define ROOT            "/"     /**< Root directory name.*/
#define ROOT_BLOCK_NUM  2       /**< Block of the initial root directory. */
#define MAX_OPENFILE    10      /**< Max files to open at the same time. */
#define NAMELENGTH      32
#define PATHLENGTH      128
#define DELIM           "/"
#define FOLDER_COLOR    "\e[1;32m"
#define DEFAULT_COLOR   "\e[0m"
#define WRITE_SIZE      1000 * BLOCK_SIZE

/**
 * @brief Store virtual disk information.
 * Contain info like block size, block count and some other information about disk.
 */
typedef struct BLOCK0 {
    char information[200];
    unsigned short root;        /**< Block number of the root directory. */
    unsigned char *start_block; /**< Location of the first data block. */
} block0;

/**
 * @brief File control block.
 * Store file info both the description and current state.
 */
typedef struct FCB {
    char filename[8];
    char exname[3];
    unsigned char attribute;    /**< 0: directory or 1: file. */
    unsigned char reserve[10];
    unsigned short time;        /**< File create time. */
    unsigned short date;        /**< File create date. */
    unsigned short first;       /**< First block num of the file. */
    unsigned long length;       /**< Block count of the file. */
    char free;
} fcb;

/**
 * @brief File allocation table.
 * Record the next block num of file.
 * When value is 0xffff, this block is the last block of the file.
 */
typedef struct FAT {
    unsigned short id;
} fat;

/**
 * @brief A file entry opened by user.
 * Contain file control block and current state.
 */
typedef struct USEROPEN {
    /** FCB. */
    fcb open_fcb;
    /** Current state. */
    char dir[80];       //like /a/b
    int count;
    char fcb_state;     //文件的fcb是否被修改,如果修改了,则置为1,否则置为0
    char free;
} useropen;

/** Global variables. */
unsigned char *fs_head;         /**< Initial address of the virtual disk. */
useropen openfile_list[MAX_OPENFILE];   /**< File array opened by user. */
int curdir;                     /**< File descriptor of current directory. */
char current_dir[80];           /**< Current directory name. like /a/b */
unsigned char *start;           /**< Location of the first data block. */

/** Declaration of functions */
int start_sys(void);

int my_format(char **args);

int do_format(void);

int my_cd(char **args);

void do_chdir(int fd);

int my_pwd(char **args);

int my_mkdir(char **args);

int do_mkdir(const char *parpath, const char *dirname);

int my_rmdir(char **args);

void do_rmdir(fcb *dir);

int my_ls(char **args);

void do_ls(int first, char mode);

int my_create(char **args);

int do_create(const char *parpath, const char *filename);

int my_rm(char **args);

void do_rm(fcb *file);

int my_open(char **args);

int do_open(char *path);

int my_close(char **args);

void do_close(int fd);

int my_write(char **args);

int do_write(int fd, char *content, int len, int wstyle);

int my_read(char **args);

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

int my_exit_sys();

int get_free(int count);

int set_fat(unsigned short first, unsigned short length, int mode);

int set_fcb(fcb *f, const char *filename, const char *exname, unsigned char attr, unsigned short first,
            unsigned long length,
            char ffree);

unsigned short get_time(struct tm *timeinfo);

unsigned short get_date(struct tm *timeinfo);

fcb * fcb_cpy(fcb *dest, fcb *src);

char * get_abspath(char *abspath, const char *relpath);

int get_useropen();

fcb *find_fcb(const char *path);

fcb *find_fcb_r(char *token, int root);

void init_folder(int first, int second);

void get_fullname(char *fullname, fcb *fcb1);

char *trans_date(char *sdate, unsigned short date);

char *trans_time(char *stime, unsigned short time);

int seeUSEROPEN();

#endif //OPERATOR_SYSTEM_EXP4_SIMPLEFS_H

CMakeLists.txt

cmake_minimum_required(VERSION 3.12)
project(Operator_System_Exp5 C)

set(CMAKE_C_STANDARD 11)

add_executable(simplefs main.c simplefs.h simplefs.c)
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值