isozap 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
isozap-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shariar Imtiaz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
isozap-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,223 @@
1
+ Metadata-Version: 2.4
2
+ Name: isozap
3
+ Version: 0.1.0
4
+ Summary: A safe cross-platform command-line utility for creating bootable USB drives from ISO images.
5
+ Author: Shariar Imtiaz
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/simtiaz019/IsoZap
8
+ Project-URL: Issues, https://github.com/simtiaz019/IsoZap/issues
9
+ Keywords: iso,usb,bootable-usb,usb-flasher,iso-flasher,windows,macos,linux,cli,bootable,disk,flash
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: System :: Installation/Setup
22
+ Classifier: Topic :: System :: Systems Administration
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.2; extra == "dev"
28
+ Requires-Dist: pytest>=8; extra == "dev"
29
+ Requires-Dist: twine>=5; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # IsoZap
33
+
34
+ Create bootable USB drives safely from your terminal.
35
+
36
+ IsoZap is a cross-platform command-line utility for creating bootable USB drives from ISO images on Windows, macOS, and Linux.
37
+
38
+ > **WARNING:** IsoZap writes directly to storage devices. Selecting the wrong disk may permanently destroy data. IsoZap includes safeguards designed to prevent accidental system-disk selection, but users remain responsible for confirming the correct destination device.
39
+
40
+ ## Overview
41
+
42
+ IsoZap discovers conservative removable/external whole-disk targets, validates the source and capacity, displays a destructive-operation summary, and requires explicit confirmation before writing. It supports raw hybrid images and specially prepared FAT32 Windows installer media.
43
+
44
+ ## Features
45
+
46
+ - Native device discovery for Windows, macOS, and Linux
47
+ - System-, boot-, internal-, and partition-target rejection
48
+ - Device identity and capacity revalidation immediately before writing
49
+ - Exact `ERASE` confirmation or an intentionally verbose automation flag
50
+ - Non-destructive `--dry-run`
51
+ - Source SHA-256 checking and optional raw-write read-back verification
52
+ - Windows x64 and ARM64 UEFI loader validation
53
+ - FAT32-compatible splitting of Windows `install.wim` files larger than 4 GiB
54
+ - Standard-library-only runtime
55
+
56
+ ## Supported operating systems
57
+
58
+ | Host | Raw/hybrid images | Prepared Windows installer media |
59
+ |---|---:|---:|
60
+ | Windows | Yes | Yes, UEFI-focused |
61
+ | macOS | Yes | Yes, UEFI-focused |
62
+ | Linux | Yes | Yes, UEFI-focused |
63
+
64
+ Python 3.9 or newer is required. ChromeOS, Android, iOS, and restricted Python environments generally do not expose raw USB disks and are not supported.
65
+
66
+ ## Supported ISO types and limitations
67
+
68
+ - Linux/Unix hybrid ISOs and raw `.img` files are written byte-for-byte.
69
+ - Arbitrary non-hybrid ISOs are not made bootable merely by writing them.
70
+ - Windows installer mode creates MBR + FAT32 media and validates the selected x64 or ARM64 UEFI loader.
71
+ - Windows installer mode does not guarantee legacy BIOS/CSM booting or support every customized ISO.
72
+ - Windows host support and creating media from a Windows ISO are separate capabilities.
73
+ - Platform logic and package builds are tested with mocks; release 0.1.0 has not yet completed broad physical-device testing.
74
+
75
+ ## Installation
76
+
77
+ Recommended:
78
+
79
+ ```bash
80
+ pipx install isozap
81
+ ```
82
+
83
+ Alternative:
84
+
85
+ ```bash
86
+ python -m pip install isozap
87
+ ```
88
+
89
+ Do not install IsoZap with `sudo pip` or an Administrator-level pip. Install as your normal user; elevate only the later flashing command when required.
90
+
91
+ ## Usage
92
+
93
+ ```text
94
+ isozap --help
95
+ isozap --version
96
+ python -m isozap --help
97
+ ```
98
+
99
+ The existing direct command style is retained:
100
+
101
+ ```bash
102
+ isozap IMAGE --type {hybrid,raw,windows}
103
+ ```
104
+
105
+ ## Device listing
106
+
107
+ ```bash
108
+ isozap --list
109
+ ```
110
+
111
+ Only whole disks considered removable or external and not identified as system/boot disks are displayed.
112
+
113
+ ## Flashing an ISO
114
+
115
+ macOS or Linux:
116
+
117
+ ```bash
118
+ sudo "$(command -v isozap)" ~/Downloads/ubuntu.iso --type hybrid
119
+ sudo "$(command -v isozap)" ~/Downloads/windows.iso --type windows --arch x64
120
+ ```
121
+
122
+ Windows Administrator PowerShell:
123
+
124
+ ```powershell
125
+ isozap C:\Users\me\Downloads\ubuntu.iso --type hybrid
126
+ isozap C:\Users\me\Downloads\windows.iso --type windows --arch x64
127
+ ```
128
+
129
+ Verify a publisher-provided source checksum and read back a raw write:
130
+
131
+ ```bash
132
+ sudo "$(command -v isozap)" image.iso --type hybrid --sha256 EXPECTED_SHA256 --verify-write
133
+ ```
134
+
135
+ For automation, `--device` still requires a currently safe discovered disk and `--yes-i-really-mean-it` explicitly replaces the prompt:
136
+
137
+ ```bash
138
+ sudo "$(command -v isozap)" image.img --type raw --device /dev/sdX --yes-i-really-mean-it
139
+ ```
140
+
141
+ Never guess a device identifier.
142
+
143
+ ## Dry-run
144
+
145
+ ```bash
146
+ isozap image.iso --type hybrid --device /dev/sdX --dry-run
147
+ ```
148
+
149
+ Dry-run performs source, target, identity, and capacity checks and displays the intended operation. It does not mount, unmount, repartition, format, write, or eject any device. Windows installer structural mounting is deferred until an actual operation.
150
+
151
+ ## Safety protections
152
+
153
+ IsoZap rejects partitions, internal/non-removable targets, known system or boot disks, insufficient capacity, targets that disappear, and targets whose size/name/hardware identity changes between selection and execution. Windows explicitly maps the running system drive; macOS protects startup and reported APFS physical stores; Linux follows the block-device tree containing `/`, `/boot`, or `/boot/efi`.
154
+
155
+ No automated safeguard can replace checking the displayed identifier, model, and size.
156
+
157
+ ## Administrator/root requirements
158
+
159
+ Listing and dry-run do not require application installation as an administrator. Actual raw-device access does. Windows users should open an Administrator terminal. macOS/Linux users should install normally, then use `sudo` only when launching the destructive flash operation.
160
+
161
+ ## Windows notes
162
+
163
+ IsoZap uses structured `Get-Disk`/`Get-Partition` PowerShell data and `\\.\PhysicalDriveN`; drive letters are never treated as physical disks. Raw writes take the selected disk offline during access and restore it afterward. Windows installer mode uses diskpart because repartitioning is necessary for MBR + FAT32 media. Windows' built-in formatter limits FAT32 to 32 GiB, so larger USB drives receive a 32 GiB installer partition and leave the remaining space unallocated.
164
+
165
+ ## macOS notes
166
+
167
+ IsoZap uses plist output from `diskutil`, rejects internal disks and the startup disk/known APFS backing stores, unmounts the selected whole disk, and writes through `/dev/rdiskN` for raw-image performance.
168
+
169
+ ## Linux notes
170
+
171
+ IsoZap uses JSON `lsblk` data and protects the top-level disk containing `/`, `/boot`, or `/boot/efi`, including nested LVM/device-mapper layouts represented in the block tree. Windows media additionally needs `parted`, `dosfstools`, and mount tools.
172
+
173
+ ## Development
174
+
175
+ macOS/Linux:
176
+
177
+ ```bash
178
+ python3 -m venv .venv
179
+ source .venv/bin/activate
180
+ python -m pip install -e ".[dev]"
181
+ ```
182
+
183
+ Windows PowerShell:
184
+
185
+ ```powershell
186
+ py -m venv .venv
187
+ .\.venv\Scripts\Activate.ps1
188
+ python -m pip install -e ".[dev]"
189
+ ```
190
+
191
+ ## Testing
192
+
193
+ ```bash
194
+ python -m pytest
195
+ isozap --help
196
+ isozap --version
197
+ python -m isozap --help
198
+ ```
199
+
200
+ Tests mock destructive and system-level operations. Never point a test at a real disk.
201
+
202
+ ## Building
203
+
204
+ ```bash
205
+ python -m build
206
+ python -m twine check dist/*
207
+ ```
208
+
209
+ ## Contributing
210
+
211
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Platform changes require mocked safety tests.
212
+
213
+ ## Security
214
+
215
+ See [SECURITY.md](SECURITY.md) for private reporting guidance and security scope.
216
+
217
+ ## License
218
+
219
+ IsoZap is available under the MIT License. See [LICENSE](LICENSE).
220
+
221
+ ## Disclaimer
222
+
223
+ IsoZap is provided without warranty. Successful writing does not guarantee that an image supports USB booting or that the target firmware supports the image. Back up important data and test only with a disposable USB drive until you are confident in your platform and image combination.
isozap-0.1.0/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # IsoZap
2
+
3
+ Create bootable USB drives safely from your terminal.
4
+
5
+ IsoZap is a cross-platform command-line utility for creating bootable USB drives from ISO images on Windows, macOS, and Linux.
6
+
7
+ > **WARNING:** IsoZap writes directly to storage devices. Selecting the wrong disk may permanently destroy data. IsoZap includes safeguards designed to prevent accidental system-disk selection, but users remain responsible for confirming the correct destination device.
8
+
9
+ ## Overview
10
+
11
+ IsoZap discovers conservative removable/external whole-disk targets, validates the source and capacity, displays a destructive-operation summary, and requires explicit confirmation before writing. It supports raw hybrid images and specially prepared FAT32 Windows installer media.
12
+
13
+ ## Features
14
+
15
+ - Native device discovery for Windows, macOS, and Linux
16
+ - System-, boot-, internal-, and partition-target rejection
17
+ - Device identity and capacity revalidation immediately before writing
18
+ - Exact `ERASE` confirmation or an intentionally verbose automation flag
19
+ - Non-destructive `--dry-run`
20
+ - Source SHA-256 checking and optional raw-write read-back verification
21
+ - Windows x64 and ARM64 UEFI loader validation
22
+ - FAT32-compatible splitting of Windows `install.wim` files larger than 4 GiB
23
+ - Standard-library-only runtime
24
+
25
+ ## Supported operating systems
26
+
27
+ | Host | Raw/hybrid images | Prepared Windows installer media |
28
+ |---|---:|---:|
29
+ | Windows | Yes | Yes, UEFI-focused |
30
+ | macOS | Yes | Yes, UEFI-focused |
31
+ | Linux | Yes | Yes, UEFI-focused |
32
+
33
+ Python 3.9 or newer is required. ChromeOS, Android, iOS, and restricted Python environments generally do not expose raw USB disks and are not supported.
34
+
35
+ ## Supported ISO types and limitations
36
+
37
+ - Linux/Unix hybrid ISOs and raw `.img` files are written byte-for-byte.
38
+ - Arbitrary non-hybrid ISOs are not made bootable merely by writing them.
39
+ - Windows installer mode creates MBR + FAT32 media and validates the selected x64 or ARM64 UEFI loader.
40
+ - Windows installer mode does not guarantee legacy BIOS/CSM booting or support every customized ISO.
41
+ - Windows host support and creating media from a Windows ISO are separate capabilities.
42
+ - Platform logic and package builds are tested with mocks; release 0.1.0 has not yet completed broad physical-device testing.
43
+
44
+ ## Installation
45
+
46
+ Recommended:
47
+
48
+ ```bash
49
+ pipx install isozap
50
+ ```
51
+
52
+ Alternative:
53
+
54
+ ```bash
55
+ python -m pip install isozap
56
+ ```
57
+
58
+ Do not install IsoZap with `sudo pip` or an Administrator-level pip. Install as your normal user; elevate only the later flashing command when required.
59
+
60
+ ## Usage
61
+
62
+ ```text
63
+ isozap --help
64
+ isozap --version
65
+ python -m isozap --help
66
+ ```
67
+
68
+ The existing direct command style is retained:
69
+
70
+ ```bash
71
+ isozap IMAGE --type {hybrid,raw,windows}
72
+ ```
73
+
74
+ ## Device listing
75
+
76
+ ```bash
77
+ isozap --list
78
+ ```
79
+
80
+ Only whole disks considered removable or external and not identified as system/boot disks are displayed.
81
+
82
+ ## Flashing an ISO
83
+
84
+ macOS or Linux:
85
+
86
+ ```bash
87
+ sudo "$(command -v isozap)" ~/Downloads/ubuntu.iso --type hybrid
88
+ sudo "$(command -v isozap)" ~/Downloads/windows.iso --type windows --arch x64
89
+ ```
90
+
91
+ Windows Administrator PowerShell:
92
+
93
+ ```powershell
94
+ isozap C:\Users\me\Downloads\ubuntu.iso --type hybrid
95
+ isozap C:\Users\me\Downloads\windows.iso --type windows --arch x64
96
+ ```
97
+
98
+ Verify a publisher-provided source checksum and read back a raw write:
99
+
100
+ ```bash
101
+ sudo "$(command -v isozap)" image.iso --type hybrid --sha256 EXPECTED_SHA256 --verify-write
102
+ ```
103
+
104
+ For automation, `--device` still requires a currently safe discovered disk and `--yes-i-really-mean-it` explicitly replaces the prompt:
105
+
106
+ ```bash
107
+ sudo "$(command -v isozap)" image.img --type raw --device /dev/sdX --yes-i-really-mean-it
108
+ ```
109
+
110
+ Never guess a device identifier.
111
+
112
+ ## Dry-run
113
+
114
+ ```bash
115
+ isozap image.iso --type hybrid --device /dev/sdX --dry-run
116
+ ```
117
+
118
+ Dry-run performs source, target, identity, and capacity checks and displays the intended operation. It does not mount, unmount, repartition, format, write, or eject any device. Windows installer structural mounting is deferred until an actual operation.
119
+
120
+ ## Safety protections
121
+
122
+ IsoZap rejects partitions, internal/non-removable targets, known system or boot disks, insufficient capacity, targets that disappear, and targets whose size/name/hardware identity changes between selection and execution. Windows explicitly maps the running system drive; macOS protects startup and reported APFS physical stores; Linux follows the block-device tree containing `/`, `/boot`, or `/boot/efi`.
123
+
124
+ No automated safeguard can replace checking the displayed identifier, model, and size.
125
+
126
+ ## Administrator/root requirements
127
+
128
+ Listing and dry-run do not require application installation as an administrator. Actual raw-device access does. Windows users should open an Administrator terminal. macOS/Linux users should install normally, then use `sudo` only when launching the destructive flash operation.
129
+
130
+ ## Windows notes
131
+
132
+ IsoZap uses structured `Get-Disk`/`Get-Partition` PowerShell data and `\\.\PhysicalDriveN`; drive letters are never treated as physical disks. Raw writes take the selected disk offline during access and restore it afterward. Windows installer mode uses diskpart because repartitioning is necessary for MBR + FAT32 media. Windows' built-in formatter limits FAT32 to 32 GiB, so larger USB drives receive a 32 GiB installer partition and leave the remaining space unallocated.
133
+
134
+ ## macOS notes
135
+
136
+ IsoZap uses plist output from `diskutil`, rejects internal disks and the startup disk/known APFS backing stores, unmounts the selected whole disk, and writes through `/dev/rdiskN` for raw-image performance.
137
+
138
+ ## Linux notes
139
+
140
+ IsoZap uses JSON `lsblk` data and protects the top-level disk containing `/`, `/boot`, or `/boot/efi`, including nested LVM/device-mapper layouts represented in the block tree. Windows media additionally needs `parted`, `dosfstools`, and mount tools.
141
+
142
+ ## Development
143
+
144
+ macOS/Linux:
145
+
146
+ ```bash
147
+ python3 -m venv .venv
148
+ source .venv/bin/activate
149
+ python -m pip install -e ".[dev]"
150
+ ```
151
+
152
+ Windows PowerShell:
153
+
154
+ ```powershell
155
+ py -m venv .venv
156
+ .\.venv\Scripts\Activate.ps1
157
+ python -m pip install -e ".[dev]"
158
+ ```
159
+
160
+ ## Testing
161
+
162
+ ```bash
163
+ python -m pytest
164
+ isozap --help
165
+ isozap --version
166
+ python -m isozap --help
167
+ ```
168
+
169
+ Tests mock destructive and system-level operations. Never point a test at a real disk.
170
+
171
+ ## Building
172
+
173
+ ```bash
174
+ python -m build
175
+ python -m twine check dist/*
176
+ ```
177
+
178
+ ## Contributing
179
+
180
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Platform changes require mocked safety tests.
181
+
182
+ ## Security
183
+
184
+ See [SECURITY.md](SECURITY.md) for private reporting guidance and security scope.
185
+
186
+ ## License
187
+
188
+ IsoZap is available under the MIT License. See [LICENSE](LICENSE).
189
+
190
+ ## Disclaimer
191
+
192
+ IsoZap is provided without warranty. Successful writing does not guarantee that an image supports USB booting or that the target firmware supports the image. Back up important data and test only with a disposable USB drive until you are confident in your platform and image combination.
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "isozap"
7
+ version = "0.1.0"
8
+ description = "A safe cross-platform command-line utility for creating bootable USB drives from ISO images."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ authors = [{ name = "Shariar Imtiaz" }]
13
+ keywords = ["iso", "usb", "bootable-usb", "usb-flasher", "iso-flasher", "windows", "macos", "linux", "cli", "bootable", "disk", "flash"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Operating System :: MacOS",
18
+ "Operating System :: Microsoft :: Windows",
19
+ "Operating System :: POSIX :: Linux",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: System :: Installation/Setup",
27
+ "Topic :: System :: Systems Administration",
28
+ ]
29
+
30
+ [project.scripts]
31
+ isozap = "isozap.cli:main"
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/simtiaz019/IsoZap"
35
+ Issues = "https://github.com/simtiaz019/IsoZap/issues"
36
+
37
+ [project.optional-dependencies]
38
+ dev = ["build>=1.2", "pytest>=8", "twine>=5"]
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
42
+
43
+ [tool.pytest.ini_options]
44
+ addopts = "-q"
45
+ testpaths = ["tests"]
isozap-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """IsoZap: create bootable USB media safely."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("isozap")
7
+ except PackageNotFoundError: # source tree without an installed distribution
8
+ __version__ = "0+unknown"
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,151 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from . import __version__
8
+ from .devices import safe_disks
9
+ from .flash import raw_write, sha256
10
+ from .models import Disk
11
+ from .safety import assert_safe
12
+ from .utils import IsoZapError, host_os, human_bytes, require_admin, validate_image
13
+ from .windows_media import create_windows_media, inspect_windows_iso
14
+
15
+
16
+ def parser() -> argparse.ArgumentParser:
17
+ result = argparse.ArgumentParser(
18
+ prog="isozap",
19
+ description="Create bootable USB drives safely from your terminal.",
20
+ )
21
+ result.add_argument("image", nargs="?", type=Path, help="ISO or IMG file")
22
+ result.add_argument("--type", choices=("windows", "hybrid", "raw"), dest="image_type", help="image creation method")
23
+ result.add_argument("--arch", choices=("x64", "arm64"), default="x64", help="Windows target architecture")
24
+ result.add_argument("--device", help="whole target device; must be detected as removable/external")
25
+ result.add_argument("--sha256", dest="expected_hash", help="verify the source's publisher-provided SHA-256")
26
+ result.add_argument("--verify-write", action="store_true", help="read back and checksum a raw write")
27
+ result.add_argument("--dry-run", action="store_true", help="validate and show operations without changing a device")
28
+ result.add_argument("--list", action="store_true", help="list safe removable/external disks and exit")
29
+ result.add_argument("--yes-i-really-mean-it", action="store_true", help="non-interactive destructive confirmation")
30
+ result.add_argument("--version", action="version", version=f"IsoZap {__version__}")
31
+ return result
32
+
33
+
34
+ def choose(prompt: str, values: list, render):
35
+ if not values:
36
+ raise IsoZapError("No safe removable USB disks were detected.")
37
+ print(prompt)
38
+ for index, value in enumerate(values, 1):
39
+ print(f" {index}) {render(value)}")
40
+ while True:
41
+ try:
42
+ selected = int(input("Selection: "))
43
+ if 1 <= selected <= len(values):
44
+ return values[selected - 1]
45
+ except ValueError:
46
+ pass
47
+ print("Enter a number from the list.")
48
+
49
+
50
+ def progress(label: str):
51
+ def report(done: int, total: int, elapsed: float) -> None:
52
+ speed = done / elapsed
53
+ end = "\n" if done == total else ""
54
+ print(
55
+ f"\r{label}: {done * 100 / total:6.2f}% "
56
+ f"{human_bytes(done)} / {human_bytes(total)} {human_bytes(int(speed))}/s",
57
+ end=end, flush=True,
58
+ )
59
+ return report
60
+
61
+
62
+ def _select_disk(requested: str | None, disks: list[Disk]) -> Disk:
63
+ if requested:
64
+ matches = [item for item in disks if item.path.casefold() == requested.casefold()]
65
+ if not matches:
66
+ raise IsoZapError("Requested target is not in the safe whole removable-disk list.")
67
+ return matches[0]
68
+ return choose(
69
+ "Target USB (ALL DATA WILL BE DESTROYED):",
70
+ disks,
71
+ lambda item: f"{item.path} — {human_bytes(item.size)} — {item.name}",
72
+ )
73
+
74
+
75
+ def _overview(system: str, image: Path, disk: Disk, image_type: str) -> None:
76
+ print("\nWARNING: ALL DATA ON THIS DEVICE WILL BE DESTROYED.\n")
77
+ print(f"Host OS: {system}")
78
+ print(f"ISO/image: {image}")
79
+ print(f"ISO size: {human_bytes(image.stat().st_size)}")
80
+ print(f"Device: {disk.path}")
81
+ print(f"Model: {disk.name}")
82
+ print(f"Size: {human_bytes(disk.size)}")
83
+ print(f"External: {'Yes' if disk.external else 'No'}")
84
+ print(f"Removable: {'Yes' if disk.removable else 'No'}")
85
+ print(f"Method: {'FAT32 Windows installer preparation' if image_type == 'windows' else 'raw image write'}")
86
+
87
+
88
+ def main(argv: list[str] | None = None) -> int:
89
+ args = parser().parse_args(argv)
90
+ try:
91
+ system = host_os()
92
+ disks = safe_disks()
93
+ if args.list:
94
+ if not disks:
95
+ print(f"No safe removable USB disks detected on {system}.")
96
+ for disk in disks:
97
+ print(f"{disk.path}\t{human_bytes(disk.size)}\t{disk.name}\t{disk.transport}")
98
+ return 0
99
+
100
+ image_input = args.image or Path(input("Path to ISO/IMG: ").strip().strip("\"'"))
101
+ image = validate_image(image_input)
102
+ image_type = args.image_type or choose("Image type:", ["windows", "hybrid", "raw"], str)
103
+ disk = _select_disk(args.device, disks)
104
+ disk = assert_safe(disk, image, safe_disks)
105
+
106
+ if args.expected_hash:
107
+ print("Verifying source SHA-256...")
108
+ actual = sha256(image)
109
+ if actual.casefold() != args.expected_hash.casefold():
110
+ raise IsoZapError(f"SHA-256 mismatch. Expected {args.expected_hash}, got {actual}.")
111
+ print("Source SHA-256 verified.")
112
+
113
+ needs_split = inspect_windows_iso(image, args.arch) if image_type == "windows" and not args.dry_run else False
114
+ _overview(system, image, disk, image_type)
115
+ if args.dry_run:
116
+ print("\nDRY RUN: safety checks passed; no device was unmounted, formatted, or written.")
117
+ if image_type == "windows":
118
+ print("Windows ISO structural mounting is deferred until an actual operation.")
119
+ return 0
120
+ if not args.yes_i_really_mean_it and input("\nType exactly ERASE to continue: ") != "ERASE":
121
+ print("Cancelled; no changes were made.")
122
+ return 0
123
+
124
+ require_admin()
125
+ disk = assert_safe(disk, image, safe_disks)
126
+ if image_type == "windows":
127
+ create_windows_media(image, disk, args.arch, needs_split)
128
+ print("Write completed; required Windows boot files were found on the USB.")
129
+ else:
130
+ verified = raw_write(
131
+ image, disk, progress("Writing"), verify=args.verify_write,
132
+ verify_progress=progress("Verifying"),
133
+ )
134
+ if args.verify_write:
135
+ if not verified:
136
+ raise IsoZapError("Write completed, but read-back verification failed.")
137
+ print("Write completed and read-back SHA-256 verification passed.")
138
+ else:
139
+ print("Write completed. Read-back verification was not requested.")
140
+ print("The USB has been ejected where supported and can be removed safely.")
141
+ return 0
142
+ except KeyboardInterrupt:
143
+ print("\nCancelled by user.", file=sys.stderr)
144
+ return 130
145
+ except (IsoZapError, OSError, ValueError) as exc:
146
+ print(f"ERROR: {exc}", file=sys.stderr)
147
+ return 1
148
+
149
+
150
+ if __name__ == "__main__":
151
+ raise SystemExit(main())
@@ -0,0 +1,22 @@
1
+ """Compatibility exports for IsoZap's original public module."""
2
+
3
+ from .devices import safe_disks as list_disks
4
+ from .flash import raw_write, sha256
5
+ from .models import Disk
6
+ from .platforms.windows import physical_drive_number
7
+ from .utils import IsoZapError, host_os, human_bytes, require_admin, require_commands, run
8
+
9
+ FAT32_MAX = 4_294_967_295
10
+ WIM_SPLIT_MB = 3_800
11
+
12
+
13
+ def validate_target(disk: Disk, image_size: int) -> None:
14
+ current = {item.path: item for item in list_disks()}
15
+ if disk.path not in current:
16
+ raise IsoZapError("The selected removable disk is no longer present or is no longer safe.")
17
+ if image_size > current[disk.path].size:
18
+ raise IsoZapError("The source image is larger than the selected disk.")
19
+
20
+
21
+ def windows_loader(arch: str) -> str:
22
+ return "efi/boot/bootx64.efi" if arch == "x64" else "efi/boot/bootaa64.efi"