Unmount target partition when running syslinux/dd/qemu to prevent filesystem corruption.

Fix master allocation table and backup allocation table going out of sync after syslinuxing the target.
Detect lvm partition as well.
pull/340/head
Shinji Suzuki 6 years ago committed by shinji-s
parent 10bc802a18
commit fc56319187

@ -42,6 +42,7 @@ editors_linux = ["xdg-open", "gedit", "kate", "kwrite"]
editors_win = ["notepad++.exe", "notepad.exe"]
imager_usb_disk = []
mounted_partitions = []
debug = False
@ -59,3 +60,10 @@ qemu_use_builtin = True # Relevant on Windows only
# 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
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

@ -17,6 +17,7 @@ import tempfile
import re
import ctypes
from . import config
def scripts_dir_path():
return os.path.dirname(os.path.realpath(__file__))
@ -190,10 +191,8 @@ def copy_mbusb_dir_usb(usb_disk):
:param usb_mount_path: Path to USB mount.
:return:
"""
# from .iso import iso_size
from .usb import details
usb_details = details(usb_disk)
usb_details = config.usb_details
usb_mount_path = usb_details['mount_point']
result = ''
if not os.path.exists(os.path.join(usb_mount_path, "multibootusb")):
@ -299,9 +298,8 @@ def strings(filename, _min=4):
def size_not_enough(iso_link, usb_disk):
from .iso import iso_size
from .usb import details
isoSize = iso_size(iso_link)
usb_details = details(usb_disk)
usb_details = config.usb_details
usb_size = usb_details['size_free']
return bool(isoSize > usb_size)

@ -163,7 +163,7 @@ def install_progress():
"""
from . import progressbar
usb_details = details(config.usb_disk)
usb_details = config.usb_details
config.usb_mount = usb_details['mount_point']
usb_size_used = usb_details['size_used']
thrd = threading.Thread(target=install_distro, name="install_progress")

