Merge remote-tracking branch 'upstream/master' into fixes_397

pull/417/head
Alan D Moore 6 years ago
commit 22089c63d3

2
.gitignore vendored

@ -19,6 +19,7 @@ lib/
lib64/
parts/
sdist/
deb_dist/
var/
*.egg-info/
.installed.cfg
@ -97,6 +98,7 @@ multibootusb*.tar.gz
# Temp files
data/multibootusb/grub/menus.zip
MANIFEST
# Setup file

@ -1,3 +1,20 @@
Version - 9.3.0
---------------
* Provide information about free and total space of selected USB device in the main GUI
* Unmount partitions and lock physical drive when dd-ing iso on Windows
* Unmount the USB device partition while using QEMU to boot from USB on Linux
* Fix description of an exception which gets raised of diskpart.exe fails
* Fix repeated calls to update GUI
* Fix syntax of an exception description
* Imager writing on entire disk instead of partitions
* Fix crash when using imager under certain conditions
* Unmount partitions before dding iso image on Linux
* Catch an error generated while copying iso image to the target USB
* Fix uuid generation from NTFS/FAT32 partition on Windows
* Add gptmbr.bin to package data files
* Catch exceptions raised during install_syslinux() and make effort to undo partially completed installation
* Fix a doc string to keep up with the function signature change
Version - 9.2.0
---------------
* Welcome onboard Shiniji Suzuki. The most of the bug fixes and additional features implemented are done by him. A big tanks to him.

@ -126,12 +126,12 @@ class pkg():
Depends3: python3-pyqt5, parted, util-linux, mtools, python3-dbus, python3-pyudev, p7zip-full, python3-six
Build-Depends: python3-all
Section: system
XS-Python-Version: = 3.5
XS-Python-Version: = 3
Debian-Version: 1"""
with open("stdeb.cfg", "w") as f:
f.write(stdcfg)
if subprocess.call('/usr/bin/python3.5 setup.py --command-packages=stdeb.command bdist_deb', shell=True) == 0 and \
if subprocess.call('/usr/bin/python3 setup.py --command-packages=stdeb.command bdist_deb', shell=True) == 0 and \
os.path.exists(os.path.join("deb_dist", "python3-multibootusb_" + self.version + "-1_all.deb")):
try:
shutil.copy2(os.path.join("deb_dist", "python3-multibootusb_" + self.version + "-1_all.deb"),
@ -159,7 +159,7 @@ class pkg():
"Requires = " + require)
with open("setup.cfg", "w") as f:
f.write(setup_cfg)
if subprocess.call('/usr/bin/python3.5 setup.py bdist_rpm', shell=True) == 0 and \
if subprocess.call('/usr/bin/python3 setup.py bdist_rpm', shell=True) == 0 and \
os.path.exists(os.path.join("dist", "multibootusb-" + self.version + "-1.noarch.rpm")):
if self.pkg_name == 'suse':
package = "multibootusb-" + self.version + "-1suse.noarch.rpm"

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
9.2.0
9.3.0

@ -9,6 +9,7 @@ import os
import getopt
import sys
import platform
import shutil
# The following line is required for distros based on rpm so as to avoid import errors when running from
# installed system
@ -33,13 +34,15 @@ try:
from scripts import admin
from scripts import gen
from scripts import config
except:
from scripts import osdriver
except ImportError:
try:
from .scripts.mbusb_cli import *
from .scripts import admin
from .scripts import gen
from .scripts import config
except:
from .scripts import osdriver
except ImportError:
import scripts
gui = True
@ -102,7 +105,7 @@ Example for installing multiple distros without user intervention:
Windows:
python3 multibootusb -c -y -i ../../favourite.iso,../../other-distro.iso -t G:
Example for writing ISO image to target USB disk (will destroy data on USB disk):
Linux:
@ -160,7 +163,7 @@ if __name__ == '__main__':
config.image_path = arg.split(',')
elif opt in ('-t', '--target'):
# Convert to upper if windows drive name is given
config.usb_disk = len(arg)==2 and arg[0].isalpha() and arg[1]==':'\
config.usb_disk = len(arg) == 2 and arg[0].isalpha() and arg[1] == ':'\
and arg.upper() or arg
elif opt in ('-c', '--command'):
gui = False
@ -184,33 +187,46 @@ if __name__ == '__main__':
sys.exit()
'''
if config.debug is True:
from scripts.debug import colors
log(colors.HEADER + "=== DEBUG ENABLED ===")
def main():
if config.debug is True:
from scripts.debug import colors
log(colors.HEADER + "=== DEBUG ENABLED ===")
if gui is False:
check_admin()
if uninstall is True and config.usb_disk is not '':
cli_uninstall_distro()
elif uninstall is True and config.usb_disk is '':
log('\nYou must provide \'-t\' option to point to your USB disk for uninstalling a distro.\n'
'See the usage example below.')
usage()
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()
elif config.cli_dd is True:
cli_dd()
else:
if gui is False:
check_admin()
if uninstall is True and config.usb_disk is not '':
cli_uninstall_distro()
elif uninstall is True and config.usb_disk is '':
log('\nYou must provide \'-t\' option to point to your USB disk for uninstalling a distro.\n'
'See the usage example below.')
usage()
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()
elif config.cli_dd is True:
cli_dd()
else:
running_from()
cli_install_distro()
elif gui is True:
running_from()
cli_install_distro()
start_gui()
elif gui is True:
running_from()
start_gui()
if __name__ == '__main__':
osdriver.initialize()
try:
main()
finally:
from scripts import usb
for p in config.remounted_partitions:
log('Unmouting %s at exit' % p)
usb.unmount(p)

@ -2,12 +2,19 @@
# Generic wrapper for locating multibootusb file and execute pkexec accordingly
# Bin pat of this file and policy file have to match
# Ensure that root is added in to xhost list to access X-Server
# Otherwise script will fail to launch GUI
if type "xhost" > /dev/null; then
xhost local:root > /dev/null
fi
if [ $(which pkexec) ]; then
if [ -f /usr/bin/multibootusb ]; then
if [ -f /usr/bin/multibootusb ]; then # path to debian based distros
pkexec --disable-internal-agent "/usr/bin/multibootusb" "$@"
fi
if [ -f /usr/local/bin/multibootusb ]; then
if [ -f /usr/local/bin/multibootusb ]; then # path to fedora based distros
pkexec --disable-internal-agent "/usr/local/bin/multibootusb" "$@"
fi
else

@ -12,7 +12,7 @@
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/local/bin/multibootusb</annotate>
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/multibootusb</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>

@ -7,12 +7,12 @@
# under the terms of GNU General Public License, v.2 or above
iso_link = ""
usb_disk = ""
usb_disk = None
usb_mount = ""
usb_uuid = ""
usb_label = ""
usb_details = ''
image_path = ""
image_path = None
persistence = 0
persistence_available = False
persistence_max_size = 0
@ -35,6 +35,7 @@ imager_usb_disk_selected = ""
imager_lock = ""
imager_percentage = ""
imager_status_text = ""
imager_return = ""
install_size = ""
@ -42,5 +43,32 @@ editors_linux = ["xdg-open", "gedit", "kate", "kwrite"]
editors_win = ["notepad++.exe", "notepad.exe"]
imager_usb_disk = []
remounted_partitions = []
debug = False
# protected_drives = ['C:','D:','E:', '/dev/sda', '/dev/sdb', '/dev/sdc']
# If turned off, qemu will be sought at a few preset locations
# first before deciding to use the bundled exe.
# Set 'qemu_exe_path' to explicitly specify.
qemu_use_builtin = True # Relevant on Windows only
# qemu_exe_path = r"C:\pkgs\qemu\qemu-system-x86_64.exe"
# Relevant on Windows only
# Enable QEMU accelaration by Intel HAXM hypervisor.
# Bundled QEMU does not support this.
# See https://www.qemu.org/2017/11/22/haxm-usage-windows/ for setup.
qemu_use_haxm = not qemu_use_builtin # Relevant on Windows only
# qemu_use_kvm = False
# qemu_bios = 'OVMF.fd'
def update_usb_mount(new_usb_details):
global usb_mount, usb_details
usb_mount = new_usb_details['mount_point'].replace('\\x20', ' ')
usb_details = new_usb_details
def add_remounted(usb_disk):
if usb_disk not in remounted_partitions:
remounted_partitions.append(usb_disk)

@ -6,13 +6,15 @@
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
# under the terms of GNU General Public License, v.2 or above
from functools import partial
import os
import platform
import re
from .iso import *
from .isodump3 import ISO9660
from .gen import *
from . import _7zip
from .gen import *
from . import iso
from .isodump3 import ISO9660
def distro(iso_cfg_ext_dir, iso_link, expose_exception=False):
@ -23,181 +25,235 @@ def distro(iso_cfg_ext_dir, iso_link, expose_exception=False):
"""
# iso9660fs = ISO9660(iso_link)
# iso_file_list = iso9660fs.readDir("/")
iso_file_list = _7zip.list_iso(iso_link, expose_exception=expose_exception)
distro = None # tenatively set to None
iso_file_list = _7zip.list_iso(
iso_link, expose_exception=expose_exception)
iso_file_list_lower = [f.lower() for f in iso_file_list]
v_isolinux_bin_exists = iso.isolinux_bin_exist(iso_link)
# Let's have less costly checks first.
# We'll have to make these checks as strictive as possible
# so that keyword based tests will not be skipped because
# of a false positive.
if iso_file_list:
distro = perform_strict_detections(iso_cfg_ext_dir, iso_file_list)
if distro:
return distro
distro = detect_iso_from_file_list(iso_file_list)
if distro:
return distro
else:
iso_file_list = []
def run_contains(keywords, filename, file_content, iso_flielist,
isolinux_bin_exists):
return any(k in file_content for k in keywords.split('|'))
def contains(keywords):
return partial(run_contains, keywords.lower())
def run_file_exists(filename_sought, filename, file_content, iso_filelist,
isolinux_bin_exists):
return filename_sought in iso_filelist
def file_exists(filename_sought):
return partial(run_file_exists, filename_sought)
def run_isolinux_bin_exists(exist_or_not, filename, file_content,
iso_filelist, isolinux_bin_exists):
return exist_or_not is isolinux_bin_exists
def isolinux_bin_exists(exist_or_not):
return partial(run_isolinux_bin_exists, exist_or_not)
def run_not(predicate, filename, file_content,
iso_filelist, isolinux_bin_exists):
return not predicate(filename, file_content, iso_filelist,
isolinux_bin_exists)
def not_(predicate):
return partial(run_not, predicate)
# contains(X) predicates that X is contained in an examined text file.
# Multiple keywords can be concatenated by '|'. Predicates gets aaserted
# if anyone of the keywords is found in the text file.
# Sorry you can't include | in a keyword for now.
test_vector = [
('ubcd', contains('ubcd')),
('hbcd', contains('hbcd')),
('systemrescuecd', contains('systemrescuecd')),
('parted-magic', [contains('pmagic|partedmagic'),
isolinux_bin_exists(True)]),
# mounting fat filesystem hard coded in to initrd.
# Can be modified only under linux.
('mageialive', contains('mgalive')),
('arch', contains('archisolabel|misolabel|parabolaisolabel')),
('chakra', contains('chakraisolabel')),
('kaos', contains('kdeosisolabel')),
('debian', [contains('boot=live'), isolinux_bin_exists(True)]),
('grml', [contains('grml'), contains('live-media-path')]),
('debian-install', [contains('debian-installer'),
not_(file_exists('casper'))]),
('solydx', contains('solydx')),
('knoppix', contains('knoppix')),
('centos', contains('root=live:CDLABEL=CentOS')),
('fedora', contains('root=live:CDLABEL=|root=live:LABEL=')),
('fedora', contains('redcore')),
('redhat', contains('redhat')),
('slitaz', contains('slitaz|dban |ophcrack|tinycore|rescue.cpi'
'|xpud|untangle|4mlinux|partition wizard'
'|android-x86.png|riplinux|lebel dummy'
'|http://pogostick.net/~pnh/ntpasswd/'
'|AVG Rescue CD|AntivirusLiveCD'
'|lkrn|Nanolinux|OSForensics|PING')),
('slitaz', contains('minimal Slackware|Slackware-HOWTO')),
#('suse', contains('suse')),
('opensuse-install', contains('class opensuse')),
('ubuntu', contains('boot=casper')),
('wifislax', contains('wifislax')),
('slax', contains('slax')),
('sms', [contains('sms.jpg|vector |autoexec'),
isolinux_bin_exists(True)]),
('antix', contains('antix')),
('porteus', contains('porteus')),
('pclinuxos', contains('livecd=livecd|PCLinuxOS')),
('gentoo', contains('looptype=squashfs|http://dee.su/liberte')),
('finnix', contains('finnix')),
('wifiway', contains('wifiway')),
('puppy', contains('puppy|quirky|fatdog|slacko|xenialpup')),
('ipcop', contains('ipcop')),
('ipfire', contains('ipfire')),
('salix-live', [contains('zenwalk|slack|salix'),
contains('live')]),
('zenwalk', contains('zenwalk|slack|salix')),
('ubuntu-server', contains('ubuntu server')),
('centos-install', contains('Install CentOS')),
('centos', contains('centos')),
('trinity-rescue', contains('Trinity Rescue Kit')),
('alpine', contains('alpine')),
('kaspersky', contains('http://support.kaspersky.com')),
('alt-linux', contains('ALT Linux')),
('Windows', contains('Sergei Strelec')),
('ReactOS', contains('ReactOS')),
('fsecure', contains('fsecure')),
('pc-unlocker', contains('default rwp')),
('pc-tool', contains('/system/stage1')),
('grub2only', contains('vba32rescue')),
('rising-av', contains('BOOT_IMAGE=rising')),
('Avira-RS', contains('Avira Rescue System')),
('insert', contains('BOOT_IMAGE=insert')),
('sgrubd2', contains('Super Grub Disk')),
]
# I'm not sure if this platform check is necessary but I will
# avoid removal to not alter the behaviour.
if platform.system() == "Linux" or platform.system() == "Windows":
for path, subdirs, files in os.walk(iso_cfg_ext_dir):
for name in files:
if name.endswith(('.cfg', '.CFG', '.txt', '.TXT', '.lst')):
if name.lower()=='i18n.cfg':
# i18n.cfg in salitaz-rolling cause misdetection
# of centos by the following line.
# MENU LABEL English US (acentos)
continue
try:
# errors='ignore' is required as some files also contain non utf character
string = open(os.path.join(path, name), errors='ignore').read()
except IOError:
return "Read Error."
else:
if any("f4ubcd" in s.lower() for s in iso_file_list):
return "f4ubcd"
if re.search(r'ubcd', string, re.I):
return "ubcd"
elif re.search(r'Super Grub Disk', string, re.I):
return "sgrubd2"
elif re.search(r'hbcd', string, re.I):
return "hbcd"
elif re.search(r'systemrescuecd', string, re.I):
return "systemrescuecd"
elif re.search(r'pmagic|partedmagic', string, re.I) and isolinux_bin_exist(iso_link):
return "parted-magic"
elif re.search(r'mgalive', string, re.I): # mounting fat filesystem hard coded in to initrd.
# Can be modified only under linux.
return "mageialive"
elif re.search(r'archisolabel|misolabel|parabolaisolabel', string, re.I):
return "arch"
elif re.search(r'chakraisolabel', string, re.I):
return "chakra"
elif re.search(r'kdeosisolabel', string, re.I):
return "kaos"
elif re.search(r'boot=live', string, re.I) and isolinux_bin_exist(iso_link):
return "debian"
elif re.search(r'grml', string, re.I) and re.search(r'live-media-path=', string, re.I):
return "grml"
elif re.search(r'debian-installer', string, re.I) and not any("casper" in s.lower() for s in iso_file_list):
return "debian-install"
elif re.search(r'solydx', string, re.I):
return "solydx"
elif re.search(r'knoppix', string, re.I):
return "knoppix"
elif re.search(r'root=live:CDLABEL=', string, re.I) or re.search(r'root=live:LABEL=', string, re.I):
return "fedora"
elif re.search(r'redcore', string, re.I):
return "fedora"
elif re.search(r'redhat', string, re.I):
return "redhat"
elif re.search(
r'slitaz|dban |ophcrack|tinycore|rescue.cpi|xpud|untangle|4mlinux|partition wizard|android-x86.png|'
r'riplinux|lebel dummy|http://pogostick.net/~pnh/ntpasswd/|AVG Rescue CD|AntivirusLiveCD|'
r'lkrn|Nanolinux|OSForensics|acentos', string, re.I): # acentos is a text found in slitaz distro
return "slitaz"
elif re.search(r'minimal Slackware|Slackware-HOWTO', string, re.I):
# for minimal slackware detection
return "slitaz"
# elif re.search(r'suse', string, re.I):
# return "suse"
elif re.search(r'class opensuse', string, re.I):
return "opensuse-install"
elif re.search(r'boot=casper', string, re.I):
return "ubuntu"
elif re.search(r'wifislax', string, re.I):
return "wifislax"
elif re.search(r'slax', string, re.I):
return "slax"
elif re.search(r'sms.jpg|vector |autoexec', string, re.I) and isolinux_bin_exist(iso_link):
return "sms"
elif re.search(r'antix', string, re.I):
return "antix"
elif re.search(r'porteus', string, re.I):
return "porteus"
elif re.search(r'livecd=livecd|PCLinuxOS', string, re.I):
return "pclinuxos"
elif re.search(r'looptype=squashfs|http://dee.su/liberte', string, re.I):
return "gentoo"
elif re.search(r'finnix', string, re.I):
return "finnix"
elif re.search(r'wifiway', string, re.I):
return "wifiway"
elif re.search(r'puppy|quirky|fatdog|slacko|xenialpup', string, re.I):
return "puppy"
elif re.search(r'ipcop', string, re.I):
return "ipcop"
elif re.search(r'ipfire', string, re.I):
return "ipfire"
elif re.search(r'zenwalk|slack|salix', string, re.I) and re.search(r'live', string, re.I):
return "salix-live"
elif re.search(r'zenwalk|slack|salix', string, re.I) and not re.search(r'slacko', string, re.I):
return "zenwalk"
elif re.search(r'ubuntu server', string, re.I):
return "ubuntu-server"
elif re.search(r'root=live:CDLABEL=CentOS',
string, re.I):
return 'centos' # centos-live
elif re.search(r'Install CentOS', string, re.I):
return "centos-install"
elif re.search(r'CentOS', string, re.I):
return "centos"
elif re.search(r'Trinity Rescue Kit', string, re.I):
return "trinity-rescue"
elif re.search(r'alpine', string, re.I):
return "alpine"
elif re.search(r'http://support.kaspersky.com', string, re.I):
return "kaspersky"
elif re.search(r'ALT Linux', string, re.I):
return "alt-linux"
elif re.search(r'Sergei Strelec', string, re.I):
return "Windows"
elif re.search(r'ReactOS', string, re.I):
return "ReactOS"
elif re.search(r'fsecure', string, re.I):
return "fsecure"
elif re.search(r'default rwp', string, re.I):
return "pc-unlocker"
elif re.search(r'/system/stage1', string, re.I):
return 'pc-tool'
elif re.search(r'vba32rescue', string, re.I):
return 'grub2only'
elif re.search(r'BOOT_IMAGE=rising', string, re.I):
return 'rising-av'
elif re.search(r'Avira Rescue System', string, re.I):
return 'Avira-RS'
elif any("alpine-release" in s.lower() for s in iso_file_list):
return 'alpine'
elif re.search(r'BOOT_IMAGE=insert', string, re.I):
return 'insert'
distro = detect_iso_from_file_list(iso_link, iso_file_list)
name_lower = name.lower()
if not name_lower.endswith(('.cfg', '.txt', '.lst')):
continue
if name_lower=='i18n.cfg':
# i18n.cfg in salitaz-rolling cause misdetection
# of centos by the following line.
# MENU LABEL English US (acentos)
continue
try:
# errors='ignore' is required as some files also
# contain non utf character
string = open(os.path.join(path, name),
errors='ignore').read()
except IOError:
log("Read Error on %s." % name)
continue
if distro:
return distro
# FIXME: See the below comments.
# else:
# # FIXME: The idea of detecting as generic is to work like a unetbootin if other methods fails.
# # This simply extracts distro to root of the USB and install syslinux on isolinux.bin directory.
# # All works fine but unable to boot the distro successfully. Also, see the generic section from
# # syslinux, update_cfg and install_distro modules.
# if self.isolinux_bin_exist():
# return "generic"
elif str(iso_link).lower().endswith('.iso'):
return 'memdisk_iso'
elif str(iso_link).lower().endswith('.img'):
return 'memdisk_img'
else:
return None
def detect_iso_from_file_list(iso_link, iso_file_list):
for distro_, predicate in test_vector:
predicates = [predicate] if callable(predicate) \
else predicate
if all( p(name_lower, string.lower(), iso_file_list_lower,
v_isolinux_bin_exists) for p in predicates ):
return distro_
if distro:
return distro
# FIXME: See the below comments.
# else:
# # FIXME: The idea of detecting as generic is to work like a unetbootin if other methods fails.
# # This simply extracts distro to root of the USB and install syslinux on isolinux.bin directory.
# # All works fine but unable to boot the distro successfully. Also, see the generic section from
# # syslinux, update_cfg and install_distro modules.
# if self.isolinux_bin_exist():
# return "generic"
elif str(iso_link).lower().endswith('.iso'):
return 'memdisk_iso'
elif str(iso_link).lower().endswith('.img'):
return 'memdisk_img'
else:
return None
def detect_iso_from_file_list(iso_file_list):
"""
Fallback detection script from the content of an ISO.
:return: supported distro as string
"""
if os.path.exists(iso_link):
if any("sources" in s.lower() for s in iso_file_list) and any("boot.wim" in s.lower() for s in iso_file_list):
return "Windows"
elif any("config.isoclient" in s.lower() for s in iso_file_list):
return "opensuse"
elif any("dban" in s.lower() for s in iso_file_list):
return "slitaz"
elif any("memtest.img" in s.lower() for s in iso_file_list):
return "memtest"
elif any("mt86.png" in s.lower() for s in iso_file_list) and any("isolinux" in s.lower() for s in iso_file_list):
return 'raw_iso'
elif any("menu.lst" in s.lower() for s in iso_file_list):
return "grub4dos"
elif any("bootwiz.cfg" in s.lower() for s in iso_file_list) and any("bootmenu_logo.png" in s.lower() for s in iso_file_list):
return "grub4dos_iso"
else:
log(iso_file_list)
keys_to_distro = [
(['f4ubcd'], 'f4ubcd'),
(['alpine-release'], 'alpine'),
(['sources', 'boot.wim'], 'Windows'),
(['config.isoclient'], 'opensuse'),
(['dban'], 'slitaz'),
(['memtest.img'], 'memtest'),
(['mt86.png', 'isolinux'], 'raw_iso'),
(['menu.lst'], 'grub4dos'),
(['bootwiz.cfg', 'bootmenu_logo.png'], 'grub4dos_iso') ]
filenames = [f.lower() for f in iso_file_list]
for keys, distro in keys_to_distro:
if all(k in filenames for k in keys):
return distro
#log("Examined %d %s in the iso but could not determine the distro."
# % (len(filenames), len(filenames)==1 and 'filename' or 'filenames'))
return None
def perform_strict_detections(iso_cfg_ext_dir, iso_file_list):
def run_contains(filepath, keyword, cfg_dir=iso_cfg_ext_dir):
fullpath = os.path.join(cfg_dir, filepath.replace('/', os.sep))
if not os.path.exists(fullpath):
return False
try:
with open(fullpath, 'rb') as f:
data = f.read().lower()
return keyword in data
except (IOError, OSError):
log("Failed to open %s" % fullpath)
return False
def contains(relataive_filepath, keyword):
return partial(run_contains, relataive_filepath,
bytes(keyword.lower(),'us-ascii'))
# contains(P, K) predicates that file P contains the specified
# string K. The predicate get never asserted if P has not been
# extracted into the staging area (iso_cfg_ext_dir).
test_vector = [
('wifislax', contains('boot/syslinux/menu/vesamenu.cfg',
'menu label Wifislax64 Live')),
('salix-live', contains('boot/menus/mainmenu.cfg',
'MENU LABEL SALIX LIVE')),
('grml', contains('boot/isolinux/vesamenu.cfg',
'menu title Grml - Live Linux'))
]
for distro, predicate in test_vector:
predicates = [predicate] if callable(predicate) else predicate
if all(p() for p in predicates):
return distro
return None
if __name__ == '__main__':
iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")

