fix(fs): remove 256-file limit in RAMFS using a dynamic linked list

This commit is contained in:
boreddevnl 2026-04-21 16:40:21 +02:00
parent c1411e378a
commit 9988a6e420

View file

@ -15,7 +15,6 @@
static spinlock_t ramfs_lock = SPINLOCK_INIT; // Protects the RAM-based filesystem (/) static spinlock_t ramfs_lock = SPINLOCK_INIT; // Protects the RAM-based filesystem (/)
#define MAX_FILES 256
#define MAX_CLUSTERS 8192 #define MAX_CLUSTERS 8192
#define MAX_OPEN_HANDLES 32 #define MAX_OPEN_HANDLES 32
@ -23,8 +22,7 @@ static spinlock_t ramfs_lock = SPINLOCK_INIT; // Protects the RAM-based filesyst
static uint32_t fat_table[MAX_CLUSTERS]; static uint32_t fat_table[MAX_CLUSTERS];
static uint8_t cluster_data[MAX_CLUSTERS][FAT32_CLUSTER_SIZE]; static uint8_t cluster_data[MAX_CLUSTERS][FAT32_CLUSTER_SIZE];
// File/Directory tracking typedef struct FileEntry {
typedef struct {
char full_path[FAT32_MAX_PATH]; char full_path[FAT32_MAX_PATH];
char filename[FAT32_MAX_FILENAME]; char filename[FAT32_MAX_FILENAME];
uint32_t start_cluster; uint32_t start_cluster;
@ -32,9 +30,10 @@ typedef struct {
uint32_t attributes; uint32_t attributes;
bool used; bool used;
char parent_path[FAT32_MAX_PATH]; char parent_path[FAT32_MAX_PATH];
struct FileEntry *next;
} FileEntry; } FileEntry;
static FileEntry files[MAX_FILES]; static FileEntry *file_list_head = NULL;
static uint32_t next_cluster = 3; // Start after reserved clusters 0, 1, 2 static uint32_t next_cluster = 3; // Start after reserved clusters 0, 1, 2
static FAT32_FileHandle open_handles[MAX_OPEN_HANDLES]; static FAT32_FileHandle open_handles[MAX_OPEN_HANDLES];
static char current_dir[FAT32_MAX_PATH] = "/"; static char current_dir[FAT32_MAX_PATH] = "/";
@ -234,9 +233,9 @@ static FileEntry* ramfs_find_file(const char *path) {
if (!normalized) return NULL; if (!normalized) return NULL;
fat32_normalize_path(path, normalized); fat32_normalize_path(path, normalized);
FileEntry *ret = NULL; FileEntry *ret = NULL;
for (int i = 0; i < MAX_FILES; i++) { for (FileEntry *n = file_list_head; n; n = n->next) {
if (files[i].used && fs_strcmp(files[i].full_path, normalized) == 0) { if (fs_strcmp(n->full_path, normalized) == 0) {
ret = &files[i]; ret = n;
break; break;
} }
} }
@ -244,11 +243,29 @@ static FileEntry* ramfs_find_file(const char *path) {
return ret; return ret;
} }
static FileEntry* ramfs_find_free_entry(void) { static FileEntry* ramfs_alloc_entry(void) {
for (int i = 0; i < MAX_FILES; i++) { FileEntry *e = (FileEntry*)kmalloc(sizeof(FileEntry));
if (!files[i].used) return &files[i]; if (!e) return NULL;
for (int i = 0; i < (int)sizeof(FileEntry); i++) ((char*)e)[i] = 0;
e->used = true;
e->next = file_list_head;
file_list_head = e;
return e;
}
static void ramfs_free_entry(FileEntry *entry) {
if (!entry) return;
if (file_list_head == entry) {
file_list_head = entry->next;
} else {
for (FileEntry *n = file_list_head; n && n->next; n = n->next) {
if (n->next == entry) {
n->next = entry->next;
break;
} }
return NULL; }
}
kfree(entry);
} }
static FAT32_FileHandle* ramfs_find_free_handle(void) { static FAT32_FileHandle* ramfs_find_free_handle(void) {
@ -267,10 +284,8 @@ static uint32_t ramfs_allocate_cluster(void) {
static int ramfs_count_files_in_dir(const char *normalized_path) { static int ramfs_count_files_in_dir(const char *normalized_path) {
int count = 0; int count = 0;
for (int i = 0; i < MAX_FILES; i++) { for (FileEntry *n = file_list_head; n; n = n->next) {
if (files[i].used && fs_strcmp(files[i].parent_path, normalized_path) == 0) { if (fs_strcmp(n->parent_path, normalized_path) == 0) count++;
count++;
}
} }
return count; return count;
} }
@ -306,14 +321,13 @@ static FAT32_FileHandle* ramfs_open(const char *normalized_path, const char *mod
} else if (mode[0] == 'w' || (mode[0] == 'a')) { } else if (mode[0] == 'w' || (mode[0] == 'a')) {
if (!entry) { if (!entry) {
if (!check_desktop_limit(normalized_path)) return NULL; if (!check_desktop_limit(normalized_path)) return NULL;
entry = ramfs_find_free_entry(); entry = ramfs_alloc_entry();
if (!entry) return NULL; if (!entry) return NULL;
entry->used = true;
fs_strcpy(entry->full_path, normalized_path); fs_strcpy(entry->full_path, normalized_path);
extract_filename(normalized_path, entry->filename); extract_filename(normalized_path, entry->filename);
extract_parent_path(normalized_path, entry->parent_path); extract_parent_path(normalized_path, entry->parent_path);
entry->start_cluster = ramfs_allocate_cluster(); entry->start_cluster = ramfs_allocate_cluster();
if (!entry->start_cluster) return NULL; if (!entry->start_cluster) { ramfs_free_entry(entry); return NULL; }
entry->size = 0; entry->size = 0;
entry->attributes = 0; entry->attributes = 0;
} }
@ -411,9 +425,9 @@ static int ramfs_write(FAT32_FileHandle *handle, const void *buffer, int size) {
} }
} }
for (int i = 0; i < MAX_FILES; i++) { for (FileEntry *n = file_list_head; n; n = n->next) {
if (files[i].used && files[i].start_cluster == handle->start_cluster) { if (n->start_cluster == handle->start_cluster) {
files[i].size = handle->size; n->size = handle->size;
break; break;
} }
} }
@ -1467,27 +1481,26 @@ static int vfs_ramfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_
vfs_ramfs_get_abs_path(rel_path, abs); vfs_ramfs_get_abs_path(rel_path, abs);
uint64_t rflags = spinlock_acquire_irqsave(&ramfs_lock); uint64_t rflags = spinlock_acquire_irqsave(&ramfs_lock);
for (int i = 0; i < MAX_FILES && count < max; i++) { for (FileEntry *n = file_list_head; n && count < max; n = n->next) {
bool match = false; bool match = false;
if (files[i].used && files[i].filename[0] != '\0') { if (n->filename[0] != '\0') {
if (fs_strcmp(files[i].parent_path, abs) == 0) match = true; if (fs_strcmp(n->parent_path, abs) == 0) match = true;
// Root unification: Treat "", "/", and "A:/" as root parent
if (!match && abs[0] == '/' && abs[1] == '\0') { if (!match && abs[0] == '/' && abs[1] == '\0') {
if (files[i].parent_path[0] == '\0' || if (n->parent_path[0] == '\0' ||
fs_strcmp(files[i].parent_path, "/") == 0 || fs_strcmp(n->parent_path, "/") == 0 ||
fs_strcmp(files[i].parent_path, "A:/") == 0) { fs_strcmp(n->parent_path, "A:/") == 0) {
match = true; match = true;
} }
} }
} }
if (match) { if (match) {
fs_strcpy(entries[count].name, files[i].filename); fs_strcpy(entries[count].name, n->filename);
entries[count].size = files[i].size; entries[count].size = n->size;
entries[count].is_directory = (files[i].attributes & ATTR_DIRECTORY) ? 1 : 0; entries[count].is_directory = (n->attributes & ATTR_DIRECTORY) ? 1 : 0;
entries[count].start_cluster = files[i].start_cluster; entries[count].start_cluster = n->start_cluster;
entries[count].write_date = 0; // Not tracked in RAMFS for now entries[count].write_date = 0;
entries[count].write_time = 0; entries[count].write_time = 0;
count++; count++;
} }
@ -1840,16 +1853,13 @@ void* fat32_mount_volume(void *disk_ptr) {
// === Public API (Dispatch) === // === Public API (Dispatch) ===
void fat32_init(void) { void fat32_init(void) {
// Explicitly zero out all structures for RAMFS safety FileEntry *node = file_list_head;
for (int i = 0; i < MAX_FILES; i++) { while (node) {
files[i].used = false; FileEntry *next = node->next;
files[i].full_path[0] = 0; kfree(node);
files[i].filename[0] = 0; node = next;
files[i].parent_path[0] = 0;
files[i].size = 0;
files[i].attributes = 0;
files[i].start_cluster = 0;
} }
file_list_head = NULL;
// Initialize FAT table for RAMFS // Initialize FAT table for RAMFS
for (int i = 0; i < MAX_CLUSTERS; i++) { for (int i = 0; i < MAX_CLUSTERS; i++) {
@ -1917,11 +1927,6 @@ FAT32_FileHandle* fat32_open_nolock(const char *path, const char *mode) {
} }
} }
} else if (path[0] == '/') { } else if (path[0] == '/') {
// Absolute VFS path - bypass legacy drive letters
// This is safe to call WITHOUT the fat32_lock because vfs_open was called formerly
// and it will resolve to one of our mounts which will then acquire the lock.
// HOWEVER, if we are already inside a fat32_lock call, we SHOULD NOT call vfs_open.
// For now, absolute paths starting with / are handled by VFS entry points.
vfs_file_t *vf = vfs_open(path, mode); vfs_file_t *vf = vfs_open(path, mode);
if (vf && vf->fs_handle) { if (vf && vf->fs_handle) {
return (FAT32_FileHandle*)vf->fs_handle; return (FAT32_FileHandle*)vf->fs_handle;
@ -2223,7 +2228,7 @@ bool fat32_mkdir(const char *path) {
return false; return false;
} }
FileEntry *entry = ramfs_find_free_entry(); FileEntry *entry = ramfs_alloc_entry();
if (!entry) { if (!entry) {
kfree(normalized); kfree(normalized);
spinlock_release_irqrestore(&ramfs_lock, rflags); spinlock_release_irqrestore(&ramfs_lock, rflags);
@ -2263,7 +2268,7 @@ bool fat32_rmdir(const char *path) {
return false; return false;
} }
entry->used = false; ramfs_free_entry(entry);
kfree(normalized); kfree(normalized);
wm_notify_fs_change(); wm_notify_fs_change();
spinlock_release_irqrestore(&ramfs_lock, rflags); spinlock_release_irqrestore(&ramfs_lock, rflags);
@ -2287,7 +2292,7 @@ bool fat32_delete(const char *path) {
FileEntry *entry = ramfs_find_file(normalized); FileEntry *entry = ramfs_find_file(normalized);
if (entry && !(entry->attributes & ATTR_DIRECTORY)) { if (entry && !(entry->attributes & ATTR_DIRECTORY)) {
entry->used = false; ramfs_free_entry(entry);
result = true; result = true;
} }
kfree(normalized); kfree(normalized);
@ -2426,27 +2431,26 @@ bool fat32_rename(const char *old_path, const char *new_path) {
char *suffix = (char*)kmalloc(FAT32_MAX_PATH); char *suffix = (char*)kmalloc(FAT32_MAX_PATH);
if (!suffix) { spinlock_release_irqrestore(&ramfs_lock, rflags); return false; } if (!suffix) { spinlock_release_irqrestore(&ramfs_lock, rflags); return false; }
for (int i = 0; i < MAX_FILES; i++) { for (FileEntry *n = file_list_head; n; n = n->next) {
if (!files[i].used) continue; if (fs_strcmp(n->full_path, old_path) == 0) {
if (fs_strcmp(files[i].full_path, old_path) == 0) { fs_strcpy(n->full_path, new_path);
fs_strcpy(files[i].full_path, new_path); extract_filename(new_path, n->filename);
extract_filename(new_path, files[i].filename); extract_parent_path(new_path, n->parent_path);
extract_parent_path(new_path, files[i].parent_path); } else if (fs_strlen(n->full_path) > old_len &&
} else if (fs_strlen(files[i].full_path) > old_len && fs_starts_with(n->full_path, old_path) &&
fs_starts_with(files[i].full_path, old_path) && n->full_path[old_len] == '/') {
files[i].full_path[old_len] == '/') { fs_strcpy(suffix, n->full_path + old_len);
fs_strcpy(suffix, files[i].full_path + old_len); fs_strcpy(n->full_path, new_path);
fs_strcpy(files[i].full_path, new_path); fs_strcat(n->full_path, suffix);
fs_strcat(files[i].full_path, suffix);
} }
if (fs_strcmp(files[i].parent_path, old_path) == 0) { if (fs_strcmp(n->parent_path, old_path) == 0) {
fs_strcpy(files[i].parent_path, new_path); fs_strcpy(n->parent_path, new_path);
} else if (fs_strlen(files[i].parent_path) > old_len && } else if (fs_strlen(n->parent_path) > old_len &&
fs_starts_with(files[i].parent_path, old_path) && fs_starts_with(n->parent_path, old_path) &&
files[i].parent_path[old_len] == '/') { n->parent_path[old_len] == '/') {
fs_strcpy(suffix, files[i].parent_path + old_len); fs_strcpy(suffix, n->parent_path + old_len);
fs_strcpy(files[i].parent_path, new_path); fs_strcpy(n->parent_path, new_path);
fs_strcat(files[i].parent_path, suffix); fs_strcat(n->parent_path, suffix);
} }
} }
kfree(suffix); kfree(suffix);
@ -2524,15 +2528,16 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr
if (!normalized) { spinlock_release_irqrestore(&ramfs_lock, rflags); return 0; } if (!normalized) { spinlock_release_irqrestore(&ramfs_lock, rflags); return 0; }
fat32_normalize_path(p, normalized); fat32_normalize_path(p, normalized);
for (int i = 0; i < MAX_FILES && count < max_entries; i++) { for (FileEntry *_n = file_list_head; _n && count < max_entries; _n = _n->next) {
if (files[i].used && fs_strcmp(files[i].parent_path, normalized) == 0) { if (fs_strcmp(_n->parent_path, normalized) != 0) continue;
fs_strcpy(entries[count].name, files[i].filename); fs_strcpy(entries[count].name, _n->filename);
entries[count].size = files[i].size; entries[count].size = _n->size;
entries[count].is_directory = (files[i].attributes & ATTR_DIRECTORY) != 0; entries[count].is_directory = (_n->attributes & ATTR_DIRECTORY) != 0;
entries[count].start_cluster = files[i].start_cluster; entries[count].start_cluster = _n->start_cluster;
entries[count].write_date = 0;
entries[count].write_time = 0;
count++; count++;
} }
}
kfree(normalized); kfree(normalized);
} }