code wiki / (root) / nx_fsops_lib.nx

nx_fsops_lib.nx source

↩ module page · 580 lines · 28495 B

1// nx_fsops_lib.nx -- CONSOLIDATED filesystem tool (MCP name: nx_fs, tool #4 of the 15), LIBRARY half. 2// (Source is named nx_fsops because nx_fs.nx is the safety-enveloped file-I/O STDLIB -- a different thing.) 3// READ-ONLY first increment: `read` (bounded file read) + `ls` (typed dir listing). Retires ssh-cat for 4// remote reads per rule 27 (api-first, no shell plumbing). 5// 6// BOUNDARY DEFENSE (rule 12 -- MCP callers are EXTERNAL input): `read` REFUSES any path that matches the 7// secret DENY-LIST: compiled-in default needles (secret/key/token/passw/.pem, matched case-insensitively 8// against the WHOLE path) plus data-driven extras from fs_read_deny.conf (one lowercase needle per line, 9// CWD-relative -- rule 11: policy in data, not code). The tools-api runs where key material lives; an 10// arbitrary-read tool that could return opaque_keys.bin or tools_cap_secret.key would convert a read-cap 11// into a key-theft primitive. Over-blocking is the SAFE failure direction for v1. 12// WRITE/EDIT increment (2026-07-16): fsx_write (ATOMIC tmp+fsync+rename) + fsx_edit (exact-string replace 13// with the Claude-Edit UNIQUENESS contract). Exposed as the SEPARATE tools-api name `nx_fs_write` (its own 14// cap class per knowledge/mcp/exposure_policy.txt: read=broad, write=cap) -- the `nx_fs` name stays read-only. 15// The write DENY is a superset of the read deny (never clobber key material) PLUS the OS device/kernel/ 16// firmware namespace via the nx_os_fs seam (rule 26 never-brick BY CONSTRUCTION -- not config-disableable) 17// PLUS the tool-registry escalation surface ("allowlist") PLUS data-driven extras (fs_write_deny.conf). 18// license_tier: ORIGINAL 19import "nx_syscalls.nx" 20import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 21import "nx_vsz_watchdog_core.nx" // vw_read (bounded, procfs-safe) / vw_slen / vw_contains -- proven helpers 22import "nx_os_fs.nx" // osf_write_forbidden -- device/firmware-namespace deny (OS seam, rule 26) 23import "nx_os_proc.nx" // osp_selfpid -- unique atomic-write tmp suffix (no torn tmp under concurrency) 24const FSX_MAGIC_4095: i64 = 4095 25 26const FSX_READ_CAP: i64 = 1048576 // max bytes returned by `read` (truncation is MARKED, never silent) 27const FSX_DENY_CAP: i64 = 8192 // fs_read_deny.conf read cap 28const FSX_PATH_CAP: i64 = 1024 // lowercased path work buffer 29const FSX_DENT_BUF: i64 = 65536 // getdents64 batch buffer (matches the proven vsz/heal sizing) 30const FSX_LS_CAP: i64 = 200 // scale-law: max ls entries EMITTED; true total ALWAYS declared (65KB-dump fix) 31const FSX_RC_ABSENT: i64 = 3 // exit: path absent/unreadable (mirrors nx_fileop's exists convention) 32const FSX_RC_DENIED: i64 = 5 // exit: deny-list refused the read 33const FSX_UPPER_A: i64 = 65 // 'A' (ASCII lowercasing) 34const FSX_UPPER_Z: i64 = 90 // 'Z' 35const FSX_CASE_OFF: i64 = 32 // 'a' - 'A' 36const FSX_ASCII_0: i64 = 48 // '0' (decimal print) 37 38func fsx_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 39// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 40// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 41// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 42// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 43func fsx_putn(v: i64) -> i64 { nxi_out(v); return 0 } 44// lowercase copy of s into out (bounded), returns length 45func fsx_lower(s: *u8, out: *u8, cap: i64) -> i64 { 46 var i: i64 = 0 47 while s[i] != (0 as u8) { 48 if i >= cap - 1 { out[i] = 0 as u8; return i } 49 var c: i64 = s[i] as i64 50 if c >= FSX_UPPER_A { if c <= FSX_UPPER_Z { c = c + FSX_CASE_OFF } } 51 out[i] = c as u8 52 i = i + 1 53 } 54 out[i] = 0 as u8 55 return i 56} 57// exact NUL-terminated string equality 58func fsx_seq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 } 59// is `needle` (NUL-terminated, lowercase) contained in lowercase path lp[0..ln)? 60// ---------- compare-and-swap decision (seq1422/seq1456) ---------- 61// 62// PURE, and in the LIB on purpose: the decision used to live inside the CLI's 63// main(), where a gate cannot reach it -- which is exactly how it shipped 64// refusing every correct expectation (seq1422). A rule nothing can drive is a 65// rule nothing can prove. 66// 67// tok is the raw argv token (`expect=<n>` / `expect=any` / a bare number); 68// cur is the file's real size. Returns 1 = ALLOW, 0 = REFUSE. 69func fsx_cas_val(tok: *u8) -> *u8 { 70 var i: i64 = 0 71 while tok[i] != (0 as u8) { 72 if tok[i] == (61 as u8) { return ((tok as i64) + i + 1) as *u8 } 73 i = i + 1 74 } 75 return tok 76} 77func fsx_cas_ok(cur: i64, tok: *u8) -> i64 { 78 let v: *u8 = fsx_cas_val(tok) 79 if fsx_seq(v, "any" as *u8) == 1 { return 1 } 80 var n: i64 = 0 81 var i: i64 = 0 82 var got: i64 = 0 83 while v[i] != (0 as u8) { 84 let c: i64 = v[i] as i64 85 if c >= 48 { if c <= 57 { n = n * 10 + (c - 48); got = 1 } } 86 i = i + 1 87 } 88 if got == 0 { return 0 } 89 if n == cur { return 1 } 90 return 0 91} 92 93func fsx_deny_hit(lp: *u8, ln: i64, needle: *u8) -> i64 { 94 let nl: i64 = vw_slen(needle) 95 if nl == 0 { return 0 } 96 return vw_contains(lp, ln, needle, nl) 97} 98// data-driven deny extras: one lowercase needle per line in `conf`; 1 = some line matches the path. 99// Factored out so the read deny (fs_read_deny.conf) and write deny (fs_write_deny.conf) share ONE scanner. 100func fsx_conf_deny(lp: *u8, ln: i64, conf: *u8) -> i64 { 101 let cb: *u8 = sys_mmap(FSX_DENY_CAP) 102 let cn: i64 = vw_read(conf, cb, FSX_DENY_CAP - 1) 103 if cn > 0 { 104 var ls: i64 = 0 105 var i: i64 = 0 106 while i <= cn { 107 var eol: i64 = 0 108 if i == cn { eol = 1 } else { if cb[i] == (10 as u8) { eol = 1 } } 109 if eol == 1 { 110 if i > ls { 111 cb[i] = 0 as u8 // terminate the line in place 112 if fsx_deny_hit(lp, ln, (cb as i64 + ls) as *u8) == 1 { return 1 } 113 } 114 ls = i + 1 115 } 116 i = i + 1 117 } 118 } 119 return 0 120} 121const FSX_SNIFF_CAP: i64 = 4096 122 123func fsx_isalnum(c: i64) -> i64 { 124 if c >= 48 { if c <= 57 { return 1 } } 125 if c >= 97 { if c <= 122 { return 1 } } 126 if c >= 65 { if c <= 90 { return 1 } } 127 return 0 128} 129 130func fsx_ends_with(lp: *u8, ln: i64, suf: *u8) -> i64 { 131 let sl: i64 = vw_slen(suf) 132 if sl == 0 { return 0 } 133 if sl > ln { return 0 } 134 var i: i64 = 0 135 while i < sl { 136 if lp[ln - sl + i] != suf[i] { return 0 } 137 i = i + 1 138 } 139 return 1 140} 141 142func fsx_basename_is(lp: *u8, ln: i64, name: *u8) -> i64 { 143 let nl: i64 = vw_slen(name) 144 if nl == 0 { return 0 } 145 if nl > ln { return 0 } 146 if fsx_ends_with(lp, ln, name) == 0 { return 0 } 147 if nl == ln { return 1 } 148 let c: i64 = lp[ln - nl - 1] as i64 149 if c == 47 { return 1 } 150 if c == 92 { return 1 } 151 return 0 152} 153 154// Whole-word containment: bounded by non-alphanumeric on BOTH sides, so `api_secret.txt` is denied and 155// `secretary_notes.md` is not. 156func fsx_word_has(lp: *u8, ln: i64, w: *u8) -> i64 { 157 let wl: i64 = vw_slen(w) 158 if wl == 0 { return 0 } 159 if wl > ln { return 0 } 160 var i: i64 = 0 161 while i + wl <= ln { 162 var eq: i64 = 1 163 var k: i64 = 0 164 while k < wl { if lp[i + k] != w[k] { eq = 0; k = wl } else { k = k + 1 } } 165 if eq == 1 { 166 var lb: i64 = 1 167 if i > 0 { if fsx_isalnum(lp[i - 1] as i64) == 1 { lb = 0 } } 168 var rb: i64 = 1 169 if i + wl < ln { if fsx_isalnum(lp[i + wl] as i64) == 1 { rb = 0 } } 170 if lb == 1 { if rb == 1 { return 1 } } 171 } 172 i = i + 1 173 } 174 return 0 175} 176 177// CONTENT LEG: sniff the leading bytes for what a secret actually IS. This is the half a name-only list 178// can never do -- it denies a private key no matter what it is called, including `notes.txt`. 179// A CERTIFICATE is deliberately NOT denied: certs are public by definition, and denying them is the same 180// category error as denying the tokenizer. 181func fsx_content_secret(path: *u8) -> i64 { 182 let fd: i64 = sys_openat_rd(path) 183 if fd < 0 { return 0 } 184 let b: *u8 = sys_mmap(FSX_SNIFF_CAP) 185 let n: i64 = sys_read(fd, b, FSX_SNIFF_CAP - 1) 186 sys_close(fd) 187 if n <= 0 { return 0 } 188 if vw_contains(b, n, "PRIVATE KEY-----" as *u8, 16) == 1 { return 1 } 189 if vw_contains(b, n, "OPENSSH PRIVATE KEY" as *u8, 19) == 1 { return 1 } 190 if vw_contains(b, n, "PGP PRIVATE KEY BLOCK" as *u8, 21) == 1 { return 1 } 191 if vw_contains(b, n, "PuTTY-User-Key-File" as *u8, 19) == 1 { return 1 } 192 return 0 193} 194 195// DENY check: 1 = refuse this path. SOTA-2026 REWRITE (2026-07-31). 196// 197// THE OLD RULE WAS WRONG IN BOTH DIRECTIONS, measured on real paths: 198// OVER-BLOCKED substring "token" denied runtime/nx_tokenizer.nx -- the compiler's own tokenizer, which 199// contains no secret -- and blocked BOTH nx_fs read AND nx_fs_write on it, while 200// nx_shelltool grep returned the same bytes freely. It cost real work and bought nothing. 201// substring "key" likewise denies monkey / keyword / keyboard. 202// UNDER-BLOCKED `id_rsa`, the canonical SSH private key filename, contains NONE of 203// secret/key/token/passw/.pem and sailed straight through. 204// A denylist that blocks source and passes private keys is not a security control -- it is a rename away 205// from useless in one direction and a permanent nuisance in the other. 206// 207// REPLACEMENT -- two INDEPENDENT legs, either one denies: 208// (1) PATH leg: real secret-bearing EXTENSIONS and exact BASENAMES, matched at a true suffix/segment 209// boundary, plus whole-word `secret`/`password`. No substring-anywhere matching survives. 210// (2) CONTENT leg: PEM/OpenSSH/PGP/PuTTY private-key armour, which catches a secret regardless of name. 211// Net effect: strictly MORE secrets denied (id_rsa, a renamed key, a key with no extension) and strictly 212// FEWER ordinary sources blocked. 213func fsx_denied(path: *u8) -> i64 { 214 let lp: *u8 = sys_mmap(FSX_PATH_CAP) 215 let ln: i64 = fsx_lower(path, lp, FSX_PATH_CAP) 216 217 if fsx_ends_with(lp, ln, ".pem" as *u8) == 1 { return 1 } 218 if fsx_ends_with(lp, ln, ".key" as *u8) == 1 { return 1 } 219 if fsx_ends_with(lp, ln, ".cap" as *u8) == 1 { return 1 } 220 if fsx_ends_with(lp, ln, ".p12" as *u8) == 1 { return 1 } 221 if fsx_ends_with(lp, ln, ".pfx" as *u8) == 1 { return 1 } 222 if fsx_ends_with(lp, ln, ".jks" as *u8) == 1 { return 1 } 223 if fsx_ends_with(lp, ln, ".ppk" as *u8) == 1 { return 1 } 224 if fsx_ends_with(lp, ln, "_rsa" as *u8) == 1 { return 1 } 225 if fsx_ends_with(lp, ln, "_dsa" as *u8) == 1 { return 1 } 226 if fsx_ends_with(lp, ln, "_ecdsa" as *u8) == 1 { return 1 } 227 if fsx_ends_with(lp, ln, "_ed25519" as *u8) == 1 { return 1 } 228 229 if fsx_basename_is(lp, ln, ".env" as *u8) == 1 { return 1 } 230 if fsx_basename_is(lp, ln, "credentials" as *u8) == 1 { return 1 } 231 if fsx_basename_is(lp, ln, "shadow" as *u8) == 1 { return 1 } 232 if fsx_basename_is(lp, ln, "opaque_keys.bin" as *u8) == 1 { return 1 } 233 234 // CALIBRATED BY WORD FREQUENCY, not by one uniform rule -- the gate proved a uniform rule wrong in 235 // BOTH directions within minutes. `secret` and `passw` are high-signal and essentially absent from 236 // ordinary source, so SUBSTRING matching is correct for them and catches mysecret_key.bin. `key` and 237 // `token` are common English fragments (tokenizer, monkey, keyword, keyboard) and must NEVER be 238 // substring-matched -- that is what denied the compiler's own tokenizer. They are covered instead by 239 // the extension/suffix rules above and by the content leg below. 240 if fsx_deny_hit(lp, ln, "secret" as *u8) == 1 { return 1 } 241 if fsx_deny_hit(lp, ln, "passw" as *u8) == 1 { return 1 } 242 if fsx_deny_hit(lp, ln, "credential" as *u8) == 1 { return 1 } 243 244 if fsx_content_secret(path) == 1 { return 1 } 245 246 return fsx_conf_deny(lp, ln, "fs_read_deny.conf" as *u8) 247} 248// read: emit up to `cap` bytes of path to stdout. Returns bytes emitted; -1 absent; -2 DENIED. 249// deniedp/absent are ALSO visible in the CLI exit code. Truncation is marked with a trailing banner. 250// Failure reporter that KEEPS THE ERRNO. sys_openat_rd returns -errno, and the old message printed 251// "ABSENT" for every negative -- so EACCES (-13, EXISTS but unopenable) read as "missing", which are 252// OPPOSITE remedies. Cost a real hour on 2026-08-01: knowledge/foundation existed with mode 0100 and 253// every instrument in the stack called it absent (the mkdirp read-back that printed the errno cracked 254// the case in one call). rc>=0 means a probe re-open SUCCEEDED: the earlier read failed for a 255// non-open reason (an empty file), so say THAT. Always returns -1 (callers' contract unchanged; 256// the -2 DENIED sentinel stays distinct). 257func fsx_fail(path: *u8, rc: i64) -> i64 { 258 if rc >= 0 { sys_close(rc); fsx_puts("NX-FS EMPTY: 0 bytes: " as *u8); fsx_puts(path); fsx_puts("\n" as *u8); return 0 - 1 } 259 if rc == 0 - 13 { 260 fsx_puts("NX-FS PERMISSION (EACCES): exists but this process may not open it: " as *u8) 261 fsx_puts(path); fsx_puts("\n" as *u8) 262 return 0 - 1 263 } 264 if rc == 0 - 2 { fsx_puts("NX-FS ABSENT: " as *u8); fsx_puts(path); fsx_puts("\n" as *u8); return 0 - 1 } 265 fsx_puts("NX-FS ERROR rc=" as *u8); fsx_putn(rc) 266 fsx_puts(": " as *u8); fsx_puts(path); fsx_puts("\n" as *u8) 267 return 0 - 1 268} 269 270func fsx_read(path: *u8, cap: i64) -> i64 { 271 if fsx_denied(path) == 1 { 272 fsx_puts("NX-FS DENIED: path matches the secret deny-list (defaults + fs_read_deny.conf)\n" as *u8) 273 return 0 - (2 as i64) // DENIED sentinel (distinct from -1 absent) 274 } 275 var want: i64 = cap 276 if want <= 0 { want = FSX_READ_CAP } 277 if want > FSX_READ_CAP { want = FSX_READ_CAP } 278 let buf: *u8 = sys_mmap(want + 1) 279 let n: i64 = vw_read(path, buf, want) 280 // vw_read flattens the errno (-1 for every failure); re-probe the open ONLY on the failure path 281 // so the message can distinguish absent / permission / empty. Zero cost on success. 282 if n <= 0 { return fsx_fail(path, sys_openat_rd(path)) } 283 sys_write(1, buf, n) 284 if n == want { 285 fsx_puts("\n[NX-FS TRUNCATED at " as *u8); fsx_putn(n); fsx_puts(" bytes]\n" as *u8) 286 } 287 return n 288} 289// WINDOWED read (eats debt seq222: the tools-call transport caps ~64KB, so files past the cap were 290// unreadable over MCP): emit up to `cap` bytes starting at byte `off`. Same deny-list as fsx_read. 291// A separate function (NOT an fsx_read arity change) so every existing caller keeps its exact contract. 292func fsx_read_at(path: *u8, cap: i64, off: i64) -> i64 { 293 if fsx_denied(path) == 1 { 294 fsx_puts("NX-FS DENIED: path matches the secret deny-list (defaults + fs_read_deny.conf)\n" as *u8) 295 return 0 - (2 as i64) 296 } 297 var want: i64 = cap 298 if want <= 0 { want = FSX_READ_CAP } 299 if want > FSX_READ_CAP { want = FSX_READ_CAP } 300 let fd: i64 = sys_openat_rd(path) 301 if fd < 0 { return fsx_fail(path, fd) } 302 if off > 0 { if sys_lseek(fd, off, 0) < 0 { sys_close(fd); fsx_puts("NX-FS ABSENT: seek failed " as *u8); fsx_puts(path); fsx_puts("\n" as *u8); return 0 - 1 } } 303 let buf: *u8 = sys_mmap(want + 1) 304 var got: i64 = 0 305 var sc: i64 = 1 306 while sc == 1 { 307 let r: i64 = sys_read(fd, ((buf as i64 + got) as *u8), want - got) 308 if r <= 0 { sc = 0 } else { got = got + r; if got >= want { sc = 0 } } 309 } 310 sys_close(fd) 311 if got <= 0 { fsx_puts("NX-FS EOF: no bytes at offset " as *u8); fsx_putn(off); fsx_puts(" in " as *u8); fsx_puts(path); fsx_puts("\n" as *u8); return 0 - 1 } 312 sys_write(1, buf, got) 313 if got == want { 314 fsx_puts("\n[NX-FS WINDOW off=" as *u8); fsx_putn(off); fsx_puts(" n=" as *u8); fsx_putn(got); fsx_puts(" -- more remains]\n" as *u8) 315 } 316 return got 317} 318const FSX_LINES_SCAN: i64 = 1048576 // line-addressing scan window (matches the proven read cap) 319const FSX_LINES_MAXOUT: i64 = 262144 // max bytes emitted by one `lines` call (transport-friendly) 320const FSX_LINES_DEFN: i64 = 40 // default line count when the caller omits it 321const FSX_LINES_MAXN: i64 = 400 // max lines per call 322 323// LINE-ADDRESSED read -- THE MISSING PRIMITIVE (measured 2026-07-20): `grep` reports file:LINE but `read` 324// takes BYTES, so the two did NOT compose -- locating one function in a remote file meant hand 325// binary-searching byte offsets (cost one subagent 70K tokens + 22 calls for a single extraction). 326// Emits lines [start, start+count) 1-based, then a DECLARED envelope banner (scale-law: a caller can 327// NEVER be silently windowed -- scanned bytes, scan cap, over-window and clip flags are all stated). 328// Same deny-list as fsx_read. Returns bytes emitted; -1 absent; -2 DENIED. 329func fsx_read_lines(path: *u8, start: i64, count: i64) -> i64 { 330 if fsx_denied(path) == 1 { 331 fsx_puts("NX-FS DENIED: path matches the secret deny-list (defaults + fs_read_deny.conf)\n" as *u8) 332 return 0 - (2 as i64) 333 } 334 var s: i64 = start 335 if s < 1 { s = 1 } 336 var c: i64 = count 337 if c <= 0 { c = FSX_LINES_DEFN } 338 if c > FSX_LINES_MAXN { c = FSX_LINES_MAXN } 339 let buf: *u8 = sys_mmap(FSX_LINES_SCAN + 1) 340 let n: i64 = vw_read(path, buf, FSX_LINES_SCAN) 341 if n <= 0 { return fsx_fail(path, sys_openat_rd(path)) } 342 // walk to the first byte of line `s`; cur > s afterwards means we ran off the end (fail-loud, not empty) 343 var i: i64 = 0 344 var cur: i64 = 1 345 while cur < s { 346 if i >= n { cur = s + 1 } else { 347 if buf[i] == (10 as u8) { cur = cur + 1 } 348 i = i + 1 349 } 350 } 351 if cur > s { 352 fsx_puts("NX-FS LINES: start line " as *u8); fsx_putn(s) 353 fsx_puts(" is beyond EOF (scanned " as *u8); fsx_putn(n); fsx_puts(" bytes)\n" as *u8) 354 return 0 355 } 356 let from: i64 = i 357 var lines_out: i64 = 0 358 var j: i64 = i 359 var go: i64 = 1 360 while go == 1 { 361 if j >= n { go = 0 } else { 362 if buf[j] == (10 as u8) { 363 lines_out = lines_out + 1 364 j = j + 1 365 if lines_out >= c { go = 0 } 366 } else { j = j + 1 } 367 } 368 } 369 var outn: i64 = j - from 370 var clipped: i64 = 0 371 if outn > FSX_LINES_MAXOUT { outn = FSX_LINES_MAXOUT; clipped = 1 } 372 if outn > 0 { sys_write(1, ((buf as i64 + from) as *u8), outn) } 373 fsx_puts("\n[NX-FS LINES start=" as *u8); fsx_putn(s) 374 fsx_puts(" lines=" as *u8); fsx_putn(lines_out) 375 fsx_puts(" next=" as *u8); fsx_putn(s + lines_out) 376 fsx_puts(" bytes=" as *u8); fsx_putn(outn) 377 fsx_puts(" scanned=" as *u8); fsx_putn(n) 378 fsx_puts(" scan_cap=" as *u8); fsx_putn(FSX_LINES_SCAN) 379 if n >= FSX_LINES_SCAN { fsx_puts(" FILE-EXCEEDS-SCAN-WINDOW" as *u8) } 380 if clipped == 1 { fsx_puts(" BYTE-CLIPPED" as *u8) } 381 fsx_puts("]\n" as *u8) 382 return outn 383} 384// ==== WRITE/EDIT half (cap class: write; tools-api name nx_fs_write) ========================= 385const FSX_MODE_RW: i64 = 0x1a4 // 0644 -- the ecosystem's file-create mode idiom 386const FSX_DEC: i64 = 10 // decimal base (pid rendering in the tmp suffix) 387const FSX_EDIT_OUT: i64 = 2097152 // edit output buffer (2x read cap: bounded replacement growth) 388const FSX_TMP_ROOM: i64 = 32 // reserved room for ".nxw" + pid digits + NUL in the tmp name 389const FSX_RC_IO: i64 = 4 // exit: io failure (open/short-write/rename) 390const FSX_RC_NOMATCH: i64 = 6 // exit: edit found 0 occurrences (file UNCHANGED) 391const FSX_RC_AMBIG: i64 = 7 // exit: edit found >1 occurrences without `all` (file UNCHANGED) 392 393// write-DENY: read deny (never clobber key material) + OS device/firmware namespace (rule 26, seam, 394// BY CONSTRUCTION) + registry-escalation needle + fs_write_deny.conf extras (data-driven). 395func fsx_write_denied(path: *u8) -> i64 { 396 if fsx_denied(path) == 1 { return 1 } 397 if osf_write_forbidden(path) == 1 { return 1 } 398 let lp: *u8 = sys_mmap(FSX_PATH_CAP) 399 let ln: i64 = fsx_lower(path, lp, FSX_PATH_CAP) 400 if fsx_deny_hit(lp, ln, "allowlist" as *u8) == 1 { return 1 } 401 return fsx_conf_deny(lp, ln, "fs_write_deny.conf" as *u8) 402} 403// ATOMIC full-file write: content lands via <path>.nxw<pid> + fsync + rename, so a reader NEVER sees a 404// torn file and concurrent writers each land whole (last rename wins; pid suffix = no shared tmp). 405// Returns bytes written; -2 DENIED; -3 io error (path too long / open / short write / rename). 406func fsx_write(path: *u8, body: *u8, blen: i64) -> i64 { 407 if fsx_write_denied(path) == 1 { 408 fsx_puts("NX-FS DENIED: write refused (secret/device-namespace/allowlist deny)\n" as *u8) 409 return 0 - (2 as i64) 410 } 411 let plen: i64 = vw_slen(path) 412 if plen + FSX_TMP_ROOM >= FSX_PATH_CAP { return 0 - (3 as i64) } 413 let tmp: *u8 = sys_mmap(FSX_PATH_CAP) 414 var i: i64 = 0 415 while i < plen { tmp[i] = path[i]; i = i + 1 } 416 let suf: *u8 = ".nxw" as *u8 417 var s: i64 = 0 418 while suf[s] != (0 as u8) { tmp[i] = suf[s]; i = i + 1; s = s + 1 } 419 var pid: i64 = osp_selfpid() 420 if pid < 0 { pid = 0 } 421 if pid == 0 { tmp[i] = FSX_ASCII_0 as u8; i = i + 1 } else { 422 let ds: *u8 = sys_mmap(FSX_TMP_ROOM) 423 var k: i64 = 0 424 while pid > 0 { ds[k] = (FSX_ASCII_0 + (pid % FSX_DEC)) as u8; pid = pid / FSX_DEC; k = k + 1 } 425 while k > 0 { tmp[i] = ds[k-1]; i = i + 1; k = k - 1 } 426 } 427 tmp[i] = 0 as u8 428 let fd: i64 = sys_openat_wr(tmp, FSX_MODE_RW) 429 if fd < 0 { return 0 - (3 as i64) } 430 var off: i64 = 0 431 while off < blen { 432 let w: i64 = sys_write(fd, ((body as i64 + off) as *u8), blen - off) 433 if w <= 0 { sys_close(fd); return 0 - (3 as i64) } 434 off = off + w 435 } 436 sys_fsync(fd) 437 sys_close(fd) 438 // PRESERVE the original file's mode across tmp+rename (debt eaten 2026-07-18: an edit of an 439 // executable script used to land 0644 -- the exec bit vanished and the cron runner broke with 440 // rc=126). st_mode = u32 at stat offset 24; keep the permission bits (low 12) only. 441 let sb: *u8 = sys_mmap(160) 442 if sys_fstatat(path, sb) == 0 { 443 let m0: i64 = sb[24] as i64 444 let m1: i64 = sb[25] as i64 445 let om: i64 = (m0 + (m1 * 256)) & FSX_MAGIC_4095 446 if om != FSX_MODE_RW { nx_chmod(tmp, om) } 447 } 448 if sys_renameat(tmp, path) < 0 { return 0 - (3 as i64) } 449 return blen 450} 451// count non-overlapping occurrences of nee[0..nl) in hay[0..hn) 452func fsx_count_occ(hay: *u8, hn: i64, nee: *u8, nl: i64) -> i64 { 453 if nl <= 0 { return 0 } 454 var c: i64 = 0 455 var i: i64 = 0 456 while i + nl <= hn { 457 var m: i64 = 1 458 var j: i64 = 0 459 while j < nl { if hay[i+j] != nee[j] { m = 0; j = nl } else { j = j + 1 } } 460 if m == 1 { c = c + 1; i = i + nl } else { i = i + 1 } 461 } 462 return c 463} 464// replace occurrences of nee with rep into out (allf=0: first only; 1: all). Returns new length; -1 overflow. 465func fsx_replace(hay: *u8, hn: i64, nee: *u8, nl: i64, rep: *u8, rl: i64, out: *u8, ocap: i64, allf: i64) -> i64 { 466 var o: i64 = 0 467 var i: i64 = 0 468 var used: i64 = 0 469 while i < hn { 470 var m: i64 = 0 471 if i + nl <= hn { if nl > 0 { 472 var ok: i64 = 1 473 if allf == 0 { if used == 1 { ok = 0 } } 474 if ok == 1 { 475 m = 1 476 var j: i64 = 0 477 while j < nl { if hay[i+j] != nee[j] { m = 0; j = nl } else { j = j + 1 } } 478 } 479 } } 480 if m == 1 { 481 if o + rl > ocap { return 0 - 1 } 482 var k: i64 = 0 483 while k < rl { out[o] = rep[k]; o = o + 1; k = k + 1 } 484 i = i + nl 485 used = 1 486 } else { 487 if o + 1 > ocap { return 0 - 1 } 488 out[o] = hay[i] 489 o = o + 1 490 i = i + 1 491 } 492 } 493 return o 494} 495// EDIT: exact-string replace with the UNIQUENESS contract (the Claude-Edit SOTA semantic): 496// 0 matches -> -6 NOMATCH (file untouched); >1 without allf -> -7 AMBIGUOUS (file untouched); 497// otherwise replace (allf=1: every occurrence) and land ATOMICALLY via fsx_write. 498// Returns new byte length; -1 absent; -2 DENIED; -3 io/overflow; -6 nomatch; -7 ambiguous. 499func fsx_edit(path: *u8, olds: *u8, news: *u8, allf: i64) -> i64 { 500 if fsx_write_denied(path) == 1 { 501 fsx_puts("NX-FS DENIED: edit refused (secret/device-namespace/allowlist deny)\n" as *u8) 502 return 0 - (2 as i64) 503 } 504 let buf: *u8 = sys_mmap(FSX_READ_CAP + 1) 505 let n: i64 = vw_read(path, buf, FSX_READ_CAP) 506 if n <= 0 { return 0 - 1 } 507 if n == FSX_READ_CAP { return 0 - (3 as i64) } // file at/over the edit cap: refuse rather than corrupt 508 let ol: i64 = vw_slen(olds) 509 let cnt: i64 = fsx_count_occ(buf, n, olds, ol) 510 if cnt == 0 { return 0 - FSX_RC_NOMATCH } 511 if cnt > 1 { if allf == 0 { return 0 - FSX_RC_AMBIG } } 512 let out: *u8 = sys_mmap(FSX_EDIT_OUT) 513 let nn: i64 = fsx_replace(buf, n, olds, ol, news, vw_slen(news), out, FSX_EDIT_OUT, allf) 514 if nn < 0 { return 0 - (3 as i64) } 515 let w: i64 = fsx_write(path, out, nn) 516 if w < 0 { return w } 517 return nn 518} 519 520// ls: one entry per line "<t> <name>" (t: d=dir f=file l=link o=other; . and .. skipped). 521// Returns entry count; -1 if the dir cannot be opened. 522// PAGING (2026-08-05). The cap was always honest -- it declared total= and truncated=1 -- but an 523// honest refusal is not access: knowledge/status/ holds 1027 entries, so 827 of them were simply 524// UNREACHABLE through this tool, and a worker that listed it reported "queue empty" over a job that 525// was sitting right there. u2605u2605u2605u2605u2605DECLARING A TRUNCATION IS NOT THE SAME AS OFFERING A WAY PAST IT -- 526// a loud cap with no next page is still a wall. `skip` is that way past. 527// Contract preserved exactly (rule 19): fsx_ls(dir) keeps its old signature and behaviour. 528func fsx_ls(dir: *u8) -> i64 { return fsx_ls_from(dir, 0) } 529 530func fsx_ls_from(dir: *u8, skip: i64) -> i64 { 531 let fd: i64 = sys_openat_rd(dir) 532 if fd < 0 { return fsx_fail(dir, fd) } 533 let dbuf: *u8 = sys_mmap(FSX_DENT_BUF) 534 var cnt: i64 = 0 535 var shown: i64 = 0 536 var run: i64 = 1 537 while run == 1 { 538 let n: i64 = sys_getdents64(fd, dbuf, FSX_DENT_BUF) 539 if n <= 0 { run = 0 } else { 540 var off: i64 = 0 541 while off < n { 542 let rec: *u8 = ((dbuf as i64 + off) as *u8) 543 let reclen: i64 = dirent_reclen(rec) 544 if reclen <= 0 { off = n } else { 545 let name: *u8 = dirent_name(rec) 546 // skip "." and ".." 547 var isdot: i64 = 0 548 if fsx_seq(name, "." as *u8) == 1 { isdot = 1 } 549 if fsx_seq(name, ".." as *u8) == 1 { isdot = 1 } 550 if isdot == 0 { 551 if cnt >= skip { if shown < FSX_LS_CAP { 552 let t: i64 = dirent_type(rec) 553 if t == DT_DIR { fsx_puts("d " as *u8) } else { 554 if t == DT_REG { fsx_puts("f " as *u8) } else { 555 if t == DT_LNK { fsx_puts("l " as *u8) } else { fsx_puts("o " as *u8) } } } 556 fsx_puts(name) 557 fsx_puts("\n" as *u8) 558 shown = shown + 1 559 } } 560 cnt = cnt + 1 561 } 562 off = off + reclen 563 } 564 } 565 } 566 } 567 sys_close(fd) 568 // SCALE-LAW: cap the emitted list but ALWAYS declare the true total; truncation is LOUD not silent 569 fsx_puts("NX-FS-LS skip=" as *u8) 570 fsx_putn(skip) 571 fsx_puts(" shown=" as *u8) 572 fsx_putn(shown) 573 fsx_puts(" total=" as *u8) 574 fsx_putn(cnt) 575 // u26a0THE OLD PREDICATE (cnt > shown) BECOMES A LIE THE MOMENT skip EXISTS: the LAST page would 576 // still report truncated=1 forever, so a caller paging until truncated=0 would never stop. 577 // What actually remains is everything past the window just emitted. 578 if cnt > skip + shown { fsx_puts(" truncated=1 (more remain -- next page: ls <dir> " as *u8); fsx_putn(skip + shown); fsx_puts(")\n" as *u8) } else { fsx_puts(" truncated=0\n" as *u8) } 579 return cnt 580}