初音ミクの消失

FAT16文件系统

字数统计: 5.6k阅读时长: 27 min
2019/02/04 Share

参考资料:

https://blog.csdn.net/sikuon/article/details/75939434

https://blog.csdn.net/cassie_huang/article/details/62227023

https://blog.csdn.net/yeruby/article/details/41978199

最详细:https://blog.csdn.net/mj5742356/article/details/20125511

长短文件名 https://wenku.baidu.com/view/d729d62a680203d8cf2f2474.html

FAT16

一、关于需要掌握的基础知识

1.文件读写与文件指针的移动操作

2.十六进制计算

3.链表

4.小端序

二、FAT16文件系统的结构

1.FAT16第一个扇区是DBR,是对整个分区的格式及参数进行说明的部分,从中可以读取到FAT所占扇区数、每簇扇区数、每个扇区的字节数、保留扇区数……

系统通过这些参数可以计算出FAT1、FAT2、根目录、数据区的位置,其中,保留扇区之后的即为FAT区,FAT区由两个完全相同的FAT1、FAT2构成,FAT2作为FAT1的一个备份,保障数据安全。

2.FAT1中存放了每个簇之间的对应关系以及自身的属性。

FAT2是FAT1的一个备份,以便于磁盘意外损坏的恢复

通过查表我们可以了解到这个簇到底是未被使用还是已被使用,以及这个簇后面还有没有链接到下一个簇。从而将连续的数据离散储存。

3.FAT2后的是根目录,根目录占用32个扇区,当中储存了根节点下面所拥有的所有文件和文件夹(文件夹可以当作一种特殊的文件),每个文件占用32个字节(当文件名过长的时候可能会占用更多空间),这32个字节分别对应的是这个文件对应的各种属性以及簇号。

我们可以通过寻找该簇来读取该文件的具体内容,如果是文件夹,这个簇中储存的是文件夹内的所有子目录元素(与根目录的作用类似)。

文件夹创建的时候会生成两个32字节的项

一个是 “.” ,其中储存的是文件夹自身的信息

另一个是 “..”, 储存了这个文件夹的父目录的信息(方便返回上一级)

三、删除文件(夹)的过程

找到描述该文件的那个32字节的项,将偏移值为0h的数据改为 E5,以说明该数据被删除。如果是文件夹的话,还应递归删除其目录下的所有文件。

四、创建文件(夹)的过程

在当前目录的簇下寻找一个空位置(32字节) 将新文件(夹)的信息写入,并且在磁盘中寻找一个未使用的新簇,将其分配给创建的文件(夹)

五、读取文件(夹)的过程

读取文件(夹)所对应的32字节数据,从而读取到整个文件的信息尤其是簇号,根据这个簇号去磁盘中的相应区域读取这个文件(夹)所储存的信息(文件)

六、写入文件的过程

类似于读取文件

七、遇到当前簇满的处理措施

在磁盘中寻找一个新簇,将原来的簇指向新簇,从而将两个簇连接在一起,形成一个连续的数据区域

八、注意事项

1.文件系统中的数据以小端序保存(数字的低位 存在 地址的低位上,这与普通人的思维有所区别)

e.g. 数据1234h, 用两个字节进行存储,则其在内存中的存储形式为: 34 12

2.DBR(DOS boot record) 占一个扇区,里面储存了整个文件系统的各种配置信息(如每个扇区的字节数,每个簇的扇区数,FAT区的个数,每个FAT所占扇区数等等)

3.FAT表中每个记录的位数成为FAT大小(fat16就代表一个簇的簇号所占空间为十六位,两个字节),这意味着一个fat16磁盘最多能储存20000h个簇,也就限制了fat16的一个分区的大小

八、相关图表

FAT16记录表(可先记录一下自己磁盘的如下信息,方便之后的分析,以下只是我自己SD卡的信息)

对于FAT16的一个分区:

每簇占多少个扇区:64

DBR(保留扇区数)0h

FAT1(FAT扇区数):标记某个簇的状态(已使用 未使用 坏簇),以两个字节为一个单位。 1000h

FAT2(FAT扇区数):与FAT1完全一样,作为FAT1的备份 1D800h

根目录(32个扇区) ~ 3C000h

数据区(第二个簇) 40000h

第五个簇58000h

第六个簇60000h

DBR数据对照表

DBR数据对照表

注意:1Ch: 20为文件 10为文件夹

