从零开始写一个建立FAT32文件系统程序
嵌入式设备实现FAT32系统建立可以参考实现util-linux 和 dosfstools 开发 ,fdisk mkfs工具移植-CSDN博客
本文旨在FAT32文件系统的学习实践
一.建立DOS/MBR分区
1.首先介绍一下MBR,MBR主要存储磁盘分区相关的信息,如果存在MBR的话,那么它占用第一个扇区的512个字节,前446个字节为MBR启动代码,接下来16*4个字节为磁盘分区表DPT。
在MBR(主引导记录)分区中,DPT(Disk Partition Table,硬盘分区表)是一个关键部分。它位于MBR的第446到509字节之间,总共占用64字节
DPT的主要作用是记录硬盘的分区信息。每个分区的信息占用16字节,因此DPT最多可以记录四个主分区或三个主分区加一个扩展分区。具体来说,DPT包含以下信息:
1. 分区状态:表示分区是否为活动分区。
2. 分区起始和结束位置:包括磁头号、扇区号和柱面号。
3. 分区类型:例如FAT32、NTFS等。
4. 分区起始绝对扇区:分区在硬盘上的起始位置。
5. 分区总扇区数:分区的大小
如下图所示,目前只有一个主分区起始地址:0x800 总大小:0x1D4B8
程序要实现分区 只要对DPT读写即可
主要代码实现代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <stdio.h>
#include <endian.h>
#define SECTOR_SIZE 512
#define _1G 2097152
struct PartitionEntry
{
unsigned char boot_flag; // 引导标志
unsigned char bh; // 起始磁头
unsigned char bs; // 起始扇区
unsigned char bc; // 起始柱面
unsigned char part_type; // 分区类型
unsigned char eh; // 结束磁头
unsigned char es; // 结束扇区
unsigned char ec; // 结束柱面
unsigned int start_sector_abs; // 起始扇区的绝对地址
unsigned int num_sectors; // 分区的扇区数
} __attribute__((packed));
void dos_partition_sync_chs(struct PartitionEntry *p, unsigned int geom_sectors, unsigned int geom_heads)
{
unsigned long long int start = le32toh(p->start_sector_abs);
unsigned long long int stop = le32toh(p->num_sectors) - 1;
unsigned int spc = geom_heads * geom_sectors;
if (start / spc > 1023)
start = spc * 1024 - 1;
if (stop / spc > 1023)
stop = spc * 1024 - 1;
p->bc = (start / spc) & 0xff;
p->bh = (start / geom_sectors) % geom_heads;
p->bs = ((start % geom_sectors + 1) & 0x3f) |
(((start / spc) >> 2) & 0xc0);
p->ec = (stop / spc) & 0xff;
p->eh = (stop / geom_sectors) % geom_heads;
p->es = ((stop % geom_sectors + 1) & 0x3f) |
(((stop / spc) >> 2) & 0xc0);
}
int get_geometry(int fd, unsigned int *h, unsigned int *s, unsigned int *st)
{
struct hd_geometry geometry;
if (ioctl(fd, HDIO_GETGEO, &geometry) == 0)
{
*h = geometry.heads;
*s = geometry.sectors;
*st = geometry.start;
return 0;
}
}
void create_partition(const char *device)
{
int geom_heads, geom_sectors, geom_start;
int fd = open(device, O_RDWR);
if (fd < 0)
{
perror("Open device");
exit(1);
}
// 获取分区基础信息
get_geometry(fd, &geom_heads, &geom_sectors, &geom_start);
struct PartitionEntry partition;
memset(&partition, 0, sizeof(partition));
partition.boot_flag = 0x00; //
partition.part_type = 0x83; // Linux分区类型
// 分区1
partition.start_sector_abs = htole32(2048); // 起始扇区
partition.num_sectors = htole32(_1G * 10); // 10G 分区大小(以扇区为单位)
// 计算 磁头 柱面 扇区
dos_partition_sync_chs(&partition, geom_sectors, geom_heads);
lseek(fd, 446, SEEK_SET); // 移动到分区表的位置
write(fd, &partition, sizeof(partition));
// 分区2
partition.start_sector_abs = htole32(partition.start_sector_abs + partition.num_sectors);
partition.num_sectors = htole32(_1G * 3); // 3G 区大小(以扇区为单位)
// 计算 磁头 柱面 扇区
dos_partition_sync_chs(&partition, geom_sectors, geom_heads);
write(fd, &partition, sizeof(partition));
fsync(fd);
// 让内核重新读取分区表 产生 sdx1 sdx2
if (ioctl(fd, BLKRRPART) == -1)
{
perror("Failed to re-read partition table");
}
close(fd);
}
int main(int argc, char **argv)
{
create_partition(argv[1]);
printf("Partition created successfully.\n");
return 0;
}
上面程序主要实现了将一个磁盘分成两个主分区一个9G 和 3G
代码解释:
1.一个DPT 16字节 用PartitionEntry结构体描述
struct PartitionEntry
{
unsigned char boot_flag; // 引导标志
unsigned char bh; // 起始磁头
unsigned char bs; // 起始扇区
unsigned char bc; // 起始柱面
unsigned char part_type; // 分区类型
unsigned char eh; // 结束磁头
unsigned char es; // 结束扇区
unsigned char ec; // 结束柱面
unsigned int start_sector_abs; // 起始扇区的绝对地址
unsigned int num_sectors; // 分区的扇区数
} __attribute__((packed));
2. get_geometry从驱动上获取磁盘的基础信息
int get_geometry(int fd, unsigned int *h, unsigned int *s, unsigned int *st)
{
struct hd_geometry geometry;
if (ioctl(fd, HDIO_GETGEO, &geometry) == 0)
{
*h = geometry.heads;
*s = geometry.sectors;
*st = geometry.start;
return 0;
}
}
3. 一般第一个分区地址为第2048块开始
partition.start_sector_abs = htole32(2048);
4. 根据给的起始地址和分区空间 获取开始/结束 磁头 柱面 扇区参数 。(我尝试乱填也无影响硬盘分区,这些数据可能为非必要数据,博客这里按照标准赋值)
void dos_partition_sync_chs(struct PartitionEntry *p, unsigned int geom_sectors, unsigned int geom_heads)
{
unsigned long long int start = le32toh(p->start_sector_abs);
unsigned long long int stop = le32toh(p->num_sectors) - 1;
unsigned int spc = geom_heads * geom_sectors;
if (start / spc > 1023)
start = spc * 1024 - 1;
if (stop / spc > 1023)
stop = spc * 1024 - 1;
p->bc = (start / spc) & 0xff;
p->bh = (start / geom_sectors) % geom_heads;
p->bs = ((start % geom_sectors + 1) & 0x3f) |
(((start / spc) >> 2) & 0xc0);
p->ec = (stop / spc) & 0xff;
p->eh = (stop / geom_sectors) % geom_heads;
p->es = ((stop % geom_sectors + 1) & 0x3f) |
(((stop / spc) >> 2) & 0xc0);
}
写入分区表后,要让驱动重新扫描MBR获取分区表
// 让内核重新读取分区表 产生 sdx1 sdx2
if (ioctl(fd, BLKRRPART) == -1)
{
perror("Failed to re-read partition table");
}
运行结果如下,sda1 sda2 就是生成的两个主分区,接下来在这两个主分区上建立FAT32分区
workSpace$ sudo ./a.out /dev/sda
Partition created successfully.
workSpace$ ls /dev/sda
sda sda1 sda2
二。建立FAT32分区
FAT32的基础信息可以参考这篇博客:1. SD卡中FAT32文件系统快速入门-CSDN博客
主要程序代码如下
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <stdint.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <endian.h>
/* The "main" entry point into the utility - we pick up the options and attempt to process them in some sort of sensible
way. In the event that some/all of the options are invalid we need to tell the user so that something can be done! */
#define BLOCK_SIZE 1024
#define HARD_SECTOR_SIZE 512
#define MESSAGE_OFFSET 29 /* Offset of message in above code */
#define MSG_OFFSET_OFFSET 3
#define BOOT_SIGN 0xAA55 /* Boot sector magic number */
#define MSDOS_NAME 11 /* maximum name length */
#define MSDOS_EXT_SIGN 0x29 /* extended boot sector signature */
#define MSDOS_FAT32_SIGN "FAT32 " /* FAT32 filesystem signature */
#define FAT_EOF (0x0ffffff8)
#define SECTORS_PER_BLOCK (BLOCK_SIZE / HARD_SECTOR_SIZE)
#define BOOTCODE_SIZE 448
#define BOOTCODE_FAT32_SIZE 420
#define HDIO_GETGEO 0x0301
#define BLKGETSIZE _IO(0x12, 96) /* return device size /512 (long *arg) */
#define BLKGETSIZE64 _IOR(0x12, 114, size_t) /* return device size in bytes (u64 *arg) */
struct hd_geometry
{
unsigned char heads;
unsigned char sectors;
unsigned short cylinders; /* truncated */
unsigned long start;
};
struct msdos_dir_entry
{
uint8_t name[MSDOS_NAME]; /* name including extension */
uint8_t attr; /* attribute bits */
uint8_t ntbyte; /* Case for base and extension, encryption flags and padding size */
uint8_t ctime_cs; /* Creation time, centiseconds (0-199) */
uint16_t ctime; /* Creation time */
uint16_t cdate; /* Creation date */
uint16_t adate; /* Last access date */
uint16_t starthi; /* High 16 bits of cluster in FAT32 */
uint16_t time, date, start; /* time, date and first cluster */
uint32_t size; /* file size (in bytes) */
} __attribute__((packed));
struct msdos_volume_info
{
uint8_t drive_number; /* BIOS drive number */
uint8_t boot_flags; /* bit 0: dirty, bit 1: need surface test */
uint8_t ext_boot_sign; /* 0x29 if fields below exist (DOS 3.3+) */
uint8_t volume_id[4]; /* Volume ID number */
uint8_t volume_label[11]; /* Volume label */
uint8_t fs_type[8]; /* Typically FAT12 or FAT16 */
} __attribute__((packed));
struct msdos_boot_sector
{
uint8_t boot_jump[3]; /* Boot strap short or near jump */
uint8_t system_id[8]; /* Name - can be used to special case
partition manager volumes */
uint16_t sector_size; /* bytes per logical sector */
uint8_t cluster_size; /* sectors/cluster */
uint16_t reserved; /* reserved sectors */
uint8_t fats; /* number of FATs */
uint16_t dir_entries; /* root directory entries */
uint16_t sectors; /* number of sectors */
uint8_t media; /* media code (unused) */
uint16_t fat_length; /* sectors/FAT */
uint16_t secs_track; /* sectors per track */
uint16_t heads; /* number of heads */
uint32_t hidden; /* hidden sectors (unused) */
uint32_t total_sect; /* number of sectors (if sectors == 0) */
struct
{
uint32_t fat32_length; /* sectors/FAT */
uint16_t flags; /* bit 8: fat mirroring, low 4: active fat */
uint8_t version[2]; /* major, minor filesystem version */
uint32_t root_cluster; /* first cluster in root directory */
uint16_t info_sector; /* filesystem info sector */
uint16_t backup_boot; /* backup boot sector */
uint16_t reserved2[6]; /* Unused */
struct msdos_volume_info vi;
uint8_t boot_code[BOOTCODE_FAT32_SIZE];
} __attribute__((packed)) fat32;
uint16_t boot_sign;
} __attribute__((packed));
struct fat32_fsinfo
{
uint32_t reserved1; /* Nothing as far as I can tell */
uint32_t signature; /* 0x61417272L */
uint32_t free_clusters; /* Free cluster count. -1 if unknown */
uint32_t next_cluster; /* Most recently allocated cluster.
* Unused under Linux. */
uint32_t reserved2[4];
} __attribute__((packed));
static int dev = -1; /* FS block device file handle */
static unsigned sector_size = 512; /* Size of a logical sector */
static off_t part_sector = 0; /* partition offset in sector */
static int orphaned_sectors = 0; /* Sectors that exist in the last block of filesystem */
static unsigned long long blocks; /* Number of blocks in filesystem */
int geom_heads;
int geom_sectors;
int geom_start;
static int reserved_sectors = 0; /* Number of reserved sectors */
static unsigned hidden_sectors = 0; /* Number of hidden sectors */
static uint32_t num_sectors; /* Total number of sectors in device */
static unsigned fat_entries; /* total entries in FAT table (including reserved) */
static struct msdos_boot_sector bs; /* Boot sector data */
static long volume_id; /* Volume ID number */
static int start_data_sector; /* Sector number for the start of the data area */
static int start_data_block; /* Block number for the start of the data area */
static int size_root_dir; /* Size of the root directory in bytes */
static struct msdos_dir_entry *root_dir; /* Root directory */
static int root_dir_entries = 512; /* Number of root directory entries */
static unsigned char *fat; /* File allocation table */
static struct fat32_fsinfo *info;
static char *blank_sector; /* Blank sector - all zeros */
static unsigned char *info_sector_buffer; /* FAT32 info sector */
char dummy_boot_code[BOOTCODE_SIZE] = "\x0e" /* push cs */
"\x1f" /* pop ds */
"\xbe\x5b\x7c" /* mov si, offset message_txt */
/* write_msg: */
"\xac" /* lodsb */
"\x22\xc0" /* and al, al */
"\x74\x0b" /* jz key_press */
"\x56" /* push si */
"\xb4\x0e" /* mov ah, 0eh */
"\xbb\x07\x00" /* mov bx, 0007h */
"\xcd\x10" /* int 10h */
"\x5e" /* pop si */
"\xeb\xf0" /* jmp write_msg */
/* key_press: */
"\x32\xe4" /* xor ah, ah */
"\xcd\x16" /* int 16h */
"\xcd\x19" /* int 19h */
"\xeb\xfe" /* foo: jmp foo */
/* message_txt: */
"This is not a bootable disk. Please insert a bootable floppy and\r\n"
"press any key to try again ... \r\n";
int get_size(int fd, unsigned long *size)
{
if (ioctl(dev, BLKGETSIZE64, size) < 0)
{
return -1;
}
}
int get_geometry(int fd, unsigned int *h, unsigned int *s, unsigned int *st)
{
struct hd_geometry geometry;
if (ioctl(fd, HDIO_GETGEO, &geometry) == 0)
{
*h = geometry.heads;
*s = geometry.sectors;
*st = geometry.start;
return 0;
}
}
uint32_t generate_volume_id(void)
{
struct timeval now;
if (gettimeofday(&now, NULL) != 0 || now.tv_sec == (time_t)-1 || now.tv_sec < 0)
{
srand(getpid());
/* rand() returns int from [0,RAND_MAX], therefore only 31 bits */
return (((uint32_t)(rand() & 0xFFFF)) << 16) | ((uint32_t)(rand() & 0xFFFF));
}
/* volume ID = current time, fudged for more uniqueness */
return ((uint32_t)now.tv_sec << 20) | (uint32_t)now.tv_usec;
}
/* Set a byte in FAT to a particular value */
static int set_FAT_byte(int index, unsigned char value)
{
unsigned char old;
old = fat[index];
fat[index] = value;
return old != value;
}
/* Mark the specified cluster as having a particular value */
static int mark_FAT_cluster(int cluster, unsigned int value)
{
int changed = 0;
if (cluster < 0 || cluster >= fat_entries)
printf("Internal error: out of range cluster number in mark_FAT_cluster\n");
value &= 0xfffffff;
changed |= set_FAT_byte(4 * cluster, value & 0x000000ff);
changed |= set_FAT_byte(4 * cluster + 1, (value & 0x0000ff00) >> 8);
changed |= set_FAT_byte(4 * cluster + 2, (value & 0x00ff0000) >> 16);
changed |= set_FAT_byte(4 * cluster + 3, (value & 0xff000000) >> 24);
return changed;
}
#define seekto(pos, errstr) \
do \
{ \
off_t __pos = (pos); \
if (lseek(dev, part_sector * sector_size + __pos, SEEK_SET) != part_sector * sector_size + __pos) \
printf("seek to " errstr " failed whilst writing tables\n"); \
} while (0)
/* Compute ceil(a/b) */
static inline int cdiv(int a, int b)
{
return (a + b - 1) / b;
}
static unsigned int align_object(unsigned int sectors, unsigned int clustsize)
{
return (sectors + clustsize - 1) & ~(clustsize - 1);
}
static void write_tables(void)
{
int x;
int fat_length;
fat_length = le32toh(bs.fat32.fat32_length);
seekto(0, "start of device");
/* clear all reserved sectors */
for (x = 0; x < reserved_sectors; ++x)
write(dev, blank_sector, sector_size);
/* seek back to sector 0 and write the boot sector */
seekto(0, "boot sector");
write(dev, (char *)&bs, sizeof(struct msdos_boot_sector));
/* on FAT32, write the info sector and backup boot sector */
seekto(le16toh(bs.fat32.info_sector) * sector_size, "info sector");
write(dev, info_sector_buffer, 512);
/* seek to start of FATS and write them all */
seekto(reserved_sectors * sector_size, "first FAT");
for (x = 1; x <= bs.fats; x++)
{
int y;
int blank_fat_length = fat_length - 1;
write(dev, fat, sector_size);
for (y = 0; y < blank_fat_length; y++)
write(dev, blank_sector, sector_size);
}
/* Write the root directory. On FAT12/16 it is directly after the last
* FAT. On FAT32 seek to root cluster. */
unsigned int root_cluster = le32toh(bs.fat32.root_cluster);
off_t root_sector = (off_t)reserved_sectors + bs.fats * fat_length +
(root_cluster - 2) * bs.cluster_size;
seekto(root_sector * sector_size, "root sector");
write(dev, (char *)root_dir, size_root_dir);
if (blank_sector)
free(blank_sector);
free(info_sector_buffer);
free(root_dir); /* Free up the root directory space from setup_tables */
free(fat); /* Free up the fat table space reserved during setup_tables */
fsync(dev);
}
int main(int argc, char **argv)
{
unsigned long long cblocks = 0;
unsigned int sec_per_track;
unsigned int heads;
unsigned int media = 0xf8;
unsigned int cluster_size = 4; /* starting point for FAT12 and FAT16 */
int def_root_dir_entries = 512;
unsigned long size;
unsigned char dummy_boot_jump[3] = {0xeb, 0x3c, 0x90};
struct msdos_volume_info *vi = &bs.fat32.vi;
static time_t create_time = -1; /* Creation time */
struct tm *ctime;
dev = open(argv[1], O_EXCL | O_RDWR); /* Is it a suitable device to build the FS on? */
if (dev < 0)
{
printf("unable to open %s", argv[1]);
return -1;
}
// 获取存储字节
get_size(dev, &size);
get_geometry(dev, &geom_heads, &geom_sectors, &geom_start);
printf("get size %lu %d %d\n", size, geom_heads, geom_sectors);
cblocks = (size - part_sector * sector_size) / BLOCK_SIZE;
orphaned_sectors = ((size - part_sector * sector_size) % BLOCK_SIZE) / sector_size;
blocks = cblocks;
unsigned long long int sectors = size / sector_size;
cluster_size = sectors > 32 * 1024 * 1024 * 2 ? 64 : sectors > 16 * 1024 * 1024 * 2 ? 32
: sectors > 8 * 1024 * 1024 * 2 ? 16
: sectors > 260 * 1024 * 2 ? 8
: 1;
if (!bs.secs_track)
bs.secs_track = htole16(geom_sectors);
if (!bs.heads)
bs.heads = htole16(geom_heads);
bs.media = media;
bs.cluster_size = cluster_size;
memcpy((char *)bs.system_id, "mkfs.fat", strlen("mkfs.fat"));
memcpy(bs.boot_jump, dummy_boot_jump, 3);
/* Patch in the correct offset to the boot code */
bs.boot_jump[1] = (bs.fat32.boot_code) - 2;
int offset = (char *)&bs.fat32.boot_code -
(char *)&bs + MESSAGE_OFFSET + 0x7c00;
dummy_boot_code[BOOTCODE_FAT32_SIZE - 1] = 0;
memcpy(bs.fat32.boot_code, dummy_boot_code, BOOTCODE_FAT32_SIZE);
bs.fat32.boot_code[MSG_OFFSET_OFFSET] = offset & 0xff;
bs.fat32.boot_code[MSG_OFFSET_OFFSET + 1] = offset >> 8;
hidden_sectors = geom_start + part_sector;
reserved_sectors = 32;
bs.reserved = htole16(reserved_sectors);
bs.fats = (char)2;
bs.boot_sign = htole16(BOOT_SIGN);
bs.hidden = htole32(hidden_sectors);
num_sectors =
(long long)(blocks * BLOCK_SIZE / sector_size) + orphaned_sectors;
num_sectors = num_sectors / le16toh(bs.secs_track) * le16toh(bs.secs_track);
// vi
vi->drive_number = 0x80;
volume_id = generate_volume_id();
vi->volume_id[0] = (unsigned char)(volume_id & 0x000000ff);
vi->volume_id[1] = (unsigned char)((volume_id & 0x0000ff00) >> 8);
vi->volume_id[2] = (unsigned char)((volume_id & 0x00ff0000) >> 16);
vi->volume_id[3] = (unsigned char)(volume_id >> 24);
memcpy(vi->volume_label, "hhh pdx ", MSDOS_NAME);
// 计算簇 计算FAT表
unsigned fatdata32; /* Sectors for FATs + data area (FAT32) */
unsigned fatlength32;
unsigned maxclust32;
unsigned clust32;
int maxclustsize;
unsigned root_dir_sectors = cdiv(root_dir_entries * 32, sector_size);
unsigned long long fatdata_bytes = sector_size *
(num_sectors - reserved_sectors - root_dir_sectors);
/* An initial guess for bs.cluster_size should already be set */
maxclustsize = 128;
do
{
#define ALIGNED_DATA(fat_length) (num_sectors - align_object( \
reserved_sectors + bs.fats * fat_length + root_dir_sectors, bs.cluster_size))
long cluster_bytes = bs.cluster_size * sector_size;
clust32 = (fatdata_bytes - bs.fats * 8) / (cluster_bytes + bs.fats * 4);
fatlength32 = cdiv((clust32 + 2) * 4, sector_size); // 2493 //
/* Need to recalculate number of clusters, since the unused parts of the
* FATS and data area together could make up space for an additional,
* not really present cluster. */
clust32 = ALIGNED_DATA(fatlength32) / bs.cluster_size; // 1891910 //单位块
fatlength32 = cdiv((clust32 + 2) * 4, sector_size); // 14781 //单位块
maxclust32 = fatlength32 * sector_size / 4;
if ((clust32))
break;
bs.cluster_size <<= 1;
} while (bs.cluster_size && bs.cluster_size <= maxclustsize);
printf("reserved_sectors:%d num_sectors:%d fatdata32:%d clust32:%d fatlength32:%d\n",
reserved_sectors, num_sectors, fatdata32, clust32, fatlength32);
bs.fat_length = htole16(0);
bs.fat32.fat32_length = htole32(fatlength32);
memcpy(vi->fs_type, MSDOS_FAT32_SIGN, 8);
/* Adjust the reserved number of sectors for alignment */
reserved_sectors = align_object(reserved_sectors, bs.cluster_size);
bs.reserved = htole16(reserved_sectors);
/* Adjust the number of root directory entries to help enforce alignment */
root_dir_entries = align_object(root_dir_sectors, bs.cluster_size) * (sector_size >> 5);
bs.sector_size = htole16((uint16_t)sector_size);
bs.dir_entries = htole16((uint16_t)root_dir_entries);
bs.fat32.flags = htole16(0);
bs.fat32.version[0] = 0;
bs.fat32.version[1] = 0;
bs.fat32.root_cluster = htole32(2);
int info_sector = 1;
bs.fat32.info_sector = htole16(info_sector);
bs.fat32.backup_boot = htole16(0);
memset(&bs.fat32.reserved2, 0, sizeof(bs.fat32.reserved2));
bs.sectors = htole16(0);
bs.total_sect = htole32(num_sectors);
vi->ext_boot_sign = MSDOS_EXT_SIGN;
fat_entries = clust32 + 2;
/* The two following vars are in hard sectors, i.e. 512 byte sectors! */
start_data_sector = (reserved_sectors + bs.fats * fatlength32 +
cdiv(root_dir_entries * 32, sector_size)) *
(sector_size / HARD_SECTOR_SIZE);
start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) /
SECTORS_PER_BLOCK;
// 创建FAT表
fat = (unsigned char *)malloc(sector_size);
memset(fat, 0, sector_size);
mark_FAT_cluster(0, 0xffffffff); /* Initial fat entries */
mark_FAT_cluster(1, 0xffffffff);
fat[0] = (unsigned char)bs.media; /* Put media type in first byte! */
/* Mark cluster 2 as EOF (used for root dir) */
mark_FAT_cluster(2, FAT_EOF);
size_root_dir = bs.cluster_size * sector_size;
root_dir = (struct msdos_dir_entry *)malloc(size_root_dir);
memset(root_dir, 0, size_root_dir);
struct msdos_dir_entry *de = &root_dir[0];
memcpy(de->name, vi->volume_label, MSDOS_NAME);
if (de->name[0] == 0xe5)
de->name[0] = 0x05;
de->attr = 8;
ctime = gmtime(&create_time);
if (ctime && ctime->tm_year >= 80 && ctime->tm_year <= 207)
{
de->time = htole16((unsigned short)((ctime->tm_sec >> 1) +
(ctime->tm_min << 5) +
(ctime->tm_hour << 11)));
de->date = htole16((unsigned short)(ctime->tm_mday +
((ctime->tm_mon + 1) << 5) +
((ctime->tm_year - 80) << 9)));
}
else
{
/* fallback to 1.1.1980 00:00:00 */
de->time = htole16(0);
de->date = htole16(1 + (1 << 5));
}
de->ctime_cs = 0;
de->ctime = de->time;
de->cdate = de->date;
de->adate = de->date;
de->starthi = htole16(0);
de->start = htole16(0);
de->size = htole32(0);
//fsinfo 表
info_sector_buffer = malloc(sector_size);
memset(info_sector_buffer, 0, sector_size);
/* fsinfo structure is at offset 0x1e0 in info sector by observation */
info = (struct fat32_fsinfo *)(info_sector_buffer + 0x1e0);
/* Info sector magic */
info_sector_buffer[0] = 'R';
info_sector_buffer[1] = 'R';
info_sector_buffer[2] = 'a';
info_sector_buffer[3] = 'A';
/* Magic for fsinfo structure */
info->signature = htole32(0x61417272);
/* We've allocated cluster 2 for the root dir. */
info->free_clusters = htole32(clust32 - 1);
info->next_cluster = htole32(2);
/* Info sector also must have boot sign */
*(uint16_t *)(info_sector_buffer + 0x1fe) = htole16(BOOT_SIGN);
blank_sector = malloc(sector_size);
memset(blank_sector, 0, sector_size);
// 写入存储器
write_tables();
close(dev);
return 0;
}
代码讲解:
如图保留区在FAT32占用一般为32个扇区,DBR一般占用1个扇区(512字节),FSINFO占用一个扇区(512字节)。剩下30个扇区基本上不会有使用
在代码中DBR用msdos_boot_sector表示 , FSINFO 信息扇区用fat32_fsinfo表示,根目录为:msdos_dir_entry
代码步骤:
1.获取磁盘 分区总的存储空间字节数
2.填充msdos_boot_sector
3.计算簇
// 计算簇 计算FAT表
unsigned fatdata32; /* Sectors for FATs + data area (FAT32) */
unsigned fatlength32;
unsigned maxclust32;
unsigned clust32;
int maxclustsize;
unsigned root_dir_sectors = cdiv(root_dir_entries * 32, sector_size);
unsigned long long fatdata_bytes = sector_size *
(num_sectors - reserved_sectors - root_dir_sectors);
/* An initial guess for bs.cluster_size should already be set */
maxclustsize = 128;
do
{
#define ALIGNED_DATA(fat_length) (num_sectors - align_object( \
reserved_sectors + bs.fats * fat_length + root_dir_sectors, bs.cluster_size))
long cluster_bytes = bs.cluster_size * sector_size;
clust32 = (fatdata_bytes - bs.fats * 8) / (cluster_bytes + bs.fats * 4);//单位簇
fatlength32 = cdiv((clust32 + 2) * 4, sector_size); // 2493 //单位簇
/* Need to recalculate number of clusters, since the unused parts of the
* FATS and data area together could make up space for an additional,
* not really present cluster. */
clust32 = ALIGNED_DATA(fatlength32) / bs.cluster_size; // 1891910 //簇转换为块
fatlength32 = cdiv((clust32 + 2) * 4, sector_size); // 14781 //簇转换为块
maxclust32 = fatlength32 * sector_size / 4;
if ((clust32))
break;
bs.cluster_size <<= 1;
} while (bs.cluster_size && bs.cluster_size <= maxclustsize);
clust32 = (fatdata_bytes - bs.fats * 8) / (cluster_bytes + bs.fats * 4);
- fatdata_bytes:表示FAT文件系统中可用于数据存储的总字节数。
-bs.fats:表示文件分配表(FAT)的数量。
- cluster_bytes:表示单个簇的大小(以字节为单位)。
因为FAT表4个字节表示占用一个簇,有两张FAT表。默认FAT表 0 1号簇被占用,所以要-bs.fats*8
这个公式通过调整总数据字节数(fatdata_bytes),减去FAT表占用的空间,然后除以调整后的簇大小,来计算簇的数量(clust32)
4.创建FAT表
fat = (unsigned char *)malloc(sector_size);
memset(fat, 0, sector_size);
mark_FAT_cluster(0, 0xffffffff); /* Initial fat entries */
mark_FAT_cluster(1, 0xffffffff);
fat[0] = (unsigned char)bs.media; /* Put media type in first byte! */
/* Mark cluster 2 as EOF (used for root dir) */
mark_FAT_cluster(2, FAT_EOF);
size_root_dir = bs.cluster_size * sector_size;
5.分配根目录赋值
root_dir = (struct msdos_dir_entry *)malloc(size_root_dir);
memset(root_dir, 0, size_root_dir);
struct msdos_dir_entry *de = &root_dir[0];
memcpy(de->name, vi->volume_label, MSDOS_NAME);
if (de->name[0] == 0xe5)
de->name[0] = 0x05;
de->attr = 8;
ctime = gmtime(&create_time);
if (ctime && ctime->tm_year >= 80 && ctime->tm_year <= 207)
{
de->time = htole16((unsigned short)((ctime->tm_sec >> 1) +
(ctime->tm_min << 5) +
(ctime->tm_hour << 11)));
de->date = htole16((unsigned short)(ctime->tm_mday +
((ctime->tm_mon + 1) << 5) +
((ctime->tm_year - 80) << 9)));
}
else
{
/* fallback to 1.1.1980 00:00:00 */
de->time = htole16(0);
de->date = htole16(1 + (1 << 5));
}
de->ctime_cs = 0;
de->ctime = de->time;
de->cdate = de->date;
de->adate = de->date;
de->starthi = htole16(0);
de->start = htole16(0);
de->size = htole32(0);
6.实现FSINFO表
info_sector_buffer = malloc(sector_size);
memset(info_sector_buffer, 0, sector_size);
/* fsinfo structure is at offset 0x1e0 in info sector by observation */
info = (struct fat32_fsinfo *)(info_sector_buffer + 0x1e0);
/* Info sector magic */
info_sector_buffer[0] = 'R';
info_sector_buffer[1] = 'R';
info_sector_buffer[2] = 'a';
info_sector_buffer[3] = 'A';
/* Magic for fsinfo structure */
info->signature = htole32(0x61417272);
/* We've allocated cluster 2 for the root dir. */
info->free_clusters = htole32(clust32 - 1);
info->next_cluster = htole32(2);
/* Info sector also must have boot sign */
*(uint16_t *)(info_sector_buffer + 0x1fe) = htole16(BOOT_SIGN);
7.写入磁盘,建立文件系统:
static void write_tables(void)
{
int x;
int fat_length;
fat_length = le32toh(bs.fat32.fat32_length);
seekto(0, "start of device");
/* clear all reserved sectors */
for (x = 0; x < reserved_sectors; ++x)
write(dev, blank_sector, sector_size);
/* seek back to sector 0 and write the boot sector */
seekto(0, "boot sector");
write(dev, (char *)&bs, sizeof(struct msdos_boot_sector));
/* on FAT32, write the info sector and backup boot sector */
seekto(le16toh(bs.fat32.info_sector) * sector_size, "info sector");
write(dev, info_sector_buffer, 512);
/* seek to start of FATS and write them all */
seekto(reserved_sectors * sector_size, "first FAT");
for (x = 1; x <= bs.fats; x++)
{
int y;
int blank_fat_length = fat_length - 1;
write(dev, fat, sector_size);
for (y = 0; y < blank_fat_length; y++)
write(dev, blank_sector, sector_size);
}
/* Write the root directory. On FAT12/16 it is directly after the last
* FAT. On FAT32 seek to root cluster. */
unsigned int root_cluster = le32toh(bs.fat32.root_cluster);
off_t root_sector = (off_t)reserved_sectors + bs.fats * fat_length +
(root_cluster - 2) * bs.cluster_size;
seekto(root_sector * sector_size, "root sector");
write(dev, (char *)root_dir, size_root_dir);
if (blank_sector)
free(blank_sector);
free(info_sector_buffer);
free(root_dir); /* Free up the root directory space from setup_tables */
free(fat); /* Free up the fat table space reserved during setup_tables */
fsync(dev);
}
运行
~/workSpace$ sudo ./a.out /dev/sda1
get size 10737418240 64 32
reserved_sectors:32 num_sectors:20971520 fatdata32:1 clust32:1310460 fatlength32:10238
workSpace$ sudo ./a.out /dev/sda2
get size 3221225472 64 32
reserved_sectors:32 num_sectors:6291456 fatdata32:1 clust32:784891 fatlength32:6132