From: Tulio A M Mendes Date: Fri, 13 Feb 2026 00:58:05 +0000 (-0300) Subject: refactor: route link() through VFS callback — remove last diskfs bypass from syscall.c X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.js?a=commitdiff_plain;h=995914dfc4492640b4eb473d044ca2d48a6982d7;p=AdrOS.git refactor: route link() through VFS callback — remove last diskfs bypass from syscall.c - fs.h: add link callback to fs_node_t (dir, name, target_node) - fs.c: implement vfs_link() wrapper — resolves old_path to node, new_path to parent+basename, calls parent->link() - diskfs.c: implement diskfs_vfs_link() using parent ino + target ino, wire into diskfs_set_dir_ops() - syscall.c: syscall_link_impl now calls vfs_link() instead of extern diskfs_link() with /disk/ prefix stripping syscall.c no longer references any diskfs symbol. --- diff --git a/include/fs.h b/include/fs.h index 5f094c7..afb9595 100644 --- a/include/fs.h +++ b/include/fs.h @@ -38,6 +38,7 @@ typedef struct fs_node { 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); } fs_node_t; struct vfs_dirent { @@ -65,6 +66,7 @@ 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_link(const char* old_path, const char* new_path); int vfs_mount(const char* mountpoint, fs_node_t* root); diff --git a/src/kernel/diskfs.c b/src/kernel/diskfs.c index 1921997..4727d9d 100644 --- a/src/kernel/diskfs.c +++ b/src/kernel/diskfs.c @@ -1001,6 +1001,47 @@ static int diskfs_vfs_truncate(struct fs_node* node, uint32_t length) { return diskfs_super_store(&sb); } +static int diskfs_vfs_link(struct fs_node* dir, const char* name, struct fs_node* target) { + if (!dir || !name || name[0] == 0 || !target) return -EINVAL; + if (!g_ready) return -ENODEV; + + struct diskfs_node* parent = (struct diskfs_node*)dir; + struct diskfs_node* src = (struct diskfs_node*)target; + + struct diskfs_super sb; + if (diskfs_super_load(&sb) < 0) return -EIO; + + uint16_t src_ino = src->ino; + if (src_ino >= DISKFS_MAX_INODES) return -EIO; + if (sb.inodes[src_ino].type != DISKFS_INODE_FILE) return -EPERM; + + /* Check new name doesn't already exist */ + if (diskfs_find_child(&sb, parent->ino, name) >= 0) return -EEXIST; + + /* Find a free inode slot */ + uint16_t new_ino = 0; + for (uint16_t i = 1; i < DISKFS_MAX_INODES; i++) { + if (sb.inodes[i].type == DISKFS_INODE_FREE) { new_ino = i; break; } + } + if (new_ino == 0) return -ENOSPC; + + /* Create new inode sharing same data blocks */ + sb.inodes[new_ino].type = DISKFS_INODE_FILE; + sb.inodes[new_ino].nlink = 2; + sb.inodes[new_ino].parent = parent->ino; + memset(sb.inodes[new_ino].name, 0, sizeof(sb.inodes[new_ino].name)); + diskfs_strlcpy(sb.inodes[new_ino].name, name, sizeof(sb.inodes[new_ino].name)); + sb.inodes[new_ino].start_lba = sb.inodes[src_ino].start_lba; + sb.inodes[new_ino].size_bytes = sb.inodes[src_ino].size_bytes; + sb.inodes[new_ino].cap_sectors = sb.inodes[src_ino].cap_sectors; + + /* Update source inode nlink */ + if (sb.inodes[src_ino].nlink < 2) sb.inodes[src_ino].nlink = 2; + else sb.inodes[src_ino].nlink++; + + return diskfs_super_store(&sb); +} + static void diskfs_set_dir_ops(fs_node_t* vfs) { if (!vfs) return; vfs->create = &diskfs_vfs_create; @@ -1008,6 +1049,7 @@ static void diskfs_set_dir_ops(fs_node_t* vfs) { vfs->unlink = &diskfs_vfs_unlink; vfs->rmdir = &diskfs_vfs_rmdir; vfs->rename = &diskfs_vfs_rename; + vfs->link = &diskfs_vfs_link; } fs_node_t* diskfs_create_root(void) { diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 3ab06fb..158b6ea 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -244,3 +244,17 @@ int vfs_truncate(const char* path, uint32_t length) { if (!node->truncate) return -ENOSYS; return node->truncate(node, length); } + +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->link) return -ENOSYS; + return parent->link(parent, name, target); +} diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index c7b6132..d310565 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -1718,12 +1718,7 @@ static int syscall_link_impl(const char* user_oldpath, const char* user_newpath) if (rc1 < 0) return rc1; int rc2 = path_resolve_user(user_newpath, new_path, sizeof(new_path)); if (rc2 < 0) return rc2; - extern int diskfs_link(const char*, const char*); - const char* old_rel = old_path; - const char* new_rel = new_path; - if (memcmp(old_path, "/disk/", 6) == 0) old_rel = old_path + 6; - if (memcmp(new_path, "/disk/", 6) == 0) new_rel = new_path + 6; - return diskfs_link(old_rel, new_rel); + return vfs_link(old_path, new_path); } static int syscall_chmod_impl(const char* user_path, uint32_t mode) {