文件信息对照表

文件信息对照表1
文件信息对照表2

系统时间与文件系统中的时间的换算

1.0x160x17中的时间=小时2048+分钟32+秒/2。
得出的结果换算成16进制填入即可。
也就是:
0x16字节的0
4位是以2秒为单位的量值;
0x16字节的57位和0x17字节的02位是分钟;
0x17字节的3~7位是小时。

2.0x180x19中的日期=(年份-1980)512+月份32+日。
得出的结果换算成16进制填入即可。
也就是:
0x18字节0
4位是日期数;
0x18字节57位和0x19字节0位是月份;
0x19字节的1
7位为年号,
原定义中0119分别代表19802099,
目前高版本的Windows允许取0~127,即年号最大可以到2107年。

FAT16簇空间大小与分区空间大小的关系

表9 FAT16分区大小与对因簇大小
分区空间大小 每个簇的扇区 簇空间大小
0MB-32MB 1 512个字节
33MB-64MB 2 1k
65MB-128MB 4 2k
129MB-225MB 8 4k
256MB-511MB 16 8k
512MB-1023MB 32 16k
1024MB-2047MB 64 32k
2048MB-4095MB 128 64k

文件操作函数:

fseek函数可以把位置指针调整到文件任意位置

fseek函数里需要提供一个基准位置和目标位置到基准位置间的距离,计算机根据他们计算出目标位置
SEEK_SET 0 把文件头作为基准位置
SEEK_CUR 1 把当前位置作为基准位置

SEEK_END 2 把文件尾作为基准位置

以下为完整代码:
程序结构设计有一点毛病,本来簇在读入程序之后可以切割成若干个32字节的块,方便操作。结果我把一个簇当成字符串处理了,所以写了很多个字符串操作函数

#include<stdio.h>
#include<time.h>
#include<string.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define ISCMD(t) strcmp(a, t) == 0
#define COMMAND_MAX 100
#define SECTORS_PER_CLUST 1//每簇扇区数
#define BYTES_PER_SECTOR 512//每扇区字节数
#define SECTORS_PER_FAT 128//每FAT扇区数
#define RESERVED_SECTORS 1//保留扇区数
#define SECTORS_ROOT SECTORS_PER_CLUST//根目录的扇区数
#define CLUST_MAX_COUNT (SECTORS_PER_FAT * BYTES_PER_SECTOR >> 1) //簇的总个数
#define ROOT (RESERVED_SECTORS + SECTORS_PER_FAT * 2) * BYTES_PER_SECTOR//根目录的位置
#define CLUST_SECOND ROOT + SECTORS_ROOT * BYTES_PER_SECTOR//第二个簇的位置
FILE *fp;

struct clu{//一个簇
int status;
char byte[BYTES_PER_SECTOR * SECTORS_PER_CLUST + 10]; //簇里的扇区
}clust[CLUST_MAX_COUNT];

struct fi{//一个文件
bool dir;
char name[2][20];
int time;//修改时间
int date;//修改日期
int clust;//首簇号
long long len;//文件大小
fi *next;//下一个文件
}file[1 + CLUST_MAX_COUNT * SECTORS_PER_CLUST * BYTES_PER_SECTOR / 32];
int FileCnt, Clust_now;//当前文件的总个数与当前的簇
fi *HEAD;//头指针
char dir[10000][6];//存放所有文件夹名字的数组
int dir_now;

void Manu_Print();//打印帮助菜单
bool Command_Read();//读取命令

bool Data_ReadOrWrite(const int);//文件操作,读、写、关闭
void Data_Initialize();//初始化数据文件
void Data_Create_New();//创建新的类FAT16数据文件
void Data_Read();//将数据读入内存
void Data_Save();//保存文件
void Data_DBR_Print();//创建DBR
void Data_Add_A_File(fi *, int, int);//将clust中指定位置的字符串填充为新文件的信息

fi *Filelist_Search_File(char *, bool);//在已经读入的文件中寻找对应名称的文件
void Filelist_Update(int);//更新文件列表
void Filelist_Destroy();//文件列表清空
void Filelist_Add_File(fi *);//在文件列表中加入一个元素
void Filelist_Bubble_Sort();//用冒泡排序把文件列表按照字典序排序
void Filelist_Print();//打印出所有的文件

