extract-blk.sh (3240B)
1 #!/usr/bin/env python3 2 """extract-blk — extract files from a SEEDFS image written to blk1 by the 3 seed kernel. 4 5 Layout (sector-aligned, little-endian): 6 sector 0: char magic[8] = "SEEDFS\0\0"; u32 nfiles; u32 reserved; 7 sector 1..T: nfiles * { char path[96]; u32 data_offset_sectors; 8 u32 _pad; u64 size_bytes; } 9 (entry size 112 B, 4 entries/sector → T=ceil(nfiles/4)) 10 sector T+1..: file data, each padded up to a 512-byte boundary. 11 12 The kernel dumps every tmpfs entry, including in/-prefixed inputs. We 13 filter to out/-prefixed entries here, strip the prefix, and write to 14 <outdir>/<rel> — so callers can point <outdir> at $STAGE/out/ directly. 15 16 Usage: extract-blk.sh <outdir> <out.img> 17 """ 18 19 import os 20 import struct 21 import sys 22 23 ENT_SIZE = 112 24 HDR_FMT = "<8sII" # magic[8], nfiles u32, reserved u32 25 ENT_FMT = "<96sIIQ" # path[96], data_offset u32, _pad u32, size u64 26 27 28 def fail(msg: str) -> None: 29 print(f"extract-blk: {msg}", file=sys.stderr) 30 sys.exit(3) 31 32 33 def main() -> int: 34 if len(sys.argv) != 3: 35 print("usage: extract-blk.sh <outdir> <out.img>", file=sys.stderr) 36 return 2 37 38 outdir, img_path = sys.argv[1], sys.argv[2] 39 if not os.path.isfile(img_path): 40 fail(f"missing {img_path}") 41 os.makedirs(outdir, exist_ok=True) 42 43 with open(img_path, "rb") as f: 44 hdr = f.read(16) 45 if len(hdr) < 16: 46 fail("image too small for header") 47 magic, nfiles, _reserved = struct.unpack(HDR_FMT, hdr) 48 if magic != b"SEEDFS\0\0": 49 fail(f"bad magic at sector 0 (got {magic!r}) — kernel didn't reach exit") 50 if nfiles < 0: 51 fail(f"bad nfiles={nfiles}") 52 53 table_sectors = (nfiles + 3) // 4 54 f.seek(512) 55 table = f.read(table_sectors * 512) 56 if len(table) < nfiles * ENT_SIZE: 57 fail("image truncated mid-table") 58 59 written = 0 60 for i in range(nfiles): 61 path_bytes, off_sectors, _pad, size_bytes = struct.unpack_from( 62 ENT_FMT, table, i * ENT_SIZE 63 ) 64 path = path_bytes.split(b"\0", 1)[0].decode("utf-8", "replace") 65 if not path: 66 fail(f"empty path at entry {i}") 67 68 if not path.startswith("out/"): 69 continue 70 rel = path[len("out/"):] 71 if not rel: 72 continue 73 74 out = os.path.join(outdir, rel) 75 os.makedirs(os.path.dirname(out) or ".", exist_ok=True) 76 77 if size_bytes == 0: 78 open(out, "wb").close() 79 written += 1 80 continue 81 82 f.seek(off_sectors * 512) 83 remaining = size_bytes 84 with open(out, "wb") as g: 85 while remaining > 0: 86 chunk = f.read(min(remaining, 1 << 20)) 87 if not chunk: 88 fail(f"image truncated reading {path}") 89 g.write(chunk) 90 remaining -= len(chunk) 91 written += 1 92 93 print(f"extract-blk: wrote {written} file(s) to {outdir} (filtered from {nfiles})", file=sys.stderr) 94 return 0 95 96 97 if __name__ == "__main__": 98 sys.exit(main())