def checksum_file(path: str) -> str: """ Gets the checksum of a file Parameters ---------- path : str The path to checksum Returns ------- string Checksum """ pprint_start("Getting MD5 hash...") result = run_piped_command([["md5sum", path], ["awk", "'{ print $1 }'"]]) if result["exit_code"]: pprint_complete( "Failed! Exit code: {0}".format(result["exit_code"]), False, Color.ERROR ) checksum = None else: pprint_complete("Complete", True) checksum = result["stdout"].strip() return checksum
def get_device_uuid(mount_point: str) -> str: """ Get the system UUID for a device Parameters ---------- mount_point : str The mount point for the drive """ message = "Checking device UUID..." pprint_start(message) uuid = None partition = __get_device_path(mount_point) if partition: commands = [["blkid", partition, "-o", "value"], ["head", "-1"]] output = run_piped_command(commands) if output["stdout"].strip(): uuid = output["stdout"].strip().decode("utf-8") if uuid: pprint_complete(message + "Found " + uuid, True, Color.GREEN) else: pprint_complete(message + "No UUID found!", False, Color.ERROR) return uuid
def get_device_serial(mount_point: str) -> str: """ Get the serial ID for a device Parameters ---------- mount_point : str The mount point for the drive """ message = "Checking device serial..." pprint_start(message) serial = None partition = __get_device_path(mount_point) if partition: commands = [ ["udevadm", "info", "--query=all", "--name=" + partition], ["grep", "ID_SERIAL_SHORT"], ["sed", "s/.*=//"], ] output = run_piped_command(commands) if output["stdout"].strip(): serial = output["stdout"].strip().decode("utf-8") if serial: pprint_complete(message + "Found " + serial, True, Color.GREEN) else: pprint_complete(message + "No serial found!", False, Color.ERROR) return serial
def get_file_security(path: str) -> dict: """ Get security details for a file Parameters ---------- path : str File path to get details about Returns ------- dict Containing owner, group, and permissions """ message = "Checking file permissions..." pprint_start(message) file_stats = os.stat(path) permission_mask = oct(file_stats.st_mode)[-3:] owner = pwd.getpwuid(file_stats.st_uid).pw_name group = grp.getgrgid(file_stats.st_gid).gr_name pprint_complete(message + "Done.", True) return {"permissions": permission_mask, "owner": owner, "group": group}
def add_file( file_path: str, mount_point: str = None, size_checked: bool = False ) -> bool: """ Will add a file to the backup archive Parameters ---------- file_path : str The file path to add mount_point : str Optionally, the mount point to prefer size_checked : bool Used for folder addition to specific device True if the size of the specified device has already been checked for required capacity of file/s Returns ------- bool True if added, False otherwise - due to database failure, hard drive failure, etc - or if it already exists """ if db.file_exists(file_path): pprint("File is already backed up!", Color.ERROR) return False if mount_point and not size_checked: message = "Checking drive space..." pprint_start(message) file_size = utility.get_file_size(file_path) drive_space = utility.get_device_space(mount_point) if file_size >= drive_space: pprint_complete(message + "Insufficient space!", False, Color.ERROR) confirm = input("Switch drive? (Y/n, n exits) ") if confirm == "n": return False else: mount_point = None else: pprint_complete(message + "Done.", True, Color.BLUE) message = "Getting file size..." pprint_start(message) file_size = utility.get_file_size(file_path) pprint_complete(message + "Read. File is " + readable_bytes(file_size), True) # This also needs to happen if we unset it due to space problems if not mount_point: message = "Auto-selecting device to use..." pprint_start(message) devices = db.get_devices() for device in devices: path = device.device_path space = utility.get_device_space(path) if space > file_size: pprint_complete("Selected " + device.device_name, True) mount_point = path break security_details = utility.get_file_security(file_path) checksum = utility.checksum_file(file_path) if not checksum: return False new_name = utility.create_backup_name(file_path) new_path = os_path.join(mount_point, new_name) shutil.copyfile(file_path, new_path) checksum2 = utility.checksum_file(new_path) if checksum != checksum2: pprint("Checksum mismatch after copy!", Color.ERROR) os.remove(new_path) return False file_obj = File() file_obj.set_properties(os_path.basename(file_path), file_path, checksum) file_obj.set_security(**security_details) message = "Saving file record to DB..." pprint_start(message) succeeded = db.add_file(file_obj) if succeeded: pprint_complete(message + "Done.", True, Color.GREEN) else: pprint_complete(message + "Failed!", False, Color.ERROR) os.remove(new_path) return succeeded
def add_device(mount_point: str) -> bool: """ Adds a device to the database Parameters ---------- mount_point : str The path to where the device is mounted Returns ------- bool True if added, False otherwise - due to database failure, or if it path does not exist """ device_name = input("Device name: ") identifier = utility.get_device_serial(mount_point) identifier_type = "Device Serial" if not identifier: identifier = utility.get_device_uuid(mount_point) identifier_type = "System UUID" if not identifier: identifier = input( "Unable to find systemic identifier. " "Please provide a unique identifier for the device: " ) identifier_type = "User Specified" message = "Saving device..." pprint_start(message) device = Device() device.set(device_name, mount_point, identifier_type, identifier) result = db.add_device(device) if result == DatabaseError.SUCCESS: pprint_complete(message + "Done", True, Color.GREEN) else: message += "Failed. " if result == DatabaseError.INVALID_IDENTIFIER_TYPE: pprint_complete( message + "Unrecognized device identifier!", False, Color.ERROR ) elif result == DatabaseError.DEVICE_NAME_EXISTS: pprint_complete(message + "Name already taken!", False, Color.ERROR) elif result == DatabaseError.DEVICE_PATH_EXISTS: pprint_complete( message + "Device already registered at mount point!", False, Color.ERROR, ) elif result == DatabaseError.DEVICE_IDENTIFIER_EXISTS: pprint_complete( message + "Serial already registered for another device!", False, Color.ERROR, ) elif result == DatabaseError.UNKNOWN_ERROR: pprint_complete(message + "Unknown error occurred!", False, Color.ERROR) else: pprint_complete( message + "Super-unknown error occurred!", False, Color.ERROR, formats=[Format.UNDERLINE], ) return False return True