FEAT: VFS overhaul

This commit is contained in:
boreddevnl
2026-04-12 17:53:31 +02:00
parent 921e8a5658
commit 700839e6be
48 changed files with 1897 additions and 482 deletions

View File

@@ -276,19 +276,22 @@ static int ramfs_count_files_in_dir(const char *normalized_path) {
static bool check_desktop_limit(const char *normalized_path) {
if (desktop_file_limit < 0) return true;
if (fs_strlen(normalized_path) > 9 &&
if (fs_strlen(normalized_path) > 14 &&
normalized_path[0] == '/' &&
normalized_path[1] == 'D' && normalized_path[2] == 'e' &&
normalized_path[3] == 's' && normalized_path[4] == 'k' &&
normalized_path[5] == 't' && normalized_path[6] == 'o' &&
normalized_path[7] == 'p' && normalized_path[8] == '/') {
const char *p = normalized_path + 9;
normalized_path[1] == 'r' && normalized_path[2] == 'o' &&
normalized_path[3] == 'o' && normalized_path[4] == 't' &&
normalized_path[5] == '/' &&
normalized_path[6] == 'D' && normalized_path[7] == 'e' &&
normalized_path[8] == 's' && normalized_path[9] == 'k' &&
normalized_path[10] == 't' && normalized_path[11] == 'o' &&
normalized_path[12] == 'p' && normalized_path[13] == '/') {
const char *p = normalized_path + 14;
while (*p) {
if (*p == '/') return true;
p++;
}
int count = ramfs_count_files_in_dir("/Desktop");
int count = ramfs_count_files_in_dir("/root/Desktop");
if (count >= desktop_file_limit) return false;
}
return true;
@@ -707,7 +710,27 @@ static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *pat
to_dos_filename(component, dos_name);
int name_len = fs_strlen(component);
bool needs_lfn = (name_len > 12);
// Determine if LFN is needed:
// LFN is needed if the name contains characters that don't fit in 8.3 format
// or if the extension is longer than 3 chars or name part is longer than 8 chars
bool needs_lfn = false;
int dot_pos = -1;
for (int i = 0; i < name_len; i++) {
if (component[i] == '.') { dot_pos = i; break; }
}
if (dot_pos == -1) {
// No extension - need LFN if name > 8 chars
needs_lfn = (name_len > 8);
} else {
// Has extension
int name_part = dot_pos;
int ext_part = name_len - dot_pos - 1;
// Need LFN if name > 8 chars or extension > 3 chars
needs_lfn = (name_part > 8) || (ext_part > 3);
}
int lfn_entries = needs_lfn ? ((name_len + 12) / 13) : 0;
int total_entries = lfn_entries + 1;
@@ -753,29 +776,29 @@ static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *pat
d->start_cluster_low = 0;
d->file_size = 0;
realfs_write_cluster(vol, free_cluster, lfn_cl_buf);
uint32_t lba = vol->cluster_begin_lba + (free_cluster - 2) * vol->sectors_per_cluster;
entry_sector = lba + ((start_entry_idx + lfn_entries) * 32) / 512;
entry_offset = ((start_entry_idx + lfn_entries) * 32) % 512;
kfree(lfn_cl_buf);
if (cluster_buf) kfree(cluster_buf);
FAT32_FileHandle *fh = ramfs_find_free_handle();
if (fh) {
fh->valid = true;
fh->volume = vol;
fh->start_cluster = 0;
fh->cluster = 0;
fh->position = 0;
fh->size = 0;
fh->mode = (mode[0] == 'a' ? 2 : 1);
fh->is_directory = false;
fh->attributes = ATTR_ARCHIVE;
fh->dir_sector = entry_sector;
fh->dir_offset = entry_offset;
return fh;
if (realfs_write_cluster(vol, free_cluster, lfn_cl_buf) == 0) {
uint32_t lba = vol->cluster_begin_lba + (free_cluster - 2) * vol->sectors_per_cluster;
entry_sector = lba + ((start_entry_idx + lfn_entries) * 32) / 512;
entry_offset = ((start_entry_idx + lfn_entries) * 32) % 512;
kfree(lfn_cl_buf);
if (cluster_buf) kfree(cluster_buf);
FAT32_FileHandle *fh = ramfs_find_free_handle();
if (fh) {
fh->valid = true;
fh->volume = vol;
fh->start_cluster = 0;
fh->cluster = 0;
fh->position = 0;
fh->size = 0;
fh->mode = (mode[0] == 'a' ? 2 : 1);
fh->is_directory = false;
fh->attributes = ATTR_ARCHIVE;
fh->dir_sector = entry_sector;
fh->dir_offset = entry_offset;
return fh;
}
}
}
}
@@ -951,6 +974,9 @@ static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
uint32_t current = 2;
uint32_t fat_entries = (vol->fat_size * 512) / 4;
// Skip cluster 2 as it's reserved for the root directory in FAT32
if (current == vol->root_cluster) current++;
uint8_t *fat_buf = (uint8_t*)kmalloc(512);
if (!fat_buf) return 0;
@@ -1006,18 +1032,33 @@ static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int s
}
handle->start_cluster = new_cluster;
handle->cluster = new_cluster;
handle->position = 0;
handle->size = 0;
// Update directory entry immediately with correct start_cluster
// This ensures the directory always points to the right cluster
realfs_update_dir_entry_size(vol, handle);
}
while (bytes_written < size) {
if (realfs_read_cluster(vol, handle->cluster, cluster_buf) != 0) break;
uint32_t offset = handle->position % cluster_size;
// Always zero the buffer first to ensure clean state
for (int i = 0; i < (int)cluster_size; i++) cluster_buf[i] = 0;
// If we're in the middle of a cluster, read the existing data first
if (offset > 0) {
if (realfs_read_cluster(vol, handle->cluster, cluster_buf) != 0) break;
}
int to_copy = size - bytes_written;
int available = cluster_size - offset;
if (to_copy > available) to_copy = available;
for (int i = 0; i < to_copy; i++) cluster_buf[offset + i] = src_buf[bytes_written + i];
// Copy new data into the cluster buffer
for (int i = 0; i < to_copy; i++) {
cluster_buf[offset + i] = src_buf[bytes_written + i];
}
if (realfs_write_cluster(vol, handle->cluster, cluster_buf) != 0) break;
@@ -1152,10 +1193,15 @@ static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path) {
int lfn_start_entry = -1;
if (has_lfn) {
// This is an oversimplification, but for same-sector LFNs:
// Find all LFN entries in reverse from the main entry
// LFN entries are marked by their order field and must be contiguous
for (int k = e - 1; k >= 0; k--) {
if (entry[k].attributes == ATTR_LFN) lfn_start_entry = k;
else break;
if (entry[k].attributes == ATTR_LFN) {
lfn_start_entry = k; // Keep updating to find earliest LFN
} else {
// Stop when we hit a non-LFN entry that's not deleted
if (entry[k].filename[0] != 0xE5) break;
}
}
}
@@ -1170,7 +1216,7 @@ static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path) {
if (*p == 0) {
// Found target file/directory to delete
// Mark LFN entries as deleted too (if in same sector)
// Mark LFN entries as deleted too
if (lfn_start_entry != -1) {
for (int k = lfn_start_entry; k < e; k++) {
entry[k].filename[0] = 0xE5;
@@ -1180,10 +1226,49 @@ static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path) {
entry[e].filename[0] = 0xE5;
// Persist the changes to disk
// Calculate exactly which sector within the cluster we modified
// CRITICAL FIX: Write ALL sectors that contain modified entries
// LFN entries and main entry may span multiple sectors in the cluster
uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster;
int sect_in_cluster = (e * 32) / 512;
vol->disk->write_sector(vol->disk, lba + sect_in_cluster, ((uint8_t*)entry) + (sect_in_cluster * 512));
// Find all sectors touched by LFN and main entries
uint8_t sectors_to_write[8] = {0}; // Max 8 sectors per cluster
int num_sectors = 0;
// Mark sectors containing LFN entries
if (lfn_start_entry != -1) {
for (int k = lfn_start_entry; k < e; k++) {
int sect_idx = (k * 32) / 512;
bool already_marked = false;
for (int s = 0; s < num_sectors; s++) {
if (sectors_to_write[s] == sect_idx) {
already_marked = true;
break;
}
}
if (!already_marked && num_sectors < 8) {
sectors_to_write[num_sectors++] = sect_idx;
}
}
}
// Mark sector containing main entry
int main_sect_idx = (e * 32) / 512;
bool already_marked = false;
for (int s = 0; s < num_sectors; s++) {
if (sectors_to_write[s] == main_sect_idx) {
already_marked = true;
break;
}
}
if (!already_marked && num_sectors < 8) {
sectors_to_write[num_sectors++] = main_sect_idx;
}
// Write all modified sectors
for (int i = 0; i < num_sectors; i++) {
int sect_idx = sectors_to_write[i];
vol->disk->write_sector(vol->disk, lba + sect_idx, ((uint8_t*)entry) + (sect_idx * 512));
}
found = true;
} else {

290
src/fs/procfs.c Normal file
View File

@@ -0,0 +1,290 @@
#include "vfs.h"
#include "../sys/process.h"
#include "../sys/syscall.h"
#include "../dev/disk.h"
#include "memory_manager.h"
#include "core/kutils.h"
typedef struct {
uint32_t pid;
char type[32];
int offset;
bool is_root;
} procfs_handle_t;
void* procfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
procfs_handle_t *h = (procfs_handle_t*)kmalloc(sizeof(procfs_handle_t));
k_memset(h, 0, sizeof(procfs_handle_t));
h->offset = 0;
if (path[0] == '\0') {
h->is_root = true;
return h;
}
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
h->pid = k_atoi(pid_str);
if (path[i] == '/') {
k_strcpy(h->type, path + i + 1);
} else {
h->type[0] = 0;
}
return h;
}
h->pid = 0xFFFFFFFF;
k_strcpy(h->type, path);
return h;
}
void procfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
int procfs_read(void *fs_private, void *handle, void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h) return -1;
char out[1024];
out[0] = 0;
if (h->pid == 0xFFFFFFFF) {
if (k_strcmp(h->type, "version") == 0) {
extern void get_os_info(os_info_t *info);
os_info_t info;
get_os_info(&info);
k_strcpy(out, info.os_name);
k_strcpy(out + k_strlen(out), " [");
k_strcpy(out + k_strlen(out), info.os_codename);
k_strcpy(out + k_strlen(out), "] Version ");
k_strcpy(out + k_strlen(out), info.os_version);
k_strcpy(out + k_strlen(out), "\nKernel: ");
k_strcpy(out + k_strlen(out), info.kernel_name);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.kernel_version);
k_strcpy(out + k_strlen(out), "\nBuild: ");
k_strcpy(out + k_strlen(out), info.build_date);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.build_time);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "uptime") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
k_itoa(ticks / 60, out);
k_strcpy(out + k_strlen(out), " seconds\nRaw_Ticks:");
char t_s[16]; k_itoa(ticks, t_s);
k_strcpy(out + k_strlen(out), t_s);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "cpuinfo") == 0) {
extern uint32_t smp_cpu_count(void);
extern void platform_get_cpu_model(char *model);
char model[64];
platform_get_cpu_model(model);
k_strcpy(out, "Processor: ");
k_strcpy(out + k_strlen(out), model);
k_strcpy(out + k_strlen(out), "\nCores: ");
char c_s[16]; k_itoa(smp_cpu_count(), c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\nArchitecture: x86_64\n");
} else if (k_strcmp(h->type, "meminfo") == 0) {
extern MemStats memory_get_stats(void);
MemStats stats = memory_get_stats();
k_strcpy(out, "MemTotal: ");
char m_s[32]; k_itoa(stats.total_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nMemFree: ");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nMemUsed: ");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nPeak: ");
k_itoa(stats.peak_memory_used / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nBlocks: ");
k_itoa(stats.allocated_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\nFragmentation: ");
k_itoa(stats.fragmentation_percent, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "%\n");
} else if (k_strcmp(h->type, "devices") == 0) {
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
k_strcpy(out, "Block Devices:\n");
for (int i = 0; i < dcount; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
k_strcpy(out + k_strlen(out), " - ");
k_strcpy(out + k_strlen(out), d->devname);
k_strcpy(out + k_strlen(out), "\n");
}
}
}
}
else {
process_t *proc = process_get_by_pid(h->pid);
if (!proc) return -1;
if (k_strcmp(h->type, "name") == 0 || k_strcmp(h->type, "cmdline") == 0) {
k_strcpy(out, proc->name);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "status") == 0) {
k_strcpy(out, "Name: ");
k_strcpy(out + k_strlen(out), proc->name);
k_strcpy(out + k_strlen(out), "\nPID: ");
char pid_s[16]; k_itoa(proc->pid, pid_s);
k_strcpy(out + k_strlen(out), pid_s);
k_strcpy(out + k_strlen(out), "\nState: RUNNING\nMemory: ");
uint64_t mem_val = proc->used_memory;
if (h->pid == 0) {
extern MemStats memory_get_stats(void);
mem_val = memory_get_stats().used_memory;
}
char mem_s[32]; k_itoa(mem_val / 1024, mem_s);
k_strcpy(out + k_strlen(out), mem_s);
k_strcpy(out + k_strlen(out), " KB\nTicks: ");
char tick_s[32]; k_itoa(proc->ticks, tick_s);
k_strcpy(out + k_strlen(out), tick_s);
k_strcpy(out + k_strlen(out), "\nIdle: ");
k_strcpy(out + k_strlen(out), proc->is_idle ? "1" : "0");
k_strcpy(out + k_strlen(out), "\n");
}
}
int len = k_strlen(out);
if (h->offset >= len) return 0;
int to_copy = len - h->offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + h->offset, to_copy);
h->offset += to_copy;
return to_copy;
}
int procfs_write(void *fs_private, void *handle, const void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h || h->pid == 0xFFFFFFFF) return -1;
if (k_strcmp(h->type, "signal") == 0) {
char cmd[16];
int to_copy = size < 15 ? size : 15;
k_memcpy(cmd, buf, to_copy);
cmd[to_copy] = 0;
if (k_strcmp(cmd, "9") == 0 || k_strcmp(cmd, "kill") == 0) {
process_t *proc = process_get_by_pid(h->pid);
if (proc && proc->pid != 0) {
process_terminate(proc);
return size;
}
}
}
return -1;
}
int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
if (path[0] == '/') path++;
if (path[0] == '\0') {
int out = 0;
k_strcpy(entries[out++].name, "version");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "uptime");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "cpuinfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "meminfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "devices");
entries[out-1].is_directory = 0;
extern process_t processes[];
for (int i = 0; i < 16 && out < max; i++) {
if (processes[i].pid != 0xFFFFFFFF) {
k_itoa(processes[i].pid, entries[out].name);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
}
}
return out;
}
if (path[0] >= '0' && path[0] <= '9') {
int out = 0;
k_strcpy(entries[out++].name, "name");
k_strcpy(entries[out++].name, "status");
k_strcpy(entries[out++].name, "cmdline");
k_strcpy(entries[out++].name, "signal");
for(int i=0; i<out; i++) entries[i].is_directory = 0;
return out;
}
return 0;
}
bool procfs_exists(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
uint32_t pid = k_atoi(pid_str);
if (process_get_by_pid(pid)) return true;
}
if (k_strcmp(path, "version") == 0 || k_strcmp(path, "uptime") == 0) return true;
if (k_strcmp(path, "cpuinfo") == 0 || k_strcmp(path, "meminfo") == 0) return true;
if (k_strcmp(path, "devices") == 0) return true;
return false;
}
bool procfs_is_dir(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
int i = 0;
while (path[i] && path[i] != '/') i++;
if (path[i] == '\0') return true;
return false;
}
return false;
}
vfs_fs_ops_t procfs_ops = {
.open = procfs_open,
.close = procfs_close,
.read = procfs_read,
.write = procfs_write,
.readdir = procfs_readdir,
.exists = procfs_exists,
.is_dir = procfs_is_dir
};
vfs_fs_ops_t* procfs_get_ops(void) {
return &procfs_ops;
}

