From: Tulio A M Mendes Date: Wed, 11 Feb 2026 22:53:00 +0000 (-0300) Subject: feat: symbolic links (symlink, readlink) and link stub X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.js?a=commitdiff_plain;h=1bdce8f3f85fa2efe946dce174ed82b6beb2cf06;p=AdrOS.git feat: symbolic links (symlink, readlink) and link stub - include/fs.h: added FS_SYMLINK type and symlink_target[128] field to fs_node_t - include/stat.h: added S_IFLNK define - include/syscall.h: added SYSCALL_LINK(54), SYSCALL_SYMLINK(55), SYSCALL_READLINK(56) - src/kernel/fs.c: vfs_lookup follows symlinks with depth limit (max 8) - src/kernel/tmpfs.c: tmpfs_create_symlink creates FS_SYMLINK nodes - src/kernel/syscall.c: symlink_impl, readlink_impl, link_impl (stub -ENOSYS) stat_from_node reports S_IFLNK for symlink nodes - cppcheck clean, 19/19 smoke tests pass --- diff --git a/include/fs.h b/include/fs.h index a7fda11..b6f3c9e 100644 --- a/include/fs.h +++ b/include/fs.h @@ -8,6 +8,7 @@ #define FS_DIRECTORY 0x02 #define FS_CHARDEVICE 0x03 #define FS_BLOCKDEVICE 0x04 +#define FS_SYMLINK 0x05 typedef struct fs_node { char name[128]; @@ -17,6 +18,7 @@ typedef struct fs_node { uint32_t uid; uint32_t gid; uint32_t mode; + char symlink_target[128]; // Function pointers for operations (Polymorphism in C) uint32_t (*read)(struct fs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer); diff --git a/include/stat.h b/include/stat.h index 77941b8..0c93881 100644 --- a/include/stat.h +++ b/include/stat.h @@ -7,6 +7,7 @@ #define S_IFREG 0100000 #define S_IFDIR 0040000 #define S_IFCHR 0020000 +#define S_IFLNK 0120000 #define S_IRWXU 00700 #define S_IRUSR 00400 diff --git a/include/syscall.h b/include/syscall.h index 3fbaf68..c7cc012 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -72,6 +72,10 @@ enum { SYSCALL_CHOWN = 51, SYSCALL_GETUID = 52, SYSCALL_GETGID = 53, + + SYSCALL_LINK = 54, + SYSCALL_SYMLINK = 55, + SYSCALL_READLINK = 56, }; #endif diff --git a/include/tmpfs.h b/include/tmpfs.h index cc56087..a88b365 100644 --- a/include/tmpfs.h +++ b/include/tmpfs.h @@ -9,5 +9,6 @@ int tmpfs_add_file(fs_node_t* root_dir, const char* name, const uint8_t* data, u int tmpfs_mkdir_p(fs_node_t* root_dir, const char* path); fs_node_t* tmpfs_create_file(fs_node_t* root_dir, const char* path, const uint8_t* data, uint32_t len); +int tmpfs_create_symlink(fs_node_t* root_dir, const char* link_path, const char* target); #endif diff --git a/src/kernel/fs.c b/src/kernel/fs.c index bd4ed98..b7ec4a9 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -91,8 +91,15 @@ void vfs_close(fs_node_t* node) { node->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; @@ -137,6 +144,11 @@ fs_node_t* vfs_lookup(const char* path) { if (!cur || !cur->finddir) return NULL; cur = cur->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; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index a22344d..0e0489a 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -11,6 +11,7 @@ #include "tty.h" #include "pty.h" #include "diskfs.h" +#include "tmpfs.h" #include "errno.h" #include "shm.h" @@ -533,6 +534,7 @@ static int stat_from_node(const fs_node_t* node, struct stat* st) { uint32_t mode = node->mode & 07777; if (node->flags == FS_DIRECTORY) mode |= S_IFDIR; else if (node->flags == FS_CHARDEVICE) mode |= S_IFCHR; + else if (node->flags == FS_SYMLINK) mode |= S_IFLNK; else mode |= S_IFREG; if ((mode & 07777) == 0) mode |= 0755; st->st_mode = mode; @@ -1609,6 +1611,85 @@ static uintptr_t syscall_brk_impl(uintptr_t addr) { return addr; } +static int syscall_symlink_impl(const char* user_target, const char* user_linkpath) { + if (!user_target || !user_linkpath) return -EFAULT; + + char target[128], linkpath[128]; + if (copy_from_user(target, user_target, sizeof(target)) < 0) return -EFAULT; + target[sizeof(target) - 1] = 0; + + int prc = path_resolve_user(user_linkpath, linkpath, sizeof(linkpath)); + if (prc < 0) return prc; + + /* Find parent directory */ + char parent[128]; + char leaf[128]; + strcpy(parent, linkpath); + char* last_slash = NULL; + for (char* p = parent; *p; p++) { + if (*p == '/') last_slash = p; + } + if (!last_slash) return -EINVAL; + if (last_slash == parent) { + parent[1] = 0; + strcpy(leaf, linkpath + 1); + } else { + *last_slash = 0; + strcpy(leaf, last_slash + 1); + } + if (leaf[0] == 0) return -EINVAL; + + fs_node_t* dir = vfs_lookup(parent); + if (!dir || dir->flags != FS_DIRECTORY) return -ENOENT; + + return tmpfs_create_symlink(dir, leaf, target); +} + +static int syscall_readlink_impl(const char* user_path, char* user_buf, uint32_t bufsz) { + if (!user_path || !user_buf) return -EFAULT; + if (bufsz == 0) return -EINVAL; + if (user_range_ok(user_buf, (size_t)bufsz) == 0) return -EFAULT; + + char path[128]; + int prc = path_resolve_user(user_path, path, sizeof(path)); + if (prc < 0) return prc; + + /* readlink must NOT follow the final symlink — look up parent + finddir */ + char parent[128]; + char leaf[128]; + strcpy(parent, path); + char* last_slash = NULL; + for (char* p = parent; *p; p++) { + if (*p == '/') last_slash = p; + } + if (!last_slash) return -EINVAL; + if (last_slash == parent) { + parent[1] = 0; + strcpy(leaf, path + 1); + } else { + *last_slash = 0; + strcpy(leaf, last_slash + 1); + } + + fs_node_t* dir = vfs_lookup(parent); + if (!dir || !dir->finddir) return -ENOENT; + + fs_node_t* node = dir->finddir(dir, leaf); + if (!node) return -ENOENT; + if (node->flags != FS_SYMLINK) return -EINVAL; + + uint32_t len = (uint32_t)strlen(node->symlink_target); + if (len > bufsz) len = bufsz; + if (copy_to_user(user_buf, node->symlink_target, len) < 0) return -EFAULT; + return (int)len; +} + +static int syscall_link_impl(const char* user_oldpath, const char* user_newpath) { + (void)user_oldpath; + (void)user_newpath; + return -ENOSYS; +} + static int syscall_chmod_impl(const char* user_path, uint32_t mode) { if (!user_path) return -EFAULT; @@ -2020,6 +2101,28 @@ void syscall_handler(struct registers* regs) { return; } + if (syscall_no == SYSCALL_LINK) { + const char* oldpath = (const char*)regs->ebx; + const char* newpath = (const char*)regs->ecx; + regs->eax = (uint32_t)syscall_link_impl(oldpath, newpath); + return; + } + + if (syscall_no == SYSCALL_SYMLINK) { + const char* target = (const char*)regs->ebx; + const char* linkpath = (const char*)regs->ecx; + regs->eax = (uint32_t)syscall_symlink_impl(target, linkpath); + return; + } + + if (syscall_no == SYSCALL_READLINK) { + const char* path = (const char*)regs->ebx; + char* buf = (char*)regs->ecx; + uint32_t bufsz = regs->edx; + regs->eax = (uint32_t)syscall_readlink_impl(path, buf, bufsz); + return; + } + if (syscall_no == SYSCALL_CHMOD) { const char* path = (const char*)regs->ebx; uint32_t mode = regs->ecx; diff --git a/src/kernel/tmpfs.c b/src/kernel/tmpfs.c index b3b3fba..a1fd840 100644 --- a/src/kernel/tmpfs.c +++ b/src/kernel/tmpfs.c @@ -313,3 +313,40 @@ fs_node_t* tmpfs_create_file(fs_node_t* root_dir, const char* path, const uint8_ tmpfs_child_add(cur, f); return &f->vfs; } + +int tmpfs_create_symlink(fs_node_t* root_dir, const char* link_path, const char* target) { + if (!root_dir || root_dir->flags != FS_DIRECTORY) return -ENOTDIR; + if (!link_path || !target) return -EINVAL; + + struct tmpfs_node* cur = (struct tmpfs_node*)root_dir; + const char* p = link_path; + char part[128]; + char leaf[128]; + leaf[0] = 0; + + while (tmpfs_split_next(&p, part, sizeof(part))) { + if (*p == 0) { + strcpy(leaf, part); + break; + } + struct tmpfs_node* next = tmpfs_child_ensure_dir(cur, part); + if (!next) return -ENOMEM; + cur = next; + } + + if (leaf[0] == 0) return -EINVAL; + if (tmpfs_child_find(cur, leaf)) return -EEXIST; + + struct tmpfs_node* ln = tmpfs_node_alloc(leaf, FS_SYMLINK); + if (!ln) return -ENOMEM; + + strcpy(ln->vfs.symlink_target, target); + ln->vfs.length = (uint32_t)strlen(target); + ln->vfs.read = 0; + ln->vfs.write = 0; + ln->vfs.finddir = 0; + ln->vfs.readdir = 0; + + tmpfs_child_add(cur, ln); + return 0; +}