Update iso-prototype.py

This commit is contained in:
Kroese
2026-07-28 16:22:37 +02:00
committed by GitHub
parent f7061bb94a
commit 441a044196
+98 -39
View File
@@ -577,23 +577,55 @@ 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:
def eltorito_platforms(path: Path) -> tuple[bool, bool]:
"""Return the BIOS and UEFI platforms present in the source catalog."""
_descriptors, catalog_lba = parse_volume_descriptors(path)
catalog = parse_boot_catalog(path, catalog_lba)
if catalog is None:
return False, False
platforms = {
entry["platform_id"]
for entry in catalog["entries"]
if entry["kind"] == "boot_entry"
}
return 0x00 in platforms, 0xEF in platforms
def rebuild_windows_eltorito(
iso,
tree: Path,
*,
include_bios: bool,
include_uefi: bool,
) -> 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
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.
Rebuild only the platforms that existed in the original ISO. This preserves
BIOS-only media such as Windows 7 x86 while retaining both BIOS and UEFI on
modern hybrid installation media.
"""
if not include_bios and not include_uefi:
return
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()]
missing: list[str] = []
if include_bios and not bios_source.is_file():
missing.append(str(bios_source))
if include_uefi and not uefi_source.is_file():
missing.append(str(uefi_source))
if missing:
raise PrototypeError(
"Cannot rebuild Windows El Torito catalog; missing boot image(s): "
@@ -604,45 +636,60 @@ def rebuild_windows_eltorito(iso, tree: Path) -> None:
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)")
if include_bios:
print(f" BIOS: {bios_source} ({bios_source.stat().st_size} bytes)")
if include_uefi:
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):
# small ISO9660 tree. The original Microsoft files remain available via UDF.
required_records: list[tuple[Path, str]] = []
if include_bios:
required_records.append((bios_source, bios_iso_path))
if include_uefi:
required_records.append((uefi_source, uefi_iso_path))
for source, iso_path in required_records:
try:
iso.get_record(iso_path=iso_path)
except Exception:
continue
iso.rm_file(iso_path=iso_path)
pass
else:
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)
iso.add_file(str(source), iso_path=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,
)
if include_bios:
# 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,
)
if include_uefi:
# 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:
@@ -663,6 +710,13 @@ def build_pycdlib(args: argparse.Namespace) -> None:
print(f"Opening source ISO: {source}")
iso.open(str(source))
include_bios, include_uefi = eltorito_platforms(source)
print(
"Imported El Torito platforms:"
f" BIOS={'yes' if include_bios else 'no'},"
f" UEFI={'yes' if include_uefi else 'no'}"
)
# 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.
@@ -679,7 +733,12 @@ def build_pycdlib(args: argparse.Namespace) -> None:
print(f"Synchronizing tree: {logical}")
sync_tree(iso, tree, logical)
rebuild_windows_eltorito(iso, tree)
rebuild_windows_eltorito(
iso,
tree,
include_bios=include_bios,
include_uefi=include_uefi,
)
print("Checking PyCdlib consistency...")
iso.force_consistency()