void File_Printf_Number(long long, int);//以小端序方式打印 (对文件)
long long File_Scanf_Number(int);//读取n个字节的小端序数字
void File_Printf_String(char *,int);//以字符串向文件输出信息
void File_Printf_32Bytes(fi *);//向文件中输出文件/文件夹的信息

void Str_Printf_Number(char *, long long, int);//以小端序方式打印 (对字符串)
long long Str_Scanf_Number(char *, int);//读取n个字节的小端序数字
void Str_Scanf_String(char *, int, char *);//以二进制格式读取字符串的内容
bool Str_Read_32Bytes(char *, fi *);//读取一个文件/文件夹的信息
void Str_Printf_32Bytes(char *, fi *);//向字符串中输出文件/文件夹的信息
void Str_Cut_Name_Into_Two(char *, char*, char*);//将一个文件名分隔为两部分

int Clust_Judge_Status(int); //判断该簇的状态
void Time_Print(int t);//输出时间
void Date_Print(int t);//输出日期
int Time_Get();//获取当前时间
int Date_Get();//获取当前日期

void Dir_Change(char *, bool);//更改目录位置
void Dir_Print();//打印当前目录
int Dir_Find_Empty(int);//在一个簇里面寻找空的位置
void Make_a_New_File(fi *, bool);//返回一个创建好的空文件
void Clust_Clear_File(int);//清理某个簇内的文件的所有信息
void Clust_Clear_Dir(int);//清理某个簇内的所有文件夹

int main(){
Data_Initialize();
Manu_Print();
bool FLAG = FALSE;
char rt[6] = "root:";
dir_now = 0;
strcpy(dir[dir_now], rt);
do{
Dir_Print();
printf(">");
FLAG = Command_Read();
}while(FLAG);
return 0;


}

