Tổng quan
Bài viết giới thiệu cấu trúc dữ liệu hệ thống tệp FAT12, trình bày các vấn đề phát sinh trong quá trình học tập và lập trình, đồng thời cung cấp ba chức năng chính:
- Hiển thị cấu trúc phân vùng khởi động
- Hiển thị thông tin thư mục gốc
- Đọc nội dung tệp từ khu vực dữ liệu thông qua bảng FAT
Cấu trúc hệ thống tệp FAT12 gồm 5 phân vùng chính. Phân vùng khởi động lưu trữ thông tin cơ bản của hệ thống. Hai bảng FAT1 và FAT2 hoàn toàn giống nhau, tạo thành cơ chế dự phòng. Thư mục gốc chứa danh sách các tệp với thông tin như tên tệp, số cụm, thuộc tính, thời gian... Khu vực dữ liệu lưu trữ nội dung chính của tệp.
| STT | Vị trí Sector | Số Sector | Ghi chú |
|---|---|---|---|
| 01 | 0 | 1 | Phân vùng khởi động |
| 02 | 1 | 9 | Bảng FAT1 |
| 03 | 10 | 9 | Bảng FAT2 |
| 04 | 19 | 14 | Thư mục gốc |
| 05 | 33 | 2847 | Khu vực dữ liệu |
Cấu trúc phân vùng khởi động
| Tham số | Địa chỉ | Loại | Kích thước | Giá trị mặc định | Mô tả |
|---|---|---|---|---|---|
| BS_JmpBoot | 0 | db | 3 | - | Lệnh nhảy |
| BS_OEMName | 3 | db | 8 | FREEDOS | Chuỗi OEM (8 ký tự) |
| BPB_BytePerSec | 11 | dw | 2 | 0x200 | Bytes/sector |
| BPB_SecPerClus | 12 | db | 1 | 1 | Sector/clusters |
| BPB_RsvdSecCnt | 14 | dw | 2 | 1 | Sector dành riêng |
| BPB_NumFATs | 16 | db | 1 | 2 | Số bảng FAT |
| BPB_RootEntCnt | 17 | dw | 2 | 0xE0 | Số mục thư mục gốc |
| BPB_TotSec16 | 19 | dw | 2 | 0xB40 | Tổng sector (16-bit) |
| BPB_Media | 21 | db | 1 | 0xF0 | Mã vật lý |
| BPB_FATSz16 | 22 | dw | 2 | 9 | Kích thước bảng FAT |
| BPB_SecPerTrk | 24 | dw | 2 | 0x12 | Sector/vòng tròn |
| BPB_NumHeads | 26 | dw | 2 | 2 | Số đầu đọc |
| BPB_HiddSec | 28 | dd | 4 | 0 | Sector ẩn |
| BPB_TotSec32 | 32 | dd | 4 | 0 | Tổng sector (32-bit) |
| BS_DrvNum | 36 | db | 1 | 0 | Số ổ đĩa |
| BS_Reserved1 | 37 | db | 1 | 0 | Dự phòng |
| BS_Bootsig | 38 | db | 1 | 0x29 | Chữ ký khởi động |
| BS_VolID | 39 | dd | 4 | 0 | Mã thể tích |
| BS_VolLab | 43 | db | 11 | - | Tên thể tích |
| BS_FileSysType | 54 | db | 8 | FAT12 | Loại hệ thống tệp |
| BOOT_Code | 62 | db | 448 | 0x00 | Mã khởi động |
| END | 510 | db | 2 | 0x55, 0xAA | Chữ ký kết thúc |
Cấu trúc bảng FAT
| Trạng thái | Giá trị | Mô tả |
|---|---|---|
| 0 | BPB_Media | Mã vật lý |
| 1 | FFFh | Cụm đầu tiên đã dùng |
| 2 ~ N | 000h | Cụm trống |
| 002h~FEFh | Cụm đã dùng | |
| FF0h~FF6h | Cụm dành riêng | |
| FF7h | Cụm lỗi | |
| FF8h~FFFh | Cụm cuối cùng |
Thư mục gốc
| Tên trường | Địa chỉ | Kích thước | Mô tả |
|---|---|---|---|
| DIR_Name | 0x00 | 11 | Tên tệp (8B) + phần mở rộng (3B) |
| DIR_Attr | 0x0B | 1 | Thuộc tính tệp |
| DIR_Reserved | 0x0C | 10 | Dữ liệu dự phòng |
| DIR_WrtTime | 0x16 | 2 | Thời gian sửa đổi |
| DIR_WrtDate | 0x18 | 2 | Ngày sửa đổi |
| DIR_FstClus | 0x1A | 2 | Số cụm bắt đầu |
| DIR_FileSize | 0x1C | 4 | Kích thước tệp |
Lưu ý lập trình
- Căn chỉnh theo byte:
#pragma pack(push)
#pragma pack(1)
struct CấuTrúc
#pragma pack(pop)
- Hiển thị chuỗi 11 byte:
printf("DIR_Name: %11.11s\n", re.DIR_Name);
- Xử lý bảng FAT:
- Bảng FAT bắt đầu từ cụm thứ hai, kích thước xác định bởi BPB_FATSz16
- Mỗi mục FAT chiếm 12 bit (1.5 byte). Quy trình chuyển đổi:
// Lấy 2 byte đầu
value1 = (fat[i+1] & 0xF) << 8 | fat[i];
// Lấy 2 byte sau
value2 = (fat[i+1] >> 4) | (fat[i+2] << 4);
- Quan hệ giữa FAT và dữ liệu:
unsigned short* cluster_list = malloc(...);
readfat(..., cluster_list);
Chuẩn bị file data.img
- Tạo đĩa hình ảnh:
dd if=/dev/zero of=data.img bs=512 count=2880
mkfs.fat -F 12 data.img
- Gắn kết và sao chép tệp:
mount -o loop data.img /mnt
cp tệp.txt /mnt/
umount /mnt
Triển khai đọc MBR
#include <stdio.h>
#include <stdlib.h>
typedef struct {
unsigned char BS_JmpBoot[3];
char BS_OEMName[9];
unsigned short BPB_BytePerSec;
unsigned char BPB_SecPerClus;
unsigned short BPB_RsvdSecCnt;
unsigned char BPB_NumFATs;
unsigned short BPB_RootEntCnt;
unsigned short BPB_TotSec16;
unsigned char BPB_Media;
unsigned short BPB_FATSz16;
unsigned short BPB_SecPerTrk;
unsigned short BPB_NumHeads;
unsigned int BPB_HiddSec;
unsigned int BPB_TotSec32;
unsigned char BS_DrvNum;
unsigned char BS_Reserved1;
unsigned char BS_Bootsig;
unsigned char BS_VolID[4];
char BS_VolLab[12];
char BS_FileSysType[9];
unsigned char BOOT_Code[448];
unsigned short MBR_Flag;
} __attribute__((packed)) MBRInfo;
void inThongTinMBR(const char* path) {
FILE* fp = fopen(path, "rb");
MBRInfo mbr;
fread(&mbr, sizeof(MBRInfo), 1, fp);
mbr.BS_OEMName[7] = '\0';
mbr.BS_VolLab[10] = '\0';
mbr.BS_FileSysType[7] = '\0';
printf("OEM Name: %s\n", mbr.BS_OEMName);
printf("Bytes/Sector: %d\n", mbr.BPB_BytePerSec);
// ... In các trường khác ...
printf("Chữ ký khởi động: 0x%x\n", mbr.MBR_Flag);
fclose(fp);
}
Triển khai đọc thư mục gốc
typedef struct {
char DIR_Name[11];
unsigned char DIR_Attr;
unsigned char DIR_Reserved[10];
unsigned short DIR_WrtTime;
unsigned short DIR_WrtDate;
unsigned short DIR_FstClus;
unsigned int DIR_FileSize;
} __attribute__((packed)) RootEntry;
void inThongTinThuMucGoc(MBRInfo mbr, const char* path) {
FILE* fp = fopen(path, "rb");
RootEntry entry;
for(int i=0; i < mbr.BPB_RootEntCnt; i++) {
fseek(fp, 19*mbr.BPB_BytePerSec + i*sizeof(RootEntry), SEEK_SET);
fread(&entry, sizeof(RootEntry), 1, fp);
if(entry.DIR_FstClus == 0 || entry.DIR_Name[0] == 0)
continue;
printf("Tên tệp: %11.11s\n", entry.DIR_Name);
// ... In thông tin khác ...
}
fclose(fp);
}
Triển khai đọc nội dung tệp
void docNoiDungTep(MBRInfo mbr, const char* path, const char* tenTep) {
RootEntry entry;
if(!timTep(mbr, path, tenTep, &entry))
return;
unsigned int size = mbr.BPB_BytePerSec * mbr.BPB_SecPerClus;
char* buffer = malloc(size);
unsigned short currentCluster = entry.DIR_FstClus;
while(currentCluster < 0xFF8) {
int offset = 33 * mbr.BPB_BytePerSec + (currentCluster - 2) * size;
fseek(fp, offset, SEEK_SET);
fread(buffer, size, 1, fp);
printf("%s", buffer);
currentCluster = layGiaTriFAT(mbr, currentCluster);
}
free(buffer);
}
Kết luận
Hệ thống tệp FAT12 minh họa rõ ràng cách thiết kế cấu trúc dữ liệu hiệu quả. Việc hiểu và triển khai các hệ thống như vậy đòi hỏi không chỉ kiến thức lập trình mà còn khả năng tư duy thiết kế hệ thống. Những kỹ thuật như căn chỉnh bộ nhớ, tối ưu lưu trữ, và xử lý dữ liệu phi chuẩn là những bài học quý giá cho việc phát triển phần mềm hệ thống.