dwipe 2.0.2__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.
dwipe/DrivePreChecker.py CHANGED
@@ -1,20 +1,142 @@
1
1
  #!/usr/bin/env python3
2
2
  import subprocess
3
- import os
4
3
  import json
5
- from typing import Dict, List, Optional
6
- from dataclasses import dataclass, field
4
+ from dataclasses import dataclass
5
+ from .SataTool import SataTool
6
+ from .Utils import Utils
7
7
 
8
8
  @dataclass
9
9
  class PreCheckResult:
10
- # Key = Short Code (Frozen, Locked), Value = Long Description
11
- issues: Dict[str, str] = field(default_factory=dict)
12
- modes: Dict[str, str] = field(default_factory=dict)
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 = {}
13
19
 
14
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
+
15
105
  def __init__(self, timeout: int = 10):
16
106
  self.timeout = timeout
17
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
+
18
140
  def check_nvme_drive(self, device: str) -> PreCheckResult:
19
141
  result = PreCheckResult()
20
142
  try:
@@ -24,24 +146,21 @@ class DrivePreChecker:
24
146
  )
25
147
 
26
148
  if id_ctrl.returncode != 0:
27
- result.issues['Unresponsive'] = "NVMe controller did not respond to id-ctrl"
149
+ result.issues['Unresponsive'] = "NVMe controller did not respond"
28
150
  return result
29
151
 
30
152
  data = json.loads(id_ctrl.stdout)
31
153
 
32
154
  # 1. Sanitize Support
33
- sanicap = data.get('sanicap', 0)
34
- if sanicap > 0:
35
- if sanicap & 0x04: result.modes['CryptoNv'] = 'sanitize --action=0x04'
36
- if sanicap & 0x02: result.modes['BlockNv'] = 'sanitize --action=0x02'
37
- if sanicap & 0x08: result.modes['OvwrNv'] = 'sanitize --action=0x03'
38
-
39
- # 2. Format Support (Legacy)
40
- if 'Format NVM' in id_ctrl.stdout:
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
41
160
  fna = data.get('fna', 0)
42
- if (fna >> 2) & 0x1:
43
- result.modes['FmtCryptoNv'] = 'format --ses=2'
44
- result.modes['FmtEraseNv'] = 'format --ses=1'
161
+ if fna & 0x04:
162
+ result.modes['FCrypto'] = 'format_crypto'
163
+ result.modes['FErase'] = 'format_erase'
45
164
 
46
165
  if not result.modes:
47
166
  result.issues['Unsupported'] = "Drive lacks Sanitize or Format NVM capabilities"
@@ -54,37 +173,31 @@ class DrivePreChecker:
54
173
  def check_ata_drive(self, device: str) -> PreCheckResult:
55
174
  result = PreCheckResult()
56
175
  try:
57
- info = subprocess.run(
58
- ['hdparm', '-I', device],
59
- check=False, capture_output=True, text=True, timeout=self.timeout
60
- )
61
-
62
- if info.returncode != 0:
63
- result.issues['Unresponsive'] = "Drive did not respond to hdparm"
64
- return result
65
-
66
- out = info.stdout.lower()
67
-
68
- # 1. Hardware Support Check
69
- if "security erase unit" not in out:
70
- result.issues['Unsupported'] = "Drive does not support ATA Security Erase"
71
- return result
72
-
73
- # 2. Frozen Check
74
- if "frozen" in out and "not frozen" not in out:
75
- result.issues['Frozen'] = "Drive is FROZEN (BIOS/OS lock). Cycle power or Suspend/Resume."
76
-
77
- # 3. Security Enabled (Password set)
78
- if "enabled" in out and "not enabled" not in out:
79
- result.issues['Locked'] = "Security is ENABLED (Drive is currently password locked)"
80
-
81
- # 4. Populate Modes only if no fatal issues
82
- if not result.issues:
83
- if "enhanced erase" in out:
84
- result.modes['EnhancedHd'] = '--user-master u --security-erase-enhanced NULL'
85
- result.modes['EraseHd'] = '--user-master u --security-erase NULL'
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
86
199
 
87
200
  except Exception as e:
88
201
  result.issues['Error'] = f"ATA Probe Exception: {str(e)}"
89
202
 
90
- return result
203
+ return result