]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
refactor: VFS file_operations dispatch layer
authorTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 21:05:14 +0000 (18:05 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 21:05:14 +0000 (18:05 -0300)
Add struct file_operations to fs.h with all VFS callback signatures.
Add const struct file_operations* f_ops to fs_node_t.

Update all VFS dispatch points (fs.c wrappers + syscall.c direct
dispatch for poll, readdir, ioctl, mmap) to check f_ops first,
then fall back to legacy per-node function pointers.

This enables incremental migration: filesystems can adopt f_ops
one at a time while legacy pointers continue to work.

20/20 smoke tests pass.

include/fs.h
src/kernel/fs.c
src/kernel/syscall.c

index ed5b61c5249e783057fe8743ba5339e32dd6af71..8b6456b2f3a4c350503797c4a61c7e6670f288b4 100644 (file)
 #define VFS_POLL_ERR   0x0008
 #define VFS_POLL_HUP   0x0010
 
+struct fs_node; /* forward declaration for file_operations */
+
+/* Shared file operations table — filesystems define one static instance
+ * per node type (file, dir, device) and point every node's f_ops at it.
+ * During the migration period, the VFS checks f_ops first, then falls
+ * back to per-node function pointers (legacy). */
+struct file_operations {
+    uint32_t (*read)(struct fs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer);
+    uint32_t (*write)(struct fs_node* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
+    void (*open)(struct fs_node* node);
+    void (*close)(struct fs_node* node);
+    struct fs_node* (*finddir)(struct fs_node* node, const char* name);
+    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);
+    int (*poll)(struct fs_node* node, int events);
+    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);
+    int (*link)(struct fs_node* dir, const char* name, struct fs_node* target);
+};
+
 typedef struct fs_node {
     char name[128];
     uint32_t flags;
@@ -26,8 +52,10 @@ typedef struct fs_node {
     uint32_t gid;
     uint32_t mode;
     char symlink_target[128];
-    
-    // Function pointers for operations (Polymorphism in C)
+
+    const struct file_operations* f_ops;
+
+    // Legacy per-node function pointers (will be removed after migration)
     uint32_t (*read)(struct fs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer);
     uint32_t (*write)(struct fs_node* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
     void (*open)(struct fs_node* node);
@@ -37,8 +65,6 @@ typedef struct fs_node {
     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);
     int (*poll)(struct fs_node* node, int events);
-
-    // 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);
index 6b6b701871ac53014882a472653df783831be500..aa2f15e5a13c899d3d693dff976380088d1fa992 100644 (file)
@@ -70,24 +70,32 @@ int vfs_mount(const char* mountpoint, fs_node_t* root) {
 }
 
 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);
     if (node->read)
         return node->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);
     if (node->write)
         return node->write(node, offset, size, buffer);
     return 0;
 }
 
 void vfs_open(fs_node_t* node) {
-    if (node->open)
+    if (node->f_ops && node->f_ops->open)
+        node->f_ops->open(node);
+    else if (node->open)
         node->open(node);
 }
 
 void vfs_close(fs_node_t* node) {
-    if (node->close)
+    if (node->f_ops && node->f_ops->close)
+        node->f_ops->close(node);
+    else if (node->close)
         node->close(node);
 }
 