bool Command_Read(){
char str[COMMAND_MAX], a[COMMAND_MAX], b[COMMAND_MAX];
gets(str);
sscanf(str, "%s%s", a, b);

if(str[0] == 0) return TRUE;

Filelist_Destroy();
Filelist_Update(Clust_now);

Filelist_Bubble_Sort();
if(ISCMD("ls") || ISCMD("dir")){
printf("\n\n");
Dir_Print();
printf(" contains the following files or directories:\n");
Filelist_Print();

}else if(ISCMD("cd")){
char temp[20];
strcpy(temp, b);
fi *t = Filelist_Search_File(temp, TRUE);
if(t != NULL && t->dir == TRUE){
Clust_now = t->clust;
if(strcmp(b, "..") == 0){
if(dir_now != 0)
Dir_Change(b, FALSE);//回退
}
else if(strcmp(b, ".") != 0)
Dir_Change(b, TRUE);
}else
printf("Invalid directory!\n\n");

}else if(ISCMD("mkdir")){
fi *t = Filelist_Search_File(b, TRUE);
if(t != NULL){//如果这个文件夹已经存在,就返回错误
printf("Directory already exists!\n");
return TRUE;
}else if(strlen(b) >= 8){
printf("Name too long!\n");
return TRUE;
}

int Clust_temp = Clust_now;
int k = Dir_Find_Empty(Clust_temp);
while(k == -1){
if(Clust_Judge_Status(clust[Clust_temp].status) == 0)
Clust_temp = clust[Clust_temp].status;//如果这个链表的其他簇还有空位
else{
int Clust_new = 2;//这个链表中已经不存在空位了,要找一个空簇,从第二个簇开始找
while(clust[Clust_new].status != 0) Clust_new++;
clust[Clust_temp].status = Clust_new;
clust[Clust_new].status = 0xFFFF;
Clust_temp = Clust_new;
}
k = Dir_Find_Empty(Clust_temp);//重新寻找空位
}

fi *F = (fi*) malloc(sizeof(fi));//填充基本信息
Make_a_New_File(F, TRUE);
clust[Clust_temp].status = 0xFFFF;
strcpy(F->name[0], b);//修改文件夹名称
Data_Add_A_File(F, Clust_temp, k);//把文件写入簇

fi *temp = (fi*) malloc(sizeof(fi));//再拿一个临时变量
Make_a_New_File(temp, TRUE);

char *p = clust[F->clust].byte;
strcpy(temp->name[0], ".");
temp->clust = F->clust; // .文件夹用于存放本文件夹的信息
Str_Printf_32Bytes(p, temp);

strcpy(temp->name[0], "..");
temp->clust = Clust_now; // ..文件夹用于存放上一个文件夹的信息
Str_Printf_32Bytes(p + 32, temp);

free(F);
free(temp);


}else if(ISCMD("create")){
//类似于新建文件夹,但是不用新建 .和..
fi *t = Filelist_Search_File(b, FALSE);
if(t != NULL){//如果这个文件夹已经存在,就返回错误
printf("Directory already exists!\n");
return TRUE;
}

char temp[20];//创建一个临时字符串
strcpy(temp, b);
Str_Cut_Name_Into_Two(temp, a, b);

if(strlen(a) >= 8 || strlen(b) >=3){
printf("Name too long!\n");
return TRUE;
}

int Clust_temp = Clust_now;
int k = Dir_Find_Empty(Clust_temp);
while(k == -1){
if(Clust_Judge_Status(clust[Clust_temp].status) == 0)
Clust_temp = clust[Clust_temp].status;//如果这个链表的其他簇还有空位
else{
int Clust_new = 2;//这个链表中已经不存在空位了,要找一个空簇,从第二个簇开始找
while(clust[Clust_new].status != 0) Clust_new++;
clust[Clust_temp].status = Clust_new;
clust[Clust_new].status = 0xFFFF;
Clust_temp = Clust_new;
}
k = Dir_Find_Empty(Clust_temp);//重新寻找空位
}

fi *F = (fi*) malloc(sizeof(fi));//填充基本信息
Make_a_New_File(F, FALSE);
clust[Clust_temp].status = 0xFFFF;


strcpy(F->name[0], a);//修改文件名称
strcpy(F->name[1], b);//修改文件后缀
Data_Add_A_File(F, Clust_temp, k);//把文件写入簇

}else if(ISCMD("rmdir")){

fi *t = Filelist_Search_File(b, TRUE);
if(t == NULL){
printf("no such a directory!\n");
return TRUE;
}

Clust_Clear_Dir(t->clust);


char temp[20];
strcpy(temp, b);
Str_Cut_Name_Into_Two(temp, a, b);//把文件名分割一下
char *st = clust[Clust_now].byte;
for(int i = 0; i < SECTORS_PER_CLUST * BYTES_PER_SECTOR / 32; i++){//寻找到簇中的那个文件,并且打上0xe5标记
fi *p = (fi*) malloc(sizeof(fi));

Str_Read_32Bytes(st + i * 32, p);
if(strcmp(p->name[0], a) == 0 && strcmp(p->name[1], b) == 0 && p->dir == TRUE){
*(st + i * 32) = 0xE5;
break;
}
}

/*
直接找到那个文件,首先递归删除该文件夹下的所有内容
然后再把本身 标记为删除
*/

}else if(ISCMD("rm")){

fi *t = Filelist_Search_File(b, FALSE);
if(t == NULL){
printf("no such a file!\n");
return TRUE;
}

Clust_Clear_File(t->clust);

char *st = clust[Clust_now].byte;
char temp[20];
strcpy(temp, b);
Str_Cut_Name_Into_Two(temp, a, b);

for(int i = 0; i < SECTORS_PER_CLUST * BYTES_PER_SECTOR / 32; i++){
fi *p = (fi*) malloc(sizeof(fi));
Str_Read_32Bytes(st + i * 32, p);

if(strcmp(p->name[0], a) == 0 && strcmp(p->name[1], b) == 0 && p->dir == FALSE){
*(st + i * 32) = 0xE5;
break;
}
}

//类似于删除文件夹,但不用递归删除

}else if(ISCMD("read")){
fi *t = Filelist_Search_File(b, FALSE);
if(t == NULL){
printf("no such a file!\n");
return TRUE;
}

int nxtClust = t->clust;
bool flag = 1;
printf("\nThis is what %s contains:", b);
printf("\n----------------------------\n");
while(flag == 1){
for(int i = 0; i < SECTORS_PER_CLUST * BYTES_PER_SECTOR; i++){
if(clust[nxtClust].byte[i] == 0){
flag = 0;
break;
}
printf("%c",clust[nxtClust].byte[i]);
}
if(Clust_Judge_Status(clust[nxtClust].status) == 1)
flag = 0;
else
nxtClust = clust[nxtClust].status;
}
printf("\n----------------------------\n\n");


/*
根据簇号直接搜索到对应的文件并且读入

*/

}else if(ISCMD("write")){

fi *t = Filelist_Search_File(b, FALSE);
if(t == NULL){
printf("no such a file!\n");
return TRUE;
}


int nxtClust = t->clust;
bool flag = 1;
char c;
while(flag == 1){
for(int i = 0; i < SECTORS_PER_CLUST * BYTES_PER_SECTOR; i++){
c = getchar();
if(c == -1){
flag = 0;
clust[nxtClust].byte[i] = 0;
break;
}else
clust[nxtClust].byte[i] = c;
}
clust[nxtClust].status = 0xFFFF;
if(flag == 0) break;
if(Clust_Judge_Status(clust[nxtClust].status) == 0)
nxtClust = clust[nxtClust].status;
else{
int tempClust = 2;
while(Clust_Judge_Status(clust[tempClust].status) != 2)
tempClust++;
clust[nxtClust].status = tempClust;
nxtClust = tempClust;
}
}
/*
边写文件边开新的簇


*/

}else if(ISCMD("format")){
printf("Are you sure to FORMAT this disk? (y/n): ");
gets(a);
if(ISCMD("y") || ISCMD("Y")){
printf("Once again. You'll lose your data, continue? (y/n): ");
gets(a);
if(ISCMD("y") || ISCMD("Y")){
printf("formating... ");
Data_ReadOrWrite(2);
Data_Create_New();
Data_ReadOrWrite(3);
Data_ReadOrWrite(1);
Data_Read();
Data_ReadOrWrite(3);
printf("done.\n\n");
}
}


}else if(ISCMD("save")){
printf("Are you sure to quit with saving? (y/n): ");
scanf("%s", a);
if(ISCMD("y") || ISCMD("Y")){
Data_Save();
return FALSE;
}

}else{
printf("unknown command \"%s\"\n",str);

}
return TRUE;//继续循环
}