@ -13,75 +13,17 @@ import platform
import shutil
import string
import zipfile
import tempfile
import re
import ctypes
from . import config
from .osdriver import get_physical_disk_number, wmi_get_drive_info, \
log, resource_path, multibootusb_host_dir
def scripts_dir_path():
return os.path.dirname(os.path.realpath(__file__))
def log(message, info=True, error=False, debug=False, _print=True):
"""
Dirty function to log messages to file and also print on screen.
:param message:
:param info:
:param error:
:param debug:
:return:
"""
# LOG_FILE_PATH = os.path.join(multibootusb_host_dir(), 'multibootusb.log')
LOG_FILE_PATH = mbusb_log_file()
if os.path.exists(LOG_FILE_PATH):
log_file_size = os.path.getsize(LOG_FILE_PATH) / (1024.0 * 1024.0)
if log_file_size > 1:
print('Removing log file as it crosses beyond 1mb')
os.remove(LOG_FILE_PATH)
logging.basicConfig(filename=LOG_FILE_PATH,
filemode='a',
format='%(asctime)s.%(msecs)03d %(name)s %(levelname)s %(message)s',
datefmt='%H:%M:%S',
level=logging.DEBUG)
if _print is True:
print(message)
# remove ANSI color codes from logs
# message_clean = re.compile(r'\x1b[^m]*m').sub('', message)
if info is True:
logging.info(message)
elif error is not False:
logging.error(message)
elif debug is not False:
logging.debug(message)
def resource_path(relativePath):
"""
Function to detect the correct path of file when working with sourcecode/install or binary.
:param relativePath: Path to file/data.
:return: Modified path to file/data.
"""
try:
basePath = sys._MEIPASS # Try if we are running as standalone executable
# log('Running stand alone executable.')
except:
basePath = '/usr/share/multibootusb' # Check if we run in installed environment
#if os.path.exists('/usr/share/multibootusb'):
#log('Running from installed machine.')
if not os.path.exists(basePath):
#basePath = os.path.abspath(".")
basePath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
for dir_ in [basePath, os.path.abspath('.'), os.path.abspath('..')]:
fullpath = os.path.join(dir_, relativePath)
if os.path.exists(fullpath):
return fullpath
log("Could not find resource '%s'." % relativePath)
def print_version():
"""
Simple log the version number of the multibootusb application
@ -128,37 +70,6 @@ def sys_64bits():
return sys.maxsize > 2**32
def mbusb_log_file():
"""
Function to genrate path to log file.
Under linux path is created as /tmp/multibootusb.log
Under Windows the file is created under installation directory
"""
if platform.system() == "Linux":
# home_dir = os.path.expanduser('~')
# log_file = os.path.join(home_dir, "multibootusb.log")
log_file = os.path.join(tempfile.gettempdir(), "multibootusb.log")
elif platform.system() == "Windows":
# log_file = os.path.join(tempfile.gettempdir(), "multibootusb", "multibootusb.log")
log_file = os.path.join("multibootusb.log")
return log_file
def multibootusb_host_dir():
"""
Cross platform way to detect multibootusb directory on host system.
:return: Path to multibootusb directory of host system.
"""
if platform.system() == "Linux":
home_dir = os.path.expanduser('~')
mbusb_dir = os.path.join(home_dir, ".multibootusb")
elif platform.system() == "Windows":
mbusb_dir = os.path.join(tempfile.gettempdir(), "multibootusb")
return mbusb_dir
def iso_cfg_ext_dir():
"""
Function to return the path to ISO configuration file extraction directory.
@ -174,12 +85,12 @@ def clean_iso_cfg_ext_dir(iso_cfg_ext_dir):
:return:
"""
if os.path.exists(iso_cfg_ext_dir):
filelist = [f for f in os.listdir(iso_cfg_ext_dir)]
for f in filelist:
if os.path.isdir(os.path.join(iso_cfg_ext_dir, f)):
shutil.rmtree(os.path.join(iso_cfg_ext_dir, f))
for f in os.listdir(iso_cfg_ext_dir):
fullpath = os.path.join(iso_cfg_ext_dir, f)
if os.path.isdir(fullpath):
shutil.rmtree(fullpath)
else:
os.remove(os.path.join(iso_cfg_ext_dir, f))
os.remove(fullpath)
else:
log('iso_cfg_ext_dir directory does not exist.')
@ -190,6 +101,7 @@ def copy_mbusb_dir_usb(usb_disk):
:param usb_mount_path: Path to USB mount.
:return:
"""
<<<<<<< HEAD
# from .iso import iso_size
from .usb import details, PartitionNotMounted
@ -199,6 +111,10 @@ def copy_mbusb_dir_usb(usb_disk):
log(str(e))
return False
=======
usb_details = config.usb_details
>>>>>>> upstream/master
usb_mount_path = usb_details['mount_point']
result = ''
if not os.path.exists(os.path.join(usb_mount_path, "multibootusb")):
@ -304,6 +220,7 @@ def strings(filename, _min=4):
def size_not_enough(iso_link, usb_disk):
from .iso import iso_size
<<<<<<< HEAD
from .usb import details, PartitionNotMounted
isoSize = iso_size(iso_link)
try:
@ -311,6 +228,10 @@ def size_not_enough(iso_link, usb_disk):
except PartitionNotMounted as e:
log(str(e))
return False
=======
isoSize = iso_size(iso_link)
usb_details = config.usb_details
>>>>>>> upstream/master
usb_size = usb_details['size_free']
return bool(isoSize > usb_size)
@ -497,6 +418,7 @@ class MemoryCheck():
"""
totalMemory = os.popen("free -m").readlines()[1].split()[1]
return int(totalMemory)
<<<<<<< HEAD
def wmi_get_drive_info(usb_disk):
assert platform.system() == 'Windows'
@ -519,6 +441,9 @@ def get_physical_disk_number(usb_disk):
partition, logical_disk = wmi_get_drive_info(usb_disk)
log("Physical Device Number is %d" % partition.DiskIndex)
return partition.DiskIndex
=======
>>>>>>> upstream/master
if __name__ == '__main__':
log(quote("""Test-string"""))

@ -197,10 +197,13 @@ def write_to_file(file_path, _strings):
def locate_kernel_file(subpath, isolinux_dir):
subpath_original = subpath
if subpath[0] != '/':
gen.log("Accepting a relative kernel/initrd path '%s' as is."
% subpath)
return subpath
# Looks like relative paths don't work in grub.
#if subpath[0] != '/':
# gen.log("Accepting a relative kernel/initrd path '%s' as is."
# % subpath)
# return subpath
if subpath[:1] != '/':
subpath = '/' + subpath
if os.path.exists(os.path.join(config.usb_mount, subpath[1:])):
gen.log("Accepting kernel/initrd path '%s' as it exists." % subpath)
return subpath
@ -212,7 +215,7 @@ def locate_kernel_file(subpath, isolinux_dir):
subpath = subpath[len(drive_relative_prefix):]
gen.log("Trying to locate kernel/initrd file '%s'" % subpath)
for d in [
os.path.join('multibootusb', _iso_basename, isolinux_dir),
os.path.join('multibootusb', _iso_basename, isolinux_dir or ''),
# Down below are dire attemps to find.
os.path.join('multibootusb', _iso_basename),
os.path.join('multibootusb', _iso_basename, 'arch'),
@ -292,7 +295,7 @@ def iso2grub2(install_dir, loopback_cfg_path):
gen.log('loopback.cfg file is set to ' + loopback_cfg_path)
iso_bin_dir = iso.isolinux_bin_dir(config.image_path)
seen_menu_lines = []
seen_menu_entries = []
# Loop though the distro installed directory for finding config files
for dirpath, dirnames, filenames in os.walk(install_dir):
for f in filenames:
@ -319,8 +322,8 @@ def iso2grub2(install_dir, loopback_cfg_path):
# Append the block after the last matching position
matching_blocks.append(data[matching_blocks_re[-1].span()[1]:])
else:
m = re.match('^(label)(.*?)',
data, re.I|re.DOTALL|re.MULTILINE)
m = re.search('^(label)(.*?)',
data, re.I|re.DOTALL|re.MULTILINE)
matching_blocks = m and [data[m.start():]] or []
if not matching_blocks:
@ -342,7 +345,7 @@ def iso2grub2(install_dir, loopback_cfg_path):
menu_labels = [v for v in matches if v[0].lower()=='menu label']
if 0 == len(labels) + len(menu_labels):
gen.log('Warning: found a block without menu-entry.')
menu_line = 'menuentry "Anonymous"'
menu_entry = 'Anonymous'
menu_label = 'Unlabeled'
else:
for vec, name in [ (labels, 'label'),
@ -355,13 +358,14 @@ def iso2grub2(install_dir, loopback_cfg_path):
value = menu_labels[0][1].replace('^', '')
else:
value = labels[0][1]
menu_line = 'menuentry ' + gen.quote(value)
menu_label = value
menu_entry = menu_label = value
# Extract lines containing 'kernel','linux','initrd'
# or 'append' to convert them into grub2 compatible ones.
linux_line = initrd_line = None
appends = []
sought_archs = ['x86_64', 'i686', 'i386']
arch = []
for keyword, value in re.findall(
r'^\s*(kernel|linux|initrd|append)[= ](.*)$',
matching_block, re.I|re.MULTILINE):
@ -372,6 +376,8 @@ def iso2grub2(install_dir, loopback_cfg_path):
"'kernel/linux' lines in block '%s'."
% menu_label)
continue
arch = [(value.find(a), a) for a in sought_archs
if 0 <= value.find(a)]
linux_line = 'linux ' + \
tweak_bootfile_path(value, iso_bin_dir)
elif kw == 'initrd':
@ -393,13 +399,15 @@ def iso2grub2(install_dir, loopback_cfg_path):
"specifications in block '%s'."
% menu_label)
initrd_line = new_initrd_line
if menu_line in seen_menu_lines:
if arch: # utilize left most arch.
menu_entry += (" (%s)" % sorted(arch)[-1][1])
menu_line = 'menuentry ' + gen.quote(menu_entry)
if menu_entry in seen_menu_entries:
out_lines.append( "# '%s' is superceded by the previous "
"definition." % menu_label)
else:
if linux_line or initrd_line:
seen_menu_lines.append(menu_line)
seen_menu_entries.append(menu_entry)
out_lines.append(menu_line + ' {')
for starter, value in [
(linux_line, ' '.join(appends)),

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>498</width>
<height>369</height>
<width>626</width>
<height>393</height>
</rect>
</property>
<property name="sizePolicy">
@ -25,7 +25,7 @@
<item row="1" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;An advanced bootable usb creator with option to install/uninstall multiple distros.&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;This software is written in Python and PyQt. &lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Copyright 2010-2017 Sundar&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Author(s)&lt;/span&gt;: Sundar, Ian Bruce, LiQiong Lee and Alin Trăistaru (alindt)&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Licence&lt;/span&gt;: GPL version 2 or later&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Home page&lt;/span&gt;: &lt;a href=&quot;http://multibootusb.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://multibootusb.org&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Help/Email&lt;/span&gt;: feedback.multibootusb@gmail.com&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Source Code&lt;/span&gt;: &lt;a href=&quot;https://github.com/mbusb/multibootusb&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/mbusb/multibootusb&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;An advanced bootable usb creator with option to install/uninstall multiple distros.&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;This software is written in Python and PyQt. &lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Copyright 2010-2018 Sundar&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Author(s)&lt;/span&gt;: Sundar, Ian Bruce, LiQiong Lee, Alin Trăistaru (alindt) and Shinji Suzuki&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Licence&lt;/span&gt;: GPL version 2 or later&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Home page&lt;/span&gt;: &lt;a href=&quot;http://multibootusb.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://multibootusb.org&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Help/Email&lt;/span&gt;: feedback.multibootusb@gmail.com&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Source Code&lt;/span&gt;: &lt;a href=&quot;https://github.com/mbusb/multibootusb&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/mbusb/multibootusb&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>629</width>
<height>605</height>
<width>865</width>
<height>609</height>
</rect>
</property>
<property name="sizePolicy">
@ -23,16 +23,7 @@
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>5</number>
</property>
<item>
@ -308,7 +299,7 @@
<bool>false</bool>
</property>
<property name="currentIndex">
<number>1</number>
<number>4</number>
</property>
<widget class="QWidget" name="tab_multibootusb">
<property name="enabled">
@ -318,30 +309,12 @@
<string>MultiBootUSB</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>5</number>
</property>
<item row="0" column="3">
@ -457,33 +430,35 @@
<string>Write Image to disk</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_9">
<property name="leftMargin">
<property name="margin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item row="3" column="2">
<item row="2" column="1">
<widget class="QPushButton" name="button_write_image_to_disk">
<property name="text">
<string>Write image to USB</string>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="2">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -496,32 +471,16 @@
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="button_write_image_to_disk">
<property name="text">
<string>Write image to USB</string>
</property>
</widget>
</item>
<item row="3" column="3" colspan="2">
<item row="5" column="3" colspan="2">
<widget class="QWidget" name="widget_7" native="true">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>5</number>
</property>
</layout>
</widget>
</item>
<item row="3" column="0">
<item row="5" column="0">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -534,7 +493,7 @@
</property>
</spacer>
</item>
<item row="4" column="1">
<item row="6" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -567,20 +526,7 @@
</property>
</spacer>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="3">
<item row="3" column="1">
<widget class="QLabel" name="label_waning">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600; color:#ff0000;&quot;&gt;WARNING&lt;/span&gt; : Any bootable USB made using&lt;span style=&quot; font-weight:600;&quot;&gt; ISO Imager will destroy all data &lt;/span&gt;on the selected USB disk. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;Use it at your own risk. Developers are not responsile for loss of any data.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -945,6 +891,164 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_fsck">
<property name="enabled">
<bool>true</bool>
</property>
<attribute name="title">
<string>Check Filesystem</string>
</attribute>
<layout class="QFormLayout" name="formLayout_5">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Repair Filesystem</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Use this function to check filesystem consistency&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;and repair. Please take a backup before attempting&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;to use tis function. Users will be presented with&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;two message dialogs as repair is currently &lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;performed in two steps.&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="run_fsck_repair">
<property name="text">
<string>Repair Filesystem</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Check Filesystem</string>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="0">
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Checking filesystem integrity from time to&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;time is recommended to make sure the integrity&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;is always maintained. Some distros don't &lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;cleanly unmount a usb stick when booting off of&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;it, that will be also detected by this check.&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer_8">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="run_fsck_check">
<property name="text">
<string>Check Filesystem Integrity</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
@ -964,7 +1068,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>629</width>
<width>865</width>
<height>21</height>
</rect>
</property>

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'gui/multibootusb.ui'
# Form implementation generated from reading ui file 'scripts/gui/multibootusb.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(629, 605)
MainWindow.resize(865, 609)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -216,33 +216,38 @@ class Ui_MainWindow(object):
self.gridLayout_9 = QtWidgets.QGridLayout()
self.gridLayout_9.setContentsMargins(5, 5, 5, 5)
self.gridLayout_9.setObjectName("gridLayout_9")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout_9.addItem(spacerItem, 3, 2, 1, 1)
self.button_write_image_to_disk = QtWidgets.QPushButton(self.tab_imager)
self.button_write_image_to_disk.setObjectName("button_write_image_to_disk")
self.gridLayout_9.addWidget(self.button_write_image_to_disk, 3, 1, 1, 1)
self.gridLayout_9.addWidget(self.button_write_image_to_disk, 2, 1, 1, 1)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_9.addItem(spacerItem, 4, 1, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout_9.addItem(spacerItem1, 5, 2, 1, 1)
self.widget_7 = QtWidgets.QWidget(self.tab_imager)
self.widget_7.setObjectName("widget_7")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.widget_7)
self.verticalLayout_6.setContentsMargins(5, 5, 5, 5)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.gridLayout_9.addWidget(self.widget_7, 3, 3, 1, 2)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout_9.addItem(spacerItem1, 3, 0, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_9.addItem(spacerItem2, 4, 1, 1, 1)
self.gridLayout_9.addWidget(self.widget_7, 5, 3, 1, 2)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout_9.addItem(spacerItem2, 5, 0, 1, 1)
spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_9.addItem(spacerItem3, 6, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(self.tab_imager)
self.label_6.setObjectName("label_6")
self.gridLayout_9.addWidget(self.label_6, 1, 1, 1, 1)
spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_9.addItem(spacerItem3, 0, 1, 1, 1)
spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_9.addItem(spacerItem4, 2, 1, 1, 1)
self.gridLayout_9.addItem(spacerItem4, 0, 1, 1, 1)
self.label_waning = QtWidgets.QLabel(self.tab_imager)
self.label_waning.setWordWrap(False)
self.label_waning.setObjectName("label_waning")
self.gridLayout_9.addWidget(self.label_waning, 3, 1, 1, 1)
self.horizontalLayout_7.addLayout(self.gridLayout_9)
self.tabWidget.addTab(self.tab_imager, "")
self.tab_syslinux = QtWidgets.QWidget()
self.tab_syslinux.setObjectName("tab_syslinux")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tab_syslinux)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.gridLayout_2 = QtWidgets.QGridLayout()
self.gridLayout_2.setObjectName("gridLayout_2")
@ -291,6 +296,7 @@ class Ui_MainWindow(object):
self.tab_testboot = QtWidgets.QWidget()
self.tab_testboot.setObjectName("tab_testboot")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab_testboot)
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.gridLayout_6 = QtWidgets.QGridLayout()
self.gridLayout_6.setContentsMargins(-1, 10, -1, -1)
@ -361,6 +367,58 @@ class Ui_MainWindow(object):
spacerItem13 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem13)
self.tabWidget.addTab(self.tab_testboot, "")
self.tab_fsck = QtWidgets.QWidget()
self.tab_fsck.setEnabled(True)
self.tab_fsck.setObjectName("tab_fsck")
self.formLayout_5 = QtWidgets.QFormLayout(self.tab_fsck)
self.formLayout_5.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
self.formLayout_5.setContentsMargins(0, 0, 0, 0)
self.formLayout_5.setObjectName("formLayout_5")
self.groupBox_6 = QtWidgets.QGroupBox(self.tab_fsck)
self.groupBox_6.setObjectName("groupBox_6")
self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox_6)
self.gridLayout_5.setObjectName("gridLayout_5")
spacerItem14 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_5.addItem(spacerItem14, 0, 0, 1, 1)
self.label_8 = QtWidgets.QLabel(self.groupBox_6)
self.label_8.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_8.sizePolicy().hasHeightForWidth())
self.label_8.setSizePolicy(sizePolicy)
self.label_8.setMinimumSize(QtCore.QSize(0, 0))
self.label_8.setFrameShape(QtWidgets.QFrame.Box)
self.label_8.setObjectName("label_8")
self.gridLayout_5.addWidget(self.label_8, 1, 0, 1, 1)
spacerItem15 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_5.addItem(spacerItem15, 2, 0, 1, 1)
self.run_fsck_repair = QtWidgets.QPushButton(self.groupBox_6)
self.run_fsck_repair.setObjectName("run_fsck_repair")
self.gridLayout_5.addWidget(self.run_fsck_repair, 3, 0, 1, 1)
self.formLayout_5.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.groupBox_6)
self.groupBox = QtWidgets.QGroupBox(self.tab_fsck)
self.groupBox.setObjectName("groupBox")
self.gridLayout_10 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_10.setObjectName("gridLayout_10")
spacerItem16 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_10.addItem(spacerItem16, 0, 0, 1, 1)
self.label_7 = QtWidgets.QLabel(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth())
self.label_7.setSizePolicy(sizePolicy)
self.label_7.setFrameShape(QtWidgets.QFrame.Box)
self.label_7.setObjectName("label_7")
self.gridLayout_10.addWidget(self.label_7, 1, 0, 1, 1)
spacerItem17 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_10.addItem(spacerItem17, 2, 0, 1, 1)
self.run_fsck_check = QtWidgets.QPushButton(self.groupBox)
self.run_fsck_check.setObjectName("run_fsck_check")
self.gridLayout_10.addWidget(self.run_fsck_check, 3, 0, 1, 1)
self.formLayout_5.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.groupBox)
self.tabWidget.addTab(self.tab_fsck, "")
self.verticalLayout_7.addWidget(self.tabWidget)
self.progressbar = QtWidgets.QProgressBar(self.centralwidget)
self.progressbar.setProperty("value", 0)
@ -369,7 +427,7 @@ class Ui_MainWindow(object):
self.verticalLayout_7.addWidget(self.progressbar)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 629, 19))
self.menubar.setGeometry(QtCore.QRect(0, 0, 865, 21))
self.menubar.setNativeMenuBar(True)
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
@ -390,7 +448,7 @@ class Ui_MainWindow(object):
self.menubar.addAction(self.menu_Help.menuAction())
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(1)
self.tabWidget.setCurrentIndex(4)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
@ -421,6 +479,7 @@ class Ui_MainWindow(object):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_multibootusb), _translate("MainWindow", "MultiBootUSB"))
self.button_write_image_to_disk.setText(_translate("MainWindow", "Write image to USB"))
self.label_6.setText(_translate("MainWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600; color:#ff0000;\">WARNING!</span></p><p align=\"center\"><span style=\" color:#000000;\">This operation destroys </span><span style=\" font-weight:600; color:#000000;\">ALL</span><span style=\" color:#000000;\"> data on the selected disk.</span></p><p align=\"center\"><span style=\" color:#000000;\">Please select the destination disk carefully.</span></p></body></html>"))
self.label_waning.setText(_translate("MainWindow", "<html><head/><body><p align=\"justify\"><span style=\" font-weight:600; color:#ff0000;\">WARNING</span> : Any bootable USB made using<span style=\" font-weight:600;\"> ISO Imager will destroy all data </span>on the selected USB disk. </p><p align=\"justify\">Use it at your own risk. Developers are not responsile for loss of any data.</p></body></html>"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_imager), _translate("MainWindow", "Write Image to disk"))
self.groupBox_2.setTitle(_translate("MainWindow", "Install Syslinux"))
self.check_install_sys_only.setText(_translate("MainWindow", "Install only syslinu&x (existing configurations will not be altered)."))
@ -451,6 +510,33 @@ class Ui_MainWindow(object):
self.combo_iso_boot_ram.setItemText(5, _translate("MainWindow", "2048"))
self.label.setText(_translate("MainWindow", "MB"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_testboot), _translate("MainWindow", "Boot ISO/USB"))
self.groupBox_6.setTitle(_translate("MainWindow", "Repair Filesystem"))
self.label_8.setText(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Sans\'; font-size:10pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Use this function to check filesystem consistency</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">and repair. Please take a backup before attempting</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">to use tis function. Users will be presented with</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">two message dialogs as repair is currently </p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">performed in two steps.</p>\n"
"<p align=\"justify\" style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))
self.run_fsck_repair.setText(_translate("MainWindow", "Repair Filesystem"))
self.groupBox.setTitle(_translate("MainWindow", "Check Filesystem"))
self.label_7.setText(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Sans\'; font-size:10pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Checking filesystem integrity from time to</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">time is recommended to make sure the integrity</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">is always maintained. Some distros don\'t </p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">cleanly unmount a usb stick when booting off of</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">it, that will be also detected by this check.</p>\n"
"<p align=\"justify\" style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))
self.run_fsck_check.setText(_translate("MainWindow", "Check Filesystem Integrity"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_fsck), _translate("MainWindow", "Check Filesystem"))
self.menuFile.setTitle(_translate("MainWindow", "&File"))
self.menu_Help.setTitle(_translate("MainWindow", "&Help"))
self.action_Quit.setText(_translate("MainWindow", "&Quit"))

@ -7,32 +7,41 @@
# under the terms of GNU General Public License, v.2 or above
# WARNING : Any boot-able USB made using this module will destroy data stored on USB disk.
import os
import subprocess
import collections
import io
import os
import platform
import signal
import time
import subprocess
import traceback
from PyQt5 import QtWidgets
from .gui.ui_multibootusb import Ui_MainWindow
from .gen import *
from . import iso
from . import config
from . import iso
from . import osdriver
from . import progressbar
from . import osdriver
from . import usb
if platform.system() == "Windows":
import win32com.client
def dd_linux():
import time
_input = "if=" + config.image_path
in_file_size = float(os.path.getsize(config.image_path))
_output = "of=" + config.usb_disk
os.system("umount " + config.usb_disk + "1")
command = ['dd', _input, _output, "bs=1M", "oflag=sync"]
log("Executing ==> " + " ".join(command))
dd_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
def dd_iso_image(dd_progress_thread):
try:
dd_progress_thread.set_error(None)
_dd_iso_image(dd_progress_thread)
except:
# config.imager_return = False
o = io.StringIO()
traceback.print_exc(None, o)
log(o.getvalue())
dd_progress_thread.set_error(o.getvalue())
def _dd_iso_image(dd_progress_thread):
pbar = progressbar.ProgressBar(
maxval=100,
widgets=[
@ -43,52 +52,34 @@ def dd_linux():
]
).start()
while dd_process.poll() is None:
time.sleep(0.1) # If this time delay is not given, the Popen does not execute the actual command
dd_process.send_signal(signal.SIGUSR1)
dd_process.stderr.flush()
while True:
time.sleep(0.1)
out_error = dd_process.stderr.readline().decode()
if out_error:
if 'bytes' in out_error:
copied = int(out_error.split(' ', 1)[0])
config.imager_percentage = round((float(copied) / float(in_file_size) * 100))
pbar.update(config.imager_percentage)
break
if dd_process.poll() is not None:
log("\nExecuting ==> sync")
os.sync()
log("ISO has been written to USB disk...")
return
def dd_win():
windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe"))
if os.path.exists(resource_path(os.path.join("data", "tools", "dd", "dd.exe"))):
log("dd exist")
_input = "if=" + config.image_path
in_file_size = float(os.path.getsize(config.image_path) / 1024 / 1024)
_output = "of=\\\.\\" + config.usb_disk
command = [windd, _input, _output, "bs=1M", "--progress"]
log("Executing ==> " + " ".join(command))
dd_process = subprocess.Popen(command, universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE,
shell=False)
while dd_process.poll() is None:
for line in iter(dd_process.stderr.readline, ''):
line = line.strip()
if 'error' in line.lower() or 'invalid' in line.lower():
log("Error writing to disk...")
break
if line and line[-1] == 'M':
copied = float(line.strip('M').replace(',', ''))
config.imager_percentage = round((copied / float(in_file_size) * 100))
log("ISO has been written to USB disk...")
return
def gui_update(percentage):
config.imager_percentage = percentage
pbar.update(percentage)
def status_update(text):
config.status_text = text
mounted_partitions = osdriver.find_mounted_partitions_on(config.usb_disk)
unmounted = []
try:
for x in mounted_partitions:
partition_dev, mount_point = x[:2]
c = usb.UnmountedContext(partition_dev, config.update_usb_mount)
c.__enter__()
unmounted.append((c, partition_dev))
error = osdriver.dd_iso_image(
config.image_path, config.usb_disk, gui_update, status_update)
if error:
dd_progress_thread.set_error(error)
log('Error writing iso image...')
# config.imager_return = False
else:
log('ISO has been written to USB disk...')
# config.imager_return = True
finally:
for c, partition_dev in unmounted:
c.__exit__(None, None, None)
class Imager(QtWidgets.QMainWindow, Ui_MainWindow):
@ -152,42 +143,34 @@ class Imager(QtWidgets.QMainWindow, Ui_MainWindow):
return disk
@staticmethod
def imager_usb_detail(usb_disk, partition=1):
def imager_usb_detail(physical_disk):
"""
Function to detect details of USB disk using lsblk
:param usb_disk: path to usb disk
:param partition: by default partition is set (but yet to code for it)
:param physical_disk: /dev/sd? (linux) or integer disk number (win)
:return: details of size, type and model as tuples
"""
_ntuple_diskusage = collections.namedtuple('usage', 'total_size usb_type model')
_ntuple_diskusage = collections.namedtuple(
'usage', 'total_size usb_type model')
if platform.system() == "Linux":
output = subprocess.check_output("lsblk -ib " + usb_disk, shell=True)
output = subprocess.check_output("lsblk -ib " + physical_disk,
shell=True)
for line in output.splitlines():
line = line.split()
if partition != 1:
if line[2].strip() == b'1' and line[5].strip() == b'disk':
total_size = line[3]
if not total_size:
total_size = "Unknown"
usb_type = "Removable"
model = subprocess.check_output("lsblk -in -f -o MODEL " + usb_disk, shell=True).decode().strip()
if not model:
model = "Unknown"
if line[2].strip() == b'1' and line[5].strip() == b'disk':
total_size = line[3]
if not total_size:
total_size = "Unknown"
usb_type = "Removable"
model = subprocess.check_output(
"lsblk -in -f -o MODEL " + physical_disk,
shell=True).decode().strip()
if not model:
model = "Unknown"
else:
try:
selected_usb_part = str(usb_disk)
oFS = win32com.client.Dispatch("Scripting.FileSystemObject")
d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part)))
# selected_usb_device = d.DriveLetter
label = (d.VolumeName).strip()
if not label.strip():
label = "No label."
total_size = d.TotalSize
usb_type = "Removable"
model = label
except:
log("Error detecting USB details.")
dinfo = osdriver.wmi_get_physicaldrive_info_ex(physical_disk)
return _ntuple_diskusage(*[dinfo[a] for a in [
'size_total', 'mediatype', 'model']])
return _ntuple_diskusage(total_size, usb_type, model)

@ -6,11 +6,12 @@
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
# under the terms of GNU General Public License, v.2 or above
import lzma
import os
import shutil
import platform
import threading
import shutil
import subprocess
import threading
import time
from .usb import *
from .gen import *
@ -76,10 +77,10 @@ def install_distro():
config.status_text = "Copying ISO..."
iso.iso_extract_file(config.image_path, install_dir, "kernel")
copy_iso(config.image_path, install_dir)
elif config.distro == "salix-live":
elif config.distro in ["salix-live", 'wifislax']:
# iso.iso_extract_file(config.image_path, install_dir, "boot")
iso.iso_extract_file(config.image_path, install_dir,
['*syslinux', '*menus', '*vmlinuz', '*initrd*',
['*syslinux', '*isolinux', '*system_tools', '*menus', '*vmlinuz', '*initrd*',
'EFI'])
iso.iso_extract_file(config.image_path, usb_mount,
['*modules', '*packages', '*optional',
@ -125,9 +126,6 @@ def install_distro():
else:
iso.iso_extract_full(config.image_path, install_dir)
if platform.system() == 'Linux':
log('ISO extracted successfully. Sync is in progress...')
os.sync()
if config.persistence != 0:
log('Creating persistence...')
@ -167,6 +165,10 @@ def install_progress():
log(str(e))
return
<<<<<<< HEAD
=======
usb_details = config.usb_details
>>>>>>> upstream/master
config.usb_mount = usb_details['mount_point']
usb_size_used = usb_details['size_used']
thrd = threading.Thread(target=install_distro, name="install_progress")
@ -186,48 +188,74 @@ def install_progress():
time.sleep(0.1)
def replace_syslinux_modules(syslinux_version, under_this_dir):
# Replace modules files extracted from iso with corresponding
# version provided by multibootusb.
modules_src_dir = os.path.join(
multibootusb_host_dir(), "syslinux", "modules", syslinux_version)
for dirpath, dirnames, filenames in os.walk(under_this_dir):
for fname in filenames:
if not fname.lower().endswith('.c32'):
continue
dst_path = os.path.join(under_this_dir, dirpath, fname)
src_path = os.path.join(modules_src_dir, fname)
if not os.path.exists(src_path):
log("Suitable replacement of '%s' is not bundled. "
"Trying to unlzma." % fname)
try:
with lzma.open(dst_path) as f:
expanded = f.read()
except lzma.LZMAError:
continue
except (OSError, IOError) as e:
log("%s while accessing %s." % (e, dst_path))
continue
with open(dst_path, 'wb') as f:
f.write(expanded)
log("Successfully decompressed %s." % fname)
continue
try:
os.remove(dst_path)
shutil.copy(src_path, dst_path)
log("Replaced %s module" % fname)
except (OSError, IOError) as err:
log(err)
log("Could not update " + fname)
def install_patch():
"""
Function to certain distros which uses makeboot.sh script for making bootable usb disk.
This is required to make sure that same version (32/64 bit) of modules present is the isolinux directory
:return:
"""
if config.distro == 'debian':
if platform.system() == 'Linux': # Need to syn under Linux. Otherwise, USB disk becomes random read only.
os.sync()
iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")
isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(config.image_path))
# iso_linux_bin_dir = isolinux_bin_dir(config.image_path)
config.syslinux_version = isolinux_version(isolinux_path)
iso_file_list = iso.iso_file_list(config.image_path)
isobin_path = isolinux_bin_path(config.image_path)
if not isobin_path:
return
iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(),
"iso_cfg_ext_dir")
isolinux_path = os.path.join(iso_cfg_ext_dir, isobin_path)
# iso_linux_bin_dir = isolinux_bin_dir(config.image_path)
distro_install_dir = os.path.join(
config.usb_mount, "multibootusb", iso_basename(config.image_path))
config.syslinux_version = isolinux_version(isolinux_path)
if config.distro in ['slitaz', 'ubunu']:
replace_syslinux_modules(config.syslinux_version, distro_install_dir)
elif config.distro == 'gentoo':
replace_syslinux_modules(config.syslinux_version, distro_install_dir)
elif config.distro == 'debian':
iso_file_list = iso.iso_file_list(config.image_path)
if not any(s.strip().lower().endswith("makeboot.sh")
for s in iso_file_list):
log('Patch not required...')
return
# Replace modules files extracted from iso with corresponding
# version provided by multibootusb.
distro_install_dir = os.path.join(config.usb_mount, "multibootusb",
iso_basename(config.image_path))
isolinux_bin_dir_ = os.path.join(
distro_install_dir, isolinux_bin_dir(config.image_path))
modules_src_dir = os.path.join(
multibootusb_host_dir(), "syslinux", "modules",
config.syslinux_version)
for module in os.listdir(isolinux_bin_dir_):
if not module.endswith(".c32"):
continue
fpath = os.path.join(isolinux_bin_dir_, module)
try:
os.remove(fpath)
src_module_path = os.path.join(modules_src_dir, module)
log("Copying " + module)
log((src_module_path, fpath))
shutil.copy(src_module_path, fpath)
except Exception as err:
log(err)
log("Could not copy " + module)
replace_syslinux_modules(config.syslinux_version, isolinux_bin_dir_)
class DirectoryRelocator:
def __init__(self, src_dir, dst_dir):

@ -131,7 +131,7 @@ def cli_uninstall_distro():
for index, _distro_dir in enumerate(distro_list):
if index == user_input:
config.uninstall_distro_dir_name = _distro_dir
unin_distro()
do_uninstall_distro(_distro_dir, _distro_dir)
else:
log('No distro installed on ' + config.usb_disk)

@ -5,7 +5,8 @@
# Authors: Sundar
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
# under the terms of GNU General Public License, v.2 or above
from functools import partial
import io
import os
import platform
import sys
@ -13,23 +14,29 @@ import signal
from PyQt5 import QtCore, QtGui, QtWidgets
import subprocess
import time
import traceback
import webbrowser
if platform.system() == 'Linux':
import dbus
from scripts.gui.ui_multibootusb import Ui_MainWindow
from scripts.gui.ui_about import Ui_About
from . import usb
from .gen import *
from .install import *
from .uninstall_distro import *
from . import uninstall_distro
from .syslinux import *
from .distro import *
from .qemu import *
from .iso import *
# from .imager import *
from .imager import Imager, dd_linux, dd_win
from .imager import Imager, dd_iso_image
from . import persistence
from . import config
from . import admin
from . import qemu
from . import osdriver
from .update_cfg_file import update_distro_cfg_files
import scripts.gui.resources
@ -51,9 +58,6 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.ui.label_persistence.setVisible(False)
self.ui.slider_persistence.setVisible(False)
config.usb_disk = None
config.image_path = None
# Main Tab
self.ui.checkbox_all_drives.clicked.connect(self.onAllDrivesClicked)
self.ui.button_detect_drives.clicked.connect(self.onRefreshClick)
@ -80,6 +84,12 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
# self.ui.combo_iso_boot_ram.activated[str].connect(self.qemu_iso_ram)
# self.ui.combo_usb_boot_ram.activated[str].connect(self.qemu_usb_ram)
# self.ui.boot_usb_qemu.clicked.connect(lambda: self.on_Qemu_Boot_usb_Click(str(self.ui.combo_drives.currentText())))
self.ui.run_fsck_repair.clicked.connect(
partial(self.onFsckClick, usb.repair_vfat_filesystem))
self.ui.run_fsck_check.clicked.connect(
partial(self.onFsckClick, usb.check_vfat_filesystem))
# Update progressbar and status (Main ISO install)
self.progress_thread_install = GuiInstallProgress()
self.progress_thread_install.finished.connect(self.install_syslinux)
@ -98,6 +108,11 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.progress_thread_dd.finished.connect(self.dd_finished)
self.progress_thread_dd.status.connect(self.ui.statusbar.showMessage)
if platform.system() == 'Windows' or os.system('which fsck.vfat') != 0:
i = self.ui.tabWidget.indexOf(self.ui.tab_fsck)
if 0<=i:
self.ui.tabWidget.removeTab(i)
prepare_mbusb_host_dir()
self.onRefreshClick()
@ -110,12 +125,16 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.onRefreshClick()
return
reply = QtWidgets.QMessageBox.warning(self, "WARNING!",
"This option enables working with fixed drives\n\
and is potentially VERY DANGEROUS\n\n\
Are you SURE you want to enable it?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No)
if getattr(config, 'protected_drives', []):
reply = QtWidgets.QMessageBox.Yes
else:
reply = QtWidgets.QMessageBox.warning(
self, "WARNING!",
"This option enables working with fixed drives\n"
"and is potentially VERY DANGEROUS\n\n"
"Are you SURE you want to enable it?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.No:
self.ui.checkbox_all_drives.setChecked(False)
@ -140,31 +159,29 @@ Are you SURE you want to enable it?",
:return:
"""
self.ui.installed_distros.clear()
config.usb_disk = str(self.ui.combo_drives.currentText())
config.imager_usb_disk = str(self.ui.combo_drives.currentText())
if config.usb_disk:
log("Selected device " + config.usb_disk)
config.usb_disk = osdriver.listbox_entry_to_device(
self.ui.combo_drives.currentText())
if config.usb_disk == 0 or config.usb_disk:
# Get the GPT status of the disk and store it on a variable
try:
config.usb_details = usb.details(config.usb_disk)
except usb.PartitionNotMounted as e:
log(str(e))
QtWidgets.QMessageBox.error("Error: partition not mounted", str(e))
usb.gpt_device(config.usb_disk)
config.imager_usb_disk \
= self.ui.combo_drives.currentText()
config.usb_details \
= usb.details(config.usb_disk)
except Exception as e:
o = io.StringIO()
traceback.print_exc(None, o)
log(o.getvalue())
QtWidgets.QMessageBox.critical(
self, "The disk/partition is not usable.",
str(e))
self.ui.combo_drives.setCurrentIndex(0)
# Above statement triggers call to this method.
return
config.persistence_max_size = persistence.max_disk_persistence(config.usb_disk)
config.usb_mount = config.usb_details.get('mount_point', "")
self.ui.usb_dev.setText(config.usb_disk)
self.ui.usb_vendor.setText(config.usb_details.get('vendor', ""))
self.ui.usb_model.setText(config.usb_details.get('model', ""))
self.ui.usb_size.setText(str(usb.bytes2human(config.usb_details.get('size_total', ""))))
self.ui.usb_mount.setText(config.usb_details.get('mount_point', ""))
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)
log("Selected device " +
osdriver.usb_disk_desc(config.usb_disk))
self.update_target_info()
self.update_list_box(config.usb_disk)
self.ui_update_persistence()
else:
@ -175,7 +192,6 @@ Are you SURE you want to enable it?",
self.ui.usb_mount.clear()
self.ui.usb_type.clear()
self.ui.usb_fs.clear()
log("No USB disk found...")
def onRefreshClick(self):
@ -184,15 +200,15 @@ Are you SURE you want to enable it?",
:return:
"""
self.ui.combo_drives.clear()
if self.ui.checkbox_all_drives.isChecked():
detected_devices = usb.list_devices(fixed=True)
else:
detected_devices = usb.list_devices()
if detected_devices:
for device in detected_devices:
detected_devices = usb.list_devices(
fixed=self.ui.checkbox_all_drives.isChecked())
if not detected_devices:
return
protected_drives = getattr(config, 'protected_drives', [])
for device in detected_devices:
if all(not device.startswith(d) for d in protected_drives):
self.ui.combo_drives.addItem(str(device))
self.ui.combo_drives.setCurrentIndex(0)
self.ui.combo_drives.setCurrentIndex(0)
def update_list_box(self, usb_disk):
"""
@ -200,7 +216,7 @@ Are you SURE you want to enable it?",
:param usb_mount: Selected USB disk from combobox.
:return:
"""
distro_list = install_distro_list()
distro_list = uninstall_distro.install_distro_list()
if distro_list is not None:
self.ui.installed_distros.clear()
for name in distro_list:
@ -246,7 +262,7 @@ Are you SURE you want to enable it?",
# Detect supported distro
try:
clean_iso_cfg_ext_dir( # Need to be cleaned.
clean_iso_cfg_ext_dir( # Need to be cleaned.
os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir"))
extract_cfg_file(config.image_path)
config.distro = distro(iso_cfg_ext_dir(), config.image_path,
@ -322,21 +338,23 @@ Are you SURE you want to enable it?",
self.ui.label_persistence.setVisible(False)
self.ui.slider_persistence.setVisible(False)
def get_controls(self):
return [
self.ui.combo_drives,
self.ui.checkbox_all_drives,
self.ui.button_detect_drives,
self.ui.button_browse_image,
self.ui.image_path,
self.ui.tabWidget,
self.ui.button_install_distro,
self.ui.button_uninstall_distro,
]
def ui_disable_controls(self):
self.ui.combo_drives.setEnabled(False)
self.ui.checkbox_all_drives.setEnabled(False)
self.ui.button_detect_drives.setEnabled(False)
self.ui.button_browse_image.setEnabled(False)
self.ui.image_path.setEnabled(False)
self.ui.tabWidget.setEnabled(False)
[c.setEnabled(False) for c in self.get_controls()]
def ui_enable_controls(self):
self.ui.combo_drives.setEnabled(True)
self.ui.checkbox_all_drives.setEnabled(True)
self.ui.button_detect_drives.setEnabled(True)
self.ui.button_browse_image.setEnabled(True)
self.ui.image_path.setEnabled(True)
self.ui.tabWidget.setEnabled(True)
[c.setEnabled(True) for c in self.get_controls()]
def update_slider_text(self):
slide_value = self.ui.slider_persistence.value() * 1024 * 1024
@ -344,6 +362,26 @@ Are you SURE you want to enable it?",
config.persistence = slide_value
def install_syslinux(self):
try:
try:
self.install_syslinux_impl()
finally:
config.process_exist = None
self.ui_enable_controls()
except (KeyboardInterrupt, SystemExit):
raise
except:
uninstall_distro.do_uninstall_distro(
config.distro, iso_basename(config.image_path))
o = io.StringIO()
traceback.print_exc(None, o)
QtWidgets.QMessageBox.information(
self, 'install_syslinux() failed',
o.getvalue())
log("install_syslinux() failed.")
log(o.getvalue())
def install_syslinux_impl(self):
"""
Function to install syslinux on distro directory and on selected USB disks.
:return:
@ -353,18 +391,13 @@ Are you SURE you want to enable it?",
syslinux_default(config.usb_disk)
replace_grub_binary()
update_distro_cfg_files(config.image_path, config.usb_disk,
config.distro, config.persistence)
config.distro, config.persistence)
self.update_list_box(config.usb_disk)
if sys.platform.startswith("linux"):
self.ui.statusbar.showMessage("Status: Sync is in progress...")
os.sync()
self.ui.statusbar.showMessage("Status: Idle")
self.ui_disable_persistence()
log(iso_name(config.image_path) + ' has been successfully installed.')
QtWidgets.QMessageBox.information(self, 'Finished...',
iso_name(config.image_path) + ' has been successfully installed.')
config.process_exist = None
self.ui_enable_controls()
def onInstall_syslinuxClick(self):
"""
@ -415,9 +448,6 @@ Are you SURE you want to enable it?",
dest_fp)
QtWidgets.QMessageBox.information(self, 'Install Success...',
'Syslinux installed successfully on ' + config.usb_disk)
elif ret is True and self.ui.check_install_sys_only.isChecked():
QtWidgets.QMessageBox.information(self, 'Install Success...',
'Syslinux installed successfully on ' + config.usb_disk)
elif ret is False:
QtWidgets.QMessageBox.information(self, 'Install error...',
'Sorry. Syslinux failed to install on ' + config.usb_disk)
@ -427,6 +457,38 @@ Are you SURE you want to enable it?",
self.ui_enable_controls()
def onFsckClick(self, fsck_func):
try:
self.onFsckClick_impl(fsck_func)
except (KeyboardInterrupt, SystemExit):
raise
except:
o = io.StringIO()
traceback.print_exc(None, o)
QtWidgets.QMessageBox.information(
self, 'Failed to run fsck',
o.getvalue())
def onFsckClick_impl(self, fsck_func):
if not config.usb_disk:
QtWidgets.QMessageBox.information(
self, 'No partition is selected',
'Please select the partition to check.')
return
if not config.usb_disk[-1:].isdigit():
QtWidgets.QMessageBox.information(
self, 'Selected device is not partition',
'Please select a partition not a disk.')
return
output = []
with usb.UnmountedContext(config.usb_disk, self.update_usb_mount):
fsck_func(config.usb_disk, output)
for resultcode, msgout, cmd in output:
QtWidgets.QMessageBox.information(
self, 'Integrity Check',
cmd + ' said:\n' + str(msgout[0], 'utf-8'))
def onedit_syslinux(self):
"""
Function to edit main syslinux.cfg file.
@ -493,8 +555,8 @@ Are you SURE you want to enable it?",
config.uninstall_distro_dir_name)):
log("Distro install directory not found. "
"Just updating syslinux.cfg and grub.cfg.")
update_sys_cfg_file(config.uninstall_distro_dir_name)
update_grub_cfg_file(config.uninstall_distro_dir_name)
uninstall_distro.update_sys_cfg_file(config.uninstall_distro_dir_name)
uninstall_distro.update_grub_cfg_file(config.uninstall_distro_dir_name)
self.uninstall_sys_file_update()
# self.uninstall.update_sys_cfg_file()
self.ui_enable_controls()
@ -513,134 +575,159 @@ Are you SURE you want to enable it?",
# update_sys_cfg_file(config.uninstall_distro_dir_name)
self.update_list_box(config.usb_mount)
if sys.platform.startswith("linux"):
self.ui.statusbar.showMessage("Status: Sync in progress...")
os.sync()
self.ui.statusbar.showMessage("Status: Idle")
QtWidgets.QMessageBox.information(self, 'Uninstall Complete...',
config.uninstall_distro_dir_name + ' has been successfully removed.')
self.ui_enable_controls()
def onCreateClick(self):
installing = False
self.ui_disable_controls()
try:
installing = self.onCreateClick_impl()
finally:
if not installing:
self.ui_enable_controls()
def onCreateClick_impl(self):
"""
Main function to create bootable USB disk.
:param usb_disk: ComboBox text as detected USB disk.
:param iso_link: LineEdit text as selected ISO link.
:return:
"""
self.ui_disable_controls()
try:
usb_details = usb.details(config.usb_disk)
except usb.PartitionNotMounted as e:
log(str(e))
QtWidgets.QMessageBox.error("Error: partition not mounted", str(e))
return
if not config.usb_disk:
log("ERROR: No USB device found.")
QtWidgets.QMessageBox.information(self, "No Device...",
"No USB device found.\n\nInsert USB and use Refresh USB button to detect USB.")
self.ui_enable_controls()
elif not config.image_path:
log("No ISO selected.")
QtWidgets.QMessageBox.information(self, "No ISO...", "No ISO found.\n\nPlease select an ISO.")
self.ui_enable_controls()
elif usb_details['mount_point'] == 'No_Mount':
for cond, log_msg, dialog_title, dialog_msg in [
(lambda: config.usb_disk is None,
'ERROR: No USB device found.',
'No Device...',
'No USB device found.\n\nInsert USB and '
'use Refresh USB button to detect USB.'),
(lambda: not config.image_path,
'No ISO selected.',
'No ISO...',
'No ISO found.\n\nPlease select an ISO.'),
(lambda: ' ' in
os.path.basename(config.image_path),
'Spaces in iso-file name is not allowed.',
'Bad ISO filename...',
'Filename that contains space(s) is not '
'supported.')]:
if cond():
QtWidgets.QMessageBox.information(
self, dialog_title, dialog_msg)
return False
usb_details = config.usb_details
if usb_details['mount_point'] == 'No_Mount':
log("ERROR: USB disk is not mounted.")
QtWidgets.QMessageBox.information(self, "No Mount...", "USB disk is not mounted.\n"
"Please mount USB disk and press refresh USB button.")
self.ui_enable_controls()
elif platform.system() == 'Linux' and config.usb_disk[-1].isdigit() is False:
gen.log('Selected USB is a disk. Please select a disk partition from the drop down list')
QtWidgets.QMessageBox.information(self, 'No Partition...!',
'USB disk selected doesn\'t contain a partition.\n'
'Please select the partition (ending '
'with a digit eg. /dev/sdb1)\nfrom the drop down list.')
self.ui_enable_controls()
elif 0 < config.persistence and \
QtWidgets.QMessageBox.information(
self, "No Mount...",
"USB disk is not mounted.\n"
"Please mount USB disk and press refresh "
"USB button.")
return False
if config.usb_details['devtype'] == 'disk':
gen.log('Selected USB is a physical disk. '
'Please select '
'a partition or volume from the drop down list')
QtWidgets.QMessageBox.information(
self, 'No Partition...!',
'Selected USB is a physical disk. '
'Please select a partition (e.g. /dev/sdc1) '
'or a volume (e.g. G:) '
'from the drop down list.')
return False
if 0 < config.persistence and \
persistence.detect_missing_tools(config.distro):
QtWidgets.QMessageBox.information(
self, 'Missing tools...!',
persistence.detect_missing_tools(config.distro))
self.ui_enable_controls()
else:
# clean_iso_cfg_ext_dir(os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")) # Need to be cleaned.
# extract_cfg_file(config.image_path) # Extract files from ISO
# config.distro = distro(iso_cfg_ext_dir(), config.image_path) # Detect supported distro
try:
usb_details = usb.details(config.usb_disk)
except usb.PartitionNotMounted as e:
log(str(e))
QtWidgets.QMessageBox.error("Error: partition not mounted", str(e))
return
log("MultiBoot Install: USB Disk: " + config.usb_disk)
log("MultiBoot Install: USB Label: " + config.usb_label)
log("MultiBoot Install: USB UUID: " + config.usb_uuid)
log("MultiBoot Install: USB mount path: " + config.usb_mount)
log("MultiBoot Install: Disk total size: " + str(usb.bytes2human(usb_details['size_total'])))
log("MultiBoot Install: Disk used size: " + str(usb.bytes2human(usb_details['size_used'])))
log("MultiBoot Install: Disk free size: " + str(usb.bytes2human(usb_details['size_free'])))
log("MultiBoot Install: Filesystem: " + usb_details['file_system'])
log("MultiBoot Install: Disk vendor: " + usb_details['vendor'])
log("MultiBoot Install: Disk model: " + usb_details['model'])
log("MultiBoot Install: ISO file: " + iso_name(config.image_path))
persistence.detect_missing_tools(
config.distro))
return False
if not self.check_remount():
self.update_target_info()
return False
# clean_iso_cfg_ext_dir(os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")) # Need to be cleaned.
# extract_cfg_file(config.image_path) # Extract files from ISO
# config.distro = distro(iso_cfg_ext_dir(), config.image_path) # Detect supported distro
log("MultiBoot Install: USB Disk: " + config.usb_disk)
log("MultiBoot Install: USB Label: " + config.usb_label)
log("MultiBoot Install: USB UUID: " + config.usb_uuid)
log("MultiBoot Install: USB mount path: " + config.usb_mount)
log("MultiBoot Install: Disk total size: " + str(usb.bytes2human(usb_details['size_total'])))
log("MultiBoot Install: Disk used size: " + str(usb.bytes2human(usb_details['size_used'])))
log("MultiBoot Install: Disk free size: " + str(usb.bytes2human(usb_details['size_free'])))
log("MultiBoot Install: Filesystem: " + usb_details['file_system'])
log("MultiBoot Install: Disk vendor: " + usb_details['vendor'])
log("MultiBoot Install: Disk model: " + usb_details['model'])
log("MultiBoot Install: ISO file: " + iso_name(config.image_path))
if not os.path.exists(config.image_path):
return False
# self.ui.image_path.clear()
if not config.distro:
QtWidgets.QMessageBox.information(
self, 'No support...',
'Sorry.\n' +
os.path.basename(config.image_path) +
' is not supported at the moment.\n'
'Please email this issue to '
'feedback.multibootusb@gmail.com')
return False
log("MultiBoot Install: Distro type detected: " + config.distro)
full_image_path = os.path.join(
config.usb_mount, "multibootusb",
iso_basename(config.image_path))
if os.path.exists(full_image_path):
QtWidgets.QMessageBox.information(
self, 'Already exists...',
os.path.basename(config.image_path) +
' is already installed.')
return False
config.persistence = self.ui.slider_persistence.value() \
* 1024 * 1024
log("Persistence chosen is " +
str(bytes2human(config.persistence)))
install_size = iso_size(config.image_path) + config.persistence
if install_size >= disk_usage(config.usb_mount).free:
log("ERROR: Not enough space available on " +
config.usb_disk)
QtWidgets.QMessageBox.information(
self, "No Space.",
"No space available on " + config.usb_disk)
return False
msg = '''
The ISO sleceted is not supported at the moment.
You can try booting ISO using memdisk.
Distro can be uninstalled anytime from main menu.
Proceed with installation?'''.lstrip() if config.distro == 'memdisk_iso' else \
'''
Selected USB disk: %s
USB mount point: %s
Selected distro: %s
Log location: %s
Proceed with installation?'''.lstrip() % \
(config.usb_disk, config.usb_mount, iso_name(config.image_path),
osdriver.mbusb_log_file())
reply = QtWidgets.QMessageBox.question(
self, 'Review selection...', msg)
if reply == QtWidgets.QMessageBox.Yes:
self.ui.slider_persistence.setEnabled(False)
copy_mbusb_dir_usb(config.usb_disk)
config.process_exist = True
self.progress_thread_install.start()
return True
if os.path.exists(config.image_path):
# self.ui.image_path.clear()
if config.distro:
log("MultiBoot Install: Distro type detected: " + config.distro)
if not os.path.exists(
os.path.join(config.usb_mount, "multibootusb", iso_basename(config.image_path))):
config.persistence = self.ui.slider_persistence.value() * 1024 * 1024
log("Persistence chosen is " + str(bytes2human(config.persistence)))
install_size = iso_size(config.image_path) + config.persistence
if install_size >= disk_usage(config.usb_mount).free:
log("ERROR: Not enough space available on " + config.usb_disk)
QtWidgets.QMessageBox.information(self, "No Space.",
"No space available on " + config.usb_disk)
self.ui_enable_controls()
else:
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)
copy_mbusb_dir_usb(config.usb_disk)
config.process_exist = True
self.progress_thread_install.start()
elif reply == QtWidgets.QMessageBox.No:
self.ui_enable_controls()
return False
else:
QtWidgets.QMessageBox.information(self, 'Already exists...',
os.path.basename(
config.image_path) + ' is already installed.')
self.ui_enable_controls()
else:
QtWidgets.QMessageBox.information(self, 'No support...',
'Sorry.\n' + os.path.basename(config.image_path) +
' is not supported at the moment.\n'
'Please email this issue to feedback.multibootusb@gmail.com')
self.ui_enable_controls()
# Added to refresh usb disk remaining size after distro installation
# self.update_gui_usb_info()
def dd_finished(self):
"""
@ -652,8 +739,15 @@ Are you SURE you want to enable it?",
config.process_exist = None
msgBox = QtWidgets.QMessageBox()
msgBox.setText("Image succesfully written to USB disk.")
msgBox.setInformativeText("Reboot to boot from USB or test it from <b>Boot ISO/USB</b> tab.");
if self.progress_thread_dd.error:
title = "Failed to write the iso image to the USB disk."
msg = self.progress_thread_dd.error
else:
title = "Image succesfully written to USB disk."
msg = "Reboot to boot from USB or test it from " \
"<b>Boot ISO/USB</b> tab."
msgBox.setText(title)
msgBox.setInformativeText(msg);
msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
msgBox.setIcon(QtWidgets.QMessageBox.Information)
msgBox.exec_()
@ -671,7 +765,9 @@ Are you SURE you want to enable it?",
self.ui.button_browse_image.setEnabled(False)
self.ui.combo_drives.setEnabled(False)
# FIXME self.ui.pushbtn_imager_refreshusb.setEnabled(False)
status_text = ("Status: Writing " + os.path.basename(config.image_path) + " to " + config.usb_disk)
status_text = ("Status: Writing " +
os.path.basename(config.image_path) + " to " +
osdriver.usb_disk_desc(config.usb_disk))
self.ui.statusbar.showMessage(status_text)
def dd_quit(self):
@ -693,19 +789,19 @@ Are you SURE you want to enable it?",
self.ui_enable_controls()
else:
imager = Imager()
if platform.system() == 'Linux' and config.usb_details['devtype'] == "partition":
if config.usb_details['devtype'] == "partition":
gen.log('Selected device is a partition. Please select a disk from the drop down list')
QtWidgets.QMessageBox.information(self, 'Incompatible device', 'Selected device (%s) is a partition!\n'
'ISO must be written to a whole disk.'
'\n\nPlease select a disk from the drop down list.' % config.usb_disk)
self.ui_enable_controls()
else:
usb_disk_size = int(imager.imager_usb_detail(config.usb_disk, partition=0).total_size)
usb_disk_size = int(imager.imager_usb_detail(config.usb_disk).total_size)
self.iso_size = os.path.getsize(config.image_path)
if self.iso_size >= usb_disk_size:
QtWidgets.QMessageBox.information(self, "No enough space on disk.",
os.path.basename(config.image_path) +
" size is larger than the size of " + config.usb_disk)
" size is larger than the size of " + osdriver.usb_disk_desc(config.usb_disk))
self.ui_enable_controls()
# elif gen.process_exist('explorer.exe') is not False:
# # Check if windows explorer is running and inform user to close it.
@ -715,7 +811,7 @@ Are you SURE you want to enable it?",
else:
reply = QtWidgets.QMessageBox.question \
(self, 'Review selection',
'Selected disk: %s\n' % config.usb_disk +
'Selected disk: %s\n' % osdriver.usb_disk_desc(config.usb_disk) +
'Selected image: %s\n\n' % os.path.basename(config.image_path) +
'Proceed with writing image to disk?',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
@ -734,6 +830,42 @@ Are you SURE you want to enable it?",
"""
self.close()
def update_usb_mount(self, new_usb_details):
config.update_usb_mount(new_usb_details)
self.ui.usb_mount.setText(config.usb_mount)
def check_remount(self):
if config.usb_details['file_system'] != 'vfat':
return True
try:
with UnmountedContext(config.usb_disk,
self.update_usb_mount) as m:
pass
return True
except usb.RemountError:
QtWidgets.QMessageBox.critical(
self,"Remount failed.",
"Could not remount '{0}'. "
"Please make sure no process has open "
"handle(s) to previously mounted filesystem."
.format(config.usb_disk))
return False
def update_target_info(self):
usb_total_size= str(usb.bytes2human(config.usb_details.get('size_total', "")))
usb_free_size= str(usb.bytes2human(config.usb_details.get('size_free', "")))
config.persistence_max_size = persistence.max_disk_persistence(config.usb_disk)
config.usb_mount = config.usb_details.get('mount_point', "")
self.ui.usb_dev.setText(osdriver.usb_disk_desc(config.usb_disk))
self.ui.usb_vendor.setText(config.usb_details.get('vendor', ""))
self.ui.usb_model.setText(config.usb_details.get('model', ""))
self.ui.usb_size.setText('Free :: ' + usb_free_size + ' / Total :: ' + usb_total_size)
self.ui.usb_mount.setText(config.usb_details.get('mount_point', ""))
self.ui.usb_type.setText(config.usb_details.get('devtype', ""))
self.ui.usb_fs.setText(config.usb_details.get('file_system', ""))
def closeEvent(self, event):
"""
To capture the main close event.
@ -756,7 +888,6 @@ Are you SURE you want to enable it?",
log("Close event cancelled.")
event.ignore()
class GuiInstallProgress(QtCore.QThread):
"""
Update GUI thread during install.
@ -810,7 +941,7 @@ class GuiUninstallProgress(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
self.thread = GenericThread(uninstall_progress)
self.thread = GenericThread(uninstall_distro.uninstall_progress)
def __del__(self):
self.wait()
@ -849,21 +980,23 @@ class DD_Progress(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
if platform.system() == 'Linux':
self.thread = GenericThread(dd_linux)
elif platform.system() == 'Windows':
self.thread = GenericThread(dd_win)
self.error = None
self.thread = GenericThread(partial(dd_iso_image, self))
def __del__(self):
self.wait()
def set_error(self, error):
self.error = error
def run(self):
config.imager_percentage = 0
self.thread.start()
while self.thread.isRunning():
if config.imager_percentage:
self.update.emit(config.imager_percentage)
if not self.thread.isFinished() and config.percentage == 100:
if not self.thread.isFinished() and \
config.percentage == 100:
config.imager_status_text = ""
self.status.emit("Please wait...")
time.sleep(0.1)

@ -0,0 +1,463 @@
import collections
import logging
import logging.handlers
import os
import platform
import queue
import shutil
import signal
import subprocess
import sys
import tempfile
import time
if platform.system() == 'Windows':
import wmi
from scripts import win32
else:
try:
from . import udisks
except ImportError:
import udisks
def log(message, info=True, error=False, debug=False, _print=True):
"""
Dirty function to log messages to file and also print on screen.
:param message:
:param info:
:param error:
:param debug:
:return:
"""
if _print is True:
print(message)
# remove ANSI color codes from logs
# message_clean = re.compile(r'\x1b[^m]*m').sub('', message)
if info is True:
logging.info(message)
elif error is not False:
logging.error(message)
elif debug is not False:
logging.debug(message)
def resource_path(relativePath):
"""
Function to detect the correct path of file when working with sourcecode/install or binary.
:param relativePath: Path to file/data.
:return: Modified path to file/data.
"""
# This is not strictly needed because Windows recognize '/'
# as a path separator but we follow the discipline here.
relativePath = relativePath.replace('/', os.sep)
for dir_ in [
os.path.abspath('.'),
os.path.abspath('..'),
getattr(sys, '_MEIPASS', None),
os.path.dirname(os.path.dirname( # go up two levels
os.path.realpath(__file__))),
'/usr/share/multibootusb'.replace('/', os.sep),
]:
if dir_ is None:
continue
fullpath = os.path.join(dir_, relativePath)
if os.path.exists(fullpath):
return fullpath
log("Could not find resource '%s'." % relativePath)
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.
"""
partition, logical_disk = wmi_get_drive_info(usb_disk)
log("Physical Device Number is %d" % partition.DiskIndex)
return partition.DiskIndex
def wmi_get_drive_info(usb_disk):
assert platform.system() == 'Windows'
for partition in wmi.WMI().Win32_DiskPartition():
logical_disks = partition.associators("Win32_LogicalDiskToPartition")
# Here, 'disk' is a windows logical drive rather than a physical drive
for disk in logical_disks:
if disk.Caption == usb_disk:
return (partition, disk)
raise RuntimeError('Failed to obtain drive information ' + usb_disk)
def collect_relevant_info(obj, tuple_name, attributes, named_tuple):
if len(named_tuple)==0:
names = [x[0] for x in attributes]
named_tuple.append(collections.namedtuple(tuple_name, names))
L = []
for (attr, convfunc) in attributes:
v = getattr(obj, attr)
L.append(None if v is None else convfunc(v))
return named_tuple[0](*L)
def collect_relevant_physicaldrive_info(d, physicaldrive_info_tuple=[]):
attributes = [
('BytesPerSector', int),
('DeviceID', str),
('Index', int),
('Manufacturer', str),
('MediaType', str),
('Model', str),
('Partitions', int),
('SerialNumber', str),
('Size', int),
('TotalSectors', int),
]
return collect_relevant_info(d, 'PhysicalDrive', attributes,
physicaldrive_info_tuple)
def collect_relevant_volume_info(v, volume_info_tuple=[]):
attributes = [
('DeviceID', str),
('DriveType', int),
('FreeSpace', int),
('FileSystem', str),
('Size', int),
]
return collect_relevant_info(v, 'Volume', attributes, volume_info_tuple)
def wmi_get_physicaldrive_info(usb_disk):
"Return information about the drive that contains 'usb_disk'."
partition, disk = wmi_get_drive_info(usb_disk)
import wmi
c = wmi.WMI()
drv_list = [d for d in c.Win32_DiskDrive()
if d.Index == partition.DiskIndex]
assert len(drv_list)==1
return collect_relevant_physicaldrive_info(drv_list[0])
def wmi_get_physicaldrive_info_all():
c = wmi.WMI()
L = [collect_relevant_physicaldrive_info(d) for d in c.Win32_DiskDrive()]
L.sort(key=lambda x: x.Index)
return L
def wmi_get_volume_info_on(diskIndex):
L = [volumes for (dindex, volumes) in wmi_get_volume_info_all().items()
if dindex==diskIndex]
return [] if len(L)==0 else L[0]
def wmi_get_volume_info_all():
r = {}
for dindex, volumes in [
(p.DiskIndex, map(lambda d: collect_relevant_volume_info(d),
p.associators("Win32_LogicalDiskToPartition")))
for p in wmi.WMI().Win32_DiskPartition()]:
r.setdefault(dindex, []).extend(volumes)
for dindex, volumes in r.items():
volumes.sort(key=lambda x: x.DeviceID)
return r
def wmi_get_volume_info_ex(usb_disk):
assert platform.system() == 'Windows'
partition, disk = wmi_get_drive_info(usb_disk)
#print (disk.Caption, partition.StartingOffset, partition.DiskIndex,
# disk.FileSystem, disk.VolumeName)
# Extract Volume serial number off of the boot sector because
# retrieval via COM object 'Scripting.FileSystemObject' or wmi interface
# truncates NTFS serial number to 32 bits.
with open('//./Physicaldrive%d'%partition.DiskIndex, 'rb') as f:
f.seek(int(partition.StartingOffset))
bs_ = f.read(512)
serial_extractor = {
'NTFS' : lambda bs: \
''.join('%02X' % c for c in reversed(bs[0x48:0x48+8])),
'FAT32' : lambda bs: \
'%02X%02X-%02X%02X' % tuple(
map(int,reversed(bs[67:71])))
}.get(disk.FileSystem, lambda bs: None)
uuid = serial_extractor(bs_)
mount_point = usb_disk + '\\'
size_total, size_used, size_free \
= shutil.disk_usage(mount_point)[:3]
r = {
'uuid' : uuid,
'file_system' : disk.FileSystem,
'label' : disk.VolumeName.strip() or 'No_label',
'mount_point' : mount_point,
'size_total' : size_total,
'size_used' : size_used,
'size_free' : size_free,
'vendor' : 'Not_Found',
'model' : 'Not_Found',
'devtype' : 'partition',
'mediatype' : {
0 : 'Unknown',
1 : 'Fixed Disk',
2 : 'Removable Disk',
3 : 'Local Disk',
4 : 'Network Drive',
5 : 'Compact Disc',
6 : 'RAM Disk',
}.get(disk.DriveType, 'DiskType(%d)' % disk.DriveType),
'disk_index' : partition.DiskIndex,
}
# print (r)
return r
def wmi_get_physicaldrive_info_ex(diskIndex):
drv_list = [d for d in wmi.WMI().Win32_DiskDrive()
if d.Index == diskIndex]
assert len(drv_list)==1
d = collect_relevant_physicaldrive_info(drv_list[0])
r = {}
for src, dst in [
('Size', 'size_total'),
('Model', 'model'),
('Manufacturer', 'vendor'),
('MediaType', 'mediatype'),
('SerialNumber', 'uuid'),
('DeviceID', 'label'),
]:
r[dst] = getattr(d, src)
r['devtype'] = 'disk'
r['size_free'] = 0
r['file_system'] = 'N/A'
r['mount_point'] = 'N/A'
return r
def win_physicaldrive_to_listbox_entry(pdrive):
return '%d:%s' % (pdrive.Index,pdrive.Model)
def win_volume_to_listbox_entry(v):
return v.DeviceID
class Base:
def run_dd(self, input, output, bs, count):
cmd = [self.dd_exe, 'if='+input, 'of='+output,
'bs=%d' % bs, 'count=%d'%count]
self.dd_add_args(cmd, input, output, bs, count)
if subprocess.call(cmd) != 0:
log('Failed to execute [%s]' % str(cmd))
else:
log("%s succeeded." % str(cmd))
def dd_iso_image(self, input_, output, gui_update, status_update):
''' Implementation for OS that use dd to write the iso image.
'''
in_file_size = os.path.getsize(input_)
cmd = [self.dd_exe, 'if=' + input_,
'of=' + self.physical_disk(output), 'bs=1M']
self.dd_iso_image_add_args(cmd, input_, output)
kw_args = {
'stdout' : subprocess.PIPE,
'stderr' : subprocess.PIPE,
'shell' : False,
}
self.add_dd_iso_image_popen_args(kw_args)
self.dd_iso_image_prepare(input, output, status_update)
log('Executing => ' + str(cmd))
dd_process = subprocess.Popen(cmd, **kw_args)
output_q = queue.Queue()
while dd_process.poll() is None:
self.dd_iso_image_readoutput(dd_process, gui_update, in_file_size,
output_q)
output_lines = [output_q.get() for i in range(output_q.qsize())]
for l in output_lines:
log('dd: ' + l)
return self.dd_iso_image_interpret_result(
dd_process.returncode, output_lines)
class Windows(Base):
def __init__(self):
self.dd_exe = resource_path('data/tools/dd/dd.exe')
def dd_add_args(self, cmd_vec, input, output, bs, count):
pass
def dd_iso_image(self, input_, output, gui_update, status_update):
assert type(output) is int
status_update('Zapping PhyiscalDisk%d' % output)
win32.ZapPhysicalDrive(output, wmi_get_volume_info_on, log)
# Ouch. Needs sometime for the zapping to take effect...
# Better way than sleeping constant time?
time.sleep(3)
status_update('Writing to PhysicalDisk%d' % output)
in_file_size = os.path.getsize(input_)
with win32.openHandle('\\\\.\\PhysicalDrive%d' % output,
True, False, log) as hDrive:
hDrive.LockPhysicalDrive()
hDrive.CopyFrom(input_, lambda bytes_copied:
gui_update(float(bytes_copied)/in_file_size*100.))
def physical_disk(self, usb_disk):
if type(usb_disk) is str:
usb_disk = get_physical_disk_number(usb_disk)
return r'\\.\physicaldrive%d' % usb_disk
def mbusb_log_file(self):
return os.path.join(os.getcwd(), 'multibootusb.log')
def find_mounted_partitions_on(self, usb_disk):
return [] # No-op until UnmountedContext() get implemented for Windows
def multibootusb_host_dir(self):
return os.path.join(tempfile.gettempdir(), "multibootusb")
def gpt_device(self, dev_name):
if type(dev_name) is int:
diskIndex = dev_name
for p in wmi.WMI().Win32_DiskPartition():
if p.DiskIndex == diskIndex:
return p.Type.startswith('GPT:')
log(usb_disk_desc(dev_name) + ' seems not partitioned. ' +
'assuming msdos.')
return False
else:
partition, disk = wmi_get_drive_info(dev_name)
return partition.Type.startswith('GPT:')
def usb_disk_desc(self, dev_name):
if type(dev_name) is int:
return 'PhysicalDrive%d' % dev_name
return dev_name
def listbox_entry_to_device(self, lb_entry):
left = lb_entry.split(':', 1)[0]
if left.isdigit():
return int(left) # see win_physicaldrive_to_listbox_entry()
else:
return lb_entry # see win_volume_to_listbox_entry()
def qemu_more_params(self, disk):
return ['-L', '.', '-boot', 'c', '-hda', self.physical_disk(disk)]
class Linux(Base):
def __init__(self):
self.dd_exe = 'dd'
def dd_iso_image_prepare(self, input, output, status_update):
pass
def dd_add_args(self, cmd_vec, input, output, bs, count):
cmd_vec.append('conv=notrunc')
def dd_iso_image_add_args(self, cmd_vec, input_, output):
cmd_vec.append('oflag=sync')
def add_dd_iso_image_popen_args(self, dd_iso_image_popen_args):
pass
def dd_iso_image_readoutput(self, dd_process, gui_update, in_file_size,
output_q):
# If this time delay is not given, the Popen does not execute
# the actual command
time.sleep(0.1)
dd_process.send_signal(signal.SIGUSR1)
dd_process.stderr.flush()
while True:
time.sleep(0.1)
out_error = dd_process.stderr.readline().decode()
if out_error:
if 'bytes' in out_error:
bytes_copied = float(out_error.split(' ', 1)[0])
gui_update( bytes_copied / in_file_size * 100. )
break
if 15 < output_q.qsize():
output_q.get()
output_q.put(out_error.rstrip())
else:
# stderr is closed
break
def dd_iso_image_interpret_result(self, returncode, output_list):
return None if returncode==0 else '\n'.join(output_list)
def physical_disk(self, usb_disk):
return usb_disk.rstrip('0123456789')
def mbusb_log_file(self):
return '/var/log/multibootusb.log'
def find_mounted_partitions_on(self, usb_disk):
return udisks.find_mounted_partitions_on(usb_disk)
def multibootusb_host_dir(self):
return os.path.join(os.path.expanduser('~'), ".multibootusb")
def gpt_device(self, dev_name):
disk_dev = self.physical_disk(dev_name)
cmd = ['parted', disk_dev, '-s', 'print']
with open(os.devnull) as devnull:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stdin=devnull)
_cmd_out, _err_out = p.communicate()
p.wait()
if p.returncode != 0:
lang = os.getenv('LANG')
encoding = lang.rsplit('.')[-1] if lang else 'utf-8'
raise RuntimeError(str(_err_out, encoding))
subprocess.check_call(['partprobe', disk_dev])
if b'msdos' in _cmd_out:
return False
if b'gpt' in _cmd_out:
return True
raise RuntimeError("Disk '%s' is uninitialized and not usable." %
disk_dev)
def usb_disk_desc(self, dev_name):
return dev_name
def listbox_entry_to_device(self, lb_entry):
return lb_entry
def qemu_more_params(self, disk):
return ['-hda', self.physical_disk(disk), '-vga', 'std']
driverClass = {
'Windows' : Windows,
'Linux' : Linux,
}.get(platform.system(), None)
if driverClass is None:
raise Exception('Platform [%s] is not supported.' % platform.system())
osdriver = driverClass()
for func_name in [
'run_dd',
'physical_disk',
'mbusb_log_file',
'dd_iso_image',
'find_mounted_partitions_on',
'multibootusb_host_dir',
'gpt_device',
'listbox_entry_to_device',
'usb_disk_desc',
'qemu_more_params',
]:
globals()[func_name] = getattr(osdriver, func_name)
def initialize():
logging.root.setLevel(logging.DEBUG)
fmt = '%(asctime)s.%(msecs)03d %(name)s %(levelname)s %(message)s'
datefmt = '%H:%M:%S'
the_handler = logging.handlers.RotatingFileHandler(
osdriver.mbusb_log_file(), 'a', 1024*1024, 5)
the_handler.setFormatter(logging.Formatter(fmt, datefmt))
logging.root.addHandler(the_handler)
if platform.system() == 'Windows':
import pythoncom
pythoncom.CoInitialize()

@ -30,10 +30,11 @@ def max_disk_persistence(usb_disk):
config.usb_uuid = usb_details['uuid']
config.usb_label = usb_details['label']
if usb_details['file_system'] in ['vfat', 'FAT32'] and usb_details['size_free'] > fat_max_size:
_max_size = fat_max_size
size_free = usb_details['size_free']
if usb_details['file_system'] in ['vfat', 'FAT32']:
_max_size = min(fat_max_size, size_free)
else:
_max_size = usb_details['size_free']
_max_size = size_free
return _max_size
@ -189,7 +190,8 @@ def detect_missing_tools(distro):
try:
with open(os.devnull) as devnull:
for tool in [e2fsck_exe, resize2fs_exe]:
subprocess.Popen([tool], stdout=devnull, stderr=devnull)
p = subprocess.Popen([tool], stdout=devnull, stderr=devnull)
p.communicate()
except FileNotFoundError: # Windows
return "'%s.exe' is not installed or not available for use." % tool
except OSError: # Linux

@ -9,13 +9,15 @@
# under the terms of GNU General Public License, v.2 or above
import os
import subprocess
import platform
import subprocess
import traceback
from PyQt5 import QtWidgets
from .gui.ui_multibootusb import Ui_MainWindow
from .gen import *
from . import config
from . import osdriver
from . import usb
class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
"""
@ -27,35 +29,66 @@ class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def run_qemu(self, ram_size, qemu_more_params,
qemu_not_found_log_msg,
exec_error_title, exec_error_msg):
qemu = self.find_qemu()
if not qemu:
log(qemu_not_found_log_msg)
QtWidgets.QMessageBox.information(
self, 'No QEMU...',
'Please install qemu to use this feature.')
return
options = [] # '-bios', 'OVMF.fd']
if ram_size:
options.extend(['-m', ram_size])
if getattr(config, 'qemu_use_haxm', False):
options.extend(['-accel', 'hax'])
bios = getattr(config, 'qemu_bios', None)
if bios:
options.extend(['-bios', bios])
if platform.system()=='Linux' and getattr(config,'qemu_use_kvm', True):
options.append('-enable-kvm')
cmd = [qemu] + options + qemu_more_params
try:
new_wd = os.path.split(qemu)[0]
if new_wd:
old_wd = os.getcwd()
os.chdir(new_wd)
try:
with usb.UnmountedContext(config.usb_disk,
self.update_usb_mount):
log("Executing ==> %s" % cmd)
out = subprocess.check_output(cmd)
if out:
log('%s => %s' % (cmd, out))
finally:
if new_wd:
os.chdir(old_wd)
except (KeyboardInterrupt, SystemExit):
raise
except:
traceback.print_exc()
QtWidgets.QMessageBox.information(
self, exec_error_title, exec_error_msg)
def on_Qemu_Boot_iso_Click(self):
"""
Main function to boot a selected ISO.
:return:
"""
# if not self.ui.lineEdit_2.text():
if not config.image_path:
QtWidgets.QMessageBox.information(self, 'No ISO...', 'No ISO selected.\n\nPlease choose an ISO first.')
else:
qemu = self.check_qemu_exist()
qemu_iso_link = config.image_path
if not qemu:
log("ERROR: ISO Boot: qemu not found!")
QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to use this feature.')
else:
ram = self.qemu_iso_ram()
if ram:
ram = " -m " + ram
else:
ram = ""
cmd = qemu + ram + ' -enable-kvm -boot d' + ' -cdrom "' + str(qemu_iso_link) + '"'
try:
log("Executing ==> " + cmd)
subprocess.Popen(cmd, shell=True)
except:
QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting ISO\n'
'Unable to start QEMU.')
QtWidgets.QMessageBox.information(
self, 'No ISO...',
'No ISO selected.\n\nPlease choose an ISO first.')
return
self.run_qemu(
self.qemu_iso_ram(), ['-boot', 'd','-cdrom', config.image_path],
"ERROR: ISO Boot: qemu not found!",
'Error...', 'Error booting ISO\nUnable to start QEMU.')
def on_Qemu_Boot_usb_Click(self):
"""
@ -63,79 +96,40 @@ class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
:param usb_disk: Path to usb disk.
:return:
"""
qemu = self.check_qemu_exist()
if not config.usb_disk:
QtWidgets.QMessageBox.information(self, 'No disk...', 'No USB disk selected.\n\nPlease choose a disk first.')
else:
qemu = self.check_qemu_exist()
if platform.system() == 'Linux' and config.usb_disk[-1].isdigit() is True:
qemu_usb_disk = config.usb_disk[:-1]
else:
qemu_usb_disk = config.usb_disk
if qemu is None:
log("ERROR: USB Boot: qemu not found!")
QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to use this feature.')
else:
ram = self.qemu_usb_ram()
if ram:
ram = " -m " + ram
else:
ram = ""
if platform.system() == "Windows":
disk_number = get_physical_disk_number(qemu_usb_disk)
parent_dir = os.getcwd()
os.chdir(resource_path(os.path.join("data", "tools", "qemu")))
cmd = quote(qemu) + ' -L . -boot c' + ram \
+ ' -hda //./PhysicalDrive' + str(disk_number)
try:
log("Executing ==> " + cmd)
subprocess.Popen(cmd, shell=True)
except:
QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\nUnable to start QEMU.')
os.chdir(parent_dir)
elif platform.system() == "Linux":
cmd = qemu + ' -enable-kvm -hda "' + qemu_usb_disk + '"' + ram + ' -vga std'
try:
log('Executing ==> ' + cmd)
subprocess.Popen(cmd, shell=True)
except:
QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\n\nUnable to start QEMU.')
QtWidgets.QMessageBox.information(
self, 'No disk...',
'No USB disk selected.\n\nPlease choose a disk first.')
return
more_params = osdriver.qemu_more_params(config.usb_disk)
self.run_qemu(self.qemu_usb_ram(), more_params,
"ERROR: USB Boot: qemu not found!",
'Error...', 'Error booting USB\nUnable to start QEMU.')
def qemu_ram_size(self, combo, log_msg):
selected_ram = combo.currentText()
log(log_msg % selected_ram)
return selected_ram != 'Default' and selected_ram or None
def qemu_iso_ram(self):
"""
Choose a ram size for ISO booting.
:return: Ram size as string.
"""
selected_ram = self.ui.combo_iso_boot_ram.currentText()
log("QEMU: ISO RAM = " + selected_ram)
if selected_ram == "Default":
return None
else:
return selected_ram
return self.qemu_ram_size(self.ui.combo_iso_boot_ram,
"QEMU: ISO RAM = %s")
def qemu_usb_ram(self):
"""
Choose a ram size for USB booting.
:return: Ram size as string.
"""
selected_ram = self.ui.combo_usb_boot_ram.currentText()
log("QEMU: USB RAM = " + selected_ram)
if selected_ram == "Default":
return None
else:
return selected_ram
return self.qemu_ram_size(self.ui.combo_usb_boot_ram,
"QEMU: USB RAM = %s")
@staticmethod
def check_qemu_exist():
def find_qemu():
"""
Check if QEMU is available on host system.
Check if QEMU is available on host system and return path of the binary
:return: path to QEMU program or None otherwise.
"""
if platform.system() == "Linux":
@ -147,8 +141,7 @@ class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
qemu = ""
elif platform.system() == "Windows":
qemu = resource_path(os.path.join("data", "tools", "qemu", "qemu-system-x86_64.exe"))
log(qemu)
qemu = find_qemu_exe()
if qemu:
log("QEMU: using " + qemu)
@ -157,3 +150,17 @@ class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
return qemu
def find_qemu_exe():
exe_name = 'qemu-system-x86_64.exe'
if hasattr(config, 'qemu_exe_path'):
return config.qemu_exe_path
if not getattr(config, 'qemu_use_builtin', True):
for wellknown_path in [
r'c:\Program Files\qemu',
r'd:\Program Files\qemu',
r'e:\Program Files\qemu',
]:
exe_path = os.path.join(wellknown_path, exe_name)
if os.path.exists(exe_path):
return exe_path
return resource_path(os.path.join("data", "tools", "qemu", exe_name))

@ -13,11 +13,13 @@ from .gen import *
from . import usb
from .iso import *
from . import config
from . import osdriver
extlinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux4")
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4")
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4")
mbr_bin = resource_path(os.path.join("data", "tools", "mbr.bin"))
win_gdisk = resource_path(os.path.join('data', 'tools', 'gdisk', 'gdisk.exe'))
# Force Linux to install extlinux on NTFS
if platform.system() == 'Linux':
extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs", "NTFS", "ntfs"]
@ -25,6 +27,8 @@ if platform.system() == 'Linux':
else:
extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs"]
syslinux_fs = ["vfat", "ntfs", "FAT32", "NTFS"]
win_gdisk = resource_path(os.path.join(
'data', 'tools', 'gdisk', 'gdisk.exe'))
def gpt_part_table(usb_disk):
@ -90,6 +94,39 @@ def set_boot_flag(usb_disk):
log("\nUnable to set legacy_boot flag on " + usb_disk[:-1], '\n')
return False
def linux_install_default_bootsector(usb_disk, mbr_install_cmd):
with usb.UnmountedContext(usb_disk, config.update_usb_mount):
syslinux_cmd = [syslinux_path, '-i', '-d', 'multibootusb', usb_disk]
if os.access(syslinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + syslinux_path, shell=True)
log("\nExecuting ==> %s\n" % syslinux_cmd)
config.status_text = 'Installing default syslinux version 4...'
if subprocess.call(syslinux_cmd) == 0:
# On my system, it takes hours long to complete a single check
# So not included as of now
# usb.repair_vfat_filesystem(usb_disk)
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")
if set_boot_flag(usb_disk) is True:
return True
else:
log("\nFailed to install default syslinux...\n")
config.status_text = 'Failed to install default syslinux...'
return False
return None
def create_syslinux_bs(usb_disk, usb_mount):
osdriver.run_dd(osdriver.physical_disk(usb_disk),
os.path.join(usb_mount, 'multibootusb', 'syslinux.bin'),
512, 1)
def syslinux_default(usb_disk):
"""
@ -107,18 +144,20 @@ def syslinux_default(usb_disk):
usb_fs = usb_details['file_system']
usb_mount = usb_details['mount_point']
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]
mbr_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin \
+ ' of=' + usb_disk[:-1]
else:
win_usb_disk_no = get_physical_disk_number(config.usb_disk)
win_usb_disk_no = get_physical_disk_number(usb_disk)
_windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe"))
_input = "if=" + mbr_bin
_output = 'of=\\\.\\physicaldrive' + str(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')
extlinu_cmd = extlinux_path + ' --install ' + \
os.path.join(usb_mount, 'multibootusb')
if os.access(extlinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + extlinux_path, shell=True)
log("\nExecuting ==> " + extlinu_cmd)
@ -132,30 +171,16 @@ def syslinux_default(usb_disk):
config.status_text = 'mbr install is successful...'
log("\nmbr install is success...\n")
if set_boot_flag(usb_disk) is True:
create_syslinux_bs(usb_disk, usb_mount)
return True
elif usb_fs in syslinux_fs:
if platform.system() == "Linux":
syslinux_cmd = syslinux_path + ' -i -d multibootusb ' + usb_disk
if os.access(syslinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + syslinux_path, shell=True)
log("\nExecuting ==> " + syslinux_cmd + "\n")
config.status_text = 'Installing default syslinux version 4...'
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")
if set_boot_flag(usb_disk) is True:
return True
else:
log("\nFailed to install default syslinux...\n")
config.status_text = 'Failed to install default syslinux...'
return False
r = linux_install_default_bootsector(usb_disk, mbr_install_cmd)
if r:
create_syslinux_bs(usb_disk, usb_mount)
return r
elif platform.system() == "Windows":
syslinux = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4.exe"))
config.status_text = 'Installing default syslinux version 4...'
@ -180,12 +205,19 @@ def syslinux_default(usb_disk):
'''
if config.usb_gpt is False:
log('\nExecuting ==> ' + mbr_install_cmd)
#
# Updating mbr using dd results in catastrophy.
# Windows will lose track of the filesystem and
# the target volume will go away.
# Never enable this code without proper work around!
#
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...')
'''
create_syslinux_bs(usb_disk, usb_mount)
return True
else:
@ -194,6 +226,54 @@ def syslinux_default(usb_disk):
return False
def build_distro_bootsector(usb_disk, options,
distro_syslinux_install_dir,
distro_sys_install_bs):
with usb.UnmountedContext(config.usb_disk, config.update_usb_mount):
tmp_bs = build_distro_bootsector_impl(
usb_disk, options, distro_syslinux_install_dir)
if tmp_bs:
shutil.copy(tmp_bs, distro_sys_install_bs)
def build_distro_bootsector_impl(usb_disk, options,
distro_syslinux_install_dir):
syslinux_path = os.path.join(
multibootusb_host_dir(), "syslinux", "bin", "syslinux") \
+ config.syslinux_version
if os.access(syslinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + syslinux_path, shell=True)
sys_cmd = [syslinux_path] + options + [
distro_syslinux_install_dir, usb_disk]
log("Executing ==> %s" % sys_cmd)
if subprocess.call(sys_cmd) == 0:
config.status_text = \
'Syslinux install on distro directory is successful...'
log("\nSyslinux install on distro directory is successful...\n")
# On my system, it takes hours long to complete a single check
# So not included as of now
# usb.repair_vfat_filesystem(usb_disk)
tmp_bs_file = '/tmp/mbusb_temp.bs'
dd_cmd = ['dd', 'if=' + usb_disk, 'of=' + tmp_bs_file, 'count=1']
log('Executing ==> %s' % dd_cmd + '\n')
config.status_text = 'Copying boot sector...'
config.status_text = 'Installing distro specific syslinux...'
if subprocess.call(dd_cmd) == 0:
config.status_text = 'Bootsector copy is successful...'
log("\nBootsector copy is successful...\n")
else:
config.status_text = 'Failed to copy boot sector...'
log("\nFailed to copy boot sector...\n")
return tmp_bs_file
else:
config.status_text = 'Failed to install syslinux on distro directory...'
log("\nFailed to install syslinux on distro directory...\n")
return None
def syslinux_distro_dir(usb_disk, iso_link, distro):
"""
Install syslinux/extlinux on distro specific isolinux directory.
@ -224,60 +304,43 @@ def syslinux_distro_dir(usb_disk, iso_link, distro):
if distro in ["generic"]:
install_dir = usb_mount
distro_syslinux_install_dir = os.path.join(usb_mount, iso_linux_bin_dir.strip("/")).replace(usb_mount, "")
distro_sys_install_bs = os.path.join(install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
distro_syslinux_install_dir = os.path.join(
usb_mount, iso_linux_bin_dir.strip("/")).replace(usb_mount, "")
distro_sys_install_bs = os.path.join(
install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
else:
install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link))
distro_syslinux_install_dir = os.path.join(install_dir, iso_linux_bin_dir.strip("/")).replace(usb_mount, "")
distro_sys_install_bs = os.path.join(install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
install_dir = os.path.join(usb_mount, "multibootusb",
iso_basename(iso_link))
distro_syslinux_install_dir = os.path.join(
install_dir, iso_linux_bin_dir.strip("/")
).replace(usb_mount, "")
distro_sys_install_bs = os.path.join(
install_dir, iso_linux_bin_dir.strip("/"), distro + '.bs')
# log(distro_sys_install_bs)
# log(distro_syslinux_install_dir)
if usb_fs in syslinux_fs:
if config.syslinux_version == str(3):
if distro == "generic" and iso_linux_bin_dir == "/":
option = ""
else:
option = " -d "
else:
if distro == "generic" and iso_linux_bin_dir == "/":
option = " -i "
else:
option = " -i -d "
options = ['-f']
if config.syslinux_version != '3':
options.append('-i')
if not (distro == "generic" and iso_linux_bin_dir == "/"):
options.append('-d')
if platform.system() == "Linux":
syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux") + config.syslinux_version
if os.access(syslinux_path, os.X_OK) is False:
subprocess.call('chmod +x ' + syslinux_path, shell=True)
sys_cmd = syslinux_path + option + quote(distro_syslinux_install_dir) + ' ' + usb_disk
dd_cmd = 'dd if=' + usb_disk + ' ' + 'of=' + quote(distro_sys_install_bs) + ' count=1'
log("Executing ==> " + sys_cmd)
config.status_text = 'Installing distro specific syslinux...'
if subprocess.call(sys_cmd, shell=True) == 0:
config.status_text = 'Syslinux install on distro directory is successful...'
log("\nSyslinux install on distro directory is successful...\n")
log('Executing ==> ' + dd_cmd + '\n')
config.status_text = 'Copying boot sector...'
if subprocess.call(dd_cmd, shell=True) == 0:
config.status_text = 'Bootsector copy is successful...'
log("\nBootsector copy is successful...\n")
else:
config.status_text = 'Failed to copy boot sector...'
log("\nFailed to copy boot sector...\n")
else:
config.status_text = 'Failed to install syslinux on distro directory...'
log("\nFailed to install syslinux on distro directory...\n")
build_distro_bootsector(usb_disk, options,
distro_syslinux_install_dir,
distro_sys_install_bs)
elif platform.system() == "Windows":
syslinux_path = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin")) + \
"\syslinux" + config.syslinux_version + ".exe"
distro_syslinux_install_dir = "/" + distro_syslinux_install_dir.replace("\\", "/")
distro_sys_install_bs = distro_sys_install_bs.replace("/", "\\")
sys_cmd = syslinux_path + option + distro_syslinux_install_dir + ' ' + usb_disk + ' ' + \
distro_sys_install_bs
log("\nExecuting ==> " + sys_cmd + '\n')
sys_cmd = [syslinux_path] + options + \
[distro_syslinux_install_dir, usb_disk,
distro_sys_install_bs]
log("\nExecuting ==> %s" % sys_cmd )
config.status_text = 'Installing distro specific syslinux...'
if subprocess.call(sys_cmd, shell=True) == 0:
if subprocess.call(sys_cmd) == 0:
config.status_text = 'Syslinux install on distro directory is successful...'
log("\nSyslinux install was successful on distro directory...\n")
else:
@ -301,6 +364,8 @@ def syslinux_distro_dir(usb_disk, iso_link, distro):
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.

@ -15,18 +15,25 @@ import os
import re
def node_mountpoint(node):
def de_mangle_mountpoint(raw):
return raw.replace('\\040', ' ').replace('\\011', '\t') \
.replace('\\012', '\n').replace('\\0134', '\\')
def de_mangle(raw):
return raw.replace('\\040', ' ').replace('\\011', '\t').replace('\\012',
'\n').replace('\\0134', '\\')
def node_mountpoint(node):
for line in open('/proc/mounts').readlines():
line = line.split()
if line[0] == node:
return de_mangle(line[1])
return de_mangle_mountpoint(line[1])
return None
def find_mounted_partitions_on(disk):
assert not disk[-1:].isdigit()
with open('/proc/mounts') as f:
relevant_lines = [l.split(' ') for l in f.readlines()
if l.startswith(disk)]
return [ [v[0], de_mangle_mountpoint(v[1])] + v[2:] for v
in relevant_lines ]
class NoUDisks1(Exception):
pass
@ -51,27 +58,24 @@ class UDisks(object):
return dbus.Interface(self.bus.get_object('org.freedesktop.UDisks',
devpath), 'org.freedesktop.UDisks.Device')
def mount(self, device_node_path):
def mount(self, device_node_path, remounted=None):
mp = node_mountpoint(str(device_node_path))
if mp:
return mp
d = self.device(device_node_path)
try:
return str(d.FilesystemMount('',
['auth_no_user_interaction', 'rw', 'noexec', 'nosuid',
r = str(d.FilesystemMount(
'', ['auth_no_user_interaction', 'rw', 'noexec', 'nosuid',
'nodev', 'uid=%d'%os.geteuid(), 'gid=%d'%os.getegid()]))
except:
# May be already mounted, check
mp = node_mountpoint(str(device_node_path))
if mp is None:
raise
return mp
if remounted is not None:
remounted.append(True)
return r
def unmount(self, device_node_path):
d = self.device(device_node_path)
d.FilesystemUnmount(['force'])
def eject(self, device_node_path):
parent = device_node_path
while parent[-1] in '0123456789':
parent = parent[:-1]
parent = device_node_path.rstrip('0123456789')
d = self.device(parent)
d.DriveEject([])
@ -132,24 +136,20 @@ class UDisks2(object):
raise ValueError('%r not known to UDisks2'%device_node_path)
def mount(self, device_node_path):
def mount(self, device_node_path, remounted=None):
mp = node_mountpoint(str(device_node_path))
if mp:
return mp
d = self.device(device_node_path)
mount_options = ['rw', 'noexec', 'nosuid', 'nodev']
try:
mp = str(d.Mount(
{
'auth.no_user_interaction':True,
'options':','.join(mount_options)
mp = str(d.Mount(
{
'auth.no_user_interaction':True,
'options':','.join(mount_options)
},
dbus_interface=self.FILESYSTEM))
print(mp)
except:
# May be already mounted, check
mp = node_mountpoint(str(device_node_path))
if mp is None:
raise
if remounted is not None:
remounted.append(True)
return mp
def unmount(self, device_node_path):

@ -99,9 +99,6 @@ def delete_frm_file_list(iso_file_list, uninstall_distro_dir_name):
if os.path.exists(os.path.join(usb_mount, generic.strip("/"))):
os.remove(os.path.join(usb_mount, generic.strip("/")))
gen.log('Removed files from ' + uninstall_distro_dir_name)
if platform.system() == 'Linux':
gen.log('Syncing....')
os.sync()
@ -109,7 +106,6 @@ def delete_frm_file_list(iso_file_list, uninstall_distro_dir_name):
def do_uninstall_distro(target_distro, uninstall_distro_dir_name):
"""
Uninstall selected distro from selected USB disk.
:param config.usb_disk: Path of the USB disk
:param target_distro: Generic name applied to distro to be uninstalled
:param uninstall_distro_dir_name: Directory where the distro is installed
:return:
@ -122,7 +118,6 @@ def do_uninstall_distro(target_distro, uninstall_distro_dir_name):
usb_mount = usb_details['mount_point']
if platform.system() == 'Linux':
os.sync()
# remove 'immutable' from files on ext2/3/4 fs
if usb_mount:
subprocess.call("chattr -i -R %s/* 2>/dev/null" % usb_mount, shell=True)
@ -163,8 +158,6 @@ def do_uninstall_distro(target_distro, uninstall_distro_dir_name):
shutil.rmtree(os.path.join(usb_mount, "trk3"))
if os.path.exists(uninstall_distro_dir_name_fullpath):
if platform.system() == 'Linux':
os.sync()
shutil.rmtree(uninstall_distro_dir_name_fullpath)
delete_frm_file_list(iso_file_list, uninstall_distro_dir_name)
@ -192,8 +185,6 @@ def update_sys_cfg_file(uninstall_distro_dir_name):
Main function to remove uninstall distro specific operations.
:return:
"""
if platform.system() == 'Linux':
os.sync()
sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg")
if not os.path.exists(sys_cfg_file):
@ -215,8 +206,6 @@ def update_grub_cfg_file(uninstall_distro_dir_name):
Main function to remove uninstall distro name from the grub.cfg file.
:return:
"""
if platform.system() == 'Linux':
os.sync()
grub_cfg_file = os.path.join(config.usb_mount, "multibootusb",
"grub", "grub.cfg")
@ -250,8 +239,6 @@ def uninstall_progress():
return
usb_mount = usb_details['mount_point']
if platform.system() == 'Linux':
os.sync()
uninstall_distro_dir_name = config.uninstall_distro_dir_name \
.replace('\n', '')

@ -135,6 +135,8 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
usb_mount = usb_details['mount_point']
usb_uuid = usb_details['uuid']
usb_label = usb_details['label']
usb_fs_type = usb_details['file_system']
# iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")
config.status_text = "Updating config files..."
_iso_name = iso_basename(iso_link)
@ -143,17 +145,19 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
log('Updating distro specific config files...')
tweaker_params = ConfigTweakerParam(
_iso_name, install_dir_for_grub,
persistence, usb_uuid, usb_mount, usb_disk)
iso_link, install_dir_for_grub,
persistence, usb_uuid, usb_mount, usb_disk, usb_fs_type)
tweaker_class_dict = {
'ubuntu' : UbuntuConfigTweaker,
'debian' : DebianConfigTweaker,
'debian-install' : DebianConfigTweaker,
'gentoo' : GentooConfigTweaker,
'centos' : CentosConfigTweaker,
'centos-install' : CentosConfigTweaker,
'centos' : FedoraConfigTweaker,
'centos-install' : FedoraConfigTweaker,
'fedora' : FedoraConfigTweaker,
'antix' : AntixConfigTweaker,
'salix-live' : SalixConfigTweaker,
'wifislax' : WifislaxConfigTweaker,
}
tweaker_class = tweaker_class_dict.get(distro)
@ -181,24 +185,6 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
string = re.sub(r'file',
'cdrom-detect/try-usb=true floppy.allowed_drive_mask=0 ignore_uuid ignore_bootid root=UUID=' +
usb_uuid + ' file', string)
elif distro == "fedora":
string = re.sub(r'root=\S*', 'root=live:UUID=' + usb_uuid, string)
if re.search(r'liveimg', string, re.I):
string = re.sub(r'liveimg', 'liveimg live_dir=/multibootusb/' +
iso_basename(iso_link) + '/LiveOS', string)
elif re.search(r'rd.live.image', string, re.I):
string = re.sub(r'rd.live.image', 'rd.live.image rd.live.dir=/multibootusb/' +
iso_basename(iso_link) + '/LiveOS', string)
elif re.search(r'Solus', string, re.I):
string = re.sub(r'initrd=', 'rd.live.dir=/multibootusb/' + iso_basename(iso_link) +
'/LiveOS initrd=', string)
if persistence != 0:
if re.search(r'liveimg', string, re.I):
string = re.sub(r'liveimg', 'liveimg overlay=UUID=' + usb_uuid, string)
elif re.search(r'rd.live.image', string, re.I):
string = re.sub(r'rd.live.image', 'rd.live.image rw rd.live.overlay=UUID=' + usb_uuid, string)
string = re.sub(r' ro ', '', string)
elif distro == 'kaspersky':
if not os.path.exists(os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg')):
shutil.copyfile(resource_path(os.path.join('data', 'multibootusb', 'syslinux.cfg')),
@ -247,7 +233,7 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
string)
elif distro == "slax":
string = re.sub(r'initrd=',
r'from=/multibootusb/' + iso_basename(iso_link) + '/slax fromusb initrd=', string)
r'from=/multibootusb/' + iso_basename(iso_link) + '/slax changes=/multibootusb/' + iso_basename(iso_link) + '/slax fromusb initrd=', string)
elif distro == "finnix":
string = re.sub(r'initrd=',
r'finnixdir=/multibootusb/' + iso_basename(iso_link) + '/finnix initrd=', string)
@ -277,7 +263,7 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
'isodevice=/dev/disk/by-uuid/' + usb_uuid, string, flags=re.I)
string = re.sub(r'isobasedir=',
'isobasedir=/multibootusb/' + iso_basename(iso_link) + '/', string, flags=re.I)
string = re.sub(r'ui gfxboot', '# ui gfxboot', string) # Bug in the isolinux package
string = commentout_gfxboot(string)
string = string.replace('%INSTALL_DIR%', 'arch')
if 'manjaro' in string:
if not os.path.exists(os.path.join(usb_mount, '.miso')):
@ -288,7 +274,7 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
'kdeosisodevice=/dev/disk/by-uuid/' + usb_uuid, string, flags=re.I)
string = re.sub(r'append',
'append kdeosisobasedir=/multibootusb/' + iso_basename(iso_link) + '/kdeos/', string, flags=re.I)
string = re.sub(r'ui gfxboot', '# ui gfxboot', string) # Bug in the isolinux package
string = commentout_gfxboot(string)
elif distro in ["suse", "opensuse"]:
if re.search(r'opensuse_12', string, re.I):
string = re.sub(r'append',
@ -307,7 +293,7 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
'fromusb livecd=' + '/multibootusb/' + iso_basename(iso_link) + '/',
string)
string = re.sub(r'prompt', '#prompt', string)
string = re.sub(r'ui gfxboot.com', '#ui gfxboot.com', string)
string = commentout_gfxboot(string)
string = re.sub(r'timeout', '#timeout', string)
elif distro == "wifislax":
string = re.sub(r'vmlinuz',
@ -432,58 +418,66 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
gen.log('multibootusb EFI image already exist. Not copying...')
# Bug in the isolinux package
def commentout_gfxboot(input_text):
return re.sub(r'(ui\s+.*?gfxboot\.c32.*)$', r'# \1', input_text,
flags=re.I | re.MULTILINE)
def update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro):
"""
Update main multibootusb suslinux.cfg file after distro is installed.
Update main multibootusb syslinux.cfg file after distro is installed.
:return:
"""
if platform.system() == 'Linux':
os.sync()
log('Updating multibootusb config file...')
name_from_iso = iso_basename(iso_link)
name_of_iso = iso_name(iso_link)
_isolinux_bin_exists = isolinux_bin_exist(config.image_path)
_isolinux_bin_dir = isolinux_bin_dir(iso_link)
sys_cfg_file = os.path.join(usb_mount, "multibootusb", "syslinux.cfg")
install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link))
if os.path.exists(sys_cfg_file):
install_dir = os.path.join(usb_mount, "multibootusb", name_from_iso)
label = name_from_iso + ('' if _isolinux_bin_exists else ' via GRUB')
if os.path.exists(sys_cfg_file):
if distro == "hbcd":
if os.path.exists(os.path.join(usb_mount, "multibootusb", "menu.lst")):
_config_file = os.path.join(usb_mount, "multibootusb", "menu.lst")
config_file = open(_config_file, "w")
string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', _config_file)
string = re.sub(r'/HBCD', '/multibootusb/' + name_from_iso + '/HBCD', _config_file)
config_file.write(string)
config_file.close()
with open(sys_cfg_file, "a") as f:
f.write("#start " + iso_basename(config.image_path) + "\n")
f.write("LABEL " + iso_basename(config.image_path) + "\n")
f.write("MENU LABEL " + iso_basename(config.image_path) + "\n")
f.write("BOOT " + '/multibootusb/' + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/' + distro + '.bs' + "\n")
f.write("LABEL " + label + "\n")
f.write("MENU LABEL " + label + "\n")
f.write("BOOT " + '/multibootusb/' + name_from_iso + '/' + _isolinux_bin_dir.replace("\\", "/") + '/' + distro + '.bs' + "\n")
f.write("#end " + iso_basename(config.image_path) + "\n")
elif distro == "Windows":
if os.path.exists(sys_cfg_file):
config_file = open(sys_cfg_file, "a")
config_file.write("#start " + iso_basename(iso_link) + "\n")
config_file.write("LABEL " + iso_basename(iso_link) + "\n")
config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n")
config_file.write("#start " + name_from_iso + "\n")
config_file.write("LABEL " + label + "\n")
config_file.write("MENU LABEL " + label + "\n")
config_file.write("KERNEL chain.c32 hd0 1 ntldr=/bootmgr" + "\n")
config_file.write("#end " + iso_basename(iso_link) + "\n")
config_file.write("#end " + name_from_iso + "\n")
config_file.close()
elif distro == 'f4ubcd':
if os.path.exists(sys_cfg_file):
config_file = open(sys_cfg_file, "a")
config_file.write("#start " + iso_basename(iso_link) + "\n")
config_file.write("LABEL " + iso_basename(iso_link) + "\n")
config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n")
config_file.write("#start " + name_from_iso + "\n")
config_file.write("LABEL " + label + "\n")
config_file.write("MENU LABEL " + label + "\n")
config_file.write("KERNEL grub.exe" + "\n")
config_file.write('APPEND --config-file=/multibootusb/' + iso_basename(config.image_path) + '/menu.lst' + "\n")
config_file.write("#end " + iso_basename(iso_link) + "\n")
config_file.write("#end " + name_from_iso + "\n")
config_file.close()
elif distro == 'kaspersky':
if os.path.exists(sys_cfg_file):
config_file = open(sys_cfg_file, "a")
config_file.write("#start " + iso_basename(iso_link) + "\n")
config_file.write("LABEL " + iso_basename(iso_link) + "\n")
config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n")
config_file.write("#start " + name_from_iso + "\n")
config_file.write("LABEL " + label + "\n")
config_file.write("MENU LABEL " + label + "\n")
config_file.write("CONFIG " + '/multibootusb/' + iso_basename(config.image_path) + '/kaspersky.cfg' + "\n")
config_file.write("#end " + iso_basename(iso_link) + "\n")
config_file.write("#end " + name_from_iso + "\n")
config_file.close()
elif distro == 'grub4dos':
update_menu_lst()
@ -491,27 +485,30 @@ def update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro):
update_grub4dos_iso_menu()
else:
config_file = open(sys_cfg_file, "a")
config_file.write("#start " + iso_basename(iso_link) + "\n")
config_file.write("LABEL " + iso_basename(iso_link) + "\n")
config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n")
config_file.write("#start " + name_from_iso + "\n")
config_file.write("LABEL " + label + "\n")
config_file.write("MENU LABEL " + label + "\n")
if distro == "salix-live":
if os.path.exists(os.path.join(config.usb_mount, 'multibootusb', iso_basename(iso_link), 'boot', 'grub2-linux.img')):
if os.path.exists(
os.path.join(install_dir, 'boot', 'grub2-linux.img')):
config_file.write(
"LINUX " + '/multibootusb/' + iso_basename(iso_link) + '/boot/grub2-linux.img' + "\n")
"LINUX " + '/multibootusb/' + name_from_iso +
'/boot/grub2-linux.img' + "\n")
else:
config_file.write("BOOT " + '/multibootusb/' + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/' + distro + '.bs' + "\n")
config_file.write("BOOT " + '/multibootusb/' + name_from_iso + '/' + _isolinux_bin_dir.replace("\\", "/") + '/' + distro + '.bs' + "\n")
elif distro == "pclinuxos":
config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/isolinux/vmlinuz' + "\n")
config_file.write("kernel " + '/multibootusb/' + name_from_iso
+ '/isolinux/vmlinuz' + "\n")
config_file.write("append livecd=livecd root=/dev/rd/3 acpi=on vga=788 keyb=us vmalloc=256M nokmsboot "
"fromusb root=UUID=" + usb_uuid + " bootfromiso=/multibootusb/" +
iso_basename(iso_link) + "/" + iso_name(iso_link) + " initrd=/multibootusb/"
+ iso_basename(iso_link) + '/isolinux/initrd.gz' + "\n")
name_from_iso + "/" + name_of_iso + " initrd=/multibootusb/"
+ name_from_iso + '/isolinux/initrd.gz' + "\n")
elif distro == "memtest":
config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/BOOT/MEMTEST.IMG\n')
config_file.write("kernel " + '/multibootusb/' + name_from_iso + '/BOOT/MEMTEST.IMG\n')
elif distro == "sgrubd2" or config.distro == 'raw_iso':
config_file.write("LINUX memdisk\n")
config_file.write("INITRD " + "/multibootusb/" + iso_basename(iso_link) + '/' + iso_name(iso_link) + '\n')
config_file.write("INITRD " + "/multibootusb/" + name_from_iso + '/' + name_of_iso + '\n')
config_file.write("APPEND iso\n")
elif distro == 'ReactOS':
@ -529,30 +526,37 @@ def update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro):
elif distro == 'memdisk_img':
config_file.write(menus.memdisk_img_cfg(syslinux=True, grub=False))
else:
if isolinux_bin_exist(config.image_path) is True:
if _isolinux_bin_exists is True:
if distro == "generic":
distro_syslinux_install_dir = isolinux_bin_dir(iso_link)
if isolinux_bin_dir(iso_link) != "/":
distro_sys_install_bs = os.path.join(usb_mount, isolinux_bin_dir(iso_link)) + '/' + distro + '.bs'
distro_syslinux_install_dir = _isolinux_bin_dir
if _isolinux_bin_dir != "/":
distro_sys_install_bs = os.path.join(usb_mount, _isolinux_bin_dir) + '/' + distro + '.bs'
else:
distro_sys_install_bs = '/' + distro + '.bs'
else:
distro_syslinux_install_dir = install_dir
distro_syslinux_install_dir = distro_syslinux_install_dir.replace(usb_mount, '')
distro_sys_install_bs = distro_syslinux_install_dir + '/' + isolinux_bin_dir(iso_link) + '/' + distro + '.bs'
distro_sys_install_bs = distro_syslinux_install_dir + '/' + _isolinux_bin_dir + '/' + distro + '.bs'
distro_sys_install_bs = "/" + distro_sys_install_bs.replace("\\", "/") # Windows path issue.
if config.syslinux_version == '3':
config_file.write("CONFIG /multibootusb/" + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/isolinux.cfg\n')
config_file.write("APPEND /multibootusb/" + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '\n')
config_file.write("CONFIG /multibootusb/" + name_from_iso + '/' + _isolinux_bin_dir.replace("\\", "/") + '/isolinux.cfg\n')
config_file.write("APPEND /multibootusb/" + name_from_iso + '/' + _isolinux_bin_dir.replace("\\", "/") + '\n')
config_file.write("# Delete or comment above two lines using # and remove # from below line if "
"you get not a COM module error.\n")
config_file.write("#BOOT " + distro_sys_install_bs.replace("//", "/") + "\n")
else:
config_file.write("BOOT " + distro_sys_install_bs.replace("//", "/") + "\n")
config_file.write("#end " + iso_basename(iso_link) + "\n")
else:
# isolinux_bin does not exist.
config_file.write('Linux /multibootusb/grub/lnxboot.img\n')
config_file.write('INITRD /multibootusb/grub/core.img\n')
config_file.write('TEXT HELP\n')
config_file.write('Booting via syslinux is not supported. '
'Please boot via GRUB\n')
config_file.write('ENDTEXT\n')
config_file.write("#end " + name_from_iso + "\n")
config_file.close()
# Update extlinux.cfg file by copying updated syslinux.cfg
shutil.copy(os.path.join(usb_mount, 'multibootusb', 'syslinux.cfg'),
@ -612,15 +616,18 @@ def update_grub4dos_iso_menu():
f.write("#end " + iso_basename(config.image_path) + "\n")
class ConfigTweakerParam:
def __init__(self, distro_name, distro_path, persistence_size,
usb_uuid, usb_mount, usb_disk):
self.distro_name = distro_name
# 'iso_link' is also known as 'image_path'
def __init__(self, iso_link, distro_path, persistence_size,
usb_uuid, usb_mount, usb_disk, usb_fs_type):
self.iso_fname = os.path.split(iso_link)[1]
self.distro_name = os.path.splitext(self.iso_fname)[0]
assert distro_path[0] == '/'
self.distro_path = distro_path # drive relative
self.persistence_size = persistence_size
self.usb_uuid = usb_uuid
self.usb_mount = usb_mount
self.usb_disk = usb_disk
self.usb_fs_type = usb_fs_type
class ConfigTweaker:
@ -721,7 +728,8 @@ class ConfigTweaker:
def fullpath(self, subpath):
p = self.setup_params
return os.path.join(p.usb_mount, p.distro_path[1:], subpath)
return os.path.join(p.usb_mount, p.distro_path[1:],
subpath).replace('/', os.sep)
def file_is_installed(self, subpath):
return os.path.exists(self.fullpath(subpath))
@ -733,6 +741,26 @@ class ConfigTweaker:
with open(fp, errors='ignore') as f:
return f.read()
def extract_distroinfo_from_file(self, subpath, regex, distro_group,
version_group):
content = self.file_content(subpath)
if not content:
return None
m = re.compile(regex, re.I).search(content)
if not m:
return None
return (m.group(distro_group),
[int(x) for x in m.group(version_group).split('.')])
def extract_distroinfo_from_fname(self, which_dir, regex, distro_group,
version_group):
p = re.compile(regex, re.I)
for fname in os.listdir(self.fullpath(which_dir)):
m = p.match(fname)
if m:
return (m.group(distro_group),
[int(x) for x in m.group(version_group).split('.')])
return None
class PersistenceConfigTweaker(ConfigTweaker):
def __init__(self, pac_re, *args, **kw):
@ -818,8 +846,9 @@ class NoPersistenceTweaker(ConfigTweaker):
class GentooConfigTweaker(NoPersistenceTweaker):
def param_operations(self):
uuid_spec = 'UUID=%s' % self.setup_params.usb_uuid
ops = [
([add_or_replace_kv('real_root=', self.setup_params.usb_disk),
([add_or_replace_kv('real_root=', uuid_spec),
add_tokens('slowusb'),
add_or_replace_kv('subdir=', self.setup_params.distro_path),
remove_keys('cdroot_hash='),
@ -833,6 +862,10 @@ class GentooConfigTweaker(NoPersistenceTweaker):
],
starter_is_either('append', 'linux')),
]
fs_type = self.setup_params.usb_fs_type
if fs_type == 'vfat':
ops.append( (add_or_replace_kv('cdroot_type=', fs_type),
always) )
self.add_op_if_file_exists(
ops, add_or_replace_kv,
'loop=', ['liberte/boot/root-x86.sfs', 'image.squashfs'],
@ -840,13 +873,13 @@ class GentooConfigTweaker(NoPersistenceTweaker):
return ops
class CentosConfigTweaker(PersistenceConfigTweaker):
class FedoraConfigTweaker(PersistenceConfigTweaker):
def __init__(self, *args, **kw):
persistence_awareness_checking_re = re.compile(
r'^\s*(%s).*?\s(rd.live.overlay|overlay)=.+?' %
self.BOOT_PARAMS_STARTER, flags=re.I|re.MULTILINE)
super(CentosConfigTweaker, self).__init__(
super(FedoraConfigTweaker, self).__init__(
persistence_awareness_checking_re, *args, **kw)
def has_persistency_param(self, params):
@ -877,9 +910,9 @@ class CentosConfigTweaker(PersistenceConfigTweaker):
(add_or_replace_kv(
'inst.repo=',
'hd:UUID=%s:%s' % (
self.setup_params.usb_uuid,
self.setup_params.distro_path + '/' +
self.setup_params.distro_name + '.iso')),
self.setup_params.usb_uuid,
self.setup_params.distro_path + '/' +
self.setup_params.iso_fname)),
starter_is_either('append', 'linux')))
return ops
@ -896,8 +929,15 @@ class CentosConfigTweaker(PersistenceConfigTweaker):
class AntixConfigTweaker(NoPersistenceTweaker):
def param_operations(self):
content = self.file_content('version')
if content and 0 <= content.find('antiX-17'):
dinfo = self.extract_distroinfo_from_file(
'version', r'(antiX|MX)-(\d+\.\d+)', 1, 2)
if not dinfo:
dinfo = self.extract_distroinfo_from_file(
'boot/isolinux/isolinux.cfg', r'(antiX|MX)-(\d+\.\d+)', 1, 2)
if not dinfo:
dinfo = self.extract_distroinfo_from_fname(
'', r'(MX)-(\d+\.\d+).*', 1, 2)
if dinfo and 17<=dinfo[1][0]:
ops = [
add_or_replace_kv('buuid=', self.setup_params.usb_uuid),
add_or_replace_kv('bdir=',
@ -907,6 +947,11 @@ class AntixConfigTweaker(NoPersistenceTweaker):
self.setup_params.distro_path)
return [(ops, starter_is_either('append', 'APPEND', 'linux'))]
def post_process(self, s):
s = re.sub(r'^(\s*UI\s+(.*?gfxboot(\.c32|)))\s+(.*?)\s+(.*)$',
r'# \1 \4.renamed-to-avoid-lockup \5', s,
flags=re.I + re.MULTILINE)
return s
class SalixConfigTweaker(NoPersistenceTweaker):
@ -915,18 +960,26 @@ class SalixConfigTweaker(NoPersistenceTweaker):
return None
p = self.setup_params
for replacee, replacer in [
('iso_path', "%s/%s.iso" % (p.distro_path, p.distro_name)),
('initrd=', 'fromiso=%s/%s.iso initrd=' % (
p.distro_path, p.distro_name)),
('iso_path', "%s/%s" % (p.distro_path, p.iso_fname)),
('initrd=', 'fromiso=%s/%s initrd=' % (
p.distro_path, p.iso_fname)),
]:
content = content.replace(replacee, replacer)
return content
# salixlive-xfce-14.2.1 assumes that the installation media is
# labeled "LIVE" and the file tree is exploded at the root.
# (See /init for details.) Supporing it in harmony with installation
# of other distros is very hard to impossible. Do nothing here.
def param_operations(self):
return []
class WifislaxConfigTweaker(NoPersistenceTweaker):
def param_operations(self):
ops = [
(add_or_replace_kv('livemedia=','%s:%s/%s.iso' % (
(add_or_replace_kv('livemedia=','%s:%s/%s' % (
self.setup_params.usb_uuid, self.setup_params.distro_path,
self.setup_params.distro_name)),
self.setup_params.iso_fname)),
starter_is_either('append', 'linux'))]
return ops
@ -957,7 +1010,7 @@ def _test_tweak_objects():
'{usb-uuid}', usb_mount, usb_disk)
debian_tweaker = DebianConfigTweaker('debian', setup_params_no_persistence)
ubuntu_tweaker = UbuntuConfigTweaker('ubuntu', setup_params_no_persistence)
centos_tweaker = CentosConfigTweaker('centos', setup_params_no_persistence)
centos_tweaker = FedoraConfigTweaker('centos', setup_params_no_persistence)
salix_tweaker = SalixConfigTweaker('centos', setup_params_no_persistence)
# Test awareness on 'persistent'
@ -1038,7 +1091,7 @@ append foo"""
'debian', setup_params_persistent)
ubuntu_persistence_tweaker = UbuntuConfigTweaker(
'ubuntu', setup_params_persistent)
centos_persistence_tweaker = CentosConfigTweaker(
centos_persistence_tweaker = FedoraConfigTweaker(
'centos', setup_params_persistent)
print ("Testing if debian tweaker appends persistence parameters.")

@ -6,23 +6,28 @@
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
# under the terms of GNU General Public License, v.2 or above
import sys
import platform
import os
import shutil
import collections
import ctypes
import os
import platform
import shutil
import subprocess
import sys
import time
if platform.system()=='Linux':
import dbus
from . import config
from . import gen
from . import osdriver
if platform.system() == 'Linux':
from . import udisks
UDISKS = udisks.get_udisks(ver=None)
if platform.system() == 'Windows':
import psutil
import win32com.client
# import wmi
import pythoncom
class PartitionNotMounted(Exception):
@ -120,7 +125,6 @@ def list_devices(fixed=False):
except Exception as e:
gen.log(e)
import dbus
bus = dbus.SystemBus()
try:
# You should come here only if your system does'nt have udev installed.
@ -174,29 +178,14 @@ def list_devices(fixed=False):
devices.sort()
elif platform.system() == "Windows":
if fixed is True:
for drive in psutil.disk_partitions():
if 'cdrom' in drive.opts or drive.fstype == '':
# Skip cdrom drives or the disk with no filesystem
continue
devices.append(drive[0][:-1])
else:
try:
# Try new method using psutil. It should also detect USB 3.0 (but not tested by me)
for drive in psutil.disk_partitions():
if 'cdrom' in drive.opts or drive.fstype == '':
# Skip cdrom drives or the disk with no filesystem
continue
if 'removable' in drive.opts:
devices.append(drive[0][:-1])
except:
# Revert back to old method if psutil fails (which is unlikely)
oFS = win32com.client.Dispatch("Scripting.FileSystemObject")
oDrives = oFS.Drives
for drive in oDrives:
if drive.DriveType == 1 and drive.IsReady:
devices.append(drive)
volumes = osdriver.wmi_get_volume_info_all()
devices = []
for pdrive in osdriver.wmi_get_physicaldrive_info_all():
if (not fixed) and pdrive.MediaType != 'Removable Media':
continue
devices.append(osdriver.win_physicaldrive_to_listbox_entry(pdrive))
devices.extend([osdriver.win_volume_to_listbox_entry(d)
for d in volumes.get(pdrive.Index, [])])
if devices:
return devices
else:
@ -252,27 +241,29 @@ def details_udev(usb_disk_part):
str(usb_disk_part))
return None
if b'Extended' in fdisk_cmd_out:
mount_point = ''
uuid = ''
file_system = ''
vendor = ''
model = ''
label = ''
devtype = "extended partition"
elif b'swap' in fdisk_cmd_out:
detected_type = None
for keyword, ptype in [(b'Extended', 'extended partition'),
(b'swap', 'swap partition'),
(b'Linux LVM', 'lvm partition'),]:
if keyword in fdisk_cmd_out:
detected_type = ptype
break
if detected_type:
mount_point = ''
uuid = ''
file_system = ''
vendor = ''
model = ''
label = ''
devtype = "swap partition"
devtype = detected_type
elif device.get('DEVTYPE') == "partition":
uuid = device.get('ID_FS_UUID') or ""
file_system = device.get('ID_FS_TYPE') or ""
label = device.get('ID_FS_LABEL') or ""
mount_point = UDISKS.mount(usb_disk_part) or ""
remounted = []
mount_point = UDISKS.mount(usb_disk_part, remounted) or ""
if remounted and remounted[0]:
config.add_remounted(usb_disk_part)
mount_point = mount_point.replace('\\x20', ' ')
vendor = device.get('ID_VENDOR') or ""
model = device.get('ID_MODEL') or ""
@ -338,6 +329,7 @@ def details_udisks2(usb_disk_part):
else:
try:
mount_point = UDISKS.mount(usb_disk_part)
config.add_remounted(usb_disk_part)
except:
mount_point = "No_Mount"
try:
@ -355,9 +347,8 @@ def details_udisks2(usb_disk_part):
except:
model = str('No_Model')
if not mount_point == "No_Mount":
size_total = shutil.disk_usage(mount_point)[0]
size_used = shutil.disk_usage(mount_point)[1]
size_free = shutil.disk_usage(mount_point)[2]
size_total, size_used, size_free = \
shutil.disk_usage(mount_point)[:3]
else:
raise PartitionNotMounted(usb_disk_part)
@ -393,77 +384,114 @@ def gpt_device(dev_name):
:param dev_name:
:return: True if GPT else False
"""
if platform.system() == 'Windows':
partition, disk = gen.wmi_get_drive_info(dev_name)
is_gpt = partition.Type.startswith('GPT:')
gen.log('Device %s is a %s disk...' %
(dev_name, is_gpt and 'GPT' or 'MBR'))
config.usb_gpt = is_gpt
return is_gpt
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
is_gpt = osdriver.gpt_device(dev_name)
config.usb_gpt = is_gpt
gen.log('Device %s is a %s disk.' % (dev_name, is_gpt and 'GPT' or 'MBR'))
def win_disk_details(disk_drive):
"""
Populate and get details of an USB disk under windows. Minimum required windows version is Vista.
:param disk_drive: USB disk like 'G:'
:return: See the details(usb_disk_part) function for return values.
"""
pythoncom.CoInitialize()
vendor = 'Not_Found'
model = 'Not_Found'
devtype = 'Not_Found'
selected_usb_part = str(disk_drive)
oFS = win32com.client.Dispatch("Scripting.FileSystemObject")
d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part)))
selected_usb_device = d.DriveLetter
if d.DriveType == 1:
devtype = "Removable Disk"
elif d.DriveType == 2:
devtype = "Fixed Disk"
label = (d.VolumeName).strip()
if not label.strip():
label = "No_label"
mount_point = selected_usb_device + ":\\"
serno = "%X" % (int(d.SerialNumber) & 0xFFFFFFFF)
uuid = serno[:4] + '-' + serno[4:]
file_system = (d.FileSystem).strip()
size_total = shutil.disk_usage(mount_point)[0]
size_used = shutil.disk_usage(mount_point)[1]
size_free = shutil.disk_usage(mount_point)[2]
# The below code works only from vista and above. I have removed it as many people reported that the software
# was not working under windows xp. Even then, it is significantly slow if 'All Drives' option is checked.
# Removing the code doesn't affect the functionality as it is only used to find vendor id and model of the drive.
# c = wmi.WMI()
# for physical_disk in c.Win32_DiskDrive(InterfaceType="USB"):
# for partition in physical_disk.associators("Win32_DiskDriveToDiskPartition"):
# for logical_disk in partition.associators("Win32_LogicalDiskToPartition"):
# if logical_disk.Caption == disk_drive:
# vendor = (physical_disk.PNPDeviceID.split('&VEN_'))[1].split('&PROD_')[0]
# model = (physical_disk.PNPDeviceID.split('&PROD_'))[1].split('&REV_')[0]
def unmount(usb_disk):
UDISKS.unmount(usb_disk)
return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point,
'size_total': size_total, 'size_used': size_used, 'size_free': size_free,
'vendor': vendor, 'model': model, 'devtype': devtype}
class RemountError(Exception):
def __init__(self, caught_exception, *args, **kw):
super(RemountError, self).__init__(*args, **kw)
self.caught_exception = caught_exception
def __str__(self):
return "%s due to '%s'" % (
self.__class__.__name__, self.caught_exception)
def details(usb_disk_part):
class UnmountError(RemountError):
def __init__(self, *args, **kw):
super(UnmountError, self).__init__(*args, **kw)
class MountError(RemountError):
def __init__(self, *args, **kw):
super(MountError, self).__init__(*args, **kw)
class UnmountedContext:
def __init__(self, usb_disk, exit_callback):
self.usb_disk = usb_disk
self.exit_callback = exit_callback
self.is_relevant = platform.system() != 'Windows' and \
self.usb_disk[-1:].isdigit()
def assert_no_access(self):
p = subprocess.Popen(['lsof', self.usb_disk],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
output = p.communicate()
if len(output[0].strip()) != 0:
gen.log("Open handle exists.")
gen.log(output[0])
raise UnmountError(Exception('open handle exists.'))
def __enter__(self):
if not self.is_relevant:
return
self.assert_no_access()
try:
gen.log("Unmounting %s" % self.usb_disk)
os.sync() # This is needed because UDISK.unmount() can timeout.
UDISKS.unmount(self.usb_disk)
except dbus.exceptions.DBusException as e:
gen.log("Unmount of %s has failed." % self.usb_disk)
# This may get the partition mounted. Don't call!
# self.exit_callback(details(self.usb_disk))
raise UnmountError(e)
gen.log("Unmounted %s" % self.usb_disk)
return self
def __exit__(self, type_, value, traceback_):
if not self.is_relevant:
return
os.sync() # This should not be strictly necessary
time.sleep(1) # Yikes, mount always fails without this sleep().
try:
mount_point = UDISKS.mount(self.usb_disk)
config.add_remounted(self.usb_disk)
self.exit_callback(details(self.usb_disk))
except dbus.exceptions.DBusException as e:
raise MountError(e)
gen.log("Mounted %s" % (self.usb_disk))
def check_vfat_filesystem(usb_disk, result=None):
p = subprocess.Popen(['fsck.vfat', '-n', usb_disk],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
output = p.communicate()
gen.log("fsck.vfat -n returned %d" % p.returncode)
gen.log(b"fsck.vfat -n said:" + b'\n---\n'.join(f for f in output if f))
if result is not None:
result.append((p.returncode, output, 'fsck.vfat -n'))
return len(output[0].split(b'\n'))==3 and output[1]==b'' \
and p.returncode==0
def repair_vfat_filesystem(usb_disk, result=None):
for args, input_ in [
(['-a', usb_disk], None, ),
(['-r', usb_disk], b'1\ny\n', ),
]:
cmd = ['fsck.vfat'] + args
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stdin=subprocess.PIPE)
output = p.communicate(input=input_)
gen.log("%s returned %d" % (' '.join(cmd), p.returncode))
gen.log(b"It said:" + b'\n---\n'.join(f for f in output if f))
if result is not None:
result.append((p.returncode, output, ' '.join(cmd)))
return None
def details(disk_or_partition):
"""
Populate and get details of an USB disk.
:param usb_disk_part: USB disk. Example.. "/dev/sdb1" on Linux and "D:\" on Windows.
:param disk_or_partition: USB disk. Example.. "/dev/sdb1" on Linux and "D:\" on Windows.
:return: label == > returns name/label of an inserted USB device.
mount_point == > returns mount path of an inserted USB device.
uuid == > returns uuid of an inserted USB device.
@ -476,18 +504,20 @@ def details(usb_disk_part):
model == > returns the model name of the USB.
"""
assert usb_disk_part is not None
assert disk_or_partition is not None
details = {}
if platform.system() == 'Linux':
try:
details = details_udev(usb_disk_part)
details = details_udev(disk_or_partition)
except:
details = details_udisks2(usb_disk_part)
details = details_udisks2(disk_or_partition)
elif platform.system() == 'Windows':
details = win_disk_details(usb_disk_part)
if type(disk_or_partition) == int:
details = osdriver.wmi_get_physicaldrive_info_ex(disk_or_partition)
else:
details = osdriver.wmi_get_volume_info_ex(disk_or_partition)
return details

@ -0,0 +1,258 @@
import collections
import ctypes
import io
import pywintypes
import struct
import sys
import time
import win32api
import win32con
import win32file
import winerror
import winioctlcon
import wmi
from ctypes import wintypes
from functools import reduce
kernel32 = ctypes.WinDLL('kernel32') # , use_last_error=True)
kernel32.FindFirstVolumeW.restype = wintypes.HANDLE
kernel32.FindNextVolumeW.argtypes = (wintypes.HANDLE,
wintypes.LPWSTR,
wintypes.DWORD)
kernel32.FindVolumeClose.argtypes = (wintypes.HANDLE,)
def FindFirstVolume():
volume_name = ctypes.create_unicode_buffer(" " * 255)
h = kernel32.FindFirstVolumeW(volume_name, 255)
if h == win32file.INVALID_HANDLE_VALUE:
raise RuntimeError("FindFirstVolume() returned an invalid handle.")
return h, volume_name.value
def FindNextVolume(hSearch):
volume_name = ctypes.create_unicode_buffer(" " * 255)
if kernel32.FindNextVolumeW(hSearch, volume_name, 255) != 0:
return volume_name.value
else:
errno = ctypes.GetLastError()
if errno == winerror.ERROR_NO_MORE_FILES:
FindVolumeClose(hSearch)
return None
raise RuntimeError("FindNextVolume failed (%s)" % errno)
def FindVolumeClose(hSearch):
"""Close a search handle opened by FindFirstVolume, typically
after the last volume has been returned.
"""
if kernel32.FindVolumeClose(hSearch) == 0:
raise RuntimeError("FindVolumeClose() failed.")
def findAvailableDrives():
return [(d, win32file.GetDriveType(d)) for d in
win32api.GetLogicalDriveStrings().rstrip('\0').split('\0')]
def findNewDriveLetter(used_letters):
all_letters = set([chr(i) for i in range(ord('C'), ord('Z')+1)])
return min(list(all_letters - set([s[0] for s in used_letters])))
def _openHandle(path, bWriteAccess, bWriteShare,
logfunc = lambda s: None):
TIMEOUT, NUM_RETRIES = 10, 20
for retry_count in range(6):
try:
access_flag = win32con.GENERIC_READ | \
(bWriteAccess and win32con.GENERIC_WRITE or 0)
share_flag = win32con.FILE_SHARE_READ | \
(bWriteShare and win32con.FILE_SHARE_WRITE or 0)
handle = win32file.CreateFile(
path, access_flag, share_flag, None,
win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL, None)
nth = { 0: 'first', 1:'second', 2:'third'}
logfunc("Opening [%s]: success at the %s iteration" %
(path, nth.get(retry_count, '%sth' % (retry_count+1))))
return handle
except pywintypes.error as e:
logfunc('Exception=>'+str(e))
if NUM_RETRIES/3 < retry_count:
bWriteShare = True
time.sleep(TIMEOUT / float(NUM_RETRIES))
else:
raise RuntimeError("Couldn't open handle for %s." % path)
def _closeHandle(h):
x = win32file.CloseHandle(h)
assert x != win32file.INVALID_HANDLE_VALUE
return x
class openHandle:
def __init__(self, path, bWriteAccess, bWriteShare,
logfunc = lambda s: None):
self.path = path
self.bWriteAccess = bWriteAccess
self.bWriteShare = bWriteShare
self.logfunc = logfunc
self.h = None
def __enter__(self):
self.h = _openHandle(self.path, self.bWriteAccess, self.bWriteShare,
self.logfunc)
return self
def __exit__(self, type_, value, traceback_):
_closeHandle(self.h)
def assert_physical_drive(self):
if self.path.lower().find('physicaldrive')<0:
raise RuntimeError("Handle is not one of a physical drive.")
def LockPhysicalDrive(self):
self.assert_physical_drive()
lockPhysicalDrive(self.h, self.logfunc)
self.logfunc("Successfully locked '%s'" % self.path)
def ReadFile(self, size):
return win32file.ReadFile(self.h, size, None)
def WriteFile(self, b):
return win32file.WriteFile(self.h, b, None)
geometory_tuple = collections.namedtuple(
'DiskGeometory',
['number_of_cylinders', 'media_type', 'tracks_per_cylinder',
'sectors_per_track', 'bytes_per_sector', 'disk_size'])
def DiskGeometory(self):
self.assert_physical_drive()
o = win32file.DeviceIoControl(
self.h, winioctlcon.IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
None, 256, None)
return self.geometory_tuple(*struct.unpack('<qiiiiq', o[:32]))
MAX_SECTORS_TO_CLEAR=128
def ZapMBRGPT(self, disk_size, sector_size, add1MB):
self.assert_physical_drive()
# Implementation borrowed from rufus: https://github.com/pbatard/rufus
num_sectors_to_clear \
= (add1MB and 2048 or 0) + self.MAX_SECTORS_TO_CLEAR
zeroBuf = b'\0' * sector_size
for i in range(num_sectors_to_clear):
self.WriteFile(zeroBuf)
offset = disk_size - self.MAX_SECTORS_TO_CLEAR * sector_size
win32file.SetFilePointer(self.h, offset, win32con.FILE_BEGIN)
for i in range(num_sectors_to_clear):
self.WriteFile(zeroBuf)
# We need to append paddings as CREATE_DISK structure contains a union.
param = struct.pack('<IIIHH8s',
winioctlcon.PARTITION_STYLE_MBR, 0xdeadbeef,
0,0,0,b'abcdefgh')
win32file.DeviceIoControl(
self.h, winioctlcon.IOCTL_DISK_CREATE_DISK, param, 0, None)
def CopyFrom(self, src_file, progress_cb):
with openHandle(src_file, True, False,
lambda s:sys.stdout.write(s+'\n')) as src:
total_bytes = 0
hr, b = src.ReadFile(1024*1024)
# win32file.ReadFile() seems to have a bug in the interpretation
# of 'hr'. https://sourceforge.net/p/pywin32/bugs/689/
# The following loop condition is a workaround, which may not
# work properly.
while hr == 0 and len(b):
win32file.WriteFile(self.h, b, None)
total_bytes += len(b)
progress_cb(total_bytes)
hr, b = src.ReadFile(1024*1024)
def lockPhysicalDrive(handle, logfunc=lambda s: None):
try:
win32file.DeviceIoControl(
handle, winioctlcon.FSCTL_ALLOW_EXTENDED_DASD_IO,
None, 0, None)
except pywintypes.error as e:
logfunc('IO boundary checks diabled.')
for retry in range(20):
try:
win32file.DeviceIoControl(handle, winioctlcon.FSCTL_LOCK_VOLUME,
None, 0, None)
return
except pywintypes.error as e:
logfunc( str(e) )
time.sleep(1)
raise RuntimeError("Couldn't lock the Volume.")
def findVolumeGuids():
DiskExtent = collections.namedtuple(
'DiskExtent', ['DiskNumber', 'StartingOffset', 'ExtentLength'])
Volume = collections.namedtuple(
'Volume', ['Guid', 'MediaType', 'DosDevice', 'Extents'])
found = []
h, guid = FindFirstVolume()
while h and guid:
#print (guid)
#print (guid, win32file.GetDriveType(guid),
# win32file.QueryDosDevice(guid[4:-1]))
hVolume = win32file.CreateFile(
guid[:-1], win32con.GENERIC_READ,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None, win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL, None)
extents = []
driveType = win32file.GetDriveType(guid)
if driveType in [win32con.DRIVE_REMOVABLE, win32con.DRIVE_FIXED]:
x = win32file.DeviceIoControl(
hVolume, winioctlcon.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
None, 512, None)
instream = io.BytesIO(x)
numRecords = struct.unpack('<q', instream.read(8))[0]
fmt = '<qqq'
sz = struct.calcsize(fmt)
while 1:
b = instream.read(sz)
if len(b) < sz:
break
rec = struct.unpack(fmt, b)
extents.append( DiskExtent(*rec) )
vinfo = Volume(guid, driveType, win32file.QueryDosDevice(guid[4:-1]),
extents)
found.append(vinfo)
guid = FindNextVolume(h)
return found
def ZapPhysicalDrive(target_drive, get_volume_info_func, log_func):
with openHandle('\\\\.\\PhysicalDrive%d' % target_drive, True, False,
lambda s:sys.stdout.write(s+'\n')) as hDrive:
hDrive.LockPhysicalDrive()
geom = hDrive.DiskGeometory()
for v in get_volume_info_func(target_drive):
volume_path = '\\\\.\\'+v.DeviceID
log_func('Dismounting volume ' + volume_path)
with openHandle(volume_path, False, False) as h:
x = win32file.DeviceIoControl(
h.h, winioctlcon.FSCTL_DISMOUNT_VOLUME, None, None)
print ('FSCTL_DISMOUNT_VOLUME=>%s' % x)
x = win32file.DeleteVolumeMountPoint(volume_path+'\\')
log_func('DeleteVolumeMountPoint=>%s' % x)
else:
log_func('No volumes on %s' % target_drive)
add1MB = False
hDrive.ZapMBRGPT(geom.disk_size, geom.bytes_per_sector, add1MB)
if __name__ == '__main__':
# used_letters = [d for d in
# win32api.GetLogicalDriveStrings().rstrip('\0').split('\0')]
# print (used_letters)
# print (findNewDriveLetter(used_letters))
# TargetDrive = 2
# vinfo_list = [x for x in findVolumeGuids()
# if x.Extents and x.Extents[0].DiskNumber==TargetDrive]
TargetDrive = 5
with openHandle('\\\\.\\PhysicalDrive%d' % TargetDrive, True, False,
lambda s:sys.stdout.write(s+'\n')) as hDrive:
hDrive.CopyFrom('c:/Users/shinj/Downloads/salitaz-rolling_iso',
lambda b: None)

@ -0,0 +1,146 @@
import sys
import unittest
from unittest.mock import MagicMock as MM, patch, mock_open
sys.path = ['..'] + sys.path
from scripts import distro
from scripts import gen
class DistoDetection(unittest.TestCase):
def distro(self, isobin_exists, filelist_in_iso, input_text):
mock_isobin_exists = MM(return_value=isobin_exists)
mock_iso_list = MM(return_value=filelist_in_iso)
mock_os_walk = MM(return_value=[('/', [], ['grub.cfg'])])
@patch('scripts.iso.isolinux_bin_exist', mock_isobin_exists)
@patch('os.walk', mock_os_walk)
@patch('scripts._7zip.list_iso', mock_iso_list)
@patch('builtins.open', mock_open(read_data=input_text))
def test_when_isolinux_bin_is_available():
return (distro.distro('{iso-cfg-dir}', 'ProDOS2.iso'))
return test_when_isolinux_bin_is_available()
def test_filebased_detection(self):
test_inputs = [
('f4ubcd', '', ['f4ubcd']),
('memdisk_iso', '', []),
('memdisk_iso', 'debian-installer', ['casper']),
('debian-install', 'debian-installer', []),
('alpine', '', ['alpine-release']),
('memdisk_iso', '', ['']),
]
for expected_distro, input_texts, file_list in test_inputs:
for input_text in input_texts.split('|'):
distro = self.distro(True, file_list, input_text)
assert distro==expected_distro, (
"From \"%s&%s\", '%s' is expected but got '%s'" %
(input_text, file_list, expected_distro, distro))
def test_detection_with_isobin(self):
test_inputs = [
('parted-magic', 'pmagic|partedmagic', True),
('memdisk_iso', 'pmagic|partedmagic', False),
('debian', 'boot=live', True),
('memdisk_iso', 'boot=live', False),
('sms', 'sms.jpg|vector |autoexec', True),
('memdisk_iso', 'sms.jpg|vector |autoexec', False),
]
for expected_distro, input_texts, isobin_exists in test_inputs:
for input_text in input_texts.split('|'):
distro = self.distro(isobin_exists, [], input_text)
assert distro==expected_distro, (
"From \"%s&isobin=%s\", '%s' is expected but got '%s'" %
(input_text, isobin_exists, expected_distro, distro))
def test_detection_isobin_agnostic(self):
test_inputs = [
('ubcd', 'ubcd'),
('sgrubd2', 'Super Grub Disk'),
('hbcd', 'hbcd'),
('systemrescuecd', 'systemrescuecd'),
('mageialive', 'mgalive'),
('arch', 'archisolabel|misolabel|parabolaisolabel'),
('chakra', 'chakraisolabel'),
('kaos', 'kdeosisolabel'),
('memdisk_iso', 'grml'),
('grml', 'grml live-media-path=/dev/sda1'),
('solydx', 'solydx'),
('knoppix', 'knoppix'),
('fedora', 'root=live:CDLABEL=|redcore'),
('redhat', 'redhat'),
('slitaz', 'slitaz|dban |ophcrack|tinycore'
'|rescue.cpi|xpud|untangle|4mlinux|partition wizard'
'|android-x86.png|riplinux|lebel dummy'
'|http://pogostick.net/~pnh/ntpasswd/|AVG Rescue CD'
'|AntivirusLiveCD|lkrn|Nanolinux|OSForensics'
'|minimal Slackware|Slackware-HOWTO'),
('opensuse-install', 'class opensuse'),
('ubuntu', 'boot=casper'),
('wifislax', 'wifislax'),
('slax', 'slax'),
('antix', 'antix'),
('porteus', 'porteus'),
('pclinuxos', 'livecd=livecd|PCLinuxOS'),
('gentoo', 'looptype=squashfs|http://dee.su/liberte'),
('finnix', 'finnix'),
('wifiway', 'wifiway'),
('puppy', 'puppy|quirky|fatdog|slacko|xenialpup'),
('ipcop', 'ipcop'),
('ipfire', 'ipfire'),
('zenwalk', 'zenwalk|slack|salix'),
('salix-live', 'zenwalk live|live slack|live salix'),
('zenwalk', 'zenwalk|slack|salix'),
('puppy', 'zenwalk slacko|slacko slack'),
('ubuntu-server', 'ubuntu server'),
('centos', 'root=live:CDLABEL=CentOS'),
('centos-install', 'Install CentOS'),
('centos', 'CentOS'),
('trinity-rescue', 'Trinity Rescue Kit'),
('kaspersky', 'http://support.kaspersky.com'),
('alt-linux', 'ALT Linux'),
('Windows', 'Sergei Strelec'),
('ReactOS', 'ReactOS'),
('fsecure', 'fsecure'),
('pc-unlocker', 'default rwp'),
('pc-tool', '/system/stage1'),
('grub2only', 'vba32rescue'),
('rising-av', 'BOOT_IMAGE=rising'),
('Avira-RS', 'Avira Rescue System'),
('insert', 'BOOT_IMAGE=insert'),
]
for expected_distro, input_texts in test_inputs:
for input_text in input_texts.lower().split('|'):
distro = self.distro(False, [], input_text)
assert distro==expected_distro, (
"From \"%s\", '%s' is expected but got '%s'" %
(input_text, expected_distro, distro))
def test_distro_detection(self):
def os_path_exists(f):
if f.endswith('multibootusb.log'):
return False
return True
os_path_exists_mock = MM()
log_mock = MM()
@patch('os.path.exists', os_path_exists)
@patch('scripts.distro.log', log_mock)
def _():
fn = distro.detect_iso_from_file_list
assert fn(['BOOT.wim', 'Sources']) == 'Windows'
assert fn(['BOOT.wim', 'Sause']) is None
assert fn(['config.isoclient', 'foo']) == 'opensuse'
assert fn(['bar', 'dban', 'foo']) == 'slitaz'
assert fn(['memtest.img']) == 'memtest'
assert fn(['mt86.png','isolinux']) == 'raw_iso'
assert fn(['menu.lst']) == 'grub4dos'
assert fn(['bootwiz.cfg', 'bootmenu_logo.png']) == \
'grub4dos_iso'
_()
if __name__ == '__main__':
unittest.main()
Loading…
Cancel
Save