code wiki / _hdl_build / nx_gpt_relocate.nx
nx_gpt_relocate.nx
buildroot/runtime/_hdl_build/nx_gpt_relocate.nx
about
nx_gpt_relocate.nx -- FIX THE WRITE, NOT THE IMAGE (debt 1785972535).
An image is a FIXED-SIZE disk. A USB stick is usually BIGGER. So a raw byte-for-byte write leaves
the BACKUP GPT sitting in the middle of the device instead of at its final LBA, and the primary
header still describes the image's geometry. Every gate we had passed because every gate judged
the IMAGE -- but the artifact that ships is the DEVICE. Measured on a real stick: 7,987,200 device
sectors vs 135,269 image sectors; the disk came up GPT with the ESP visible and the data partition
missing entirely.
This organ makes a written device's GPT describe THAT DEVICE:
* primary header : AlternateLBA = last LBA, LastUsableLBA = last - 33, header CRC32 recomputed
* backup entry array: copied to (last - 32)
* backup header : written at the last LBA, MyLBA/AlternateLBA/PartitionEntryLBA corrected
* the STALE backup : zeroed where it used to sit -- a leftover secondary GPT mid-device is a
landmine, because a future repair tool can "restore" the old geometry from
it and silently shrink the disk back.
It REFUSES rather than corrupting: no "EFI PART" signature, a device smaller than the image, or a
partition that would extend past the new last-usable LBA all stop before a single byte is written.
HOST-INDEPENDENT BY CONSTRUCTION: it seeks and writes a block device or a file through plain
syscalls, so the same organ serves Linux, Nishi OS itself, and any host that can run our ELF; hosts
that cannot (Windows today) apply the identical byte ranges, and `plan` prints exactly those ranges
so the two can be compared instead of trusted. One brain, many hands.
usage: nx_gpt_relocate selftest
nx_gpt_relocate plan <path> <device_sectors> (prints the byte ranges, writes nothing)
nx_gpt_relocate apply <path> <device_sectors> (writes, then RE-READS and verifies)
license_tier: ORIGINAL expect_exit: 0
dependencies 1 imports · 0 importers
imports: nx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 31 | const SEC: i64 = 512 |
| 32 | const GPT_ENTRIES: i64 = 128 |
| 33 | const GPT_ENTSZ: i64 = 128 |
| 34 | const GPT_ARR_SEC: i64 = 32 |
| 35 | const GPT_HDR_SZ: i64 = 92 |
| 36 | const HDR_ALT: i64 = 32 |
| 37 | const HDR_LASTUSE: i64 = 48 |
| 38 | const HDR_ARRLBA: i64 = 72 |
| 39 | const HDR_CRC: i64 = 16 |
| 40 | const HDR_MY: i64 = 24 |
functions
| 42 | func p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 43 | func pn(v: i64) -> i64 |
| 51 | func r32(b: *u8, o: i64) -> i64 called by 1: relocate |
| 54 | func r64(b: *u8, o: i64) -> i64 called by 1: relocate |
| 59 | func w32(b: *u8, o: i64, v: i64) -> i64 |
| 63 | func w64(b: *u8, o: i64, v: i64) -> i64 { w32(b,o,v & 0xFFFFFFFF); w32(b,o+4,(v>>32) & 0xFFFFFFFF); return 0 } |
| 67 | func crc32(b: *u8, off: i64, n: i64) -> i64 |
| 84 | func is_efipart(b: *u8, o: i64) -> i64 |
| 91 | func pread(fd: i64, off: i64, buf: *u8, n: i64) -> i64 |
| 101 | func pwrite(fd: i64, off: i64, buf: *u8, n: i64) -> i64 |
| 113 | func fix_hdr(h: *u8, my_lba: i64, alt_lba: i64, arr_lba: i64, last_use: i64) -> i64 |
| 125 | func relocate(path: *u8, dev_sectors: i64, mode: i64) -> i64 |
| 229 | func selftest() -> i64 |
| 284 | func s_eq(a: *u8, b: *u8) -> i64 called by 1: main |
| 290 | func atoi(s: *u8) -> i64 called by 1: main |
| 301 | func main(argc: i64, argv: *i64) -> i64 |