dwipe 2.0.1__py3-none-any.whl → 3.0.0__py3-none-any.whl

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.
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env python3
2
+ import subprocess
3
+ import json
4
+ from dataclasses import dataclass
5
+ from .SataTool import SataTool
6
+ from .Utils import Utils
7
+
8
+ @dataclass
9
+ class PreCheckResult:
10
+ # Dict of capabilities and issues
11
+ issues: dict = None # e.g. {"Frozen": reason, "Locked": reason}
12
+ modes: dict = None # e.g. {"Crypto": ..., "Block": ..., "Ovwr": ...}
13
+
14
+ def __post_init__(self):
15
+ if self.issues is None:
16
+ self.issues = {}
17
+ if self.modes is None:
18
+ self.modes = {}
19
+
20
+ class DrivePreChecker:
21
+ # Firmware wipe rankings for SSDs: lower number = better (1 = most desirable)
22
+ # SSDs benefit from firmware wipes due to wear leveling and hidden blocks
23
+ WIPE_RANKS_SSD = {
24
+ # NVMe modes (rank 1-5)
25
+ 'Crypto': 1, # Sanitize Cryptographic Erase - fastest, instant key invalidation
26
+ 'Block': 2, # Sanitize Block Erase - fast, deallocates blocks
27
+ 'FCrypto': 3, # Format with Crypto Erase - fast, namespace reformat
28
+ 'FErase': 4, # Format with User Data Erase - less thorough than crypto
29
+ 'Ovwr': 5, # Sanitize Overwrite - slow, pattern write
30
+ # SATA SSD modes (rank 1-5)
31
+ 'SCrypto': 1, # Sanitize Cryptographic Erase - fastest
32
+ 'Enhanced': 2, # ATA Security Erase Enhanced - fast with crypto key destruction
33
+ 'SBlock': 3, # Sanitize Block Erase - fast
34
+ 'Erase': 4, # ATA Security Erase Normal - slow but widely supported
35
+ 'SOverwrite': 5, # Sanitize Overwrite - slow
36
+ # Software wipes (fallback - can't reach wear-leveled blocks on SSDs)
37
+ 'Rand': 6, # Software Random Write - fallback
38
+ 'Zero': 7, # Software Zero Write - fallback
39
+ }
40
+
41
+ # Rankings for HDDs: software wipes preferred (interruptible, resumable, progress)
42
+ # HDDs have no wear leveling or hidden blocks, so software wipes are equally thorough
43
+ WIPE_RANKS_HDD = {
44
+ # Software wipes preferred for HDDs
45
+ 'Rand': 1, # Software Random Write - preferred, interruptible, resumable
46
+ 'Zero': 2, # Software Zero Write - preferred, fast verification
47
+ # Firmware wipes available but not recommended (slow, no progress, not interruptible)
48
+ 'Enhanced': 3, # ATA Security Erase Enhanced - slow, no advantage over software
49
+ 'Erase': 4, # ATA Security Erase Normal - slow, no advantage over software
50
+ 'SCrypto': 5, # Sanitize Cryptographic Erase - rare on HDDs
51
+ 'SBlock': 6, # Sanitize Block Erase - rare on HDDs
52
+ 'SOverwrite': 7, # Sanitize Overwrite - very slow
53
+ }
54
+
55
+ # Default to SSD rankings (used for display, etc.)
56
+ WIPE_RANKS = WIPE_RANKS_SSD
57
+
58
+ @staticmethod
59
+ def sort_modes_by_rank(modes, add_star=True, is_rotational=False):
60
+ """Sort wipe modes from worst to best rank (highest rank number first).
61
+
62
+ Args:
63
+ modes: iterable of mode names (e.g., ['Crypto', 'Block', 'Ovwr'])
64
+ add_star: if True, append '*' to the last (best) mode
65
+ is_rotational: if True, use HDD rankings (prefer software wipes)
66
+
67
+ Returns:
68
+ list of mode names sorted worst to best, optionally with '*' on last
69
+ """
70
+ ranks = DrivePreChecker.WIPE_RANKS_HDD if is_rotational else DrivePreChecker.WIPE_RANKS_SSD
71
+ # Sort by rank descending (worst first), unknown modes get rank 99
72
+ sorted_modes = sorted(modes, key=lambda m: ranks.get(m, 99), reverse=True)
73
+ if add_star and sorted_modes:
74
+ sorted_modes[-1] = sorted_modes[-1] + '*'
75
+ return sorted_modes
76
+
77
+ @staticmethod
78
+ def get_fw_caps_summary(modes):
79
+ """Get a compact summary of firmware wipe capabilities.
80
+
81
+ Args:
82
+ modes: iterable of mode names (e.g., ['Crypto', 'Block', 'Ovwr'])
83
+
84
+ Returns:
85
+ str: Summary like 'ϟCrypto', 'ϟErase', or 'ϟOverwrite' based on best available,
86
+ or empty string if no firmware modes available
87
+ """
88
+ if not modes:
89
+ return ''
90
+
91
+ ranks = DrivePreChecker.WIPE_RANKS
92
+ # Find the best (lowest rank) mode
93
+ best_mode = min(modes, key=lambda m: ranks.get(m, 99))
94
+ best_rank = ranks.get(best_mode, 99)
95
+
96
+ # Categorize: Crypto (rank 1 or name contains Crypto), Erase (2-4), Overwrite (5)
97
+ if best_rank == 1 or 'Crypto' in best_mode:
98
+ return 'ϟCrypto'
99
+ elif best_rank <= 4:
100
+ return 'ϟErase'
101
+ elif best_rank == 5:
102
+ return 'ϟOverwrite'
103
+ return ''
104
+
105
+ def __init__(self, timeout: int = 10):
106
+ self.timeout = timeout
107
+
108
+ @staticmethod
109
+ def get_wipe_command_args(wipe_type: str) -> str:
110
+ """Get command arguments for a given wipe type name.
111
+
112
+ Maps wipe mode names to their corresponding firmware command arguments.
113
+ This is a static mapping independent of device capabilities.
114
+
115
+ Args:
116
+ wipe_type: Wipe mode name (e.g., 'Crypto', 'Block', 'Enhanced', 'Erase')
117
+
118
+ Returns:
119
+ str: Command argument string (e.g., 'sanitize_crypto', 'enhanced')
120
+ Empty string if wipe_type is unknown
121
+ """
122
+ wipe_args_map = {
123
+ # NVMe sanitize operations
124
+ 'Crypto': 'sanitize_crypto',
125
+ 'Block': 'sanitize_block',
126
+ 'Ovwr': 'sanitize_overwrite',
127
+ # NVMe format operations
128
+ 'FCrypto': 'format_crypto',
129
+ 'FErase': 'format_erase',
130
+ # SATA sanitize operations
131
+ 'SCrypto': 'sanitize_crypto',
132
+ 'SBlock': 'sanitize_block',
133
+ 'SOverwrite': 'sanitize_overwrite',
134
+ # SATA/ATA security erase operations
135
+ 'Enhanced': 'enhanced',
136
+ 'Erase': 'normal',
137
+ }
138
+ return wipe_args_map.get(wipe_type, '')
139
+
140
+ def check_nvme_drive(self, device: str) -> PreCheckResult:
141
+ result = PreCheckResult()
142
+ try:
143
+ id_ctrl = subprocess.run(
144
+ ['nvme', 'id-ctrl', device, '-o', 'json'],
145
+ check=False, capture_output=True, text=True, timeout=self.timeout
146
+ )
147
+
148
+ if id_ctrl.returncode != 0:
149
+ result.issues['Unresponsive'] = "NVMe controller did not respond"
150
+ return result
151
+
152
+ data = json.loads(id_ctrl.stdout)
153
+
154
+ # 1. Sanitize Support
155
+ result.modes.update(Utils.parse_nvme_sanitize_capabilities(data))
156
+
157
+ # 2. Format Support
158
+ oncs = data.get('oncs', 0)
159
+ if oncs & 0x04: # Format NVM command supported
160
+ fna = data.get('fna', 0)
161
+ if fna & 0x04:
162
+ result.modes['FCrypto'] = 'format_crypto'
163
+ result.modes['FErase'] = 'format_erase'
164
+
165
+ if not result.modes:
166
+ result.issues['Unsupported'] = "Drive lacks Sanitize or Format NVM capabilities"
167
+
168
+ except Exception as e:
169
+ result.issues['Error'] = f"NVMe Probe Exception: {str(e)}"
170
+
171
+ return result
172
+
173
+ def check_ata_drive(self, device: str) -> PreCheckResult:
174
+ result = PreCheckResult()
175
+ try:
176
+ tool = SataTool(device)
177
+ verdict = tool.get_wipe_verdict()
178
+ if verdict == 'OK':
179
+ # Populate Modes only if no fatal issues
180
+ secures = tool.secures
181
+
182
+ # ATA Security Erase modes
183
+ if secures.enhanced_erase_supported:
184
+ result.modes['Enhanced'] = 'enhanced'
185
+ result.modes['Erase'] = 'normal'
186
+
187
+ # SATA Sanitize modes (if supported)
188
+ if secures.sanitize_supported:
189
+ if secures.sanitize_crypto_supported:
190
+ result.modes['SCrypto'] = 'sanitize_crypto'
191
+ if secures.sanitize_block_supported:
192
+ result.modes['SBlock'] = 'sanitize_block'
193
+ if secures.sanitize_overwrite_supported:
194
+ result.modes['SOverwrite'] = 'sanitize_overwrite'
195
+ elif verdict == 'DumbDevice':
196
+ pass # No security feature - don't report as error (e.g., USB thumb drive)
197
+ else:
198
+ result.issues[verdict] = verdict
199
+
200
+ except Exception as e:
201
+ result.issues['Error'] = f"ATA Probe Exception: {str(e)}"
202
+
203
+ return result