int (*readdir)(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len);
int (*ioctl)(struct fs_node* node, uint32_t cmd, void* arg);
uintptr_t (*mmap)(struct fs_node* node, uintptr_t addr, uint32_t length, uint32_t prot, uint32_t offset);
+
+ // Directory mutation operations (called on the parent directory node)
+ int (*create)(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out);
+ int (*mkdir)(struct fs_node* dir, const char* name);
+ int (*unlink)(struct fs_node* dir, const char* name);
+ int (*rmdir)(struct fs_node* dir, const char* name);
+ int (*rename)(struct fs_node* old_dir, const char* old_name,
+ struct fs_node* new_dir, const char* new_name);
+ int (*truncate)(struct fs_node* node, uint32_t length);
} fs_node_t;
struct vfs_dirent {
fs_node_t* vfs_lookup(const char* path);
+// Resolve path to (parent_dir, basename). Returns parent node or NULL.
+fs_node_t* vfs_lookup_parent(const char* path, char* name_out, size_t name_sz);
+
+// Directory mutation wrappers — route through mount points transparently
+int vfs_create(const char* path, uint32_t flags, fs_node_t** out);
+int vfs_mkdir(const char* path);
+int vfs_unlink(const char* path);
+int vfs_rmdir(const char* path);
+int vfs_rename(const char* old_path, const char* new_path);
+int vfs_truncate(const char* path, uint32_t length);
+
int vfs_mount(const char* mountpoint, fs_node_t* root);
// Global root of the filesystem
return (int)(nents * (uint32_t)sizeof(struct vfs_dirent));
}
+static void diskfs_set_dir_ops(fs_node_t* vfs);
+static int diskfs_vfs_truncate(struct fs_node* node, uint32_t length);
+
static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* name) {
struct diskfs_node* parent = (struct diskfs_node*)node;
if (!g_ready) return 0;
dn->vfs.write = 0;
dn->vfs.finddir = &diskfs_root_finddir;
dn->vfs.readdir = &diskfs_readdir_impl;
+ diskfs_set_dir_ops(&dn->vfs);
} else {
dn->vfs.flags = FS_FILE;
dn->vfs.length = sb.inodes[cino].size_bytes;
dn->vfs.read = &diskfs_read_impl;
dn->vfs.write = &diskfs_write_impl;
+ dn->vfs.truncate = &diskfs_vfs_truncate;
dn->vfs.finddir = 0;
}
return (int)(written * (uint32_t)sizeof(struct diskfs_kdirent));
}
+/* ---- VFS callback wrappers ---- */
+
+static int diskfs_vfs_create(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out) {
+ if (!dir || !name || !out) return -EINVAL;
+ *out = 0;
+ if (!g_ready) return -ENODEV;
+
+ struct diskfs_node* parent = (struct diskfs_node*)dir;
+ uint16_t parent_ino = parent->ino;
+
+ struct diskfs_super sb;
+ if (diskfs_super_load(&sb) < 0) return -EIO;
+ if (parent_ino >= DISKFS_MAX_INODES) return -EIO;
+ if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) return -ENOTDIR;
+
+ /* Check if it already exists */
+ int existing = diskfs_find_child(&sb, parent_ino, name);
+ if (existing >= 0) {
+ uint16_t ino = (uint16_t)existing;
+ if (sb.inodes[ino].type != DISKFS_INODE_FILE) return -EISDIR;
+
+ if ((flags & 0x200U) != 0U) { /* O_TRUNC */
+ sb.inodes[ino].size_bytes = 0;
+ if (diskfs_super_store(&sb) < 0) return -EIO;
+ }
+
+ struct diskfs_node* dn = (struct diskfs_node*)kmalloc(sizeof(*dn));
+ if (!dn) return -ENOMEM;
+ memset(dn, 0, sizeof(*dn));
+ diskfs_strlcpy(dn->vfs.name, name, sizeof(dn->vfs.name));
+ dn->vfs.flags = FS_FILE;
+ dn->vfs.inode = 100 + (uint32_t)ino;
+ dn->vfs.length = sb.inodes[ino].size_bytes;
+ dn->vfs.read = &diskfs_read_impl;
+ dn->vfs.write = &diskfs_write_impl;
+ dn->vfs.close = &diskfs_close_impl;
+ dn->ino = ino;
+ *out = &dn->vfs;
+ return 0;
+ }
+
+ /* Create new file */
+ if ((flags & 0x40U) == 0U) return -ENOENT; /* O_CREAT not set */
+
+ uint16_t new_ino = 0;
+ int rc = diskfs_alloc_inode_file(&sb, parent_ino, name, DISKFS_DEFAULT_CAP_SECTORS, &new_ino);
+ if (rc < 0) return rc;
+ if (diskfs_super_store(&sb) < 0) return -EIO;
+
+ struct diskfs_node* dn = (struct diskfs_node*)kmalloc(sizeof(*dn));
+ if (!dn) return -ENOMEM;
+ memset(dn, 0, sizeof(*dn));
+ diskfs_strlcpy(dn->vfs.name, name, sizeof(dn->vfs.name));
+ dn->vfs.flags = FS_FILE;
+ dn->vfs.inode = 100 + (uint32_t)new_ino;
+ dn->vfs.length = 0;
+ dn->vfs.read = &diskfs_read_impl;
+ dn->vfs.write = &diskfs_write_impl;
+ dn->vfs.close = &diskfs_close_impl;
+ dn->ino = new_ino;
+ *out = &dn->vfs;
+ return 0;
+}
+
+static int diskfs_vfs_mkdir(struct fs_node* dir, const char* name) {
+ if (!dir || !name || name[0] == 0) return -EINVAL;
+ if (!g_ready) return -ENODEV;
+
+ struct diskfs_node* parent = (struct diskfs_node*)dir;
+ uint16_t parent_ino = parent->ino;
+
+ struct diskfs_super sb;
+ if (diskfs_super_load(&sb) < 0) return -EIO;
+ if (parent_ino >= DISKFS_MAX_INODES) return -EIO;
+ if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) return -ENOTDIR;
+
+ if (diskfs_find_child(&sb, parent_ino, name) >= 0) return -EEXIST;
+
+ for (uint16_t i = 1; i < DISKFS_MAX_INODES; i++) {
+ if (sb.inodes[i].type != DISKFS_INODE_FREE) continue;
+ sb.inodes[i].type = DISKFS_INODE_DIR;
+ sb.inodes[i].parent = parent_ino;
+ memset(sb.inodes[i].name, 0, sizeof(sb.inodes[i].name));
+ diskfs_strlcpy(sb.inodes[i].name, name, sizeof(sb.inodes[i].name));
+ sb.inodes[i].start_lba = 0;
+ sb.inodes[i].size_bytes = 0;
+ sb.inodes[i].cap_sectors = 0;
+ return diskfs_super_store(&sb);
+ }
+
+ return -ENOSPC;
+}
+
+static int diskfs_vfs_unlink(struct fs_node* dir, const char* name) {
+ if (!dir || !name || name[0] == 0) return -EINVAL;
+ if (!g_ready) return -ENODEV;
+
+ struct diskfs_node* parent = (struct diskfs_node*)dir;
+ uint16_t parent_ino = parent->ino;
+
+ struct diskfs_super sb;
+ if (diskfs_super_load(&sb) < 0) return -EIO;
+
+ int child = diskfs_find_child(&sb, parent_ino, name);
+ if (child < 0) return -ENOENT;
+ uint16_t ino = (uint16_t)child;
+ if (ino == 0) return -EPERM;
+
+ if (sb.inodes[ino].type == DISKFS_INODE_DIR) return -EISDIR;
+ if (sb.inodes[ino].type != DISKFS_INODE_FILE) return -ENOENT;
+
+ memset(&sb.inodes[ino], 0, sizeof(sb.inodes[ino]));
+ return diskfs_super_store(&sb);
+}
+
+static int diskfs_vfs_rmdir(struct fs_node* dir, const char* name) {
+ if (!dir || !name || name[0] == 0) return -EINVAL;
+ if (!g_ready) return -ENODEV;
+
+ struct diskfs_node* parent = (struct diskfs_node*)dir;
+ uint16_t parent_ino = parent->ino;
+
+ struct diskfs_super sb;
+ if (diskfs_super_load(&sb) < 0) return -EIO;
+
+ int child = diskfs_find_child(&sb, parent_ino, name);
+ if (child < 0) return -ENOENT;
+ uint16_t ino = (uint16_t)child;
+ if (ino == 0) return -EPERM;
+ if (sb.inodes[ino].type != DISKFS_INODE_DIR) return -ENOTDIR;
+
+ /* Check directory is empty */
+ for (uint16_t i = 0; i < DISKFS_MAX_INODES; i++) {
+ if (sb.inodes[i].type == DISKFS_INODE_FREE) continue;
+ if (sb.inodes[i].parent == ino && i != ino) return -ENOTEMPTY;
+ }
+
+ memset(&sb.inodes[ino], 0, sizeof(sb.inodes[ino]));
+ return diskfs_super_store(&sb);
+}
+
+static int diskfs_vfs_rename(struct fs_node* old_dir, const char* old_name,
+ struct fs_node* new_dir, const char* new_name) {
+ if (!old_dir || !old_name || !new_dir || !new_name) return -EINVAL;
+ if (!g_ready) return -ENODEV;
+
+ struct diskfs_node* odir = (struct diskfs_node*)old_dir;
+ struct diskfs_node* ndir = (struct diskfs_node*)new_dir;
+
+ struct diskfs_super sb;
+ if (diskfs_super_load(&sb) < 0) return -EIO;
+
+ int src = diskfs_find_child(&sb, odir->ino, old_name);
+ if (src < 0) return -ENOENT;
+ uint16_t src_ino = (uint16_t)src;
+ if (src_ino == 0) return -EPERM;
+
+ /* Check if destination exists */
+ int dst = diskfs_find_child(&sb, ndir->ino, new_name);
+ if (dst >= 0) {
+ uint16_t dst_ino = (uint16_t)dst;
+ if (sb.inodes[dst_ino].type != sb.inodes[src_ino].type) return -EINVAL;
+ if (dst_ino == src_ino) return 0;
+ memset(&sb.inodes[dst_ino], 0, sizeof(sb.inodes[dst_ino]));
+ }
+
+ sb.inodes[src_ino].parent = ndir->ino;
+ memset(sb.inodes[src_ino].name, 0, sizeof(sb.inodes[src_ino].name));
+ diskfs_strlcpy(sb.inodes[src_ino].name, new_name, sizeof(sb.inodes[src_ino].name));
+
+ return diskfs_super_store(&sb);
+}
+
+static int diskfs_vfs_truncate(struct fs_node* node, uint32_t length) {
+ if (!node) return -EINVAL;
+ if (!g_ready) return -ENODEV;
+
+ struct diskfs_node* dn = (struct diskfs_node*)node;
+ struct diskfs_super sb;
+ if (diskfs_super_load(&sb) < 0) return -EIO;
+ if (dn->ino >= DISKFS_MAX_INODES) return -EIO;
+ if (sb.inodes[dn->ino].type != DISKFS_INODE_FILE) return -EISDIR;
+
+ if (length < sb.inodes[dn->ino].size_bytes) {
+ sb.inodes[dn->ino].size_bytes = length;
+ }
+ node->length = sb.inodes[dn->ino].size_bytes;
+ return diskfs_super_store(&sb);
+}
+
+static void diskfs_set_dir_ops(fs_node_t* vfs) {
+ if (!vfs) return;
+ vfs->create = &diskfs_vfs_create;
+ vfs->mkdir = &diskfs_vfs_mkdir;
+ vfs->unlink = &diskfs_vfs_unlink;
+ vfs->rmdir = &diskfs_vfs_rmdir;
+ vfs->rename = &diskfs_vfs_rename;
+}
+
fs_node_t* diskfs_create_root(void) {
if (!g_ready) {
if (ata_pio_init_primary_master() == 0) {
g_root.vfs.close = 0;
g_root.vfs.finddir = &diskfs_root_finddir;
g_root.vfs.readdir = &diskfs_readdir_impl;
+ diskfs_set_dir_ops(&g_root.vfs);
g_root.ino = 0;
if (g_ready) {
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->create) return -ENOSYS;
+ return parent->create(parent, name, flags, out);
+}
+
+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->mkdir) return -ENOSYS;
+ return parent->mkdir(parent, name);
+}
+
+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->unlink) return -ENOSYS;
+ return parent->unlink(parent, name);
+}
+
+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->rmdir) return -ENOSYS;
+ return parent->rmdir(parent, name);
+}
+
+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->rename) return -ENOSYS;
+ return old_parent->rename(old_parent, old_name, new_parent, new_name);
+}
+
+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->truncate) return -ENOSYS;
+ return node->truncate(node, length);
+}
#include "heap.h"
#include "tty.h"
#include "pty.h"
-#include "diskfs.h"
#include "tmpfs.h"
#include "errno.h"
int prc = path_resolve_user(user_path, path, sizeof(path));
if (prc < 0) return prc;
- fs_node_t* node = NULL;
- if (path[0] == '/' && path[1] == 'd' && path[2] == 'i' && path[3] == 's' && path[4] == 'k' && path[5] == '/') {
- // With hierarchical diskfs, /disk may contain directories.
- // Use diskfs_open_file only when creation/truncation is requested.
- if ((flags & 0x40U) != 0U || (flags & 0x200U) != 0U) {
- const char* rel = path + 6;
- if (rel[0] == 0) return -ENOENT;
- int rc = diskfs_open_file(rel, flags, &node);
- if (rc < 0) return rc;
- } else {
- node = vfs_lookup(path);
- if (!node) return -ENOENT;
+ fs_node_t* node = vfs_lookup(path);
+ if (!node && (flags & 0x40U) != 0U) {
+ /* O_CREAT: create file through VFS */
+ int rc = vfs_create(path, flags, &node);
+ if (rc < 0) return rc;
+ } else if (!node) {
+ return -ENOENT;
+ } else if ((flags & 0x200U) != 0U && node->flags == FS_FILE) {
+ /* O_TRUNC on existing file */
+ if (node->truncate) {
+ node->truncate(node, 0);
+ node->length = 0;
}
- } else {
- node = vfs_lookup(path);
- if (!node) return -ENOENT;
}
/* Permission check based on open flags */
int prc = path_resolve_user(user_path, path, sizeof(path));
if (prc < 0) return prc;
- if (path[0] == '/' && path[1] == 'd' && path[2] == 'i' && path[3] == 's' && path[4] == 'k' && path[5] == '/') {
- const char* rel = path + 6;
- if (rel[0] == 0) return -EINVAL;
- return diskfs_mkdir(rel);
- }
-
- return -ENOSYS;
+ return vfs_mkdir(path);
}
static int syscall_getdents_impl(int fd, void* user_buf, uint32_t len) {
int prc = path_resolve_user(user_path, path, sizeof(path));
if (prc < 0) return prc;
- if (path[0] == '/' && path[1] == 'd' && path[2] == 'i' && path[3] == 's' && path[4] == 'k' && path[5] == '/') {
- const char* rel = path + 6;
- if (rel[0] == 0) return -EINVAL;
- return diskfs_unlink(rel);
- }
-
- return -ENOSYS;
+ return vfs_unlink(path);
}
static int syscall_unlinkat_impl(int dirfd, const char* user_path, uint32_t flags) {
int prc = path_resolve_user(user_path, path, sizeof(path));
if (prc < 0) return prc;
- if (path[0] == '/' && path[1] == 'd' && path[2] == 'i' && path[3] == 's' && path[4] == 'k' && path[5] == '/') {
- const char* rel = path + 6;
- if (rel[0] == 0) return -EINVAL;
- return diskfs_rmdir(rel);
- }
-
- return -ENOSYS;
+ return vfs_rmdir(path);
}
static int syscall_rename_impl(const char* user_old, const char* user_new) {
rc = path_resolve_user(user_new, newp, sizeof(newp));
if (rc < 0) return rc;
- // Both must be under /disk/
- if (oldp[0] == '/' && oldp[1] == 'd' && oldp[2] == 'i' && oldp[3] == 's' && oldp[4] == 'k' && oldp[5] == '/' &&
- newp[0] == '/' && newp[1] == 'd' && newp[2] == 'i' && newp[3] == 's' && newp[4] == 'k' && newp[5] == '/') {
- const char* old_rel = oldp + 6;
- const char* new_rel = newp + 6;
- if (old_rel[0] == 0 || new_rel[0] == 0) return -EINVAL;
- return diskfs_rename(old_rel, new_rel);
- }
-
- return -ENOSYS;
+ return vfs_rename(oldp, newp);
}
static int syscall_read_impl(int fd, void* user_buf, uint32_t len) {