From 0e989d48df03e450d74e35e32651917b41f67675 Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Mon, 9 Jun 2014 14:21:03 +0000 Subject: Fix spaces between function name and parameters --- system-version-manager/system-version-manager | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system-version-manager/system-version-manager b/system-version-manager/system-version-manager index 6638a35..659825f 100755 --- a/system-version-manager/system-version-manager +++ b/system-version-manager/system-version-manager @@ -244,7 +244,7 @@ class SystemVersionManager(object): device = os.path.realpath(device) return device, current_system - def cmd_remove (self, system_name): + def cmd_remove(self, system_name): self._check_system_exists(system_name) default_system = self._get_default() @@ -268,7 +268,7 @@ class SystemVersionManager(object): self._rewrite_boot_menu(self.device, default_system, self._get_systems()) - def cmd_set_default (self, system_name): + def cmd_set_default(self, system_name): self._check_system_exists(system_name) self._rewrite_boot_menu(self.device, system_name, self._get_systems()) -- cgit v1.2.1 From 29dd6a7f8520a146f2f1ebeb3bdef695b8a49666 Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Mon, 9 Jun 2014 14:38:35 +0000 Subject: Use fd from mkstemp to create file object, rather than path It's a shame to throw away the fd that ensures we aren't hit by a symlink attack. --- system-version-manager/system-version-manager | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system-version-manager/system-version-manager b/system-version-manager/system-version-manager index 659825f..64ca5a4 100755 --- a/system-version-manager/system-version-manager +++ b/system-version-manager/system-version-manager @@ -129,9 +129,8 @@ class SystemVersionManager(object): # Logic copied from morphlib.SaveFile to not create # a morphlib dependency. fd, temp_config = tempfile.mkstemp(dir=self.mount_dir) - os.close(fd) config = os.path.join(self.mount_dir, 'extlinux.conf') - with open(temp_config, 'w') as f: + with os.fdopen(fd, 'w') as f: f.write('default menu.c32\n') f.write('timeout 50\n') f.write('prompt 0\n') -- cgit v1.2.1 From c01d44f22983b5e61271c728e8e209e73d69fe16 Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Mon, 9 Jun 2014 15:48:11 +0000 Subject: Actually use atomic_symlink_update for something This function was added to ensure safe, automatic updates of the default system symlink, but for some reason, was never used. Instead it open-coded a vulnerable way of doing this, which could fail if an upgrade was aborted at the wrong time. So now we use atomic_symlink_update, which won't fail, and the worst that can happen is a temporary directory gets left around. --- system-version-manager/system-version-manager | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system-version-manager/system-version-manager b/system-version-manager/system-version-manager index 64ca5a4..bd31c66 100755 --- a/system-version-manager/system-version-manager +++ b/system-version-manager/system-version-manager @@ -144,10 +144,8 @@ class SystemVersionManager(object): os.rename(temp_config, config) default_path = os.path.join(self.mount_dir, 'systems', 'default') - default_path_tmp = os.path.join(self.mount_dir, 'systems', 'default-tmp') if os.path.islink(default_path): - os.symlink(default, default_path_tmp) - os.rename(default_path_tmp, default_path) + self._atomic_symlink_update(default, default_path) def cmd_list(self): for system in self._get_systems(): -- cgit v1.2.1 From 86322cbe09f404929a7d4b205a262cead79e03ce Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Mon, 9 Jun 2014 15:52:37 +0000 Subject: Generate extlinux.conf using deployment options from /baserock/deployment.meta This will add the initramfs to the config and tell it to mount the disk by UUID if INITRAMFS_PATH was provided in the initial deployment. It will also include the extra KERNEL_ARGS provided. This is required to be able to upgrade a system that uses an initramfs and have it continue to use the initramfs. The system will continue to work without this patch if the initramfs' only responsibility was translating a UUID into a disk path, since system-version-manager would generate an extlinux.conf that boots with the same disk as it is currently using, but that would break if the device enumeration order changed e.g. if another disk was inserted before the rootfs, or the disk is transplanted into another machine. --- system-version-manager/system-version-manager | 57 ++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/system-version-manager/system-version-manager b/system-version-manager/system-version-manager index bd31c66..4d7dff3 100755 --- a/system-version-manager/system-version-manager +++ b/system-version-manager/system-version-manager @@ -17,8 +17,10 @@ import argparse +import errno import subprocess import tempfile +import json import os import sys import shutil @@ -39,6 +41,7 @@ class SystemVersionManager(object): def __init__(self, args, mount_dir): self.device, self.current_system = self._get_mount_info() self.mount_dir = mount_dir + self.device_uuid = self._get_device_uuid(self.device) # create the top-level parser parser = argparse.ArgumentParser(prog='system-version-manager') @@ -112,6 +115,19 @@ class SystemVersionManager(object): return self.current_system + def _get_deployment_config(self, system): + try: + meta = open(os.path.join(self.mount_dir, 'systems', system, + 'run/baserock/deployment.meta')) + except IOError as e: + if e.errno != errno.ENOENT: + raise + deployment_config = {} + else: + deployment_config = json.load(meta).get('configuration', {}) + meta.close() + return deployment_config + def _atomic_symlink_update(self, source, link_name): dirname = os.path.dirname(link_name) temp_dir = tempfile.mkdtemp(dir=dirname) @@ -136,11 +152,18 @@ class SystemVersionManager(object): f.write('prompt 0\n') f.write('ontimeout ' + default +'\n') for system in systems: - f.write('label ' + system +'\n') - f.write('kernel /systems/'+ system +'/kernel\n') - f.write('append root='+ device +' ' - 'rootflags=subvol=systems/'+ system +'/run ' - 'init=/sbin/init rw\n') + deployment_config = self._get_deployment_config(system) + f.write('label ' + system + '\n') + f.write('kernel /systems/' + system + '/kernel\n') + kernel_args = ('rw init=/sbin/init rootfstype=btrfs ' + 'rootflags=subvol=systems/'+ system +'/run ') + if 'INITRAMFS_PATH' in deployment_config: + f.write('initrd /systems/%s/initramfs\n' % system) + kernel_args += ('root=UUID=%s ' % self.device_uuid) + else: + kernel_args += ('root=%s ' % self.device) + kernel_args += deployment_config.get('KERNEL_ARGS', '') + f.write('append %s\n' % kernel_args) os.rename(temp_config, config) default_path = os.path.join(self.mount_dir, 'systems', 'default') @@ -209,11 +232,18 @@ class SystemVersionManager(object): self.status(msg="Installing the kernel") self._install_kernel(version_root) + deployment_config = self._get_deployment_config(label) + if 'INITRAMFS_PATH' in deployment_config: + self.status(msg="Installing the initramfs") + self._install_initramfs(deployment_config['INITRAMFS_PATH'], + version_root) + except Exception as e: # We are not controlling if deleting the suvolume fails subprocess.call(['btrfs', 'subvolume', 'delete', run_dir]) raise + self.status(msg="Rewriting boot menu") self._rewrite_boot_menu(self.device, self._get_default(), self._get_systems()) def _install_kernel(self, version_root): @@ -230,6 +260,16 @@ class SystemVersionManager(object): shutil.copy2(try_path, kernel_dest) break + def _install_initramfs(self, initramfs_path, version_root): + '''Install the initramfs outside of 'orig' or 'run' subvolumes + + This code is kind of duplicated in morphlib/writeexts.py. + + ''' + initramfs_dest = os.path.join(version_root, 'initramfs') + initramfs_src = os.path.join(version_root, 'run', initramfs_path) + shutil.copy2(initramfs_src, initramfs_dest) + def _get_mount_info(self): mountpoint = subprocess.check_output( ['findmnt', '/', '-l', '-n', '-o', 'SOURCE']) @@ -241,6 +281,13 @@ class SystemVersionManager(object): device = os.path.realpath(device) return device, current_system + def _get_device_uuid(self, device): + # Find block device's UUID. Does not work with busybox blkid, + # but given this is written in python, that's probably the least + # of our worries + return subprocess.check_output( + ['blkid', '-s', 'UUID', '-o', 'value', device]).strip() + def cmd_remove(self, system_name): self._check_system_exists(system_name) -- cgit v1.2.1