code wiki / _hdl_build / nx_mtime_probe.nx

nx_mtime_probe.nx source

↩ module page · 65 lines · 3913 B

1// nx_mtime_probe.nx -- PROVES THE MISSING PRIMITIVE: file MODIFICATION TIME, which this ecosystem cannot 2// currently ask for at all. MEASURED 2026-07-30: `sys_stat` and `statx` have ZERO matches across all 3// 19,813 tree files, so no organ can answer "is this file newer than that one?". That absence is why 4// /api/unpack cannot refuse to BACKDATE a canonical source (seq1379 half-2, the path that reverted 5// nx_hostctl.nx), why nx_stale_check must infer staleness indirectly, and why the GPU lane had to bank 6// the lesson "a file can GROW and still be OLDER" -- they had no clock to consult, only a size. 7// 8// MECHANISM (no core-file surgery): syscall wrappers can be declared LOCALLY via the __syscall intrinsic 9// -- nx_movie_probe.nx already does exactly this for lseek. So the primitive costs ZERO blast radius on 10// nx_syscalls.nx, the tree's most load-bearing file (~14k importers). 11// newfstatat(AT_FDCWD, path, statbuf, 0) = syscall 262 on x86-64; in `struct stat` st_mtim.tv_sec sits at 12// BYTE OFFSET 88 (dev 0, ino 8, nlink 16, mode 24, uid 28, gid 32, pad 36, rdev 40, size 48, blksize 56, 13// blocks 64, atime 72, atime_ns 80, mtime 88). ⚠That offset is the ONLY assumption here, so this organ 14// EXISTS to falsify it: it prints size (offset 48, independently checkable) beside mtime, and the caller 15// compares both against `ls`. An offset that is wrong shows up instantly as a garbage epoch. 16// nx_mtime_probe <path> [<path2>] -> "MTIME <epoch> SIZE <bytes> <path>" per arg; NEWER=<path> verdict 17// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 18import "nx_syscalls.nx" 19 20// derived: AT_FDCWD is -100 by the kernel ABI; struct stat is 144 bytes on x86-64, 256 is generous headroom 21const MP_AT_FDCWD: i64 = 0 - 100 22const MP_SYS_NEWFSTATAT: i64 = 262 23const MP_STATBUF: i64 = 256 24const MP_OFF_SIZE: i64 = 48 25const MP_OFF_MTIME: i64 = 88 26 27func mp_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 28func mp_n(v: i64) -> i64 { 29 if v == 0 { mp_w("0" as *u8); return 0 } 30 var x: i64 = v 31 if x < 0 { mp_w("-" as *u8); x = 0 - x } 32 let b: *u8 = sys_mmap(32) 33 var i: i64 = 0 34 while x > 0 { b[i] = ((x % 10) + 48) as u8; x = x / 10; i = i + 1 } 35 while i > 0 { i = i - 1; sys_write(1, ((b as i64) + i) as *u8, 1) } 36 return 0 37} 38 39// 1 = stat ok (mtime->outs[0], size->outs[1]) · 0 = absent/unreadable. Fail-safe: never invents a time. 40func mp_stat(path: *u8, outs: *i64) -> i64 { 41 let sb: *u8 = sys_mmap(MP_STATBUF) 42 let rc: i64 = __syscall(MP_SYS_NEWFSTATAT, MP_AT_FDCWD, path as i64, sb as i64, 0, 0, 0) 43 if rc != 0 { return 0 } 44 let mp: *i64 = ((sb as i64) + MP_OFF_MTIME) as *i64 45 let sp: *i64 = ((sb as i64) + MP_OFF_SIZE) as *i64 46 outs[0] = mp[0] 47 outs[1] = sp[0] 48 return 1 49} 50 51func main(argc: i64, argv: *i64) -> i64 { 52 if argc < 2 { mp_w("usage: nx_mtime_probe <path> [<path2>]\n" as *u8); sys_exit(2); return 2 } 53 let o1: *i64 = sys_mmap(32) as *i64 54 let o2: *i64 = sys_mmap(32) as *i64 55 if mp_stat(argv[1] as *u8, o1) == 0 { mp_w("ABSENT " as *u8); mp_w(argv[1] as *u8); mp_w("\n" as *u8); sys_exit(3); return 3 } 56 mp_w("MTIME " as *u8); mp_n(o1[0]); mp_w(" SIZE " as *u8); mp_n(o1[1]); mp_w(" " as *u8); mp_w(argv[1] as *u8); mp_w("\n" as *u8) 57 if argc < 3 { return 0 } 58 if mp_stat(argv[2] as *u8, o2) == 0 { mp_w("ABSENT " as *u8); mp_w(argv[2] as *u8); mp_w("\n" as *u8); sys_exit(3); return 3 } 59 mp_w("MTIME " as *u8); mp_n(o2[0]); mp_w(" SIZE " as *u8); mp_n(o2[1]); mp_w(" " as *u8); mp_w(argv[2] as *u8); mp_w("\n" as *u8) 60 // the verdict the whole class needs: which one is NEWER (the question unpack must ask before overwriting) 61 if o1[0] > o2[0] { mp_w("NEWER=arg1 (arg2 would BACKDATE it)\n" as *u8) } else { 62 if o2[0] > o1[0] { mp_w("NEWER=arg2\n" as *u8) } else { mp_w("NEWER=same-second\n" as *u8) } 63 } 64 return 0 65}