summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTails developers <tails@boum.org>2011-07-09 23:19:19 +0200
committerTails developers <tails@boum.org>2011-07-09 23:19:19 +0200
commit53b7955ca227538c73eb71e58fa13ff213d27abc (patch)
tree804d1b34d5c1dbaff4b2292736a17a8550171f3e
parentd10d30ffa72405e9b3f962d60bc7d910dd73e583 (diff)
Implement partioning mode (or at least a first working version)feature/partitioning
-rwxr-xr-xliveusb/creator.py120
-rwxr-xr-xliveusb/gui.py66
2 files changed, 159 insertions, 27 deletions
diff --git a/liveusb/creator.py b/liveusb/creator.py
index 69d88b3..7078b9d 100755
--- a/liveusb/creator.py
+++ b/liveusb/creator.py
@@ -47,6 +47,16 @@ from liveusb import _
from liveusb.config import config
from liveusb.source import SourceError
+#XXX: size should be configurable
+SYSTEM_PARTITION_SIZE = 1500 # MiB
+SYSTEM_PARTITION_FLAGS = [ #XXX: not until we ship an UEFI bootloader
+ #0, # system partition
+ 2, # legacy BIOS bootable
+ 60, # read-only
+ 62, # hidden
+ 63 # do not automount
+ ]
+
class LiveUSBError(Exception):
""" A generic error message that is thrown by the LiveUSBCreator """
@@ -205,6 +215,64 @@ class LiveUSBCreator(object):
""" Unmount the device mounted at self.mount """
raise NotImplementedError
+ def _set_partition_flags(self, part_number, flags):
+ cmd = ( [ '/sbin/sgdisk' ]
+ + [ '--attributes=1:set:%d' % flag for flag in flags ]
+ + [ self.drive['device'] ])
+ self.popen(cmd, shell=False)
+
+ def partition_device(self):
+ """ Partition device listed at self.drive """
+ if not self.opts.partition:
+ return
+
+ #XXX: size should be configurable
+ #XXX: set label for system partition
+ cmd = [ '/sbin/sgdisk',
+ # create a new partition table
+ '--clear',
+ # new partition (first), starts at begining, with given MiB size
+ '--new=1:0:+%dM' % SYSTEM_PARTITION_SIZE,
+ # first part, set type 8301 (Linux reserved)
+ '--typecode=1:8301',
+ self.drive['device']
+ ]
+ self.popen(cmd, shell=False)
+ self._set_partition_flags(1, SYSTEM_PARTITION_FLAGS)
+
+ def _gdisk_script(self, gdisk_cmds):
+ cmd = [ '/sbin/gdisk', self.drive['device'] ]
+ stdin = tempfile.TemporaryFile(mode='w')
+ stdin.write('\n'.join(gdisk_cmds) + '\n')
+ stdin.seek(0)
+ self.popen(cmd, shell=False, stdin=stdin)
+
+ def create_hybrid_mbr(self):
+ # only relevant with OS that don't know how to access partitons on GPT
+ self._gdisk_script([ 'r', # recovery and transformation options
+ 'h', # make hybrid MBR
+ '1', # add first partition
+ 'N', # no EFI GPT (0xEE) partition
+ '0c', # partition type (Win95 FAT32 LBA)
+ 'N', # don't set the bootable flag
+ 'N', # no protective partition
+ 'w', # write table to disk and exit
+ 'Y', # proceed, possibly destroying data
+ ])
+
+ def remove_hybrid_mbr(self):
+ self._gdisk_script([ 'x', # extra functionality
+ 'n', # create a new protective MBR
+ 'w', # write table to disk and exit
+ 'Y', # proceed, possibly destroying data
+ ])
+
+ def switch_drive_to_system_partition(self):
+ pass
+
+ def switch_back_to_full_drive(self):
+ pass
+
def popen(self, cmd, passive=False, ret='proc', **user_kwargs):
""" A wrapper method for running subprocesses.
@@ -461,7 +529,7 @@ class LiveUSBCreator(object):
def mbr_matches_syslinux_bin(self):
"""
Return whether or not the MBR on the drive matches the system's
- syslinux mbr.bin
+ syslinux gptmbr.bin
"""
return True
@@ -499,7 +567,7 @@ class LinuxLiveUSBCreator(LiveUSBCreator):
"/org/freedesktop/UDisks")
self.udisks = dbus.Interface(udisks_obj, "org.freedesktop.UDisks")
- def detect_removable_drives(self, callback=None):
+ def detect_removable_drives(self, callback=None, force_partitions=False):
""" Detect all removable USB storage devices using UDisks via D-Bus """
def handle_reply(devices):
# Map from device to a set of (currently mounted) partitions
@@ -565,7 +633,7 @@ class LinuxLiveUSBCreator(LiveUSBCreator):
self.log.debug(pformat(data))
- if self.opts.partition:
+ if not force_partitions and self.opts.partition:
# Add whole drive in partitioning mode
if data['parent'] is None:
self.drives[data['device']] = data
@@ -582,8 +650,9 @@ class LinuxLiveUSBCreator(LiveUSBCreator):
for device, data in self.drives.iteritems():
if device in mounted_parts and len(mounted_parts[device]) > 0:
data['mounted_partitions'] = mounted_parts[device]
- self.log.info('Some partitions of USB device %s are mounted' % data['device'])
- self.log.info('If %s is the USB device you want to install on, please unmount' % data['device'])
+ self.log.info(('Some partitions of USB device %s are mounted. '
+ 'They will be unmounted before starting the '
+ 'installation process.') % data['device'])
if callback:
callback()
@@ -687,6 +756,39 @@ class LinuxLiveUSBCreator(LiveUSBCreator):
self.log.error("Mount %s exists after unmounting" % self.dest)
self.dest = None
+ def partition_device(self):
+ if not self.opts.partition:
+ return
+
+ # Use udisks instead of plain sgdisk will allow unprivileged users
+ # to get a refreshed partition table from the kernel
+ dev = self._get_device(self.drive['udi'])
+ dev.PartitionTableCreate('gpt', [])
+
+ offset = 0
+ size = SYSTEM_PARTITION_SIZE * 2**20
+ partition_type = '8DA63339-0007-60C0-C436-083AC8230908'
+ label = 'Linux'
+ flags = []
+ options = []
+ fstype = ''
+ fsoptions = []
+
+ dev.PartitionCreate(offset, size, partition_type, label, flags,
+ options, fstype, fsoptions)
+
+ # We still need to set the system partition flags as udisks cannot
+ # do it (yet?).
+ self._set_partition_flags(1, SYSTEM_PARTITION_FLAGS)
+
+ def switch_drive_to_system_partition(self):
+ self._full_drive = self._drive
+ # XXX: fairly fragile... assume /dev/sdx and /dev/sdx1
+ self.drive = '%s1' % self._full_drive
+
+ def switch_back_to_full_drive(self):
+ self.drive = self._full_drive
+
def verify_filesystem(self):
self.log.info(_("Verifying filesystem..."))
if self.fstype not in self.valid_fstypes:
@@ -890,8 +992,8 @@ class LinuxLiveUSBCreator(LiveUSBCreator):
def _get_mbr_bin(self):
mbr = None
- for mbr_bin in ('/usr/lib/syslinux/mbr.bin',
- '/usr/share/syslinux/mbr.bin'):
+ for mbr_bin in ('/usr/lib/syslinux/gptmbr.bin',
+ '/usr/share/syslinux/gptmbr.bin'):
if os.path.exists(mbr_bin):
mbr = mbr_bin
return mbr
@@ -899,7 +1001,7 @@ class LinuxLiveUSBCreator(LiveUSBCreator):
def mbr_matches_syslinux_bin(self):
"""
Return whether or not the MBR on the drive matches the system's
- syslinux mbr.bin
+ syslinux gptmbr.bin
"""
mbr_bin = open(self._get_mbr_bin(), 'rb')
mbr = ''.join(['%02X' % ord(x) for x in mbr_bin.read(2)])
@@ -944,7 +1046,7 @@ class LinuxLiveUSBCreator(LiveUSBCreator):
return hexdigest
def flush_buffers(self):
- self.popen('sync', passive=True)
+ self.popen('sync')
def is_admin(self):
return os.getuid() == 0
diff --git a/liveusb/gui.py b/liveusb/gui.py
index 90234d5..af0d3e5 100755
--- a/liveusb/gui.py
+++ b/liveusb/gui.py
@@ -141,6 +141,8 @@ class ProgressThread(QtCore.QThread):
def run(self):
while True:
free = self.get_free_bytes()
+ if free is None:
+ break
value = (self.orig_free - free) / 1024
self.emit(QtCore.SIGNAL("progress(int)"), value)
if value >= self.totalsize:
@@ -163,11 +165,33 @@ class LiveUSBThread(QtCore.QThread):
def status(self, text):
self.emit(QtCore.SIGNAL("status(PyQt_PyObject)"), text)
+ def rescan_devices(self, force_partitions=False):
+ self._waiting_detection = True
+
+ def detection_done():
+ self._waiting_detection = False
+
+ self.live.detect_removable_drives(
+ callback=detection_done,
+ force_partitions=force_partitions)
+
+ while self._waiting_detection:
+ self.sleep(1)
+
def run(self):
- handler = LiveUSBLogHandler(self.status)
- self.live.log.addHandler(handler)
- now = datetime.now()
+ self.handler = LiveUSBLogHandler(self.status)
+ self.live.log.addHandler(self.handler)
+ self.now = datetime.now()
try:
+ if self.parent.opts.partition:
+ self.live.unmount_device()
+ self.live.partition_device()
+ self.live.create_hybrid_mbr()
+ self.rescan_devices(force_partitions=True)
+ self.live.switch_drive_to_system_partition()
+ self.live.format_device()
+ self.live.mount_device()
+
#if self.parent.opts.format:
# self.live.unmount_device()
# self.live.format_device()
@@ -187,12 +211,11 @@ class LiveUSBThread(QtCore.QThread):
if self.live.opts.clone_running:
self.live.source = RunningLiveSystemSource()
- if not self.opts.partition:
- self.live.verify_filesystem()
+ self.live.verify_filesystem()
if not self.live.drive['uuid'] and not self.live.label:
self.status(_("Error: Cannot set the label or obtain "
"the UUID of your device. Unable to continue."))
- self.live.log.removeHandler(handler)
+ self.live.log.removeHandler(self.handler)
return
self.live.check_free_space()
@@ -200,14 +223,14 @@ class LiveUSBThread(QtCore.QThread):
if not self.parent.opts.noverify:
# Verify the MD5 checksum inside of the ISO image
if not self.live.verify_iso_md5():
- self.live.log.removeHandler(handler)
+ self.live.log.removeHandler(self.handler)
return
# If we know about this ISO, and it's SHA1 -- verify it
release = self.live.source.get_release()
if release and ('sha1' in release or 'sha256' in release):
if not self.live.verify_iso_sha1(progress=self):
- self.live.log.removeHandler(handler)
+ self.live.log.removeHandler(self.handler)
return
# Setup the progress bar
@@ -219,6 +242,9 @@ class LiveUSBThread(QtCore.QThread):
self.live.extract_iso()
self.live.create_persistent_overlay()
self.live.update_configs()
+
+ if self.parent.opts.partition:
+ self.live.reset_mbr()
self.live.install_bootloader()
self.live.bootable_partition()
@@ -231,7 +257,12 @@ class LiveUSBThread(QtCore.QThread):
self.live.flush_buffers()
self.live.unmount_device()
- duration = str(datetime.now() - now).split('.')[0]
+ if self.parent.opts.partition:
+ self.rescan_devices()
+ self.live.switch_back_to_full_drive()
+ self.live.remove_hybrid_mbr()
+
+ duration = str(datetime.now() - self.now).split('.')[0]
self.status(_("Complete! (%s)" % duration))
except Exception, e:
@@ -239,7 +270,7 @@ class LiveUSBThread(QtCore.QThread):
self.status(_("LiveUSB creation failed!"))
self.live.log.exception(e)
- self.live.log.removeHandler(handler)
+ self.live.log.removeHandler(self.handler)
self.progress.terminate()
def set_max_progress(self, maximum):
@@ -335,11 +366,10 @@ class LiveUSBDialog(QtGui.QDialog, LiveUSBInterface):
self.startButton.setEnabled(False)
return
for device, info in self.live.drives.items():
- if len(info['mounted_partitions']) == 0:
- if info['label']:
- self.driveBox.addItem("%s (%s)" % (device, info['label']))
- else:
- self.driveBox.addItem("%s (%0.1f GB)" % (device, info['size'] / 10.0**9))
+ if info['label']:
+ self.driveBox.addItem("%s (%s)" % (device, info['label']))
+ else:
+ self.driveBox.addItem("%s (%0.1f GB)" % (device, info['size'] / 10.0**9))
self.startButton.setEnabled(True)
try:
@@ -546,9 +576,9 @@ class LiveUSBDialog(QtGui.QDialog, LiveUSBInterface):
"continue."))
self.confirmed = True
else:
- # The user has confirmed that they wish to overwrite their
- # existing Live OS. Here we delete it first, in order to
- # accurately calculate progress.
+ # The user has confirmed that they wish to overwrite their
+ # existing Live OS. Here we delete it first, in order to
+ # accurately calculate progress.
self.confirmed = False
try:
self.live.delete_liveos()