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:
parent
cf239b9b7a
commit
48f1f33c49
2
Makefile
2
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
|
all: build build/test-get-set-cluster build/test-mkfs-simple build/test-fragment-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);
|
||||||
|
|
||||||
|
202
src/fat12.c
202
src/fat12.c
@ -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;
|
||||||
}
|
}
|
||||||
@ -226,4 +251,177 @@ 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;
|
||||||
}
|
}
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user