aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG11
-rwxr-xr-xlib/isodumper.py196
-rwxr-xr-xlib/raw_format.py39
-rw-r--r--share/isodumper/isodumper.glade141
4 files changed, 306 insertions, 81 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f7e3574..0289443 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,17 @@
The initial state comes from https://launchpad.net/usb-imagewriter
The modifications are:
+IsoDumper 0.33
+------------
+ - added a help button and dialog box.
+ - added a Refresh button to find new devices.
+ - added a UEFI feature, allowing creating a FAT32 stick, and copy the ISO file the new partition.
+ - adding a catch for protected devices when formatting.
+ - take the device size from the selected device, not the last one.
+ - detect also SD-cards.
+ - check the size for destination when backup.
+
+
IsoDumper 0.32
------------
- fix wrong progress steps with small images.
diff --git a/lib/isodumper.py b/lib/isodumper.py
index fc15ba2..f15bf6e 100755
--- a/lib/isodumper.py
+++ b/lib/isodumper.py
@@ -32,9 +32,9 @@ import gettext
from gettext import gettext as _
from subprocess import call, Popen, PIPE
import time
+import dbus
def find_devices():
- import dbus
bus = dbus.SystemBus()
proxy = bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks")
iface = dbus.Interface(proxy, "org.freedesktop.UDisks")
@@ -42,10 +42,11 @@ def find_devices():
list=[]
for dev in devs:
- dev_obj = bus.get_object("org.freedesktop.UDisks", dev)
- dev = dbus.Interface(dev_obj, "org.freedesktop.DBus.Properties")
- item=[]
- if str(dev.Get('', 'DriveConnectionInterface')) == 'usb' and not str(dev.Get('', 'PartitionType')) and str(dev.Get('', 'DeviceIsMediaAvailable')) == '1':
+ dev_obj = bus.get_object("org.freedesktop.UDisks", dev)
+ dev = dbus.Interface(dev_obj, "org.freedesktop.DBus.Properties")
+ item=[]
+ dci=str(dev.Get('', 'DriveConnectionInterface'))
+ if (dci == 'usb' or dci == 'sdio') and not str(dev.Get('', 'PartitionType')) and str(dev.Get('', 'DeviceIsMediaAvailable')) == '1':
vend = str(dev.Get('', 'DriveVendor'))
path = str(dev.Get('', 'DeviceFile'))
name = str(dev.Get('', 'DriveModel'))
@@ -56,12 +57,25 @@ def find_devices():
list.append(item)
return list
+def mount(device, fs):
+ res = ''
+ _bus = dbus.SystemBus()
+ _proxy = _bus.get_object('org.freedesktop.UDisks','/org/freedesktop/UDisks')
+ _iface = dbus.Interface(_proxy, 'org.freedesktop.UDisks')
+ for _dev in _iface.EnumerateDevices():
+ _dev_obj = _bus.get_object('org.freedesktop.UDisks', _dev)
+ _dev_prop = dbus.Interface(_dev_obj, 'org.freedesktop.DBus.Properties')
+ if _dev_prop.Get('','DeviceFile')==device:
+ _idev = dbus.Interface(_dev_obj, 'org.freedesktop.DBus.UDisks.Device')
+ res = _idev.get_dbus_method('FilesystemMount',
+ dbus_interface='org.freedesktop.UDisks.Device')(fs,[])
+ return res
class IsoDumper:
def __init__(self,user):
APP="isodumper"
DIR="/usr/share/locale"
- RELEASE="v0.32"
+ RELEASE="v0.33"
gettext.bindtextdomain(APP, DIR)
gettext.textdomain(APP)
@@ -114,7 +128,6 @@ class IsoDumper:
"on_emergency_button_clicked" : self.restore,
"on_confirm_cancel_button_clicked": self.restore,
"on_filechooserbutton_file_set" : self.activate_devicelist,
-# "on_detail_expander_activate" : self.expander_control,
"on_device_combobox_changed" : self.device_selected,
"on_nodev_close_clicked" : self.close,
"on_backup_button_clicked" : self.backup_go,
@@ -126,6 +139,9 @@ class IsoDumper:
"on_format_cancel_clicked" : self.format_cancel,
"on_format_go_clicked" : self.do_format,
"on_write_button_clicked" : self.do_write,
+ "on_help_close_clicked": self.help_close,
+ "on_help_clicked": self.help_dialog,
+ "on_update_button_clicked":self.update_list,
}
self.wTree.signal_autoconnect(dict)
@@ -133,31 +149,38 @@ class IsoDumper:
# make sure we have a target device
self.get_devices()
+ def update_list(self, widget):
+ self.devicelist.remove_text(0)
+ self.get_devices()
+ self.restore(widget)
def get_devices(self):
dialog = self.wTree.get_widget("nodev_dialog")
- list=find_devices()
- while len(list)==0:
+ self.list=find_devices()
+ while len(self.list)==0:
exit_dialog=dialog.run()
- list = find_devices()
+ self.list = find_devices()
if (exit_dialog==2) :
dialog.destroy()
exit(0)
-# self.combo = self.wTree.get_widget("device_combobox")
- for name, path, size in list:
- self.deviceSize=size
+ for name, path, size in self.list:
# convert in Mbytes
sizeM=str(int(size)/(1024*1024))
self.devicelist.append_text(name+' ('+path.lstrip()+') '+sizeM+_('Mb'))
- self.device_name=name.rstrip().replace(' ', '')
- dialog.destroy()
+ dialog.hide()
def device_selected(self, widget):
self.dev = self.devicelist.get_active_text()
- self.backup_select.set_sensitive(True)
- self.wTree.get_widget("format_button").set_sensitive(True)
- self.wTree.get_widget("filechooserbutton").set_sensitive(True)
- self.logger(_('Target Device: ')+ self.dev)
+ if self.dev != None:
+ for name, path, size in self.list:
+ if self.dev.startswith(name):
+ self.deviceSize=eval(size)
+ self.device_name=name.rstrip().replace(' ', '')
+ break
+ self.backup_select.set_sensitive(True)
+ self.wTree.get_widget("format_button").set_sensitive(True)
+ self.wTree.get_widget("filechooserbutton").set_sensitive(True)
+ self.logger(_('Target Device: ')+ self.dev)
def backup_sel(self,widget):
if self.backup_bname.get_current_folder_uri() == None :
@@ -212,11 +235,31 @@ class IsoDumper:
if resp:
dialog.hide()
if self.wTree.get_widget("format_fat").get_active():
- self.raw_format(target, 'fat32', dev_name.upper()[:11])
+ rc=self.raw_format(target, 'fat32', dev_name.upper()[:11])
if self.wTree.get_widget("format_ntfs").get_active():
- self.raw_format(target, 'ntfs', dev_name[:32])
+ rc=self.raw_format(target, 'ntfs', dev_name[:32])
if self.wTree.get_widget("format_ext4").get_active():
- self.raw_format(target, 'ext4', dev_name)
+ rc=self.raw_format(target, 'ext4', dev_name)
+ self.operation=False
+ if rc == 0:
+ message = _('The device was formatted successfully.')
+ self.logger(message)
+ self.success()
+ elif rc == 5:
+ message = _("An error occurred while creating a partition.")
+ self.logger(message)
+ self.emergency()
+ elif rc == 127:
+ message = _('Authentication error.')
+ self.logger(message)
+ self.emergency()
+ else:
+ message = _('An error occurred.')
+ self.emergency()
+ self.wTree.get_widget("format").hide()
+ self.backup_select.set_sensitive(True)
+ self.wTree.get_widget("format_button").set_sensitive(True)
+ self.wTree.get_widget("filechooserbutton").set_sensitive(True)
else:
dialog.hide()
@@ -225,8 +268,11 @@ class IsoDumper:
self.wTree.get_widget("format_button").set_sensitive(True)
self.wTree.get_widget("filechooserbutton").set_sensitive(True)
self.devicelist.set_sensitive(True)
-# self.write_logfile()
self.wTree.get_widget("emergency_dialog").hide()
+ progress = self.wTree.get_widget("progressbar")
+ progress.set_text("")
+ progress.set_fraction(0)
+ progress.set_sensitive(False)
def raw_format(self, usb_path, fstype, label):
self.operation=True
@@ -243,27 +289,9 @@ class IsoDumper:
if rc is None:
working=True
else:
- if rc == 0:
- message = _('The device was formatted successfully.')
- self.logger(message)
- self.success()
- elif rc == 5:
- message = _("An error occurred while creating a partition.")
- self.logger(message)
- self.emergency()
- elif rc == 127:
- message = _('Authentication error.')
- self.logger(message)
- self.emergency()
- else:
- message = _('An error occurred.')
- self.emergency()
- self.wTree.get_widget("format").hide()
self.process = None
working= False
- self.backup_select.set_sensitive(True)
- self.wTree.get_widget("format_button").set_sensitive(True)
- self.wTree.get_widget("filechooserbutton").set_sensitive(True)
+ return rc
def format_cancel(self, widget):
dialog=self.wTree.get_widget("format")
@@ -280,18 +308,26 @@ class IsoDumper:
if os.path.exists(dest):
dialog=self.wTree.get_widget("confirm_overwrite")
resp=dialog.run()
- if resp !=-5:
+ if resp !=-5: # GTK_RESPONSE_OK
dialog.hide()
return True
else:
dialog.hide()
- source = self.dev.split('(')[1].split(')')[0]
- self.logger(_('Backup in:')+' '+dest)
- task = self.raw_write(source, dest, eval(self.deviceSize))
- gobject.idle_add(task.next)
- while gtk.events_pending():
- gtk.main_iteration(True)
- self.success()
+ # check free space
+ st = os.statvfs(os.path.dirname(dest))
+ free = st.f_bavail * st.f_frsize
+ if free<self.deviceSize :
+ sizeM=str(self.deviceSize/(1024*1024))
+ self.logger(_("The destination directory is too small to receive the backup (%s Mb needed)")%(sizeM))
+ self.emergency()
+ else:
+ source = self.dev.split('(')[1].split(')')[0]
+ self.logger(_('Backup in:')+' '+dest)
+ task = self.raw_write(source, dest, self.deviceSize)
+ gobject.idle_add(task.next)
+ while gtk.events_pending():
+ gtk.main_iteration(True)
+ self.success()
def do_write(self, widget):
write_button = self.wTree.get_widget("write_button")
@@ -310,13 +346,13 @@ class IsoDumper:
self.logger(_('Image: ')+source)
self.logger(_('Target Device: ')+self.dev)
b = os.path.getsize(source)
- if b > (eval(self.deviceSize)) :
+ if b > (self.deviceSize):
self.logger(_('The device is too small to contain the ISO file.'))
self.emergency()
else:
resp = dialog.run()
if resp:
- if eval(self.deviceSize)> 1024*1024*1024*32 :
+ if self.deviceSize> 1024*1024*1024*32 :
message=self.wTree.get_widget("label1")
message.set_text(_('The device is bigger than 32 Gbytes. Are you sure you want use it?'))
resp = dialog.run()
@@ -332,16 +368,54 @@ class IsoDumper:
self.do_umount(target)
dialog.hide()
# Writing step
- task = self.raw_write(source, target, os.path.getsize(source))
- gobject.idle_add(task.next)
- while gtk.events_pending():
- gtk.main_iteration(True)
- self.success()
+ # Iso dump or Uefi preparation
+ uefi_checkbox=self.wTree.get_widget("uefi_check")
+ if uefi_checkbox.get_active():
+ #uefi mode : formats FAT32, names MGALIVE, copies the ISO
+ target = self.dev.split('(')[1].split(')')[0]
+ dev_name="MGALIVE"
+ rc=self.raw_format(target, 'fat32', dev_name)
+ if rc == 0:
+ message = _('The device was formatted successfully.')
+ self.logger(message)
+ elif rc == 5:
+ message = _("An error occurred while creating a partition.")
+ self.logger(message)
+ self.emergency()
+ elif rc == 127:
+ message = _('Authentication error.')
+ self.logger(message)
+ self.emergency()
+ else:
+ message = _('An error occurred.')
+ self.emergency()
+ if rc == 0:
+ dest=mount(target+'1','vfat')
+ if dest!="":
+ self.logger(_("Mounted in: ")+dest)
+ dest+='/'+os.path.basename(source)
+ self.logger(_('Executing copy from ')+source+_(' to ')+dest)
+ task = self.raw_write(source, dest, os.path.getsize(source))
+ gobject.idle_add(task.next)
+ while gtk.events_pending():
+ gtk.main_iteration(True)
+ self.success()
+ else:
+ self.logger(_("Error mounting the partition"))
+ else:
+ #Dump mode
+ task = self.raw_write(source, target, os.path.getsize(source))
+ gobject.idle_add(task.next)
+ while gtk.events_pending():
+ gtk.main_iteration(True)
+ self.success()
else:
# self.close('dummy')
dialog.hide()
combo.set_sensitive(True)
write_button.set_sensitive(True)
+ format_button.set_sensitive(True)
+ backup_select.set_sensitive(True)
def do_umount(self, target):
mounts = self.get_mounted(target)
if mounts:
@@ -533,6 +607,14 @@ class IsoDumper:
# if widget.get_expanded():
# gobject.timeout_add(130, lambda: self.window.reshow_with_initial_size())
+ def help_dialog(self, widget):
+ dialog = self.wTree.get_widget("help_dialog")
+ dialog.run()
+
+ def help_close(self, widget):
+ dialog = self.wTree.get_widget("help_dialog")
+ dialog.hide()
+
def about(self, widget):
about_button = self.wTree.get_widget("about_button")
about_button.set_sensitive(True)
diff --git a/lib/raw_format.py b/lib/raw_format.py
index 8ef036b..84ca8c7 100755
--- a/lib/raw_format.py
+++ b/lib/raw_format.py
@@ -5,11 +5,11 @@
# Copyright (C) 2013 THE isodumper'S COPYRIGHT HOLDER
# This file is distributed under the same license as the isodumper package.
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -59,18 +59,23 @@ def get_mounted(target):
sys.exit(6)
def raw_format(device_path, fstype, volume_label, uid, gid):
-
+
do_umount(device_path)
-
+
# First erase MBR and partition table , if any
os.system ("dd if=/dev/zero of=%s bs=512 count=1 >/dev/null 2>&1" % device_path)
-
+
device = parted.getDevice(device_path)
-
- # Create a default partition set up
+
+ # Create a default partition set up
disk = parted.freshDisk(device, 'msdos')
- disk.commit()
- regions = disk.getFreeSpaceRegions()
+ try:
+ disk.commit()
+ except:
+ # Unable to write the new partition
+ print "Can't create partition. The device could be read-only."
+ sys.exit(5)
+ regions = disk.getFreeSpaceRegions()
if len(regions) > 0:
#print "Build partition"
@@ -79,7 +84,7 @@ def raw_format(device_path, fstype, volume_label, uid, gid):
#print "start %s" % start
end = device.getLength() - start - 1024
#print end
-
+
# Alignment
#cylinder = device.endSectorToCylinder(end)
#end = device.endCylinderToSector(cylinder)
@@ -89,16 +94,16 @@ def raw_format(device_path, fstype, volume_label, uid, gid):
except:
print "Geometry error - Can't create partition"
sys.exit(5)
-
+
# fstype
fs = parted.FileSystem(type=fstype, geometry=geometry)
-
+
# Create partition
partition = parted.Partition(disk=disk, type=parted.PARTITION_NORMAL, geometry=geometry, fs=fs)
constraint = parted.Constraint(exactGeom=geometry)
disk.addPartition(partition=partition, constraint=constraint)
disk.commit()
-
+
# Format partition according to the fstype specified
if fstype == "fat32":
os.system("mkdosfs -F 32 -n \"%s\" %s >/dev/null 2>&1" % (volume_label, partition.path))
@@ -144,14 +149,14 @@ def main():
uid = a
elif o in ("-g"):
gid = a
-
+
argc = len(sys.argv)
if argc < 11:
print "Too few arguments"
print "for help use --help"
exit(2)
-
+
raw_format(device, fstype, label, uid, gid)
-
+
if __name__ == "__main__":
main()
diff --git a/share/isodumper/isodumper.glade b/share/isodumper/isodumper.glade
index 64769c4..15e4246 100644
--- a/share/isodumper/isodumper.glade
+++ b/share/isodumper/isodumper.glade
@@ -418,9 +418,6 @@ Public License instead of this License.
<property name="position">0</property>
</packing>
</child>
- <child>
- <placeholder/>
- </child>
</widget>
</child>
</widget>
@@ -885,6 +882,91 @@ Public License instead of this License.
</widget>
</child>
</widget>
+ <widget class="GtkDialog" id="help_dialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Isodumper - Help</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="default_width">500</property>
+ <property name="default_height">300</property>
+ <property name="type_hint">dialog</property>
+ <property name="transient_for">main_dialog</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <widget class="GtkButton" id="help_close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_help_close_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <widget class="GtkTextView" id="textview1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="pixels_above_lines">2</property>
+ <property name="pixels_below_lines">2</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="left_margin">5</property>
+ <property name="right_margin">5</property>
+ <property name="text" translatable="yes">Mageia IsoDumper
+----------------
+This GUI program is primarily for safely writing a bootable ISO image to a USB flash drive, an operation devious &amp; potentially hazardous when done by hand. As a bonus, it can also back up the entire previous contents of the flash drive onto the hard disc, and restore the flash drive to its previous state subsequently. It gives also a feature for formatting the USB device.
+
+IsoDumper can be launched either from the menus, or a user or root console with the command 'isodumper'. For normal users, the root password is solicited; this is necessary for the program's operation. The flash drive can be inserted beforehand or once the program is started. In the latter case, a dialogue will say that there is no flash drive inserted, and allow a 'retry' to find it once it is. (You may have to close any automatically opened File Manager window).
+
+The fields of the main window are as follows:
+- Device to work on: the device of the USB flash drive, a drop-down list to choose from.
+- Write Image: to choose the source ISO image *.iso (or flash drive backup file *.img) to write out.
+- Write to device: This button launches the operation - with a prior warning dialogue. If a flash drive backup was requested, this is done first. Then (or only) the image file write. Each operation is shown in the progress bar beneath.
+- Backup in: define the name and placement of the backup image file. The current flash drive will be backed up to a disc file. Note that the entire flash drive is preserved, regardless of its actual contents; ensure that you have the necessary free disc space (the same size as the USB device). This backup file can be used later to restore the flash drive by selecting it as the source *.img file to write out.
+- Backup the device: launch the backup operation.
+- Format the device: create an unique partition on the entire volume in the specified format in FAT, NTFS or ext. You can specify a volume name and the format in a new dialog box.
+- Details: this button shows detailed log information.
+
+</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
<widget class="GtkWindow" id="main_dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">IsoDumper</property>
@@ -1011,6 +1093,21 @@ Public License instead of this License.
</packing>
</child>
<child>
+ <widget class="GtkCheckButton" id="uefi_check">
+ <property name="label" translatable="yes">For UEFI boot</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip" translatable="yes">Use FAT32 format with MGALIVE as volume name.</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
<widget class="GtkButton" id="write_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
@@ -1053,7 +1150,7 @@ Public License instead of this License.
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
</widget>
@@ -1362,9 +1459,24 @@ Public License instead of this License.
<widget class="GtkHButtonBox" id="hbuttonbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="spacing">5</property>
+ <property name="spacing">4</property>
<property name="layout_style">end</property>
<child>
+ <widget class="GtkButton" id="update_button">
+ <property name="label">gtk-refresh</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_update_button_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
<widget class="GtkButton" id="about_button">
<property name="label">gtk-about</property>
<property name="visible">True</property>
@@ -1376,7 +1488,22 @@ Public License instead of this License.
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">0</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_help_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
@@ -1391,7 +1518,7 @@ Public License instead of this License.
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">1</property>
+ <property name="position">3</property>
</packing>
</child>
</widget>