// // Created by rick on 07-02-21. // #include #include #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 { uint8_t jump[3]; char oem_identifier[8]; uint16_t bytes_per_sector; uint8_t sectors_per_cluster; uint16_t reserved_sectors; uint8_t fats; uint16_t directories; uint16_t total_sectors; uint8_t media_descriptor; uint16_t sectors_per_fat; uint16_t sectors_per_track; uint16_t heads_sides; uint32_t hidden_sectors; uint32_t large_total_sectors; } att_packed bpb; union { struct { uint8_t drive_number; uint8_t flags; uint8_t signature; uint32_t serial; uint8_t label[11]; uint8_t system_id[8]; } att_packed ebr_12_16; struct { uint32_t sectors_per_fat; uint16_t flags; uint16_t fat_version; uint32_t root_directory; uint16_t fs_info; uint16_t backup_boot; uint8_t reserved[12]; uint8_t drive_number; uint8_t flags_nt; uint8_t signature; uint32_t serial; uint8_t label[11]; uint8_t system_id[8]; } att_packed ebr_32; }; } att_packed fat_bpb; typedef struct { uint8_t hours: 5; uint8_t minutes: 6; uint8_t seconds: 5; } att_packed time_83; typedef struct { uint8_t hours: 5; uint8_t minutes: 6; uint8_t seconds: 5; } att_packed date_83; typedef struct { union { struct { uint8_t name[11]; uint8_t attributes; uint8_t reserved; uint8_t created_seconds; time_83 created_time; date_83 created_date; date_83 access_date; uint16_t high_first_cluster; time_83 last_mod_time; date_83 last_mod_date; uint16_t low_first_cluster; uint32_t file_size; } att_packed name_83; struct { uint8_t order; uint16_t text_1[5]; uint8_t attribute; uint8_t long_entry_type; uint8_t checksum; uint16_t text_2[6]; uint16_t reserved; uint16_t text_3[2]; } att_packed long_name; }; } att_packed fat_directory_entry; void print_chars(char *chars, int amount) { for (int i = 0; i < amount; ++i) { if (chars[i] == 0) break; printf("%c", chars[i]); } } uint8_t att_used fat_check_device(const block_device_t *device) { uint8_t *first_sector = malloc(512); if (first_sector == NULL) { printf("No mem\n"); return BLOCK_DEV_DRIVER_CHECK_NO_MATCH; } uint8_t result = device->access(device, BLOCK_DEV_DIRECTION_READ, 0, 1, first_sector); if (result != BLOCK_DEV_ACCESS_OK) { printf("Could not access device\n"); free(first_sector); return BLOCK_DEV_DRIVER_CHECK_NO_MATCH; } if (first_sector[510] != 0x55 || first_sector[511] != 0xAA) { printf("No boot signature\n"); free(first_sector); return BLOCK_DEV_DRIVER_CHECK_NO_MATCH; } fat_bpb bpb; memcpy((uint8_t *) &bpb, first_sector, sizeof(fat_bpb)); free(first_sector); if (bpb.bpb.sectors_per_fat == 0 || bpb.bpb.sectors_per_cluster == 0) { printf("Definitely not FAT\n"); return BLOCK_DEV_DRIVER_CHECK_NO_MATCH; } printf("OEM: "); print_chars(bpb.bpb.oem_identifier, 11); printf("\n"); uint32_t total_sectors = bpb.bpb.total_sectors == 0 ? bpb.bpb.large_total_sectors : bpb.bpb.total_sectors; uint32_t fat_size = bpb.bpb.sectors_per_fat == 0 ? bpb.ebr_32.sectors_per_fat : bpb.bpb.sectors_per_fat; uint32_t root_dir_sectors = ((bpb.bpb.directories * 32) + (bpb.bpb.bytes_per_sector - 1)) / bpb.bpb.bytes_per_sector; uint32_t first_data_sector = bpb.bpb.reserved_sectors + (bpb.bpb.fats * fat_size) + root_dir_sectors; uint32_t first_fat_sector = bpb.bpb.reserved_sectors; uint32_t data_sectors = total_sectors - (bpb.bpb.reserved_sectors + (bpb.bpb.fats * fat_size) + root_dir_sectors); uint32_t total_clusters = data_sectors / bpb.bpb.sectors_per_cluster; uint8_t 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 uint32_t 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 { uint32_t sector; uint32_t value; } table_result; table_result get_fat_table_value(uint8_t fat_type, const uint8_t *fat_table, uint32_t active_cluster, uint32_t first_fat_cluster, uint32_t sector_size) { uint32_t 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 = *(uint16_t *) &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 = *(uint16_t *) &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 = *(uint32_t *) &fat_table[fat_offset % sector_size] & 0x0FFFFFFF; return result; default: break; } return result; } BLOCK_DEV_DRIVER(900) = { .name = "fat", .check_device = fat_check_device, .free_device = NULL, // todo }; // steal validation code from here https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/fs/fat/inode.c#L1456