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}