@@ -141,8 +149,12 @@ static fs_node_t* vfs_lookup_depth(const char* path, int depth) {
 
         if (part[0] == 0) continue;
 
-        if (!cur || !cur->finddir) return NULL;
-        cur = cur->finddir(cur, part);
+        if (!cur) return NULL;
+        fs_node_t* (*fn_finddir)(fs_node_t*, const char*) = NULL;
+        if (cur->f_ops && cur->f_ops->finddir) fn_finddir = cur->f_ops->finddir;
+        else if (cur->finddir) fn_finddir = cur->finddir;
+        if (!fn_finddir) return NULL;
+        cur = fn_finddir(cur, part);
         if (!cur) return NULL;
 
         if (cur->flags == FS_SYMLINK && cur->symlink_target[0]) {
@@ -192,6 +204,8 @@ int vfs_create(const char* path, uint32_t flags, fs_node_t** out) {
     fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
     if (!parent) return -ENOENT;
     if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
+    if (parent->f_ops && parent->f_ops->create)
+        return parent->f_ops->create(parent, name, flags, out);
     if (!parent->create) return -ENOSYS;
     return parent->create(parent, name, flags, out);
 }
@@ -202,6 +216,8 @@ int vfs_mkdir(const char* path) {
     fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
     if (!parent) return -ENOENT;
     if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
+    if (parent->f_ops && parent->f_ops->mkdir)
+        return parent->f_ops->mkdir(parent, name);
     if (!parent->mkdir) return -ENOSYS;
     return parent->mkdir(parent, name);
 }
@@ -212,6 +228,8 @@ int vfs_unlink(const char* path) {
     fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
     if (!parent) return -ENOENT;
     if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
+    if (parent->f_ops && parent->f_ops->unlink)
+        return parent->f_ops->unlink(parent, name);
     if (!parent->unlink) return -ENOSYS;
     return parent->unlink(parent, name);
 }
@@ -222,6 +240,8 @@ int vfs_rmdir(const char* path) {
     fs_node_t* parent = vfs_lookup_parent(path, name, sizeof(name));
     if (!parent) return -ENOENT;
     if (parent->flags != FS_DIRECTORY) return -ENOTDIR;
+    if (parent->f_ops && parent->f_ops->rmdir)
+        return parent->f_ops->rmdir(parent, name);
     if (!parent->rmdir) return -ENOSYS;
     return parent->rmdir(parent, name);
 }
@@ -232,6 +252,8 @@ int vfs_rename(const char* old_path, const char* new_path) {
     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->f_ops && old_parent->f_ops->rename)
+        return old_parent->f_ops->rename(old_parent, old_name, new_parent, new_name);
     if (!old_parent->rename) return -ENOSYS;
     return old_parent->rename(old_parent, old_name, new_parent, new_name);
 }
@@ -241,6 +263,8 @@ int vfs_truncate(const char* path, uint32_t length) {
     fs_node_t* node = vfs_lookup(path);
     if (!node) return -ENOENT;
     if (node->flags != FS_FILE) return -EISDIR;
+    if (node->f_ops && node->f_ops->truncate)
+        return node->f_ops->truncate(node, length);
     if (!node->truncate) return -ENOSYS;
     return node->truncate(node, length);
 }
@@ -255,6 +279,8 @@ int vfs_link(const char* old_path, const char* new_path) {
     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->f_ops && parent->f_ops->link)
+        return parent->f_ops->link(parent, name, target);
     if (!parent->link) return -ENOSYS;
     return parent->link(parent, name, target);
 }
