code wiki / _hdl_build / nx_treepack_overwrite_gate.nx

nx_treepack_overwrite_gate.nx source

↩ module page · 141 lines · 9141 B

1// nx_treepack_overwrite_gate.nx -- regression gate for the seq142/seq124 STALE-SOURCE root: nx_treepack unpack 2// must OVERWRITE existing destination files even when the existing file denies O_TRUNC (mixed-ownership / 3// read-only buildroot survivors), by unlink-then-recreate; and must FAIL LOUD (nonzero exit + named path) when 4// even that is impossible. Runs the REAL /tmp/nx_treepack.sov.elf as a child -- gates the shipped binary, not a 5// reimplementation. T1 fresh round-trip / T2 plain overwrite / T3 read-only-file overwrite (THE stale-source 6// repro) / T4 unwritable-dir = loud failure, old bytes intact. 7// license_tier: ORIGINAL expect_exit: 0 8import "nx_syscalls.nx" 9 10func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 11func g_pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 } 12func g_chmod(path: *u8, mode: i64) -> i64 { return sys_fchmodat(path, mode) } 13func g_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 14func g_wfile(path: *u8, content: *u8) -> i64 { 15 let fd: i64=sys_openat_wr(path, 0x1a4) 16 if fd<0 { return 0-1 } 17 sys_write(fd, content, g_slen(content)) 18 sys_close(fd) 19 return 0 20} 21// 1 iff file at path holds exactly `want` 22func g_expect(path: *u8, want: *u8) -> i64 { 23 let szp: *i64=sys_mmap(16) as *i64 24 let rb: *u8=sys_read_file(path, szp) 25 if (rb as i64)==0 { return 0 } 26 let wl: i64=g_slen(want) 27 if szp[0]!=wl { return 0 } 28 var i: i64=0 29 while i<wl { if rb[i]!=want[i] { return 0 } i=i+1 } 30 return 1 31} 32// fork+exec the real treepack elf; returns child exit code (or 200+ on plumbing failure) 33func g_run(a1: *u8, a2: *u8, a3: *u8) -> i64 { 34 let av: *i64=sys_mmap(64) as *i64 35 av[0]="/tmp/nx_treepack.sov.elf\x00" as *u8 as i64 36 av[1]=a1 as i64; av[2]=a2 as i64; av[3]=a3 as i64; av[4]=0 37 let pid: i64=sys_fork() 38 if pid<0 { return 201 } 39 if pid==0 { 40 sys_execve("/tmp/nx_treepack.sov.elf\x00" as *u8, av, 0 as *i64) 41 sys_exit(127) 42 return 127 43 } 44 let stp: *i64=sys_mmap(16) as *i64 45 sys_wait4(pid, stp, 0) 46 return (stp[0]>>8)&255 47} 48// same, but RELATIVE to a working dir -- the case production actually uses 49func g_run_in(dir: *u8, a1: *u8, a2: *u8, a3: *u8) -> i64 { 50 let av: *i64=sys_mmap(64) as *i64 51 av[0]="/tmp/nx_treepack.sov.elf\x00" as *u8 as i64 52 av[1]=a1 as i64; av[2]=a2 as i64; av[3]=a3 as i64; av[4]=0 53 let pid: i64=sys_fork() 54 if pid<0 { return 201 } 55 if pid==0 { 56 sys_chdir(dir) 57 sys_execve("/tmp/nx_treepack.sov.elf\x00" as *u8, av, 0 as *i64) 58 sys_exit(127) 59 return 127 60 } 61 let stp: *i64=sys_mmap(16) as *i64 62 sys_wait4(pid, stp, 0) 63 return (stp[0]>>8)&255 64} 65 66func main() -> i64 { 67 g_puts("=== nx_treepack_overwrite_gate -- unpack must overwrite or fail LOUD (seq142/seq124 root) ===\n" as *u8) 68 var fails: i64=0 69 // arena 70 sys_mkdir("/tmp/tpog_src\x00" as *u8, 0x1ed) 71 sys_mkdir("/tmp/tpog_src/sub\x00" as *u8, 0x1ed) 72 // make prior-run residue writable again so re-runs are idempotent 73 g_chmod("/tmp/tpog_dst/sub\x00" as *u8, 0x1ed) 74 g_chmod("/tmp/tpog_dst/sub/b.txt\x00" as *u8, 0x1a4) 75 g_chmod("/tmp/tpog_dst/a.txt\x00" as *u8, 0x1a4) 76 g_wfile("/tmp/tpog_src/a.txt\x00" as *u8, "OLD-ALPHA\x00" as *u8) 77 g_wfile("/tmp/tpog_src/sub/b.txt\x00" as *u8, "OLD-BRAVO\x00" as *u8) 78 // T1 fresh round-trip 79 var rc: i64=g_run("pack\x00" as *u8, "/tmp/tpog_src\x00" as *u8, "/tmp/tpog.pack\x00" as *u8) 80 var rc2: i64=g_run("unpack\x00" as *u8, "/tmp/tpog.pack\x00" as *u8, "/tmp/tpog_dst\x00" as *u8) 81 var t1: i64=0 82 if rc==0 { if rc2==0 { if g_expect("/tmp/tpog_dst/a.txt\x00" as *u8, "OLD-ALPHA\x00" as *u8)==1 { if g_expect("/tmp/tpog_dst/sub/b.txt\x00" as *u8, "OLD-BRAVO\x00" as *u8)==1 { t1=1 } } } } 83 if t1==1 { g_puts("T1 PASS fresh round-trip byte-exact\n" as *u8) } else { fails=fails+1; g_puts("T1 FAIL rc="); g_pn(rc); g_puts(" rc2="); g_pn(rc2); g_puts("\n" as *u8) } 84 // T2 plain overwrite of an existing writable dest 85 g_wfile("/tmp/tpog_src/a.txt\x00" as *u8, "NEW-ALPHA-TWO\x00" as *u8) 86 rc=g_run("pack\x00" as *u8, "/tmp/tpog_src\x00" as *u8, "/tmp/tpog.pack\x00" as *u8) 87 rc2=g_run("unpack\x00" as *u8, "/tmp/tpog.pack\x00" as *u8, "/tmp/tpog_dst\x00" as *u8) 88 var t2: i64=0 89 if rc==0 { if rc2==0 { if g_expect("/tmp/tpog_dst/a.txt\x00" as *u8, "NEW-ALPHA-TWO\x00" as *u8)==1 { t2=1 } } } 90 if t2==1 { g_puts("T2 PASS plain overwrite carries new bytes\n" as *u8) } else { fails=fails+1; g_puts("T2 FAIL\n" as *u8) } 91 // T3 THE stale-source repro: existing dest file READ-ONLY -> unlink+recreate must still land new bytes 92 let cm3: i64=g_chmod("/tmp/tpog_dst/a.txt\x00" as *u8, 0x124) 93 if cm3!=0 { fails=fails+1; g_puts("T3-SETUP FAIL chmod rc="); g_pn(cm3); g_puts("\n" as *u8) } 94 g_wfile("/tmp/tpog_src/a.txt\x00" as *u8, "NEW-ALPHA-THREE\x00" as *u8) 95 rc=g_run("pack\x00" as *u8, "/tmp/tpog_src\x00" as *u8, "/tmp/tpog.pack\x00" as *u8) 96 rc2=g_run("unpack\x00" as *u8, "/tmp/tpog.pack\x00" as *u8, "/tmp/tpog_dst\x00" as *u8) 97 var t3: i64=0 98 if rc==0 { if rc2==0 { if g_expect("/tmp/tpog_dst/a.txt\x00" as *u8, "NEW-ALPHA-THREE\x00" as *u8)==1 { t3=1 } } } 99 if t3==1 { g_puts("T3 PASS read-only existing file OVERWRITTEN via unlink+recreate (stale-source class dead)\n" as *u8) } else { fails=fails+1; g_puts("T3 FAIL rc2="); g_pn(rc2); g_puts(" (stale bytes survived = the seq142 bug)\n" as *u8) } 100 // T4 fail LOUD: read-only file inside an unwritable DIR (open denied AND unlink denied) -> nonzero exit, old bytes 101 let cm4a: i64=g_chmod("/tmp/tpog_dst/sub/b.txt\x00" as *u8, 0x124) 102 let cm4b: i64=g_chmod("/tmp/tpog_dst/sub\x00" as *u8, 0x16d) 103 if cm4a!=0 { fails=fails+1; g_puts("T4-SETUP FAIL chmod file rc="); g_pn(cm4a); g_puts("\n" as *u8) } 104 if cm4b!=0 { fails=fails+1; g_puts("T4-SETUP FAIL chmod dir rc="); g_pn(cm4b); g_puts("\n" as *u8) } 105 g_wfile("/tmp/tpog_src/sub/b.txt\x00" as *u8, "NEW-BRAVO-FOUR\x00" as *u8) 106 rc=g_run("pack\x00" as *u8, "/tmp/tpog_src\x00" as *u8, "/tmp/tpog.pack\x00" as *u8) 107 rc2=g_run("unpack\x00" as *u8, "/tmp/tpog.pack\x00" as *u8, "/tmp/tpog_dst\x00" as *u8) 108 var t4: i64=0 109 if rc==0 { if rc2!=0 { if g_expect("/tmp/tpog_dst/sub/b.txt\x00" as *u8, "OLD-BRAVO\x00" as *u8)==1 { t4=1 } } } 110 if t4==1 { g_puts("T4 PASS unwritable dir = LOUD nonzero exit, old bytes intact (no silent skip)\n" as *u8) } else { fails=fails+1; g_puts("T4 FAIL rc2="); g_pn(rc2); g_puts("\n" as *u8) } 111 g_chmod("/tmp/tpog_dst/sub\x00" as *u8, 0x1ed) 112 g_chmod("/tmp/tpog_dst/sub/b.txt\x00" as *u8, 0x1a4) 113 g_chmod("/tmp/tpog_dst/a.txt\x00" as *u8, 0x1a4) 114 115 // ---- T5 RELATIVE-PATH overwrite. THE production case, and the one this gate previously never covered: 116 // every /api/unpack lands on paths relative to the NAS cwd. A latent AT_FDCWD miscompile (the const was 117 // declared BELOW its callers, so nx_cc substituted 0 = stdin) made unlinkat pass the wrong dirfd -- which 118 // ABSOLUTE paths silently tolerate and RELATIVE paths do not. An absolute-only gate proves nothing about 119 // the path shape production actually uses. 120 sys_mkdir("/tmp/tpogrel\x00" as *u8, 0x1ed) 121 sys_mkdir("/tmp/tpogrel/rsrc\x00" as *u8, 0x1ed) 122 sys_mkdir("/tmp/tpogrel/rdst\x00" as *u8, 0x1ed) 123 g_chmod("/tmp/tpogrel/rdst/r.txt\x00" as *u8, 0x1a4) 124 g_wfile("/tmp/tpogrel/rsrc/r.txt\x00" as *u8, "REL-OLD\x00" as *u8) 125 g_run_in("/tmp/tpogrel\x00" as *u8, "pack\x00" as *u8, "rsrc\x00" as *u8, "rel.pack\x00" as *u8) 126 g_run_in("/tmp/tpogrel\x00" as *u8, "unpack\x00" as *u8, "rel.pack\x00" as *u8, "rdst\x00" as *u8) 127 // now make the destination READ-ONLY and overwrite it through RELATIVE paths 128 g_chmod("/tmp/tpogrel/rdst/r.txt\x00" as *u8, 0x124) 129 g_wfile("/tmp/tpogrel/rsrc/r.txt\x00" as *u8, "REL-NEW-OVERWRITE\x00" as *u8) 130 let rcp: i64=g_run_in("/tmp/tpogrel\x00" as *u8, "pack\x00" as *u8, "rsrc\x00" as *u8, "rel.pack\x00" as *u8) 131 let rcu: i64=g_run_in("/tmp/tpogrel\x00" as *u8, "unpack\x00" as *u8, "rel.pack\x00" as *u8, "rdst\x00" as *u8) 132 var t5: i64=0 133 if rcp==0 { if rcu==0 { if g_expect("/tmp/tpogrel/rdst/r.txt\x00" as *u8, "REL-NEW-OVERWRITE\x00" as *u8)==1 { t5=1 } } } 134 if t5==1 { g_puts("T5 PASS RELATIVE-path read-only overwrite works (AT_FDCWD reaches unlinkat correctly -- the production path shape)\n" as *u8) } else { fails=fails+1; g_puts("T5 FAIL relative overwrite rcp="); g_pn(rcp); g_puts(" rcu="); g_pn(rcu); g_puts(" -- unlinkat dirfd is wrong for relative paths\n" as *u8) } 135 g_chmod("/tmp/tpogrel/rdst/r.txt\x00" as *u8, 0x1a4) 136 137 if fails==0 { g_puts("TREEPACK-OVERWRITE GREEN -- unpack overwrites or fails loud; the silent stale-source class is dead\n" as *u8); sys_exit(0); return 0 } 138 g_puts("TREEPACK-OVERWRITE RED fails="); g_pn(fails); g_puts("\n" as *u8) 139 sys_exit(1) 140 return 1 141}