From 48f1f33c49f3e84a5eb600c371eaae07b835b316 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 13 Jun 2019 10:58:19 +0800 Subject: [PATCH] ops: add the ability to write and delete files Right now it only works on the root directory, and has much more functionality that needs adding. But it passes the tests. Signed-off-by: Sean Cross --- Makefile | 2 +- include/fat12.h | 5 ++ src/fat12.c | 202 +++++++++++++++++++++++++++++++++++++++++- tests/fragment-file.c | 44 +++++++-- 4 files changed, 244 insertions(+), 9 deletions(-) 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);