Added initial ddrescue pass logic
This commit is contained in:
parent
e88e4ab3eb
commit
df6f3ba8e1
2 changed files with 65 additions and 15 deletions
|
|
@ -14,8 +14,12 @@ TMUX_LAYOUT = OrderedDict({
|
||||||
})
|
})
|
||||||
|
|
||||||
# ddrescue
|
# ddrescue
|
||||||
AUTO_PASS_1_THRESHOLD = 95
|
AUTO_PASS_THRESHOLDS = {
|
||||||
AUTO_PASS_2_THRESHOLD = 98
|
# NOTE: The scrape key is set to infinity to force a break
|
||||||
|
'read': 95,
|
||||||
|
'trim': 98,
|
||||||
|
'scrape': float('inf'),
|
||||||
|
}
|
||||||
DDRESCUE_SETTINGS = {
|
DDRESCUE_SETTINGS = {
|
||||||
'Default': {
|
'Default': {
|
||||||
'--binary-prefixes': {'Selected': True, 'Hidden': True, },
|
'--binary-prefixes': {'Selected': True, 'Hidden': True, },
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ class BlockPair():
|
||||||
self.load_map_data()
|
self.load_map_data()
|
||||||
|
|
||||||
# Set initial status
|
# Set initial status
|
||||||
percent = 100 * self.map_data.get('rescued', 0) / self.size
|
percent = self.get_percent_recovered()
|
||||||
for name in self.status.keys():
|
for name in self.status.keys():
|
||||||
if self.pass_complete(name):
|
if self.pass_complete(name):
|
||||||
self.status[name] = percent
|
self.status[name] = percent
|
||||||
|
|
@ -150,6 +150,10 @@ class BlockPair():
|
||||||
self.status[name] = percent
|
self.status[name] = percent
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def get_percent_recovered(self):
|
||||||
|
"""Get percent rescued from map_data, returns float."""
|
||||||
|
return 100 * self.map_data.get('rescued', 0) / self.size
|
||||||
|
|
||||||
def get_rescued_size(self):
|
def get_rescued_size(self):
|
||||||
"""Get rescued size using map data.
|
"""Get rescued size using map data.
|
||||||
|
|
||||||
|
|
@ -581,6 +585,23 @@ class State():
|
||||||
# Done
|
# Done
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
def pass_above_threshold(self, pass_name):
|
||||||
|
"""Check if all block_pairs meet the pass threshold, returns bool."""
|
||||||
|
threshold = cfg.ddrescue.AUTO_PASS_THRESHOLDS[pass_name]
|
||||||
|
return all(
|
||||||
|
[p.get_percent_recovered() >= threshold for p in self.block_pairs],
|
||||||
|
)
|
||||||
|
|
||||||
|
def pass_complete(self, pass_name):
|
||||||
|
"""Check if all block_pairs completed pass_name, returns bool."""
|
||||||
|
return all([p.pass_complete(pass_name) for p in self.block_pairs])
|
||||||
|
|
||||||
|
def retry_all_passes(self):
|
||||||
|
"""Set all statuses to Pending."""
|
||||||
|
for pair in self.block_pairs:
|
||||||
|
for name in pair.status.keys():
|
||||||
|
pair.status[name] = 'Pending'
|
||||||
|
|
||||||
def safety_check(self, mode, working_dir):
|
def safety_check(self, mode, working_dir):
|
||||||
"""Run safety check and abort if necessary."""
|
"""Run safety check and abort if necessary."""
|
||||||
required_size = sum([pair.size for pair in self.block_pairs])
|
required_size = sum([pair.size for pair in self.block_pairs])
|
||||||
|
|
@ -687,8 +708,8 @@ class State():
|
||||||
)
|
)
|
||||||
report.append(
|
report.append(
|
||||||
std.color_string(
|
std.color_string(
|
||||||
f'{std.bytes_to_string(total_rescued, decimals=2):>{width}}',
|
[f'{std.bytes_to_string(total_rescued, decimals=2):>{width}}'],
|
||||||
get_percent_color(percent),
|
[get_percent_color(percent)],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
report.append(separator)
|
report.append(separator)
|
||||||
|
|
@ -1367,6 +1388,17 @@ def mount_raw_image_macos(path):
|
||||||
return loopback_path
|
return loopback_path
|
||||||
|
|
||||||
|
|
||||||
|
def run_ddrescue(state, block_pair, settings):
|
||||||
|
"""Run ddrescue using passed settings."""
|
||||||
|
state.update_progress_pane('Active')
|
||||||
|
print('Running ddrescue')
|
||||||
|
print(f' {block_pair.source} --> {block_pair.destination}')
|
||||||
|
print('Using these settings:')
|
||||||
|
for _s in settings:
|
||||||
|
print(f' {_s}')
|
||||||
|
std.pause()
|
||||||
|
|
||||||
|
|
||||||
def run_recovery(state, main_menu, settings_menu):
|
def run_recovery(state, main_menu, settings_menu):
|
||||||
"""Run recovery passes."""
|
"""Run recovery passes."""
|
||||||
atexit.register(state.save_debug_reports)
|
atexit.register(state.save_debug_reports)
|
||||||
|
|
@ -1390,17 +1422,31 @@ def run_recovery(state, main_menu, settings_menu):
|
||||||
|
|
||||||
# Check if retrying
|
# Check if retrying
|
||||||
if '--retrim' in settings:
|
if '--retrim' in settings:
|
||||||
for pair in state.block_pairs:
|
state.retry_all_passes()
|
||||||
for name in pair.status.keys():
|
|
||||||
pair.status[name] = 'Pending'
|
|
||||||
|
|
||||||
# TODO
|
# Run pass(es)
|
||||||
# Run ddrescue
|
for pass_name in ('read', 'trim', 'scrape'):
|
||||||
state.update_progress_pane('Active')
|
if state.pass_complete(pass_name):
|
||||||
print('ddrescue settings:')
|
# Skip to next pass
|
||||||
for arg in settings:
|
# NOTE: This bypasses auto_continue
|
||||||
print(f' {arg}')
|
continue
|
||||||
std.pause('Run ddrescue pass?')
|
|
||||||
|
# Run ddrescue
|
||||||
|
for pair in state.block_pairs:
|
||||||
|
if not pair.pass_complete(pass_name):
|
||||||
|
attempted_recovery = True
|
||||||
|
run_ddrescue(state, pair, settings)
|
||||||
|
|
||||||
|
# Continue or return to menu
|
||||||
|
all_complete = state.pass_complete(pass_name)
|
||||||
|
all_above_threshold = state.pass_above_threshold(pass_name)
|
||||||
|
if not (all_complete and all_above_threshold and auto_continue):
|
||||||
|
break
|
||||||
|
|
||||||
|
# Show warning if nothing was done
|
||||||
|
if not attempted_recovery:
|
||||||
|
std.print_warning('No actions performed')
|
||||||
|
std.print_standard(' ')
|
||||||
|
|
||||||
# Stop SMART/Journal
|
# Stop SMART/Journal
|
||||||
for pane in ('SMART', 'Journal'):
|
for pane in ('SMART', 'Journal'):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue