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 <sean@xobs.io>
This commit is contained in:
Sean Cross 2019-06-13 10:58:19 +08:00
parent cf239b9b7a
commit 48f1f33c49
4 changed files with 244 additions and 9 deletions

View File

@ -1,4 +1,4 @@
CFLAGS ?= -Iinclude CFLAGS ?= -Iinclude -ggdb2
all: build build/test-get-set-cluster build/test-mkfs-simple build/test-fragment-file all: build build/test-get-set-cluster build/test-mkfs-simple build/test-fragment-file

View File

@ -18,6 +18,8 @@ struct fat12_partition * fat12_alloc(void);
int fat12_open(struct fat12_partition *part, const char *filename); int fat12_open(struct fat12_partition *part, const char *filename);
int fat12_close(struct fat12_partition *part); int fat12_close(struct fat12_partition *part);
void fat12_free(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)); 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); void fat12_set_cluster(void *fat, uint32_t cluster, uint32_t value);
uint32_t fat12_get_cluster(void *fat, uint32_t cluster); 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_defrag(struct pang_io *io);
int fat12_mkfs(struct pang_io *io, uint32_t bytes); int fat12_mkfs(struct pang_io *io, uint32_t bytes);

View File

@ -3,6 +3,7 @@
#include "io.h" #include "io.h"
#include <stdio.h> #include <stdio.h>
#include <ctype.h>
struct fat12_partition { struct fat12_partition {
struct pang_io *io; struct pang_io *io;
@ -26,6 +27,8 @@ struct fat12_partition {
// Used for traversing directories // Used for traversing directories
uint32_t current_cluster; uint32_t current_cluster;
void *fat;
}; };
void fat12_write_u8(void *ptr, uint8_t val) { 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) { void fat12_set_cluster(void *fat, uint32_t cluster, uint32_t value) {
uint32_t offset = (cluster * 3) / 2; 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) { if (cluster&1) {
((uint8_t *)fat)[offset] = (((uint8_t *)fat)[offset] & 0x0f) | ((value << 4) & 0xf0); ((uint8_t *)fat)[offset] = (((uint8_t *)fat)[offset] & 0x0f) | ((value << 4) & 0xf0);
((uint8_t *)fat)[offset + 1] = value >> 4; ((uint8_t *)fat)[offset + 1] = value >> 4;
} }
else { else {
((uint8_t *)fat)[offset] = value; ((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) { 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; value |= (((uint8_t *)fat)[offset + 1] << 8) & 0xf00;
} }
// fprintf(stderr, "Cluster %d <- %03x\n", cluster, value);
return value; return value;
} }
@ -154,9 +161,18 @@ int fat12_open(struct fat12_partition *part, const char *filename) {
fprintf(stderr, "data_bytes: %d\n", 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; 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, 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)) { void *data, int (*callback)(void *data, const struct fat12_dirent *dirent)) {
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 { else {
sector = dir_cluster * part->cluster_size + part->first_offset; 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); // 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; 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) { int fat12_close(struct fat12_partition *part) {
if (!part) if (!part)
return -1; return -1;
if (1) {
fat12_flush(part);
free(part->fat);
}
if (-1 == pang_close(&part->io)) { if (-1 == pang_close(&part->io)) {
return -1; return -1;
} }
@ -227,3 +252,176 @@ void fat12_free(struct fat12_partition **part) {
free(*part); free(*part);
*part = NULL; *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;
}

View File

@ -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); 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) { int main(int argc, char **argv) {
// const char *img_name = "build/fat12-1800k.img"; uint8_t tmp_bfr[32768];
const char *img_name = "disk-image"; const char *img_name = "build/fat12-fragmented.img";
int ret = 0; int ret = 0;
int fd; int fd;
struct fat12_partition *part = fat12_alloc(); struct fat12_partition *part = fat12_alloc();
// ret = make_image(img_name, 1800 * 1024); ret = make_image(img_name, 9 * 1024 * 1024);
// if (ret == -1) { if (ret == -1) {
// return 1; return 1;
// } }
if (-1 == fat12_open(part, img_name)) { if (-1 == fat12_open(part, img_name)) {
fprintf(stderr, "couldn't open fat12 on %s: %s\n", img_name, strerror(errno)); fprintf(stderr, "couldn't open fat12 on %s: %s\n", img_name, strerror(errno));
return 1; 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_ls_foreach(part, 0, NULL, list_directory);
fat12_close(part); fat12_close(part);