code wiki / _hdl_build / nx_dr_incremental.nx
nx_dr_incremental.nx source
↩ module page · 94 lines · 6383 B
1// nx_dr_incremental.nx -- CAP-DR-INCREMENTAL: only re-copy CHANGED files (incremental backup, rsync/restic-style).
2// Reads the prior ARCHIVE_RECEIPT.tsv (name<TAB>sha256), and for each manifest source computes sha256; if the
3// receipt already has that name with the SAME hash -> SKIP (unchanged); else copy + verify + record. Writes a fresh
4// receipt covering all current files. Turns an O(all-bytes) re-archive into O(changed-bytes) -- the difference
5// between backing up 957 B and backing up 40 GB of galx every run. argv: [1]=manifest [2]=archive-dir.
6// Additive-safe (reads sources, writes only the archive dir). license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_site_lock_lib.nx"
9import "nx_sha256.nx"
10import "nx_seg_store.nx"
11const IN_MAGIC_1536: i64 = 1536
12const IN_MAGIC_262144: i64 = 262144
13const IN_MAGIC_1024: i64 = 1024
14
15const IN_CAP: i64 = 67108864
16
17func in_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
18func in_wn(v: i64) -> i64 { var t: i64=v; if t<0{sys_write(1,"-" as *u8,1);t=0-t} let tm:*u8=sys_mmap(24); var k:i64=0; if t==0{tm[0]=48 as u8;k=1} while t>0{tm[k]=(48+(t%10)) as u8;t=t/10;k=k+1} let b:*u8=sys_mmap(24); var j:i64=0; while j<k{b[j]=tm[k-1-j];j=j+1} sys_write(1,b,k); return 0 }
19func in_read(path: *u8, out: *u8, cap: i64) -> i64 {
20 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 - 1 }
21 var total: i64 = 0; var go: i64 = 1
22 while go == 1 { let nr: i64 = sys_read(fd, (out as i64 + total) as *u8, cap - total); if nr <= 0 { go = 0 } if nr > 0 { total = total + nr } if total >= cap { go = 0 } }
23 sys_close(fd); return total
24}
25func in_write(path: *u8, buf: *u8, n: i64) -> i64 { let fd: i64 = sys_openat_wr(path, 0x1a4); if fd < 0 { return 0 - 1 } sys_write(fd, buf, n); sys_close(fd); return n }
26func in_join(out: *u8, dir: *u8, name: *u8) -> i64 { var o: i64=0; var i: i64=0; while dir[i]!=(0 as u8){out[o]=dir[i];o=o+1;i=i+1} out[o]=47 as u8; o=o+1; i=0; while name[i]!=(0 as u8){out[o]=name[i];o=o+1;i=i+1} out[o]=0 as u8; return o }
27func in_field(reg: *u8, ls: i64, le: i64, f: i64, out: *u8, cap: i64) -> i64 {
28 let fs: *i64=sys_mmap(8); let fe: *i64=sys_mmap(8)
29 if slk_field(reg, ls, le, f, fs, fe) == 1 { var o: i64=0; let l: i64=fe[0]-fs[0]; while o<l { if o<cap-1 { out[o]=reg[fs[0]+o] } o=o+1 } out[o]=0 as u8; return l }
30 out[0]=0 as u8; return 0
31}
32func in_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 }
33func in_hex(buf: *u8, n: i64, out: *u8) -> i64 {
34 let dig: *u8 = sys_mmap(64); sha256_digest(buf, n, dig)
35 let hx: *u8 = "0123456789abcdef" as *u8
36 var h: i64=0; while h<32 { let by: i64=(dig[h] as i64)&0xff; out[h*2]=hx[(by>>4)&15]; out[h*2+1]=hx[by&15]; h=h+1 } out[64]=0 as u8; return 0
37}
38// look up `name`'s prior hash in the SOVEREIGN receipt (nx_seg_store ss_get, binary-search .keys); copy 64-hex to
39// out; 1 found / 0 absent. No TSV scan.
40func in_prior_hash(rprefix: *u8, name: *u8, out: *u8) -> i64 {
41 let pq: *i64 = sys_mmap(16) as *i64; let lq: *i64 = sys_mmap(16) as *i64
42 if ss_get(rprefix, name, pq, lq) == 1 { let v: *u8 = pq[0] as *u8; var i: i64=0; while i<64 { out[i]=v[i]; i=i+1 } out[64]=0 as u8; return 1 }
43 return 0
44}
45
46func main(argc: i64, argv: *i64) -> i64 {
47 if argc < 3 { in_w("usage: nx_dr_incremental <manifest> <archive-dir>\n" as *u8); sys_exit(2); return 2 }
48 let manifest: *u8 = argv[1] as *u8; let adir: *u8 = argv[2] as *u8
49 in_w("=== nx_dr_incremental: only re-copy CHANGED files (skip unchanged by hash) ===\n" as *u8)
50 sys_mkdir(adir, 0x1ed)
51
52 // SOVEREIGN receipt: prior read via ss_get, new written via ss_begin/ss_commit (append-only segment) -- NO TSV
53 let rprefix: *u8 = sys_mmap(IN_MAGIC_1536); in_join(rprefix, adir, "receipt" as *u8)
54 // ROOT FIX (count-as-segid, id 1785519700): ss_manifest RETURNS THE ROW COUNT, not an id. Past
55 // SS_MANIFEST_LEGACY_CAP (256) the count PINS at 256, so every later write reuses id 257 and OVERWRITES
56 // the previous segment -- data loss by CLOBBER (distinct from the seq1730 pointer poison, which loses
57 // data by SHADOWING). ss_next_segid parses the real ids and is what the lib header prescribes.
58 // The ss_segs[] buffer was filled and never read, so it goes with the call.
59 let ss_segid: i64 = ss_next_segid(rprefix)
60 let w: *i64 = ss_begin()
61
62 let reg: *u8 = sys_mmap(IN_MAGIC_262144); let rn: i64 = in_read(manifest, reg, IN_MAGIC_262144)
63 if rn <= 0 { in_w(" FATAL: cannot read manifest\n" as *u8); sys_exit(3); return 3 }
64
65 let src: *u8=sys_mmap(IN_MAGIC_1024); let name: *u8=sys_mmap(512); let dest: *u8=sys_mmap(IN_MAGIC_1536)
66 let fbuf: *u8=sys_mmap(IN_CAP); let hexb: *u8=sys_mmap(80); let phex: *u8=sys_mmap(128)
67 var copied: i64=0; var unchanged: i64=0; var skipped: i64=0
68 var ls: i64=0
69
70 while ls < rn {
71 let le: i64 = slk_line_end(reg, rn, ls)
72 if le > ls { if reg[ls] != (35 as u8) {
73 if in_field(reg, ls, le, 0, src, IN_MAGIC_1024) > 0 {
74 in_field(reg, ls, le, 1, name, 512)
75 let n: i64 = in_read(src, fbuf, IN_CAP)
76 if n < 0 { in_w(" skip(absent) " as *u8); in_w(src); in_w("\n" as *u8); skipped=skipped+1 }
77 else {
78 in_hex(fbuf, n, hexb)
79 let had: i64 = in_prior_hash(rprefix, name, phex)
80 var same: i64 = 0
81 if had == 1 { var i: i64=0; same=1; while i<64 { if phex[i]!=hexb[i] { same=0 } i=i+1 } }
82 if same == 1 { unchanged=unchanged+1; in_w(" unchanged " as *u8); in_w(name); in_w("\n" as *u8) }
83 else { in_join(dest, adir, name); in_write(dest, fbuf, n); copied=copied+1; in_w(" COPIED " as *u8); in_w(name); in_w(" bytes=" as *u8); in_wn(n); in_w("\n" as *u8) }
84 ss_add(w, 1, name, hexb, 64) // sovereign: record current state (name -> sha256hex)
85 }
86 }
87 } }
88 ls = le + 1
89 }
90 ss_commit(rprefix, w, ss_segid)
91 in_w(" ---- copied=" as *u8); in_wn(copied); in_w(" unchanged=" as *u8); in_wn(unchanged); in_w(" skipped=" as *u8); in_wn(skipped); in_w("\n" as *u8)
92 in_w("=== INCREMENTAL OK (only changed bytes re-copied; receipt refreshed) ===\n" as *u8)
93 sys_exit(0); return 0
94}