diff --git a/scripts/wk/hw/ddrescue.py b/scripts/wk/hw/ddrescue.py index 7fe5f805..da66ecd3 100644 --- a/scripts/wk/hw/ddrescue.py +++ b/scripts/wk/hw/ddrescue.py @@ -82,6 +82,7 @@ MENU_ACTIONS = ( 'Add Tech Note', f'Change settings {std.color_string("(experts only)", "YELLOW")}', f'Detect drives {std.color_string("(experts only)", "YELLOW")}', + f'Fresh start {std.color_string("(experts only)", "YELLOW")}', 'Quit') MENU_TOGGLES = { 'Auto continue (if recovery % over threshold)': True, @@ -254,6 +255,22 @@ class BlockPair(): # Done return complete + def reset_progress(self): + """Reset progress to start fresh recovery.""" + self.map_data = {} + self.status = OrderedDict({ + 'read': 'Pending', + 'trim': 'Pending', + 'scrape': 'Pending', + }) + if 'DISPLAY' in os.environ or 'WAYLAND_DISPLAY' in os.environ: + # Enable opening ddrescueview during recovery + self.view_proc = None + self.map_path.touch() + + # Set initial status + self.set_initial_status() + def safety_check(self): """Run safety check and abort if necessary.""" dest_size = -1 @@ -742,7 +759,7 @@ class State(): # Start fresh if requested if docopt_args['--start-fresh']: - clean_working_dir(self.working_dir) + clean_working_dir(self) # Add block pairs if self.mode == 'Clone': @@ -1527,23 +1544,46 @@ def check_destination_health(destination): return result -def clean_working_dir(working_dir): +def clean_working_dir(state, confirm=False): """Clean working directory to ensure a fresh recovery session. NOTE: Data from previous sessions will be preserved in a backup directory. """ - backup_dir = pathlib.Path(f'{working_dir}/prev') + backup_dir = pathlib.Path(f'{state.working_dir}/prev') backup_dir = io.non_clobber_path(backup_dir) - backup_dir.mkdir() + + # Confirm + if confirm: + std.print_error( + 'This will reset all progress and create new map file(s).', + ) + std.print_warning( + "Please only proceed if you understand what you're doing!", + ) + std.print_warning( + 'NOTE: This will keep the current partition selection(s).', + ) + if not std.ask('Continue?'): + return # Move settings, maps, etc to backup_dir - for entry in os.scandir(working_dir): + backup_dir.mkdir() + for entry in os.scandir(state.working_dir): if entry.name.endswith(('.dd', '.json', '.map')): + if confirm and entry.name.endswith('.json'): + # Keep JSON settings if using the menu option + continue new_path = f'{backup_dir}/{entry.name}' new_path = io.non_clobber_path(new_path) shutil.move(entry.path, new_path) + # Update block pairs (if using the menu option) + if confirm: + for block_pair in state.block_pairs: + block_pair.reset_progress() + state.update_progress_pane('Idle') + def detect_drives(state): """Detect connected drives and check source/dest selections.""" @@ -1932,6 +1972,10 @@ def main(): ) state.update_top_panes() + # Start over + if 'Fresh start' in selection[0]: + clean_working_dir(state, confirm=True) + # Start recovery if 'Start' in selection: std.clear_screen()