You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
767 lines
21 KiB
767 lines
21 KiB
#include "src/kernel/fat16.h"
|
|
#include "src/kernel/ata.h"
|
|
#include "src/kernel/fs.h"
|
|
#include "src/kernel/panic.h"
|
|
#include "src/kernel/stream.h"
|
|
#include "src/lib/memory.h"
|
|
#include "src/lib/string.h"
|
|
#include "src/lib/syscall.h"
|
|
#include "src/lib/util.h"
|
|
|
|
#define SECTOR_SIZE 512
|
|
#define FIRST_PARTITION_SECTOR 2048
|
|
#define ATTRIBUTE_SUBDIRECTORY 0x10
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint16_t bytes_per_sector;
|
|
uint8_t sectors_per_cluster;
|
|
uint16_t reserved_sectors;
|
|
uint8_t fats_count;
|
|
uint16_t root_entry_count;
|
|
uint16_t total_sectors_16;
|
|
uint8_t media_type;
|
|
uint16_t sectors_per_fat;
|
|
} fat16_bpb_t;
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
char name[8];
|
|
char ext[3];
|
|
uint8_t attributes;
|
|
uint8_t reserved[10];
|
|
uint16_t modified_time;
|
|
uint16_t modified_date;
|
|
uint16_t first_cluster;
|
|
uint32_t size;
|
|
} fat16_dir_entry_t;
|
|
|
|
// Assuming one persistently mounted partition.
|
|
static fat16_bpb_t *bpb = NUL;
|
|
static uint32_t data_start;
|
|
static uint16_t *fat = NUL;
|
|
|
|
typedef struct {
|
|
fs_node_t base;
|
|
uint16_t dir_cluster;
|
|
uint16_t dir_index;
|
|
uint16_t first_cluster;
|
|
} fat16_node_t;
|
|
|
|
#define MAX_NODES 256
|
|
|
|
static fat16_node_t nodes[MAX_NODES];
|
|
|
|
static fs_node_t *node_create(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster,
|
|
uint32_t size) {
|
|
for (uint64_t i = 0; i < MAX_NODES; i++) {
|
|
if (!nodes[i].base.refs) {
|
|
nodes[i].base.type = FAT16;
|
|
nodes[i].base.refs = 1;
|
|
memory_copy(name, FILENAME_SIZE_LIMIT + 1, nodes[i].base.name);
|
|
nodes[i].base.size = size;
|
|
nodes[i].base.is_dir = is_dir;
|
|
nodes[i].base.removed = 0;
|
|
nodes[i].dir_cluster = dir_cluster;
|
|
nodes[i].dir_index = dir_index;
|
|
nodes[i].first_cluster = first_cluster;
|
|
return (fs_node_t *)&nodes[i];
|
|
}
|
|
}
|
|
return NUL;
|
|
}
|
|
|
|
static fs_node_t *node_get(uint32_t dir_cluster, uint16_t dir_index) {
|
|
for (uint64_t i = 0; i < MAX_NODES; i++) {
|
|
if (nodes[i].base.refs && nodes[i].dir_cluster == dir_cluster && nodes[i].dir_index == dir_index) {
|
|
return (fs_node_t *)&nodes[i];
|
|
}
|
|
}
|
|
return NUL;
|
|
}
|
|
|
|
static fs_node_t *node_use(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster,
|
|
uint32_t size) {
|
|
fs_node_t *result = node_get(dir_cluster, dir_index);
|
|
if (!result) {
|
|
result = node_create(dir_cluster, dir_index, name, is_dir, first_cluster, size);
|
|
} else {
|
|
ASSERT(result->refs < UINT32_MAX, "node_use: too many references");
|
|
result->refs++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void node_free(fs_node_t *node) {
|
|
if (node->refs) {
|
|
node->refs--;
|
|
}
|
|
}
|
|
|
|
static void to_8_3(const char *name, char *output) {
|
|
memory_set(' ', 11, output);
|
|
output[11] = '\0';
|
|
const char *c = name;
|
|
uint64_t i = 0;
|
|
uint8_t ext = 0;
|
|
while (*c) {
|
|
if (*c == '.') {
|
|
i = 8;
|
|
ext = 1;
|
|
} else if (i < (!ext ? 8 : 11)) {
|
|
output[i++] = *c >= 'a' && *c <= 'z' ? *c - 32 : *c;
|
|
}
|
|
c++;
|
|
}
|
|
}
|
|
|
|
static void from_8_3(const char *name, const char *extension, char *output) {
|
|
memory_set(0, 13, output);
|
|
|
|
uint16_t ni = 0, ei = 0, oi = 0;
|
|
while (ni < 8 && name[ni] != ' ') {
|
|
output[oi++] = name[ni++];
|
|
}
|
|
|
|
if (extension[ei] != ' ') {
|
|
output[oi++] = '.';
|
|
while (ei < 3 && extension[ei] != ' ') {
|
|
output[oi++] = extension[ei++];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ensure_bpb() {
|
|
if (bpb) {
|
|
return;
|
|
}
|
|
|
|
uint8_t sector[512];
|
|
ata_read_sectors(FIRST_PARTITION_SECTOR, 1, §or);
|
|
bpb = memory_allocate(sizeof(fat16_bpb_t));
|
|
memory_copy(sector + 11, sizeof(fat16_bpb_t), bpb);
|
|
data_start = FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->sectors_per_fat * bpb->fats_count +
|
|
(bpb->root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
|
}
|
|
|
|
static void ensure_fat() {
|
|
if (fat) {
|
|
return;
|
|
}
|
|
|
|
ensure_bpb();
|
|
|
|
ASSERT(bpb->sectors_per_fat < 256, "ensure_fat: big FAT not implemented");
|
|
|
|
fat = memory_allocate(bpb->sectors_per_fat * SECTOR_SIZE);
|
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat);
|
|
}
|
|
|
|
static uint16_t allocate_cluster() {
|
|
ensure_fat();
|
|
for (uint16_t i = 2; i < bpb->sectors_per_fat * SECTOR_SIZE / 2; i++) {
|
|
if (fat[i] == 0x0000) {
|
|
fat[i] = 0xFFFF;
|
|
return i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void flush_fat() {
|
|
ensure_fat();
|
|
ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat);
|
|
}
|
|
|
|
static uint16_t read_directory(uint16_t dir_cluster, fat16_dir_entry_t **entries) {
|
|
ensure_bpb();
|
|
|
|
if (!dir_cluster) {
|
|
uint8_t sectors = (uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE);
|
|
*entries = memory_allocate(sectors * SECTOR_SIZE);
|
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, sectors, *entries);
|
|
return bpb->root_entry_count;
|
|
} else {
|
|
ensure_fat();
|
|
|
|
uint16_t next_cluster = dir_cluster;
|
|
uint16_t cluster_count = 0;
|
|
while (next_cluster < 0xFFF8) {
|
|
cluster_count++;
|
|
next_cluster = fat[next_cluster];
|
|
}
|
|
*entries = memory_allocate(cluster_count * bpb->sectors_per_cluster * SECTOR_SIZE);
|
|
|
|
uint8_t *chunk = (uint8_t *)*entries;
|
|
next_cluster = dir_cluster;
|
|
while (next_cluster < 0xFFF8) {
|
|
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk);
|
|
chunk += bpb->sectors_per_cluster * SECTOR_SIZE;
|
|
next_cluster = fat[next_cluster];
|
|
}
|
|
return cluster_count * bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t);
|
|
}
|
|
|
|
return (uint16_t)-1;
|
|
}
|
|
|
|
static fs_node_t *open_entry(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t index) {
|
|
const fat16_dir_entry_t *entry = &entries[index];
|
|
|
|
if (!entry->name[0]) {
|
|
return NUL;
|
|
}
|
|
|
|
char name[FILENAME_SIZE_LIMIT + 1];
|
|
from_8_3(entry->name, entry->ext, name);
|
|
|
|
return node_use(dir_cluster, index, name, entry->attributes & ATTRIBUTE_SUBDIRECTORY, entry->first_cluster, entry->size);
|
|
}
|
|
|
|
static void write_directory(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t count) {
|
|
ensure_bpb();
|
|
|
|
ASSERT((count * sizeof(fat16_dir_entry_t)) % (bpb->sectors_per_cluster * SECTOR_SIZE) == 0,
|
|
"write_directory: entries must be aligned to clusters");
|
|
|
|
if (!dir_cluster) {
|
|
ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat,
|
|
(uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE), entries);
|
|
} else {
|
|
ensure_fat();
|
|
|
|
uint8_t *chunk = (uint8_t *)entries;
|
|
uint16_t next_cluster = dir_cluster;
|
|
while (count) {
|
|
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk);
|
|
chunk += bpb->sectors_per_cluster * SECTOR_SIZE;
|
|
count -= bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t);
|
|
if (count && fat[next_cluster] >= 0xFFF8) {
|
|
fat[next_cluster] = allocate_cluster();
|
|
}
|
|
next_cluster = fat[next_cluster];
|
|
}
|
|
flush_fat();
|
|
}
|
|
}
|
|
|
|
fs_node_t *fat16_open_root() {
|
|
ensure_bpb();
|
|
|
|
return node_use(0, UINT16_MAX, "", 1, 0, sizeof(fat16_dir_entry_t) * bpb->root_entry_count);
|
|
}
|
|
|
|
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name, uint64_t flags) {
|
|
if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) {
|
|
return NUL;
|
|
}
|
|
|
|
uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster;
|
|
|
|
char name_8_3[12];
|
|
to_8_3(name, name_8_3);
|
|
|
|
fat16_dir_entry_t *entries;
|
|
uint16_t entries_count = read_directory(dir_cluster, &entries);
|
|
|
|
fat16_dir_entry_t *entry = entries;
|
|
uint16_t index = 0;
|
|
while (index < entries_count && entry->name[0]) {
|
|
if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) {
|
|
break;
|
|
}
|
|
entry++;
|
|
index++;
|
|
}
|
|
|
|
fs_node_t *result = open_entry(dir_cluster, entries, index);
|
|
|
|
if (result && (flags & OPEN_EXCLUSIVE)) {
|
|
memory_free(entries);
|
|
return NUL;
|
|
}
|
|
|
|
if (!result && (flags & OPEN_CREATE)) {
|
|
index = 0;
|
|
while (index < entries_count && entries[index].name[0]) {
|
|
index++;
|
|
}
|
|
if (index >= entries_count) {
|
|
if (!dir_cluster) {
|
|
memory_free(entries);
|
|
return NUL;
|
|
} else {
|
|
uint16_t new_entries_count = entries_count + bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t);
|
|
fat16_dir_entry_t *new_entries = memory_allocate(new_entries_count * sizeof(fat16_dir_entry_t));
|
|
memory_copy(entries, entries_count * sizeof(fat16_dir_entry_t), new_entries);
|
|
memory_free(entries);
|
|
entries_count = new_entries_count;
|
|
entries = new_entries;
|
|
}
|
|
}
|
|
|
|
memory_copy(name_8_3, 11, &entries[index]);
|
|
|
|
if (flags & OPEN_DIRECTORY && !(flags & OPEN_FILE)) {
|
|
entries[index].attributes = ATTRIBUTE_SUBDIRECTORY;
|
|
entries[index].first_cluster = allocate_cluster();
|
|
fat16_dir_entry_t *inner_entries = memory_allocate(bpb->sectors_per_cluster * SECTOR_SIZE);
|
|
memory_copy(". ", 11, inner_entries[0].name);
|
|
inner_entries[0].attributes = ATTRIBUTE_SUBDIRECTORY;
|
|
inner_entries[0].first_cluster = entries[index].first_cluster;
|
|
memory_copy(".. ", 11, inner_entries[1].name);
|
|
inner_entries[1].attributes = ATTRIBUTE_SUBDIRECTORY;
|
|
inner_entries[1].first_cluster = dir_cluster;
|
|
write_directory(entries[index].first_cluster, inner_entries, bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t));
|
|
memory_free(inner_entries);
|
|
}
|
|
|
|
write_directory(dir_cluster, entries, entries_count);
|
|
|
|
result = open_entry(dir_cluster, entries, index);
|
|
}
|
|
|
|
memory_free(entries);
|
|
|
|
if (!result) {
|
|
return NUL;
|
|
}
|
|
|
|
if (result->is_dir && !(flags & OPEN_DIRECTORY)) {
|
|
return NUL;
|
|
}
|
|
|
|
if (!result->is_dir && !(flags & OPEN_FILE)) {
|
|
return NUL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
fs_node_t *fat16_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags) {
|
|
if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) {
|
|
return NUL;
|
|
}
|
|
|
|
uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster;
|
|
|
|
fat16_dir_entry_t *entries;
|
|
uint16_t entries_count = read_directory(dir_cluster, &entries);
|
|
|
|
uint16_t ei = 0, vi = 0;
|
|
while (ei < entries_count && entries[ei].name[0]) {
|
|
if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) {
|
|
if (vi == index) {
|
|
break;
|
|
}
|
|
vi++;
|
|
}
|
|
ei++;
|
|
}
|
|
|
|
fs_node_t *result = open_entry(dir_cluster, entries, ei);
|
|
|
|
memory_free(entries);
|
|
|
|
if (result && (flags & OPEN_EXCLUSIVE)) {
|
|
return NUL;
|
|
}
|
|
|
|
if (!result) {
|
|
return NUL;
|
|
}
|
|
|
|
if (result->is_dir && !(flags & OPEN_DIRECTORY)) {
|
|
return NUL;
|
|
}
|
|
|
|
if (!result->is_dir && !(flags & OPEN_FILE)) {
|
|
return NUL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
fs_node_t *fat16_open_again(fs_node_t *source) {
|
|
ASSERT(source->refs < UINT32_MAX, "fat16_open_again: too many references");
|
|
source->refs++;
|
|
return source;
|
|
}
|
|
|
|
uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) {
|
|
if (!file || file->type != FAT16 || file->is_dir) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if (file->removed) {
|
|
return 0;
|
|
}
|
|
|
|
if (offset >= file->size) {
|
|
return 0;
|
|
}
|
|
|
|
if (offset + bytes >= file->size) {
|
|
bytes = file->size - offset;
|
|
}
|
|
|
|
ensure_fat();
|
|
|
|
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
|
|
uint32_t next_cluster = ((fat16_node_t *)file)->first_cluster;
|
|
|
|
while (offset >= cluster_size) {
|
|
next_cluster = fat[next_cluster];
|
|
offset -= cluster_size;
|
|
}
|
|
|
|
uint32_t readden = 0;
|
|
|
|
uint8_t *cursor = to;
|
|
|
|
uint8_t *tmp = memory_allocate(cluster_size);
|
|
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
|
|
uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset;
|
|
memory_copy(tmp + offset, prefix_size, cursor);
|
|
bytes -= prefix_size;
|
|
readden += prefix_size;
|
|
cursor += prefix_size;
|
|
next_cluster = fat[next_cluster];
|
|
|
|
while (bytes >= cluster_size) {
|
|
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor);
|
|
bytes -= cluster_size;
|
|
readden += cluster_size;
|
|
cursor += cluster_size;
|
|
next_cluster = fat[next_cluster];
|
|
}
|
|
|
|
if (bytes) {
|
|
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
|
|
memory_copy(tmp, bytes, cursor);
|
|
readden += bytes;
|
|
}
|
|
|
|
memory_free(tmp);
|
|
|
|
return readden;
|
|
}
|
|
|
|
uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) {
|
|
if (!file || file->type != FAT16 || file->is_dir) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if (file->removed) {
|
|
return 0;
|
|
}
|
|
|
|
ensure_fat();
|
|
|
|
fat16_node_t *fat_file = (fat16_node_t *)file;
|
|
|
|
if (!fat_file->first_cluster) {
|
|
fat_file->first_cluster = allocate_cluster();
|
|
}
|
|
|
|
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
|
|
uint32_t next_cluster = fat_file->first_cluster;
|
|
|
|
uint32_t allocated = cluster_size;
|
|
while (allocated < offset + bytes) {
|
|
if (fat[next_cluster] >= 0xFFF8) {
|
|
fat[next_cluster] = allocate_cluster();
|
|
}
|
|
next_cluster = fat[next_cluster];
|
|
allocated += cluster_size;
|
|
}
|
|
|
|
flush_fat();
|
|
|
|
cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
|
|
next_cluster = fat_file->first_cluster;
|
|
|
|
while (offset >= cluster_size) {
|
|
next_cluster = fat[next_cluster];
|
|
offset -= cluster_size;
|
|
}
|
|
|
|
uint32_t written = 0;
|
|
|
|
const uint8_t *cursor = from;
|
|
|
|
uint8_t *tmp = memory_allocate(cluster_size);
|
|
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
|
|
uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset;
|
|
memory_copy(cursor, prefix_size, tmp + offset);
|
|
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
|
|
bytes -= prefix_size;
|
|
written += prefix_size;
|
|
cursor += prefix_size;
|
|
next_cluster = fat[next_cluster];
|
|
|
|
while (bytes >= cluster_size) {
|
|
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor);
|
|
bytes -= cluster_size;
|
|
written += cluster_size;
|
|
cursor += cluster_size;
|
|
next_cluster = fat[next_cluster];
|
|
}
|
|
|
|
if (bytes) {
|
|
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
|
|
memory_copy(cursor, bytes, tmp);
|
|
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
|
|
written += bytes;
|
|
}
|
|
|
|
memory_free(tmp);
|
|
|
|
if (offset + written > file->size) {
|
|
file->size = offset + written;
|
|
|
|
fat16_dir_entry_t *entries;
|
|
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries);
|
|
entries[fat_file->dir_index].first_cluster = fat_file->first_cluster;
|
|
entries[fat_file->dir_index].size = file->size;
|
|
write_directory(fat_file->dir_cluster, entries, entries_count);
|
|
memory_free(entries);
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
uint64_t fat16_truncate(fs_node_t *file, uint32_t size) {
|
|
if (!file || file->type != FAT16 || file->is_dir) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if (file->removed) {
|
|
return 0;
|
|
}
|
|
|
|
if (file->size == size) {
|
|
return size;
|
|
} else if (file->size < size) {
|
|
uint32_t diff = size - file->size;
|
|
uint8_t *tmp = memory_allocate(diff);
|
|
uint64_t written = fat16_write(file, file->size, tmp, diff);
|
|
memory_free(tmp);
|
|
return written == (uint64_t)-1 ? (uint64_t)-1 : file->size + written;
|
|
} else if (file->size > size) {
|
|
fat16_node_t *fat_file = (fat16_node_t *)file;
|
|
|
|
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
|
|
uint32_t prev_cluster = 0;
|
|
uint32_t next_cluster = fat_file->first_cluster;
|
|
|
|
ensure_fat();
|
|
uint32_t allocated = 0;
|
|
while (allocated < size) {
|
|
prev_cluster = next_cluster;
|
|
next_cluster = fat[next_cluster];
|
|
allocated += cluster_size;
|
|
}
|
|
if (prev_cluster) {
|
|
fat[prev_cluster] = 0xFFFF;
|
|
} else {
|
|
fat_file->first_cluster = 0;
|
|
}
|
|
do {
|
|
uint32_t swap = fat[next_cluster];
|
|
fat[next_cluster] = 0;
|
|
next_cluster = swap;
|
|
} while (next_cluster < 0xFFF8);
|
|
flush_fat();
|
|
|
|
file->size = size;
|
|
fat16_dir_entry_t *entries;
|
|
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries);
|
|
entries[fat_file->dir_index].first_cluster = fat_file->first_cluster;
|
|
entries[fat_file->dir_index].size = file->size;
|
|
write_directory(fat_file->dir_cluster, entries, entries_count);
|
|
memory_free(entries);
|
|
|
|
return size;
|
|
}
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
uint64_t fat16_remove(fs_node_t *file) {
|
|
if (!file || file->type != FAT16) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if (file->removed) {
|
|
return 0;
|
|
}
|
|
|
|
fat16_node_t *fat_file = (fat16_node_t *)file;
|
|
|
|
if (file->is_dir) {
|
|
fat16_dir_entry_t *subentries;
|
|
uint16_t subentries_count = read_directory(fat_file->first_cluster, &subentries);
|
|
uint16_t ei = 0, vi = 0;
|
|
while (ei < subentries_count && subentries[ei].name[0]) {
|
|
if ((uint8_t)subentries[ei].name[0] != 0xE5 && (uint8_t)subentries[ei].attributes != 0x0F) {
|
|
vi++;
|
|
}
|
|
ei++;
|
|
}
|
|
memory_free(subentries);
|
|
if (vi > 2) {
|
|
return (uint64_t)-1;
|
|
}
|
|
}
|
|
|
|
file->removed = 1;
|
|
|
|
if (fat_file->first_cluster) {
|
|
ensure_fat();
|
|
uint64_t next_cluster = fat_file->first_cluster;
|
|
do {
|
|
uint32_t swap = fat[next_cluster];
|
|
fat[next_cluster] = 0;
|
|
next_cluster = swap;
|
|
} while (next_cluster < 0xFFF8);
|
|
flush_fat();
|
|
}
|
|
|
|
fat16_dir_entry_t *entries;
|
|
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries);
|
|
entries[fat_file->dir_index].name[0] = 0xE5;
|
|
entries[fat_file->dir_index].first_cluster = 0;
|
|
entries[fat_file->dir_index].size = 0;
|
|
write_directory(fat_file->dir_cluster, entries, entries_count);
|
|
memory_free(entries);
|
|
|
|
return 1;
|
|
}
|
|
|
|
typedef struct {
|
|
stream_t stream;
|
|
fs_node_t *node;
|
|
uint32_t offset;
|
|
} fat16_file_stream_t;
|
|
|
|
static uint64_t file_stream_write(stream_t *self, const char *from, uint64_t bytes) {
|
|
if (!self) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
|
|
uint64_t written = fat16_write(ffs->node, ffs->offset, from, bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes);
|
|
if (written != (uint64_t)-1) {
|
|
ffs->offset += written;
|
|
}
|
|
return written;
|
|
}
|
|
|
|
static uint64_t file_stream_read(stream_t *self, uint64_t max, char *to) {
|
|
if (!self) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
|
|
uint64_t readden = fat16_read(ffs->node, ffs->offset, max > UINT32_MAX ? UINT32_MAX : (uint32_t)max, to);
|
|
if (readden != (uint64_t)-1) {
|
|
ffs->offset += readden;
|
|
}
|
|
return readden;
|
|
}
|
|
|
|
static uint64_t file_stream_truncate(stream_t *self, uint64_t size) {
|
|
if (!self) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
|
|
uint64_t resized = fat16_truncate(ffs->node, size > UINT32_MAX ? UINT32_MAX : (uint32_t)size);
|
|
if (resized != (uint64_t)-1 && resized < ffs->offset) {
|
|
ffs->offset = (uint32_t)resized;
|
|
}
|
|
return resized;
|
|
}
|
|
|
|
static void file_stream_close(stream_t *self) {
|
|
if (!self) {
|
|
return;
|
|
}
|
|
|
|
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
|
|
fat16_close(ffs->node);
|
|
memory_free(self);
|
|
}
|
|
|
|
typedef struct {
|
|
stream_t stream;
|
|
fs_node_t *node;
|
|
uint16_t index;
|
|
} fat16_directory_stream_t;
|
|
|
|
static uint64_t directory_stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from,
|
|
__attribute__((unused)) uint64_t bytes) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
static uint64_t directory_stream_read(stream_t *self, uint64_t max, char *to) {
|
|
if (!self) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self;
|
|
fs_node_t *node = fat16_open_at(fds->node, fds->index++, OPEN_FILE | OPEN_DIRECTORY);
|
|
if (!node) {
|
|
return 0;
|
|
}
|
|
uint64_t length = string_length(node->name);
|
|
uint64_t to_read = length > max ? max : length;
|
|
memory_copy(node->name, to_read, to);
|
|
return to_read;
|
|
}
|
|
|
|
static uint64_t directory_stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) {
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
static void directory_stream_close(stream_t *self) {
|
|
if (!self) {
|
|
return;
|
|
}
|
|
|
|
fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self;
|
|
fat16_close(fds->node);
|
|
memory_free(self);
|
|
}
|
|
|
|
stream_t *fat16_open_stream(fs_node_t *source) {
|
|
if (!source) {
|
|
return NUL;
|
|
}
|
|
|
|
if (!source->is_dir) {
|
|
fat16_file_stream_t *result = memory_allocate(sizeof(fat16_file_stream_t));
|
|
result->stream.read = file_stream_read;
|
|
result->stream.write = file_stream_write;
|
|
result->stream.truncate = file_stream_truncate;
|
|
result->stream.close = file_stream_close;
|
|
result->node = fat16_open_again(source);
|
|
result->offset = 0;
|
|
return (stream_t *)result;
|
|
} else {
|
|
fat16_directory_stream_t *result = memory_allocate(sizeof(fat16_directory_stream_t));
|
|
result->stream.read = directory_stream_read;
|
|
result->stream.write = directory_stream_write;
|
|
result->stream.truncate = directory_stream_truncate;
|
|
result->stream.close = directory_stream_close;
|
|
result->node = fat16_open_again(source);
|
|
result->index = 0;
|
|
return (stream_t *)result;
|
|
}
|
|
}
|
|
|
|
void fat16_close(fs_node_t *node) {
|
|
if (!node || node->type != FAT16) {
|
|
return;
|
|
}
|
|
|
|
node_free(node);
|
|
}
|
|
|