bool Data_ReadOrWrite(const int t){//done
switch(t){
case 1: //只读
fp = fopen("fat16_data.bin", "rb");
return fp != NULL;
case 2: //可写
fp = fopen("fat16_data.bin", "wb");
return fp != NULL;
case 3:
fclose(fp);
return 1;
}
}

void Data_Initialize(){//done
if(Data_ReadOrWrite(1) == FALSE){
printf("Disk data doesn't exist. \nCreating a new one...\n");
Data_Create_New();
}
Data_ReadOrWrite(3);//关闭文件
Data_ReadOrWrite(1);//只读模式

Data_Read();
printf("Data loaded successfully.\n\n");
Clust_now = 1;
Data_ReadOrWrite(3);
}

void Data_Read(){
fseek(fp, BYTES_PER_SECTOR, SEEK_CUR);//跳过DBR区域
for(int i = 0; i <= -1 + SECTORS_PER_FAT * BYTES_PER_SECTOR >> 1; i++)//注意,这里是从0开始读
clust[i].status = (int) File_Scanf_Number(2);//读取FAT1
fseek(fp, SECTORS_PER_FAT * BYTES_PER_SECTOR, SEEK_CUR);//跳过FAT2
fread(clust[1].byte, sizeof(char), BYTES_PER_SECTOR * SECTORS_ROOT, fp);//读取根目录
for(int i = 2; i <= CLUST_MAX_COUNT; i++)
fread(clust[i].byte, sizeof(char), BYTES_PER_SECTOR * SECTORS_PER_CLUST, fp);//读取数据区
}

void Data_Create_New(){//done
Data_ReadOrWrite(2);
Data_DBR_Print();

for(int k = 0; k <= 1; k++){//填充FAT区域
fprintf(fp, "%c", 0xFF);
fprintf(fp, "%c", 0xFF);
for(int i = 1; i <= SECTORS_PER_FAT * BYTES_PER_SECTOR - 2; i++)
fprintf(fp, "%c", 0);
}

fi *p = (fi*) malloc(sizeof(fi));
Make_a_New_File(p, TRUE);
strcpy(p->name[0], ".");
File_Printf_32Bytes(p);
strcpy(p->name[0], "..");
File_Printf_32Bytes(p);
free(p);

for(int i = 2; i < (SECTORS_PER_CLUST * CLUST_MAX_COUNT) * BYTES_PER_SECTOR; i++)
fprintf(fp, "%c", 0);//把空扇区刷为0
Data_ReadOrWrite(3);
}