8
src/fs/procfs.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef PROCFS_H
#define PROCFS_H
#include "vfs.h"
vfs_fs_ops_t* procfs_get_ops(void);
#endif

181
src/fs/sysfs.c Normal file
View File

@@ -0,0 +1,181 @@
#include "vfs.h"
#include "../sys/kernel_subsystem.h"
#include "memory_manager.h"
#include "core/kutils.h"
typedef struct {
kernel_subsystem_t *sub;
subsystem_file_t *file;
int offset;
} sysfs_handle_t;
static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
if (path[0] == '\0') return NULL;
kernel_subsystem_t *sub = NULL;
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) {
sysfs_handle_t *h = (sysfs_handle_t*)kmalloc(sizeof(sysfs_handle_t));
h->sub = sub;
h->file = &sub->files[j];
h->offset = 0;
return h;
}
}
}
}
return NULL;
}
static void sysfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
static int sysfs_read(void *fs_private, void *handle, void *buf, int size) {
sysfs_handle_t *h = (sysfs_handle_t*)handle;
if (!h || !h->file || !h->file->read) return -1;
int bytes = h->file->read((char*)buf, size, h->offset);
if (bytes > 0) h->offset += bytes;
return bytes;
}
static int sysfs_write(void *fs_private, void *handle, const void *buf, int size) {
sysfs_handle_t *h = (sysfs_handle_t*)handle;
if (!h || !h->file || !h->file->write) return -1;
int bytes = h->file->write((const char*)buf, size, h->offset);
if (bytes > 0) h->offset += bytes;
return bytes;
}
static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
if (path[0] == '/') path++;
kernel_subsystem_t *exact_sub = subsystem_get_by_name(path);
int out = 0;
if (exact_sub) {
for (int i = 0; i < exact_sub->file_count && out < max; i++) {
k_strcpy(entries[out].name, exact_sub->files[i].name);
entries[out].is_directory = 0;
entries[out].size = 0;
out++;
}
}
int count = subsystem_get_count();
int path_len = k_strlen(path);
for (int i = 0; i < count && out < max; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (path_len == 0 || (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
const char *sub_path = s->name + (path_len ? path_len + 1 : 0);
char comp[64];
int j = 0;
while (sub_path[j] && sub_path[j] != '/' && j < 63) {
comp[j] = sub_path[j];
j++;
}
comp[j] = 0;
if (comp[0] == '\0') continue;
bool found = false;
for (int k = 0; k < out; k++) {
if (k_strcmp(entries[k].name, comp) == 0) {
found = true;
break;
}
}
if (!found) {
k_strcpy(entries[out].name, comp);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
}
}
}
return out;
}
static bool sysfs_exists(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (subsystem_get_by_name(path)) return true;
// File check
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) return true;
}
}
}
int count = subsystem_get_count();
int path_len = k_strlen(path);
for (int i = 0; i < count; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
}
return false;
}
static bool sysfs_is_dir(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) return false;
}
}
}
return sysfs_exists(fs_private, path);
}
vfs_fs_ops_t sysfs_ops = {
.open = sysfs_open,
.close = sysfs_close,
.read = sysfs_read,
.write = sysfs_write,
.readdir = sysfs_readdir,
.exists = sysfs_exists,
.is_dir = sysfs_is_dir
};
vfs_fs_ops_t* sysfs_get_ops(void) {
return &sysfs_ops;
}

