当前位置: 首页 > article >正文

从零开始写一个建立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


http://www.kler.cn/news/317044.html

相关文章:

  • MFC - 复杂控件_2
  • 【安装教程】Windows环境下Apache Jena Fuseki的安装与配置
  • qt-C++笔记之作用等同的宏和关键字
  • 模拟电路工程师面试题
  • 如何解决npm下载Puppeteer卡死的问题
  • YOLOv9改进策略【注意力机制篇】| 2024 SCI TOP FCAttention 即插即用注意力模块,增强局部和全局特征信息交互
  • Java面试指南(基础篇)
  • 如何选择适合的编程工具提高工作效率
  • Android Studio 真机USB调试运行频繁掉线问题
  • Linux:进程状态和优先级
  • 如何进行「精准测试」?
  • 【C++指南】C++中nullptr的深入解析
  • SSL 最长签发时间是多久?
  • JUC高并发编程1:JUC概述
  • 基于flask常见trick——unicode进制编码绕过
  • JavaEE: 深入探索TCP网络编程的奇妙世界(六)
  • PCL 基于kd树快速删除点云中重叠的点
  • AWS EKS 中的负载均衡和 TLS 配置:全面指南
  • 【二分算法】模板总结
  • QT菜单之快捷菜单设计
  • 解决方案:spark数据进行加工转换后,进行数据存储的时候,发现数据行为空
  • STM32如何修改外部晶振频率和主频
  • 用递归函数实现汉诺塔游戏
  • 使用命令行 (Anaconda Prompt)
  • Spring Boot | 使用 `@Scheduled`: 定时任务的实现与优化
  • MySQL和SQL的区别简单了解和分析使用以及个人总结
  • 面向对象 vs 面向过程
  • Unreal Engine 5 C++: 插件编写03 | MessageDialog
  • 线上搭子小程序:随时随地找搭子!
  • 详解Linux中cat命令