index c77fa1dc21be3cef2f7804fa88663d0e07bb043d..8a6f4efbf1767e51de2944811ee4802006681656 100644 (file)
@@ -262,12 +262,15 @@ static int poll_wait_kfds(struct pollfd* kfds, uint32_t nfds, int32_t timeout) {
 
             fs_node_t* n = f->node;
 
-            if (n->poll) {
+            int (*fn_poll)(fs_node_t*, int) = NULL;
+            if (n->f_ops && n->f_ops->poll) fn_poll = n->f_ops->poll;
+            else if (n->poll) fn_poll = n->poll;
+            if (fn_poll) {
                 int vfs_events = 0;
                 if (kfds[i].events & POLLIN)  vfs_events |= VFS_POLL_IN;
                 if (kfds[i].events & POLLOUT) vfs_events |= VFS_POLL_OUT;
 
-                int vfs_rev = n->poll(n, vfs_events);
+                int vfs_rev = fn_poll(n, vfs_events);
 
                 if (vfs_rev & VFS_POLL_IN)  kfds[i].revents |= POLLIN;
                 if (vfs_rev & VFS_POLL_OUT) kfds[i].revents |= POLLOUT;
@@ -1137,14 +1140,17 @@ static int syscall_getdents_impl(int fd, void* user_buf, uint32_t len) {
     struct file* f = fd_get(fd);
     if (!f || !f->node) return -EBADF;
     if (f->node->flags != FS_DIRECTORY) return -ENOTDIR;
-    if (!f->node->readdir) return -ENOSYS;
+    int (*fn_readdir)(struct fs_node*, uint32_t*, void*, uint32_t) = NULL;
+    if (f->node->f_ops && f->node->f_ops->readdir) fn_readdir = f->node->f_ops->readdir;
+    else if (f->node->readdir) fn_readdir = f->node->readdir;
+    if (!fn_readdir) return -ENOSYS;
 
     uint8_t kbuf[256];
     uint32_t klen = len;
     if (klen > (uint32_t)sizeof(kbuf)) klen = (uint32_t)sizeof(kbuf);
 
     uint32_t idx = f->offset;
-    int rc = f->node->readdir(f->node, &idx, kbuf, klen);
+    int rc = fn_readdir(f->node, &idx, kbuf, klen);
     if (rc < 0) return rc;
     if (rc == 0) return 0;
 
@@ -1206,10 +1212,15 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) {
     if (!f || !f->node) return -EBADF;
 
     int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0;
-    if (nonblock && f->node->poll) {
-        int rev = f->node->poll(f->node, VFS_POLL_IN);
-        if (!(rev & (VFS_POLL_IN | VFS_POLL_ERR | VFS_POLL_HUP)))
-            return -EAGAIN;
+    {
+        int (*fn_poll)(fs_node_t*, int) = NULL;
+        if (f->node->f_ops && f->node->f_ops->poll) fn_poll = f->node->f_ops->poll;
+        else if (f->node->poll) fn_poll = f->node->poll;
+        if (nonblock && fn_poll) {
+            int rev = fn_poll(f->node, VFS_POLL_IN);
+            if (!(rev & (VFS_POLL_IN | VFS_POLL_ERR | VFS_POLL_HUP)))
+                return -EAGAIN;
+        }
     }
 
     if (f->node->flags == FS_CHARDEVICE) {
@@ -1272,12 +1283,17 @@ static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) {
     if (!f || !f->node) return -EBADF;
 
     int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0;
-    if (nonblock && f->node->poll) {
-        int rev = f->node->poll(f->node, VFS_POLL_OUT);
-        if (!(rev & (VFS_POLL_OUT | VFS_POLL_ERR)))
-            return -EAGAIN;
+    {
+        int (*fn_poll)(fs_node_t*, int) = NULL;
+        if (f->node->f_ops && f->node->f_ops->poll) fn_poll = f->node->f_ops->poll;
+        else if (f->node->poll) fn_poll = f->node->poll;
+        if (nonblock && fn_poll) {
+            int rev = fn_poll(f->node, VFS_POLL_OUT);
+            if (!(rev & (VFS_POLL_OUT | VFS_POLL_ERR)))
+                return -EAGAIN;
+        }
     }
-    if (!f->node->write) return -ESPIPE;
+    if (!(f->node->f_ops && f->node->f_ops->write) && !f->node->write) return -ESPIPE;
     if (f->node->flags != FS_FILE && f->node->flags != FS_CHARDEVICE && f->node->flags != FS_SOCKET) return -ESPIPE;
 
     if ((f->flags & O_APPEND) && (f->node->flags & FS_FILE)) {
@@ -1308,6 +1324,7 @@ static int syscall_ioctl_impl(int fd, uint32_t cmd, void* user_arg) {
     if (!f || !f->node) return -EBADF;
 
     fs_node_t* n = f->node;
+    if (n->f_ops && n->f_ops->ioctl) return n->f_ops->ioctl(n, cmd, user_arg);
     if (n->ioctl) return n->ioctl(n, cmd, user_arg);
     return -ENOTTY;
 }
@@ -1490,7 +1507,7 @@ static uintptr_t syscall_mmap_impl(uintptr_t addr, uint32_t length, uint32_t pro
         if (fd < 0) return (uintptr_t)-EBADF;
         struct file* f = fd_get(fd);
         if (!f || !f->node) return (uintptr_t)-EBADF;
-        if (!f->node->mmap) return (uintptr_t)-ENOSYS;
+        if (!(f->node->f_ops && f->node->f_ops->mmap) && !f->node->mmap) return (uintptr_t)-ENOSYS;
         mmap_node = f->node;
     }
 
@@ -1514,7 +1531,10 @@ static uintptr_t syscall_mmap_impl(uintptr_t addr, uint32_t length, uint32_t pro
 
     if (mmap_node) {
         /* Device-backed mmap: delegate to the node's mmap callback */
-        uintptr_t result = mmap_node->mmap(mmap_node, base, aligned_len, prot, offset);
+        uintptr_t (*fn_mmap)(fs_node_t*, uintptr_t, uint32_t, uint32_t, uint32_t) = NULL;
+        if (mmap_node->f_ops && mmap_node->f_ops->mmap) fn_mmap = mmap_node->f_ops->mmap;
+        else fn_mmap = mmap_node->mmap;
+        uintptr_t result = fn_mmap(mmap_node, base, aligned_len, prot, offset);
         if (!result) return (uintptr_t)-ENOMEM;
         base = result;
     } else {