void Data_Save(){
Data_ReadOrWrite(2);
Data_DBR_Print();
for(int j = 0; j <= 1; j++)
for(int i = 0; i <= -1 + SECTORS_PER_FAT * BYTES_PER_SECTOR >> 1; i++)
File_Printf_Number(clust[i].status, 2);//打印FAT表,打印两份
fwrite(clust[1].byte, sizeof(char), BYTES_PER_SECTOR * SECTORS_ROOT, fp);//保存根目录
for(int i = 2; i <= CLUST_MAX_COUNT; i++)
fwrite(clust[i].byte, sizeof(char), BYTES_PER_SECTOR * SECTORS_PER_CLUST, fp);//保存数据区
Data_ReadOrWrite(3);
}

fi *Filelist_Search_File(char *name, bool IsDir){
char a[20], b[20];
Str_Cut_Name_Into_Two(name, a, b);

for(fi *p = HEAD; p != NULL; p = p->next)
if(strcmp(a, p->name[0]) == 0 && strcmp(b, p->name[1]) == 0 && p->dir == IsDir)
return p;//找到了


return NULL;//没找到
}

void Filelist_Print(){
printf("\n\n");
for(fi *p = HEAD; p != NULL; p = p->next){
Date_Print(p->date);
printf(" ");
Time_Print(p->time);
printf(" ");
printf("%s\t\t", p->dir == TRUE?"<DIR> ":"<FILE>");
printf("%s",p->name[0]);
if(!p->dir && p->name[1][0] != 0) printf(".%s", p->name[1]);
printf("\n");
}
printf("\n\n");
}

void Filelist_Update(int id){
if(id <= 0) return;

fi t;
for(int i = 0; i < SECTORS_PER_CLUST * BYTES_PER_SECTOR / 32; i++)
if(Str_Read_32Bytes(clust[id].byte + 32 * i, &t)){//如果读取成功,就申请一段新内存来存文件
fi *p = (fi *) malloc(sizeof(fi));
*p = t;
p->next = NULL;
Filelist_Add_File(p);
}


if(!Clust_Judge_Status(clust[id].status))//如果这个簇后面还有文件 就继续读取
Filelist_Update(clust[id].status);
else return;
}

void Filelist_Destroy(){
FileCnt = 0;
fi *p;
for(fi *pre = HEAD; pre != NULL; pre= p){
p = pre->next;
free(pre);
}
HEAD = NULL;
}

void Filelist_Add_File(fi *p){
p->next = HEAD;//直接在头部添加上新结点
HEAD = p;
FileCnt++;
}

int Clust_Judge_Status(int t){
if(t >= 0x2 && t <= 0xFFEF) return 0;//还有连接的簇
else if(t == 0xFFFF) return 1;//文件结束
else return 2;//空簇
}

bool Str_Read_32Bytes(char *str, fi *a){//done
Str_Scanf_String(str, 8, a->name[0]);
Str_Scanf_String(str + 8, 3, a->name[1]);
//这下面吧0xE5改为了-27 (ascii码相同)
if(a->name[0][0] == -27 || a->name[0][0] == 0){//如果该文件已经被删除或者不存在
fseek(fp, 21, SEEK_CUR);//直接跳过剩下32 - 11 = 21个字节
return FALSE;
}
str = str + 11;
if(*str++ == 0x10) //该文件是个目录
a->dir = TRUE;
else a->dir = FALSE;

str += 10;//跳过10个无意义字节

a->time = Str_Scanf_Number(str, 2);//读取时间
str += 2;
a->date = Str_Scanf_Number(str, 2);//读取日期
str += 2;
a->clust = Str_Scanf_Number(str, 2);//读取首簇号
str += 2;
a->len = Str_Scanf_Number(str, 4);//读取文件大小
str += 4;

return TRUE;
}

void File_Printf_32Bytes(fi *t){
File_Printf_String(t->name[0], 8);
File_Printf_String(t->name[1], 3);
if(t->dir == TRUE)
fprintf(fp, "%c", 0x10);
else
fprintf(fp, "%c", 0x20);
for(int i = 1; i <= 10; i++)
fprintf(fp, "%c", 0xFF);
File_Printf_Number(t->time, 2);
File_Printf_Number(t->date, 2);
File_Printf_Number(t->clust, 2);
File_Printf_Number(t->len, 4);
}

