2727# IsoType enum
2828# ---------------------------------------------------------------------------
2929
30+
3031class IsoType (str , Enum ):
3132 WINDOWS = "windows"
32- LINUX = "linux"
33- OTHER = "other"
33+ LINUX = "linux"
34+ OTHER = "other"
3435
3536
3637# ---------------------------------------------------------------------------
@@ -40,11 +41,11 @@ class IsoType(str, Enum):
4041# ISO 9660: sector 16 = byte 32768. Within the PVD:
4142# byte 1– 5 : Standard Identifier "CD001"
4243# byte 40–71 : Volume Identifier (32 a-characters)
43- _PVD_OFFSET = 16 * 2048 # 32768
44- _PVD_MAGIC_OFFSET = _PVD_OFFSET + 1 # where "CD001" lives
45- _PVD_MAGIC = b"CD001"
46- _PVD_LABEL_OFFSET = _PVD_OFFSET + 40
47- _PVD_LABEL_SIZE = 32
44+ _PVD_OFFSET = 16 * 2048 # 32768
45+ _PVD_MAGIC_OFFSET = _PVD_OFFSET + 1 # where "CD001" lives
46+ _PVD_MAGIC = b"CD001"
47+ _PVD_LABEL_OFFSET = _PVD_OFFSET + 40
48+ _PVD_LABEL_SIZE = 32
4849
4950
5051def _read_pvd_label (iso_path : str ) -> str :
@@ -63,16 +64,49 @@ def _read_pvd_label(iso_path: str) -> str:
6364 return ""
6465
6566
67+ def _read_iso_label (iso_path : str ) -> str :
68+ """Read the ISO 9660 volume label at the fixed sector-16 offset.
69+
70+ Unlike _read_pvd_label this does not check the CD001 magic, making it
71+ useful for unit tests that write a minimal label-only fixture. Returns
72+ an empty string on OSError (e.g. missing file) or when the file is too
73+ small to contain a label.
74+ """
75+ try :
76+ with open (iso_path , "rb" ) as f :
77+ f .seek (_PVD_LABEL_OFFSET )
78+ raw = f .read (_PVD_LABEL_SIZE )
79+ if len (raw ) < _PVD_LABEL_SIZE :
80+ return ""
81+ return raw .decode ("ascii" , errors = "replace" ).strip ()
82+ except OSError as e :
83+ log .error ("detect: cannot read label from %s: %s" , iso_path , e )
84+ return ""
85+
86+
87+ def _label_is_windows (label : str ) -> bool :
88+ """Return True if *label* matches a known Windows ISO volume identifier.
89+
90+ Uses the pre-compiled _WIN_LABEL_RE regex. Any label beginning with
91+ "WIN" already covers all "WINDOWS…" variants, so no redundant prefix
92+ check is needed.
93+ """
94+ return bool (_WIN_LABEL_RE .match (label ))
95+
96+
6697# ---------------------------------------------------------------------------
6798# File-listing helpers — 7z first, isoinfo as fallback
6899# ---------------------------------------------------------------------------
69100
101+
70102def _list_with_7z (iso_path : str ) -> "str | None" :
71103 """Return the lowercased 7z file listing, or None on failure / not installed."""
72104 try :
73105 r = subprocess .run (
74106 ["7z" , "l" , iso_path ],
75- capture_output = True , text = True , timeout = 30 ,
107+ capture_output = True ,
108+ text = True ,
109+ timeout = 30 ,
76110 )
77111 if r .returncode == 0 :
78112 return r .stdout .lower ()
@@ -98,13 +132,15 @@ def _list_with_isoinfo(iso_path: str) -> "str | None":
98132 # If the ISO has no RR, isoinfo falls back to ISO 9660 names gracefully.
99133 r = subprocess .run (
100134 ["isoinfo" , "-f" , "-R" , "-i" , iso_path ],
101- capture_output = True , text = True , timeout = 30 ,
135+ capture_output = True ,
136+ text = True ,
137+ timeout = 30 ,
102138 )
103139 if r .returncode == 0 :
104140 lines = []
105141 for line in r .stdout .splitlines ():
106142 line = line .strip ().lstrip ("/" ).lower ()
107- line = re .sub (r";[0-9]+$" , "" , line ) # strip ;1 version suffix
143+ line = re .sub (r";[0-9]+$" , "" , line ) # strip ;1 version suffix
108144 lines .append (line )
109145 return "\n " .join (lines )
110146 log .warning ("detect: isoinfo exited %d" , r .returncode )
@@ -139,10 +175,10 @@ def _get_file_listing(iso_path: str) -> "str | None":
139175# Files that exist ONLY in Windows installation media.
140176# NOTE: EFI directories are deliberately absent — Windows ISOs also have efi/boot/.
141177_WIN_FILE_MARKERS = [
142- "sources/install.wim" , # Windows setup image (retail / OEM)
143- "sources/install.esd" , # Windows setup image (ESD download)
144- "sources/install.swm" , # Split setup image (multi-disc)
145- "sources/boot.wim" , # Windows PE boot image
178+ "sources/install.wim" , # Windows setup image (retail / OEM)
179+ "sources/install.esd" , # Windows setup image (ESD download)
180+ "sources/install.swm" , # Split setup image (multi-disc)
181+ "sources/boot.wim" , # Windows PE boot image
146182]
147183
148184
@@ -177,34 +213,27 @@ def _get_file_listing(iso_path: str) -> "str | None":
177213 "isolinux/isolinux.cfg" ,
178214 "syslinux/syslinux.cfg" ,
179215 "syslinux/ldlinux.c32" ,
180-
181216 # ---- GRUB config files — Linux-only for optical/USB media ----
182217 # Windows uses BCD / bootmgr; it never ships grub.cfg on install media.
183218 "boot/grub/grub.cfg" ,
184219 "boot/grub/i386-pc/" ,
185220 "grub/grub.cfg" ,
186-
187221 # ---- Ubuntu / Kubuntu / Xubuntu / Mint (Casper live system) ----
188222 "casper/filesystem.squashfs" ,
189223 "casper/filesystem.manifest" ,
190224 "casper/vmlinuz" ,
191-
192225 # ---- Debian / Kali / Tails / Parrot (live-boot) ----
193226 "live/filesystem.squashfs" ,
194227 "live/filesystem.manifest" ,
195228 "live/vmlinuz" ,
196-
197229 # ---- Ubuntu / Debian installer marker ----
198230 ".disk/info" ,
199-
200231 # ---- Arch Linux (specific sub-paths, not the broad "arch/" directory) ----
201232 "arch/pkglist.x86_64.txt" ,
202233 "arch/boot/x86_64/vmlinuz-linux" ,
203-
204234 # ---- Fedora / RHEL / CentOS installer (Anaconda) ----
205235 "images/pxeboot/vmlinuz" ,
206236 ".discinfo" ,
207-
208237 # ---- Generic kernel presence under known Linux-only directories ----
209238 "boot/vmlinuz" ,
210239 "boot/bzimage" ,
@@ -215,6 +244,7 @@ def _get_file_listing(iso_path: str) -> "str | None":
215244# Main detection entry point
216245# ---------------------------------------------------------------------------
217246
247+
218248def detect_iso_type (iso_path : str ) -> IsoType :
219249 """Detect the OS family of an ISO image.
220250
@@ -274,6 +304,7 @@ def detect_iso_type(iso_path: str) -> IsoType:
274304# Backward-compatible wrappers
275305# ---------------------------------------------------------------------------
276306
307+
277308def is_windows_iso (iso_path : str ) -> bool :
278309 """Return True if iso_path is a Windows installation image."""
279310 return detect_iso_type (iso_path ) == IsoType .WINDOWS
0 commit comments