summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsegfault <segfault@riseup.net>2018-11-22 14:55:07 +0100
committersegfault <segfault@riseup.net>2018-11-22 14:55:07 +0100
commitdaaf326952d41a1afede41f42ce7ac6f8c7d05a6 (patch)
treee33d9fd09fe02740943e4f9d1068a39364681631
parenta465406f8f5ad03856778b3137a199dff46ec09c (diff)
Update create-usb-image-from-isowip/usb-image-generation
-rwxr-xr-xauto/scripts/create-usb-image-from-iso132
1 files changed, 53 insertions, 79 deletions
diff --git a/auto/scripts/create-usb-image-from-iso b/auto/scripts/create-usb-image-from-iso
index 658fa4d..b36fe76 100755
--- a/auto/scripts/create-usb-image-from-iso
+++ b/auto/scripts/create-usb-image-from-iso
@@ -5,7 +5,6 @@ import os
import logging
from contextlib import contextmanager
import re
-import tempfile
import time
import subprocess
@@ -33,13 +32,17 @@ FILESYSTEM_LABEL = 'Tails'
GET_UDISKS_OBJECT_TIMEOUT = 2
WAIT_FOR_PARTITION_TIMEOUT = 2
-# Size that the system partition will be created larger than the contents of
-# the ISO, in MiB. This is must be enough to fit the partition table, reserved
-# sectors, and filesystem metadata.
+# The size of the system partition (in MiB) will be:
+#
+# SYSTEM_PARTITION_ADDITIONAL_SIZE + size of the ISO
+#
+# SYSTEM_PARTITION_ADDITIONAL_SIZE must be large enough to fit
+# the partition table, reserved sectors, and filesystem metadata.
SYSTEM_PARTITION_ADDITIONAL_SIZE = 10
SYSLINUX_COM32MODULES_DIR = '/usr/lib/syslinux/modules/bios'
+
class ImageCreationError(Exception):
pass
@@ -52,8 +55,8 @@ class ImageCreator(object):
self.free_space = free_space
self._loop_device = None # type: str
self._partition = None # type: str
+ self._system_partition_size = None # type: int
self.mountpoint = None # type: str
- self.system_partition_size = None # type: int
@property
def loop_device(self) -> UDisks.ObjectProxy:
@@ -68,6 +71,13 @@ class ImageCreator(object):
return self.try_getting_udisks_object(self._partition)
+ @property
+ def system_partition_size(self) -> int:
+ if self._system_partition_size is None:
+ self._system_partition_size = get_file_size(self.iso) + SYSTEM_PARTITION_ADDITIONAL_SIZE
+
+ return self._system_partition_size
+
def try_getting_udisks_object(self, object_path: str) -> UDisks.Object:
start_time = time.perf_counter()
while time.perf_counter() - start_time < GET_UDISKS_OBJECT_TIMEOUT:
@@ -86,40 +96,33 @@ class ImageCreator(object):
client.settle()
def create_image(self):
- with self.extract_iso() as iso_extraction_dir:
- self.system_partition_size = self.calculate_system_partition_size(iso_extraction_dir)
- self.create_empty_image()
-
- with self.setup_loop_device():
- self.create_gpt()
- self.create_partition()
- self.set_partition_flags()
- # XXX: Rescan?
- self.format_partition()
- # XXX: Verify that now everything is as it should be (see what Tails Installer is doing)
- with self.mount_partition():
- self.copy_iso_contents_to_partition(iso_extraction_dir)
- self.set_permissions()
- self.update_configs()
- self.install_mbr()
- self.copy_syslinux_modules()
-
- # This sleep is a workaround for a race condition which causes the
- # syslinux installation to return without errors, even though the
- # bootloader isn't actually installed
- # XXX: Investigate and report this race condition
- time.sleep(1)
- self.install_syslinux()
- self.set_guid()
-
- @staticmethod
- def calculate_system_partition_size(iso_extraction_dir: str) -> int:
- """Size of the system partition that is to be created, in MiB"""
- return get_dir_size(iso_extraction_dir) + SYSTEM_PARTITION_ADDITIONAL_SIZE
-
- def copy_iso_contents_to_partition(self, iso_extraction_dir: str):
- logger.info("Copying ISO contents to the partition")
- execute(["cp", "-a", iso_extraction_dir + "/.", self.mountpoint])
+ self.create_empty_image()
+
+ with self.setup_loop_device():
+ self.create_gpt()
+ self.create_partition()
+ self.set_partition_flags()
+ # XXX: Rescan?
+ self.format_partition()
+ with self.mount_partition():
+ self.extract_iso()
+ self.set_permissions()
+ self.update_configs()
+ self.install_mbr()
+ self.copy_syslinux_modules()
+
+ # This sleep is a workaround for a race condition which causes the
+ # syslinux installation to return without errors, even though the
+ # bootloader isn't actually installed
+ # XXX: Investigate and report this race condition
+ # Might it be https://bugs.chromium.org/p/chromium/issues/detail?id=508713 ?
+ time.sleep(1)
+ self.install_syslinux()
+ self.set_guid()
+
+ def extract_iso(self):
+ logger.info("Extracting ISO contents to the partition")
+ execute(['7z', 'x', self.iso, '-x![BOOT]', '-y', '-o%s' % self.mountpoint])
def create_empty_image(self):
logger.info("Creating empty image %r", self.image)
@@ -162,7 +165,6 @@ class ImageCreator(object):
arg_options=GLib.Variant('a{sv}', None),
cancellable=None
)
- # XXX: Try again on error, as Tails Installer does?
def create_partition(self):
logger.info("Creating partition")
@@ -212,7 +214,6 @@ class ImageCreator(object):
arg_options=options,
cancellable=None
)
- # XXX: Try again on error, as Tails Installer does?
@contextmanager
def mount_partition(self):
@@ -239,13 +240,6 @@ class ImageCreator(object):
cancellable=None,
)
- @contextmanager
- def extract_iso(self) -> str:
- logger.info("Extracting ISO")
- with tempfile.TemporaryDirectory(prefix="tails-iso-") as iso_extraction_dir:
- execute(['7z', 'x', self.iso, '-x![BOOT]', '-y', '-o%s' % iso_extraction_dir])
- yield iso_extraction_dir
-
def set_permissions(self):
logger.info("Setting file access permissions")
for root, dirs, files in os.walk(self.mountpoint):
@@ -258,8 +252,6 @@ class ImageCreator(object):
logger.info("Updating config files")
grubconf = os.path.join(self.mountpoint, "EFI", "BOOT", "grub.conf")
bootconf = os.path.join(self.mountpoint, "EFI", "BOOT", "boot.conf")
- bootx64conf = os.path.join(self.mountpoint, "EFI", "BOOT", "bootx64.conf")
- bootia32conf = os.path.join(self.mountpoint, "EFI", "BOOT", "bootia32.conf")
isolinux_dir = os.path.join(self.mountpoint, "isolinux")
syslinux_dir = os.path.join(self.mountpoint, "syslinux")
isolinux_cfg = os.path.join(syslinux_dir, "isolinux.cfg")
@@ -287,29 +279,10 @@ class ImageCreator(object):
os.remove(isolinux_cfg)
def update_config(self, infile, outfile):
- uuid = self.loop_device.props.block.props.id_uuid
- label = self.loop_device.props.block.props.id_label
- fstype = self.partition.props.block.props.id_type
- usblabel = "UUID=%s" % uuid if uuid else "LABEL=%s" % label
-
with open(infile) as f_in:
- lines = f_in.readlines()
-
- new_lines = list()
-
- for line in lines:
- line = re.sub('/isolinux/', '/syslinux/', line)
- # XXX: Should we support overlay and kernel_args?
- # if self.overlay and "liveimg" in line:
- # line = line.replace("liveimg", "liveimg overlay=" + usblabel)
- # line = line.replace(" ro ", " rw ")
- # if self.opts.kernel_args:
- # line = line.replace("liveimg", "liveimg %s" %
- # ' '.join(self.opts.kernel_args.split(',')))
- new_lines.append(line)
-
+ lines = [re.sub('/isolinux/', '/syslinux/', line) for line in f_in]
with open(outfile, "w") as f_out:
- f_out.writelines(new_lines)
+ f_out.writelines(lines)
def install_mbr(self):
logger.info("Installing MBR")
@@ -333,31 +306,32 @@ class ImageCreator(object):
def install_syslinux(self):
logger.info("Installing bootloader")
- # XXX: Support --force and --stupid switches?
# We install syslinux directly on the image. Installing it on the loop
# device would cause this issue:
# https://bugs.chromium.org/p/chromium/issues/detail?id=508713#c8
execute([
- # XXX: Why does this only work as root?
- 'pkexec',
'syslinux',
'--offset', str(self.partition.props.partition.props.offset),
'--directory', '/syslinux/',
'--install', self.image
- ])
+ ],
+ as_root=True # XXX: Why does this only work as root?
+ )
def set_guid(self):
execute(["/sbin/sgdisk", "--disk-guid", "17B81DA0-8B1E-4269-9C39-FE5C7B9B58A3", self.image])
-def execute(cmd: list):
+def execute(cmd: list, as_root=False):
+ if as_root and os.geteuid() != 0:
+ cmd = ['pkexec'] + cmd
logger.info("Executing '%s'" % ' '.join(cmd))
subprocess.check_call(cmd)
-def get_dir_size(path: str) -> int:
- """Returns the size of a directory in MiB"""
- size_in_bytes = int(subprocess.check_output(["du", "-s", "--bytes", path]).split()[0])
+def get_file_size(path: str) -> int:
+ """Returns the size of a file in MiB"""
+ size_in_bytes = os.path.getsize(path)
return round(size_in_bytes // 1024 ** 2)