Update iso-prototype.py

This commit is contained in:
Kroese
2026-07-28 14:56:27 +02:00
committed by GitHub
parent 6afa31961e
commit c6a1bddfa1
+78
View File
@@ -477,6 +477,73 @@ def sync_tree(iso, tree: Path, logical_root: PurePosixPath) -> None:
add_file_all_namespaces(iso, root_path / name, logical)
def rebuild_windows_eltorito(iso, tree: Path) -> None:
"""Replace imported hidden boot entries with complete extracted images.
Microsoft installation media commonly stores the El Torito boot images as
hidden extents. When PyCdlib opens such an ISO, the catalog's 512-byte load
count is not enough to recover the real size of the hidden UEFI FAT image.
Writing the imported entry back can therefore truncate it to one sector.
Rebuild the catalog from the files extracted by the normal Windows image
preparation flow instead. Short root-level ISO9660 names are intentional:
the boot images only need stable Directory Records for PyCdlib and do not
need to be exposed through the UDF namespace used by Windows Setup.
"""
bios_source = tree / "boot/etfsboot.com"
uefi_source = tree / "efi/microsoft/boot/efisys_noprompt.bin"
missing = [str(path) for path in (bios_source, uefi_source) if not path.is_file()]
if missing:
raise PrototypeError(
"Cannot rebuild Windows El Torito catalog; missing boot image(s): "
+ ", ".join(missing)
)
bios_iso_path = "/PYBIOS.BIN;1"
uefi_iso_path = "/PYUEFI.BIN;1"
print("Rebuilding El Torito catalog from extracted boot images:")
print(f" BIOS: {bios_source} ({bios_source.stat().st_size} bytes)")
print(f" UEFI: {uefi_source} ({uefi_source.stat().st_size} bytes)")
# These private root-level records avoid depending on the source ISO's very
# small ISO9660 tree. The original Microsoft files remain available via UDF.
for iso_path in (bios_iso_path, uefi_iso_path):
try:
iso.get_record(iso_path=iso_path)
except Exception:
continue
iso.rm_file(iso_path=iso_path)
iso.add_file(str(bios_source), iso_path=bios_iso_path)
iso.add_file(str(uefi_source), iso_path=uefi_iso_path)
# Match the production genisoimage settings: no-emulation BIOS boot, eight
# 512-byte sectors loaded, and a boot info table patched into etfsboot.com.
iso.add_eltorito(
bios_iso_path,
boot_load_size=8,
platform_id=0x00,
boot_info_table=True,
media_name="noemul",
bootable=True,
boot_load_seg=0,
)
# A second add_eltorito() call creates an alternate catalog section. Leaving
# boot_load_size unset makes PyCdlib use the complete EFI FAT image rather
# than the misleading one-sector count imported from Microsoft media.
iso.add_eltorito(
uefi_iso_path,
platform_id=0xEF,
efi=True,
media_name="noemul",
bootable=True,
boot_load_seg=0,
)
def build_pycdlib(args: argparse.Namespace) -> None:
try:
import pycdlib # type: ignore
@@ -495,6 +562,14 @@ def build_pycdlib(args: argparse.Namespace) -> None:
try:
print(f"Opening source ISO: {source}")
iso.open(str(source))
# Microsoft media may import hidden El Torito records with an invalid
# index_in_parent. The Dockerfile applies a temporary PyCdlib fix that
# recovers the real child index, allowing this removal to succeed.
if getattr(iso, "eltorito_boot_catalog", None) is not None:
print("Removing imported El Torito catalog...")
iso.rm_eltorito()
for value in args.replace:
logical = PurePosixPath("/") / value.lstrip("/")
print(f"Replacing: {logical}")
@@ -503,6 +578,9 @@ def build_pycdlib(args: argparse.Namespace) -> None:
logical = PurePosixPath("/") / value.lstrip("/")
print(f"Synchronizing tree: {logical}")
sync_tree(iso, tree, logical)
rebuild_windows_eltorito(iso, tree)
print("Checking PyCdlib consistency...")
iso.force_consistency()
print(f"Writing output ISO: {output}")