mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
FEAT: VFS overhaul
This commit is contained in:
167
src/fs/fat32.c
167
src/fs/fat32.c
@@ -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
290
src/fs/procfs.c
Normal 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
8
src/fs/procfs.h
Normal 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
181
src/fs/sysfs.c
Normal 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
8
src/fs/sysfs.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef SYSFS_H
|
||||
#define SYSFS_H
|
||||
|
||||
#include "vfs.h"
|
||||
|
||||
vfs_fs_ops_t* sysfs_get_ops(void);
|
||||
|
||||
#endif
|
||||
316
src/fs/vfs.c
316
src/fs/vfs.c
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user