Add gui for fsck.

Fix bug that re-enables gui controls before distro installation finishes.
Have udisks not attempt mount if the target is already mounted.
pull/340/head
shinji-s 6 years ago
parent fc56319187
commit 6588648fc5

@ -184,33 +184,44 @@ if __name__ == '__main__':
sys.exit()
'''
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:
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:
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__':
try:
main()
finally:
from scripts import usb
for p in config.remounted_partitions:
log('Unmouting %s at exit' % p)
usb.unmount(p)

@ -42,11 +42,11 @@ editors_linux = ["xdg-open", "gedit", "kate", "kwrite"]
editors_win = ["notepad++.exe", "notepad.exe"]
imager_usb_disk = []
mounted_partitions = []
remounted_partitions = []
debug = False
# protected_drives = ['C:','D:','E:', '/dev/sda', '/dev/sdb']
# 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.
@ -60,10 +60,14 @@ 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
# 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)

@ -945,6 +945,56 @@
</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="QHBoxLayout" name="horizontalLayout_6">
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>242</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="run_fsck_repair">
<property name="text">
<string>Repair Filesystem</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="run_fsck_check">
<property name="text">
<string>Check Filesystem Integrity</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>241</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>

@ -238,6 +238,10 @@ class Ui_MainWindow(object):
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.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, 0, 1, 3)
self.horizontalLayout_7.addLayout(self.gridLayout_9)
self.tabWidget.addTab(self.tab_imager, "")
self.tab_syslinux = QtWidgets.QWidget()
@ -361,6 +365,22 @@ 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.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_fsck)
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
spacerItem14 = QtWidgets.QSpacerItem(242, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_6.addItem(spacerItem14)
self.run_fsck_repair = QtWidgets.QPushButton(self.tab_fsck)
self.run_fsck_repair.setObjectName("run_fsck_repair")
self.horizontalLayout_6.addWidget(self.run_fsck_repair)
self.run_fsck_check = QtWidgets.QPushButton(self.tab_fsck)
self.run_fsck_check.setObjectName("run_fsck_check")
self.horizontalLayout_6.addWidget(self.run_fsck_check)
spacerItem15 = QtWidgets.QSpacerItem(241, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_6.addItem(spacerItem15)
self.tabWidget.addTab(self.tab_fsck, "")
self.verticalLayout_7.addWidget(self.tabWidget)
self.progressbar = QtWidgets.QProgressBar(self.centralwidget)
self.progressbar.setProperty("value", 0)
@ -421,6 +441,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 +472,9 @@ 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.run_fsck_repair.setText(_translate("MainWindow", "Repair Filesystem"))
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"))

@ -5,6 +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
@ -12,6 +14,7 @@ import signal
from PyQt5 import QtCore, QtGui, QtWidgets
import subprocess
import time
import traceback
import webbrowser
if platform.system() == 'Linux':
@ -80,9 +83,15 @@ 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())))
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)
@ -101,6 +110,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()
@ -153,7 +167,6 @@ 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)
self.check_remount()
self.update_target_info()
# Get the GPT status of the disk and store it on a variable
@ -317,21 +330,23 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
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
@ -419,6 +434,38 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
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.
@ -514,11 +561,13 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self.ui_enable_controls()
def onCreateClick(self):
installing = False
self.ui_disable_controls()
try:
self.onCreateClick_impl()
installing = self.onCreateClick_impl()
finally:
self.ui_enable_controls()
if not installing:
self.ui_enable_controls()
def onCreateClick_impl(self):
"""
@ -534,13 +583,13 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
self,"No Device...",
"No USB device found.\n\nInsert USB and "
"use Refresh USB button to detect USB.")
return
return False
if not config.image_path:
log("No ISO selected.")
QtWidgets.QMessageBox.information(
self, "No ISO...",
"No ISO found.\n\nPlease select an ISO.")
return
return False
usb_details = config.usb_details
if usb_details['mount_point'] == 'No_Mount':
@ -550,9 +599,9 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
"USB disk is not mounted.\n"
"Please mount USB disk and press refresh "
"USB button.")
return
return False
if platform.system() == 'Linux' and \
config.usb_disk[-1].isdigit() is False:
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(
@ -562,17 +611,17 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
'Please select the partition (ending '
'with a digit eg. /dev/sdb1)\n'
'from the drop down list.')
return
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))
return
return False
if not self.check_remount():
self.update_target_info()
return
return False
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.
@ -591,7 +640,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
log("MultiBoot Install: ISO file: " + iso_name(config.image_path))
if not os.path.exists(config.image_path):
return
return False
# self.ui.image_path.clear()
if not config.distro:
QtWidgets.QMessageBox.information(
@ -600,7 +649,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
' is not supported at the moment.\n'
'Please email this issue to '
'feedback.multibootusb@gmail.com')
return
return False
log("MultiBoot Install: Distro type detected: " + config.distro)
if os.path.exists(
@ -608,7 +657,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
QtWidgets.QMessageBox.information(self, 'Already exists...',
os.path.basename(
config.image_path) + ' is already installed.')
return
return False
config.persistence = self.ui.slider_persistence.value() * 1024 * 1024
@ -618,6 +667,7 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
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
else:
if config.distro == 'memdisk_iso':
reply = QtWidgets.QMessageBox.question(self, 'Review selection...',
@ -642,8 +692,9 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
copy_mbusb_dir_usb(config.usb_disk)
config.process_exist = True
self.progress_thread_install.start()
return True
elif reply == QtWidgets.QMessageBox.No:
pass
return False
# Added to refresh usb disk remaining size after distro installation
# self.update_gui_usb_info()
@ -740,22 +791,26 @@ class AppGui(qemu.Qemu, Imager, QtWidgets.QMainWindow, Ui_MainWindow):
"""
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
with UnmountedContext(config.usb_disk,
config.update_usb_mount) as m:
pass
if m.success:
try:
with UnmountedContext(config.usb_disk,
self.update_usb_mount) as m:
pass
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
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):
config.persistence_max_size = persistence.max_disk_persistence(config.usb_disk)

@ -16,7 +16,7 @@ from PyQt5 import QtWidgets
from .gui.ui_multibootusb import Ui_MainWindow
from .gen import *
from . import config
from .usb import UnmountedContext
from . import usb
class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
"""
@ -56,8 +56,8 @@ class Qemu(QtWidgets.QMainWindow, Ui_MainWindow):
new_wd = os.path.split(qemu)[0]
os.chdir(new_wd)
try:
with UnmountedContext(config.usb_disk,
config.update_usb_mount):
with usb.UnmountedContext(config.usb_disk,
config.update_usb_mount):
log("Executing ==> %s" % cmd)
out = subprocess.check_output(cmd)
if out:

@ -51,18 +51,17 @@ 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)
@ -130,23 +129,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))
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):

@ -244,7 +244,6 @@ 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)" %
@ -270,7 +269,10 @@ def details_udev(usb_disk_part):
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 ""
@ -336,6 +338,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:
@ -416,18 +419,43 @@ def gpt_device(dev_name):
def unmount(usb_disk):
UDISKS.unmount(usb_disk)
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)
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.unmounted = False
self.success = False
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 platform.system() == 'Windows':
return self
if not(self.usb_disk and self.usb_disk[-1:].isdigit()):
return self
if not self.is_relevant:
return
self.assert_no_access()
try:
gen.log("Unmounting %s" % self.usb_disk)
UDISKS.unmount(self.usb_disk)
@ -435,24 +463,22 @@ class UnmountedContext:
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
raise UnmountError(e)
gen.log("Unmounted %s" % self.usb_disk)
os.sync()
return self
def __exit__(self, type, value, traceback_):
if not self.unmounted:
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)
except dbus.exceptions.DBusException:
pass
else:
self.success = True
self.exit_callback(details(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))
@ -499,26 +525,33 @@ def win_disk_details(disk_drive):
'vendor': vendor, 'model': model, 'devtype': devtype}
def check_vfat_filesystem(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 -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')))
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):
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 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(usb_disk_part):
"""

Loading…
Cancel
Save