diff --git a/Makefile b/Makefile index 0d3f313..288572e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS ?= -Iinclude +CFLAGS ?= -Iinclude -ggdb2 all: build build/test-get-set-cluster build/test-mkfs-simple build/test-fragment-file diff --git a/include/fat12.h b/include/fat12.h index 546318f..852d559 100644 --- a/include/fat12.h +++ b/include/fat12.h @@ -18,6 +18,8 @@ struct fat12_partition * fat12_alloc(void); int fat12_open(struct fat12_partition *part, const char *filename); int fat12_close(struct fat12_partition *part); void fat12_free(struct fat12_partition **part); +void fat12_sync(struct fat12_partition *part); +void fat12_flush(struct fat12_partition *part); int fat12_ls_foreach(struct fat12_partition *part, uint32_t dir_cluster, void *data, int (*callback)(void *data, const struct fat12_dirent *dirent)); @@ -30,6 +32,9 @@ uint32_t fat12_read_u32(void *ptr); void fat12_set_cluster(void *fat, uint32_t cluster, uint32_t value); uint32_t fat12_get_cluster(void *fat, uint32_t cluster); +int fat12_write_file(struct fat12_partition *part, const char *filename, const void *bfr, uint32_t size); +int fat12_delete_file(struct fat12_partition *part, const char *filename); + int fat12_defrag(struct pang_io *io); int fat12_mkfs(struct pang_io *io, uint32_t bytes); diff --git a/src/fat12.c b/src/fat12.c index e86bf9c..0c4ac6e 100644 --- a/src/fat12.c +++ b/src/fat12.c @@ -3,6 +3,7 @@ #include "io.h" #include +#include struct fat12_partition { struct pang_io *io; @@ -26,6 +27,8 @@ struct fat12_partition { // Used for traversing directories uint32_t current_cluster; + + void *fat; }; void fat12_write_u8(void *ptr, uint8_t val) { @@ -68,14 +71,16 @@ uint32_t fat12_read_u32(void *ptr) { 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] & 0xf0) | ((value >> 8) & 0x0f); + ((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) { @@ -91,6 +96,8 @@ uint32_t fat12_get_cluster(void *fat, uint32_t cluster) { value |= (((uint8_t *)fat)[offset + 1] << 8) & 0xf00; } + // fprintf(stderr, "Cluster %d <- %03x\n", cluster, value); + return value; } @@ -154,9 +161,18 @@ int fat12_open(struct fat12_partition *part, const char *filename) { 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; @@ -195,7 +211,7 @@ int fat12_ls_foreach(struct fat12_partition *part, uint32_t dir_cluster, void *d } else { sector = dir_cluster * part->cluster_size + part->first_offset; - while (dir_cluster > 1 && dir_cluster < 0xff0) { + while (dir_cluster > 1 && dir_cluster < 0xff8) { // dir_cluster = fat12_get_cluster(part->fd, dir_cluster); } } @@ -203,10 +219,19 @@ int fat12_ls_foreach(struct fat12_partition *part, uint32_t dir_cluster, void *d 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; } @@ -226,4 +251,177 @@ void fat12_free(struct fat12_partition **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; } \ No newline at end of file diff --git a/tests/fragment-file.c b/tests/fragment-file.c index 777961c..6cc879a 100644 --- a/tests/fragment-file.c +++ b/tests/fragment-file.c @@ -30,23 +30,55 @@ static int list_directory(void *data, const struct fat12_dirent *dirent) { printf("%d %d %x %d %d %s\n", dirent->ctime, dirent->mtime, dirent->file_attributes, dirent->size, dirent->first_cluster, dirent->filename); } +static int run_command(const char *cmd) { + int ret = system(cmd); + if (ret) { + exit(1); + } + return 0; +} + +static void memset_str(uint8_t *bfr, uint8_t *val, size_t count) { + int i; + for (i = 0; i < count; i++) { + bfr[i] = val[i & 0x7]; + } +} + int main(int argc, char **argv) { - // const char *img_name = "build/fat12-1800k.img"; - const char *img_name = "disk-image"; + uint8_t tmp_bfr[32768]; + const char *img_name = "build/fat12-fragmented.img"; int ret = 0; int fd; struct fat12_partition *part = fat12_alloc(); - // ret = make_image(img_name, 1800 * 1024); - // if (ret == -1) { - // return 1; - // } + ret = make_image(img_name, 9 * 1024 * 1024); + if (ret == -1) { + return 1; + } if (-1 == fat12_open(part, img_name)) { fprintf(stderr, "couldn't open fat12 on %s: %s\n", img_name, strerror(errno)); return 1; } + memset_str(tmp_bfr, "baz.qux\x0a", 16384); + fat12_write_file(part, "baz.qux", tmp_bfr, 16384); + // run_command("yes baz.qux | dd bs=16384 count=1 | fattool build/fat12-fragmented.img writefile baz.qux"); + + memset_str(tmp_bfr, "foo.bar\x0a", 16384); + fat12_write_file(part, "foo.bar", tmp_bfr, 16384); + // run_command("yes foo.bar | dd bs=16384 count=1 | fattool build/fat12-fragmented.img writefile foo.bar"); + + fat12_delete_file(part, "baz.qux"); + // fat12_flush(part); + // run_command("fattool build/fat12-fragmented.img deletefile baz.qux"); + // fat12_sync(part); + + memset_str(tmp_bfr, "baz.qux\x0a", 32768); + fat12_write_file(part, "baz.qux", tmp_bfr, 32768); + // run_command("yes baz.qux | dd bs=16384 count=2 | fattool build/fat12-fragmented.img writefile baz.qux"); + fat12_ls_foreach(part, 0, NULL, list_directory); fat12_close(part);