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}