99 lines
5.9 KiB
Markdown
Executable File
99 lines
5.9 KiB
Markdown
Executable File
# ISO Scalpel 1.1.0
|
|
|
|
I just released ISO Scalpel 1.1.0 — a disc-image editor I wrote in Python. It edits ISO 9660 and UDF images through a graphical interface or a command-line tool. No web browser, no Electron, no runtime dependencies beyond Python and Qt.
|
|
|
|
## Why I Built It
|
|
|
|
I wanted a graphical ISO editor that supported UDF. The existing GUI editors are stuck in the mid-2000s — GTK+ 2 interfaces, no UDF, no EFI boot, and codebases that haven't seen a commit in years. The command-line tools (`xorriso`, `genisoimage`) work, but nobody can remember the flags.
|
|
|
|
## Architecture
|
|
|
|
ISO Scalpel is split into two layers. `iso_handler.py` is the engine — it wraps pycdlib and never imports PySide6. Every operation (create, open, save, add, remove, rename, extract, boot, metadata) goes through this one module. `diff.py` is the same: a filesystem diff engine with no Qt dependency. The GUI layer (`main_window.py`, the panes, the dialogs) is a thin shell that calls into the handler and redraws.
|
|
|
|
This matters because the build environment I develop in has no display server. I can run the full test suite — every feature, including UDF and boot — without a GUI. The same engine powers the CLI.
|
|
|
|
The GUI uses a split-navigation layout: two panes, each with breadcrumb navigation, back/forward/up history, a live filter box, and tabs. Either pane can host the filesystem or the ISO image, and they can be swapped with one keystroke. I did not copy the layout of any existing ISO editor.
|
|
|
|
## What's New in 1.1.0
|
|
|
|
Two features: a command-line interface and a filesystem diff mode.
|
|
|
|
The CLI (`cli.py`) exposes every operation the GUI does. `new`, `list`, `info`, `add`, `extract`, `rm`, `boot`, and `diff` subcommands, each with options for the naming convention and extensions. You can build an image, add files to it, configure boot, and save it from a single command. The CLI is the same engine as the GUI — no duplicated logic.
|
|
|
|
The diff mode compares two ISO images at the filesystem level. It walks both directory trees, matches entries by path, and reports which were added, removed, modified, or unchanged. It does not compare file contents — only names, sizes, and dates. The output looks like a unified diff:
|
|
|
|
```
|
|
--- old.iso
|
|
+++ new.iso
|
|
|
|
2 added, 1 removed, 1 modified, 14 unchanged
|
|
|
|
+ /newfile.txt (2048 bytes)
|
|
- /oldfile.txt (1024 bytes)
|
|
M /readme.txt (12 -> 48 bytes)
|
|
|
|
(14 entries unchanged)
|
|
```
|
|
|
|
In the GUI, the same comparison is available under Tools → Compare Images (`Ctrl+D`). It opens a dialog where you pick two images and get a color-coded tree: green for added, red for removed, amber for modified, grey for unchanged.
|
|
|
|
## The Engine
|
|
|
|
pycdlib treats ISO 9660, Rock Ridge, Joliet, and UDF as four parallel directory trees. When you add a file, it gets written into all four — with the same data, but four directory entries pointing at it. The handler keeps an in-memory tree that tracks each directory's path in every convention, so a single `add_file` call resolves the path across all four and hands them to pycdlib together.
|
|
|
|
ISO 9660 level 1 names are mangled to upper-case 8.3 with a `;1` version suffix. `My Vacation Photos.jpeg` becomes `MY_VACAT.JPE;1`. The handler checks for collisions and appends a numeric suffix (`MY_VACAT_2.JPE;1`) until it finds a free slot. The original name is preserved in the Rock Ridge, Joliet, and UDF trees.
|
|
|
|
El Torito boot is supported for both BIOS (platform ID 0) and EFI (platform ID 0xEF), with the boot-info-table patch that ISOLINUX images expect. The boot catalog is generated for every enabled naming convention.
|
|
|
|
## Build It
|
|
|
|
```sh
|
|
tar xzf iso-scalpel-1.1.0.tar.gz
|
|
cd iso_scalpel_py
|
|
python3 -m venv .venv
|
|
source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
|
|
python main.py # GUI
|
|
python cli.py --help # CLI
|
|
```
|
|
|
|
Config lives at `~/.config/iso-scalpel/settings.json` and is created on first run.
|
|
|
|
## Quality Assurance Pass
|
|
|
|
The 1.1.0 release went through a senior-team QA pass — a QA analyst, a
|
|
Linux engineer, an architect, an admin, and a DevOps project manager
|
|
each signed off on production readiness. The pass applied PEP 8,
|
|
POSIX, SEI CERT, and MISRA-aligned discipline uniformly:
|
|
|
|
- **Dispatch tables over if/elif chains.** Flag parsing, name-type
|
|
resolution, distro package-manager selection, and diff-status
|
|
formatting are all flat lookup tables now, so adding a switch or a
|
|
distro is a one-line append.
|
|
- **Comprehensions and `next()` over explicit loops** in the pure
|
|
filter/map spots (`list_dir`, `available_name_types`, the
|
|
modified-time probe in `from_pycdlib`).
|
|
- **Narrow exception handling.** Every `except Exception` was either
|
|
narrowed to a specific tuple (`PyCdlibException`, `OSError`,
|
|
`ValueError`, `KeyError`) or marked as a deliberate top-level error
|
|
boundary with an auditable `# noqa: BLE001 -- <reason>` comment.
|
|
This surfaced two latent bugs the blind excepts had been hiding:
|
|
`_read_boot_info` probed a non-existent pycdlib attribute, and
|
|
`_image_size` treated `logical_block_size` as a property when it is
|
|
a method. Both are fixed.
|
|
- **Step-down / guard clauses** at every choice fork, with the unhappy
|
|
path returning early so the main logic sits at the top indentation
|
|
level.
|
|
- **Locked-in standards.** A `[tool.ruff]` block in `pyproject.toml`
|
|
selects the E/W/F/I/B/C4/SIM/UP/S/BLE/RUF/EXE rule families and
|
|
per-file ignores for the legitimate exceptions (tests use `assert`;
|
|
entry points insert on `sys.path` before imports). `ruff check .`
|
|
is part of the definition of done.
|
|
|
|
## What's Next
|
|
|
|
The roadmap for 1.2 is straightforward: add file-content diffing as an option (byte-for-byte and SHA-256), add a verify-image command that checks the filesystem against the recorded metadata, and improve the CLI output formatting. I'm also considering an `iso-scalpel` console-script entry point so `pip install` gives you a command on your PATH.
|
|
|
|
The repository is at git.dcos.net. It's GPL-2.0, and contributions are welcome.
|