@ -1,11 +1,10 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Name: mbusb_gui.py
# Purpose: Module to handle multibootusb through gui
# Authors: Sundar
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
# Name: mbusb_gui.py
# Purpose: Module to handle multibootusb through gui
# 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
import os
import platform
import sys
@ -14,6 +13,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets
import subprocess
import time
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
@ -45,7 +48,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.ui.setupUi(self)
self.ui.tabWidget.setCurrentIndex(0)
# self.qemu = Qemu()
# self.qemu = Qemu()
self.ui.label_persistence_value.setVisible(False)
self.ui.label_persistence.setVisible(False)
@ -60,12 +63,12 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.ui.action_Quit.triggered.connect(self.on_close_Click)
self.ui.action_About.triggered.connect(self.onAboutClick)
self.ui.button_browse_image.clicked.connect(self.browse_iso)
# self.ui.combo_drives.activated[str].connect(self.onComboChange)
# self.ui.combo_drives.activated[str].connect(self.onComboChange)
self.ui.combo_drives.currentIndexChanged.connect(self.onComboChange)
self.ui.button_install_distro.clicked.connect(self.onCreateClick)
self.ui.button_uninstall_distro.clicked.connect(self.OnUninstallClick)
self.ui.slider_persistence.valueChanged.connect(self.update_slider_text)
# self.ui.slider_persistence.sliderReleased.connect(self.ui_update_persistence)
# self.ui.slider_persistence.sliderReleased.connect(self.ui_update_persistence)
# ISO Imager Tab
self.ui.button_write_image_to_disk.clicked.connect(self.dd_write)
@ -77,9 +80,9 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
# QEMU Tab
self.ui.boot_iso_qemu.clicked.connect(self.on_Qemu_Boot_iso_Click)
self.ui.boot_usb_qemu.clicked.connect(self.on_Qemu_Boot_usb_Click)
# 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.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())))
# Update progressbar and status (Main ISO install)
self.progress_thread_install = GuiInstallProgress()
self.progress_thread_install.finished.connect(self.install_syslinux)
@ -120,6 +123,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
"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)
elif reply == QtWidgets.QMessageBox.Yes:
@ -148,24 +152,17 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
if config.usb_disk:
log("Selected device " + config.usb_disk)
config.usb_details = usb.details(config.usb_disk)
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', ""))
self.check_remount()
self.update_target_info()
# Get the GPT status of the disk and store it on a variable
usb.gpt_device(config.usb_disk)
self.update_list_box(config.usb_disk)
self.ui_update_persistence()
self.ui.usb_mount.setText(config.usb_mount)
else:
self.ui.usb_dev.clear()
self.ui.usb_vendor.clear()
@ -174,7 +171,6 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.ui.usb_mount.clear()
self.ui.usb_type.clear()
self.ui.usb_fs.clear()
log("No USB disk found...")
def onRefreshClick(self):
@ -187,13 +183,11 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
detected_devices = usb.list_devices(fixed=True)
else:
detected_devices = usb.list_devices()
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):
if detected_devices:
for device in detected_devices:
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):
"""
@ -247,7 +241,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
# 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,
@ -293,9 +287,9 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
log("File not selected...")
def ui_update_persistence(self):
# log("===== config.persistence_available = " + str(config.persistence_available))
# log("===== config.persistence_max_size = " + str(config.persistence_max_size))
# log("===== config.persistence = " + str(config.persistence))
# log("===== config.persistence_available = " + str(config.persistence_available))
# log("===== config.persistence_max_size = " + str(config.persistence_max_size))
# log("===== config.persistence = " + str(config.persistence))
if config.persistence_available and config.persistence_max_size:
self.ui.label_persistence_value.setVisible(True)
self.ui.label_persistence.setVisible(True)
@ -306,7 +300,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.ui.slider_persistence.setTickInterval(10)
self.ui.slider_persistence.setSingleStep(10)
self.ui.slider_persistence.setMaximum(config.persistence_max_size / 1024 / 1024)
# log("===== getMaximum = " + self.ui.slider_persistence.getMaximum()
# log("===== getMaximum = " + self.ui.slider_persistence.getMaximum()
else:
self.ui.label_persistence_value.setEnabled(False)
self.ui.label_persistence.setEnabled(False)
@ -416,9 +410,6 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
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)
@ -523,6 +514,13 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.ui_enable_controls()
def onCreateClick(self):
self.ui_disable_controls()
try:
self.onCreateClick_impl()
finally:
self.ui_enable_controls()
def onCreateClick_impl(self):
"""
Main function to create bootable USB disk.
:param usb_disk: ComboBox text as detected USB disk.
@ -530,40 +528,56 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
:return:
"""
self.ui_disable_controls()
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:
QtWidgets.QMessageBox.information(
self,"No Device...",
"No USB device found.\n\nInsert USB and "
"use Refresh USB button to detect USB.")
return
if 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(config.usb_disk)['mount_point'] == 'No_Mount':
QtWidgets.QMessageBox.information(
self, "No ISO...",
"No ISO found.\n\nPlease select an ISO.")
return
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
if 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)\n'
'from the drop down list.')
return
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:
persistence.detect_missing_tools(
config.distro))
return
if not self.check_remount():
self.update_target_info()
return
if 1: # Redundant if to keep indentation level.
# 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
usb_details = usb.details(config.usb_disk)
# 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)
@ -576,61 +590,63 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
log("MultiBoot Install: Disk model: " + usb_details['model'])
log("MultiBoot Install: ISO file: " + iso_name(config.image_path))
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()
if not os.path.exists(config.image_path):
return
# 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
log("MultiBoot Install: Distro type detected: " + config.distro)
if os.path.exists(
os.path.join(config.usb_mount, "multibootusb", iso_basename(config.image_path))):
QtWidgets.QMessageBox.information(self, 'Already exists...',
os.path.basename(
config.image_path) + ' is already installed.')
return
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()
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)
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:
pass
# Added to refresh usb disk remaining size after distro installation
# self.update_gui_usb_info()
def dd_finished(self):
"""
@ -657,7 +673,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
"""
self.ui.progressbar.setValue(0)
self.ui.statusbar.showMessage("Status: Idle")
# FIXME self.ui.lineEdit_3.clear()
# FIXME self.ui.lineEdit_3.clear()
self.ui.button_browse_image.setEnabled(False)
self.ui.combo_drives.setEnabled(False)
# FIXME self.ui.pushbtn_imager_refreshusb.setEnabled(False)
@ -698,10 +714,10 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
" size is larger than the size of " + 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.
# QtWidgets.QMessageBox.information(self, "Windows Explorer", "Windows Explorer is running\n"
# "You need to close it before writing ISO "
# "image to disk...")
# # Check if windows explorer is running and inform user to close it.
# QtWidgets.QMessageBox.information(self, "Windows Explorer", "Windows Explorer is running\n"
# "You need to close it before writing ISO "
# "image to disk...")
else:
reply = QtWidgets.QMessageBox.question \
(self, 'Review selection',
@ -724,6 +740,35 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
"""
self.close()
def check_remount(self):
if config.usb_details['file_system'] != 'vfat':
return True
with UnmountedContext(config.usb_disk,
config.update_usb_mount) as m:
pass
if m.success:
return True
QtWidgets.QMessageBox.critical(
self,"Remount failed.",
"Could not remount '{0}'. "
"Please make sure no process has open "
"handle(s) to previously mounted subtree "
"and detect drives again."
.format(config.usb_disk))
return False
def update_target_info(self):
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', ""))
def closeEvent(self, event):
"""
To capture the main close event.
@ -746,7 +791,6 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
log("Close event cancelled.")
event.ignore()
class GuiInstallProgress(QtCore.QThread):
"""
Update GUI thread during install.
@ -902,8 +946,8 @@ def show_admin_info():
def main_gui():
app = QtWidgets.QApplication(sys.argv)
# ui_about = Ui_About()
# ui = Ui_MainWindow()
# ui_about = Ui_About()
# ui = Ui_MainWindow()
if platform.system() == 'Linux' and os.getuid() != 0:
show_admin_info()

