From: Tulio A M Mendes Date: Fri, 13 Feb 2026 21:05:14 +0000 (-0300) Subject: refactor: VFS file_operations dispatch layer X-Git-Url: https://projects.tadryanom.me/docs/POSIX_ROADMAP.md?a=commitdiff_plain;h=f1ad52f2add18232e7b659cb16dcd0b5d4276037;p=AdrOS.git refactor: VFS file_operations dispatch layer 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. --- diff --git a/include/fs.h b/include/fs.h index ed5b61c..8b6456b 100644 --- a/include/fs.h +++ b/include/fs.h @@ -17,6 +17,32 @@ #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); diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 6b6b701..aa2f15e 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -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); } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index c77fa1d..8a6f4ef 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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 {