dwipe 1.0.5__py3-none-any.whl → 2.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/DeviceInfo.py +492 -0
- dwipe/DiskWipe.py +880 -0
- dwipe/PersistentState.py +195 -0
- dwipe/Utils.py +199 -0
- dwipe/WipeJob.py +1243 -0
- dwipe/WipeJobFuture.py +245 -0
- dwipe/main.py +19 -853
- dwipe-2.0.0.dist-info/METADATA +407 -0
- dwipe-2.0.0.dist-info/RECORD +13 -0
- dwipe/PowerWindow.py +0 -740
- dwipe-1.0.5.dist-info/METADATA +0 -72
- dwipe-1.0.5.dist-info/RECORD +0 -8
- {dwipe-1.0.5.dist-info → dwipe-2.0.0.dist-info}/WHEEL +0 -0
- {dwipe-1.0.5.dist-info → dwipe-2.0.0.dist-info}/entry_points.txt +0 -0
- {dwipe-1.0.5.dist-info → dwipe-2.0.0.dist-info}/licenses/LICENSE +0 -0
dwipe/WipeJobFuture.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Docstring for dwipe.WipeJobFuture. Probably, only winners here are:
|
|
3
|
+
- Detailed Status Dict - Returns structured status.
|
|
4
|
+
Low risk but not urgent since current status works fine.
|
|
5
|
+
- Custom Pattern Sequences - Adds 0xFF pattern support.
|
|
6
|
+
Low risk but adds complexity.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
class MaybeSomeDay:
|
|
10
|
+
def __init__(self, device_path, total_size, opts=None, resume_from=0, resume_mode=None):
|
|
11
|
+
""" Performance Throttling """
|
|
12
|
+
# ... existing initialization ...
|
|
13
|
+
|
|
14
|
+
# Performance throttling
|
|
15
|
+
self.max_speed_mbps = getattr(opts, 'max_speed_mbps', 0) # 0 = unlimited
|
|
16
|
+
self.min_speed_mbps = getattr(opts, 'min_speed_mbps', 0) # Minimum speed to trigger stall detection
|
|
17
|
+
|
|
18
|
+
# Stall detection and recovery
|
|
19
|
+
self.stall_timeout = getattr(opts, 'stall_timeout', 300) # Seconds before stall recovery (5 min)
|
|
20
|
+
self.last_progress_time = time.monotonic()
|
|
21
|
+
self.last_progress_bytes = resume_from
|
|
22
|
+
|
|
23
|
+
# Adaptive block sizing
|
|
24
|
+
self.adaptive_block_size = getattr(opts, 'adaptive_block_size', False)
|
|
25
|
+
self.current_write_size = WipeJob.WRITE_SIZE
|
|
26
|
+
|
|
27
|
+
# Temperature monitoring (for SSDs)
|
|
28
|
+
self.check_temperature = getattr(opts, 'check_temperature', False)
|
|
29
|
+
self.last_temp_check = 0
|
|
30
|
+
self.temp_check_interval = 60 # seconds
|
|
31
|
+
|
|
32
|
+
# Energy-efficient mode
|
|
33
|
+
self.energy_saver = getattr(opts, 'energy_saver', False)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _throttle_write_speed(self, bytes_written, start_time):
|
|
39
|
+
"""Throttle write speed to specified maximum"""
|
|
40
|
+
if self.max_speed_mbps <= 0:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
elapsed = time.monotonic() - start_time
|
|
44
|
+
if elapsed <= 0:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
actual_speed_mbps = (bytes_written / (1024 * 1024)) / elapsed
|
|
48
|
+
if actual_speed_mbps > self.max_speed_mbps:
|
|
49
|
+
# Calculate how long to sleep to hit target speed
|
|
50
|
+
target_time = (bytes_written / (1024 * 1024)) / self.max_speed_mbps
|
|
51
|
+
sleep_time = target_time - elapsed
|
|
52
|
+
if sleep_time > 0:
|
|
53
|
+
time.sleep(sleep_time)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _check_ssd_temperature(self):
|
|
58
|
+
"""Check SSD temperature and throttle if too hot"""
|
|
59
|
+
if not self.check_temperature:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
now = time.monotonic()
|
|
63
|
+
if now - self.last_temp_check < self.temp_check_interval:
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
self.last_temp_check = now
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Try to read temperature from SMART or sysfs
|
|
70
|
+
device_name = os.path.basename(self.device_path)
|
|
71
|
+
|
|
72
|
+
# Check sysfs for temperature
|
|
73
|
+
temp_paths = [
|
|
74
|
+
f"/sys/block/{device_name}/device/hwmon/hwmon*/temp1_input",
|
|
75
|
+
f"/sys/block/{device_name}/device/temperature",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
for temp_path in temp_paths:
|
|
79
|
+
for path in glob.glob(temp_path):
|
|
80
|
+
try:
|
|
81
|
+
with open(path, 'r') as f:
|
|
82
|
+
temp_millic = int(f.read().strip())
|
|
83
|
+
temp_c = temp_millic // 1000
|
|
84
|
+
|
|
85
|
+
if temp_c > 70: # Throttle if >70°C
|
|
86
|
+
# Reduce speed by 50%
|
|
87
|
+
old_max = self.max_speed_mbps
|
|
88
|
+
self.max_speed_mbps = max(10, old_max // 2)
|
|
89
|
+
if old_max != self.max_speed_mbps:
|
|
90
|
+
print(f"Temperature {temp_c}°C: Throttling to {self.max_speed_mbps} MB/s")
|
|
91
|
+
elif temp_c < 60 and self.max_speed_mbps != getattr(self.opts, 'max_speed_mbps', 0):
|
|
92
|
+
# Restore speed if cooled down
|
|
93
|
+
self.max_speed_mbps = getattr(self.opts, 'max_speed_mbps', 0)
|
|
94
|
+
|
|
95
|
+
except (OSError, ValueError):
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _energy_saver_write(self, fd, chunk, is_random_pass):
|
|
107
|
+
"""Write with energy saving considerations"""
|
|
108
|
+
if not self.energy_saver:
|
|
109
|
+
return os.write(fd, chunk)
|
|
110
|
+
|
|
111
|
+
# For energy saving:
|
|
112
|
+
# 1. Group writes
|
|
113
|
+
# 2. Add small delays between writes
|
|
114
|
+
# 3. Use larger blocks when possible
|
|
115
|
+
|
|
116
|
+
bytes_written = 0
|
|
117
|
+
chunk_size = len(chunk)
|
|
118
|
+
|
|
119
|
+
while bytes_written < chunk_size:
|
|
120
|
+
write_size = min(self.current_write_size, chunk_size - bytes_written)
|
|
121
|
+
sub_chunk = chunk[bytes_written:bytes_written + write_size]
|
|
122
|
+
|
|
123
|
+
# Write the chunk
|
|
124
|
+
written = os.write(fd, sub_chunk)
|
|
125
|
+
bytes_written += written
|
|
126
|
+
|
|
127
|
+
# Small delay for energy saving
|
|
128
|
+
if bytes_written < chunk_size:
|
|
129
|
+
time.sleep(0.001) # 1ms delay
|
|
130
|
+
|
|
131
|
+
# Update progress
|
|
132
|
+
self.total_written += written
|
|
133
|
+
|
|
134
|
+
return bytes_written
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _adjust_block_size(self, write_success, write_time):
|
|
143
|
+
"""Dynamically adjust block size based on performance"""
|
|
144
|
+
if not self.adaptive_block_size:
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
if write_success:
|
|
148
|
+
# Successful write - consider increasing block size
|
|
149
|
+
speed_mbps = (self.current_write_size / (1024 * 1024)) / write_time if write_time > 0 else 0
|
|
150
|
+
|
|
151
|
+
if speed_mbps > 100: # Good speed, try larger blocks
|
|
152
|
+
new_size = min(WipeJob.WRITE_SIZE * 4, self.current_write_size * 2)
|
|
153
|
+
if new_size != self.current_write_size:
|
|
154
|
+
self.current_write_size = new_size
|
|
155
|
+
print(f"Increased block size to {new_size // 1024}KB")
|
|
156
|
+
else:
|
|
157
|
+
# Write failed or slow - reduce block size
|
|
158
|
+
new_size = max(WipeJob.BLOCK_SIZE, self.current_write_size // 2)
|
|
159
|
+
if new_size != self.current_write_size:
|
|
160
|
+
self.current_write_size = new_size
|
|
161
|
+
print(f"Reduced block size to {new_size // 1024}KB")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def get_detailed_status(self):
|
|
170
|
+
"""Get detailed status including speed, ETA, and health metrics"""
|
|
171
|
+
elapsed, pct_str, rate_str, eta_str = self.get_status()
|
|
172
|
+
|
|
173
|
+
status = {
|
|
174
|
+
'elapsed': elapsed,
|
|
175
|
+
'percentage': pct_str,
|
|
176
|
+
'rate': rate_str,
|
|
177
|
+
'eta': eta_str,
|
|
178
|
+
'bytes_written': self.total_written,
|
|
179
|
+
'total_bytes': self.total_size * self.passes,
|
|
180
|
+
'current_pass': self.current_pass + 1 if not self.verify_phase else 'Verifying',
|
|
181
|
+
'total_passes': self.passes,
|
|
182
|
+
'verify_phase': self.verify_phase,
|
|
183
|
+
'verify_result': self.verify_result if hasattr(self, 'verify_result') else None,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# Add adaptive block size info if enabled
|
|
187
|
+
if self.adaptive_block_size:
|
|
188
|
+
status['block_size_kb'] = self.current_write_size // 1024
|
|
189
|
+
|
|
190
|
+
# Add throttle info if enabled
|
|
191
|
+
if self.max_speed_mbps > 0:
|
|
192
|
+
status['max_speed_mbps'] = self.max_speed_mbps
|
|
193
|
+
|
|
194
|
+
return status
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def get_custom_pattern(self, pass_number):
|
|
200
|
+
"""Get custom write pattern if specified"""
|
|
201
|
+
custom_patterns = getattr(self.opts, 'custom_patterns', None)
|
|
202
|
+
if custom_patterns and pass_number < len(custom_patterns):
|
|
203
|
+
pattern = custom_patterns[pass_number]
|
|
204
|
+
if pattern == 'random':
|
|
205
|
+
return True
|
|
206
|
+
elif pattern == 'zeros':
|
|
207
|
+
return False
|
|
208
|
+
elif pattern == 'ones':
|
|
209
|
+
# Special pattern: all ones (0xFF)
|
|
210
|
+
return 'ones'
|
|
211
|
+
|
|
212
|
+
# Fall back to standard pattern
|
|
213
|
+
mode_to_use = self.resume_mode if self.resume_mode else self.opts.wipe_mode.replace('+V', '')
|
|
214
|
+
return self.get_pass_pattern(pass_number, mode_to_use)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def run_benchmark(self, duration_seconds=30):
|
|
220
|
+
"""Run a benchmark to determine optimal settings"""
|
|
221
|
+
print(f"Running benchmark for {duration_seconds} seconds...")
|
|
222
|
+
|
|
223
|
+
benchmark_results = []
|
|
224
|
+
test_sizes = [4*1024, 64*1024, 512*1024, 1024*1024, 4*1024*1024] # 4KB to 4MB
|
|
225
|
+
|
|
226
|
+
for test_size in test_sizes:
|
|
227
|
+
self.current_write_size = test_size
|
|
228
|
+
start_time = time.monotonic()
|
|
229
|
+
bytes_written = 0
|
|
230
|
+
|
|
231
|
+
# Test write for duration_seconds
|
|
232
|
+
while time.monotonic() - start_time < duration_seconds:
|
|
233
|
+
# Perform test write
|
|
234
|
+
# ... benchmark logic ...
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
speed_mbps = bytes_written / (1024 * 1024) / duration_seconds
|
|
238
|
+
benchmark_results.append((test_size, speed_mbps))
|
|
239
|
+
print(f" Block size {test_size//1024}KB: {speed_mbps:.2f} MB/s")
|
|
240
|
+
|
|
241
|
+
# Find optimal block size
|
|
242
|
+
optimal_size = max(benchmark_results, key=lambda x: x[1])[0]
|
|
243
|
+
print(f"Optimal block size: {optimal_size//1024}KB")
|
|
244
|
+
|
|
245
|
+
return optimal_size
|