Intro
基本框架借鉴https://hub.fastgit.org/leslievan/Operator_System/tree/master/Operator_System_Lab5
写得非常好,有很多值得学习的地方,但bug有点多,不忍学弟学妹们再受我当时的苦,故将魔改版本公之于众。
我的主要修改在do_write
,do_read
,do_rmdir
Idea
- 初始化虚拟磁盘空间 or 从文件中读取虚拟磁盘内容
- 读取一行输入内容
- 将内容划分为tokens
- 根据tokens去执行相应的命令
- 循环2,3,4,直到输入退出命令
Conceive
-
普通目录占一个磁盘块,根目录占两个磁盘块
-
看一个文件有无fcb,看free的值即可
-
文件描述符fd的取值为从0到MAX_OPENFILE-1
-
用户打开文件表 USEROPEN
- 进入目录即打开,退出目录不关闭
-
初始化还是很有必要将一个磁盘块的fcb的free字段的位置初始化为0,因为要遍历
-
关于用户打开文件表USEROPEN
- 其中的
fcb open_fcb
字段是copy过来的 - 导致修改文件后,有关
open_fcb
的字段的修改都应该改两个地方
- 其中的
-
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; }
-
-
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); } } } }
-
-
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
-
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
-
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; }
-
-
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
- 把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
-
能直接close掉/(也许不是bug)
-
do_format能没有将USEROPEN清空(似乎)
-
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)