diff --git a/CHANGELOG b/CHANGELOG index c7a4bef3..ef4b1ba1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,17 @@ +Version - 8.9.0 +--------------- +* Support for GPT based USB disks. BIOS mode works only under USB created under Linux. UEFI for on Windows and Linux +* Added command line option to install sysinux on multibootusb director (use -s or --syslinux) +* Added command line option to direct ISO writing to USB disk (use -r or --raw) +* Boot ISO and IMG directly using memdisk +* Added feature for selecting ISO, IMG, Zip and all files options in file chooser dialog +* Corrected path to menu.lst file for distrs based on grub4dos +* Fix for crash when multicard reader is inserted on the system without a SD card +* Correctly detect USB disk information using udisk2-dbus without crash under Linux +* Fixed an issue where using a path with spaces would cause a qemu boot error +* If distro is not supported, ISO is automatically added using memdisk. You can uninstall later if it does not work +* Added Nano Linux + Version - 8.8.0 --------------- * Fix for crash when listing fixed partition diff --git a/data/EFI/BOOT/bootx64-gpt.efi b/data/EFI/BOOT/bootx64-gpt.efi new file mode 100644 index 00000000..6751971f Binary files /dev/null and b/data/EFI/BOOT/bootx64-gpt.efi differ diff --git a/data/EFI/BOOT/bootx64-msdos.efi b/data/EFI/BOOT/bootx64-msdos.efi new file mode 100644 index 00000000..84c47456 Binary files /dev/null and b/data/EFI/BOOT/bootx64-msdos.efi differ diff --git a/data/multibootusb/grub/core-gpt.img b/data/multibootusb/grub/core-gpt.img new file mode 100644 index 00000000..385e0ca8 Binary files /dev/null and b/data/multibootusb/grub/core-gpt.img differ diff --git a/data/multibootusb/grub/core-msdos.img b/data/multibootusb/grub/core-msdos.img new file mode 100644 index 00000000..d81bc24a Binary files /dev/null and b/data/multibootusb/grub/core-msdos.img differ diff --git a/data/tools/EFI/BOOT/bootx64-gpt.efi b/data/tools/EFI/BOOT/bootx64-gpt.efi new file mode 100644 index 00000000..6751971f Binary files /dev/null and b/data/tools/EFI/BOOT/bootx64-gpt.efi differ diff --git a/data/tools/EFI/BOOT/bootx64-msdos.efi b/data/tools/EFI/BOOT/bootx64-msdos.efi new file mode 100644 index 00000000..84c47456 Binary files /dev/null and b/data/tools/EFI/BOOT/bootx64-msdos.efi differ diff --git a/data/tools/gdisk/gdisk.exe b/data/tools/gdisk/gdisk.exe new file mode 100644 index 00000000..237f004e Binary files /dev/null and b/data/tools/gdisk/gdisk.exe differ diff --git a/data/tools/gdisk/list-disk.txt b/data/tools/gdisk/list-disk.txt new file mode 100644 index 00000000..61f1273f --- /dev/null +++ b/data/tools/gdisk/list-disk.txt @@ -0,0 +1 @@ +list disk \ No newline at end of file diff --git a/data/tools/gptmbr.bin b/data/tools/gptmbr.bin new file mode 100644 index 00000000..c9781b0f Binary files /dev/null and b/data/tools/gptmbr.bin differ diff --git a/data/version.txt b/data/version.txt index 3b682537..e5c15102 100644 --- a/data/version.txt +++ b/data/version.txt @@ -1 +1 @@ -8.8.0 +8.9.0 diff --git a/multibootusb b/multibootusb index 3340d351..05a6f551 100644 --- a/multibootusb +++ b/multibootusb @@ -101,7 +101,7 @@ Example for writing ISO image to target USB disk (will destroy data on USB disk) Windows: python3 multibootusb -c -i -r ../../favourite.iso -t G: ''') - exit(2) + sys.exit(2) def start_gui(): @@ -121,9 +121,9 @@ if __name__ == '__main__': admin.runAsAdmin() sys.exit(0) try: - opts, args = getopt.getopt(sys.argv[1:], 'i:t:yvhcudr', + opts, args = getopt.getopt(sys.argv[1:], 'i:t:yvhcudrs', ['iso=', 'target=', 'yes', 'version', 'help', 'command', 'uninstall', 'debug', - 'raw']) + 'raw', 'syslinux']) except getopt.GetoptError: usage() sys.exit(2) @@ -151,6 +151,8 @@ if __name__ == '__main__': config.yes = True elif opt in ('-r', '--raw'): config.cli_dd = True + elif opt in ('-s', '--syslinux'): + config.cli_syslinux = True else: gui = True #start_gui() @@ -174,6 +176,8 @@ if gui is False: elif config.image_path is '' and config.usb_disk is '': log('\nNo option provided. See the usage below.') usage() + elif config.cli_syslinux is True and config.usb_disk is not '': + cli_install_syslinux() elif config.image_path is '' or config.usb_disk is '': log('\nOptions \'-i\' and \'-t\' must be supplied together. See the usage below.') usage() diff --git a/scripts/_7zip.py b/scripts/_7zip.py index 668ab4e8..d5b16bb5 100644 --- a/scripts/_7zip.py +++ b/scripts/_7zip.py @@ -75,7 +75,8 @@ def list_iso(iso_link, suppress_out=True): file_list = [] _cmd = _7zip + ' l ' + gen.quote(iso_link) + suppress_out try: - _cmd_out = subprocess.check_output(_cmd, stderr=subprocess.PIPE, shell=True).decode('utf-8', 'ignore').splitlines() + _cmd_out = subprocess.check_output(_cmd, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, + shell=True).decode('utf-8', 'ignore').splitlines() except Exception as e: gen.log(e) _cmd_out = '' @@ -84,18 +85,6 @@ def list_iso(iso_link, suppress_out=True): line = line.split() _path = line[-1] file_list.append(_path) - ''' - for line in _cmd_out: - line = line.split() - if '.....' in line: - if gen.has_digit(line[2]) or gen.has_digit(line[4]): - if len(line) > 6: - f_path = " ".join(line[5:]) - file_list.append(f_path) - else: - f_path = line[-1] - file_list.append(f_path) - ''' return file_list diff --git a/scripts/config.py b/scripts/config.py index ed997560..3a9c5083 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -27,6 +27,8 @@ process_exist = None yes = False cli_dd = False +cli_syslinux = False +usb_gpt = '' imager_iso_link = "" imager_usb_disk_selected = "" diff --git a/scripts/gen.py b/scripts/gen.py index d024aaca..5f832731 100644 --- a/scripts/gen.py +++ b/scripts/gen.py @@ -229,6 +229,23 @@ def copy_mbusb_dir_usb(usb_disk): else: log('EFI directory already exist. Not copying.') + # For backward compatibility + if not os.path.exists(os.path.join(usb_mount_path, 'EFI', 'BOOT', 'bootx64-gpt.efi')): + shutil.copy(resource_path(os.path.join('data', 'EFI', 'BOOT', 'bootx64-gpt.efi')), + os.path.join(usb_mount_path, 'EFI', 'BOOT', 'bootx64-gpt.efi')) + + if not os.path.exists(os.path.join(usb_mount_path, 'EFI', 'BOOT', 'bootx64-msdos.efi')): + shutil.copy(resource_path(os.path.join('data', 'EFI', 'BOOT', 'bootx64-msdos.efi')), + os.path.join(usb_mount_path, 'EFI', 'BOOT', 'bootx64-msdos.efi')) + + if not os.path.exists(os.path.join(usb_mount_path, 'multibootusb', 'grub', 'core-gpt.img')): + shutil.copy(resource_path(os.path.join('data', 'multibootusb', 'grub', 'core-gpt.img')), + os.path.join(usb_mount_path, 'multibootusb', 'grub', 'core-gpt.img')) + + if not os.path.exists(os.path.join(usb_mount_path, 'multibootusb', 'grub', 'core-msdos.img')): + shutil.copy(resource_path(os.path.join('data', 'multibootusb', 'grub', 'core-msdos.img')), + os.path.join(usb_mount_path, 'multibootusb', 'grub', 'core-msdos.img')) + return result diff --git a/scripts/gui/about.ui b/scripts/gui/about.ui index 2d92633d..be6770e9 100644 --- a/scripts/gui/about.ui +++ b/scripts/gui/about.ui @@ -25,7 +25,7 @@ - <html><head/><body><p align="center">An advanced bootable usb creator with option to install/uninstall multiple distros.</p><p align="center">This software is written in Python and PyQt. </p><p align="center">Copyright 2010-2017 Sundar</p><p align="center"><span style=" font-weight:600;">Author(s)</span>: Sundar, Ian Bruce, LiQiong Lee and Alin Trăistaru (alindt)</p><p align="center"><span style=" font-weight:600;">Licence</span>: GPL version 2 or later</p><p align="center"><span style=" font-weight:600;">Home page</span>: <a href=" http://multibootusb.org"><span style=" text-decoration: underline; color:#0000ff;">http://multibootusb.org</span></a></p><p align="center"><span style=" font-weight:600;">Help/Email</span>: feedback.multibootusb@gmail.com</p><p align="center"><span style=" font-weight:600;">Source Code</span>: <a href="https://github.com/mbusb/multibootusb"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/mbusb/multibootusb</span></a></p><p><br/></p></body></html> + <html><head/><body><p align="center">An advanced bootable usb creator with option to install/uninstall multiple distros.</p><p align="center">This software is written in Python and PyQt. </p><p align="center">Copyright 2010-2017 Sundar</p><p align="center"><span style=" font-weight:600;">Author(s)</span>: Sundar, Ian Bruce, LiQiong Lee and Alin Trăistaru (alindt)</p><p align="center"><span style=" font-weight:600;">Licence</span>: GPL version 2 or later</p><p align="center"><span style=" font-weight:600;">Home page</span>: <a href="http://multibootusb.org"><span style=" text-decoration: underline; color:#0000ff;">http://multibootusb.org</span></a></p><p align="center"><span style=" font-weight:600;">Help/Email</span>: feedback.multibootusb@gmail.com</p><p align="center"><span style=" font-weight:600;">Source Code</span>: <a href="https://github.com/mbusb/multibootusb"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/mbusb/multibootusb</span></a></p><p><br/></p></body></html> diff --git a/scripts/gui/ui_about.py b/scripts/gui/ui_about.py index 4f8246f5..c16b68df 100644 --- a/scripts/gui/ui_about.py +++ b/scripts/gui/ui_about.py @@ -48,7 +48,7 @@ def setupUi(self, About): def retranslateUi(self, About): _translate = QtCore.QCoreApplication.translate About.setWindowTitle(_translate("About", "Dialog")) - self.label_6.setText(_translate("About", "

An advanced bootable usb creator with option to install/uninstall multiple distros.

This software is written in Python and PyQt.

Copyright 2010-2017 Sundar

Author(s): Sundar, Ian Bruce, LiQiong Lee and Alin Trăistaru (alindt)

Licence: GPL version 2 or later

Home page: http://multibootusb.org

Help/Email: feedback.multibootusb@gmail.com

Source Code: https://github.com/mbusb/multibootusb


")) + self.label_6.setText(_translate("About", "

An advanced bootable usb creator with option to install/uninstall multiple distros.

This software is written in Python and PyQt.

Copyright 2010-2017 Sundar

Author(s): Sundar, Ian Bruce, LiQiong Lee and Alin Trăistaru (alindt)

Licence: GPL version 2 or later

Home page: http://multibootusb.org

Help/Email: feedback.multibootusb@gmail.com

Source Code: https://github.com/mbusb/multibootusb


")) self.button_close.setText(_translate("About", "Close")) @@ -60,4 +60,3 @@ def retranslateUi(self, About): ui.setupUi(About) About.show() sys.exit(app.exec_()) - diff --git a/scripts/iso.py b/scripts/iso.py index 6c46fbf6..93a20c81 100644 --- a/scripts/iso.py +++ b/scripts/iso.py @@ -60,12 +60,20 @@ def iso_size(iso_link): return os.path.getsize(iso_link) +def is_readable(iso_link): + return os.access(iso_link, os.R_OK) + + def is_bootable(iso_link): """ Check if an ISO has the ability to boot. :return: True if ISO is bootable and False if not. """ - iso9660fs = ISO9660(iso_link) + try: + iso9660fs = ISO9660(iso_link) + except IOError as e: + log(str(e)) + raise isBootable = iso9660fs.checkISOBootable() return bool(isBootable) diff --git a/scripts/isodump3.py b/scripts/isodump3.py index f6ee215c..8c536754 100644 --- a/scripts/isodump3.py +++ b/scripts/isodump3.py @@ -44,21 +44,21 @@ E_DEVICEFILE = -2 # can't write device file class PrimaryVolume(Structure): - def __init__(self): - self.sysIdentifier = "" - self.volIdentifier = "" - self.volSize = 0 - self.volSeq = 0 - self.blockSize = 0 - self.ptSize = 0 - self.ptLRd = 0 - self.fsVer = 0 - self.rootLoc = 0 - self.rootTotal = 0 + def __init__(self): + self.sysIdentifier = "" + self.volIdentifier = "" + self.volSize = 0 + self.volSeq = 0 + self.blockSize = 0 + self.ptSize = 0 + self.ptLRd = 0 + self.fsVer = 0 + self.rootLoc = 0 + self.rootTotal = 0 class Rrip(Structure): def __init__(self): - self.offset = -1 + self.offset = -1 self.altname = "" self.devH = 0 self.devL = 0 @@ -106,7 +106,10 @@ def __init__(self, isofile): f = open(isofile, 'rb') except(IOError): sys.stderr.write("can't open {0}".format(isofile)) - sys.exit(-1) + raise + + if os.path.getsize(isofile) == 0: + raise IOError("File {0} appears to be empty".format(isofile)) self.isoFile = f self.priVol = None @@ -803,4 +806,3 @@ def usage(): else: gen.log("writeDir(%s)->(%s) with pattern(%s)"%(isodir, o_path, pattern)) sys.exit(iso9660fs.writeDir(isodir, o_path, pattern, r, True)) - diff --git a/scripts/mbusb_cli.py b/scripts/mbusb_cli.py index 71e3a9b2..a01cb0dc 100644 --- a/scripts/mbusb_cli.py +++ b/scripts/mbusb_cli.py @@ -16,6 +16,7 @@ from .syslinux import * from .install import * from . import imager +from . import syslinux def read_input_uninstall(): @@ -99,6 +100,7 @@ def iso_install(iso_image): install_progress() syslinux_distro_dir(config.usb_disk, iso_image, _distro) syslinux_default(config.usb_disk) + replace_grub_binary() update_distro_cfg_files(iso_image, config.usb_disk, _distro) log('Finished installing ' + iso.iso_basename(iso_image)) else: @@ -110,6 +112,7 @@ def iso_install(iso_image): install_progress() syslinux_distro_dir(config.usb_disk, iso_image, _distro) syslinux_default(config.usb_disk) + replace_grub_binary() update_distro_cfg_files(iso_image, config.usb_disk, _distro) log('Finished installing ' + iso.iso_basename(iso_image)) else: @@ -167,3 +170,40 @@ def cli_dd(): else: log('\nAuto install is not recommended in direct writing method. Please choose without \'-y\' option.\n') sys.exit(2) + + +def cli_install_syslinux(): + """ + Install syslinux on a target USB disk. It will installed on 'multibootusb' directory + :return: + """ + if platform.system() == 'Linux': + if config.usb_disk[-1].isdigit() is not True: + log('Selected USB disk is not a partition. Please enter the partition eg. \'/dev/sdb1\'') + sys.exit(2) + elif is_root() is False: + log("You need to have root privileges to run this script.\nPlease try again using admin privilege (sudo).") + sys.exit(2) + + if config.yes is not True: + log('\nInitiating process for installing syslinux on ' + config.usb_disk) + log('Selected target device is : ' + quote(config.usb_disk)) + log('Syslinux install directory : \'multibootusb\'\n') + log('Please confirm the option.') + log('Y/y/Yes/yes/YES or N/n/No/no/NO') + if read_input_yes() is True: + if syslinux.syslinux_default(config.usb_disk) is True: + log('Syslinux successfully installed on ' + config.usb_disk) + else: + log('Failed to install syslinux on ' + config.usb_disk) + else: + log('Operation cancelled by user. Exiting...') + sys.exit(2) + else: + log('\nSkipping user input and installing syslinux on ' + config.usb_disk) + if syslinux.syslinux_default(config.usb_disk) is True: + log('Syslinux successfully installed on ' + config.usb_disk) + else: + log('Failed to install syslinux on ' + config.usb_disk) + sys.exit(2) + diff --git a/scripts/mbusb_gui.py b/scripts/mbusb_gui.py index f47bc595..a35d7ce9 100644 --- a/scripts/mbusb_gui.py +++ b/scripts/mbusb_gui.py @@ -13,6 +13,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets import subprocess import time +import webbrowser from scripts.gui.ui_multibootusb import Ui_MainWindow from scripts.gui.ui_about import Ui_About from . import usb @@ -130,7 +131,7 @@ def onAboutClick(self): about.setWindowTitle("About MultiBootUSB - " + mbusb_version()) about.setWindowIcon(QtGui.QIcon(resource_path(os.path.join("data", "tools", "multibootusb.png")))) about.ui.button_close.clicked.connect(about.close) - + about.ui.label_6.linkActivated.connect(webbrowser.open_new_tab) about.exec_() def onComboChange(self): @@ -157,6 +158,9 @@ def onComboChange(self): self.ui.usb_type.setText(config.usb_details.get('devtype', "")) self.ui.usb_fs.setText(config.usb_details.get('file_system', "")) + # Get the GPT status of the disk and store it on a variable + usb.gpt_device(config.usb_disk) + self.update_list_box(config.usb_disk) self.ui_update_persistence() else: @@ -214,6 +218,22 @@ def browse_iso(self): 'Img Files(*.img);; All Files(*.*)')[0] if config.image_path: + # sanity checks + if not is_readable(config.image_path): + QtWidgets.QMessageBox.critical( + self, + "ISO Not readable", + "Sorry, the file \"{0}\" is not readable.".format(config.image_path) + ) + return + if iso_size(config.image_path) == 0: + QtWidgets.QMessageBox.critical( + self, + "ISO is an empty file", + "Sorry, the file \"{0}\" contains no data.".format(config.image_path) + ) + return + default_dir_path = os.path.dirname(config.image_path) gen.write_to_file(preference_file_path, default_dir_path) @@ -309,6 +329,7 @@ def install_syslinux(self): self.ui.statusbar.showMessage(str("Status: Installing Syslinux...")) syslinux_distro_dir(config.usb_disk, config.image_path, config.distro) syslinux_default(config.usb_disk) + replace_grub_binary() update_distro_cfg_files(config.image_path, config.usb_disk, config.distro, config.persistence) self.update_list_box(config.usb_disk) if sys.platform.startswith("linux"): @@ -508,14 +529,23 @@ def onCreateClick(self): "No space available on " + config.usb_disk) self.ui_enable_controls() else: - reply = QtWidgets.QMessageBox.question(self, 'Review selection...', - 'Selected USB disk: %s\n' % config.usb_disk + - 'USB mount point: %s\n' % config.usb_mount + - 'Selected distro: %s\n\n' % iso_name( - config.image_path) + - 'Proceed with installation?', - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.No) + if config.distro == 'memdisk_iso': + reply = QtWidgets.QMessageBox.question(self, 'Review selection...', + 'The ISO sleceted is not supported at the moment.\n' + 'You can try booting ISO using memdisk.\n' + 'Distro can be uninstalled anytime from main menu.\n\n' + 'Proceed with installation?', + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, + QtWidgets.QMessageBox.No) + else: + reply = QtWidgets.QMessageBox.question(self, 'Review selection...', + 'Selected USB disk: %s\n' % config.usb_disk + + 'USB mount point: %s\n' % config.usb_mount + + 'Selected distro: %s\n\n' % iso_name( + config.image_path) + + 'Proceed with installation?', + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, + QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: self.ui.slider_persistence.setEnabled(False) diff --git a/scripts/syslinux.py b/scripts/syslinux.py index 96cc9505..f195e7dc 100644 --- a/scripts/syslinux.py +++ b/scripts/syslinux.py @@ -19,23 +19,72 @@ extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs"] syslinux_fs = ["vfat", "ntfs", "FAT32", "NTFS"] mbr_bin = resource_path(os.path.join("data", "tools", "mbr.bin")) +win_gdisk = resource_path(os.path.join('data', 'tools', 'gdisk', 'gdisk.exe')) + + +def gpt_part_table(usb_disk): + """ + Check if selected USB contain GPT or MBR partition table + :return: True if GPT else False + """ + if platform.system() == "Linux": + _cmd_out = subprocess.check_output("parted " + usb_disk[:-1] + " print", shell=True) + if b'msdos' in _cmd_out: + return False + elif b'gpt' in _cmd_out: + return True + elif platform.system() == 'Windows': + win_usb_disk_no = str(usb.get_physical_disk_number(config.usb_disk)) + if config.usb_gpt is True: + return True + elif config.usb_gpt is False: + return False + + +def get_mbr_bin_path(usb_disk): + """ + Check if partition table type is mbr or gpr using parted command under Linux + :param usb_disk: path to whole USB disk '/dev/sdb' + :return: Path to mbr.bin for use + """ + if config.usb_gpt is False: + log('Using mbr.bin msdos mbr install.') + return resource_path(os.path.join("data", "tools", "mbr.bin")) + elif config.usb_gpt is True: + log('Using gptmbr.bin for mbr install.') + return resource_path(os.path.join("data", "tools", "gptmbr.bin")) + + return False def set_boot_flag(usb_disk): if platform.system() == "Linux": log("\nChecking boot flag on " + usb_disk[:-1], '\n') cmd_out = subprocess.check_output("parted -m -s " + usb_disk[:-1] + " print", shell=True) - if b'boot' in cmd_out: - log("\nDisk " + usb_disk[:-1] + " already has boot flag.\n") - return True - else: - log("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 boot on", '\n') - if subprocess.call("parted " + usb_disk[:-1] + " set 1 boot on", shell=True) == 0: - log("\nBoot flag set to bootable " + usb_disk[:-1], '\n') + if gpt_part_table(usb_disk) is False: + if b'boot' in cmd_out: + log("\nDisk " + usb_disk[:-1] + " already has boot flag.\n") return True else: - log("\nUnable to set boot flag on " + usb_disk[:-1], '\n') - return False + log("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 boot on", '\n') + if subprocess.call("parted " + usb_disk[:-1] + " set 1 boot on", shell=True) == 0: + log("\nBoot flag set to bootable " + usb_disk[:-1], '\n') + return True + else: + log("\nUnable to set boot flag on " + usb_disk[:-1], '\n') + return False + elif gpt_part_table(usb_disk) is True: + if b'legacy_boot' in cmd_out: + log("\nGPT Disk " + usb_disk[:-1] + " already has legacy_boot flag.\n") + return True + else: + log("\nExecuting ==> parted " + usb_disk[:-1] + " set 1 legacy_boot on", '\n') + if subprocess.call("parted " + usb_disk[:-1] + " set 1 legacy_boot on", shell=True) == 0: + log("\nBoot flag set to legacy_boot " + usb_disk[:-1], '\n') + return True + else: + log("\nUnable to set legacy_boot flag on " + usb_disk[:-1], '\n') + return False def syslinux_default(usb_disk): @@ -48,8 +97,17 @@ def syslinux_default(usb_disk): usb_details = usb.details(usb_disk) usb_fs = usb_details['file_system'] usb_mount = usb_details['mount_point'] - mbr_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin + ' of=' + usb_disk[:-1] - # log(usb_fs) + mbr_bin = get_mbr_bin_path(usb_disk) + + if platform.system() == 'Linux': + mbr_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin + ' of=' + usb_disk[:-1] + else: + win_usb_disk_no = str(usb.get_physical_disk_number(config.usb_disk)) + _windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe")) + _input = "if=" + mbr_bin + _output = 'of=\\\.\\physicaldrive' + win_usb_disk_no + mbr_install_cmd = _windd + ' ' + _input + ' ' + _output + ' count=1' + if usb_fs in extlinux_fs: extlinu_cmd = extlinux_path + ' --install ' + os.path.join(usb_mount, 'multibootusb') if os.access(extlinux_path, os.X_OK) is False: @@ -78,6 +136,7 @@ def syslinux_default(usb_disk): if subprocess.call(syslinux_cmd, shell=True) == 0: log("\nDefault syslinux install is success...\n") config.status_text = 'Default syslinux successfully installed...' + log('\nExecuting ==> ' + mbr_install_cmd) if subprocess.call(mbr_install_cmd, shell=True) == 0: config.status_text = 'mbr install is success...' log("\nmbr install is success...\n") @@ -90,12 +149,35 @@ def syslinux_default(usb_disk): elif platform.system() == "Windows": syslinux = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4.exe")) - log('Executing ==>' + syslinux + ' -maf -d multibootusb ' + usb_disk) config.status_text = 'Installing default syslinux version 4...' - if subprocess.call(syslinux + ' -maf -d multibootusb ' + usb_disk, shell=True) == 0: + # syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk + if config.usb_gpt is False: + syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk + else: + syslinux_cmd = syslinux + ' -af -d multibootusb ' + usb_disk + log('Executing ==> ' + syslinux_cmd) + ''' + if gpt_part_table(config.usb_disk) is False: + syslinux_cmd = syslinux + ' -maf -d multibootusb ' + usb_disk + else: + syslinux_cmd = syslinux + ' -af -d multibootusb ' + usb_disk + ''' + if subprocess.call(syslinux_cmd, shell=True) == 0: config.status_text = 'Default syslinux successfully installed...' log("\nDefault syslinux install is success...\n") - return True + # We will need to flash gptmbr.bin only for GPT disk. As of version 8.9.0 this corrupts the gpt disk. + # Therefore not included for BIOS booting. GPT disk may work on UEFI system. + # if gpt_part_table(config.usb_disk) is True: + ''' + if config.usb_gpt is False: + log('\nExecuting ==> ' + mbr_install_cmd) + if subprocess.call(mbr_install_cmd, shell=True) == 0: + log("\nmbr install is success...\n") + return True + else: + log('Disk uses GPT and mbr install is not required...') + ''' + else: log("\nFailed to install default syslinux...\n") config.status_text = 'Failed to install default syslinux...' @@ -203,6 +285,51 @@ def syslinux_distro_dir(usb_disk, iso_link, distro): else: log("\nFailed to install syslinux on distro directory...\n") + +def replace_grub_binary(): + """ + This function checks if correct binary is installed on grub and EFI directory. + If mismatch is found between partition table and binary, replace it correct one. + Default binaries will work for msdos partition table and therefore need not be replaced. + :return: + """ + grub_efi_bin_path = os.path.join(config.usb_mount, 'EFI', 'BOOT', 'bootx64.efi') + grub_efi_bin_gpt_path = os.path.join(config.usb_mount, 'EFI', 'BOOT', 'bootx64-gpt.efi') + grub_efi_bin_msdos_path = os.path.join(config.usb_mount, 'EFI', 'BOOT', 'bootx64-msdos.efi') + core_img_bin_path = os.path.join(config.usb_mount, 'multibootusb', 'grub', 'core.img') + core_img_bin_msdos_path = os.path.join(config.usb_mount, 'multibootusb', 'grub', 'core-msdos.img') + core_img_bin_gpt_path = os.path.join(config.usb_mount, 'multibootusb', 'grub', 'core-gpt.img') + + if platform.system() in ['Linux', 'Windows']: + if gpt_part_table(config.usb_disk) is True: + log('Replacing efi binary with gpt compatible one...') + try: + shutil.copy(grub_efi_bin_gpt_path, grub_efi_bin_path) + except Exception as e: + log(e) + log('Failed to replace efi binary...') + log('Replacing core.img binary with gpt compatible one...') + try: + shutil.copy(core_img_bin_gpt_path, core_img_bin_path) + except Exception as e: + log(e) + log('Failed to replace efi binary...') + else: + log('Replacing efi binary with msdos compatible one...') + try: + # shutil.copy(core_img_bin_gpt_path, core_img_bin_path) + shutil.copy(grub_efi_bin_msdos_path, grub_efi_bin_path) + except Exception as e: + log(e) + log('Failed to replace efi binary...') + try: + log('Replacing core.img with msdos compatible one...') + shutil.copy(core_img_bin_msdos_path, core_img_bin_path) + except Exception as e: + log(e) + log('Failed to replace core.img binary...') + + if __name__ == '__main__': if os.geteuid() != 0: log('Please running this script with sudo/root/admin privilage.') diff --git a/scripts/usb.py b/scripts/usb.py index 6dfdd894..d214fa3f 100644 --- a/scripts/usb.py +++ b/scripts/usb.py @@ -13,6 +13,7 @@ import collections import ctypes import subprocess +from . import config from . import gen if platform.system() == 'Linux': from . import udisks @@ -218,7 +219,11 @@ def details_udev(usb_disk_part): gen.log("ERROR: Unknown disk/partition (%s)" % str(usb_disk_part)) return None - fdisk_cmd_out = subprocess.check_output('fdisk -l ' + usb_disk_part, shell=True) + try: + fdisk_cmd_out = subprocess.check_output('fdisk -l ' + usb_disk_part, shell=True) + except subprocess.CalledProcessError: + gen.log("ERROR: fdisk failed on disk/partition (%s)" % str(usb_disk_part)) + return None if b'Extended' in fdisk_cmd_out: mount_point = '' @@ -349,6 +354,43 @@ def bytes2human(n): return "%sB" % n +def gpt_device(dev_name): + """ + Find if the device inserted is GPT or not. We will just change the variable parameter in config file for later use + :param dev_name: + :return: True if GPT else False + """ + if platform.system() == 'Windows': + diskpart_cmd = 'diskpart.exe /s ' + os.path.join('data', 'tools', 'gdisk', 'list-disk.txt') + dev_no = get_physical_disk_number(dev_name) + cmd_out = subprocess.check_output(diskpart_cmd) + cmd_spt = cmd_out.split(b'\r') + for line in cmd_spt: + line = line.decode('utf-8') + if 'Disk ' + dev_no in line: + if '*' not in line.split()[-1]: + config.usb_gpt = False + gen.log('Device ' + dev_name + ' is a MBR disk...') + return False + else: + config.usb_gpt = True + gen.log('Device ' + dev_name + ' is a GPT disk...') + return False + if platform.system() == "Linux": + if gen.has_digit(dev_name): + _cmd_out = subprocess.check_output("parted " + dev_name[:-1] + " print", shell=True) + else: + _cmd_out = subprocess.check_output("parted " + dev_name + " print", shell=True) + if b'msdos' in _cmd_out: + config.usb_gpt = False + gen.log('Device ' + dev_name + ' is a MBR disk...') + return False + elif b'gpt' in _cmd_out: + config.usb_gpt = True + gen.log('Device ' + dev_name + ' is a GPT disk...') + return True + + def win_disk_details(disk_drive): """ Populate and get details of an USB disk under windows. Minimum required windows version is Vista. @@ -421,8 +463,26 @@ def details(usb_disk_part): details = details_udisks2(usb_disk_part) elif platform.system() == 'Windows': details = win_disk_details(usb_disk_part) + return details + +def get_physical_disk_number(usb_disk): + """ + Get the physical disk number as detected ny Windows. + :param usb_disk: USB disk (Like F:) + :return: Disk number. + """ + import wmi + c = wmi.WMI() + for physical_disk in c.Win32_DiskDrive(): + for partition in physical_disk.associators("Win32_DiskDriveToDiskPartition"): + for logical_disk in partition.associators("Win32_LogicalDiskToPartition"): + if logical_disk.Caption == usb_disk: + # gen.log("Physical Device Number is " + partition.Caption[6:-14]) + return str(partition.Caption[6:-14]) + + if __name__ == '__main__': usb_devices = list_devices() if usb_devices is not None: