diff --git a/config/ddusb-exclude.conf b/config/ddusb-exclude.conf new file mode 100644 index 0000000..f24edfe --- /dev/null +++ b/config/ddusb-exclude.conf @@ -0,0 +1,3 @@ +## File for excluding particular files from being written to +## Write one device per line, shell-style globs are accepted i.e. /dev/sda? +## Lines beginning with "#" and ";" are ignored diff --git a/ddusb.py b/ddusb.py index b97e870..a3b3f30 100644 --- a/ddusb.py +++ b/ddusb.py @@ -2,9 +2,54 @@ """Write an ISO image to a usb drive using dd.""" import argparse +import glob import pathlib +import re import subprocess +# ========== Constants ========== +COMMENT_PATTERN = "[#;]" +EXCLUDE_FILE = "/etc/helper-scripts/ddusb-exclude.conf" + + +# ========== Functions ========== +def read_exclude_file(exclude_file): + """Read and return a list of exclusion paths/globs, + removing any lines that begin with COMMENT_PATTERN. + + :param exclude_file: path to the exclusion file + :type exclude_file: str, bytes, or path-like object + :returns: a list containing non-commented lines from the + exclusion file + :rtype: list + """ + with open(exclude_file, "r") as excludes: + lines = [ + l.strip("\n") + for l in excludes.readlines() + if not re.match(COMMENT_PATTERN, l) + ] + + return lines + + +def expand_globs(*globs): + """For each glob given, expand these globs and return a list + containing each glob expanded. + + :param globs: patterns to expand + :type globs: str + :returns: all expanded globs aggregated together + :rtype: list + """ + expanded_globs = [] + + for line in globs: + expanded_globs.extend(glob.glob(line, recursive=True)) + + return expanded_globs + + # ========== Main Script ========== parser = argparse.ArgumentParser() parser.add_argument("-b", "--bs", default=512, help="block size", metavar="bs") @@ -17,13 +62,21 @@ input_file = args.input_file block_device = args.output_file if not pathlib.Path(block_device).is_block_device(): - print(f"Error: {block_device} is not a block device") + print(f'Error: "{block_device}" is not a block device') exit(1) print(f"Input file: {input_file}") print(f"Block device: {block_device}") print(f"Block size: {block_size}") +# Check if block_device is excluded +exclude_patterns = read_exclude_file(EXCLUDE_FILE) +device_blacklist = expand_globs(*exclude_patterns) + +if block_device in device_blacklist: + print(f'Error: "{block_device}" is blacklisted from running dd') + exit(2) + try: subprocess.run( [ @@ -36,6 +89,6 @@ try: check=True, ) except subprocess.CalledProcessError: - exit(1) + exit(3) else: - subprocess.run(["sync"]) + subprocess.run("sync")