nx_fsops_lib.nx
buildroot/runtime/nx_fsops_lib.nx
about
nx_fsops_lib.nx -- CONSOLIDATED filesystem tool (MCP name: nx_fs, tool #4 of the 15), LIBRARY half.
(Source is named nx_fsops because nx_fs.nx is the safety-enveloped file-I/O STDLIB -- a different thing.)
READ-ONLY first increment: `read` (bounded file read) + `ls` (typed dir listing). Retires ssh-cat for
remote reads per rule 27 (api-first, no shell plumbing).
BOUNDARY DEFENSE (rule 12 -- MCP callers are EXTERNAL input): `read` REFUSES any path that matches the
secret DENY-LIST: compiled-in default needles (secret/key/token/passw/.pem, matched case-insensitively
against the WHOLE path) plus data-driven extras from fs_read_deny.conf (one lowercase needle per line,
CWD-relative -- rule 11: policy in data, not code). The tools-api runs where key material lives; an
arbitrary-read tool that could return opaque_keys.bin or tools_cap_secret.key would convert a read-cap
into a key-theft primitive. Over-blocking is the SAFE failure direction for v1.
WRITE/EDIT increment (2026-07-16): fsx_write (ATOMIC tmp+fsync+rename) + fsx_edit (exact-string replace
with the Claude-Edit UNIQUENESS contract). Exposed as the SEPARATE tools-api name `nx_fs_write` (its own
cap class per knowledge/mcp/exposure_policy.txt: read=broad, write=cap) -- the `nx_fs` name stays read-only.
The write DENY is a superset of the read deny (never clobber key material) PLUS the OS device/kernel/
firmware namespace via the nx_os_fs seam (rule 26 never-brick BY CONSTRUCTION -- not config-disableable)
PLUS the tool-registry escalation surface ("allowlist") PLUS data-driven extras (fs_write_deny.conf).
license_tier: ORIGINAL
dependencies 5 imports · 5 importers
imports: nx_syscalls.nxnx_itoa_lib.nxnx_vsz_watchdog_core.nxnx_os_fs.nxnx_os_proc.nx
imported by: nx_fsops.nxnx_fsops_gate.nxnx_fsops_outline.nxnx_fsops_write.nxnx_site_publish_lib.nx
structs
| none |
consts
| 24 | const FSX_MAGIC_4095: i64 = 4095 |
| 26 | const FSX_READ_CAP: i64 = 1048576 // max bytes returned by `read` (truncation is MARKED, never silent) |
| 27 | const FSX_DENY_CAP: i64 = 8192 // fs_read_deny.conf read cap |
| 28 | const FSX_PATH_CAP: i64 = 1024 // lowercased path work buffer |
| 29 | const FSX_DENT_BUF: i64 = 65536 // getdents64 batch buffer (matches the proven vsz/heal sizing) |
| 30 | const FSX_LS_CAP: i64 = 200 // scale-law: max ls entries EMITTED; true total ALWAYS declared (65KB-dump fix) |
| 31 | const FSX_RC_ABSENT: i64 = 3 // exit: path absent/unreadable (mirrors nx_fileop's exists convention) |
| 32 | const FSX_RC_DENIED: i64 = 5 // exit: deny-list refused the read |
| 33 | const FSX_UPPER_A: i64 = 65 // 'A' (ASCII lowercasing) |
| 34 | const FSX_UPPER_Z: i64 = 90 // 'Z' |
| 35 | const FSX_CASE_OFF: i64 = 32 // 'a' - 'A' |
| 36 | const FSX_ASCII_0: i64 = 48 // '0' (decimal print) |
| 121 | const FSX_SNIFF_CAP: i64 = 4096 |
| 318 | const FSX_LINES_SCAN: i64 = 1048576 // line-addressing scan window (matches the proven read cap) |
| 319 | const FSX_LINES_MAXOUT: i64 = 262144 // max bytes emitted by one `lines` call (transport-friendly) |
| 320 | const FSX_LINES_DEFN: i64 = 40 // default line count when the caller omits it |
| 321 | const FSX_LINES_MAXN: i64 = 400 // max lines per call |
| 385 | const FSX_MODE_RW: i64 = 0x1a4 // 0644 -- the ecosystem's file-create mode idiom |
| 386 | const FSX_DEC: i64 = 10 // decimal base (pid rendering in the tmp suffix) |
| 387 | const FSX_EDIT_OUT: i64 = 2097152 // edit output buffer (2x read cap: bounded replacement growth) |
| 388 | const FSX_TMP_ROOM: i64 = 32 // reserved room for ".nxw" + pid digits + NUL in the tmp name |
| 389 | const FSX_RC_IO: i64 = 4 // exit: io failure (open/short-write/rename) |
| 390 | const FSX_RC_NOMATCH: i64 = 6 // exit: edit found 0 occurrences (file UNCHANGED) |
| 391 | const FSX_RC_AMBIG: i64 = 7 // exit: edit found >1 occurrences without `all` (file UNCHANGED) |
functions
| 38 | func 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 } |
| 43 | func fsx_putn(v: i64) -> i64 { nxi_out(v); return 0 } |
| 45 | func fsx_lower(s: *u8, out: *u8, cap: i64) -> i64 |
| 58 | func 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 } |
| 69 | func fsx_cas_val(tok: *u8) -> *u8 called by 1: fsx_cas_ok |
| 77 | func fsx_cas_ok(cur: i64, tok: *u8) -> i64 |
| 93 | func fsx_deny_hit(lp: *u8, ln: i64, needle: *u8) -> i64 |
| 100 | func fsx_conf_deny(lp: *u8, ln: i64, conf: *u8) -> i64 |
| 123 | func fsx_isalnum(c: i64) -> i64 called by 1: fsx_word_has |
| 130 | func fsx_ends_with(lp: *u8, ln: i64, suf: *u8) -> i64 |
| 142 | func fsx_basename_is(lp: *u8, ln: i64, name: *u8) -> i64 |
| 156 | func fsx_word_has(lp: *u8, ln: i64, w: *u8) -> i64 |
| 181 | func fsx_content_secret(path: *u8) -> i64 |
| 213 | func fsx_denied(path: *u8) -> i64 |
| 257 | func fsx_fail(path: *u8, rc: i64) -> i64 |
| 270 | func fsx_read(path: *u8, cap: i64) -> i64 |
| 292 | func fsx_read_at(path: *u8, cap: i64, off: i64) -> i64 |
| 329 | func fsx_read_lines(path: *u8, start: i64, count: i64) -> i64 |
| 395 | func fsx_write_denied(path: *u8) -> i64 called by 2: fsx_writefsx_edit calls 6: fsx_deniedosf_write_forbiddensys_mmapfsx_lowerfsx_deny_hitfsx_conf_deny |
| 406 | func fsx_write(path: *u8, body: *u8, blen: i64) -> i64 called by 4: fg_putfsx_editmainsp_main calls 12: fsx_write_deniedfsx_putsvw_slensys_mmaposp_selfpidsys_openat_wr+6 |
| 452 | func fsx_count_occ(hay: *u8, hn: i64, nee: *u8, nl: i64) -> i64 called by 1: fsx_edit |
| 465 | func fsx_replace(hay: *u8, hn: i64, nee: *u8, nl: i64, rep: *u8, rl: i64, out: *u8, ocap: i64, allf: i64) -> i64 called by 1: fsx_edit |
| 499 | func fsx_edit(path: *u8, olds: *u8, news: *u8, allf: i64) -> i64 |
| 528 | func fsx_ls(dir: *u8) -> i64 { return fsx_ls_from(dir, 0) } |
| 530 | func fsx_ls_from(dir: *u8, skip: i64) -> i64 called by 2: mainfsx_ls calls 11: sys_openat_rdfsx_failsys_mmapsys_getdents64dirent_reclendirent_name+5 |