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}