diff --git a/.bin/Scripts/functions/ddrescue.py b/.bin/Scripts/functions/ddrescue.py index 00514fe8..1acc5e96 100644 --- a/.bin/Scripts/functions/ddrescue.py +++ b/.bin/Scripts/functions/ddrescue.py @@ -1,6 +1,7 @@ # Wizard Kit: Functions - ddrescue import json +import pathlib import re from functions.common import * @@ -12,9 +13,99 @@ USAGE=""" {script_name} clone [source [destination]] """ # Functions +def show_device_details(dev_path): + """Display device details on screen.""" + cmd = ( + 'lsblk', '--nodeps', + '--output', 'NAME,TRAN,TYPE,SIZE,VENDOR,MODEL,SERIAL', + dev_path) + result = run_program(cmd) + output = result.stdout.decode().splitlines() + print_info(output.pop(0)) + for line in output: + print_standard(line) + + # Children info + cmd = ('lsblk', '--output', 'NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT', dev_path) + result = run_program(cmd) + output = result.stdout.decode().splitlines() + print_info(output.pop(0)) + for line in output: + print_standard(line) + +def get_device_details(dev_path): + """Get device details via lsblk, returns JSON dict.""" + try: + cmd = ( + 'lsblk', + '--json', + '--output-all', + '--paths', + dev_path) + result = run_program(cmd) + except CalledProcessError: + print_error('Failed to get device details for {}'.format(dev_path)) + abort() + + json_data = json.loads(result.stdout.decode()) + # Just return the first device (there should only be one) + return json_data['blockdevices'][0] + +def setup_loopback_device(source_path): + """Setup a loopback device for source_path, returns dev_path as str.""" + cmd = ( + 'losetup', + '--find', + '--partscan', + '--show', + source_path) + try: + out = run_program(cmd, check=True) + dev_path = out.stdout.decode().strip() + except CalledProcessError: + print_error('Failed to setup loopback device for source.') + abort() + else: + return dev_path + def menu_clone(source_path, dest_path): + """ddrescue cloning menu.""" + source_is_image = False + source_dev_path = None print_success('GNU ddrescue: Cloning Menu') - pass + + # Select source if not preselected + if not source_path: + #TODO menu_select drive + print_warning('Select drive not implemented yet.') + source_path = '' + + # Check source + source_path = os.path.realpath(source_path) + if pathlib.Path(source_path).is_block_device(): + source_dev_path = source_path + elif pathlib.Path(source_path).is_file(): + source_dev_path = setup_loopback_device(source_path) + source_is_image = True + else: + print_error('Invalid source "{}".'.format(source_path)) + abort() + source_details = get_device_details(source_dev_path) + + # Check source type + if source_details['pkname']: + print_warning('Source "{}" is a child device.'.format(source_dev_path)) + if ask('Use parent device "{}" instead?'.format( + source_details['pkname'])): + source_dev_path = source_details['pkname'] + source_details = get_device_details(source_dev_path) + + # Show selection details + if source_is_image: + print_success('Using image file: {}'.format(source_path)) + print_success(' (via loopback device: {})'.format( + source_dev_path)) + show_device_details(source_dev_path) def menu_ddrescue(*args): """Main ddrescue loop/menu.""" @@ -45,6 +136,7 @@ def menu_ddrescue(*args): exit_script() def menu_image(source_path, dest_path): + """ddrescue imaging menu.""" print_success('GNU ddrescue: Imaging Menu') pass