ISOPilot ISO + Boot Workshop -- Pro ================================================================================ A command-line ISO 9660 filesystem reader. Most tools treat a .iso as an opaque blob: they hash it, they write it to a USB stick, they check its size. ISOPilot opens the file and actually reads the filesystem inside it. It parses the Primary Volume Descriptor, walks the directory records, and pulls individual files back out by seeking to the extent address recorded on the disc. No mounting, no loop devices, no root, no third-party libraries. The ISO 9660 structures are decoded straight from the raw bytes with os.File.ReadAt and encoding/binary. It is a single static binary that runs anywhere. -------------------------------------------------------------------------------- COMMANDS -------------------------------------------------------------------------------- isopilot info [--json] Parse the Primary Volume Descriptor at sector 16 (byte offset 32768) and validate the "CD001" standard identifier. Reports the volume identifier, system identifier, volume set, publisher, data preparer, application id, logical block size, volume space size in logical blocks, the total byte size that implies, the volume creation timestamp, and the location and size of the root directory extent. If the volume declares more data than the file actually contains, info says so ("image is truncated") on stderr and sets "truncated": true in the JSON. That is the fast way to spot a half-finished download. isopilot list [--json] Recursively walk the directory records, starting from the root directory record embedded in the Primary Volume Descriptor. Every entry is printed with its full path inside the image, its size, its extent LBA, and whether it is a directory. isopilot extract --out [--force] Extract one file. ISOPilot seeks to (extent LBA * logical block size) and reads exactly the recorded data length, so what lands on disk is byte-for- byte what was authored into the image. isopilot help isopilot -h isopilot --help Show usage. -------------------------------------------------------------------------------- OPTIONS -------------------------------------------------------------------------------- --json Machine-readable JSON output (info, list). Warnings go to stderr, so stdout stays parseable. --out FILE Destination path for extract. Required. --force Permit extract to overwrite an existing --out file. -h, --help Show usage. Flags may appear before or after positional arguments. All of these work: isopilot list --json disc.iso isopilot list disc.iso --json isopilot extract disc.iso /SUBDIR/INNER.TXT --out inner.txt isopilot extract --out inner.txt disc.iso /SUBDIR/INNER.TXT isopilot extract disc.iso --out inner.txt /SUBDIR/INNER.TXT -------------------------------------------------------------------------------- PATHS INSIDE AN IMAGE -------------------------------------------------------------------------------- Paths are absolute and use the names as they are actually recorded on the disc. Plain ISO 9660 names are upper-case and 8.3, so a file authored as "subdir/inner.txt" appears as "/SUBDIR/INNER.TXT". For convenience, extract accepts several spellings of the same path. A leading slash is optional, redundant slashes and "." components are collapsed, a ";1" version suffix may be included or omitted, and if no exact match is found the lookup retries case-insensitively. All six of these find the same file: /SUBDIR/INNER.TXT SUBDIR/INNER.TXT /subdir/inner.txt /SUBDIR/INNER.TXT;1 //SUBDIR///INNER.TXT /SUBDIR/./INNER.TXT Run "isopilot list" if you are unsure what a file ended up being called. A long or mixed-case source filename is mangled by the mastering tool, so "a-very-long-filename.txt" may well be "/A_VERY_L.TXT" on the disc. -------------------------------------------------------------------------------- OVERWRITE BEHAVIOUR -------------------------------------------------------------------------------- extract will NOT silently clobber an existing file. If --out names a file that already exists, ISOPilot refuses and exits 1: isopilot: refusing to overwrite existing file out.txt (pass --force to overwrite it) The existing file is left completely untouched. Pass --force to overwrite, in which case ISOPilot states plainly on stderr what it is about to destroy: isopilot: overwriting existing file out.txt If a write fails partway through, the partial output file is removed rather than left behind as a corrupt stub. -------------------------------------------------------------------------------- EXIT CODES -------------------------------------------------------------------------------- 0 Success, or an explicitly requested help screen. 1 Any error: bad invocation, unreadable file, not an ISO 9660 image, truncated or corrupt image, path not found in the image, refusing to overwrite. Usage is printed to stderr on a bad invocation. -------------------------------------------------------------------------------- WHAT IS IMPLEMENTED -------------------------------------------------------------------------------- * Primary Volume Descriptor parsing at sector 16, with "CD001" magic validation. If the descriptor at sector 16 is a valid ISO 9660 descriptor but not the primary one, the descriptor set is scanned forward to find it. * Both-endian numeric fields. ISO 9660 stores these little-endian first, then big-endian; ISOPilot reads the little-endian half. * Recursive directory record walking from the root directory record embedded in the PVD, to arbitrary depth. * Multi-sector directories. A directory record with a declared length of zero means "no more records in this logical sector"; the parser jumps to the next sector boundary and keeps going. Directories with more entries than fit in one 2048-byte sector are handled correctly. * The special "." and ".." entries (identifiers 0x00 and 0x01) are recognised and skipped, so recursion cannot loop through them. * The ";1" file version suffix and the trailing dot that mastering tools add to extension-less names are stripped from reported names. * Directory record timestamps (7-byte binary form) and the volume creation timestamp (17-byte ASCII form), including their 15-minute GMT offsets. * File extraction by extent: seek to (extent LBA * logical block size), read exactly the recorded data length, stream it out in 1 MiB chunks so a large file does not have to fit in memory. * Zero-byte files extract correctly as zero-byte files. * Truncation and corruption safety. Every read is bounds-checked against the real file size, directory recursion is depth-limited and cycle-guarded by extent address, entry counts and declared directory sizes are capped, and the record-parsing loop is structured so it always makes forward progress. A truncated or corrupted image produces a clear error and exit 1. It never panics and never hangs. -------------------------------------------------------------------------------- WHAT IS NOT IMPLEMENTED -------------------------------------------------------------------------------- Read this section before assuming a filename is wrong. * Joliet is NOT decoded. Only plain ISO 9660 names from the Primary Volume Descriptor are reported today. Joliet stores long and Unicode filenames in a Supplementary Volume Descriptor with UCS-2 identifiers; ISOPilot ignores that descriptor entirely. On an image mastered with Joliet you will still see the upper-case 8.3 names, e.g. "/A_VERY_L.TXT" rather than "/a-very-long-filename.txt". The file data extracts correctly either way -- it is only the name that is the short one. * Rock Ridge is NOT decoded. The POSIX extensions (long names, symlinks, ownership, permissions, device nodes) live in the System Use Area of each directory record. ISOPilot does not read that area, so symlinks are not resolved and permissions are not reported. * UDF is NOT supported. Many modern and hybrid discs, and effectively all video DVDs and Blu-rays, carry a UDF filesystem. If an image is UDF-only it has no usable ISO 9660 structure and ISOPilot will reject it. On a hybrid UDF/ISO 9660 image only the ISO 9660 side is read. * El Torito boot catalogs are NOT parsed. The Boot Record Volume Descriptor, the boot catalog, boot images, platform ids and emulation modes are not reported. ISOPilot cannot tell you whether an image is bootable or how. * Writing and authoring are NOT supported. ISOPilot is strictly a reader. It never modifies an image. It cannot create, add to, remaster or rewrite one. * Multi-extent files (the 0x80 continuation flag, used for individual files larger than 4 GiB) are detected and flagged in the listing, but only the first extent is read. Such a file will extract short. * The path table is not used. Directory traversal is done entirely through directory records, which is authoritative but does more I/O than a path table lookup would on a very large image. * Extended Attribute Records are skipped. -------------------------------------------------------------------------------- ROADMAP -------------------------------------------------------------------------------- 1. Joliet: read the Supplementary Volume Descriptor and decode UCS-2 identifiers so long and Unicode filenames are reported. 2. Rock Ridge: parse the System Use Area for POSIX names, symlinks and permissions. 3. El Torito: parse the boot catalog and report bootability, platform and emulation mode. 4. UDF: read the UDF descriptors so hybrid and UDF-only images work. 5. Writing: author and remaster ISO 9660 images, not only read them. -------------------------------------------------------------------------------- EXAMPLES -------------------------------------------------------------------------------- Inspect an image: $ isopilot info disc.iso File: disc.iso File size: 794624 bytes (776.0 KiB) Volume identifier: ISOPILOT_TEST System identifier: LINUX Logical block size: 2048 bytes Volume space size: 388 blocks Total bytes: 794624 bytes (776.0 KiB) Volume created: 2026-08-10 03:45:27 +0000 Root directory: extent LBA 23 (offset 47104), 2048 bytes List its contents: $ isopilot list disc.iso TYPE SIZE BYTES LBA PATH file 293.0 KiB 300000 28 /DATA.BIN file 44 B 44 175 /HELLO.TXT dir 2.0 KiB 2048 24 /SUBDIR file 52 B 52 176 /SUBDIR/INNER.TXT 4 files, 1 directories, 293.1 KiB of file data Pull one file out: $ isopilot extract disc.iso /SUBDIR/INNER.TXT --out inner.txt extracted /SUBDIR/INNER.TXT -> inner.txt (52 bytes, 52 B) from extent LBA 176 at offset 360448 Script against it: $ isopilot list disc.iso --json | jq -r '.entries[] | select(.is_dir | not) | .path' $ isopilot info disc.iso --json | jq .truncated -------------------------------------------------------------------------------- BUILDING -------------------------------------------------------------------------------- Go standard library only. No module downloads, no network access required. go build -o isopilot . Prebuilt binaries are in dist/: isopilot-linux-amd64 isopilot-darwin-amd64 isopilot-darwin-arm64 isopilot-windows-amd64.exe