diskmgr 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.
Files changed (40) hide show
  1. diskmgr-0.1.0/.gitignore +60 -0
  2. diskmgr-0.1.0/CHANGELOG.md +50 -0
  3. diskmgr-0.1.0/DISCLAIMER.md +41 -0
  4. diskmgr-0.1.0/LICENSE +21 -0
  5. diskmgr-0.1.0/PKG-INFO +348 -0
  6. diskmgr-0.1.0/README.md +314 -0
  7. diskmgr-0.1.0/disk_manager/__init__.py +61 -0
  8. diskmgr-0.1.0/disk_manager/__main__.py +15 -0
  9. diskmgr-0.1.0/disk_manager/cli.py +642 -0
  10. diskmgr-0.1.0/disk_manager/clone/__init__.py +41 -0
  11. diskmgr-0.1.0/disk_manager/clone/device.py +331 -0
  12. diskmgr-0.1.0/disk_manager/clone/raw.py +582 -0
  13. diskmgr-0.1.0/disk_manager/clone/safety.py +185 -0
  14. diskmgr-0.1.0/disk_manager/errors.py +58 -0
  15. diskmgr-0.1.0/disk_manager/inventory/__init__.py +117 -0
  16. diskmgr-0.1.0/disk_manager/inventory/posix.py +123 -0
  17. diskmgr-0.1.0/disk_manager/inventory/windows.py +403 -0
  18. diskmgr-0.1.0/disk_manager/models.py +291 -0
  19. diskmgr-0.1.0/disk_manager/progress.py +211 -0
  20. diskmgr-0.1.0/disk_manager/py.typed +0 -0
  21. diskmgr-0.1.0/disk_manager/transfer/__init__.py +39 -0
  22. diskmgr-0.1.0/disk_manager/transfer/engine.py +356 -0
  23. diskmgr-0.1.0/disk_manager/transfer/hashing.py +143 -0
  24. diskmgr-0.1.0/disk_manager/transfer/paths.py +97 -0
  25. diskmgr-0.1.0/disk_manager/transfer/planner.py +212 -0
  26. diskmgr-0.1.0/disk_manager/transfer/scanner.py +194 -0
  27. diskmgr-0.1.0/disk_manager/tui/__init__.py +26 -0
  28. diskmgr-0.1.0/disk_manager/tui/app.py +970 -0
  29. diskmgr-0.1.0/disk_manager/tui/forms.py +411 -0
  30. diskmgr-0.1.0/disk_manager/tui/options.py +167 -0
  31. diskmgr-0.1.0/disk_manager/tui/screens.py +477 -0
  32. diskmgr-0.1.0/pyproject.toml +89 -0
  33. diskmgr-0.1.0/tests/__init__.py +0 -0
  34. diskmgr-0.1.0/tests/conftest.py +87 -0
  35. diskmgr-0.1.0/tests/test_cli.py +188 -0
  36. diskmgr-0.1.0/tests/test_clone.py +400 -0
  37. diskmgr-0.1.0/tests/test_models.py +181 -0
  38. diskmgr-0.1.0/tests/test_safety.py +135 -0
  39. diskmgr-0.1.0/tests/test_transfer.py +320 -0
  40. diskmgr-0.1.0/tests/test_tui.py +445 -0