@ -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

@ -9,13 +9,14 @@
# 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 .usb import UnmountedContext
class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
"""
@ -27,35 +28,64 @@ 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,'use_kvm', True):
options.append('-enable-kvm')
cmd = [qemu] + options + qemu_more_params
try:
old_wd = os.getcwd()
new_wd = os.path.split(qemu)[0]
os.chdir(new_wd)
try:
with UnmountedContext(config.usb_disk,
config.update_usb_mount):
log("Executing ==> %s" % cmd)
out = subprocess.check_output(cmd)
if out:
log('%s => %s' % (cmd, out))
finally:
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):
"""
@ -64,84 +94,47 @@ class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
:return:
"""
if not config.usb_disk:
QtWidgets.QMessageBox.information(self, 'No disk...', 'No USB disk selected.\n\nPlease choose a disk first.')
QtWidgets.QMessageBox.information(
self, 'No disk...',
'No USB disk selected.\n\nPlease choose a disk first.')
return
if platform.system() == "Windows":
disk_number = get_physical_disk_number(config.usb_disk)
qemu_more_params = ['-L', '.', '-boot', 'c', '-hda',
'//./PhysicalDrive' + str(disk_number)]
elif platform.system() == "Linux":
qemu_more_params = ['-hda', config.usb_disk.rstrip('0123456789'),
'-vga', 'std']
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
assert False, "Unknown platform '%s'" % platform.system()
self.run_qemu(self.qemu_usb_ram(), qemu_more_params,
"ERROR: USB Boot: qemu not found!",
'Error...', 'Error booting USB\nUnable to start QEMU.')
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:
options = []
ram = self.qemu_usb_ram()
if ram:
options.extend(['-m', ram])
if getattr(config, 'qemu_use_haxm', False):
options.extend(['-accel', 'hax'])
bios = getattr(config, 'qemu_bios', None)
if bios:
options.extend(['-bios', bios])
optstr = (' ' + ' '.join(options)) if options else ''
if platform.system() == "Windows":
disk_number = get_physical_disk_number(qemu_usb_disk)
qemu_exe = find_qemu_exe()
qemu_dir = os.path.split(qemu_exe)[0]
parent_dir = os.getcwd()
os.chdir(qemu_dir)
cmd = quote(qemu_exe) + ' -L . -boot c' + optstr \
+ ' -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 + \
'"' + optstr + ' -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.')
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":
@ -166,7 +159,7 @@ def find_qemu_exe():
exe_name = 'qemu-system-x86_64.exe'
if hasattr(config, 'qemu_exe_path'):
return config.qemu_exe_path
if (not hasattr(config, 'qemu_use_builtin')) or not config.qemu_use_builtin:
if not getattr(config, 'qemu_use_builtin', True):
for wellknown_path in [
r'c:\Program Files\qemu',
r'd:\Program Files\qemu',

@ -90,6 +90,32 @@ 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)
log("\nExecuting ==> %s\n" % syslinux_cmd)
config.status_text = 'Installing default syslinux version 4...'
if subprocess.call(syslinux_cmd) == 0:
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 syslinux_default(usb_disk):
"""
@ -102,9 +128,9 @@ 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)
_windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe"))
@ -132,25 +158,7 @@ def syslinux_default(usb_disk):
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
return linux_install_default_bootsector(usb_disk, mbr_install_cmd)
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...'
@ -189,6 +197,52 @@ def syslinux_default(usb_disk):
return False
def build_distro_bootsector(usb_disk, option,
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, option, distro_syslinux_install_dir)
if tmp_bs:
shutil.copy(tmp_bs, distro_sys_install_bs)
def build_distro_bootsector_impl(usb_disk, option,
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] + option + [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")
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")
print ("RETURNING tmp_bs_File")
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.
@ -214,12 +268,19 @@ 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)
@ -233,38 +294,20 @@ def syslinux_distro_dir(usb_disk, iso_link, distro):
options.append('-d')
option = ' ' + ' '.join(options) + ' '
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, option,
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:
@ -288,6 +331,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.

