code wiki / _hdl_build / nx_url_scrub.nx
nx_url_scrub.nx source
↩ module page · 93 lines · 4921 B
1// nx_url_scrub.nx -- ADDITIVE repair for poisoned url:<cid> rows (the 2026-07-03 empty-literal bug stamped
2// every library doc with an unterminated-pool href). Append-only store law: never delete -- SHADOW each bad
3// row with a 0-length value in a fresh segment; the serve layer treats ul==0 as "no url" and falls back to
4// the /doc view. usage: nx_url_scrub <domain> <bad-prefix> (scrubs rows whose CURRENT value starts with
5// bad-prefix; idempotent -- an already-shadowed row has length 0 and never matches). license_tier: ORIGINAL
6import "nx_docportal_search_seg.nx"
7import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
8
9func us_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
10// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
11// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
12// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
13// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
14func us_num(v: i64) -> i64 { nxi_out(v); return 0 }
15func us_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
16
17func main(argc: i64, argv: *i64) -> i64 {
18 if argc < 3 { us_puts("usage: nx_url_scrub <domain> <bad-prefix>\n" as *u8); return 1 }
19 let domain: *u8 = argv[1] as *u8
20 let bad: *u8 = argv[2] as *u8
21 let badn: i64 = us_len(bad)
22 let prefix: *u8 = sys_mmap(512)
23 dss_prefix(domain, prefix)
24 let h: *i64 = ss_open(prefix)
25 if (h as i64) == 0 { us_puts("no shard for domain\n" as *u8); return 2 }
26 // fresh segid for the shadow segment
27 let segs: *i64 = sys_mmap(256 * 8) as *i64
28 let nseg: i64 = ss_manifest_file(prefix, "manifest.txt" as *u8, segs)
29 var segid: i64 = 1
30 var si: i64 = 0
31 // ROOT FIX seq1730 (id 1785450987): segs[] holds POINTERS to seg-<id> name strings
32 // (nx_seg_store.nx:1234 stores segs[cnt] = name as i64), NOT ids -- so segs[si] + 1 produced
33 // MMAP_ADDRESS+1 (~1.4e14), the pointer-shaped poison that PINS a plane forever: every later
34 // epoch id sorts BELOW it in supersede order and its rows are silently shadowed while rc=0.
35 // Parse the DIGITS, as nx_web_shard_compact.nx:55-58 already does on this SAME array.
36 while si < nseg {
37 let sg_nm: *u8 = segs[si] as *u8
38 var sg_v: i64 = 0
39 var sg_ci: i64 = 0
40 while sg_nm[sg_ci] != (0 as u8) { let sg_c: i64 = sg_nm[sg_ci] as i64; if sg_c >= 48 { if sg_c <= 57 { sg_v = sg_v * 10 + (sg_c - 48) } } sg_ci = sg_ci + 1 }
41 if sg_v >= segid { segid = sg_v + 1 }
42 si = si + 1
43 }
44 // walk every segment's key index; url:-keys whose CURRENT value starts with bad-prefix get shadowed
45 let ns: i64 = h[0]
46 let keybuf: *u8 = sys_mmap(600)
47 let vp: *i64 = sys_mmap(16) as *i64
48 let vl: *i64 = sys_mmap(16) as *i64
49 let w: *i64 = ss_begin()
50 var scrubbed: i64 = 0
51 var s: i64 = 0
52 while s < ns {
53 let kb: *u8 = h[1 + 8 * s] as *u8
54 if h[2 + 8 * s] >= 8 {
55 let m9: i64 = ss_r32(kb, 4)
56 var e9: i64 = 0
57 while e9 < m9 {
58 let eo: i64 = 8 + 4 * m9 + ss_r32(kb, 8 + 4 * e9)
59 if (kb[eo] as i64) == 1 {
60 let kl9: i64 = ss_r32(kb, eo + 1)
61 if kl9 >= 4 { if kl9 < 590 {
62 if kb[eo + 5] == (117 as u8) { if kb[eo + 6] == (114 as u8) { if kb[eo + 7] == (108 as u8) { if kb[eo + 8] == (58 as u8) {
63 var c: i64 = 0
64 while c < kl9 { keybuf[c] = kb[eo + 5 + c]; c = c + 1 }
65 keybuf[kl9] = 0 as u8
66 if ss_hget(h, keybuf, vp, vl) == 1 {
67 if vl[0] >= badn { if badn > 0 {
68 var mm: i64 = 1
69 var x: i64 = 0
70 let vb: *u8 = vp[0] as *u8
71 while x < badn { if vb[x] != bad[x] { mm = 0; x = badn } else { x = x + 1 } }
72 if mm == 1 {
73 ss_add(w, 1, keybuf, keybuf, 0)
74 scrubbed = scrubbed + 1
75 }
76 } }
77 }
78 } } } }
79 } }
80 }
81 e9 = e9 + 1
82 }
83 }
84 s = s + 1
85 }
86 if scrubbed > 0 {
87 if ss_commit(prefix, w, segid) != 0 { us_puts("COMMIT-FAIL\n" as *u8); return 3 }
88 }
89 us_puts("url-scrub: domain=" as *u8); us_puts(domain)
90 us_puts(" scrubbed=" as *u8); us_num(scrubbed)
91 us_puts(" (shadowed with empty values; serve falls back to /doc)\n" as *u8)
92 return 0
93}