code wiki / _hdl_build / nx_nishifs_mount.nx
nx_nishifs_mount.nx source
↩ module page · 229 lines · 13290 B
1// nx_nishifs_mount.nx -- ladder C4 (installer/disk INTEGRATION): FORMAT + MOUNT NishiFS on a C1 partition.
2//
3// Unifies C1 (MBR partition table) + C2 (content-addressed CID/Merkle store) into ONE formatted, mountable
4// disk image -- the brick the installer (C3) lays onto a target. Layout of the disk FILE:
5// sector 0 : MBR -- partition table @446, one bootable(0x80) Nishi(0x9E) partition @ LBA 1, sig 0x55AA
6// partition (LBA 1+) : [superblock 512B] [object region] [directory]
7// superblock : magic "NISHIFS1" | root_CID(32) | obj_count | objstart_rel | dir_off_rel | dir_len
8// object region : packed records [CID(32)][len(u32 LE)][bytes] (content-addressed: located BY CID)
9// directory : packed entries [name\0][childCID(32)] ; its sha256 == the superblock root_CID
10//
11// MOUNT executes the REAL parse path (off the persisted, re-read bytes, like the C1/C3 boot proofs):
12// parse MBR partition table -> find the Nishi partition -> read superblock magic -> recompute the directory
13// CID and require it == the stored root_CID (WHOLE-FS integrity) -> look up a file by name -> get its CID ->
14// find the object by CID -> require the object bytes re-hash to that CID (INTEGRITY-ON-READ) -> return bytes.
15// KAT 6/6: T1 format+mount round-trip (read a file back through the full chain); T2 partition parsed from the
16// C1 table (0x80/0x9E, not hardcoded); T3 superblock magic + root_CID present; T4 liar-kill: tamper an OBJECT
17// -> MNT_INTEGRITY; T5 liar-kill: tamper the DIRECTORY -> MNT_ROOT_MISMATCH (whole-FS tamper-evident);
18// T6 it all runs off the PERSISTED + re-read disk file.
19// composes nx_sha256 (CID) + nx_syscalls; reuses the C1 partition-entry format + the C2 CID/Merkle model.
20// NEVER-BRICK (Rule 26): writes a FILE (/tmp/nishi_fs_disk.img), no /dev. expect_exit: 0 license_tier: ORIGINAL
21import "nx_syscalls.nx"
22import "nx_sha256.nx"
23
24const DISK_SZ: i64 = 4096 // 8 sectors
25const PBYTE: i64 = 512 // partition starts at LBA 1
26const OBJSTART_REL: i64 = 512 // object region starts right after the 512B superblock
27const PART_TYPE_NISHI: i64 = 0x9E
28const MNT_OK: i64 = 0
29const MNT_BADMAGIC: i64 = 1
30const MNT_ROOT_MISMATCH: i64 = 2
31const MNT_NOTFOUND: i64 = 3
32const MNT_INTEGRITY: i64 = 4
33
34func 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 }
35func ui_num(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;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{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
36func ui_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
37func 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 }
38func 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 }
39func 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) }
40// sha256 over a slice of the disk buffer [off, off+len)
41func sha_slice(d: *u8, off: i64, len: i64, out: *u8) -> i64 { sha256_digest(((d as i64)+off) as *u8, len, out); return 0 }
42
43// write one content-addressed object record [CID][len][bytes] at cursor; return the next cursor.
44func put_obj(d: *u8, cursor: i64, blob: *u8, n: i64, cidout: *u8) -> i64 {
45 sha256_digest(blob, n, cidout)
46 var i: i64=0; while i<32 { d[cursor+i]=cidout[i]; i=i+1 }
47 wr_u32_le(d, cursor+32, n)
48 i=0; while i<n { d[cursor+36+i]=blob[i]; i=i+1 }
49 return cursor + 36 + n
50}
51// scan the object region [start,end) for the record whose CID matches; return byte-offset of its bytes, or -1.
52func find_obj(d: *u8, start: i64, endb: i64, cid: *u8, outlen: *i64) -> i64 {
53 var c: i64 = start
54 while c + 36 <= endb {
55 var same: i64 = 1
56 var i: i64 = 0
57 while i < 32 { if d[c+i]!=cid[i] { same=0; i=32 } else { i=i+1 } }
58 let ln: i64 = rd_u32_le(d, c+32)
59 if same==1 { outlen[0]=ln; return c+36 }
60 c = c + 36 + ln
61 }
62 return 0 - 1
63}
64// find a name in the directory [base,base+len); fill cidout with its child CID; 1 found / 0 not.
65func dir_find(d: *u8, base: i64, len: i64, name: *u8, cidout: *u8) -> i64 {
66 var c: i64 = base
67 let endd: i64 = base + len
68 while c < endd {
69 var k: i64 = 0
70 var same: i64 = 1
71 while d[c] != (0 as u8) {
72 if name[k] != d[c] { same=0 }
73 c = c + 1
74 k = k + 1
75 }
76 if name[k] != (0 as u8) { same=0 } // target longer than stored name
77 c = c + 1 // skip the \0
78 if same==1 { var i: i64=0; while i<32 { cidout[i]=d[c+i]; i=i+1 } return 1 }
79 c = c + 32 // skip child CID, next entry
80 }
81 return 0
82}
83
84// FORMAT a NishiFS disk into d (size DISK_SZ): MBR partition table + superblock + 2 objects + directory.
85func format_disk(d: *u8, nameA: *u8, A: *u8, nA: i64, nameB: *u8, B: *u8, nB: i64) -> i64 {
86 var z: i64=0; while z<DISK_SZ { d[z]=0 as u8; z=z+1 }
87 // ---- MBR partition table @446 (C1 format): bootable Nishi partition @ LBA 1 ----
88 let p: i64 = 446
89 d[p+0]=0x80 as u8; d[p+2]=0x02 as u8; d[p+4]=PART_TYPE_NISHI as u8; d[p+5]=0xFE as u8; d[p+6]=0xFF as u8; d[p+7]=0xFF as u8
90 wr_u32_le(d, p+8, 1) // LBA start = 1
91 wr_u32_le(d, p+12, (DISK_SZ/512) - 1) // sector count
92 d[510]=0x55 as u8; d[511]=0xAA as u8
93 // ---- objects ----
94 let cidA: *u8 = sys_mmap(40)
95 let cidB: *u8 = sys_mmap(40)
96 var cur: i64 = PBYTE + OBJSTART_REL
97 cur = put_obj(d, cur, A, nA, cidA)
98 cur = put_obj(d, cur, B, nB, cidB)
99 // ---- directory at cur ----
100 let dir_abs: i64 = cur
101 var w: i64 = dir_abs
102 var i: i64=0; while nameA[i]!=(0 as u8) { d[w]=nameA[i]; w=w+1; i=i+1 } d[w]=0 as u8; w=w+1
103 i=0; while i<32 { d[w]=cidA[i]; w=w+1; i=i+1 }
104 i=0; while nameB[i]!=(0 as u8) { d[w]=nameB[i]; w=w+1; i=i+1 } d[w]=0 as u8; w=w+1
105 i=0; while i<32 { d[w]=cidB[i]; w=w+1; i=i+1 }
106 let dir_len: i64 = w - dir_abs
107 // root CID = sha256(directory)
108 let root: *u8 = sys_mmap(40)
109 sha_slice(d, dir_abs, dir_len, root)
110 // ---- superblock @ PBYTE ----
111 let mg: *u8 = "NISHIFS1\x00" as *u8
112 i=0; while i<8 { d[PBYTE+i]=mg[i]; i=i+1 }
113 i=0; while i<32 { d[PBYTE+8+i]=root[i]; i=i+1 }
114 wr_u32_le(d, PBYTE+40, 2) // obj_count
115 wr_u32_le(d, PBYTE+44, OBJSTART_REL) // objstart (partition-relative)
116 wr_u32_le(d, PBYTE+48, dir_abs - PBYTE) // dir_off (partition-relative)
117 wr_u32_le(d, PBYTE+52, dir_len) // dir_len
118 return 0
119}
120
121// MOUNT: parse partition -> superblock -> root-CID check -> file-by-name -> object-by-CID -> integrity. status.
122func mount_read(d: *u8, name: *u8, out: *u8, cap: i64, lenout: *i64) -> i64 {
123 // parse the partition table (scan the 4 entries) for a bootable Nishi partition
124 var lba: i64 = 0 - 1
125 var e: i64 = 0
126 while e < 4 {
127 let off: i64 = 446 + e*16
128 if (d[off] as i64)==0x80 { if (d[off+4] as i64)==PART_TYPE_NISHI { lba = rd_u32_le(d, off+8); e=4 } else { e=e+1 } } else { e=e+1 }
129 }
130 if lba < 0 { return MNT_NOTFOUND }
131 let pb: i64 = lba * 512
132 let mg: *u8 = "NISHIFS1\x00" as *u8
133 var i: i64=0; while i<8 { if d[pb+i]!=mg[i] { return MNT_BADMAGIC } i=i+1 }
134 let objstart_rel: i64 = rd_u32_le(d, pb+44)
135 let dir_off_rel: i64 = rd_u32_le(d, pb+48)
136 let dir_len: i64 = rd_u32_le(d, pb+52)
137 let dir_abs: i64 = pb + dir_off_rel
138 // whole-FS integrity: recompute the directory CID, require == stored root
139 let root_stored: *u8 = sys_mmap(40)
140 i=0; while i<32 { root_stored[i]=d[pb+8+i]; i=i+1 }
141 let root_calc: *u8 = sys_mmap(40)
142 sha_slice(d, dir_abs, dir_len, root_calc)
143 if cid_eq(root_calc, root_stored)==0 { return MNT_ROOT_MISMATCH }
144 // file by name -> CID
145 let fcid: *u8 = sys_mmap(40)
146 if dir_find(d, dir_abs, dir_len, name, fcid)==0 { return MNT_NOTFOUND }
147 // object by CID
148 let lenp: *i64 = sys_mmap(8) as *i64
149 let boff: i64 = find_obj(d, pb + objstart_rel, dir_abs, fcid, lenp)
150 if boff < 0 { return MNT_NOTFOUND }
151 // integrity-on-read
152 let h: *u8 = sys_mmap(40)
153 sha_slice(d, boff, lenp[0], h)
154 if cid_eq(h, fcid)==0 { return MNT_INTEGRITY }
155 var m: i64 = lenp[0]
156 if m > cap { m = cap }
157 var j: i64=0; while j<m { out[j]=d[boff+j]; j=j+1 }
158 lenout[0]=lenp[0]
159 return MNT_OK
160}
161
162func ui_read(path: *u8, out: *u8, cap: i64) -> i64 {
163 let fd: i64 = sys_openat_rd(path)
164 if fd < 0 { return 0-1 }
165 var n: i64=0; var go: i64=1
166 while go==1 { let rr: i64 = sys_read(fd, ((out as i64)+n) as *u8, cap-n); if rr<=0 { go=0 } else { n=n+rr } if n>=cap { go=0 } }
167 sys_close(fd)
168 return n
169}
170
171func main() -> i64 {
172 ui_puts("ladder C4: FORMAT + MOUNT NishiFS on a C1 partition (unifies C1 partition + C2 content-addressed FS)\n" as *u8)
173
174 let A: *u8 = "NishiOS readme: sovereign content-addressed filesystem\x00" as *u8
175 let B: *u8 = "kernel payload bytes v1 -- the OS image\x00" as *u8
176 let nA: i64 = ui_slen(A)
177 let nB: i64 = ui_slen(B)
178
179 // FORMAT
180 let d: *u8 = sys_mmap(DISK_SZ + 16)
181 format_disk(d, "readme\x00" as *u8, A, nA, "kernel\x00" as *u8, B, nB)
182
183 // PERSIST + RE-READ (everything below runs off the on-disk bytes)
184 let fd: i64 = sys_openat_wr("knowledge/status/nishi_fs_disk.img\x00" as *u8, 0x1a4)
185 if fd<=0 { ui_puts("C4 RED: cannot write disk image\n" as *u8); sys_exit(1); return 1 }
186 sys_write(fd, d, DISK_SZ)
187 sys_close(fd)
188 let rd: *u8 = sys_mmap(DISK_SZ + 16)
189 let rn: i64 = ui_read("knowledge/status/nishi_fs_disk.img\x00" as *u8, rd, DISK_SZ)
190 ui_puts(" formatted + persisted knowledge/status/nishi_fs_disk.img ("); ui_num(rn); ui_puts(" bytes)\n" as *u8)
191
192 // T1 mount + read "readme" off the re-read disk
193 let outR: *u8 = sys_mmap(512)
194 let lenR: *i64 = sys_mmap(8) as *i64
195 let r1: i64 = mount_read(rd, "readme\x00" as *u8, outR, 512, lenR)
196 var rtmatch: i64 = 0
197 if r1==MNT_OK { if lenR[0]==nA { rtmatch=1; var i: i64=0; while i<nA { if outR[i]!=A[i] { rtmatch=0; i=nA } else { i=i+1 } } } }
198
199 // T4 tamper an OBJECT byte (in A's data) -> integrity catch
200 let bad1: *u8 = sys_mmap(DISK_SZ + 16)
201 var c1: i64=0; while c1<DISK_SZ { bad1[c1]=rd[c1]; c1=c1+1 }
202 let aData: i64 = PBYTE + OBJSTART_REL + 36 + 5 // into A's bytes (after CID[32]+len[4])
203 bad1[aData] = (((bad1[aData] as i64) + 1) & 0xFF) as u8
204 let outx: *u8 = sys_mmap(512)
205 let lenx: *i64 = sys_mmap(8) as *i64
206 let r4: i64 = mount_read(bad1, "readme\x00" as *u8, outx, 512, lenx)
207
208 // T5 tamper a DIRECTORY byte -> whole-FS root-CID mismatch
209 let bad2: *u8 = sys_mmap(DISK_SZ + 16)
210 var c2: i64=0; while c2<DISK_SZ { bad2[c2]=rd[c2]; c2=c2+1 }
211 let dirOff: i64 = rd_u32_le(rd, PBYTE+48)
212 bad2[PBYTE + dirOff + 1] = (((bad2[PBYTE + dirOff + 1] as i64) + 1) & 0xFF) as u8
213 let outy: *u8 = sys_mmap(512)
214 let leny: *i64 = sys_mmap(8) as *i64
215 let r5: i64 = mount_read(bad2, "readme\x00" as *u8, outy, 512, leny)
216
217 var pass: i64=0
218 var ttl: i64=0
219 ttl=ttl+1; ui_puts(" T1 format+mount round-trip (partition->superblock->dir->object, bytes match): " as *u8); if rtmatch==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL (r1="); ui_num(r1); ui_puts(")\n" as *u8) }
220 ttl=ttl+1; ui_puts(" T2 partition parsed from the C1 table (status 0x80 + type 0x9E): " as *u8); if (rd[446] as i64)==0x80 { if (rd[446+4] as i64)==PART_TYPE_NISHI { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) }
221 ttl=ttl+1; ui_puts(" T3 NishiFS superblock magic 'NISHIFS1' at the partition LBA: " as *u8); var mok: i64=1; let mg2: *u8="NISHIFS1\x00" as *u8; var mi: i64=0; while mi<8 { if rd[PBYTE+mi]!=mg2[mi] { mok=0 } mi=mi+1 } if mok==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
222 ttl=ttl+1; ui_puts(" T4 liar-kill: tamper an OBJECT -> integrity-on-read catches (MNT_INTEGRITY): " as *u8); if r4==MNT_INTEGRITY { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL (r4="); ui_num(r4); ui_puts(")\n" as *u8) }
223 ttl=ttl+1; ui_puts(" T5 liar-kill: tamper the DIRECTORY -> whole-FS root-CID mismatch (MNT_ROOT_MISMATCH): " as *u8); if r5==MNT_ROOT_MISMATCH { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL (r5="); ui_num(r5); ui_puts(")\n" as *u8) }
224 ttl=ttl+1; ui_puts(" T6 runs off the PERSISTED + re-read disk file (rn==DISK_SZ): " as *u8); if rn==DISK_SZ { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
225
226 ui_puts("NISHIFS-MOUNT-GATE passed " as *u8); ui_num(pass); ui_puts("/" as *u8); ui_num(ttl)
227 if pass==ttl { ui_puts(" verdict=GREEN (NishiFS FORMATTED + MOUNTED on a C1 partition: partition->superblock->root-CID->file-by-CID with integrity; CoW snapshot + encryption = next)\n" as *u8); sys_exit(0); return 0 }
228 ui_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
229}