#include "fat12.h" #include "fat12-internals.h" #include "io.h" #include #include struct fat12_partition { struct pang_io *io; // Number of bytes in a sector (512) uint32_t sector_size; uint8_t sector_buffer[512]; // These units are "sectors" uint32_t fat_offset; uint32_t fat_size; uint32_t root_offset; uint32_t first_offset; uint32_t cluster_size; // These are in single-unit quantities uint32_t root_entries; uint32_t cluster_count; uint32_t cluster_bytes; uint32_t sector_count; // Used for traversing directories uint32_t current_cluster; void *fat; }; void fat12_write_u8(void *ptr, uint8_t val) { ((uint8_t *)ptr)[0] = (val >> 0) & 0xff; } uint8_t fat12_read_u8(void *ptr) { uint16_t val = 0; val |= (((uint8_t *)ptr)[0] << 0) & 0xff; return val; } void fat12_write_u16(void *ptr, uint16_t val) { ((uint8_t *)ptr)[0] = (val >> 0) & 0xff; ((uint8_t *)ptr)[1] = (val >> 8) & 0xff; } uint16_t fat12_read_u16(void *ptr) { uint16_t val = 0; val |= (((uint8_t *)ptr)[0] << 0) & 0x00ff; val |= (((uint8_t *)ptr)[1] << 8) & 0xff00; return val; } void fat12_write_u32(void *ptr, uint32_t val) { ((uint8_t *)ptr)[0] = (val >> 0) & 0xff; ((uint8_t *)ptr)[1] = (val >> 8) & 0xff; ((uint8_t *)ptr)[2] = (val >> 16) & 0xff; ((uint8_t *)ptr)[3] = (val >> 24) & 0xff; } uint32_t fat12_read_u32(void *ptr) { uint16_t val = 0; val |= (((uint8_t *)ptr)[0] << 0) & 0x000000ff; val |= (((uint8_t *)ptr)[1] << 8) & 0x0000ff00; val |= (((uint8_t *)ptr)[2] << 16) & 0x00ff0000; val |= (((uint8_t *)ptr)[3] << 24) & 0xff000000; return val; } void fat12_set_cluster(void *fat, uint32_t cluster, uint32_t value) { uint32_t offset = (cluster * 3) / 2; // fprintf(stderr, "Cluster %d -> %03x (%02x%02x -> ", cluster, value, ((uint8_t *)fat)[offset], ((uint8_t *)fat)[offset + 1]); if (cluster&1) { ((uint8_t *)fat)[offset] = (((uint8_t *)fat)[offset] & 0x0f) | ((value << 4) & 0xf0); ((uint8_t *)fat)[offset + 1] = value >> 4; } else { ((uint8_t *)fat)[offset] = value; ((uint8_t *)fat)[offset + 1] = (((uint8_t *)fat)[offset + 1] & 0xf0) | ((value >> 8) & 0x0f); } // fprintf(stderr, "%02x%02x)\n", ((uint8_t *)fat)[offset], ((uint8_t *)fat)[offset + 1]); } uint32_t fat12_get_cluster(void *fat, uint32_t cluster) { uint32_t offset = (cluster * 3) / 2; uint32_t value = 0; if (cluster&1) { value |= (((uint8_t *)fat)[offset] >> 4) & 0x00f; value |= (((uint8_t *)fat)[offset + 1] << 4) & 0xff0; } else { value |= ((uint8_t *)fat)[offset]; value |= (((uint8_t *)fat)[offset + 1] << 8) & 0xf00; } // fprintf(stderr, "Cluster %d <- %03x\n", cluster, value); return value; } struct fat12_partition *fat12_alloc(void) { struct fat12_partition *part = malloc(sizeof(struct fat12_partition)); memset(part, 0, sizeof(*part)); return part; } int fat12_open(struct fat12_partition *part, const char *filename) { if (!part) return -1; if (part->io) return -1; part->io = pang_open(filename); if (!part->io) return -1; if (pang_read(part->io, 0, part->sector_buffer, sizeof(part->sector_buffer)) != sizeof(part->sector_buffer)) return -1; part->sector_size = fat12_read_u16(part->sector_buffer + 0x0b); // These are in single-unit quantities part->root_entries = fat12_read_u16(part->sector_buffer + 0x11); part->sector_count = fat12_read_u16(part->sector_buffer + 0x13); // These units are "sectors" part->fat_offset = fat12_read_u16(part->sector_buffer + 0x0e); part->fat_size = fat12_read_u16(part->sector_buffer + 0x16); part->root_offset = part->fat_offset + part->fat_size; part->first_offset = part->root_offset + (part->root_entries * sizeof(struct fat_directory_entry)) / part->sector_size; part->cluster_size = fat12_read_u8(part->sector_buffer + 0x0d); uint32_t bytes = (part->sector_count - part->first_offset) * part->sector_size; part->cluster_count = bytes / part->sector_size / part->cluster_size; part->cluster_bytes = (part->cluster_count * 3) / 2; // Move to the root directory part->current_cluster = part->first_offset; /// fprintf(stderr, "part->sector_size: %d\n", part->sector_size); // These are in single-unit quantities fprintf(stderr, "part->root_entries: %d\n", part->root_entries); fprintf(stderr, "part->sector_count: %d\n", part->sector_count); // These units are "sectors" fprintf(stderr, "part->fat_offset: %d\n", part->fat_offset); fprintf(stderr, "part->fat_size: %d\n", part->fat_size); fprintf(stderr, "part->root_offset: %d\n", part->root_offset); fprintf(stderr, "part->first_offset: %d\n", part->first_offset); fprintf(stderr, "part->cluster_size: %d\n", part->cluster_size); fprintf(stderr, "part->cluster_count: %d\n", part->cluster_count); fprintf(stderr, "part->cluster_bytes: %d\n", part->cluster_bytes); fprintf(stderr, "data_bytes: %d\n", bytes); /// if (1) { part->fat = malloc(part->fat_size * part->sector_size); pang_read(part->io, part->fat_offset * part->sector_size, part->fat, part->fat_size * part->sector_size); } return 0; } void fat12_sync(struct fat12_partition *part) { pang_read(part->io, part->fat_offset * part->sector_size, part->fat, part->fat_size * part->sector_size); } static int fat12_ls_foreach_sector(struct fat12_partition *part, uint32_t sector, uint32_t count, void *data, int (*callback)(void *data, const struct fat12_dirent *dirent)) { struct fat12_dirent dirent; uint8_t buf[part->sector_size]; uint32_t offset; pang_read(part->io, sector * part->sector_size, buf, sizeof(buf)); for (offset = 0; offset < count; offset++) { struct fat_directory_entry *entry = &((struct fat_directory_entry *)buf)[offset]; memset(&dirent, 0, sizeof(dirent)); memcpy(dirent.filename, entry->file_name, 8); memcpy(dirent.filename + 8, entry->extension, 3); dirent.first_cluster = fat12_read_u16(&entry->first_cluster); dirent.size = fat12_read_u32(&entry->file_size); if ((entry->file_name[0] != 0xe5) && (entry->file_name[0] != 0x00)) callback(data, &dirent); } return 0; } int fat12_ls_foreach(struct fat12_partition *part, uint32_t dir_cluster, void *data, int (*callback)(void *data, const struct fat12_dirent *dirent)) { uint32_t sector; if (dir_cluster < 2) { sector = part->root_offset; uint32_t dir_entries; uint32_t max_entries = part->root_entries; uint32_t entries_per_sector = part->sector_size / sizeof(struct fat_directory_entry); uint32_t loop = 0; for (dir_entries = 0; dir_entries < max_entries; dir_entries += entries_per_sector) { uint32_t entries_to_list = entries_per_sector; if (max_entries - dir_entries < entries_to_list) entries_to_list = max_entries - dir_entries; fat12_ls_foreach_sector(part, part->root_offset + loop++, entries_to_list, data, callback); } } else { sector = dir_cluster * part->cluster_size + part->first_offset; while (dir_cluster > 1 && dir_cluster < 0xff8) { // dir_cluster = fat12_get_cluster(part->fd, dir_cluster); } } return 0; } void fat12_flush(struct fat12_partition *part) { pang_write(part->io, part->fat_offset * part->sector_size, part->fat, part->fat_size * part->sector_size); } int fat12_close(struct fat12_partition *part) { if (!part) return -1; if (1) { fat12_flush(part); free(part->fat); } if (-1 == pang_close(&part->io)) { return -1; } return 0; } void fat12_free(struct fat12_partition **part) { if (!part) return; if (!*part) return; if ((*part)->io) fat12_close(*part); free(*part); *part = NULL; } static uint32_t fat12_find_free_cluster(struct fat12_partition *part, uint32_t start) { uint32_t cluster_value; if (start < 1) start = 1; do { cluster_value = fat12_get_cluster(part->fat, ++start); fprintf(stderr, "Cluster %d value: %03x\n", start, cluster_value); } while(cluster_value != 0); fprintf(stderr, "Found free cluster %d\n", start); return start; } static int fat12_add_dirent_sector(struct fat12_partition *part, uint32_t sector, uint32_t count, const struct fat_directory_entry *new_dirent) { struct fat12_dirent dirent; uint8_t buf[part->sector_size]; uint32_t offset; pang_read(part->io, sector * part->sector_size, buf, sizeof(buf)); for (offset = 0; offset < count; offset++) { struct fat_directory_entry *entry = &((struct fat_directory_entry *)buf)[offset]; memset(&dirent, 0, sizeof(dirent)); memcpy(dirent.filename, entry->file_name, 8); memcpy(dirent.filename + 8, entry->extension, 3); dirent.first_cluster = fat12_read_u16(&entry->first_cluster); dirent.size = fat12_read_u32(&entry->file_size); if ((entry->file_name[0] == 0xe5) || (entry->file_name[0] == 0x00)) { ((struct fat_directory_entry *)buf)[offset] = *new_dirent; pang_write(part->io, sector * part->sector_size, buf, sizeof(buf)); return 0; } } return -1; } static int fat12_dir_update_or_add_dirent(struct fat12_partition *part, uint32_t dir_cluster, const struct fat_directory_entry *dirent) { if (1) { uint32_t sector = part->root_offset; uint32_t dir_entries; uint32_t max_entries = part->root_entries; uint32_t entries_per_sector = part->sector_size / sizeof(struct fat_directory_entry); uint32_t loop = 0; for (dir_entries = 0; dir_entries < max_entries; dir_entries += entries_per_sector) { uint32_t entries_to_list = entries_per_sector; if (max_entries - dir_entries < entries_to_list) entries_to_list = max_entries - dir_entries; // TODO: See if a filename already exists if (! fat12_add_dirent_sector(part, part->root_offset + loop++, entries_to_list, dirent)) return 0; } } return 0; } static void fat_dirent_set_name(struct fat_directory_entry *dirent, const char *filename) { int i; int ext_split = -1; int name_off = 0; int dirent_off = 0; for (name_off = 0; filename[name_off] != '\0'; name_off++) { if (filename[name_off] == '.') ext_split = name_off + 1; } for (dirent_off = 0; dirent_off < 8; dirent_off++) { if (dirent_off < (ext_split - 1)) dirent->file_name[dirent_off] = toupper(filename[dirent_off]); else dirent->file_name[dirent_off] = ' '; } name_off = ext_split + 1; for (dirent_off = 0; dirent_off < 3; dirent_off++) { if ((ext_split >= 0) && (filename[ext_split])) dirent->extension[dirent_off] = toupper(filename[ext_split++]); else dirent->extension[dirent_off] = ' '; } } int fat12_write_file(struct fat12_partition *part, const char *filename, const void *bfr, uint32_t size) { // TODO: directory support uint32_t first_cluster; uint32_t cluster; cluster = first_cluster = fat12_find_free_cluster(part, 1); struct fat_directory_entry dirent; memset(&dirent, 0, sizeof(dirent)); fat_dirent_set_name(&dirent, filename); dirent.file_attributes = 0x20; dirent.reserved1 = 0x18; fat12_write_u16(dirent.first_cluster, first_cluster); fat12_write_u32(dirent.file_size, size); fat12_dir_update_or_add_dirent(part, 0, &dirent); while (size > 0) { uint32_t bytes_to_write = size; if (bytes_to_write > part->cluster_size * part->sector_size) bytes_to_write = part->cluster_size * part->sector_size; pang_write(part->io, ((cluster - 2) * part->cluster_size + part->first_offset) * part->sector_size, bfr, bytes_to_write); size -= bytes_to_write; fprintf(stderr, "%s: %d bytes left\n", filename, size); if (!size) fat12_set_cluster(part->fat, cluster, 0xff8); else { uint32_t next_cluster = fat12_find_free_cluster(part, cluster); fat12_set_cluster(part->fat, cluster, next_cluster); cluster = next_cluster; } } } static uint32_t find_root_cluster_for_file(struct fat12_partition *part, const char *filename) { uint32_t file_cluster = 0; // Find the dirent of the file uint32_t dirent_cluster = 0; // TODO: Support directories other than the root directory if (1) { struct fat_directory_entry dirent; uint32_t entries_per_sector = part->sector_size / sizeof(struct fat_directory_entry); fat_dirent_set_name(&dirent, filename); int i = 0; int check_entry_i; int sector = part->root_offset - 1; // -1 because we use it to update the entry below. while (i < part->root_entries) { struct fat_directory_entry entries[entries_per_sector]; pang_read(part->io, ++sector * part->sector_size, entries, sizeof(entries)); for (check_entry_i = 0; (check_entry_i < entries_per_sector) && (i < part->root_entries); check_entry_i++, i++) { if (!memcmp(&entries[check_entry_i], &dirent, 11)) { entries[check_entry_i].file_name[0] = 0xe5; pang_write(part->io, sector * part->sector_size, entries, sizeof(entries)); return fat12_read_u16(entries[check_entry_i].first_cluster); } } } } return 0; } int fat12_delete_file(struct fat12_partition *part, const char *filename) { // Find the root cluster of the file uint32_t root_cluster = find_root_cluster_for_file(part, filename); // A return value of 0 (or 1) is not a valid cluster, so there's // nothing to do. if (root_cluster < 2) { return 0; } // Change the first character of the dirent to 0xe5 to indicate // it's been deleted. // Set all clusters in the FAT to 0x000, indicating they're free. uint32_t this_cluster = root_cluster; do { uint32_t next_cluster = fat12_get_cluster(part->fat, this_cluster); // fprintf(stderr, "This cluster: %03d Next cluster: %03d Next-next cluster: %03d\n", this_cluster, next_cluster, next_next_cluster); fat12_set_cluster(part->fat, this_cluster, 0); this_cluster = next_cluster; } while ((this_cluster < 0xff8) && (this_cluster > 1)); return 0; }