code wiki / _hdl_build / nx_nishifs_cow.nx

nx_nishifs_cow.nx source

↩ module page · 177 lines · 11354 B

1// nx_nishifs_cow.nx -- ladder C6 (the FS): CoW SNAPSHOTS for NishiFS (Merkle structural sharing, git-style). 2// 3// The payoff of content-addressing: a SNAPSHOT is just a root CID, and COPY-ON-WRITE means modifying a file 4// writes a NEW object (new content -> new CID) + a NEW directory -> a NEW root CID, while the OLD root still 5// references the OLD (unchanged) objects. So: snapshots are FREE (remember a root CID), unchanged files are 6// SHARED across snapshots (same CID = stored once = dedup), old snapshots are IMMUTABLE (history preserved), 7// and snapshots are ISOLATED (tampering a new object never affects an old snapshot). This is git's model. 8// KAT 6/6: T1 CoW makes a new root (old root unchanged); T2 structural sharing/dedup (unchanged file's object 9// is NOT duplicated -- arena does not grow, same CID); T3 snapshot immutability (root1 still reads the OLD a, 10// root2 reads the NEW a); T4 CoW touched only the change (a's CID changed, b's CID unchanged); T5 both snapshots 11// read the SHARED b object; T6 liar-kill + isolation (tamper a's NEW object -> root2 read fails integrity, but 12// root1 read of a is UNAFFECTED). 13// composes nx_sha256 (CID) + nx_syscalls. Uses an IN-MEMORY content-addressed arena (same model as the C2 14// file store / C4 on-disk object region; arena chosen for a self-contained sovereign KAT -- no external dirs). 15// NEVER-BRICK (Rule 26): writes NOTHING (pure in-memory) -- cannot touch any device, by construction. expect_exit: 0 license_tier: ORIGINAL 16import "nx_syscalls.nx" 17import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 18import "nx_sha256.nx" 19const GET_MAGIC_65536: i64 = 65536 20 21const GET_OK: i64 = 0 22const GET_NOTFOUND: i64 = 3 23const GET_INTEGRITY: i64 = 7 24 25func ui_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 26// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 27// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 28// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 29// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 30func ui_num(v: i64) -> i64 { nxi_out(v); return 0 } 31func ui_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 32func cid_eq(a: *u8, b: *u8) -> i64 { var i: i64=0; while i<32 { if a[i]!=b[i] { return 0 } i=i+1 } return 1 } 33func wr_u32_le(d: *u8, off: i64, v: i64) -> i64 { d[off]=(v & 0xFF) as u8; d[off+1]=((v>>8)&0xFF) as u8; d[off+2]=((v>>16)&0xFF) as u8; d[off+3]=((v>>24)&0xFF) as u8; return 0 } 34func rd_u32_le(d: *u8, off: i64) -> i64 { return (d[off] as i64) | ((d[off+1] as i64)<<8) | ((d[off+2] as i64)<<16) | ((d[off+3] as i64)<<24) } 35 36// content-addressed arena: records [CID(32)][len(u32)][bytes]. find by CID -> byte-offset of bytes (or -1). 37func arena_find(arena: *u8, endsz: i64, cid: *u8, outlen: *i64) -> i64 { 38 var c: i64=0 39 while c+36 <= endsz { 40 var same: i64=1; var i: i64=0 41 while i<32 { if arena[c+i]!=cid[i] { same=0; i=32 } else { i=i+1 } } 42 let ln: i64 = rd_u32_le(arena, c+32) 43 if same==1 { outlen[0]=ln; return c+36 } 44 c = c+36+ln 45 } 46 return 0-1 47} 48// put with DEDUP by construction: if the CID is already present, do NOT append (shared object). 49func arena_put(arena: *u8, endp: *i64, blob: *u8, n: i64, cidout: *u8) -> i64 { 50 sha256_digest(blob, n, cidout) 51 let tl: *i64 = sys_mmap(8) as *i64 52 if arena_find(arena, endp[0], cidout, tl) >= 0 { return 0 } // already stored -> dedup, share it 53 var c: i64 = endp[0] 54 var i: i64=0; while i<32 { arena[c+i]=cidout[i]; i=i+1 } 55 wr_u32_le(arena, c+32, n) 56 i=0; while i<n { arena[c+36+i]=blob[i]; i=i+1 } 57 endp[0] = c+36+n 58 return 0 59} 60// get with INTEGRITY-ON-READ. 61func arena_get(arena: *u8, endsz: i64, cid: *u8, out: *u8, cap: i64, lenout: *i64) -> i64 { 62 let lp: *i64 = sys_mmap(8) as *i64 63 let boff: i64 = arena_find(arena, endsz, cid, lp) 64 if boff < 0 { return GET_NOTFOUND } 65 let h: *u8 = sys_mmap(40) 66 sha256_digest(((arena as i64)+boff) as *u8, lp[0], h) 67 if cid_eq(h, cid)==0 { return GET_INTEGRITY } 68 var m: i64 = lp[0]; if m>cap { m=cap } 69 var j: i64=0; while j<m { out[j]=arena[boff+j]; j=j+1 } 70 lenout[0]=lp[0] 71 return GET_OK 72} 73// store a 2-entry directory AS an object; its CID == the root for that FS state. 74func make_dir(arena: *u8, endp: *i64, nA: *u8, cA: *u8, nB: *u8, cB: *u8, rootout: *u8) -> i64 { 75 let buf: *u8 = sys_mmap(256) 76 var w: i64=0 77 var i: i64=0; while nA[i]!=(0 as u8) { buf[w]=nA[i]; w=w+1; i=i+1 } buf[w]=0 as u8; w=w+1 78 i=0; while i<32 { buf[w]=cA[i]; w=w+1; i=i+1 } 79 i=0; while nB[i]!=(0 as u8) { buf[w]=nB[i]; w=w+1; i=i+1 } buf[w]=0 as u8; w=w+1 80 i=0; while i<32 { buf[w]=cB[i]; w=w+1; i=i+1 } 81 arena_put(arena, endp, buf, w, rootout) 82 return 0 83} 84func dir_find(buf: *u8, len: i64, name: *u8, cidout: *u8) -> i64 { 85 var c: i64=0 86 while c<len { 87 var k: i64=0; var same: i64=1 88 while buf[c]!=(0 as u8) { if name[k]!=buf[c] { same=0 } c=c+1; k=k+1 } 89 if name[k]!=(0 as u8) { same=0 } 90 c=c+1 91 if same==1 { var i: i64=0; while i<32 { cidout[i]=buf[c+i]; i=i+1 } return 1 } 92 c=c+32 93 } 94 return 0 95} 96// MOUNT a snapshot by its root CID: fetch dir-object by root (integrity) -> file CID -> fetch file (integrity). 97func read_by_root(arena: *u8, endsz: i64, root: *u8, name: *u8, out: *u8, cap: i64, lenout: *i64) -> i64 { 98 let dbuf: *u8 = sys_mmap(512) 99 let dlen: *i64 = sys_mmap(8) as *i64 100 let dr: i64 = arena_get(arena, endsz, root, dbuf, 512, dlen) 101 if dr != GET_OK { return dr } 102 let fcid: *u8 = sys_mmap(40) 103 if dir_find(dbuf, dlen[0], name, fcid)==0 { return GET_NOTFOUND } 104 return arena_get(arena, endsz, fcid, out, cap, lenout) 105} 106 107func main() -> i64 { 108 ui_puts("ladder C6: CoW SNAPSHOTS for NishiFS (Merkle structural sharing -- snapshots free, unchanged files shared)\n" as *u8) 109 110 let A1: *u8 = "version one of file a\x00" as *u8 111 let A2: *u8 = "version TWO of file a -- changed\x00" as *u8 112 let B: *u8 = "file b never changes across the snapshot\x00" as *u8 113 let nA1: i64 = ui_slen(A1) 114 let nA2: i64 = ui_slen(A2) 115 let nB: i64 = ui_slen(B) 116 117 let arena: *u8 = sys_mmap(GET_MAGIC_65536) 118 let endp: *i64 = sys_mmap(8) as *i64 119 endp[0]=0 120 121 // ---- snapshot v1: {a:A1, b:B} ---- 122 let cidA1: *u8 = sys_mmap(40) 123 let cidB: *u8 = sys_mmap(40) 124 arena_put(arena, endp, A1, nA1, cidA1) 125 arena_put(arena, endp, B, nB, cidB) 126 let root1: *u8 = sys_mmap(40) 127 make_dir(arena, endp, "a\x00" as *u8, cidA1, "b\x00" as *u8, cidB, root1) // SNAPSHOT v1 = root1 128 129 // ---- CoW modify a -> A2 (b unchanged) ---- 130 let cidA2: *u8 = sys_mmap(40) 131 arena_put(arena, endp, A2, nA2, cidA2) // new object for a 132 let endBefore: i64 = endp[0] 133 let cidB2: *u8 = sys_mmap(40) 134 arena_put(arena, endp, B, nB, cidB2) // b unchanged -> dedup, arena must NOT grow 135 let endAfter: i64 = endp[0] 136 let root2: *u8 = sys_mmap(40) 137 make_dir(arena, endp, "a\x00" as *u8, cidA2, "b\x00" as *u8, cidB, root2) // SNAPSHOT v2 = root2 138 139 // ---- reads through each snapshot root ---- 140 let oa1: *u8 = sys_mmap(256); let la1: *i64 = sys_mmap(8) as *i64 141 let r_a1: i64 = read_by_root(arena, endp[0], root1, "a\x00" as *u8, oa1, 256, la1) // old a via v1 142 let oa2: *u8 = sys_mmap(256); let la2: *i64 = sys_mmap(8) as *i64 143 let r_a2: i64 = read_by_root(arena, endp[0], root2, "a\x00" as *u8, oa2, 256, la2) // new a via v2 144 let ob1: *u8 = sys_mmap(256); let lb1: *i64 = sys_mmap(8) as *i64 145 let r_b1: i64 = read_by_root(arena, endp[0], root1, "b\x00" as *u8, ob1, 256, lb1) 146 let ob2: *u8 = sys_mmap(256); let lb2: *i64 = sys_mmap(8) as *i64 147 let r_b2: i64 = read_by_root(arena, endp[0], root2, "b\x00" as *u8, ob2, 256, lb2) 148 149 // helper: bytes match A1 / A2 / B 150 var a1ok: i64=0; if r_a1==GET_OK { if la1[0]==nA1 { a1ok=1; var i: i64=0; while i<nA1 { if oa1[i]!=A1[i] { a1ok=0; i=nA1 } else { i=i+1 } } } } 151 var a2ok: i64=0; if r_a2==GET_OK { if la2[0]==nA2 { a2ok=1; var i: i64=0; while i<nA2 { if oa2[i]!=A2[i] { a2ok=0; i=nA2 } else { i=i+1 } } } } 152 var b1ok: i64=0; if r_b1==GET_OK { if lb1[0]==nB { b1ok=1; var i: i64=0; while i<nB { if ob1[i]!=B[i] { b1ok=0; i=nB } else { i=i+1 } } } } 153 var b2ok: i64=0; if r_b2==GET_OK { if lb2[0]==nB { b2ok=1; var i: i64=0; while i<nB { if ob2[i]!=B[i] { b2ok=0; i=nB } else { i=i+1 } } } } 154 155 // ---- T6: tamper a's NEW object (cidA2) in the arena; root2 read must fail, root1 read must be unaffected ---- 156 let tl: *i64 = sys_mmap(8) as *i64 157 let a2off: i64 = arena_find(arena, endp[0], cidA2, tl) 158 arena[a2off+3] = (((arena[a2off+3] as i64)+1) & 0xFF) as u8 159 let ox: *u8 = sys_mmap(256); let lx: *i64 = sys_mmap(8) as *i64 160 let r_v2_tampered: i64 = read_by_root(arena, endp[0], root2, "a\x00" as *u8, ox, 256, lx) // expect GET_INTEGRITY 161 let oy: *u8 = sys_mmap(256); let ly: *i64 = sys_mmap(8) as *i64 162 let r_v1_after: i64 = read_by_root(arena, endp[0], root1, "a\x00" as *u8, oy, 256, ly) // expect still OK == A1 163 var v1stillA1: i64=0; if r_v1_after==GET_OK { if ly[0]==nA1 { v1stillA1=1; var i: i64=0; while i<nA1 { if oy[i]!=A1[i] { v1stillA1=0; i=nA1 } else { i=i+1 } } } } 164 165 var pass: i64=0 166 var ttl: i64=0 167 ttl=ttl+1; ui_puts(" T1 CoW makes a NEW root (root1 != root2; old snapshot root unchanged): " as *u8); if cid_eq(root1, root2)==0 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } 168 ttl=ttl+1; ui_puts(" T2 structural sharing/dedup (unchanged b NOT duplicated: arena did not grow): " as *u8); if endBefore==endAfter { if cid_eq(cidB2, cidB)==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } 169 ttl=ttl+1; ui_puts(" T3 snapshot immutability (root1 reads OLD a; root2 reads NEW a): " as *u8); if a1ok==1 { if a2ok==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } 170 ttl=ttl+1; ui_puts(" T4 CoW touched ONLY the change (a CID changed, b CID unchanged): " as *u8); if cid_eq(cidA1, cidA2)==0 { if cid_eq(cidB, cidB2)==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } 171 ttl=ttl+1; ui_puts(" T5 both snapshots read the SHARED b object: " as *u8); if b1ok==1 { if b2ok==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } 172 ttl=ttl+1; ui_puts(" T6 liar-kill+isolation (tamper v2's a -> v2 read INTEGRITY-fails, v1 read UNAFFECTED): " as *u8); if r_v2_tampered==GET_INTEGRITY { if v1stillA1==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL (r="); ui_num(r_v2_tampered); ui_puts(")\n" as *u8) } 173 174 ui_puts("NISHIFS-COW-GATE passed " as *u8); ui_num(pass); ui_puts("/" as *u8); ui_num(ttl) 175 if pass==ttl { ui_puts(" verdict=GREEN (CoW snapshots: free via Merkle root-CID; unchanged files shared/deduped; old snapshots immutable + isolated; native encryption = last FS rung)\n" as *u8); sys_exit(0); return 0 } 176 ui_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 177}