@ -69,9 +69,7 @@ class UDisks(object):
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([])
@ -143,7 +141,6 @@ class UDisks2(object):
'options':','.join(mount_options)
},
dbus_interface=self.FILESYSTEM))
print(mp)
except:
# May be already mounted, check
mp = node_mountpoint(str(device_node_path))

@ -21,7 +21,7 @@ def install_distro_list():
List all distro names installed by previous install
:return: List of distro names as list
"""
usb_details = details(config.usb_disk)
usb_details = config.usb_details
config.usb_mount = usb_details['mount_point']
sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg")
@ -54,7 +54,7 @@ def delete_frm_file_list(iso_file_list, uninstall_distro_dir_name):
:param config.uninstall_distro_dir_name: Directory where the distro is installed
:return:
"""
usb_details = details(config.usb_disk)
usb_details = config.usb_details
usb_mount = usb_details['mount_point']
if iso_file_list is not None:
for f in iso_file_list:
@ -104,7 +104,7 @@ def do_uninstall_distro(target_distro, uninstall_distro_dir_name):
:param uninstall_distro_dir_name: Directory where the distro is installed
:return:
"""
usb_details = details(config.usb_disk)
usb_details = config.usb_details
usb_mount = usb_details['mount_point']
if platform.system() == 'Linux':
@ -229,7 +229,7 @@ def uninstall_progress():
:return:
"""
from . import progressbar
usb_details = details(config.usb_disk)
usb_details = config.usb_details
usb_mount = usb_details['mount_point']
if platform.system() == 'Linux':
os.sync()

