|
[Readme.md](uploads/0bc768dbb7fc0582616552f906240a99/Readme.md) |
|
## 头文件
|
|
\ No newline at end of file |
|
```c
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
|
|
|
```
|
|
|
|
这些是标准C库头文件,提供文件I/O、数据类型、布尔值和字符串操作的功能。
|
|
|
|
## 类型定义
|
|
|
|
typedef uint32_t fat_entry_t;
|
|
|
|
这里定义了一个名为**fat_entry_t** 的32位无符号整数类型别名,表示FAT表中的条目。
|
|
|
|
#define SECTOR_SIZE 512
|
|
|
|
这里定义了一个名为**SECTOR_SIZE**的宏,来表示每个扇区的大小为512字节。
|
|
|
|
## 结构体
|
|
|
|
struct fat32_dbr {
|
|
|
|
uint16_t bytes_per_sector; // 每个扇区的字节数
|
|
|
|
uint8_t sectors_per_cluster; // 每个簇的扇区数
|
|
|
|
uint16_t reserved_sector_count; // 保留扇区数
|
|
|
|
uint8_t fat_count; // FAT 表数量
|
|
|
|
uint16_t root_dir_entry_count; // 根目录项数
|
|
|
|
uint32_t total_sectors; // 总扇区数
|
|
|
|
uint8_t media_descriptor; // 媒体描述符
|
|
|
|
uint16_t sectors_per_fat; // 每个 FAT 表的扇区数
|
|
|
|
char volume_label[12]; // 卷标,最多11个字符,加一个结束符'\0'
|
|
|
|
char file_system_type[9]; // 文件系统类型,最多8个字符,加一个结束符'\0'
|
|
|
|
// 其他 DBR 字段...
|
|
|
|
};
|
|
|
|
这个结构体定义了一个 FAT32 文件系统的 DBR(DOS Boot Record)结构,包含了各种与文件系统相关的信息,比如每个扇区的字节数、每个簇的扇区数、保留扇区数、FAT 表数量等。
|
|
|
|
|
|
|
|
struct fat32_dir_entry {
|
|
|
|
char filename[11]; // 文件名
|
|
|
|
uint8_t attributes; // 属性
|
|
|
|
uint8_t reserved; // 保留字段
|
|
|
|
uint16_t first_cluster_high; // 簇号的高16位
|
|
|
|
uint16_t first_cluster_low; // 簇号的低16位
|
|
|
|
// 其他字段...
|
|
|
|
};
|
|
|
|
这个结构体定义了 FAT32 文件系统中的目录项结构,包含了文件名和文件属性等信息。
|
|
|
|
## 函数体
|
|
|
|
### allocate_cluster()
|
|
|
|
fat_entry_t allocate_cluster(int fd, struct fat32_dbr dbr) {
|
|
|
|
...函数内容...
|
|
|
|
}
|
|
|
|
这个函数的作用是在fat32文件系统中分配一个空闲的簇。
|
|
|
|
#### 函数参数
|
|
|
|
int fd;
|
|
|
|
表示文件描述符,用于指定要操作的文件或设备。
|
|
|
|
struct fat32_dbr dbr
|
|
|
|
表示FAT32文件系统的DBR(DOS Boot Record),包含文件系统的关键参数。
|
|
|
|
#### 函数实现
|
|
|
|
1. 首先,计算FAT表在磁盘中的偏移量,即fat_offset = dbr.reserved_sector_count * dbr.bytes_per_sector。这里的reserved_sector_count表示保留的扇区数,bytes_per_sector表示每个扇区的字节数。
|
|
|
|
|
|
|
|
2. 然后,使用一个循环遍历FAT表的所有扇区。循环的次数由dbr.sectors_per_fat决定,表示每个FAT表所占用的扇区数量。
|
|
|
|
|
|
|
|
3. 在循环中,计算当前扇区的读取偏移量read_offset,然后使用pread()函数从磁盘中读取FAT表的内容。
|
|
|
|
|
|
|
|
4. 接下来,在一个嵌套的循环中,遍历当前扇区中的所有FAT表项。循环次数由SECTOR_SIZE / sizeof(fat_entry_t)决定,其中SECTOR_SIZE表示扇区的大小,sizeof(fat_entry_t)表示每个FAT表项的大小。
|
|
|
|
|
|
|
|
5. 对于每个FAT表项,检查是否为0。如果是0,表示该簇为空闲簇,可以进行分配。
|
|
|
|
|
|
|
|
6. 如果找到了空闲簇,将其标记为已分配,然后使用pwrite()函数将更新后的FAT表项写回磁盘。
|
|
|
|
|
|
|
|
7. 最后,返回已分配簇的簇号。簇号的计算基于当前扇区的偏移量和循环变量j。
|
|
|
|
|
|
|
|
8. 如果在遍历FAT表的过程中未找到空闲簇,则返回-1,表示分配失败。
|
|
|
|
### read_file_content()
|
|
|
|
功能:从指定簇号读取文件内容。
|
|
|
|
#### 函数参数
|
|
|
|
fd: 打开的磁盘设备文件描述符。
|
|
|
|
dbr: FAT32 DBR 结构,用于获取文件系统信息。
|
|
|
|
cluster_number: 要读取的文件的起始簇号。
|
|
|
|
buffer: 存储读取内容的缓冲区。
|
|
|
|
size: 要读取的字节数。
|
|
|
|
#### 函数实现
|
|
|
|
1. 计算偏移量
|
|
|
|
2. 读取文件内容:
|
|
|
|
使用 pread 函数从打开的磁盘设备文件 (fd) 中读取数据。
|
|
|
|
buffer 是用于存储读取数据的缓冲区。
|
|
|
|
size 是要读取的字节数。
|
|
|
|
pread 函数返回实际读取的字节数,如果返回值不等于 size,则表示读取失败。
|
|
|
|
### write_file_content()
|
|
|
|
功能:将内容写入到指定簇号对应的文件中。
|
|
|
|
#### 函数参数
|
|
|
|
fd: 打开的磁盘设备文件描述符。
|
|
|
|
dbr: FAT32 DBR 结构,用于获取文件系统信息。
|
|
|
|
cluster_number: 要写入的文件的起始簇号。
|
|
|
|
content: 要写入的内容。
|
|
|
|
size: 要写入的字节数。
|
|
|
|
#### 函数实现
|
|
|
|
1. 计算偏移量
|
|
|
|
2. 写入文件内容:
|
|
|
|
使用 pwrite 函数将数据从 content 缓冲区写入到打开的磁盘设备文件 (fd) 中。
|
|
|
|
size 是要写入的字节数。
|
|
|
|
pwrite 函数返回实际写入的字节数,如果返回值不等于 size,则表示写入失败。
|
|
|
|
### is_filename_duplicate()
|
|
|
|
功能:在指定目录中检查是否存在重复的文件名。
|
|
|
|
#### 函数参数
|
|
|
|
fd: 打开的磁盘设备文件描述符。
|
|
|
|
dir_offset: 目录的偏移量。
|
|
|
|
filename: 要检查的文件名。
|
|
|
|
#### 函数实现
|
|
|
|
1. 循环遍历目录项:
|
|
|
|
函数从指定的 dir_offset 开始,通过循环逐个读取目录项。
|
|
|
|
offset 变量从 dir_offset 开始,每次增加 sizeof(dir_entry),即一个目录项的大小,以移动到下一个目录项的位置。
|
|
|
|
2. 读取目录项:
|
|
|
|
使用 pread 函数从文件描述符 fd 中读取一个目录项的数据,并将其存储在 dir_entry 结构体中。
|
|
|
|
3. 检查读取结果:
|
|
|
|
pread 函数返回值 bytes_read 表示实际读取的字节数。
|
|
|
|
如果 bytes_read 不等于 sizeof(dir_entry),则说明可能已经读取完所有的目录项,因此退出循环。
|
|
|
|
4. 比较文件名:
|
|
|
|
使用 memcmp 函数比较读取到的目录项中的文件名 (dir_entry.filename) 和传入的 filename 是否相等。
|
|
|
|
文件名在 dir_entry.filename 中以固定长度为 11 字节存储,因此使用 memcmp 来进行比较。
|
|
|
|
5. 发现重复文件名:
|
|
|
|
如果 memcmp 返回 0,表示找到了重复的文件名,函数立即返回 true。
|
|
|
|
6. 未找到重复文件名:
|
|
|
|
如果循环结束时都没有找到相同的文件名,则函数返回 false。
|
|
|
|
### create_directory_entry()
|
|
|
|
bool create_directory_entry(int fd, off_t dir_offset, const char *filename, uint8_t attributes, fat_entry_t cluster_number) {
|
|
|
|
...函数内容...
|
|
|
|
}
|
|
|
|
这个是创建目录项的函数。
|
|
|
|
#### 函数参数
|
|
|
|
int fd, off_t dir_offset, const char *filename, uint8_t attributes, fat_entry_t cluster_number
|
|
|
|
这是一个函数定义,它接受文件描述符 fd,目录偏移量 dir_offset,要创建的文件名 filename,文件属性 attributes 以及文件所属的簇号 cluster_number 作为参数,并且返回一个布尔值。
|
|
|
|
|
|
|
|
#### 函数实现
|
|
|
|
1. 在函数内部,首先创建了一个 fat32_dir_entry 结构体变量 dir_entry,并使用 memset 函数将其内容全部初始化为零。这确保了目录项的初始状态为零,以避免其中包含任何无效或者不确定的数据。
|
|
|
|
2. 接下来,使用 strncpy 函数将参数 filename 中的文件名复制到 dir_entry 的 filename 字段中。FAT32文件系统规定文件名长度为11个字符,因此这里只会复制前11个字符。
|
|
|
|
3. 然后,将参数 attributes 赋值给 dir_entry 的 attributes 字段,即设置文件的属性。
|
|
|
|
4. 接下来是关于簇号的设置。因为FAT32使用32位的簇号,但是目录项中的簇号字段只有16位,所以需要将簇号分为低16位和高16位分别存储。这里通过位操作将 cluster_number 分别赋值给 dir_entry 的 first_cluster_low 和 first_cluster_high 字段。
|
|
|
|
5. 最后,使用 pwrite 函数将目录项写入到文件中。pwrite 函数用于将数据写入文件的指定位置,参数依次为文件描述符 fd、要写入的数据的指针 &dir_entry、数据大小 sizeof(dir_entry)、写入的位置 dir_offset。如果写入的字节数不等于目录项的大小,说明写入失败,会输出错误信息并返回 false,否则返回 true。
|
|
|
|
### update_filesystem_metadata()
|
|
|
|
功能:更新文件系统的元数据,如 FAT 表和根目录项计数。
|
|
|
|
#### 函数参数
|
|
|
|
fd: 打开的磁盘设备文件描述符。
|
|
|
|
dbr: FAT32 DBR 结构,用于获取文件系统信息。
|
|
|
|
allocated_cluster: 刚刚分配的簇号。
|
|
|
|
#### 函数实现
|
|
|
|
1. 更新FAT表中分配的簇号:
|
|
|
|
计算出FAT表的起始偏移量 fat_offset,并根据分配的簇号 allocated_cluster 计算出写入位置的偏移量 write_offset。
|
|
|
|
将 FAT32 的结束标记 0xFFFFFFFF 写入到 write_offset 处,以标记该簇号已经被分配。
|
|
|
|
2. 更新根目录项计数:
|
|
|
|
计算出根目录的起始偏移量 root_dir_offset,这里假设根目录是紧接着FAT表后面的。
|
|
|
|
使用 pread 读取根目录的第一个目录项(通常占据一个固定大小的空间)到 dir_entry 结构体中。
|
|
|
|
3. 增加根目录项计数:
|
|
|
|
在 dbr 结构体中增加根目录项计数 dbr.root_dir_entry_count++。
|
|
|
|
4. 更新DBR中的根目录项计数:
|
|
|
|
使用 pwrite 将更新后的 dbr 结构体写回到文件的开头,以更新文件系统的元数据。
|
|
|
|
5. 错误处理:
|
|
|
|
如果任何一个 pwrite 或 pread 操作失败,函数会打印相应的错误信息并返回 false。
|
|
|
|
6. 返回值:
|
|
|
|
如果所有操作顺利完成,函数返回 true,表示文件系统元数据已成功更新。 |