From 1a26aead442f179b99c260d1e3e985aa0eb92f9f Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 18:38:33 -0700 Subject: [PATCH 01/18] Network test working under macOS --- scripts/wk/hw/diags.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index 03e9628a..3dd7a189 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -431,10 +431,10 @@ def build_menu(cli_mode=False, quick_mode=False): # Compatibility checks if PLATFORM != 'Linux': - for name in ('Audio Test', 'Keyboard Test', 'Network Test'): + for name in ('Audio Test', 'Keyboard Test'): menu.actions[name]['Disabled'] = True if PLATFORM not in ('Darwin', 'Linux'): - for name in ('Matrix', 'Tubes'): + for name in ('Matrix', 'Network Test', 'Tubes'): menu.actions[name]['Disabled'] = True # Done From 98032a0fed70b90d6a38b5d5ffb2d2ba17d3ee12 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Tue, 28 Jan 2020 19:00:47 -0700 Subject: [PATCH 02/18] Removed stale TODO statements --- scripts/wk/hw/diags.py | 1 - scripts/wk/hw/obj.py | 1 - scripts/wk/kit/ufd.py | 3 --- scripts/wk/net.py | 1 - scripts/wk/os/linux.py | 1 - scripts/wk/os/win.py | 2 +- 6 files changed, 1 insertion(+), 8 deletions(-) diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index 3dd7a189..9aab1887 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -383,7 +383,6 @@ def audio_test(): """Run an OS-specific audio test.""" if PLATFORM == 'Linux': audio_test_linux() - # TODO: Add tests for other OS def audio_test_linux(): diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index eca9a4db..1d9dbde6 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -385,7 +385,6 @@ class Disk(BaseObj): aligned = is_4k_aligned_macos(self.details) elif PLATFORM == 'Linux': aligned = is_4k_aligned_linux(self.path, self.details['phy-sec']) - #TODO: Add checks for other OS return aligned diff --git a/scripts/wk/kit/ufd.py b/scripts/wk/kit/ufd.py index 6df842de..9c792b3b 100644 --- a/scripts/wk/kit/ufd.py +++ b/scripts/wk/kit/ufd.py @@ -1,8 +1,5 @@ """WizardKit: UFD Functions""" # vim: sts=2 sw=2 ts=2 -# TODO: Replace some lsblk usage with hw_obj? -# TODO: Reduce imports if possible -# TODO: Needs testing import logging import os diff --git a/scripts/wk/net.py b/scripts/wk/net.py index 8b930c3b..e8e763b5 100644 --- a/scripts/wk/net.py +++ b/scripts/wk/net.py @@ -181,7 +181,6 @@ def share_is_mounted(details): if row['source'] == f'//{details["Address"]}/{details["Share"]}': mounted = True break - #TODO: Check mount status under Windows #elif PLATFORM == 'Windows': # Done diff --git a/scripts/wk/os/linux.py b/scripts/wk/os/linux.py index 7c38a03f..e4aefab8 100644 --- a/scripts/wk/os/linux.py +++ b/scripts/wk/os/linux.py @@ -179,7 +179,6 @@ def running_as_root(): def scan_corestorage_container(container, timeout=300): """Scan CoreStorage container for inner volumes, returns list.""" - # TODO: Test Scanning CoreStorage containers detected_volumes = {} inner_volumes = [] log_path = format_log_path(log_name=f'{container.path.name}_testdisk') diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py index f367e40f..7f17af37 100644 --- a/scripts/wk/os/win.py +++ b/scripts/wk/os/win.py @@ -15,7 +15,7 @@ from wk.std import GenericError, GenericWarning, sleep # STATIC VARIABLES LOG = logging.getLogger(__name__) -OS_VERSION = float(platform.win32_ver()[0]) # TODO: Check if Win8.1 returns '8' +OS_VERSION = float(platform.win32_ver()[0]) REG_MSISERVER = r'HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Network\MSIServer' SLMGR = pathlib.Path(f'{os.environ.get("SYSTEMROOT")}/System32/slmgr.vbs') From 41130a38edf14b66cc7c1fa447d45d2699de540a Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:02:41 -0700 Subject: [PATCH 03/18] Fix running tmux in live macOS env --- scripts/launch-in-tmux | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/launch-in-tmux b/scripts/launch-in-tmux index 2be76959..f0940b00 100755 --- a/scripts/launch-in-tmux +++ b/scripts/launch-in-tmux @@ -2,6 +2,12 @@ # ## Wizard Kit: TMUX Launcher +# Live macOS env workaround +tmux_args=() +if [[ -e "/.wk-live-macos" ]]; then + tmux_args=(-f "/etc/tmux.conf" -S "$(mktemp).socket") +fi + function ask() { while :; do read -p "$1 [Y/N] " -r answer @@ -25,24 +31,24 @@ function launch_in_tmux() { [[ -n "${TMUX_CMD:-}" ]] || return $(err "Required variable missing (TMUX_CMD)") # Check for running session - if tmux list-session | grep -q "$SESSION_NAME"; then + if tmux "${tmux_args[@]}" list-session | grep -q "$SESSION_NAME"; then echo "WARNING: tmux session $SESSION_NAME already exists." echo "" if ask "Connect to current session?"; then if [[ -n "${TMUX:-}" ]]; then # Running inside TMUX, switch to session - tmux switch-client -t "$SESSION_NAME" + tmux "${tmux_args[@]}" switch-client -t "$SESSION_NAME" if ! jobs %% >/dev/null 2>&1; then # No running jobs, try exiting abandoned tmux session exit 0 fi else # Running outside TMUX, attach to session - tmux attach-session -t "$SESSION_NAME" + tmux "${tmux_args[@]}" attach-session -t "$SESSION_NAME" fi return 0 elif ask "Kill current session and start new session?"; then - tmux kill-session -t "$SESSION_NAME" || \ + tmux "${tmux_args[@]}" kill-session -t "$SESSION_NAME" || \ die "Failed to kill session: $SESSION_NAME" else echo "Aborted." @@ -53,16 +59,16 @@ function launch_in_tmux() { # Start session if [[ -n "${TMUX:-}" ]]; then # Running inside TMUX, save current session/window names - ORIGINAL_SESSION_NAME="$(tmux display-message -p '#S')" - ORIGINAL_WINDOW_NAME="$(tmux display-message -p '#W')" - tmux rename-session "$SESSION_NAME" - tmux rename-window "$WINDOW_NAME" + ORIGINAL_SESSION_NAME="$(tmux "${tmux_args[@]}" display-message -p '#S')" + ORIGINAL_WINDOW_NAME="$(tmux "${tmux_args[@]}" display-message -p '#W')" + tmux "${tmux_args[@]}" rename-session "$SESSION_NAME" + tmux "${tmux_args[@]}" rename-window "$WINDOW_NAME" "$TMUX_CMD" "$@" # Restore previous session/window names - tmux rename-session "${ORIGINAL_SESSION_NAME}" - tmux rename-window "${ORIGINAL_WINDOW_NAME}" + tmux "${tmux_args[@]}" rename-session "${ORIGINAL_SESSION_NAME}" + tmux "${tmux_args[@]}" rename-window "${ORIGINAL_WINDOW_NAME}" else # Running outside TMUX, start/attach to session - tmux new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$TMUX_CMD" "$@" + tmux "${tmux_args[@]}" new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$TMUX_CMD" "$@" fi } From e1943b9fb48354fca955eb0bbc24a27c4f7ce537 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:03:21 -0700 Subject: [PATCH 04/18] Strip sudo use if running as root on Linux/macOS * Allows running under live macOS --- scripts/wk/exe.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/wk/exe.py b/scripts/wk/exe.py index 17f49249..e49b51d6 100644 --- a/scripts/wk/exe.py +++ b/scripts/wk/exe.py @@ -3,6 +3,7 @@ import json import logging +import os import re import subprocess @@ -77,6 +78,10 @@ def build_cmd_kwargs(cmd, minimized=False, pipe=True, shell=False, **kwargs): 'shell': shell, } + # Strip sudo if appropriate + if cmd[0] == 'sudo' and os.name == 'posix' and os.geteuid() == 0: + cmd.pop(0) + # Add additional kwargs if applicable for key in 'check cwd encoding errors stderr stdin stdout'.split(): if key in kwargs: From c7d57ff2667a2de170510255cdcc2bb0e7fbd6f7 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:07:04 -0700 Subject: [PATCH 05/18] Fixed logging under live macOS --- scripts/wk/log.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/wk/log.py b/scripts/wk/log.py index ee512c95..2cb39764 100644 --- a/scripts/wk/log.py +++ b/scripts/wk/log.py @@ -13,7 +13,10 @@ from wk.io import non_clobber_path # STATIC VARIABLES -if os.name == 'nt': +if os.path.exists('/.wk-live-macos'): + # Workaround for live macOS env + DEFAULT_LOG_DIR = '/var/log/WizardKit' +elif os.name == 'nt': # Example: "C:\WK\1955-11-05\WizardKit" DEFAULT_LOG_DIR = ( f'{os.environ.get("SYSTEMDRIVE", "C:")}/' From 4a3981e10c123ab038975dcbb239a18d66ae6201 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:08:13 -0700 Subject: [PATCH 06/18] Adjusted menus under live macOS --- scripts/wk/std.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/wk/std.py b/scripts/wk/std.py index 4e84a25e..60c110f9 100644 --- a/scripts/wk/std.py +++ b/scripts/wk/std.py @@ -134,6 +134,8 @@ class Menu(): checkmark = '*' if 'DISPLAY' in os.environ or PLATFORM == 'Darwin': checkmark = '✓' + if os.path.exists('/.wk-live-macos'): + checkmark = '*' display_name = f'{index if index else name[:1].upper()}: ' if not (index and index >= 10): display_name = f' {display_name}' From 417241acb51428c7ac8d5f7c0f57263a6a063973 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:09:57 -0700 Subject: [PATCH 07/18] Fixed clearing the screen under live macOS --- scripts/wk/std.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/wk/std.py b/scripts/wk/std.py index 60c110f9..b25b7b9f 100644 --- a/scripts/wk/std.py +++ b/scripts/wk/std.py @@ -728,7 +728,11 @@ def choice(choices, prompt='答えろ!'): def clear_screen(): """Simple wrapper for clear/cls.""" cmd = 'cls' if os.name == 'nt' else 'clear' - subprocess.run(cmd, check=False, shell=True, stderr=subprocess.PIPE) + proc = subprocess.run(cmd, check=False, shell=True, stderr=subprocess.PIPE) + + # Workaround for live macOS env + if proc.returncode != 0: + print('\033c') def color_string(strings, colors, sep=' '): From 86f0f1e5fdc611ff54d79f590db37e221754fa33 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:16:48 -0700 Subject: [PATCH 08/18] Avoid rare crash concerning disk attributes * Crash would occur under these circumstances: * Disk Attributes test was not selected * One or more other disk tests were selected * A non-blocking attribute error was detected --- scripts/wk/hw/diags.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index 9aab1887..87b4ec27 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -198,7 +198,11 @@ class State(): ) else: # No blocking errors encountered, check for minor attribute failures - if not disk.check_attributes(only_blocking=False): + if ('Disk Attributes' in disk.tests: + and not disk.tests['Disk Attributes'].failed + and not disk.check_attributes(only_blocking=False)): + # Mid-diag failure detected + LOG.warning('Disk attributes failure detected during diagnostics') disk.tests['Disk Attributes'].failed = True disk.tests['Disk Attributes'].set_status('Failed') From c8f95d866a572ead485e11380aa32dfc15d48fcc Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:24:49 -0700 Subject: [PATCH 09/18] Typo fix --- scripts/wk/hw/diags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index 87b4ec27..f36b5471 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -198,7 +198,7 @@ class State(): ) else: # No blocking errors encountered, check for minor attribute failures - if ('Disk Attributes' in disk.tests: + if ('Disk Attributes' in disk.tests and not disk.tests['Disk Attributes'].failed and not disk.check_attributes(only_blocking=False)): # Mid-diag failure detected From dc6dcfb84528f5f28f683c11603a3dbac7278298 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:25:46 -0700 Subject: [PATCH 10/18] Revert "Avoid crash during SMART self-test" This reverts commit cc8c0992f659c89127c24baf420313d0a9f3decc. --- scripts/wk/hw/obj.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index 1d9dbde6..7087fda9 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -368,13 +368,9 @@ class Disk(BaseObj): try: details = self.smartctl['ata_smart_data']['self_test'] except (KeyError, TypeError): - # Assuming disk lacks SMART support, ignore and return nearly empty dict. + # Assuming disk lacks SMART support, ignore and return empty dict. pass - # Ensure status is present even if empty - if 'status' not in details: - details['status'] = {} - # Done return details From 385b2158fd11e150362b6f5aa3064cf75c17b798 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:28:52 -0700 Subject: [PATCH 11/18] Improved method to avoid crash during self-tests --- scripts/wk/hw/obj.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index 7087fda9..a8719358 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -497,11 +497,11 @@ class Disk(BaseObj): _f.write(f'{header_str}\nSMART self-test status:\n {status_str}') # Check if finished - if 'remaining_percent' not in test_details['status']: + if 'remaining_percent' not in test_details.get('status', {}): finished = True break - elif 'remaining_percent' in test_details['status']: + elif 'remaining_percent' in test_details.get('status', {}): started = True # Check result From 06d1f0551b0960fdfbf304b21f7473cd968d9e92 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:31:34 -0700 Subject: [PATCH 12/18] Dropped oblogout usage in opembox menu --- .../linux/include_x/airootfs/etc/skel/.config/openbox/menu.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/linux/include_x/airootfs/etc/skel/.config/openbox/menu.xml b/setup/linux/include_x/airootfs/etc/skel/.config/openbox/menu.xml index 2d560235..2e8fd4a2 100644 --- a/setup/linux/include_x/airootfs/etc/skel/.config/openbox/menu.xml +++ b/setup/linux/include_x/airootfs/etc/skel/.config/openbox/menu.xml @@ -234,7 +234,7 @@ - oblogout + wk-exit From 154acc52802bb293f74ef70bb7a1e47cde5936e7 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:36:43 -0700 Subject: [PATCH 13/18] Run build-ufd as current user * sudo is used for elevated commands instead * Avoids splitting logs between root and current user * Addresses issue #150 --- scripts/wk/kit/ufd.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/wk/kit/ufd.py b/scripts/wk/kit/ufd.py index 9c792b3b..b703fd43 100644 --- a/scripts/wk/kit/ufd.py +++ b/scripts/wk/kit/ufd.py @@ -53,11 +53,6 @@ def build_ufd(): try_print.catch_all = False try_print.indent = 2 - # Check if running with root permissions - if not linux.running_as_root(): - std.print_error('This script is meant to be run as root') - std.abort() - # Show header std.print_success(KIT_NAME_FULL) std.print_warning('UFD Build Tool') @@ -218,6 +213,7 @@ def copy_source(source, items, overwrite=False): def create_table(dev_path, use_mbr=False): """Create GPT or DOS partition table.""" cmd = [ + 'sudo', 'parted', dev_path, '--script', '--', @@ -254,6 +250,7 @@ def find_first_partition(dev_path): def format_partition(dev_path, label): """Format first partition on device FAT32.""" cmd = [ + 'sudo', 'mkfs.vfat', '-F', '32', '-n', label, @@ -287,13 +284,14 @@ def hide_items(ufd_dev, items): # Hide items for item in items: - cmd = [f'yes | mattrib +h "U:/{item}"'] - run_program(cmd, check=False, shell=True) + cmd = [f'yes | sudo mattrib +h "U:/{item}"'] + run_program(cmd, shell=True) def install_syslinux_to_dev(ufd_dev, use_mbr): """Install Syslinux to UFD (dev).""" cmd = [ + 'sudo', 'dd', 'bs=440', 'count=1', @@ -306,6 +304,7 @@ def install_syslinux_to_dev(ufd_dev, use_mbr): def install_syslinux_to_partition(partition): """Install Syslinux to UFD (partition).""" cmd = [ + 'sudo', 'syslinux', '--install', '--directory', @@ -335,6 +334,7 @@ def is_valid_path(path_obj, path_type): def set_boot_flag(dev_path, use_mbr=False): """Set modern or legacy boot flag.""" cmd = [ + 'sudo', 'parted', dev_path, 'set', '1', 'boot' if use_mbr else 'legacy_boot', @@ -410,6 +410,7 @@ def update_boot_entries(): # Use UUID instead of label cmd = [ + 'sudo', 'sed', '--in-place', '--regexp-extended', @@ -428,6 +429,7 @@ def update_boot_entries(): # Entry found, update config files cmd = [ + 'sudo', 'sed', '--in-place', f's/#{b_comment}#//', @@ -476,6 +478,7 @@ def verify_ufd(dev_path): def zero_device(dev_path): """Zero-out first 64MB of device.""" cmd = [ + 'sudo', 'dd', 'bs=4M', 'count=16', From 64db679a4dc5119e3da077eb8c07afb6f1faad34 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:47:59 -0700 Subject: [PATCH 14/18] Allow setting verbose value for TryAndPrint obj * Instead of strictly per-call --- scripts/wk/std.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/wk/std.py b/scripts/wk/std.py index b25b7b9f..c715706a 100644 --- a/scripts/wk/std.py +++ b/scripts/wk/std.py @@ -388,6 +388,7 @@ class Menu(): class TryAndPrint(): + # pylint: disable=too-many-instance-attributes """Object used to standardize running functions and returning the result. The errors and warning attributes are used to allow fine-tuned results @@ -396,11 +397,12 @@ class TryAndPrint(): def __init__(self, msg_bad='FAILED', msg_good='SUCCESS'): self.catch_all = True self.indent = INDENT - self.msg_bad = msg_bad - self.msg_good = msg_good - self.width = WIDTH self.list_errors = ['GenericError'] self.list_warnings = ['GenericWarning'] + self.msg_bad = msg_bad + self.msg_good = msg_good + self.verbose = False + self.width = WIDTH def _format_exception_message(self, _exception): """Format using the exception's args or name, returns str.""" @@ -525,13 +527,13 @@ class TryAndPrint(): def run( self, message, function, *args, - catch_all=None, msg_good=None, verbose=False, **kwargs): + catch_all=None, msg_good=None, verbose=None, **kwargs): # pylint: disable=catching-non-exception """Run a function and print the results, returns results as dict. If catch_all is True then (nearly) all exceptions will be caught. Otherwise if an exception occurs that wasn't specified it will be - re-raised. If passed it will override self.catch_all for this call. + re-raised. If the function returns data it will be used instead of msg_good, msg_bad, or exception text. @@ -542,6 +544,9 @@ class TryAndPrint(): If verbose is True then exception names or messages will be used for the result message. Otherwise it will simply be set to result_bad. + If catch_all and/or verbose are passed it will override + self.catch_all and/or self.verbose for this call. + args and kwargs are passed to the function. """ LOG.debug('function: %s.%s', function.__module__, function.__name__) @@ -558,6 +563,8 @@ class TryAndPrint(): result_msg = 'UNKNOWN' if catch_all is None: catch_all = self.catch_all + if verbose is None: + verbose = self.verbose # Build exception tuples e_exceptions = tuple(self._get_exception(e) for e in self.list_errors) From 6eb75c38a3d33a93c0bc19aff078ec60b6de08eb Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:48:51 -0700 Subject: [PATCH 15/18] Report if item(s) are missing during build-ufd --- scripts/wk/kit/ufd.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/wk/kit/ufd.py b/scripts/wk/kit/ufd.py index b703fd43..483e2fbb 100644 --- a/scripts/wk/kit/ufd.py +++ b/scripts/wk/kit/ufd.py @@ -50,7 +50,9 @@ def build_ufd(): args = docopt(DOCSTRING) log.update_log_path(dest_name='build-ufd', timestamp=True) try_print = std.TryAndPrint() + try_print.add_error(FileNotFoundError) try_print.catch_all = False + try_print.verbose = True try_print.indent = 2 # Show header @@ -190,6 +192,7 @@ def confirm_selections(update=False): def copy_source(source, items, overwrite=False): """Copy source items to /mnt/UFD.""" is_image = source.is_file() + items_not_found = False # Mount source if necessary if is_image: @@ -202,13 +205,15 @@ def copy_source(source, items, overwrite=False): try: io.recursive_copy(i_source, i_dest, overwrite=overwrite) except FileNotFoundError: - # Going to assume (hope) that this is fine - pass + items_not_found = True # Unmount source if necessary if is_image: linux.unmount('/mnt/Source') + # Raise exception if item(s) were not found + raise FileNotFoundError('One or more items not found') + def create_table(dev_path, use_mbr=False): """Create GPT or DOS partition table.""" From ad1adba837bf432c15888d73afe8fcc9f1b9dc0b Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:51:23 -0700 Subject: [PATCH 16/18] Add APFS and HFS/HFS+ to RECOMMENDED_MAP_FSTYPES --- scripts/wk/hw/ddrescue.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/wk/hw/ddrescue.py b/scripts/wk/hw/ddrescue.py index f9b0d023..fe47461e 100644 --- a/scripts/wk/hw/ddrescue.py +++ b/scripts/wk/hw/ddrescue.py @@ -82,7 +82,9 @@ PANE_RATIOS = ( ) PLATFORM = std.PLATFORM RECOMMENDED_FSTYPES = re.compile(r'^(ext[234]|ntfs|xfs)$') -RECOMMENDED_MAP_FSTYPES = re.compile(r'^(cifs|ext[234]|ntfs|vfat|xfs)$') +RECOMMENDED_MAP_FSTYPES = re.compile( + r'^(apfs|cifs|ext[234]|hfs.?|ntfs|vfat|xfs)$' + ) SETTING_PRESETS = ( 'Default', 'Fast', From ebbdedef6cfa0ac5528fc513c7eee909ce7b739a Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 13:59:12 -0700 Subject: [PATCH 17/18] Added get_fstype_macos() --- scripts/wk/hw/ddrescue.py | 28 +++++++++++++++++++++++++--- scripts/wk/hw/obj.py | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/scripts/wk/hw/ddrescue.py b/scripts/wk/hw/ddrescue.py index fe47461e..e3433598 100644 --- a/scripts/wk/hw/ddrescue.py +++ b/scripts/wk/hw/ddrescue.py @@ -727,6 +727,8 @@ class State(): self.update_progress_pane('Idle') self.confirm_selections('Start recovery?') + # TODO: Unmount source and/or destination under macOS + # Prep destination if self.mode == 'Clone': self.prep_destination(source_parts, dry_run=docopt_args['--dry-run']) @@ -1196,7 +1198,6 @@ def build_directory_report(path): line = f'{path:<{width}}{line}' report.append(line) else: - # TODO Get dir details under macOS report.append(std.color_string('PATH', 'BLUE')) report.append(str(path)) @@ -1449,8 +1450,11 @@ def fstype_is_ok(path, map_dir=False): # Get fstype if PLATFORM == 'Darwin': - # TODO: Determine fstype under macOS - pass + try: + fstype = get_fstype_macos(path) + except (IndexError, TypeError, ValueError): + # Ignore for now + pass elif PLATFORM == 'Linux': cmd = [ 'findmnt', @@ -1517,6 +1521,24 @@ def get_etoc(): return etoc +def get_fstype_macos(path): + """Get fstype for path under macOS, returns str. + + NOTE: This method is not very effecient. + """ + cmd = ['df', path] + + # Get device based on the path + proc = exe.run_program(cmd, check=False) + dev = proc.stdout.splitlines()[1].split()[0] + + # Get device details + dev = hw_obj.Disk(dev) + + # Done + return dev.details['fstype'] + + def get_object(path): """Get object based on path, returns obj.""" obj = None diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index a8719358..dc6175ea 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -321,6 +321,7 @@ class Disk(BaseObj): self.details['bus'] = str(self.details.get('bus', '???')).upper() self.details['bus'] = self.details['bus'].replace('IMAGE', 'Image') self.details['bus'] = self.details['bus'].replace('NVME', 'NVMe') + self.details['fstype'] = self.details.get('fstype', 'Unknown') self.details['log-sec'] = self.details.get('log-sec', 512) self.details['model'] = self.details.get('model', 'Unknown Model') self.details['name'] = self.details.get('name', self.path) From a66c27be84a002f44fe91ebebb9a9cdc6966741e Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 30 Jan 2020 14:01:30 -0700 Subject: [PATCH 18/18] Updated get_disk_details_macos() --- scripts/wk/hw/obj.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index dc6175ea..0c33296e 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -656,12 +656,15 @@ def get_disk_details_macos(path): dev['label'] = dev.pop('VolumeName', '') dev['model'] = dev.pop('MediaName', 'Unknown') dev['mountpoint'] = dev.pop('MountPoint', '') + dev['name'] = dev.get('name', str(dev['path'])) dev['phy-sec'] = dev.pop('DeviceBlockSize', 512) dev['serial'] = get_disk_serial_macos(dev['path']) dev['size'] = dev.pop('Size', -1) dev['ssd'] = dev.pop('SolidState', False) dev['vendor'] = '' - if not dev.get('WholeDisk', True): + if dev.get('WholeDisk', True): + dev['parent'] = None + else: dev['parent'] = dev.pop('ParentWholeDisk', None) # Done