You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自制32位保护模式内核的FAT/EXT2文件系统C语言实现指引

Got it, let's walk through implementing a FAT32 file system for your custom 32-bit protected mode OS—since FAT32 is simpler to bootstrap than EXT2, I'll focus on that first, then touch on EXT2 basics if you want to dive deeper later. All code here is tailored for your GCC 10.2 cross-compiled environment (no libc dependencies, since you're building a standalone kernel).


Step 1: Implement Basic Disk Access

First, you need to read/write disk sectors via PIO (Programmed Input/Output)—no fancy drivers needed for this. Here's a minimal implementation:

Assembly Helpers (for port I/O)

Save this as io.asm and link it into your kernel:

global inb
global outb

inb:
    mov edx, [esp + 4]
    in al, dx
    ret

outb:
    mov edx, [esp + 4]
    mov al, [esp + 8]
    out dx, al
    ret

C Disk Read/Write Functions

#include <stdint.h>

// Read a single sector from LBA address into buf
void read_sector(uint32_t lba, uint8_t *buf) {
    // Send LBA address to disk ports
    outb(0x1F6, 0xE0 | ((lba >> 24) & 0xF));
    outb(0x1F2, 1);                // Number of sectors to read
    outb(0x1F3, (uint8_t)lba);
    outb(0x1F4, (uint8_t)(lba >> 8));
    outb(0x1F5, (uint8_t)(lba >> 16));
    outb(0x1F7, 0x20);             // Issue read command

    // Wait for disk to be ready
    while (!(inb(0x1F7) & 0x80));
    
    // Read 512 bytes (1 sector) into buf
    uint16_t *ptr = (uint16_t*)buf;
    for (int i = 0; i < 256; i++) {
        *ptr++ = inw(0x1F0);
    }
}

// Write a single sector from buf to LBA address
void write_sector(uint32_t lba, uint8_t *buf) {
    outb(0x1F6, 0xE0 | ((lba >> 24) & 0xF));
    outb(0x1F2, 1);
    outb(0x1F3, (uint8_t)lba);
    outb(0x1F4, (uint8_t)(lba >> 8));
    outb(0x1F5, (uint8_t)(lba >> 16));
    outb(0x1F7, 0x30);             // Issue write command

    while (!(inb(0x1F7) & 0x80));
    
    uint16_t *ptr = (uint16_t*)buf;
    for (int i = 0; i < 256; i++) {
        outw(0x1F0, *ptr++);
    }
    // Wait for write completion
    while (!(inb(0x1F7) & 0x80));
}

Step 2: Parse FAT32 BPB (BIOS Parameter Block)

The BPB lives in the first sector of your FAT32 partition and contains critical filesystem metadata (sector size, cluster size, FAT location, etc.). Define a packed struct to map it:

typedef struct {
    uint8_t jump[3];
    uint8_t oem[8];
    uint16_t bytes_per_sector;
    uint8_t sectors_per_cluster;
    uint16_t reserved_sectors;
    uint8_t fat_count;
    uint16_t root_dir_entries;
    uint16_t total_sectors_16;
    uint8_t media_type;
    uint16_t fat_size_16;
    uint16_t sectors_per_track;
    uint16_t heads;
    uint32_t hidden_sectors;
    uint32_t total_sectors_32;
    uint32_t fat_size_32;
    uint16_t ext_flags;
    uint16_t fs_version;
    uint32_t root_cluster;
    uint16_t fs_info_sector;
    uint16_t backup_boot_sector;
    uint8_t reserved[12];
    uint8_t drive_number;
    uint8_t reserved1;
    uint8_t boot_signature;
    uint32_t volume_id;
    uint8_t volume_label[11];
    uint8_t fs_type[8];
} __attribute__((packed)) FAT32_BPB;

FAT32_BPB g_fat_bpb;

// Load and validate the FAT32 BPB from the partition's start LBA
void load_fat32_bpb(uint32_t partition_start_lba) {
    read_sector(partition_start_lba, (uint8_t*)&g_fat_bpb);
    // Check if this is actually a FAT32 partition
    if (memcmp(g_fat_bpb.fs_type, "FAT32   ", 8) != 0) {
        // Panic if not FAT32 (replace with your error handling)
        while(1);
    }
}

// Minimal memcmp implementation (no libc)
int memcmp(const void *a, const void *b, size_t len) {
    const uint8_t *ap = (const uint8_t*)a;
    const uint8_t *bp = (const uint8_t*)b;
    for (size_t i = 0; i < len; i++) {
        if (ap[i] != bp[i]) return ap[i] - bp[i];
    }
    return 0;
}

Step 3: FAT Table Operations

The FAT table tracks cluster links (which cluster comes next for a file/directory). Here's how to read and update FAT entries:

// Get the next cluster in the chain for a given current cluster
uint32_t get_next_cluster(uint32_t current_cluster, uint32_t partition_start_lba) {
    uint32_t fat_start_lba = partition_start_lba + g_fat_bpb.reserved_sectors;
    uint32_t fat_offset = current_cluster * 4;
    uint32_t sector = fat_start_lba + (fat_offset / g_fat_bpb.bytes_per_sector);
    uint32_t offset = fat_offset % g_fat_bpb.bytes_per_sector;

    uint8_t sector_buf[512];
    read_sector(sector, sector_buf);
    uint32_t next_cluster = *(uint32_t*)(sector_buf + offset);
    // Mask out reserved high 4 bits in FAT32 entries
    return next_cluster & 0x0FFFFFFF;
}

// Update a FAT entry to point to a new cluster (or mark as end of chain)
void update_fat_entry(uint32_t cluster, uint32_t value, uint32_t partition_start_lba) {
    uint32_t fat_start_lba = partition_start_lba + g_fat_bpb.reserved_sectors;
    uint32_t fat_offset = cluster * 4;
    uint32_t sector = fat_start_lba + (fat_offset / g_fat_bpb.bytes_per_sector);
    uint32_t offset = fat_offset % g_fat_bpb.bytes_per_sector;

    uint8_t sector_buf[512];
    read_sector(sector, sector_buf);
    // Preserve reserved high 4 bits
    *(uint32_t*)(sector_buf + offset) = (value & 0x0FFFFFFF) | 0xF0000000;
    write_sector(sector, sector_buf);
}

Step 4: Directory Listing

FAT32 directories are stored as clusters of 32-byte directory entries. Here's how to list files in a directory (starting with the root directory at g_fat_bpb.root_cluster):

typedef struct {
    uint8_t name[8];
    uint8_t ext[3];
    uint8_t attributes;
    uint8_t reserved;
    uint8_t create_time_tenths;
    uint16_t create_time;
    uint16_t create_date;
    uint16_t last_access_date;
    uint16_t first_cluster_high;
    uint16_t write_time;
    uint16_t write_date;
    uint16_t first_cluster_low;
    uint32_t file_size;
} __attribute__((packed)) FAT32_DirEntry;

// Minimal printf/putchar for output (use your serial/vga output if you have it)
void putchar(char c) {
    while (!(inb(0x3F8 + 5) & 0x20));
    outb(0x3F8, c);
}

void printf(const char *fmt, ...); // Forward declaration

// List all files/directories in a given starting cluster
void list_directory(uint32_t start_cluster, uint32_t partition_start_lba) {
    uint32_t current_cluster = start_cluster;
    while (1) {
        // Calculate the starting LBA of the current cluster
        uint32_t cluster_lba = partition_start_lba 
                            + g_fat_bpb.reserved_sectors 
                            + (g_fat_bpb.fat_count * g_fat_bpb.fat_size_32) 
                            + (current_cluster - 2) * g_fat_bpb.sectors_per_cluster;

        // Read all sectors in the cluster
        for (int i = 0; i < g_fat_bpb.sectors_per_cluster; i++) {
            uint8_t sector_buf[512];
            read_sector(cluster_lba + i, sector_buf);
            FAT32_DirEntry *entries = (FAT32_DirEntry*)sector_buf;

            for (int j = 0; j < 512 / sizeof(FAT32_DirEntry); j++) {
                FAT32_DirEntry *entry = &entries[j];
                // Skip deleted entries or end of directory
                if (entry->name[0] == 0xE5 || entry->name[0] == 0x00) {
                    if (entry->name[0] == 0x00) return;
                    continue;
                }
                // Skip system/volume label entries
                if ((entry->attributes & 0x08) || (entry->attributes & 0x10)) continue;

                // Format filename to 8.3 format
                char filename[13] = {0};
                memcpy(filename, entry->name, 8);
                memcpy(filename + 8, entry->ext, 3);
                printf("%s ", filename);

                // Mark directories
                if (entry->attributes & 0x10) {
                    printf("[DIR]\n");
                } else {
                    printf("(%u bytes)\n", entry->file_size);
                }
            }
        }

        // Move to next cluster in the directory chain
        current_cluster = get_next_cluster(current_cluster, partition_start_lba);
        if (current_cluster >= 0x0FFFFFF8) break; // End of chain
    }
}

// Minimal printf implementation (supports %s and %u)
#include <stdarg.h>
void printf(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    while (*fmt) {
        if (*fmt == '%') {
            fmt++;
            if (*fmt == 's') {
                char *s = va_arg(args, char*);
                while (*s) putchar(*s++);
            } else if (*fmt == 'u') {
                uint32_t num = va_arg(args, uint32_t);
                char buf[10] = {0};
                int i = 9;
                if (num == 0) buf[i--] = '0';
                else while (num > 0) {
                    buf[i--] = (num % 10) + '0';
                    num /= 10;
                }
                for (int j = i+1; j < 10; j++) putchar(buf[j]);
            }
            fmt++;
        } else {
            putchar(*fmt++);
        }
    }
    va_end(args);
}

Step 5: File Reading

To read a file, follow its cluster chain and copy data into a buffer:

// Read a file from a directory entry into buf
void read_file(FAT32_DirEntry *entry, uint8_t *buf, uint32_t partition_start_lba) {
    uint32_t current_cluster = ((uint32_t)entry->first_cluster_high << 16) | entry->first_cluster_low;
    uint32_t bytes_read = 0;

    while (bytes_read < entry->file_size) {
        uint32_t cluster_lba = partition_start_lba 
                            + g_fat_bpb.reserved_sectors 
                            + (g_fat_bpb.fat_count * g_fat_bpb.fat_size_32) 
                            + (current_cluster - 2) * g_fat_bpb.sectors_per_cluster;

        for (int i = 0; i < g_fat_bpb.sectors_per_cluster; i++) {
            if (bytes_read >= entry->file_size) break;
            uint32_t bytes_to_read = g_fat_bpb.bytes_per_sector;
            if (bytes_read + bytes_to_read > entry->file_size) {
                bytes_to_read = entry->file_size - bytes_read;
            }
            uint8_t sector_buf[512];
            read_sector(cluster_lba + i, sector_buf);
            memcpy(buf + bytes_read, sector_buf, bytes_to_read);
            bytes_read += bytes_to_read;
        }

        current_cluster = get_next_cluster(current_cluster, partition_start_lba);
        if (current_cluster >= 0x0FFFFFF8) break;
    }
}

Step 6: File Creation (Simplified)

Creating a file involves finding an empty directory entry, allocating a cluster, and updating the FAT:

// Find a free cluster in the FAT table
uint32_t find_free_cluster(uint32_t partition_start_lba) {
    uint32_t fat_start_lba = partition_start_lba + g_fat_bpb.reserved_sectors;
    uint32_t fat_total_bytes = g_fat_bpb.fat_size_32 * g_fat_bpb.bytes_per_sector;

    for (uint32_t offset = 0; offset < fat_total_bytes; offset += 4) {
        uint32_t sector = fat_start_lba + (offset / g_fat_bpb.bytes_per_sector);
        uint32_t sect_offset = offset % g_fat_bpb.bytes_per_sector;
        uint8_t sector_buf[512];
        read_sector(sector, sector_buf);
        uint32_t cluster_val = *(uint32_t*)(sector_buf + sect_offset) & 0x0FFFFFFF;
        if (cluster_val == 0) {
            return (offset / 4) + 2; // Cluster numbers start at 2
        }
    }
    return 0; // No free clusters
}

// Minimal string helpers
char *strchr(const char *s, int c) {
    while (*s) {
        if (*s == c) return (char*)s;
        s++;
    }
    return NULL;
}

char *strncpy(char *dest, const char *src, size_t n) {
    size_t i;
    for (i = 0; i < n && src[i]; i++) dest[i] = src[i];
    for (; i < n; i++) dest[i] = '\0';
    return dest;
}

// Create a new file in the given parent directory cluster
int create_file(const char *filename, uint32_t parent_cluster, uint32_t partition_start_lba) {
    // Find an empty directory entry
    FAT32_DirEntry *empty_entry = NULL;
    uint32_t entry_sector = 0;
    int entry_idx = 0;
    uint32_t current_cluster = parent_cluster;

    while (1) {
        uint32_t cluster_lba = partition_start_lba 
                            + g_fat_bpb.reserved_sectors 
                            + (g_fat_bpb.fat_count * g_fat_bpb.fat_size_32) 
                            + (current_cluster - 2) * g_fat_bpb.sectors_per_cluster;

        for (int i = 0; i < g_fat_bpb.sectors_per_cluster; i++) {
            uint8_t sector_buf[512];
            read_sector(cluster_lba + i, sector_buf);
            FAT32_DirEntry *entries = (FAT32_DirEntry*)sector_buf;
            for (int j = 0; j < 512 / sizeof(FAT32_DirEntry); j++) {
                if (entries[j].name[0] == 0xE5 || entries[j].name[0] == 0x00) {
                    empty_entry = &entries[j];
                    entry_sector = cluster_lba + i;
                    entry_idx
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:00:24