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}