#include "fs.h"
#include "utils.h"
#include "errno.h"
fs_node_t* fs_root = NULL;
struct vfs_mount {
char mountpoint[128];
fs_node_t* root;
};
static struct vfs_mount g_mounts[16];
static int g_mount_count = 0;
static int path_is_mountpoint_prefix(const char* mp, const char* path) {
size_t mpl = strlen(mp);
if (mpl == 0) return 0;
if (strcmp(mp, "/") == 0) return 1;
if (strncmp(path, mp, mpl) != 0) return 0;
if (path[mpl] == 0) return 1;
if (path[mpl] == '/') return 1;
return 0;
}
static void normalize_mountpoint(const char* in, char* out, size_t out_sz) {
if (!out || out_sz == 0) return;
out[0] = 0;
if (!in || in[0] == 0) {
strcpy(out, "/");
return;
}
size_t i = 0;
if (in[0] != '/') {
out[i++] = '/';
}
for (size_t j = 0; in[j] != 0 && i + 1 < out_sz; j++) {
out[i++] = in[j];
}
out[i] = 0;
size_t l = strlen(out);
while (l > 1 && out[l - 1] == '/') {
out[l - 1] = 0;
l--;
}
}
int vfs_mount(const char* mountpoint, fs_node_t* root) {
if (!root) return -EINVAL;
if (g_mount_count >= (int)(sizeof(g_mounts) / sizeof(g_mounts[0]))) return -ENOSPC;
char mp[128];
normalize_mountpoint(mountpoint, mp, sizeof(mp));
for (int i = 0; i < g_mount_count; i++) {
if (strcmp(g_mounts[i].mountpoint, mp) == 0) {
g_mounts[i].root = root;
return 0;
}
}
strcpy(g_mounts[g_mount_count].mountpoint, mp);
g_mounts[g_mount_count].root = root;
g_mount_count++;
return 0;
}
int vfs_umount(const char* mountpoint) {
char mp[128];
normalize_mountpoint(mountpoint, mp, sizeof(mp));
if (strcmp(mp, "/") == 0) return -EBUSY;
for (int i = 0; i < g_mount_count; i++) {
if (strcmp(g_mounts[i].mountpoint, mp) == 0) {
for (int j = i; j < g_mount_count - 1; j++)
g_mounts[j] = g_mounts[j + 1];
g_mount_count--;
return 0;
}
}
return -EINVAL;
}
uint32_t vfs_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
if (node->f_ops && node->f_ops->read)
return node->f_ops->read(node, offset, size, buffer);
return 0;
}
uint32_t vfs_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
if (node->f_ops && node->f_ops->write)
return node->f_ops->write(node, offset, size, buffer);
return 0;
}
void vfs_open(fs_node_t* node) {
if (node->f_ops && node->f_ops->open)
node->f_ops->open(node);
}
void vfs_close(fs_node_t* node) {
if (node->f_ops && node->f_ops->close)
node->f_ops->close(node);
}
static fs_node_t* vfs_lookup_depth(const char* path, int depth);
fs_node_t* vfs_lookup(const char* path) {
return vfs_lookup_depth(path, 0);
}
static fs_node_t* vfs_lookup_depth(const char* path, int depth) {
if (!path || !fs_root) return NULL;
if (depth > 8) return NULL;
fs_node_t* base = fs_root;
const char* rel = path;
size_t best_len = 0;
for (int i = 0; i < g_mount_count; i++) {
const char* mp = g_mounts[i].mountpoint;
if (!mp[0] || !g_mounts[i].root) continue;
if (path_is_mountpoint_prefix(mp, path)) {
size_t mpl = strlen(mp);
if (mpl >= best_len) {
best_len = mpl;
base = g_mounts[i].root;
rel = path + mpl;
}
}
}
if (!rel) return NULL;
while (*rel == '/') rel++;
if (*rel == 0) return base;
const char* p = rel;
fs_node_t* cur = base;
char part[128];
while (*p != 0) {
size_t i = 0;
while (*p != 0 && *p != '/') {
if (i + 1 < sizeof(part)) {
part[i++] = *p;
}
p++;
}
part[i] = 0;
while (*p == '/') p++;
if (part[0] == 0) continue;
if (!cur) return NULL;
fs_node_t* (*fn_finddir)(fs_node_t*, const char*) = NULL;
if (cur->i_ops && cur->i_ops->lookup) fn_finddir = cur->i_ops->lookup;
if (!fn_finddir) return NULL;
cur = fn_finddir(cur, part);
if (!cur) return NULL;
if (cur->flags == FS_SYMLINK && cur->symlink_target[0]) {
cur = vfs_lookup_depth(cur->symlink_target, depth + 1);
if (!cur) return NULL;
}
}
return cur;
}
/* Split path into dirname + basename. Returns the parent directory node. */
fs_node_t* vfs_lookup_parent(const char* path, char* name_out, size_t name_sz) {
if (!path || !name_out || name_sz == 0) return NULL;
name_out[0] = 0;
/* Find last '/' separator */
const char* last_slash = NULL;
for (const char* p = path; *p; p++) {
if (*p == '/') last_slash = p;
}
if (!last_slash) return NULL; /* no slash = relative, not supported */
/* Build parent path */
char parent_path[128];
size_t plen = (size_t)(last_slash - path);
if (plen == 0) plen = 1; /* root "/" */
if (plen >= sizeof(parent_path)) plen = sizeof(parent_path) - 1;
memcpy(parent_path, path, plen);
parent_path[plen] = 0;
/* Extract basename */
const char* base = last_slash + 1;
size_t blen = strlen(base);
if (blen == 0) return NULL; /* trailing slash, no basename */
if (blen >= name_sz) blen = name_sz - 1;
memcpy(name_out, base, blen);
name_out[blen] = 0;
return vfs_lookup(parent_path);
}
int vfs_create(const char* path, uint32_t flags, fs_node_t** out) {
if (!path || !out) return -EINVAL;
char name[128];
fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
if (!parent) return -ENOENT;
if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
if (parent->i_ops && parent->i_ops->create)
return parent->i_ops->create(parent, name, flags, out);
return -ENOSYS;
}
int vfs_mkdir(const char* path) {
if (!path) return -EINVAL;
char name[128];
fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
if (!parent) return -ENOENT;
if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
if (parent->i_ops && parent->i_ops->mkdir)
return parent->i_ops->mkdir(parent, name);
return -ENOSYS;
}
int vfs_unlink(const char* path) {
if (!path) return -EINVAL;
char name[128];
fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
if (!parent) return -ENOENT;
if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
if (parent->i_ops && parent->i_ops->unlink)
return parent->i_ops->unlink(parent, name);
return -ENOSYS;
}
int vfs_rmdir(const char* path) {
if (!path) return -EINVAL;
char name[128];
fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
if (!parent) return -ENOENT;
if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
if (parent->i_ops && parent->i_ops->rmdir)
return parent->i_ops->rmdir(parent, name);
return -ENOSYS;
}
int vfs_rename(const char* old_path, const char* new_path) {
if (!old_path || !new_path) return -EINVAL;
char old_name[128], new_name[128];
fs_node_t* old_parent = vfs_lookup_parent(old_path, old_name, sizeof(old_name));
fs_node_t* new_parent = vfs_lookup_parent(new_path, new_name, sizeof(new_name));
if (!old_parent || !new_parent) return -ENOENT;
if (old_parent->i_ops && old_parent->i_ops->rename)
return old_parent->i_ops->rename(old_parent, old_name, new_parent, new_name);
return -ENOSYS;
}
int vfs_truncate(const char* path, uint32_t length) {
if (!path) return -EINVAL;
fs_node_t* node = vfs_lookup(path);
if (!node) return -ENOENT;
if (node->flags != FS_FILE) return -EISDIR;
if (node->i_ops && node->i_ops->truncate)
return node->i_ops->truncate(node, length);
return -ENOSYS;
}
int vfs_link(const char* old_path, const char* new_path) {
if (!old_path || !new_path) return -EINVAL;
fs_node_t* target = vfs_lookup(old_path);
if (!target) return -ENOENT;
if (target->flags != FS_FILE) return -EPERM;
char name[128];
fs_node_t* parent = vfs_lookup_parent(new_path, name, sizeof(name));
if (!parent) return -ENOENT;
if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
if (parent->i_ops && parent->i_ops->link)
return parent->i_ops->link(parent, name, target);
return -ENOSYS;
}