BIOSUtilities v24.10.29

Added graceful exception hanlding during "main" flow
Improved and cleaned 7-Zip and EFI compression logic
Improved too aggressive extraction directory handling
Fixed input name detection at VAIO Package Extractor
Fixed Intel IBIOSI detection at Apple EFI Identifier
This commit is contained in:
Plato Mavropoulos 2024-10-30 00:47:41 +02:00
parent d8e23f9ef3
commit f895fc208c
24 changed files with 327 additions and 380 deletions

View file

@ -5,18 +5,18 @@
Copyright (C) 2022-2024 Plato Mavropoulos
"""
import os
import subprocess
from typing import Final
from biosutilities.common.externals import szip_path, tiano_path
from biosutilities.common.paths import is_dir, is_empty_dir
from biosutilities.common.paths import is_access, is_dir, is_file, is_empty_dir, path_size
from biosutilities.common.system import printer
from biosutilities.common.texts import file_to_bytes
# 7-Zip switches to auto rename, ignore passwords, ignore prompts, ignore wildcards,
# eliminate root duplication, set UTF-8 charset, suppress stdout, suppress stderr,
# suppress progress, disable headers, disable progress, disable output logs
# suppress progress, disable headers, disable progress, disable output logging
SZIP_COMMON: Final[list[str]] = ['-aou', '-p', '-y', '-spd', '-spe', '-sccUTF-8',
'-bso0', '-bse0', '-bsp0', '-ba', '-bd', '-bb0']
@ -24,13 +24,6 @@ SZIP_COMMON: Final[list[str]] = ['-aou', '-p', '-y', '-spd', '-spe', '-sccUTF-8'
SZIP_SUCCESS: Final[list[int]] = [0, 1]
def szip_code_assert(exit_code: int) -> None:
""" Check 7-Zip bad exit codes (0 OK, 1 Warning) """
if exit_code not in SZIP_SUCCESS:
raise ValueError(f'Bad exit code: {exit_code}')
def szip_switches(in_switches: list[str]) -> list[str]:
""" Generate 7-Zip command line switches """
@ -46,77 +39,73 @@ def szip_switches(in_switches: list[str]) -> list[str]:
return [*set(common_switches + in_switches), '--']
def is_szip_supported(in_path: str, padding: int = 0, args: list | None = None, silent: bool = True) -> bool:
def is_szip_successful(exit_code: int) -> bool:
""" Check 7-Zip success exit codes """
if exit_code in SZIP_SUCCESS:
return True
return False
def is_szip_supported(in_path: str, args: list | None = None) -> bool:
""" Check if file is 7-Zip supported """
try:
if args is None:
args = []
szip_a: list[str] = [] if args is None else args
szip_c: list[str] = [szip_path(), 't', *szip_switches(in_switches=[*args]), in_path]
szip_c: list[str] = [szip_path(), 't', *szip_switches(in_switches=[*szip_a]), in_path]
szip_t: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False)
szip_t: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False, stdout=subprocess.DEVNULL)
szip_code_assert(exit_code=szip_t.returncode)
except Exception as error: # pylint: disable=broad-except
if not silent:
printer(message=f'Error: 7-Zip could not check support for file {in_path}: {error}!', padding=padding)
return False
return True
return is_szip_successful(exit_code=szip_t.returncode)
def szip_decompress(in_path: str, out_path: str, in_name: str = 'archive', padding: int = 0, args: list | None = None,
check: bool = False, silent: bool = False) -> bool:
""" Archive decompression via 7-Zip """
try:
if args is None:
args = []
szip_a: list[str] = [] if args is None else args
szip_c: list[str] = [szip_path(), 'x', *szip_switches(in_switches=[*args, f'-o{out_path}']), in_path]
szip_c: list[str] = [szip_path(), 'x', *szip_switches(in_switches=[*szip_a, f'-o{out_path}']), in_path]
szip_x: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False)
szip_x: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False, stdout=subprocess.DEVNULL)
if check:
szip_code_assert(exit_code=szip_x.returncode)
szip_s: bool = is_szip_successful(exit_code=szip_x.returncode) if check else True
if not (is_dir(in_path=out_path) and not is_empty_dir(in_path=out_path)):
raise OSError(f'Extraction directory is empty or missing: {out_path}')
except Exception as error: # pylint: disable=broad-except
if szip_s and is_dir(in_path=out_path) and not is_empty_dir(in_path=out_path):
if not silent:
printer(message=f'Error: 7-Zip could not extract {in_name} file {in_path}: {error}!', padding=padding)
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
return False
return True
if not silent:
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
return True
return False
def efi_compress_sizes(data: bytes | bytearray) -> tuple[int, int]:
""" Get EFI compression sizes """
def efi_header_info(in_object: str | bytes | bytearray) -> dict[str, int]:
""" Get EFI compression sizes from header """
size_compress: int = int.from_bytes(data[0x0:0x4], byteorder='little')
efi_data: bytes = file_to_bytes(in_object=in_object)
size_original: int = int.from_bytes(data[0x4:0x8], byteorder='little')
size_compressed: int = int.from_bytes(efi_data[0x0:0x4], byteorder='little')
return size_compress, size_original
size_decompressed: int = int.from_bytes(efi_data[0x4:0x8], byteorder='little')
return {'size_compressed': size_compressed, 'size_decompressed': size_decompressed}
def is_efi_compressed(data: bytes | bytearray, strict: bool = True) -> bool:
def is_efi_compressed(in_object: str | bytes | bytearray, strict: bool = True) -> bool:
""" Check if data is EFI compressed, controlling EOF padding """
size_comp, size_orig = efi_compress_sizes(data=data)
efi_data: bytes = file_to_bytes(in_object=in_object)
check_diff: bool = size_comp < size_orig
efi_sizes: dict[str, int] = efi_header_info(in_object=efi_data)
check_diff: bool = efi_sizes['size_compressed'] < efi_sizes['size_decompressed']
if strict:
check_size: bool = size_comp + 0x8 == len(data)
check_size: bool = efi_sizes['size_compressed'] + 0x8 == len(efi_data)
else:
check_size = size_comp + 0x8 <= len(data)
check_size = efi_sizes['size_compressed'] + 0x8 <= len(efi_data)
return check_diff and check_size
@ -125,22 +114,15 @@ def efi_decompress(in_path: str, out_path: str, padding: int = 0, silent: bool =
comp_type: str = '--uefi') -> bool:
""" EFI/Tiano Decompression via TianoCompress """
try:
subprocess.run([tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type],
check=True, stdout=subprocess.DEVNULL)
tiano_c: list[str] = [tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type]
with open(in_path, 'rb') as file:
_, size_orig = efi_compress_sizes(data=file.read())
tiano_x: subprocess.CompletedProcess[bytes] = subprocess.run(tiano_c, check=False, stdout=subprocess.DEVNULL)
if os.path.getsize(out_path) != size_orig:
raise OSError('EFI decompressed file & header size mismatch!')
except Exception as error: # pylint: disable=broad-except
if not silent:
printer(message=f'Error: TianoCompress could not extract file {in_path}: {error}!', padding=padding)
if tiano_x.returncode == 0 and is_file(in_path=out_path) and is_access(in_path=out_path):
if efi_header_info(in_object=in_path)['size_decompressed'] == path_size(in_path=out_path):
if not silent:
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
return False
return True
if not silent:
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
return True
return False