@@ -0,0 +1,60 @@
1
+ # Environment
2
+ env/
3
+ venv/
4
+ ENV/
5
+ VENV/
6
+ .venv/
7
+
8
+ # Python cache
9
+ __pycache__/
10
+ *.py[cod]
11
+ *$py.class
12
+ *.so
13
+ .Python
14
+
15
+ # Build and distribution
16
+ build/
17
+ dist/
18
+ *.egg-info/
19
+ .eggs/
20
+ *.egg
21
+ *.whl
22
+ *.tar.gz
23
+ pip-log.txt
24
+ pip-delete-this-directory.txt
25
+
26
+ # Testing / typing / linting
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+ .tox/
33
+
34
+ # Data and downloads
35
+ dl/
36
+ downloads/
37
+ data/
38
+
39
+ # Databases
40
+ *.db
41
+ *.sqlite
42
+ *.sqlite3
43
+
44
+ # Secrets / local config
45
+ .env
46
+ *.local
47
+
48
+ # Local working notes (per-chat session summaries; never committed)
49
+ SCRATCHPAD.md
50
+
51
+ # IDE and OS
52
+ .DS_Store
53
+ .vscode/
54
+ .idea/
55
+ *.swp
56
+ *.swo
57
+ *~
58
+
59
+ # Temporary files
60
+ SCRATCHPAD.md
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to diskmgr. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/), and this project uses
5
+ [semantic versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] — 2026-08-29
10
+
11
+ First release.
12
+
13
+ ### Added
14
+
15
+ - **Inventory** — enumerate physical disks, partitions and volumes. Windows
16
+ queries `Get-Disk`/`Get-Partition`/`Get-Volume` in a single PowerShell
17
+ round-trip, falling back to `Win32_DiskDrive` CIM; Linux uses `lsblk`.
18
+ - **File transfer** — `copy`, `mirror` and `sync` between drives, with glob
19
+ include/exclude filters, blake2b verification, per-file retries and
20
+ extended-length path support. Each file is written to a temporary name and
21
+ atomically renamed, so an interrupted run never leaves a half-written file
22
+ that a later comparison would mistake for up to date.
23
+ - **Raw imaging and cloning** — disk to image, image to disk, and disk to disk,
24
+ with gzip-compressed images, sector-by-sector bad-sector recovery, and
25
+ read-back digest verification.
26
+ - **Safety model** — the system disk is refused; `--allow-system` downgrades it
27
+ to a warning but still requires the typed `ERASE DISK <n>` phrase even with
28
+ `--yes`. Cloning a disk onto itself, an undersized target, a read-only disk,
29
+ a destination nested inside its own source, and a mirror that would delete
30
+ its source are all refused before anything is written.
31
+ - **CLI** (`diskmgr`) — standard library only, so the tool installs with no
32
+ dependencies.
33
+ - **Terminal UI** (`diskmgr tui`) — optional, behind the `[tui]` extra. Reaches
34
+ every CLI capability, shows each target's full safety report before the
35
+ confirmation gate, and gives a blocked target no way through: the dialog
36
+ composes no input and no proceed button at all. Copy is preceded by a plan
37
+ preview that lists every action and runs the free-space check, which is this
38
+ flow's dry run.
39
+ - PEP 561 `py.typed` marker: the package is fully annotated.
40
+
41
+ ### Notes
42
+
43
+ - **This software destroys data by design and is provided AS IS, with no
44
+ warranty and no liability accepted.** See `DISCLAIMER.md` and `LICENSE`.
45
+
46
+ - Raw device access requires an elevated terminal on Windows, or `root` on
47
+ Linux. Listing disks and copying files do not.
48
+ - The build pins `hatchling<1.28`, which emits Metadata-Version 2.4. Newer
49
+ hatchling emits 2.5, which PyPI does not yet accept and `twine check`
50
+ rejects.
@@ -0,0 +1,41 @@
1
+ # Disclaimer
2
+
3
+ **This software permanently destroys data by design.** Cloning, imaging and
4
+ mirroring overwrite entire disks and delete files. A mistyped disk number or
5
+ path can wipe the wrong drive in seconds, and there is no undo.
6
+
7
+ **USE ENTIRELY AT YOUR OWN RISK.** This software is provided "AS IS", without
8
+ warranty of any kind, express or implied, including but not limited to the
9
+ warranties of merchantability, fitness for a particular purpose and
10
+ non-infringement. See the `LICENSE` file for the full terms, which govern.
11
+
12
+ To the maximum extent permitted by applicable law, **the author and
13
+ contributors accept no liability whatsoever** for any claim, damages or other
14
+ liability — including but not limited to data loss, data corruption, hardware
15
+ damage, rendering a system unbootable, business interruption, lost profits, or
16
+ any direct, indirect, incidental, special, exemplary or consequential damages —
17
+ arising from or in connection with this software or its use, however caused and
18
+ on any theory of liability, even if advised of the possibility of such damage.
19
+
20
+ **You are solely responsible for:**
21
+ - verifying that the target disk is the one you intend to write to, every time;
22
+ - maintaining tested, independent backups before running any destructive
23
+ operation;
24
+ - confirming you are authorised to access and modify the devices concerned;
25
+ - complying with all applicable laws, regulations, licences and organisational
26
+ policies where you use it.
27
+
28
+ This is **not** certified, validated or warranted for any regulated,
29
+ forensic, evidentiary, medical, safety-critical, or otherwise high-assurance
30
+ purpose, and must not be relied upon as the sole safeguard for irreplaceable
31
+ data. Its safety checks reduce risk; they do not eliminate it, and they can be
32
+ overridden deliberately.
33
+
34
+ If you do not accept these terms, do not install or use this software.
35
+
36
+ ---
37
+
38
+ This document restates, in plain language, the warranty disclaimer and
39
+ limitation of liability in the MIT licence under which this software is
40
+ distributed. **The `LICENSE` file is the governing legal text**; where the two
41
+ differ, `LICENSE` prevails. Nothing here is legal advice.
diskmgr-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pandiyaraj Karuppasamy
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.
diskmgr-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,348 @@
1
+ Metadata-Version: 2.4
2
+ Name: diskmgr
3
+ Version: 0.1.0
4
+ Summary: Manage hard disks and USB drives: enumerate, copy/sync files between drives, and raw block-level clone or image.
5
+ Project-URL: Repository, https://github.com/Pandiyarajk/disk-manager
6
+ Project-URL: Issues, https://github.com/Pandiyarajk/disk-manager/issues
7
+ Author-email: Pandiyaraj Karuppasamy <pandiyarajk@live.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: backup,clone,dd,disk,imaging,mirror,sync,usb,windows
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
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 :: Archiving :: Backup
22
+ Classifier: Topic :: System :: Filesystems
23
+ Classifier: Topic :: Utilities
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.0; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0; extra == 'dev'
29
+ Requires-Dist: textual<9,>=8; extra == 'dev'
30
+ Requires-Dist: twine>=4.0; extra == 'dev'
31
+ Provides-Extra: tui
32
+ Requires-Dist: textual<9,>=8; extra == 'tui'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # diskmgr
36
+
37
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue?logo=python&logoColor=white)](https://www.python.org/downloads/)
38
+ [![License](https://img.shields.io/badge/license-MIT-green)](#licence)
39
+ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux-lightgrey)](#install)
40
+ [![Status](https://img.shields.io/badge/status-beta-orange)](#disclaimer)
41
+ [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](#install)
42
+
43
+ [![Tests](https://img.shields.io/badge/tests-163%20passing-brightgreen?logo=pytest&logoColor=white)](#development)
44
+ [![Lint](https://img.shields.io/badge/lint-flake8-blueviolet)](#development)
45
+ [![Typed](https://img.shields.io/badge/typed-py.typed-blue)](#development)
46
+ [![Build](https://img.shields.io/badge/build-hatchling-4051b5)](#install)
47
+ [![TUI](https://img.shields.io/badge/TUI-Textual-5a4fcf)](#interactive-terminal-ui)
48
+ [![SemVer](https://img.shields.io/badge/semver-2.0.0-blue)](https://semver.org/)
49
+ [![Changelog](https://img.shields.io/badge/changelog-keep%20a%20changelog-orange)](https://keepachangelog.com/)
50
+
51
+ <!--
52
+ Badge notes, so nobody "fixes" what is not broken:
53
+
54
+ * Every badge above is STATIC and renders unconditionally - each was fetched and
55
+ its rendered text checked. Each states a fact about this repo: zero runtime
56
+ dependencies for the CLI, a PEP 561 py.typed marker, flake8 as the linter,
57
+ hatchling as the build backend, Textual behind the optional [tui] extra.
58
+
59
+ * The Tests badge is hand-maintained. Update the number when the suite changes.
60
+
61
+ * There is NO GitHub Actions status badge because this repo is PRIVATE.
62
+ shields.io queries api.github.com anonymously; a private repo returns 404, and
63
+ the badge renders "repo or workflow not found". That is an ACCESS error, not a
64
+ filename error - verified against this repo. GitHub's own badge.svg fails the
65
+ same way, because README images are proxied through camo, which also fetches
66
+ anonymously. The gist-backed workaround needs a CI workflow, and this repo
67
+ deliberately has none.
68
+
69
+ * The two PyPI badges below are DYNAMIC. They read "package or version not
70
+ found" until the first release is uploaded, so they are commented out to keep
71
+ the badge row clean. Uncomment both immediately after the first successful
72
+ `twine upload`:
73
+
74
+ [![PyPI](https://img.shields.io/pypi/v/diskmgr)](https://pypi.org/project/diskmgr/)
75
+ [![Downloads](https://img.shields.io/pypi/dm/diskmgr)](https://pypi.org/project/diskmgr/)
76
+
77
+ * Badge targets are in-document anchors, not relative file links: this README is
78
+ also the PyPI long description, where a relative link 404s.
79
+ -->
80
+
81
+ Manage hard disks and USB drives from one tool: list what is attached, copy or
82
+ mirror files between drives, and clone a whole disk sector-for-sector.
83
+
84
+ Windows-first (it uses the Storage PowerShell module and raw `\\.\PhysicalDrive`
85
+ access); disk listing and all file-level operations also work on Linux.
86
+
87
+ > ## Disclaimer
88
+ >
89
+ > ⚠️ **READ BEFORE USE.** **This software permanently destroys data by design.** Cloning, imaging and
90
+ > mirroring overwrite entire disks and delete files. A mistyped disk number or
91
+ > path can wipe the wrong drive in seconds, and there is no undo.
92
+ >
93
+ > **USE ENTIRELY AT YOUR OWN RISK.** This software is provided "AS IS", without
94
+ > warranty of any kind, express or implied, including but not limited to the
95
+ > warranties of merchantability, fitness for a particular purpose and
96
+ > non-infringement. See [the licence](#licence) for the full terms, which govern.
97
+ >
98
+ > To the maximum extent permitted by applicable law, **the author and
99
+ > contributors accept no liability whatsoever** for any claim, damages or other
100
+ > liability — including but not limited to data loss, data corruption, hardware
101
+ > damage, rendering a system unbootable, business interruption, lost profits, or
102
+ > any direct, indirect, incidental, special, exemplary or consequential damages —
103
+ > arising from or in connection with this software or its use, however caused and
104
+ > on any theory of liability, even if advised of the possibility of such damage.
105
+ >
106
+ > **You are solely responsible for:**
107
+ > - verifying that the target disk is the one you intend to write to, every time;
108
+ > - maintaining tested, independent backups before running any destructive
109
+ > operation;
110
+ > - confirming you are authorised to access and modify the devices concerned;
111
+ > - complying with all applicable laws, regulations, licences and organisational
112
+ > policies where you use it.
113
+ >
114
+ > This is **not** certified, validated or warranted for any regulated,
115
+ > forensic, evidentiary, medical, safety-critical, or otherwise high-assurance
116
+ > purpose, and must not be relied upon as the sole safeguard for irreplaceable
117
+ > data. Its safety checks reduce risk; they do not eliminate it, and they can be
118
+ > overridden deliberately.
119
+ >
120
+ > If you do not accept these terms, do not install or use this software.
121
+
122
+ ## Why
123
+
124
+ Copying a folder to a backup drive and cloning a bootable USB stick are two
125
+ different jobs that usually need two different tools. This does both, with the
126
+ same safety model:
127
+
128
+ - **Nothing destructive happens without a typed confirmation.** Erasing disk 2
129
+ means typing `ERASE DISK 2`, not pressing `y`.
130
+ - **The system disk is refused outright**, and no combination of flags erases
131
+ the running OS without that phrase.
132
+ - **`--dry-run` prints the exact plan** a real run would execute — the same
133
+ plan object, not an approximation of it.
134
+ - **Everything written is verified**: files by hash, disks by reading the
135
+ target back and comparing digests.
136
+
137
+ ## Install
138
+
139
+ ```bash
140
+ pip install diskmgr # CLI only, zero dependencies
141
+ pip install "diskmgr[tui]" # adds the interactive terminal UI
142
+ ```
143
+
144
+ From a clone:
145
+
146
+ ```bash
147
+ pip install -e ".[dev]"
148
+ ```
149
+
150
+ ## Usage
151
+
152
+ The CLI is `diskmgr`; `python -m disk_manager` works too.
153
+
154
+ ### See what is attached
155
+
156
+ ```console
157
+ $ diskmgr list
158
+ # MODEL SIZE BUS STYLE FLAGS VOLUMES
159
+ ---------------------------------------------------------------------------------
160
+ 0 UMIS RPJTJ512MGE1QDQ 476.9 GB NVMe GPT SYSTEM C: [OS] NTFS, D: [Data] NTFS
161
+ 1 SanDisk Ultra USB 3.0 28.9 GB USB MBR REMOVABLE E: [STICK] FAT32
162
+ ```
163
+
164
+ ```bash
165
+ diskmgr list --json # machine-readable
166
+ diskmgr list --removable-only # just the USB sticks and SD cards
167
+ diskmgr info 1 # one disk in full, including write safety
168
+ ```
169
+
170
+ `diskmgr info` also tells you whether the disk may be written to and what the
171
+ confirmation phrase would be:
172
+
173
+ ```console
174
+ Write safety : REFUSED
175
+ BLOCKED: disk 0 carries the running operating system (C:, D:)
176
+ ```
177
+
178
+ A disk can be named by index (`1`), device path (`\\.\PhysicalDrive1`), or a
179
+ drive letter it hosts (`E:`).
180
+
181
+ ### Copy files between drives
182
+
183
+ ```bash
184
+ # Add new and changed files; never deletes anything
185
+ diskmgr copy D:\projects E:\backup\projects
186
+
187
+ # Make the destination identical to the source, deleting extras
188
+ diskmgr copy D:\projects E:\backup\projects --mode mirror
189
+
190
+ # Only take files that are strictly newer than the destination copy
191
+ diskmgr copy D:\projects E:\backup\projects --mode sync
192
+ ```
193
+
194
+ Useful flags:
195
+
196
+ | Flag | Effect |
197
+ |---|---|
198
+ | `--dry-run` | Print the plan, write nothing |
199
+ | `--show-files` | List every planned copy/overwrite/delete |
200
+ | `--verify hash` | Hash every written file against its source (default is `size`) |
201
+ | `--exclude GLOB` | Skip matching files or folders; repeatable |
202
+ | `--include GLOB` | Only copy matching files; repeatable |
203
+ | `--retries N` | Retries per file on a transient I/O error (default 2) |
204
+ | `--stop-on-error` | Abort on the first failure instead of continuing |
205
+
206
+ ```bash
207
+ diskmgr copy D:\code E:\backup --mode mirror \
208
+ --exclude "*.tmp" --exclude node_modules --exclude .git \
209
+ --verify hash --dry-run --show-files
210
+ ```
211
+
212
+ Each file is written to a temporary name and atomically renamed into place, so
213
+ an interrupted run leaves either the old file or the complete new one — never a
214
+ half-written file that the next run would mistake for up to date.
215
+
216
+ ### Image a disk
217
+
218
+ ```bash
219
+ # Read a USB stick into an image file (.gz compresses it)
220
+ diskmgr image save 1 D:\images\stick.img.gz
221
+
222
+ # Write it back, to the same stick or a different one
223
+ diskmgr image restore D:\images\stick.img.gz 1
224
+ ```
225
+
226
+ `image save` is read-only but still needs elevation, because raw device access
227
+ does. `--skip-bad-sectors` re-reads a failing block one sector at a time and
228
+ zero-fills only the sectors that genuinely cannot be read, reporting the count
229
+ rather than silently producing a short image.
230
+
231
+ ### Clone one disk onto another
232
+
233
+ ```bash
234
+ diskmgr clone 1 2 # copy disk 1 onto disk 2, sector for sector
235
+ ```
236
+
237
+ The target must be at least as large as the source. Before any byte is written
238
+ the tool locks and dismounts every volume on the target — if a program still
239
+ has a file open there, the clone is refused rather than corrupting a live
240
+ filesystem. Afterwards the target is read back and its digest compared with the
241
+ source:
242
+
243
+ ```console
244
+ source digest : 9f2c...
245
+ destination digest : 9f2c...
246
+ verification : PASSED
247
+ ```
248
+
249
+ ### Interactive terminal UI
250
+
251
+ ```bash
252
+ diskmgr tui
253
+ ```
254
+
255
+ | Key | Does |
256
+ |---|---|
257
+ | `↑` `↓` | Select a disk |
258
+ | `r` | Re-scan (runs off the UI thread; the interface stays responsive) |
259
+ | `u` | Show only removable devices, or everything again |
260
+ | `f` | Copy files — options form, then a plan preview |
261
+ | `i` | Save an image of the selected disk |
262
+ | `R` | **Restore** an image onto the selected disk (shifted on purpose) |
263
+ | `c` | Clone the selected disk onto another |
264
+ | `x` | Cancel the running operation |
265
+ | `q` | Quit — refused while an operation is writing |
266
+
267
+ Long operations run on a worker thread, so the interface stays responsive and
268
+ cancellable throughout, and a crash in one cannot leave the app wedged.
269
+
270
+ **Every flow starts with an options form** carrying the same settings as the
271
+ corresponding CLI command — transfer mode, verify depth, glob filters, retries,
272
+ bad-sector recovery, size limits, block size, digests. The last values you used
273
+ are pre-filled, so a repeated job is a couple of keystrokes. Paths can be typed
274
+ or browsed with a directory tree.
275
+
276
+ **The copy plan preview is the dry run.** Before anything is written you get the
277
+ full list of actions — deletions sorted to the top — plus the free-space check
278
+ and any folders that could not be scanned. Cancelling there is exactly a dry
279
+ run, and it shows *more* than `copy --dry-run` does, because the engine's own
280
+ dry run skips the free-space check. For that reason `--dry-run`, `--quiet` and
281
+ `--show-files` have no TUI equivalent: the preview replaces all three.
282
+ `--yes` is deliberately absent too — it exists so scripts can skip the
283
+ confirmation, which is not something an interactive UI should offer.
284
+
285
+ **Destructive operations show the whole safety picture before the gate**: the
286
+ target's blockers and warnings, whether you are elevated, and the exact phrase
287
+ required. If the target is refused — the system disk, a read-only or undersized
288
+ disk — or you are not elevated, the dialog has **no input and no proceed
289
+ button** at all. There is nothing to press.
290
+
291
+ Afterwards the verdict stays on screen until the next run, with **both full
292
+ digests**, so a clone that finished hours ago can still be checked against a
293
+ digest produced by the CLI:
294
+
295
+ ```
296
+ CLONED - VERIFICATION PASSED
297
+ 32.0 GB written in 12:41
298
+ source digest : 9f2c…
299
+ destination digest : 9f2c…
300
+ ```
301
+
302
+ If you are not running elevated, a banner says so on startup and the imaging,
303
+ restore and clone actions are disabled rather than failing several screens
304
+ later.
305
+
306
+ ## Safety model
307
+
308
+ | Guard | Behaviour |
309
+ |---|---|
310
+ | System disk | Refused. `--allow-system` downgrades it to a warning but **still** requires the typed phrase, even with `--yes`. |
311
+ | Confirmation | Exact phrase `ERASE DISK <n>`, case-insensitive. `--yes` skips it for non-system disks only. |
312
+ | Same disk | Cloning a disk onto itself is refused. |
313
+ | Size | A target smaller than the source or image is refused before any write. |
314
+ | Read-only | A write-protected disk is refused. |
315
+ | Mounted volumes | Locked and dismounted first; a busy volume aborts the operation. |
316
+ | Destination inside source | Refused — it would copy the transfer's own output. |
317
+ | Mirror onto a parent of the source | Refused — it would delete the source. |
318
+ | Not elevated | Surfaced in the TUI on startup and before the phrase gate, rather than as a failure after it. |
319
+
320
+ Raw device access needs an elevated terminal on Windows (or `root` on Linux).
321
+ Listing disks and copying files do not.
322
+
323
+ ## Development
324
+
325
+ ```bash
326
+ pip install -e ".[dev]"
327
+ pytest # 160 tests, no hardware or elevation required
328
+ flake8 disk_manager tests
329
+ ```
330
+
331
+ The raw-clone tests substitute a plain file for the block device, so the read,
332
+ write, hash and verify paths all execute for real — including bad-sector
333
+ recovery and cancellation — without touching any hardware.
334
+
335
+ The TUI is tested through Textual's own `run_test`/Pilot, needing no extra
336
+ dependency. Those tests pin the guarantees that matter: a blocked target's
337
+ dialog composes no confirmation input at all, declining the gate never starts
338
+ the clone engine, and a worker crashing with an exception the engine never
339
+ raises still leaves the app usable.
340
+
341
+ ## Licence
342
+
343
+ MIT — see the `LICENSE` file in the source distribution.
344
+
345
+ The MIT licence's warranty disclaimer and limitation of liability are the
346
+ operative legal terms and apply in full; the disclaimer at the top of this
347
+ document restates and expands on them in plain language. Nothing in this
348
+ README creates any warranty, guarantee or obligation of support.