From 3e56ae9fe13a62e56edea8ba9265208818899b1b Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 14:35:25 +0900 Subject: [PATCH] lib/os: add symlink wrapper (ref/hare/os/os.ha:106) --- lib/os/os.ww | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/os/os.ww b/lib/os/os.ww index 95dd9fe4..4bdcf383 100644 --- a/lib/os/os.ww +++ b/lib/os/os.ww @@ -46,6 +46,7 @@ type nr = enum i64 { MKDIR = 83, RMDIR = 84, UNLINK = 87, + SYMLINK = 88, SETPGID = 109, GETDENTS64 = 217, NEWFSTATAT = 262, @@ -362,6 +363,23 @@ export fn rename(oldpath: str, newpath: str) i32 = { return syscall2(nr.RENAME, p: i64, (&pathbuf2[0]): i64): i32; }; +// symlink — symlink(2). Mirrors Hare's os::symlink +// (ref/hare/os/os.ha:106, `symlink(target, path)`), but returns the +// raw i32 errno like sibling remove/mkdir/rename rather than Hare's +// (void | fs::error): ww's os is the flat syscall floor. `target` is +// stored verbatim (it may be relative and is not resolved); `path` +// lands in the second [[pathbuf2]] slot since kpath's single +// [[pathbuf]] can't hold both paths at once. +export fn symlink(target: str, path: str) i32 = { + let p: *u8 = kpath(target); + if (p == nil: *u8) { return -36i32; }; + if (path.len + 1 >= PATH_MAX) { return -36i32; }; + let i: i32 = 0; + for (i < path.len) { pathbuf2[i] = path[i]; i += 1; }; + pathbuf2[path.len] = 0u8; + return syscall2(nr.SYMLINK, p: i64, (&pathbuf2[0]): i64): i32; +}; + // mkdirs — recursive mkdir. Creates `path` and any non-existent // parent directories with the given mode. EEXIST is silently // accepted (matches Hare's `errors::exists` skip in os::mkdirs);