void Str_Printf_32Bytes(char *str, fi *t){
strncpy(str, t->name[0], 8);
str += 8;
strncpy(str, t->name[1], 3);
str += 3;

if(t->dir == TRUE)
*str++ = 0x10;
else
*str++ = 0x20;

for(int i = 1; i <= 10; i++)
*str++ = 0xFF;

Str_Printf_Number(str, t->time, 2);
str += 2;
Str_Printf_Number(str, t->date, 2);
str += 2;
Str_Printf_Number(str, t->clust, 2);
str += 2;
Str_Printf_Number(str, t->len, 4);
}

void Str_Cut_Name_Into_Two(char *name, char *a, char *b){
char temp[30];
strcpy(temp, name);
char *t = temp;

if(strcmp(t, "..") == 0 || strcmp(t, ".") == 0)
strcpy(a, temp);
else{
while(*t != 0 && *t != '.') t++;
if(*t == 0) *(t + 1) = 0;
*t = 0;
strcpy(a, temp);
strcpy(b, t + 1);
}
}

void Filelist_Bubble_Sort(){
for(int i = 0; i < FileCnt; i++)
for(fi *p = HEAD; p->next != NULL; p = p->next)
if(strcmp(p->name[0], p->next->name[0]) > 0){
fi _a, _b, *pnx;//交换函数
pnx = p->next;
_a = *p;
_b = *pnx;

*p = _b;
*pnx = _a;
p->next = _a.next;
pnx->next = _b.next;
}
}

void Data_DBR_Print(){//done
fprintf(fp, "%c%c%c",0xEB ,0x3C ,0x90 );
fprintf(fp, "ByMrh929");
File_Printf_Number(BYTES_PER_SECTOR, 2);
File_Printf_Number(SECTORS_PER_CLUST, 1);
File_Printf_Number(RESERVED_SECTORS, 2);//保留扇区数
File_Printf_Number(2, 1);//FAT数量
File_Printf_Number(512, 2);//根目录项数
File_Printf_Number(SECTORS_PER_FAT, 2);
File_Printf_Number(63, 2);
File_Printf_Number(0, 4);
fprintf(fp, "FAT16 ");
for(int i = 1; i <= 475; i++)
fprintf(fp, "%c", 0x99);
File_Printf_Number(43605, 2);
}

void Data_Add_A_File(fi *F, int id, int k){
char *p = clust[id].byte;
p += k * 32;//将指针移动到需要修改的那个文件信息之前

int newclust = 2;
while(clust[newclust].status != 0) newclust++;//寻找一个簇,用来存放文件的信息
clust[newclust].status = 0xFFFF;
memset(clust[newclust].byte, 0, sizeof(clust[newclust].byte));
F->clust = newclust;

Str_Printf_32Bytes(p, F);

}

void Str_Printf_Number(char *str, long long sum, int n){
long long m;
for(int i = 0; i < n; i++){
m = sum % 256;
*str++ = m;//输出一位数并且指针后移
sum /= 256;
}
}

long long Str_Scanf_Number(char *str, int n){//done
long long sum = 0;
char t;
for(int i = 0; i < n ; i++){
t = *str++; //读取一位并且指针后移
sum += t * (1 << (i * 8));
}
return sum;
}

void File_Printf_Number(long long sum, int n){//done
long long m;
for(int i = 0; i < n; i++){
m = sum % 256;
fprintf(fp, "%c", m);
sum /= 256;
}
}

long long File_Scanf_Number(int n){//done
long long sum = 0;
char t;
for(int i = 0; i < n ; i++){
fscanf(fp, "%c", &t);
sum += t * (1 << (i * 8));
}
return sum;
}

void Str_Scanf_String(char *str, int t, char *dest){//done
int i = 0;
while(i < t){
if(*(str + i) != ' ' && *(str + i) != 0){
*(dest + i) = *(str + i);
i++;
}else break;
}
*(dest + i) = 0;
}

void File_Printf_String(char *str, int n){
for(int i = 0; i < n; i++)
fprintf(fp, "%c", *(str + i));
}

void Time_Print(int t){//done
int h, m, s;
s = (t & 0x1F) * 2;//取后五位
t >>= 5;
m = t & 0x3F;
t >>= 6;
h = t & 0x1F;
printf("%02d:%02d:%02d", h, m, s);
}

void Date_Print(int t){//done
int y, m, d;
d = t & 0x1F;
t >>= 5;
m = t & 0xF;
t >>= 4;
y = (t & 0xFF) + 1980;
printf("%04d/%02d/%02d", y, m, d);

}