@ -126,7 +126,7 @@ def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0):
Main function to modify/update distro specific strings on distro config files.
:return:
"""
usb_details = details(usb_disk)
usb_details = config.usb_details
usb_mount = usb_details['mount_point']
usb_uuid = usb_details['uuid']
usb_label = usb_details['label']

@ -6,13 +6,18 @@
# 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
if platform.system() == 'Linux':
@ -114,7 +119,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.
@ -240,28 +244,28 @@ def details_udev(usb_disk_part):
out.append(l)
# filter out non-relevant partition info
fdisk_cmd_out = b'\n'.join(out)
print("FDISK CMDOUT=>%s" % fdisk_cmd_out)
except subprocess.CalledProcessError:
gen.log("ERROR: fdisk failed on disk/partition (%s)" %
str(usb_disk_part))
return None
if b'Extended' in fdisk_cmd_out:
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 = "extended partition"
elif b'swap' in fdisk_cmd_out:
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 ""
@ -291,7 +295,7 @@ def details_udev(usb_disk_part):
' | grep "^Disk /" | sed -re "s/.*\s([0-9]+)\sbytes.*/\\1/"'
size_total = subprocess.check_output(fdisk_cmd, shell=True).strip()
size_used = ""
size_free = ""
size_free = 0
mount_point = ""
return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point,
@ -349,13 +353,12 @@ 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:
size_total = str('No_Mount')
size_used = str('No_Mount')
size_free = str('No_Mount')
size_free = 0
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,
@ -410,6 +413,48 @@ def gpt_device(dev_name):
gen.log('Device ' + dev_name + ' is a GPT disk...')
return True
def unmount(usb_disk):
UDISKS.unmount(usb_disk)
class UnmountedContext:
def __init__(self, usb_disk, exit_callback):
self.usb_disk = usb_disk
self.exit_callback = exit_callback
self.unmounted = False
self.success = False
def __enter__(self):
if platform.system() == 'Windows':
return self
if not(self.usb_disk and self.usb_disk[-1:].isdigit()):
return self
try:
gen.log("Unmounting %s" % self.usb_disk)
UDISKS.unmount(self.usb_disk)
except dbus.exceptions.DBusException:
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))
return self
self.unmounted = True
gen.log("Unmounted %s" % self.usb_disk)
os.sync()
return self
def __exit__(self, type, value, traceback_):
if not self.unmounted:
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)
except dbus.exceptions.DBusException:
pass
else:
self.success = True
self.exit_callback(details(self.usb_disk))
gen.log("Mounted %s" % (self.usb_disk))
def win_disk_details(disk_drive):
"""
@ -436,9 +481,7 @@ def win_disk_details(disk_drive):
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]
size_total, size_used, size_free = shutil.disk_usage(mount_point)[:3]
# 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.
@ -456,6 +499,27 @@ def win_disk_details(disk_drive):
'vendor': vendor, 'model': model, 'devtype': devtype}
def check_vfat_filesystem(usb_disk):
p = subprocess.Popen(['fsck.vfat', '-n', usb_disk],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
output = p.communicate()
gen.log("fsck -n returned %d" % p.returncode)
gen.log(b"fsck -n said:" + b'\n---\n'.join(f for f in output if f))
gen.log(output)
gen.log('len=>%d' % len(output[0].split(b'\n')))
return len(output[0].split(b'\n'))==3 and output[1]==b'' \
and p.returncode==0
def repair_vfat_filesystem(usb_disk):
p = subprocess.Popen(['fsck.vfat', '-r', usb_disk],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
output = p.communicate(input=b'1\ny\n')
gen.log("fsck -r returned %d" % p.returncode)
gen.log(b"fsck -r said:" + b'\n---\n'.join(f for f in output if f))
def details(usb_disk_part):
"""
Populate and get details of an USB disk.

Loading…
Cancel
Save