def mount_source_filesystem(source_media, source_fs_mountpoint): """ :param source_media: :param source_fs_mountpoint: :return: 1 - failure """ utils.check_kill_signal() utils.print_with_color("Mounting source filesystem...", "green") # os.makedirs(source_fs_mountpoint, exist_ok=True) if subprocess.run(["mkdir", "--parents", source_fs_mountpoint ]).returncode != 0: utils.print_with_color( "Error: Unable to create " + source_fs_mountpoint + " mountpoint directory", "red") return 1 if os.path.isfile(source_media): if subprocess.run([ "mount", "--options", "loop,ro", "--types", "udf,iso9660", source_media, source_fs_mountpoint ]).returncode != 0: utils.print_with_color("Error: Unable to mount source media", "red") return 1 else: if subprocess.run([ "mount", "--options", "ro", source_media, source_fs_mountpoint ]).returncode != 0: utils.print_with_color("Error: Unable to mount source media", "red") return 1
def install_legacy_pc_bootloader_grub_config(target_fs_mountpoint, target_device, command_grubinstall, name_grub_prefix): """ Install a GRUB config file to chainload Microsoft Windows's bootloader in Legacy PC bootmode :param target_fs_mountpoint: Target filesystem's mountpoint(where GRUB is installed) :param target_device: :param command_grubinstall: :param name_grub_prefix: May be different between distributions, so need to be specified (grub/grub2) :return: None """ utils.check_kill_signal() utils.print_with_color( "Installing custom GRUB config for legacy PC booting...", "green") grub_cfg = target_fs_mountpoint + "/" + name_grub_prefix + "/grub.cfg" os.makedirs(target_fs_mountpoint + "/" + name_grub_prefix, exist_ok=True) with open(grub_cfg, "w") as cfg: cfg.write("ntldr /bootmgr\n") cfg.write("boot")
def copy_large_file(source, target): """ Because python's copy is atomic it is not possible to do anything during process. It is not a big problem when using cli (user can just hit ctrl+c and throw exception), but when using gui this part of script needs to "ping" gui for progress reporting and check if user didn't click "cancel" (see utils.check_kill_signal()) :param source: :param target: :return: None """ source_file = open(source, "rb") # Open for reading in byte mode target_file = open(target, "wb") # Open for writing in byte mode while True: utils.check_kill_signal() data = source_file.read( 5 * 1024 * 1024 ) # Read 5 MiB, speeds of shitty pendrives can be as low as 2 MiB/s if data == b"": break target_file.write(data) source_file.close() target_file.close()
def mount_target_filesystem(target_partition, target_fs_mountpoint): """ Mount target filesystem to existing path as mountpoint :param target_partition: The partition device file target filesystem resides, for example /dev/sdX1 :param target_fs_mountpoint: The existing directory used as the target filesystem's mountpoint, for example /mnt/target_filesystem :return: 1 - failure """ utils.check_kill_signal() utils.print_with_color("Mounting target filesystem...", "green") # os.makedirs(target_fs_mountpoint, exist_ok=True) if subprocess.run(["mkdir", "--parents", target_fs_mountpoint ]).returncode != 0: utils.print_with_color( "Error: Unable to create " + target_fs_mountpoint + " mountpoint directory", "red") return 1 if subprocess.run(["mount", target_partition, target_fs_mountpoint ]).returncode != 0: utils.print_with_color("Error: Unable to mount target media", "red") return 1
def install_uefi_ntfs_support_partition(uefi_ntfs_partition, download_directory): """ Install UEFI:NTFS partition by writing the partition image into the created partition FIXME: Currently this requires internet access to download the image from GitHub directly, it should be replaced by including the image in our datadir :param uefi_ntfs_partition: The previously allocated partition for installing UEFI:NTFS, requires at least 512KiB :param download_directory: The temporary directory for downloading UEFI:NTFS image from GitHub :return: 1 - failure """ utils.check_kill_signal() try: file = urllib.request.urlretrieve( "https://github.com/pbatard/rufus/raw/master/res/uefi/", "uefi-ntfs.img")[0] except urllib.request.ContentTooShortError: utils.print_with_color( "Warning: Unable to download UEFI:NTFS partition image from GitHub, installation skipped. Target device might not be bootable if the UEFI firmware doesn't support NTFS filesystem." ) return 1 shutil.move(file, download_directory + "/" + file) # move file to download_directory shutil.copy2(download_directory + "/uefi-ntfs.img", uefi_ntfs_partition)
def create_target_partition_table(target_device, partition_table_type): """ :param target_device: :param partition_table_type: :return: 0 - success; 1 - failure """ utils.check_kill_signal() utils.print_with_color( "Creating new partition table on " + target_device + "...", "green") if partition_table_type in ["legacy", "msdos", "mbr", "pc"]: parted_partiton_table_argument = "msdos" elif partition_table_type in ["gpt", "guid"]: utils.print_with_color( "Error: Currently GUID partition table is not supported.", "red") return 1 else: utils.print_with_color("Error: Partition table not supported.", "red") return 1 # Create partition table(and overwrite the old one, whatever it was) subprocess.run([ "parted", "--script", target_device, "mklabel", parted_partiton_table_argument ]) return 0
def check_if_the_drive_is_really_wiped(target_device): """ Some broken locked-down flash drive will appears to be successfully wiped but actually nothing is written into it and will shown previous partition scheme afterwards. This is the detection of the case and will bail out if such things happened :param target_device: The target device file to be checked :return: 0 - success; 1 - failure """ utils.check_kill_signal() utils.print_with_color("Ensure that " + target_device + " is really wiped...") lsblk = subprocess.run( ["lsblk", "--pairs", "--output", "NAME,TYPE", target_device], stdout=subprocess.PIPE).stdout grep = subprocess.Popen(["grep", "--count", "TYPE=\"part\""], stdin=subprocess.PIPE, stdout=subprocess.PIPE) if grep.communicate(input=lsblk)[0].decode("utf-8").strip() != "0": utils.print_with_color( "Error: Partition is still detected after wiping all signatures, this indicates that the drive might be locked into readonly mode due to end of lifespan." ) return 1 return 0
def create_uefi_ntfs_support_partition(target_device): """ :param target_device: :return: None """ utils.check_kill_signal() # FIXME: The partition type should be `fat12` but `fat12` isn't recognized by Parted... # NOTE: The --align is set to none because this partition is indeed misaligned, but ignored due to it's small size subprocess.run([ "parted", "--align", "none", "--script", target_device, "mkpart", "primary", "fat16", "--", "-1024s", "-1s" ])
def install_legacy_pc_bootloader_grub(target_fs_mountpoint, target_device, command_grubinstall): """ :param target_fs_mountpoint: :param target_device: :param command_grubinstall: :return: None """ utils.check_kill_signal() utils.print_with_color( "Installing GRUB bootloader for legacy PC booting support...", "green") subprocess.run([ command_grubinstall, "--target=i386-pc", "--boot-directory=" + target_fs_mountpoint, "--force", target_device ])
def copy_filesystem_files(source_fs_mountpoint, target_fs_mountpoint): """ Copying all files from one filesystem to another, with progress reporting :param source_fs_mountpoint: :param target_fs_mountpoint: :return: None """ global CopyFiles_handle utils.check_kill_signal() total_size = 0 for dirpath, dirnames, filenames in os.walk(source_fs_mountpoint): for file in filenames: path = os.path.join(dirpath, file) total_size += os.path.getsize(path) utils.print_with_color("Copying files from source media...", "green") CopyFiles_handle = ReportCopyProgress(source_fs_mountpoint, target_fs_mountpoint) CopyFiles_handle.start() for dirpath, _, filenames in os.walk(source_fs_mountpoint): utils.check_kill_signal() if not os.path.isdir(target_fs_mountpoint + dirpath.replace(source_fs_mountpoint, "")): os.mkdir(target_fs_mountpoint + dirpath.replace(source_fs_mountpoint, "")) for file in filenames: path = os.path.join(dirpath, file) CopyFiles_handle.file = path if os.path.getsize( path) > 5 * 1024 * 1024: # Files bigger than 5 MiB copy_large_file( path, target_fs_mountpoint + path.replace(source_fs_mountpoint, "")) else: shutil.copy2( path, target_fs_mountpoint + path.replace(source_fs_mountpoint, "")) CopyFiles_handle.stop = True
def create_target_partition(target_device, target_partition, filesystem_type, filesystem_label, command_mkdosfs, command_mkntfs): """ :param target_device: :param target_partition: :param filesystem_type: :param filesystem_label: :param command_mkdosfs: :param command_mkntfs: :return: 1,2 - failure """ utils.check_kill_signal() if filesystem_type in ["FAT", "vfat"]: parted_mkpart_fs_type = "fat32" elif filesystem_type in ["NTFS", "ntfs"]: parted_mkpart_fs_type = "ntfs" else: utils.print_with_color("Error: Filesystem not supported", "red") return 2 utils.print_with_color("Creating target partition...", "green") # Create target partition # We start at 4MiB for grub (it needs a post-mbr gap for its code) and alignment of flash memery block erase segment in general, for details see http://www.gnu.org/software/grub/manual/grub.html#BIOS-installation and http://lwn.net/Articles/428584/ # If NTFS filesystem is used we leave a 512KiB partition at the end for installing UEFI:NTFS partition for NTFS support if parted_mkpart_fs_type == "fat32": subprocess.run([ "parted", "--script", target_device, "mkpart", "primary", parted_mkpart_fs_type, "4MiB", "100%" ]) # last sector of the disk elif parted_mkpart_fs_type == "ntfs": # Major partition for storing user files # NOTE: Microsoft Windows has a bug that only recognize the first partition for removable storage devices, that's why this partition should always be the first one subprocess.run( [ "parted", "--script", target_device, "mkpart", "primary", parted_mkpart_fs_type, "4MiB", "--", "-1025s" ] ) # Leave 512KiB==1024sector in traditional 512bytes/sector disk, disks with sector with more than 512bytes only result in partition size greater than 512KiB and is intentionally let-it-be. # FIXME: Leave exact 512KiB in all circumstances is better, but the algorithm to do so is quite brainkilling. else: utils.print_with_color( "FATAL: Illegal parted_mkpart_fs_type, please report bug.", "green") utils.check_kill_signal() workaround.make_system_realize_partition_table_changed(target_device) # Format target partition's filesystem if filesystem_type in ["FAT", "vfat"]: subprocess.run([command_mkdosfs, "-F", "32", target_partition]) elif filesystem_type in ["NTFS", "ntfs"]: subprocess.run([ command_mkntfs, "--quick", "--label", filesystem_label, target_partition ]) else: utils.print_with_color("FATAL: Shouldn't be here") return 1