int Time_Get(){//done
time_t t;
struct tm *lt;
time (&t);//获取Unix时间戳。
lt = localtime (&t);//转为时间结构。
return (lt->tm_hour * 2048) + (lt->tm_min * 32) + (lt->tm_sec / 2);
}

int Date_Get(){//done
time_t t;
struct tm *lt;
time (&t);//获取Unix时间戳。
lt = localtime (&t);//转为时间结构。
lt->tm_year += 1900;
lt->tm_mon ++;//0代表1月
return (lt->tm_year - 1980) * 512 + (lt->tm_mon * 32) + (lt->tm_mday);
}

void Dir_Change(char *p, bool NEW){
if(NEW == FALSE)
dir_now--;
else
strcpy(dir[++dir_now], p);
}

void Dir_Print(){
printf("\\");
for(int i = 0; i <= dir_now; i++)
printf("%s\\",dir[i]);
}

int Dir_Find_Empty(int id){
for(int i = 0; i < SECTORS_PER_CLUST * BYTES_PER_SECTOR / 32; i++)
if(clust[id].byte[i * 32] == -27 || clust[id].byte[i * 32] == 0)
return i;
return -1;//没找到一个空位
}

void Make_a_New_File(fi *p, bool IsDir){
p->clust = 1;
p->len = 0;
p->dir = IsDir;
p->time = Time_Get();
p->date = Date_Get();
p->name[1][0] = 0;
}

void Clust_Clear_File(int id){
if(Clust_Judge_Status(clust[id].status) == 0)//还有连接的簇
Clust_Clear_File(clust[id].status);
clust[id].status = 0;//标记为未使用
return;
}

void Clust_Clear_Dir(int id){
//先搜索这个文件夹下有哪些子文件和目录,把它们删除,然后再删除自身

if(id <= 0) return;

fi t;
for(int i = 2; i < SECTORS_PER_CLUST * BYTES_PER_SECTOR / 32; i++)
if(Str_Read_32Bytes(clust[id].byte + 32 * i, &t)){//如果读取成功,就申请一段新内存来存文件
if(t.dir == TRUE)
Clust_Clear_Dir(t.clust);//递归删除子目录
else
Clust_Clear_File(t.clust);//递归删除子文件

*(clust[id].byte + 32 * i) = 0xE5;//标记为删除
}

if(Clust_Judge_Status(clust[id].status) == 0)//如果这个簇后面还有文件 就继续读取
Clust_Clear_Dir(clust[id].status);
}

void Manu_Print(){//done
printf("This system supports the following commands:\n\n");
printf("ls/dir\t\t\t\t\tShow the files and folders contained.\n");
printf("cd [DIR]\t\t\t\tEnter an appinted folder.\n");
printf("mkdir [DIR]\t\t\t\tCreate a folder.\n");
printf("create [NAME]\t\t\t\tCreate a file.\n");
printf("rmdir [DIR]\t\t\t\tDelete a folder.\n");
printf("rm [NAME]\t\t\t\tDelete a file.\n");
printf("read [NAME]\t\t\t\tRead a file.\n");
printf("write [NAME]\t\t\t\tWrite a file.\n");
printf("format\t\t\t\t\tFormat this disk.(be careful)\n");
printf("save\t\t\t\t\tSave this disk and quit.(without which you'll lose your data)\n\n\n");
}

原文作者:mrh929

原文链接:https://mrh1s.top/posts/46621246/

发表日期:February 4th 2019, 6:06:10 pm

更新日期:May 5th 2019, 1:48:21 pm

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 参考资料:
  • FAT16
    1. 0.0.0.1. 一、关于需要掌握的基础知识
    2. 0.0.0.2. 二、FAT16文件系统的结构
    3. 0.0.0.3. 三、删除文件(夹)的过程
    4. 0.0.0.4. 四、创建文件(夹)的过程
    5. 0.0.0.5. 五、读取文件(夹)的过程
    6. 0.0.0.6. 六、写入文件的过程
    7. 0.0.0.7. 七、遇到当前簇满的处理措施
    8. 0.0.0.8. 八、相关图表
  • 0.1. FAT16记录表(可先记录一下自己磁盘的如下信息,方便之后的分析,以下只是我自己SD卡的信息)
  • 0.2. DBR数据对照表
  • 0.3. 文件信息对照表
  • 0.4. 系统时间与文件系统中的时间的换算
  • 0.5. FAT16簇空间大小与分区空间大小的关系