8
src/fs/sysfs.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef SYSFS_H
#define SYSFS_H
#include "vfs.h"
vfs_fs_ops_t* sysfs_get_ops(void);
#endif

View File

@@ -6,10 +6,8 @@
#include "spinlock.h"
#include <stddef.h>
#include "disk.h"
#include "process.h"
// ============================================================================
// VFS Mount Table and File Handle Pool
// ============================================================================
static vfs_mount_t mounts[VFS_MAX_MOUNTS];
static int mount_count = 0;
@@ -19,10 +17,6 @@ static spinlock_t vfs_lock = SPINLOCK_INIT;
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
// ============================================================================
// String helpers (freestanding — no libc)
// ============================================================================
static int vfs_strlen(const char *s) {
int n = 0;
while (s[n]) n++;
@@ -53,44 +47,63 @@ static bool vfs_starts_with(const char *str, const char *prefix) {
return true;
}
// ============================================================================
// Path Normalization
// ============================================================================
static bool vfs_path_is_parent(const char *parent, const char *child) {
int plen = vfs_strlen(parent);
if (vfs_strncmp(parent, child, plen) != 0) return false;
if (child[plen] == '\0') return true;
if (child[plen] == '/') return true;
if (plen == 1 && parent[0] == '/') return true;
return false;
}
void vfs_normalize_path(const char *path, char *normalized) {
// Resolve . and .. components, remove duplicate slashes
char parts[32][128];
void vfs_normalize_path(const char *cwd, const char *path, char *normalized) {
char parts[32][64]; // Reduced size to save stack, 64 is enough for most names
int depth = 0;
int i = 0;
// Skip leading slash
// Handle relative path by starting with CWD
if (path[0] != '/' && cwd) {
int ci = 0;
if (cwd[0] == '/') ci = 1;
while (cwd[ci]) {
if (cwd[ci] == '/') { ci++; continue; }
int j = 0;
while (cwd[ci] && cwd[ci] != '/' && j < 63) {
parts[depth][j++] = cwd[ci++];
}
parts[depth][j] = 0;
if (j > 0) depth++;
if (depth >= 32) break;
if (cwd[ci] == '/') ci++;
}
}
if (path[0] == '/') i = 1;
while (path[i]) {
// Skip duplicate slashes
if (path[i] == '/') { i++; continue; }
// Extract component
int j = 0;
while (path[i] && path[i] != '/' && j < 127) {
while (path[i] && path[i] != '/' && j < 63) {
parts[depth][j++] = path[i++];
}
parts[depth][j] = 0;
if (parts[depth][0] == '.' && parts[depth][1] == 0) {
// "." skip
// "." skip
} else if (parts[depth][0] == '.' && parts[depth][1] == '.' && parts[depth][2] == 0) {
// ".." — go up
// ".." pop
if (depth > 0) depth--;
} else {
depth++;
if (depth >= 32) break;
if (j > 0) {
depth++;
if (depth >= 32) break;
}
}
if (path[i] == '/') i++;
}
// Reconstruct
normalized[0] = '/';
int pos = 1;
for (int k = 0; k < depth; k++) {
@@ -102,16 +115,11 @@ void vfs_normalize_path(const char *path, char *normalized) {
}
normalized[pos] = 0;
// Ensure root is just "/"
if (pos == 1 && normalized[0] == '/') {
normalized[1] = 0;
}
}
// ============================================================================
// Mount Resolution — find the longest-prefix mount for a path
// ============================================================================
static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_out) {
vfs_mount_t *best = NULL;
int best_len = -1;
@@ -121,7 +129,6 @@ static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_ou
int mlen = mounts[i].path_len;
// Root mount "/" matches everything
if (mlen == 1 && mounts[i].path[0] == '/') {
if (best_len < 1) {
best = &mounts[i];
@@ -130,9 +137,7 @@ static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_ou
continue;
}
// Check if path starts with this mount point
if (vfs_strncmp(path, mounts[i].path, mlen) == 0) {
// Must be followed by '/' or end of string
if (path[mlen] == '/' || path[mlen] == '\0') {
if (mlen > best_len) {
best = &mounts[i];
@@ -144,7 +149,6 @@ static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_ou
if (best && rel_path_out) {
const char *rel = path + best_len;
// Skip leading slash in relative path
while (*rel == '/') rel++;
*rel_path_out = rel;
}
@@ -152,14 +156,14 @@ static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_ou
return best;
}
// ============================================================================
// File Handle Pool
// ============================================================================
static vfs_file_t* vfs_alloc_file(void) {
for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) {
if (!open_files[i].valid) {
open_files[i].valid = true;
open_files[i].fs_handle = NULL;
open_files[i].mount = NULL;
open_files[i].position = 0;
open_files[i].is_device = false;
return &open_files[i];
}
}
@@ -171,13 +175,11 @@ static void vfs_free_file(vfs_file_t *f) {
f->valid = false;
f->fs_handle = NULL;
f->mount = NULL;
f->position = 0;
f->is_device = false;
}
}
// ============================================================================
// Initialization
// ============================================================================
void vfs_init(void) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
mounts[i].active = false;
@@ -190,9 +192,9 @@ void vfs_init(void) {
serial_write("[VFS] Virtual File System initialized\n");
}
// ============================================================================
// ===============
// Mount / Unmount
// ============================================================================
// ===============
bool vfs_mount(const char *mount_path, const char *device, const char *fs_type,
vfs_fs_ops_t *ops, void *fs_private) {
@@ -204,7 +206,6 @@ bool vfs_mount(const char *mount_path, const char *device, const char *fs_type,
return false;
}
// Check for duplicate mount
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) {
spinlock_release_irqrestore(&vfs_lock, flags);
@@ -243,7 +244,6 @@ bool vfs_umount(const char *mount_path) {
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) {
// Close any open files on this mount
for (int j = 0; j < VFS_MAX_OPEN_FILES; j++) {
if (open_files[j].valid && open_files[j].mount == &mounts[i]) {
if (mounts[i].ops->close) {
@@ -274,27 +274,44 @@ bool vfs_umount(const char *mount_path) {
return false;
}
// ============================================================================
// ==============
// File Operations
// ============================================================================
// ==============
vfs_file_t* vfs_open(const char *path, const char *mode) {
if (!path || !mode) return NULL;
// Normalize path
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
process_t *proc = process_get_current();
vfs_normalize_path(proc ? proc->cwd : "/", path, normalized);
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
// Fallback for block devices (/dev/sda etc)
if (vfs_starts_with(normalized, "/dev/")) {
const char *devname = normalized + 5;
Disk *d = disk_get_by_name(devname);
if (d && (!mount || mount->path_len == 1)) {
vfs_file_t *vf = vfs_alloc_file();
if (vf) {
vf->mount = &mounts[0];
vf->fs_handle = (void*)d;
vf->is_device = true;
vf->position = 0;
spinlock_release_irqrestore(&vfs_lock, flags);
return vf;
}
}
}
if (!mount || !mount->ops->open) {
spinlock_release_irqrestore(&vfs_lock, flags);
return NULL;
}
// If rel_path is empty, use root
if (!rel_path || rel_path[0] == '\0') {
rel_path = "/";
}
@@ -308,7 +325,6 @@ vfs_file_t* vfs_open(const char *path, const char *mode) {
vf->mount = mount;
// Release lock before calling FS ops (FS has its own locking)
spinlock_release_irqrestore(&vfs_lock, flags);
void *fs_handle = mount->ops->open(mount->fs_private, rel_path, mode);
@@ -338,9 +354,38 @@ void vfs_close(vfs_file_t *file) {
int vfs_read(vfs_file_t *file, void *buf, int size) {
if (!file || !file->valid || !file->mount) return -1;
if (!file->mount->ops->read) return -1;
return file->mount->ops->read(file->mount->fs_private, file->fs_handle, buf, size);
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
if (!d) return -1;
uint32_t total_read = 0;
uint32_t sector = (uint32_t)(file->position / 512);
uint32_t offset = (uint32_t)(file->position % 512);
uint8_t sector_buf[512];
while (total_read < (uint32_t)size) {
if (sector >= d->total_sectors) break;
if (d->read_sector(d, sector, sector_buf) != 0) break;
uint32_t to_copy = 512 - offset;
if (to_copy > (uint32_t)size - total_read) to_copy = (uint32_t)size - total_read;
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy((uint8_t*)buf + total_read, sector_buf + offset, to_copy);
total_read += to_copy;
file->position += to_copy;
sector++;
offset = 0;
}
return (int)total_read;
}
if (!file->mount->ops->read) return -1;
int ret = file->mount->ops->read(file->mount->fs_private, file->fs_handle, buf, size);
if (ret > 0) file->position += ret;
return ret;
}
int vfs_write(vfs_file_t *file, const void *buf, int size) {
@@ -352,32 +397,60 @@ int vfs_write(vfs_file_t *file, const void *buf, int size) {
int vfs_seek(vfs_file_t *file, int offset, int whence) {
if (!file || !file->valid || !file->mount) return -1;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
if (!d) return -1;
uint64_t new_pos = file->position;
if (whence == 0) new_pos = (uint64_t)offset; // SET
else if (whence == 1) new_pos += (uint64_t)offset; // CUR
else if (whence == 2) new_pos = (uint64_t)(d->total_sectors * 512 + offset); // END
if (new_pos > (uint64_t)d->total_sectors * 512) new_pos = (uint64_t)d->total_sectors * 512;
file->position = new_pos;
return 0;
}
if (!file->mount->ops->seek) return -1;
return file->mount->ops->seek(file->mount->fs_private, file->fs_handle, offset, whence);
int ret = file->mount->ops->seek(file->mount->fs_private, file->fs_handle, offset, whence);
if (ret == 0) {
// Sync position back from driver if possible
if (file->mount->ops->get_position) {
file->position = file->mount->ops->get_position(file->fs_handle);
} else {
// Manual sync if driver doesn't support get_position but seek succeeded
if (whence == 0) file->position = offset;
else if (whence == 1) file->position += offset;
}
}
return ret;
}
uint32_t vfs_file_position(vfs_file_t *file) {
if (!file || !file->valid || !file->mount) return 0;
if (file->is_device) return (uint32_t)file->position;
if (!file->mount->ops->get_position) return 0;
return file->mount->ops->get_position(file->fs_handle);
}
uint32_t vfs_file_size(vfs_file_t *file) {
if (!file || !file->valid || !file->mount) return 0;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
return d ? d->total_sectors * 512 : 0;
}
if (!file->mount->ops->get_size) return 0;
return file->mount->ops->get_size(file->fs_handle);
}
// ============================================================================
// Directory Operations
// ============================================================================
int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) {
if (!path || !entries) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
@@ -386,22 +459,19 @@ int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) {
if (mount && mount->ops->readdir) {
if (!rel_path || rel_path[0] == '\0') rel_path = "/";
count = mount->ops->readdir(mount->fs_private, rel_path, entries, max);
if (count < 0) count = 0; // Treat as virtual if readdir fails
if (count < 0) count = 0;
}
// Merge in other mount points that are direct children of this path
uint64_t v_flags = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (!mounts[i].active) continue;
if (vfs_strcmp(mounts[i].path, normalized) == 0) continue; // Skip ourselves
if (vfs_strcmp(mounts[i].path, normalized) == 0) continue;
// Check if mount path starts with current path
if (vfs_starts_with(mounts[i].path, normalized)) {
if (vfs_path_is_parent(normalized, mounts[i].path)) {
const char *sub = mounts[i].path + vfs_strlen(normalized);
if (*sub == '/') sub++; // skip slash
if (*sub == '/') sub++;
if (*sub != '\0') {
// Extract first component (direct child name)
char comp[VFS_MAX_NAME];
int j = 0;
while (sub[j] && sub[j] != '/' && j < VFS_MAX_NAME - 1) {
@@ -410,7 +480,6 @@ int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) {
}
comp[j] = 0;
// Check if already in results
bool found = false;
for (int k = 0; k < count; k++) {
if (vfs_strcmp(entries[k].name, comp) == 0) {
@@ -431,21 +500,24 @@ int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) {
}
spinlock_release_irqrestore(&vfs_lock, v_flags);
// Special case: Ensure "dev" is visible in "/"
// Special case: Ensure "dev", "sys", "proc" are visible in "/"
if (vfs_strcmp(normalized, "/") == 0) {
bool found_dev = false;
for (int i = 0; i < count; i++) {
if (vfs_strcmp(entries[i].name, "dev") == 0) {
found_dev = true;
break;
const char *virtual_dirs[] = {"dev", "sys", "proc"};
for (int v = 0; v < 3; v++) {
bool found = false;
for (int i = 0; i < count; i++) {
if (vfs_strcmp(entries[i].name, virtual_dirs[v]) == 0) {
found = true;
break;
}
}
if (!found && count < max) {
vfs_strcpy(entries[count].name, virtual_dirs[v]);
entries[count].is_directory = 1;
entries[count].size = 0;
entries[count].start_cluster = 0;
count++;
}
}
if (!found_dev && count < max) {
vfs_strcpy(entries[count].name, "dev");
entries[count].is_directory = 1;
entries[count].size = 0;
entries[count].start_cluster = 0;
count++;
}
}
@@ -465,7 +537,7 @@ int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) {
if (!found) {
vfs_strcpy(entries[count].name, d->devname);
entries[count].size = d->total_sectors * 512;
entries[count].is_directory = d->is_partition ? 1 : 0;
entries[count].is_directory = 0;
entries[count].start_cluster = 0;
entries[count].write_date = 0;
entries[count].write_time = 0;
@@ -482,15 +554,14 @@ bool vfs_mkdir(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
// If it's in /dev/, check if it's within a mounted volume deeper than the device node
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false; // Protect raw device nodes
return false;
}
}
@@ -502,19 +573,17 @@ bool vfs_rmdir(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
vfs_normalize_path("/", path, normalized);
// Protect root and virtual /dev directory itself
if (normalized[0] == '/' && normalized[1] == '\0') return false;
if (vfs_strcmp(normalized, "/dev") == 0) return false;
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
// If it's in /dev/, allow only if it's inside a mount beyond the device node
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false; // Protect raw device nodes
return false;
}
}
@@ -526,19 +595,17 @@ bool vfs_delete(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
vfs_normalize_path("/", path, normalized);
// Protect root and virtual /dev directory itself
if (normalized[0] == '/' && normalized[1] == '\0') return false;
if (vfs_strcmp(normalized, "/dev") == 0) return false;
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
// If it's in /dev/, allow only if it's inside a mount beyond the device node
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false; // Protect raw device nodes
return false;
}
}
@@ -550,14 +617,13 @@ bool vfs_rename(const char *old_path, const char *new_path) {
if (!old_path || !new_path) return false;
char norm_old[VFS_MAX_PATH], norm_new[VFS_MAX_PATH];
vfs_normalize_path(old_path, norm_old);
vfs_normalize_path(new_path, norm_new);
vfs_normalize_path("/", old_path, norm_old);
vfs_normalize_path("/", new_path, norm_new);
const char *rel_old = NULL, *rel_new = NULL;
vfs_mount_t *mount_old = vfs_resolve_mount(norm_old, &rel_old);
vfs_mount_t *mount_new = vfs_resolve_mount(norm_new, &rel_new);
// Can only rename within the same mount
if (!mount_old || mount_old != mount_new) return false;
if (!mount_old->ops->rename) return false;
@@ -567,20 +633,14 @@ bool vfs_rename(const char *old_path, const char *new_path) {
return mount_old->ops->rename(mount_old->fs_private, rel_old, rel_new);
}
// ============================================================================
// Query Operations
// ============================================================================
bool vfs_exists(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
vfs_normalize_path("/", path, normalized);
// Root always exists
if (normalized[0] == '/' && normalized[1] == '\0') return true;
// Check if it's a prefix of any active mount point
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) {
@@ -590,10 +650,10 @@ bool vfs_exists(const char *path) {
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
// /dev always exists as a virtual directory
if (vfs_strcmp(normalized, "/dev") == 0) return true;
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) return true;
// Check if it's a device in /dev
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
if (disk_get_by_name(dev)) return true;
@@ -603,7 +663,7 @@ bool vfs_exists(const char *path) {
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount || !mount->ops->exists) return false;
if (!rel_path || rel_path[0] == '\0') return true; // Mount point itself exists
if (!rel_path || rel_path[0] == '\0') return true;
return mount->ops->exists(mount->fs_private, rel_path);
}
@@ -612,43 +672,38 @@ bool vfs_is_directory(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
vfs_normalize_path("/", path, normalized);
// Root is always a directory
if (normalized[0] == '/' && normalized[1] == '\0') return true;
// Check if it's a prefix of any active mount point (virtual directory)
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) {
// If it matches exactly a mount point, we still need to check if that FS is a dir
if (mounts[i].active && vfs_path_is_parent(normalized, mounts[i].path)) {
if (vfs_strcmp(mounts[i].path, normalized) == 0) {
// Exact mount point - it is a directory (mount root)
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
// Prefix only - it is a virtual intermediate directory
// If normalized is a parent of a mount, it's a virtual directory
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
// /dev is always a virtual directory
if (vfs_strcmp(normalized, "/dev") == 0) return true;
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) return true;
// Device check
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
Disk *d = disk_get_by_name(dev);
if (d) return d->is_partition ? true : false;
if (d) return false;
}
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount) return false;
// If it's a mount point and we're at its root, it definitely exists as a dir
if (!rel_path || rel_path[0] == '\0') return true;
if (!mount->ops->is_dir) return false;
@@ -659,9 +714,8 @@ int vfs_get_info(const char *path, vfs_dirent_t *info) {
if (!path || !info) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(path, normalized);
vfs_normalize_path("/", path, normalized);
// Root info
if (normalized[0] == '/' && normalized[1] == '\0') {
vfs_strcpy(info->name, "/");
info->size = 0;
@@ -672,9 +726,11 @@ int vfs_get_info(const char *path, vfs_dirent_t *info) {
return 0;
}
// /dev virtual directory info
if (vfs_strcmp(normalized, "/dev") == 0) {
vfs_strcpy(info->name, "dev");
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) {
const char *name = normalized + 1;
vfs_strcpy(info->name, name);
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
@@ -683,13 +739,10 @@ int vfs_get_info(const char *path, vfs_dirent_t *info) {
return 0;
}
// Check if it's a prefix of any active mount point (virtual directory)
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) {
if (mounts[i].active && vfs_path_is_parent(normalized, mounts[i].path)) {
if (vfs_strcmp(mounts[i].path, normalized) != 0) {
// Virtual intermediate directory
// Get component name
const char *p = normalized + vfs_strlen(normalized);
while (p > normalized && *(p-1) != '/') p--;
vfs_strcpy(info->name, p);
@@ -712,7 +765,7 @@ int vfs_get_info(const char *path, vfs_dirent_t *info) {
if (d) {
vfs_strcpy(info->name, d->devname);
info->size = d->total_sectors * 512;
info->is_directory = d->is_partition ? 1 : 0;
info->is_directory = 0;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
@@ -738,10 +791,6 @@ int vfs_get_info(const char *path, vfs_dirent_t *info) {
return mount->ops->get_info(mount->fs_private, rel_path, info);
}
// ============================================================================
// Mount Enumeration
// ============================================================================
int vfs_get_mount_count(void) {
return mount_count;
}
@@ -752,12 +801,7 @@ vfs_mount_t* vfs_get_mount(int index) {
return &mounts[index];
}
// ============================================================================
// Auto-Mount (called when a new partition is discovered)
// ============================================================================
void vfs_automount_partition(const char *devname) {
// Build mount point: /mnt/<devname>
char mount_path[64] = "/mnt/";
int i = 5;
const char *d = devname;
@@ -769,8 +813,4 @@ void vfs_automount_partition(const char *devname) {
serial_write(" at ");
serial_write(mount_path);
serial_write("\n");
// The actual FAT32 volume creation and mount happens in disk_manager
// after probing the partition. This function is called by disk_manager
// after it has created the FAT32_Volume.
}

View File

@@ -58,6 +58,8 @@ struct vfs_file {
void *fs_handle; // FS-specific handle (e.g. FAT32_FileHandle*)
vfs_mount_t *mount; // Mount this file belongs to
bool valid;
uint64_t position; // Current Seek Position (for raw devices/fallbacks)
bool is_device; // Is this a raw device handle?
};
// Mount entry
@@ -106,7 +108,7 @@ vfs_mount_t* vfs_get_mount(int index);
void vfs_automount_partition(const char *devname);
// Path utilities
void vfs_normalize_path(const char *path, char *normalized);
void vfs_normalize_path(const char *cwd, const char *path, char *normalized);
// Backward compat: get position/size from vfs_file
uint32_t vfs_file_position(vfs_file_t *file);