From 61f116bc5367e8ba26e8fd0f48aa71e644cb9b1e Mon Sep 17 00:00:00 2001 From: James Thomas Date: Thu, 20 Nov 2014 11:03:18 +0000 Subject: Support booting from a different partition using BOOT_DEVICE This allows bootloaders without btrfs support (e.g u-boot) to boot from an ext partition --- system-version-manager/system-version-manager | 58 ++++++++++++++++++--------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/system-version-manager/system-version-manager b/system-version-manager/system-version-manager index dcd7b80..2f46dfc 100755 --- a/system-version-manager/system-version-manager +++ b/system-version-manager/system-version-manager @@ -41,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.boot_dir = mount_dir self.device_uuid = self._get_device_uuid(self.device) # create the top-level parser @@ -103,7 +104,7 @@ class SystemVersionManager(object): # value in the extlinux.conf file. If it's not present, then pick # the first of the present systems. def _get_default(self): - extlinux = os.path.join(self.mount_dir, 'extlinux.conf') + extlinux = os.path.join(self.boot_dir, 'extlinux.conf') with open(extlinux, 'r') as f: for line in f: line = line.rstrip('\n') @@ -144,8 +145,8 @@ class SystemVersionManager(object): def _rewrite_boot_menu(self, device, default, systems): # Logic copied from morphlib.SaveFile to not create # a morphlib dependency. - fd, temp_config = tempfile.mkstemp(dir=self.mount_dir) - config = os.path.join(self.mount_dir, 'extlinux.conf') + fd, temp_config = tempfile.mkstemp(dir=self.boot_dir) + config = os.path.join(self.boot_dir, 'extlinux.conf') with os.fdopen(fd, 'w') as f: # If theres no menu.c32 file, add a menu to the extlinux.conf file if self._check_system_syslinux(): @@ -174,8 +175,13 @@ class SystemVersionManager(object): kernel_args += deployment_config.get('KERNEL_ARGS', '') f.write('append %s\n' % kernel_args) os.rename(temp_config, config) + self._update_default_symlink(default, self.mount_dir) + # We may also need to do this for the boot partition + if self.mount_dir != self.boot_dir: + self._update_default_symlink(default, self.boot_dir) - default_path = os.path.join(self.mount_dir, 'systems', 'default') + def _update_default_symlink(self, default, mount_dir): + default_path = os.path.join(mount_dir, 'systems', 'default') if os.path.islink(default_path): self._atomic_symlink_update(default, default_path) @@ -216,6 +222,10 @@ class SystemVersionManager(object): label = self._parse_deploy_location(location) version_root = os.path.join(self.mount_dir, 'systems', label) + install_root = version_root + if self.mount_dir != self.boot_dir: + install_root = os.path.join(self.boot_dir, 'systems', label) + subprocess.call(['mkdir', '-p', install_root]) orig_dir = os.path.join(version_root, 'orig') run_dir = os.path.join(version_root, 'run') @@ -245,17 +255,17 @@ class SystemVersionManager(object): subprocess.call(['cp', '-a', os.path.join(new_var, file), shared_var]) self.status(msg="Installing the kernel") - self._install_kernel(version_root) + self._install_kernel(version_root, install_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) + version_root, install_root) if 'DTB_PATH' in deployment_config: self.status(msg="Installing the device tree") self._install_dtb(deployment_config['DTB_PATH'], - version_root) + version_root, install_root) except Exception as e: # We are not controlling if deleting the suvolume fails @@ -265,21 +275,21 @@ class SystemVersionManager(object): self.status(msg="Rewriting boot menu") self._rewrite_boot_menu(self.device, self._get_default(), self._get_systems()) - def _install_kernel(self, version_root): + def _install_kernel(self, version_root, dest_root): '''Install the kernel outside of 'orig' or 'run' subvolumes This code is kind of duplicated in morphlib/writeexts.py. ''' image_names = ['vmlinuz', 'zImage', 'uImage'] - kernel_dest = os.path.join(version_root, 'kernel') + kernel_dest = os.path.join(dest_root, 'kernel') for name in image_names: try_path = os.path.join(version_root, 'run', 'boot', name) if os.path.exists(try_path): shutil.copy2(try_path, kernel_dest) break - def _install_dtb(self, dtb_path, version_root): + def _install_dtb(self, dtb_path, version_root, dest_root): '''Install the devicetree outside of 'orig' or 'run' subvolumes This code is kind of duplicated in morphlib/writeexts.py. @@ -287,17 +297,17 @@ class SystemVersionManager(object): ''' self.status(msg='Installing devicetree') self.status(msg='Device tree path=%s' % dtb_path) - dtb_dest = os.path.join(version_root, 'dtb') + dtb_dest = os.path.join(dest_root, 'dtb') try_path = os.path.join(version_root, 'run', dtb_path) shutil.copy2(try_path, dtb_dest) - def _install_initramfs(self, initramfs_path, version_root): + def _install_initramfs(self, initramfs_path, version_root, dest_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_dest = os.path.join(dest_root, 'initramfs') initramfs_src = os.path.join(version_root, 'run', initramfs_path) shutil.copy2(initramfs_src, initramfs_dest) @@ -347,16 +357,16 @@ class SystemVersionManager(object): self._check_system_exists(system_name) self._rewrite_boot_menu(self.device, system_name, self._get_systems()) - def mount_fs(self): - subprocess.check_call(['mount', self.device, self.mount_dir]) + def mount_fs(self, device, mount_dir): + subprocess.check_call(['mount', device, mount_dir]) - def umount_fs(self): - subprocess.call(['umount', self.mount_dir]) + def umount_fs(self, mount_dir): + subprocess.call(['umount', mount_dir]) def _check_system_syslinux(self): # It's not essential to have a menu.c32 file, if it's not there we can # add a menu directly to the extlinux.conf file later - menu_file = os.path.join(self.mount_dir, 'menu.c32') + menu_file = os.path.join(self.boot_dir, 'menu.c32') if not os.path.isfile(menu_file): return False return True @@ -364,8 +374,13 @@ class SystemVersionManager(object): def run(self): args = self.args action = args.action + self.mount_fs(self.device, self.mount_dir) + + deployment_config = self._get_deployment_config(self.current_system) + if 'BOOT_DEVICE' in deployment_config: + self.boot_dir = tempfile.mkdtemp() + self.mount_fs(deployment_config['BOOT_DEVICE'], self.boot_dir) - self.mount_fs() try: if action == "list": self.cmd_list() @@ -385,7 +400,10 @@ class SystemVersionManager(object): sys.stderr.write("ERROR, system not compatible: %s\n" % e.args[0]) raise finally: - self.umount_fs() + self.umount_fs(self.mount_dir) + if self.mount_dir != self.boot_dir: + self.umount_fs(self.boot_dir) + os.rmdir(self.boot_dir) mount_dir = tempfile.mkdtemp() try: -- cgit v1.2.1 From 7e9fc9e90b9f766cdd0bf7dbd142088dd604c1a5 Mon Sep 17 00:00:00 2001 From: James Thomas Date: Thu, 20 Nov 2014 12:22:18 +0000 Subject: Check multiple locations for extlinux.conf U-boot wants the extlinux.conf to be in /extlinux/extlinux.conf, rather than /, so if we don't find it there, search in /extlinux --- system-version-manager/system-version-manager | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/system-version-manager/system-version-manager b/system-version-manager/system-version-manager index 2f46dfc..554e459 100755 --- a/system-version-manager/system-version-manager +++ b/system-version-manager/system-version-manager @@ -100,11 +100,22 @@ class SystemVersionManager(object): if os.path.isdir(os.path.join(systems, filename)) and not os.path.islink(os.path.join(systems, filename))] + # U-boot expects extlinux.conf to be in an extlinux subfolder, so also + # check for this if /extlinux.conf isn't found + def _get_extlinux_path(self): + file_paths = ['extlinux.conf','extlinux/extlinux.conf'] + for path in file_paths: + extlinux_path = os.path.join(self.boot_dir, path) + if os.path.isfile(extlinux_path): + return extlinux_path + raise AppException( + "No extlinux.conf file found") + # To check which system is the default one, it checks the 'ontimeout' # value in the extlinux.conf file. If it's not present, then pick # the first of the present systems. def _get_default(self): - extlinux = os.path.join(self.boot_dir, 'extlinux.conf') + extlinux = os.path.join(self.boot_dir, self._get_extlinux_path()) with open(extlinux, 'r') as f: for line in f: line = line.rstrip('\n') @@ -146,7 +157,7 @@ class SystemVersionManager(object): # Logic copied from morphlib.SaveFile to not create # a morphlib dependency. fd, temp_config = tempfile.mkstemp(dir=self.boot_dir) - config = os.path.join(self.boot_dir, 'extlinux.conf') + config = os.path.join(self.boot_dir, self._get_extlinux_path()) with os.fdopen(fd, 'w') as f: # If theres no menu.c32 file, add a menu to the extlinux.conf file if self._check_system_syslinux(): -- cgit v1.2.1