code wiki / _hdl_build / nx_ark_snapshot.nx

nx_ark_snapshot.nx source

↩ module page · 345 lines · 12595 B

1// nx_ark_snapshot.nx -- X-REP-005 (CALLOUT-002 REP-1): the ARK. Packs the 2// data-driven survival set (knowledge/registry/ark_set.tsv) into ONE file 3// with a CID manifest -- the dead-laptop seed's first organ. 4// snap (default): read every set row -> entry CIDs -> manifest text. If 5// the manifest is BYTE-IDENTICAL to the existing one, SKIP the pack 6// write entirely (unchanged=1; the resource-conscious beat is a read 7// pass). Else write pack.new + manifest.new and renameat both (atomic; 8// the lane's temp-rename discipline). 9// verify: walk the pack against the manifest -- entry count, path, size, 10// recomputed CID must all match; any mismatch = named row + RED. An 11// ark that cannot prove itself is hope, not backup. 12// Pack format NXARK1: "NXARK1\n" then per entry "ENT <pathlen> <bytes> 13// <cid>\n" + path bytes + raw content. Manifest rows are DETERMINISTIC 14// (no epoch -- epoch lives in the ARKSNAP log row only) so unchanged 15// detection is a byte compare. 16// argv[1]=mode snap|verify argv[2]=set argv[3]=pack argv[4]=manifest 17// Evidence: ARKSNAP / ARKVERIFY rows -> stdout + knowledge/status/ark.log. 18// license_tier: ORIGINAL 19 20import "nx_syscalls.nx" 21const ARK_MAGIC_65536: i64 = 65536 22const ARK_MAGIC_1469598103: i64 = 1469598103 23const ARK_MAGIC_262144: i64 = 262144 24 25const ARK_PACKCAP: i64 = 16777216 26const ARK_MAXENT: i64 = 128 27 28func ak_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 29func ak_wn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; sys_write(fd,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 30 31func ak_cat(dst: *u8, off: i64, s: *u8) -> i64 { 32 var i: i64 = 0 33 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 34 dst[off + i] = 0 as u8 35 return off + i 36} 37 38func ak_catn(dst: *u8, off: i64, v: i64) -> i64 { 39 var m: i64 = v 40 if m < 0 { m = 0 } 41 let t: *u8 = sys_mmap(28) 42 var k: i64 = 0 43 if m == 0 { t[0] = 48 as u8; k = 1 } 44 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 45 var o: i64 = off 46 var i: i64 = 0 47 while i < k { dst[o] = t[k - 1 - i]; o = o + 1; i = i + 1 } 48 dst[o] = 0 as u8 49 return o 50} 51 52func ak_readall(path: *u8, szout: *i64) -> *u8 { 53 let fd: i64 = sys_openat_rd(path) 54 if fd < 0 { szout[0] = 0 - 1; return 0 as *u8 } 55 let sz: i64 = sys_lseek(fd, 0, 2) 56 sys_lseek(fd, 0, 0) 57 let buf: *u8 = sys_mmap(sz + 64) 58 var got: i64 = 0 59 var n: i64 = 1 60 while n > 0 { 61 n = sys_read(fd, (buf as i64 + got) as *u8, ARK_MAGIC_65536) 62 if n > 0 { got = got + n } 63 } 64 sys_close(fd) 65 szout[0] = got 66 return buf 67} 68 69func ak_fnv(b: *u8, n: i64) -> i64 { 70 var h: i64 = ARK_MAGIC_1469598103 71 var i: i64 = 0 72 while i < n { h = (h * 131 + (b[i] as i64)) & 0x7fffffffffff; i = i + 1 } 73 return h 74} 75 76func ak_writefile(path: *u8, b: *u8, n: i64) -> i64 { 77 let fd: i64 = sys_openat_wr(path, 0x1a4) 78 if fd < 0 { return 0 } 79 var off: i64 = 0 80 while off < n { 81 let w: i64 = sys_write(fd, b + off, n - off) 82 if w <= 0 { sys_close(fd); return 0 } 83 off = off + w 84 } 85 sys_fsync(fd) 86 sys_close(fd) 87 return 1 88} 89 90// parse set rows into arena (256B/slot: path NUL note-ignored); returns count 91func ak_set(setpath: *u8, arena: *u8) -> i64 { 92 let szp: *i64 = sys_mmap(16) as *i64 93 let b: *u8 = ak_readall(setpath, szp) 94 let n: i64 = szp[0] 95 if n <= 0 { return 0 } 96 var cnt: i64 = 0 97 var i: i64 = 0 98 while i < n { 99 var le: i64 = i 100 var go: i64 = 1 101 while go == 1 { 102 if le >= n { go = 0 } else { 103 if b[le] == (10 as u8) { go = 0 } else { le = le + 1 } 104 } 105 } 106 if le > i { 107 if b[i] != (35 as u8) { 108 if cnt < ARK_MAXENT { 109 let dst: *u8 = (arena as i64 + cnt * 256) as *u8 110 var k: i64 = 0 111 var p: i64 = i 112 var go2: i64 = 1 113 while go2 == 1 { 114 if p >= le { go2 = 0 } else { 115 if b[p] == (9 as u8) { go2 = 0 } else { 116 if k < 255 { dst[k] = b[p]; k = k + 1 } 117 p = p + 1 118 } 119 } 120 } 121 dst[k] = 0 as u8 122 if k > 0 { cnt = cnt + 1 } 123 } 124 } 125 } 126 i = le + 1 127 } 128 return cnt 129} 130 131// build manifest text for the CURRENT sources; returns length (neg on miss) 132func ak_manifest_build(arena: *u8, cnt: i64, mb: *u8) -> i64 { 133 var o: i64 = ak_cat(mb, 0, "ARKMANIFEST v1 entries=" as *u8) 134 o = ak_catn(mb, o, cnt) 135 o = ak_cat(mb, o, "\n" as *u8) 136 let szp: *i64 = sys_mmap(16) as *i64 137 var e: i64 = 0 138 while e < cnt { 139 let path: *u8 = (arena as i64 + e * 256) as *u8 140 let b: *u8 = ak_readall(path, szp) 141 if szp[0] < 0 { 142 ak_w(1, "ARK-MISS path=" as *u8) 143 ak_w(1, path) 144 ak_w(1, "\n" as *u8) 145 return 0 - 1 146 } 147 o = ak_cat(mb, o, "ARKENT path=" as *u8) 148 o = ak_cat(mb, o, path) 149 o = ak_cat(mb, o, " bytes=" as *u8) 150 o = ak_catn(mb, o, szp[0]) 151 o = ak_cat(mb, o, " cid=" as *u8) 152 o = ak_catn(mb, o, ak_fnv(b, szp[0])) 153 o = ak_cat(mb, o, "\n" as *u8) 154 e = e + 1 155 } 156 return o 157} 158 159// SNAP: returns 0 written, 1 unchanged-skip, neg fail 160func ak_snap(arena: *u8, cnt: i64, packp: *u8, manp: *u8) -> i64 { 161 let mb: *u8 = sys_mmap(ARK_MAGIC_262144) 162 let ml: i64 = ak_manifest_build(arena, cnt, mb) 163 if ml < 0 { return 0 - 1 } 164 // unchanged? byte-compare against the existing manifest 165 let oszp: *i64 = sys_mmap(16) as *i64 166 let old: *u8 = ak_readall(manp, oszp) 167 if oszp[0] == ml { 168 var same: i64 = 1 169 var i: i64 = 0 170 while i < ml { 171 if old[i] != mb[i] { same = 0; i = ml } 172 i = i + 1 173 } 174 if same == 1 { return 1 } 175 } 176 // build the pack 177 let pk: *u8 = sys_mmap(ARK_PACKCAP) 178 var po: i64 = ak_cat(pk, 0, "NXARK1\n" as *u8) 179 let szp: *i64 = sys_mmap(16) as *i64 180 var e: i64 = 0 181 while e < cnt { 182 let path: *u8 = (arena as i64 + e * 256) as *u8 183 let b: *u8 = ak_readall(path, szp) 184 if szp[0] < 0 { return 0 - 1 } 185 var pl: i64 = 0 186 while path[pl] != (0 as u8) { pl = pl + 1 } 187 if po + pl + szp[0] + 64 > ARK_PACKCAP { 188 ak_w(1, "ARK-CAP-REFUSED (pack would exceed cap -- raise ARK_PACKCAP deliberately)\n" as *u8) 189 return 0 - 2 190 } 191 po = ak_cat(pk, po, "ENT " as *u8) 192 po = ak_catn(pk, po, pl) 193 po = ak_cat(pk, po, " " as *u8) 194 po = ak_catn(pk, po, szp[0]) 195 po = ak_cat(pk, po, " " as *u8) 196 po = ak_catn(pk, po, ak_fnv(b, szp[0])) 197 po = ak_cat(pk, po, "\n" as *u8) 198 var k: i64 = 0 199 while k < pl { pk[po] = path[k]; po = po + 1; k = k + 1 } 200 k = 0 201 while k < szp[0] { pk[po] = b[k]; po = po + 1; k = k + 1 } 202 e = e + 1 203 } 204 // atomic: .new then rename (pack first, manifest second) 205 let tmpp: *u8 = sys_mmap(512) 206 var to: i64 = ak_cat(tmpp, 0, packp) 207 to = ak_cat(tmpp, to, ".new" as *u8) 208 if ak_writefile(tmpp, pk, po) != 1 { return 0 - 3 } 209 sys_renameat(tmpp, packp) 210 let tmpm: *u8 = sys_mmap(512) 211 to = ak_cat(tmpm, 0, manp) 212 to = ak_cat(tmpm, to, ".new" as *u8) 213 if ak_writefile(tmpm, mb, ml) != 1 { return 0 - 3 } 214 sys_renameat(tmpm, manp) 215 return 0 216} 217 218// VERIFY: pack entries vs manifest; returns bad count (neg = unreadable) 219func ak_verify(packp: *u8, manp: *u8, lfd: i64) -> i64 { 220 let pszp: *i64 = sys_mmap(16) as *i64 221 let pk: *u8 = ak_readall(packp, pszp) 222 let pn: i64 = pszp[0] 223 if pn <= 7 { return 0 - 1 } 224 let mszp: *i64 = sys_mmap(16) as *i64 225 let mn0: *u8 = ak_readall(manp, mszp) 226 let mn: i64 = mszp[0] 227 if mn <= 0 { return 0 - 1 } 228 var bad: i64 = 0 229 var ents: i64 = 0 230 var i: i64 = 7 231 while i < pn { 232 // expect "ENT <pl> <bytes> <cid>\n" 233 if i + 4 > pn { i = pn } else { 234 i = i + 4 235 var pl: i64 = 0 236 while pk[i] != (32 as u8) { pl = pl * 10 + ((pk[i] as i64) - 48); i = i + 1 } 237 i = i + 1 238 var bs: i64 = 0 239 while pk[i] != (32 as u8) { bs = bs * 10 + ((pk[i] as i64) - 48); i = i + 1 } 240 i = i + 1 241 var cid: i64 = 0 242 while pk[i] != (10 as u8) { cid = cid * 10 + ((pk[i] as i64) - 48); i = i + 1 } 243 i = i + 1 244 let pathp: *u8 = (pk as i64 + i) as *u8 245 i = i + pl 246 let datap: *u8 = (pk as i64 + i) as *u8 247 i = i + bs 248 ents = ents + 1 249 let real: i64 = ak_fnv(datap, bs) 250 if real != cid { 251 bad = bad + 1 252 ak_w(1, "ARKVERIFY-BAD entry=" as *u8) 253 ak_wn(1, ents) 254 ak_w(1, " cid-mismatch\n" as *u8) 255 if lfd >= 0 { ak_w(lfd, "ARKVERIFY-BAD entry cid-mismatch\n" as *u8) } 256 } 257 // the manifest must contain the exact row for this entry 258 let row: *u8 = sys_mmap(512) 259 var ro: i64 = ak_cat(row, 0, "ARKENT path=" as *u8) 260 var k: i64 = 0 261 while k < pl { row[ro] = pathp[k]; ro = ro + 1; k = k + 1 } 262 ro = ak_cat(row, ro, " bytes=" as *u8) 263 ro = ak_catn(row, ro, bs) 264 ro = ak_cat(row, ro, " cid=" as *u8) 265 ro = ak_catn(row, ro, cid) 266 var found: i64 = 0 267 var j: i64 = 0 268 while j + ro <= mn { 269 var m: i64 = 0 270 var hit: i64 = 1 271 while m < ro { 272 if mn0[j + m] != row[m] { hit = 0; m = ro } else { m = m + 1 } 273 } 274 if hit == 1 { found = 1; j = mn } 275 j = j + 1 276 } 277 if found == 0 { 278 bad = bad + 1 279 ak_w(1, "ARKVERIFY-BAD manifest-row-missing\n" as *u8) 280 if lfd >= 0 { ak_w(lfd, "ARKVERIFY-BAD manifest-row-missing\n" as *u8) } 281 } 282 } 283 } 284 if ents == 0 { return 0 - 1 } 285 return bad 286} 287 288func main(argc: i64, argv: *i64) -> i64 { 289 var mode: *u8 = "snap" as *u8 290 var setp: *u8 = "knowledge/registry/ark_set.tsv" as *u8 291 var packp: *u8 = "knowledge/ark_pack.bin" as *u8 292 var manp: *u8 = "knowledge/ark_manifest.txt" as *u8 293 if argc >= 2 { mode = argv[1] as *u8 } 294 if argc >= 3 { setp = argv[2] as *u8 } 295 if argc >= 4 { packp = argv[3] as *u8 } 296 if argc >= 5 { manp = argv[4] as *u8 } 297 let lfd: i64 = sys_openat_append("knowledge/status/ark.log" as *u8, 0x1a4) 298 299 var rc: i64 = 0 300 if mode[0] == (118 as u8) { 301 let bad: i64 = ak_verify(packp, manp, lfd) 302 var verdict: *u8 = "GREEN" as *u8 303 if bad != 0 { verdict = "RED" as *u8; rc = 1 } 304 var p: i64 = 0 305 while p < 2 { 306 var fd: i64 = 1 307 if p == 1 { fd = lfd } 308 if fd >= 0 { 309 ak_w(fd, "ARKVERIFY bad=" as *u8); ak_wn(fd, bad) 310 ak_w(fd, " epoch=" as *u8); ak_wn(fd, sys_now_realtime_sec()) 311 ak_w(fd, " verdict=" as *u8); ak_w(fd, verdict) 312 ak_w(fd, "\n" as *u8) 313 } 314 p = p + 1 315 } 316 } else { 317 let arena: *u8 = sys_mmap(ARK_MAXENT * 256) 318 let cnt: i64 = ak_set(setp, arena) 319 if cnt <= 0 { 320 ak_w(1, "ARKSNAP verdict=RED reason=set-missing\n" as *u8) 321 sys_exit(101) 322 } 323 let r: i64 = ak_snap(arena, cnt, packp, manp) 324 var verdict: *u8 = "GREEN" as *u8 325 var unch: i64 = 0 326 if r == 1 { unch = 1 } 327 if r < 0 { verdict = "RED" as *u8; rc = 1 } 328 var p: i64 = 0 329 while p < 2 { 330 var fd: i64 = 1 331 if p == 1 { fd = lfd } 332 if fd >= 0 { 333 ak_w(fd, "ARKSNAP entries=" as *u8); ak_wn(fd, cnt) 334 ak_w(fd, " unchanged=" as *u8); ak_wn(fd, unch) 335 ak_w(fd, " epoch=" as *u8); ak_wn(fd, sys_now_realtime_sec()) 336 ak_w(fd, " verdict=" as *u8); ak_w(fd, verdict) 337 ak_w(fd, "\n" as *u8) 338 } 339 p = p + 1 340 } 341 } 342 if lfd >= 0 { sys_close(lfd) } 343 sys_exit(rc) 344 return rc 345}