Added initial ddrescue pass logic

This commit is contained in:
2Shirt 2019-12-30 20:21:37 -07:00
parent e88e4ab3eb
commit df6f3ba8e1
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
2 changed files with 65 additions and 15 deletions

View file

@ -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, },

View file

@ -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'):