lib/os+selfhost: *u8→str path migration (#23)
Path-shaped entrypoints now take str: open, tryopen, access, remove, mkdir, rmdir, mkdirs, stat, lstat, exists, execve (path arg only). Each cites its Hare source (ref/hare/os/*.ha, ref/hare/sys/+linux/ *.ha). New internal kpath(str) *u8 copies into module-level pathbuf: [4096]u8 and NUL-terminates; mirrors ref/hare/sys/+linux/syscalls.ha:25,53. Non-reentrant — graduates with thread story. mkdirs flattens to one kpath at entry then walks pathbuf invoking raw SYS_mkdir to avoid nested kpath clobber. One Hare divergence at kpath: ships *u8 with nil ENAMETOOLONG sentinel instead of (*const u8 | errno). Reason: wwstage over-allocates 1-word-payload tagged returns to 24B (cstage emits 16B); filed as follow-up. Repro at .ai/probe_tagged_return_pointer_payload.ww; graduates when fix lands. Each selfhost cmd grew a private pathstr(*u8) str (cstrlen + bs) for remaining *u8 path sites; w6l shares via obj.ww. Probe 7 in smoke updated. Tests 975/976/981 cover migrated entrypoints; 976 extended with two ENAMETOOLONG rows (-36 for stat, false for exists).
This commit is contained in:
@@ -114,7 +114,9 @@ fn view() str = {
|
||||
// behaviour Hare's lookup uses (`fmt::fatalf` on the HOME branch).
|
||||
// Centralised so both branches in [[lookup]] share the error path.
|
||||
fn ensure() void = {
|
||||
match (os.mkdirs(&pathbuf[0], MODE_0755)) {
|
||||
let pv: str;
|
||||
pv.ptr = &pathbuf[0]; pv.len = pathlen;
|
||||
match (os.mkdirs(pv, MODE_0755)) {
|
||||
case void => {};
|
||||
case let _e: os.oserror => rtabort("dirs: mkdirs failed");
|
||||
};
|
||||
|
||||
161
lib/os/os.ww
161
lib/os/os.ww
@@ -87,6 +87,36 @@ export fn exit(code: i32) void = {
|
||||
syscall1(nr.EXIT, code: i64);
|
||||
};
|
||||
|
||||
// PATH_MAX / pathbuf / kpath — port of Hare's ref/hare/sys/+linux/
|
||||
// syscalls.ha:25,27,29-55. Hare's `path` accepts a sum `(str |
|
||||
// []u8 | *const u8)`; ww's lib/os public surface narrows to `str`
|
||||
// (the Hare-faithful surface at ref/hare/os/os.ha:37,47,50 etc).
|
||||
// Internally, [[kpath]] copies the `str` bytes into a single
|
||||
// module-level [[pathbuf]] scratch slot and NUL-terminates so the
|
||||
// raw Linux syscalls (which require C strings) see a valid
|
||||
// terminator. Same precedent as Hare's static `pathbuf`.
|
||||
//
|
||||
// Non-reentrant: one buffer, every [[stat]] / [[open]] / etc.
|
||||
// rewrites it. Same caveat as strconv's `*tos` family (overwritten
|
||||
// on next call). Caller must NOT hold a kpath-returned pointer
|
||||
// across another lib/os path call. Graduates when ww grows a
|
||||
// thread story.
|
||||
//
|
||||
// `nil`-as-overflow over `(*u8 | oserror)`: wwstage over-allocates
|
||||
// 1-word-payload tagged returns to 24B (cstage emits 16B).
|
||||
// Task #9; revert at task #10 when fixed. Repro at
|
||||
// .ai/probe_tagged_return_pointer_payload.ww.
|
||||
export def PATH_MAX: i32 = 4096;
|
||||
let pathbuf: [4096]u8;
|
||||
|
||||
fn kpath(p: str) *u8 = {
|
||||
if (p.len + 1 >= PATH_MAX) { return nil: *u8; }; // ENAMETOOLONG
|
||||
let i: i32 = 0;
|
||||
for (i < p.len) { pathbuf[i] = p[i]; i += 1; };
|
||||
pathbuf[p.len] = 0u8;
|
||||
return &pathbuf[0];
|
||||
};
|
||||
|
||||
// Raw, non-fallible primitives. These return Linux's int conventions
|
||||
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
|
||||
// Hare-style fallible API use the wrappers below.
|
||||
@@ -125,15 +155,17 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
|
||||
return r;
|
||||
};
|
||||
|
||||
// open — Linux open(2). Path must be NUL-terminated; callers using ww
|
||||
// `str` must ensure the bytes are followed by a 0 byte (literals are,
|
||||
// arena-copied paths usually are by construction). Returns -errno on
|
||||
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
|
||||
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
|
||||
return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
|
||||
// open — Linux open(2). Returns -errno on failure, fd otherwise.
|
||||
// Higher-level callers prefer `tryopen`. Mirrors Hare's os::open
|
||||
// (ref/hare/os/os.ha:117); kpath lands the bytes in pathbuf.
|
||||
// Returns -ENAMETOOLONG (-36) if the path overflows PATH_MAX.
|
||||
export fn open(path: str, flags: flag, mode: i32) i32 = {
|
||||
let p: *u8 = kpath(path);
|
||||
if (p == nil: *u8) { return -36i32; }; // ENAMETOOLONG
|
||||
return syscall3(nr.OPEN, p: i64, (flags as i32): i64, mode: i64): i32;
|
||||
};
|
||||
|
||||
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
|
||||
export fn tryopen(path: str, flags: flag, mode: i32) (i32 | oserror) = {
|
||||
let fd: i32 = open(path, flags, mode);
|
||||
if (fd < 0) { return fd: i64: oserror; };
|
||||
return fd;
|
||||
@@ -193,26 +225,37 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
|
||||
|
||||
// access(2): returns 0 if the file is reachable, negative errno
|
||||
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
|
||||
export fn access(path: *u8, mode: i32) i32 = {
|
||||
return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
|
||||
// Mirrors Hare's os::access (ref/hare/os/+linux/fs.ha:access).
|
||||
// Returns -ENAMETOOLONG (-36) if the path overflows PATH_MAX.
|
||||
export fn access(path: str, mode: i32) i32 = {
|
||||
let p: *u8 = kpath(path);
|
||||
if (p == nil: *u8) { return -36i32; };
|
||||
return syscall2(nr.ACCESS, p: i64, mode: i64): i32;
|
||||
};
|
||||
|
||||
// remove — unlink(2). Hare name; the underlying syscall is unlink(2).
|
||||
export fn remove(path: *u8) i32 = {
|
||||
return syscall1(nr.UNLINK, path: i64): i32;
|
||||
// remove — unlink(2). Mirrors Hare's os::remove
|
||||
// (ref/hare/os/os.ha:12).
|
||||
export fn remove(path: str) i32 = {
|
||||
let p: *u8 = kpath(path);
|
||||
if (p == nil: *u8) { return -36i32; };
|
||||
return syscall1(nr.UNLINK, p: i64): i32;
|
||||
};
|
||||
|
||||
// mkdir — mkdir(2). Path must be NUL-terminated. Mode is the unix
|
||||
// permission bitset (e.g. 0o700). Returns 0 on success, negative
|
||||
// errno otherwise. Hare name (os::mkdir).
|
||||
export fn mkdir(path: *u8, mode: i32) i32 = {
|
||||
return syscall2(nr.MKDIR, path: i64, mode: i64): i32;
|
||||
// mkdir — mkdir(2). Mode is the unix permission bitset (e.g. 0o700).
|
||||
// Returns 0 on success, negative errno otherwise. Mirrors Hare's
|
||||
// os::mkdir (ref/hare/os/os.ha:50).
|
||||
export fn mkdir(path: str, mode: i32) i32 = {
|
||||
let p: *u8 = kpath(path);
|
||||
if (p == nil: *u8) { return -36i32; };
|
||||
return syscall2(nr.MKDIR, p: i64, mode: i64): i32;
|
||||
};
|
||||
|
||||
// rmdir — rmdir(2). Path must be NUL-terminated. Returns 0 on
|
||||
// success, negative errno otherwise. Hare name (os::rmdir).
|
||||
export fn rmdir(path: *u8) i32 = {
|
||||
return syscall1(nr.RMDIR, path: i64): i32;
|
||||
// rmdir — rmdir(2). Mirrors Hare's os::rmdir
|
||||
// (ref/hare/os/os.ha:58).
|
||||
export fn rmdir(path: str) i32 = {
|
||||
let p: *u8 = kpath(path);
|
||||
if (p == nil: *u8) { return -36i32; };
|
||||
return syscall1(nr.RMDIR, p: i64): i32;
|
||||
};
|
||||
|
||||
// mkdirs — recursive mkdir. Creates `path` and any non-existent
|
||||
@@ -220,29 +263,28 @@ export fn rmdir(path: *u8) i32 = {
|
||||
// accepted (matches Hare's `errors::exists` skip in os::mkdirs);
|
||||
// any other syscall failure surfaces as `oserror`.
|
||||
//
|
||||
// `path` must be NUL-terminated AND its bytes must be writable —
|
||||
// mkdirs temporarily replaces '/' separators with NUL while
|
||||
// invoking [[mkdir]] on each prefix, then restores them. Pointing
|
||||
// `path` at a string literal will segfault. Callers hold the bytes
|
||||
// in a writable buffer (rt_alloc'd, a static `[N]u8`, etc.) — same
|
||||
// precedent as [[temp.named]]'s pathbuf.
|
||||
//
|
||||
// Mirrors Hare's os::mkdirs (recursive variant of os::mkdir).
|
||||
export fn mkdirs(path: *u8, mode: i32) (void | oserror) = {
|
||||
// Find the path length (excluding trailing NUL).
|
||||
let n: i32 = 0;
|
||||
for (path[n] != 0u8) { n += 1; };
|
||||
// Mirrors Hare's os::mkdirs (ref/hare/os/os.ha:54). The in-place
|
||||
// '/' → NUL splice walks the kpath-loaded [[pathbuf]] directly
|
||||
// instead of recursing through [[mkdir]] — re-entering kpath would
|
||||
// clobber the buffer mid-walk (single static slot, see kpath's
|
||||
// non-reentrancy note above).
|
||||
export fn mkdirs(path: str, mode: i32) (void | oserror) = {
|
||||
let cp: *u8 = kpath(path);
|
||||
if (cp == nil: *u8) { return -36i64: oserror; };
|
||||
let n: i32 = path.len;
|
||||
if (n == 0) { return; };
|
||||
|
||||
// Walk forward; at each '/' boundary, NUL-terminate the prefix,
|
||||
// mkdir it, restore the slash, continue. Skip index 0 so a
|
||||
// leading '/' on absolute paths doesn't trigger an empty mkdir.
|
||||
// raw MKDIR syscall on pathbuf, restore the slash, continue.
|
||||
// Skip index 0 so a leading '/' on absolute paths doesn't
|
||||
// trigger an empty mkdir.
|
||||
let i: i32 = 1;
|
||||
for (i < n) {
|
||||
if (path[i] == 47u8) { // '/'
|
||||
path[i] = 0u8;
|
||||
let r: i32 = mkdir(path, mode);
|
||||
path[i] = 47u8;
|
||||
if (pathbuf[i] == 47u8) { // '/'
|
||||
pathbuf[i] = 0u8;
|
||||
let r: i32 = syscall2(nr.MKDIR,
|
||||
(&pathbuf[0]): i64, mode: i64): i32;
|
||||
pathbuf[i] = 47u8;
|
||||
if (r < 0) {
|
||||
if (r != -17) { return r: i64: oserror; };
|
||||
};
|
||||
@@ -250,8 +292,8 @@ export fn mkdirs(path: *u8, mode: i32) (void | oserror) = {
|
||||
i += 1;
|
||||
};
|
||||
|
||||
// mkdir the full path.
|
||||
let r: i32 = mkdir(path, mode);
|
||||
let r: i32 = syscall2(nr.MKDIR,
|
||||
(&pathbuf[0]): i64, mode: i64): i32;
|
||||
if (r < 0) {
|
||||
if (r != -17) { return r: i64: oserror; };
|
||||
};
|
||||
@@ -269,9 +311,14 @@ export fn fork() i32 = {
|
||||
return syscall0(nr.FORK): i32;
|
||||
};
|
||||
|
||||
// execve(2): on success, does not return.
|
||||
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
|
||||
return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
|
||||
// execve(2): on success, does not return. Mirrors Hare's
|
||||
// os::exec::exec path arg (str). argv/envp stay `**u8` — the
|
||||
// kernel takes a NUL-pointer-terminated table of NUL-terminated
|
||||
// C strings, a different shape from a path.
|
||||
export fn execve(path: str, argv: **u8, envp: **u8) i32 = {
|
||||
let p: *u8 = kpath(path);
|
||||
if (p == nil: *u8) { return -36i32; };
|
||||
return syscall3(nr.EXECVE, p: i64, argv: i64, envp: i64): i32;
|
||||
};
|
||||
|
||||
// wait4(2): wait for `pid` (or any child if -1), store status in
|
||||
@@ -544,27 +591,31 @@ fn fillfilestat(out: *filestat, k: *kstat) void = {
|
||||
};
|
||||
|
||||
// stat — fill *out with metadata for `path`. Follows symlinks.
|
||||
// `path` must be NUL-terminated (lib/os convention; see task #23
|
||||
// for a planned `path: str` migration).
|
||||
// Returns ENAMETOOLONG (-36) as `oserror` if the path overflows
|
||||
// PATH_MAX.
|
||||
//
|
||||
// Mirrors Hare's sys::stat (ref/hare/sys/+linux/stat.ha:51) modulo
|
||||
// the out-param shape forced by the cgreturn 24B cap. Note: Hare's
|
||||
// higher-level fs::stat (ref/hare/fs/fs.ha:172) instead has lstat
|
||||
// semantics — we follow sys::stat's POSIX-stat behavior here.
|
||||
export fn stat(out: *filestat, path: *u8) (void | oserror) = {
|
||||
export fn stat(out: *filestat, path: str) (void | oserror) = {
|
||||
let cp: *u8 = kpath(path);
|
||||
if (cp == nil: *u8) { return -36i64: oserror; };
|
||||
let k: kstat;
|
||||
let r: i64 = syscall4(nr.NEWFSTATAT,
|
||||
AT_FDCWD: i64, path: i64, (&k): i64, 0i64);
|
||||
AT_FDCWD: i64, cp: i64, (&k): i64, 0i64);
|
||||
if (r < 0) { return r: oserror; };
|
||||
fillfilestat(out, &k);
|
||||
};
|
||||
|
||||
// lstat — like [[stat]] but does NOT follow a terminal symlink.
|
||||
// Mirrors Hare's sys::lstat (ref/hare/sys/+linux/stat.ha:57).
|
||||
export fn lstat(out: *filestat, path: *u8) (void | oserror) = {
|
||||
export fn lstat(out: *filestat, path: str) (void | oserror) = {
|
||||
let cp: *u8 = kpath(path);
|
||||
if (cp == nil: *u8) { return -36i64: oserror; };
|
||||
let k: kstat;
|
||||
let r: i64 = syscall4(nr.NEWFSTATAT,
|
||||
AT_FDCWD: i64, path: i64, (&k): i64,
|
||||
AT_FDCWD: i64, cp: i64, (&k): i64,
|
||||
AT_SYMLINK_NOFOLLOW: i64);
|
||||
if (r < 0) { return r: oserror; };
|
||||
fillfilestat(out, &k);
|
||||
@@ -585,7 +636,9 @@ export fn fstat(out: *filestat, fd: i32) (void | oserror) = {
|
||||
// exists — true if `path` resolves to anything (regular file,
|
||||
// directory, symlink, ...). Stat-shaped (Hare's `fs::exists`,
|
||||
// ref/hare/fs/fs.ha:196) — no separate syscall. Symlinks are
|
||||
// followed; a dangling symlink is `false`.
|
||||
// followed; a dangling symlink is `false`. ENAMETOOLONG is
|
||||
// swallowed as `false` — Hare's os::exists doc says "true if a
|
||||
// node exists at the given path, or false if not."
|
||||
//
|
||||
// Race warning: prefer "open and handle the error" over "exists
|
||||
// then open" in real code (Hare's docstring carries the same
|
||||
@@ -598,9 +651,11 @@ export fn fstat(out: *filestat, fd: i32) (void | oserror) = {
|
||||
// — same class as STATUS #22, surfaced first time a match on this
|
||||
// shape combined with an 80B local-struct local frame). Use the
|
||||
// match shape once #22 lands.
|
||||
export fn exists(path: *u8) bool = {
|
||||
export fn exists(path: str) bool = {
|
||||
let cp: *u8 = kpath(path);
|
||||
if (cp == nil: *u8) { return false; };
|
||||
let k: kstat;
|
||||
let r: i64 = syscall4(nr.NEWFSTATAT,
|
||||
AT_FDCWD: i64, path: i64, (&k): i64, 0i64);
|
||||
AT_FDCWD: i64, cp: i64, (&k): i64, 0i64);
|
||||
return r >= 0i64;
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
||||
| os.stat_mask.SIZE | os.stat_mask.INODE
|
||||
| os.stat_mask.ATIME | os.stat_mask.MTIME
|
||||
| os.stat_mask.CTIME) as u32;
|
||||
match (os.stat(&fi, p.ptr)) {
|
||||
match (os.stat(&fi, p)) {
|
||||
case let e: os.oserror => fail();
|
||||
case void => {
|
||||
if ((fi.mask as u32) != wantmask) { fail(); };
|
||||
@@ -87,7 +87,7 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
||||
@test fn test_stat_subdir() void = {
|
||||
let p = envpath("WW_TEST_STAT_SUBDIR");
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, p.ptr)) {
|
||||
match (os.stat(&fi, p)) {
|
||||
case let e: os.oserror => fail();
|
||||
case void => {
|
||||
if (!istype(fi.mode, os.mode.DIR)) { fail(); };
|
||||
@@ -100,7 +100,7 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
||||
@test fn test_stat_noent() void = {
|
||||
let p = envpath("WW_TEST_STAT_NOENT");
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, p.ptr)) {
|
||||
match (os.stat(&fi, p)) {
|
||||
case void => fail();
|
||||
case let e: os.oserror => {
|
||||
// ENOENT = 2 → raw errno is -2.
|
||||
@@ -117,7 +117,7 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
||||
@test fn test_stat_symlink_follow() void = {
|
||||
let p = envpath("WW_TEST_STAT_SYMLINK");
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, p.ptr)) {
|
||||
match (os.stat(&fi, p)) {
|
||||
case let e: os.oserror => fail();
|
||||
case void => {
|
||||
if (!istype(fi.mode, os.mode.REG)) { fail(); };
|
||||
@@ -129,7 +129,7 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
||||
@test fn test_lstat_symlink_nofollow() void = {
|
||||
let p = envpath("WW_TEST_STAT_SYMLINK");
|
||||
let fi: os.filestat;
|
||||
match (os.lstat(&fi, p.ptr)) {
|
||||
match (os.lstat(&fi, p)) {
|
||||
case let e: os.oserror => fail();
|
||||
case void => {
|
||||
if (!istype(fi.mode, os.mode.LINK)) { fail(); };
|
||||
@@ -141,7 +141,7 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
||||
|
||||
@test fn test_fstat_regfile() void = {
|
||||
let p = envpath("WW_TEST_STAT_REGFILE");
|
||||
let fd: i32 = os.open(p.ptr, os.flag.RDONLY, 0i32);
|
||||
let fd: i32 = os.open(p, os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { fail(); };
|
||||
let fi: os.filestat;
|
||||
match (os.fstat(&fi, fd)) {
|
||||
@@ -158,17 +158,53 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
||||
|
||||
@test fn test_exists_regfile() void = {
|
||||
let p = envpath("WW_TEST_STAT_REGFILE");
|
||||
if (!os.exists(p.ptr)) { fail(); };
|
||||
if (!os.exists(p)) { fail(); };
|
||||
};
|
||||
|
||||
@test fn test_exists_subdir() void = {
|
||||
let p = envpath("WW_TEST_STAT_SUBDIR");
|
||||
if (!os.exists(p.ptr)) { fail(); };
|
||||
if (!os.exists(p)) { fail(); };
|
||||
};
|
||||
|
||||
@test fn test_exists_noent() void = {
|
||||
let p = envpath("WW_TEST_STAT_NOENT");
|
||||
if (os.exists(p.ptr)) { fail(); };
|
||||
if (os.exists(p)) { fail(); };
|
||||
};
|
||||
|
||||
// ---- ENAMETOOLONG: kpath rejects paths >= PATH_MAX -------------------
|
||||
//
|
||||
// kpath copies into a single [PATH_MAX]u8 buffer and reserves one byte
|
||||
// for the NUL terminator (`p.len + 1 >= PATH_MAX` → reject). The
|
||||
// rejection surfaces as `oserror = -36` (ENAMETOOLONG) on (... |
|
||||
// oserror)-returning wrappers, and as `false` on [[os.exists]]
|
||||
// (Hare's os::exists doc: "true if a node exists at the given path,
|
||||
// or false if not.").
|
||||
|
||||
let bigbuf: [4200]u8;
|
||||
|
||||
fn makebig(n: i32) str = {
|
||||
let i: i32 = 0;
|
||||
for (i < n) { bigbuf[i] = 97u8; i += 1; }; // 'a'
|
||||
let r: str;
|
||||
r.ptr = &bigbuf[0];
|
||||
r.len = n;
|
||||
return r;
|
||||
};
|
||||
|
||||
@test fn test_stat_toolong() void = {
|
||||
let bp = makebig(os.PATH_MAX);
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, bp)) {
|
||||
case void => fail();
|
||||
case let e: os.oserror => {
|
||||
if ((e: i64) != -36i64) { fail(); }; // ENAMETOOLONG
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@test fn test_exists_toolong() void = {
|
||||
let bp = makebig(os.PATH_MAX);
|
||||
if (os.exists(bp)) { fail(); };
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
@@ -181,5 +217,7 @@ export fn main() i32 = {
|
||||
signalled = 7; test_exists_regfile();
|
||||
signalled = 8; test_exists_subdir();
|
||||
signalled = 9; test_exists_noent();
|
||||
signalled = 10; test_stat_toolong();
|
||||
signalled = 11; test_exists_toolong();
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -186,7 +186,9 @@ export fn named(outfd: *i32, outpath: *str,
|
||||
};
|
||||
for (true) {
|
||||
pathlen = makenamed(dir);
|
||||
let fd: i32 = os.open(&pathbuf[0], flags, perm);
|
||||
let bs: str;
|
||||
bs.ptr = &pathbuf[0]; bs.len = pathlen;
|
||||
let fd: i32 = os.open(bs, flags, perm);
|
||||
if (fd >= 0) {
|
||||
*outfd = fd;
|
||||
let p: str;
|
||||
@@ -227,7 +229,9 @@ export fn file(iomode: mode, perm: i32) (i32 | os.oserror) = {
|
||||
export fn dir() str = {
|
||||
for (true) {
|
||||
pathlen = makedir();
|
||||
let r: i32 = os.mkdir(&pathbuf[0], 448i32); // 0o700
|
||||
let bs: str;
|
||||
bs.ptr = &pathbuf[0]; bs.len = pathlen;
|
||||
let r: i32 = os.mkdir(bs, 448i32); // 0o700
|
||||
if (r >= 0) {
|
||||
let p: str;
|
||||
p.ptr = &pathbuf[0];
|
||||
|
||||
@@ -94,13 +94,13 @@ fn streq(a: str, b: str) bool = {
|
||||
};
|
||||
|
||||
// File exists pre-cleanup.
|
||||
if (os.access(p.ptr, 0i32) != 0) { fail(); };
|
||||
if (os.access(p, 0i32) != 0) { fail(); };
|
||||
|
||||
if (os.close(fd) != 0) { fail(); };
|
||||
if (os.remove(p.ptr) != 0) { fail(); };
|
||||
if (os.remove(p) != 0) { fail(); };
|
||||
|
||||
// Cleanup landed.
|
||||
if (os.access(p.ptr, 0i32) == 0) { fail(); };
|
||||
if (os.access(p, 0i32) == 0) { fail(); };
|
||||
|
||||
i += 1;
|
||||
};
|
||||
@@ -156,10 +156,10 @@ fn strslice(p: str, lo: i32, hi: i32) str = {
|
||||
if (fd1 == fd2) { fail(); };
|
||||
|
||||
if (os.close(fd2) != 0) { fail(); };
|
||||
if (os.remove(p2.ptr) != 0) { fail(); };
|
||||
if (os.remove(p2) != 0) { fail(); };
|
||||
|
||||
if (os.close(fd1) != 0) { fail(); };
|
||||
if (os.remove(&snap[0]) != 0) { fail(); };
|
||||
if (os.remove(psnap) != 0) { fail(); };
|
||||
};
|
||||
|
||||
// NOTE: temp.file() has no test of its own. The function is a thin
|
||||
@@ -187,17 +187,20 @@ fn strslice(p: str, lo: i32, hi: i32) str = {
|
||||
if (d.ptr[d.len] != 0u8) { fail(); };
|
||||
|
||||
// Dir exists.
|
||||
if (os.access(d.ptr, 0i32) != 0) { fail(); };
|
||||
if (os.access(d, 0i32) != 0) { fail(); };
|
||||
|
||||
// Snapshot the dir path into a local NUL-terminated buffer:
|
||||
// we'll need it after os.open() (the child create) leaves
|
||||
// the buffer alone, but it's good hygiene given future
|
||||
// helpers might share pathbuf.
|
||||
// the os.* path entrypoints now copy through lib/os.pathbuf
|
||||
// (kpath), so d's view into temp.pathbuf is safe; the
|
||||
// snapshot still buys robustness against future helpers
|
||||
// that might share temp's buffer.
|
||||
let dsnap: [128]u8;
|
||||
let dlen: i32 = d.len;
|
||||
let s: i32 = 0;
|
||||
for (s < d.len) { dsnap[s] = d[s]; s += 1; };
|
||||
dsnap[d.len] = 0u8;
|
||||
let dview: str;
|
||||
dview.ptr = &dsnap[0]; dview.len = dlen;
|
||||
|
||||
if (childn[i] > 0) {
|
||||
// Build "<d>/x\0" in a local buffer.
|
||||
@@ -208,23 +211,25 @@ fn strslice(p: str, lo: i32, hi: i32) str = {
|
||||
cbuf[off] = 47u8; off += 1; // '/'
|
||||
cbuf[off] = 120u8; off += 1; // 'x'
|
||||
cbuf[off] = 0u8;
|
||||
let cview: str;
|
||||
cview.ptr = &cbuf[0]; cview.len = off;
|
||||
let cflags: os.flag = os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL;
|
||||
let fd: i32 = os.open(&cbuf[0], cflags, 384i32);
|
||||
let fd: i32 = os.open(cview, cflags, 384i32);
|
||||
if (fd < 0) { fail(); };
|
||||
let payload: [3]u8;
|
||||
payload[0] = 88u8; payload[1] = 89u8; payload[2] = 90u8; // "XYZ"
|
||||
let wr: i64 = os.write(fd, &payload[0], 3u64);
|
||||
if (wr != 3i64) { fail(); };
|
||||
if (os.close(fd) != 0) { fail(); };
|
||||
if (os.remove(&cbuf[0]) != 0) { fail(); };
|
||||
if (os.remove(cview) != 0) { fail(); };
|
||||
};
|
||||
|
||||
// Rmdir uses dsnap (more robust if the static buffer were
|
||||
// Rmdir uses dview (more robust if the static buffer were
|
||||
// touched between dir() and here).
|
||||
if (os.rmdir(&dsnap[0]) != 0) { fail(); };
|
||||
if (os.rmdir(dview) != 0) { fail(); };
|
||||
|
||||
// Cleanup landed.
|
||||
if (os.access(&dsnap[0], 0i32) == 0) { fail(); };
|
||||
if (os.access(dview, 0i32) == 0) { fail(); };
|
||||
|
||||
i += 1;
|
||||
};
|
||||
@@ -249,8 +254,8 @@ fn strslice(p: str, lo: i32, hi: i32) str = {
|
||||
if (streq(strslice(d2, 0, d2.len), dsnap)) { fail(); };
|
||||
|
||||
// Cleanup both, using snap for d1's old contents.
|
||||
if (os.rmdir(d2.ptr) != 0) { fail(); };
|
||||
if (os.rmdir(&snap[0]) != 0) { fail(); };
|
||||
if (os.rmdir(d2) != 0) { fail(); };
|
||||
if (os.rmdir(dsnap) != 0) { fail(); };
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
|
||||
Reference in New Issue
Block a user