// // Created by rick on 07-02-21. // #include "fat.h" #include "blockdev.h" #include #include #include #include #define FAT_TYPE_12 1 #define FAT_TYPE_16 2 #define FAT_TYPE_32 3 #define FAT_TYPE_EXFAT 4 typedef struct { struct { u8 jump[3]; char oem_identifier[8]; u16 bytes_per_sector; u8 sectors_per_cluster; u16 reserved_sectors; u8 fats; u16 directories; u16 total_sectors; u8 media_descriptor; u16 sectors_per_fat; u16 sectors_per_track; u16 heads_sides; u32 hidden_sectors; u32 large_total_sectors; } __attribute((packed)) bpb; union { struct { u8 drive_number; u8 flags; u8 signature; u32 serial; u8 label[11]; u8 system_id[8]; } __attribute((packed)) ebr_12_16; struct { u32 sectors_per_fat; u16 flags; u16 fat_version; u32 root_directory; u16 fs_info; u16 backup_boot; u8 reserved[12]; u8 drive_number; u8 flags_nt; u8 signature; u32 serial; u8 label[11]; u8 system_id[8]; } __attribute((packed)) ebr_32; }; } __attribute((packed)) fat_bpb; typedef struct { u8 hours: 5; u8 minutes: 6; u8 seconds: 5; } __attribute((packed)) time_83; typedef struct { u8 hours: 5; u8 minutes: 6; u8 seconds: 5; } __attribute((packed)) date_83; typedef struct { union { struct { u8 name[11]; u8 attributes; u8 reserved; u8 created_seconds; time_83 created_time; date_83 created_date; date_83 access_date; u16 high_first_cluster; time_83 last_mod_time; date_83 last_mod_date; u16 low_first_cluster; u32 file_size; } __attribute((packed)) name_83; struct { u8 order; u16 text_1[5]; u8 attribute; u8 long_entry_type; u8 checksum; u16 text_2[6]; u16 reserved; u16 text_3[2]; } __attribute((packed)) long_name; }; } __attribute((packed)) fat_directory_entry; u8 fat_check_device(const block_device *device, u8 *first_sector); block_dev_driver fat_driver = { .name = "fat", .check_device = fat_check_device, .free_device = NULL, // todo }; void fat_register_block_driver() { block_dev_register_driver(&fat_driver); } void print_chars(char *chars, int amount) { for (int i = 0; i < amount; ++i) { if (chars[i] == 0) break; printf("%c", chars[i]); } } u8 fat_check_device(const block_device *device, u8 *first_sector) { fat_bpb bpb; memcpy((u8 *) &bpb, first_sector, sizeof(fat_bpb)); printf("OEM: "); print_chars(bpb.bpb.oem_identifier, 11); printf("\n"); u32 total_sectors = bpb.bpb.total_sectors == 0 ? bpb.bpb.large_total_sectors : bpb.bpb.total_sectors; u32 fat_size = bpb.bpb.sectors_per_fat == 0 ? bpb.ebr_32.sectors_per_fat : bpb.bpb.sectors_per_fat; u32 root_dir_sectors = ((bpb.bpb.directories * 32) + (bpb.bpb.bytes_per_sector - 1)) / bpb.bpb.bytes_per_sector; u32 first_data_sector = bpb.bpb.reserved_sectors + (bpb.bpb.fats * fat_size) + root_dir_sectors; u32 first_fat_sector = bpb.bpb.reserved_sectors; u32 data_sectors = total_sectors - (bpb.bpb.reserved_sectors + (bpb.bpb.fats * fat_size) + root_dir_sectors); u32 total_clusters = data_sectors / bpb.bpb.sectors_per_cluster; u8 fat_type; if (total_clusters < 4085) { fat_type = FAT_TYPE_12; } else if (total_clusters < 65525) { fat_type = FAT_TYPE_16; } else if (total_clusters < 268435445) { fat_type = FAT_TYPE_32; } else { fat_type = FAT_TYPE_EXFAT; } // dump dir // fat12/16 only u32 first_root_dir_sector = first_data_sector - root_dir_sectors; // todo fat32 fat_directory_entry *entries = malloc(root_dir_sectors * bpb.bpb.bytes_per_sector); device->access(device, BLOCK_DEV_DIRECTION_READ, first_root_dir_sector, root_dir_sectors, entries); // todo read table free(entries); return BLOCK_DEV_DRIVER_CHECK_NO_MATCH; } typedef struct { u32 sector; u32 value; } table_result; table_result get_fat_table_value(u8 fat_type, const u8 *fat_table, u32 active_cluster, u32 first_fat_cluster, u32 sector_size) { u32 fat_offset; table_result result = { .value = 0, .sector = 0, }; switch (fat_type) { case FAT_TYPE_12: fat_offset = active_cluster * (active_cluster / 2); result.sector = first_fat_cluster + (fat_offset / sector_size); result.value = *(u16 *) &fat_table[fat_offset % sector_size]; if (active_cluster & 0x1) { result.value = result.value >> 4; } else { result.value = result.value & 0x0FFF; } return result; case FAT_TYPE_16: fat_offset = active_cluster * 2; result.sector = first_fat_cluster + (fat_offset / sector_size); result.value = *(u16 *) &fat_table[fat_offset % sector_size]; return result; case FAT_TYPE_32: case FAT_TYPE_EXFAT: fat_offset = active_cluster * 4; result.sector = first_fat_cluster + (fat_offset / sector_size); result.value = *(u32 *) &fat_table[fat_offset % sector_size] & 0x0FFFFFFF; return result; default: break; } return result; } // steal validation code from here https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/fs/fat/inode.c#L1456