def _create_root_fsimage(self, location: Location, system_context: SystemContext, *, usr_only: bool) -> str: rootfs_label = system_context.substitution_expanded( "ROOTFS_PARTLABEL", "") if not rootfs_label: raise GenerateError("ROOTFS_PARTLABEL is unset.") squashfs_file = os.path.join( system_context.cache_directory, rootfs_label, ) self._execute( location, system_context, "_create_root_fsimage", squashfs_file, usr_only=usr_only, ) return squashfs_file
def _create_initrd(self, location: Location, system_context: SystemContext): location.set_description("Create initrd") initrd_parts = os.path.join(system_context.boot_directory, "initrd-parts") os.makedirs(initrd_parts, exist_ok=True) initrd_generator = system_context.substitution_expanded( "INITRD_GENERATOR", "mkinitcpio") assert initrd_generator self._execute( location.next_line(), system_context, f"_create_initrd_{initrd_generator}", os.path.join(initrd_parts, f"50-{initrd_generator}"), ) assert os.path.exists( os.path.join(system_context.boot_directory, f"initrd-parts/50-{initrd_generator}"))
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" cert = kwargs.get("efi_cert", "") efi_emulator = kwargs.get("efi_emulator", "") key = kwargs.get("efi_key", "") skip_validation = kwargs.get("skip_validation", False) repository = args[0] repository_compression = kwargs.get("repository_compression", "zstd") repository_compression_level = kwargs.get( "repository_compression_level", 5) usr_only = kwargs.get("usr_only", True) debug_initrd = kwargs.get("debug_initrd", False) h2(f'Exporting system "{system_context.system_name}".') debug("Running Hooks.") self._run_all_exportcommand_hooks(system_context) verbose("Preparing system for export.") self._execute(location.next_line(), system_context, "_write_deploy_info") # Create some extra data: self._create_root_tarball(location, system_context) root_partition = self._create_root_fsimage(location, system_context, usr_only=usr_only) assert root_partition (verity_partition, root_hash) = self._create_rootverity_fsimage( location, system_context, rootfs=root_partition, ) assert root_hash has_kernel = os.path.exists( os.path.join(system_context.boot_directory, "vmlinuz")) if has_kernel: self._create_initrd(location, system_context) self._create_clrm_config_initrd(location, system_context, root_hash, debug=debug_initrd) cmdline = system_context.set_or_append_substitution( "KERNEL_CMDLINE", "systemd.volatile=true rootfstype=squashfs") cmdline = _setup_kernel_commandline(cmdline, root_hash) kernel_file = "" if has_kernel: trace( f'KERNEL_FILENAME: {system_context.substitution("KERNEL_FILENAME", "")}' ) kernel_file = os.path.join( system_context.boot_directory, system_context.substitution_expanded("KERNEL_FILENAME", ""), ) assert kernel_file self._create_complete_kernel( location, system_context, cmdline, kernel_file=kernel_file, efi_key=key, efi_cert=cert, ) efi_partition = os.path.join(system_context.cache_directory, "efi_partition.img") self._create_efi_partition( location, system_context, efi_partition=efi_partition, kernel_file=kernel_file, efi_emulator=efi_emulator, root_hash=root_hash, ) info("Validating installation for export.") if not skip_validation: _validate_installation(location.next_line(), system_context) export_directory = self.create_export_directory(system_context) assert export_directory self.create_image( location, system_context, export_directory, efi_partition=efi_partition, root_partition=root_partition, verity_partition=verity_partition, root_hash=root_hash, ) system_context.set_substitution("EXPORT_DIRECTORY", export_directory) verbose(f"Exporting all data in {export_directory}.") self._execute( location.next_line(), system_context, "_export_directory", export_directory, compression=repository_compression, compression_level=repository_compression_level, repository=repository, ) info("Cleaning up export location.") self.delete_export_directory(export_directory) system_context.set_substitution("EXPORT_DIRECTORY", "")
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" if not os.path.exists(os.path.join(system_context.boot_directory, "vmlinuz")): info("Skipping initrd generation: No vmlinuz in boot directory.") return initrd = args[0] self._install_dracut(location, system_context) copy( system_context, os.path.join(system_context.boot_directory, "vmlinuz"), "/boot/vmlinuz", from_outside=True, ) dracut_args: typing.List[str] = [] modules = ( system_context.substitution_expanded("INITRD_EXTRA_MODULES", "") .replace(",", " ") .replace(" ", " ") .split(" ") ) modules = list(set(modules)) modules.sort() if modules: dracut_args += [ "--add-drivers", " ".join(modules), ] run( "/usr/bin/dracut", *dracut_args, "--no-early-microcode", "--no-hostonly", "--no-compress", "--reproducible", "--omit", "iscsi nbd network network-legacy nfs qemu qemu-net stratis", "--add", "busybox", "/boot/initramfs.img", chroot=system_context.fs_directory, chroot_helper=self._binary(Binaries.CHROOT_HELPER), ) initrd_directory = os.path.dirname(initrd) os.makedirs(initrd_directory, exist_ok=True) move(system_context, "/boot/initramfs.img", initrd, to_outside=True) self._remove_dracut(location, system_context) assert os.path.isfile(initrd)
def _install_mkinitcpio( self, location: Location, system_context: SystemContext ) -> typing.Sequence[str]: to_clean_up = ["/etc/mkinitcpio.d", "/etc/mkinitcpio.conf", "/boot/vmlinu*"] location.set_description("Install mkinitcpio") self._execute(location, system_context, "pacman", "mkinitcpio") location.set_description("Fix up mkinitcpio.conf") self._execute( location.next_line(), system_context, "sed", "/^HOOKS=/ " "cHOOKS=(base systemd keyboard sd-vconsole " "sd-encrypt block sd-lvm2 filesystems btrfs " "sd-shutdown)", "/etc/mkinitcpio.conf", ) self._fix_mkinitcpio_modules( location.next_line(), system_context, system_context.substitution_expanded("INITRD_EXTRA_MODULES", ""), ) self._execute( location.next_line(), system_context, "append", "/etc/mkinitcpio.conf", 'COMPRESSION="cat"', ) location.set_description("Create mkinitcpio presets") create_file( system_context, "/etc/mkinitcpio.d/cleanroom.preset", textwrap.dedent( """\ # mkinitcpio preset file for cleanroom ALL_config="/etc/mkinitcpio.conf" ALL_kver="/boot/vmlinuz" PRESETS=('default') #default_config="/etc/mkinitcpio.conf" default_image="/boot/initramfs.img" #default_options="" """ ).encode("utf-8"), ) self._execute( location.next_line(), system_context, "sed", "s%/initramfs-linux.*.img%/initrd%", "/etc/mkinitcpio.d/cleanroom.preset", ) return to_clean_up
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" name = args[0] if not name.endswith(".conf"): name += ".conf" device_id = kwargs.get("device", "disk0") assert not " " in device_id contents = "[Partition]\n" type = kwargs.get("type", "") contents += f"Type={type}\n" label = kwargs.get("label", "") if label: contents += f"Label={label}\n" uuid = kwargs.get("uuid", "") if uuid: contents += f"UUID={uuid}\n" priority = kwargs.get("priority", 0) if priority != 0: contents += f"Priority={priority}\n" weight = kwargs.get("weight", 1000) if weight != 1000: contents += f"Weight={weight}\n" padding_weight = kwargs.get("paddingWeight", 0) if padding_weight != 0: contents += f"PaddingWeight={padding_weight}\n" minSize = kwargs.get("minSize", "") if minSize: contents += f"SizeMinBytes={minSize}\n" maxSize = kwargs.get("maxSize", "") if maxSize: contents += f"SizeMaxBytes={maxSize}\n" minPadding = kwargs.get("minPadding", "") if minPadding: contents += f"PaddingMinBytes={minPadding}\n" maxPadding = kwargs.get("maxPadding", "") if maxPadding: contents += f"PaddingMaxBytes={maxPadding}\n" rel_path = os.path.join( system_context.substitution("DISTRO_ID"), "repart.d", device_id, ) path = os.path.join(system_context.boot_directory, "extra", rel_path,) filename = os.path.join(path, name) trace(f"Creating repart.d file {filename} (relative: {rel_path}).") os.makedirs(path, mode=0o750, exist_ok=True) with open(filename, "wb") as f: f.write(contents.encode("utf-8")) os.chown(filename, 0, 0, follow_symlinks=False) os.chmod(filename, 0o644) # set up substitutions: device_ids = system_context.substitution_expanded( "DEPLOY_DEVICE_IDS", "" ).split() if not device_id in device_ids: device_ids.append(device_id) system_context.set_substitution("DEPLOY_DEVICE_IDS", " ".join(device_ids)) key = f"DEPLOY_{device_id}_REPART_D" repart_path = system_context.substitution_expanded(key, "") if not repart_path: trace(f"Setting {key} to {rel_path}.") system_context.set_substitution(key, rel_path) else: assert rel_path == repart_path if type == "esp" and uuid: system_context.set_substitution("EFI_PARTITION_PARTUUID", uuid)
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" extra_dir = os.path.join(system_context.boot_directory, "extra") os.makedirs(extra_dir, exist_ok=True) deployment_type = system_context.substitution_expanded( "DEPLOY_TYPE", "") deployment_parameters: typing.List[str] = [] if not deployment_type: image_device = system_context.substitution_expanded( "IMAGE_DEVICE", "") if image_device: deployment_type = "image_partition" efi_partition = system_context.substitution_expanded( "EFI_PARTITION_PARTUUID", "") if not efi_partition: raise GenerateError( "No EFI_PARTITION_PARTUUID substitution defined when trying to write deploy.json", location=location, ) image_fs = system_context.substitution_expanded("IMAGE_FS", "") assert image_fs image_options = system_context.substitution_expanded( "IMAGE_OPTIONS", "") if image_options: image_options = f"nodev,nosuid,noexec,{image_options}" else: image_options = "nodev,nosuid,noexec" deployment_parameters = [ f"--efi-device=PARTUUID={efi_partition}", f"--image-device={image_device}", f"--image-fs-type={image_fs}", f"--image-options={image_options}", ] deployment_parameters += system_context.substitution_expanded( "DEPLOY_TYPE_EXTRA_PARAMETERS", "").split() devices: typing.List[typing.Dict[str, str]] = [] device_ids = system_context.substitution_expanded( "DEPLOY_DEVICE_IDS", "").split() for di in device_ids: assert di path = system_context.substitution_expanded( f"DEPLOY_{di}_REPART_D", "") assert path devices.append({"device_id": di, "repart_d": path}) data: typing.Dict[str, typing.Any] = {} data["version"] = 1 data["type"] = deployment_type if deployment_parameters: data["parameters"] = deployment_parameters if devices: data["storage"] = devices extra_systems = system_context.substitution_expanded( "DEPLOY_EXTRA_SYSTEMS", "").split() if extra_systems: data["extra_systems"] = extra_systems deploy_file = os.path.join(extra_dir, "deploy.json") with open(deploy_file, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=4)
def __call__( self, location: Location, system_context: SystemContext, *args: typing.Any, **kwargs: typing.Any, ) -> None: """Execute command.""" if not os.path.exists( os.path.join(system_context.boot_directory, "vmlinuz")): info("Skipping initrd generation: No vmlinuz in boot directory.") return root_hash = kwargs.get("root_hash", "") debug = kwargs.get("debug", False) vg = system_context.substitution_expanded("DEFAULT_VG", None) image_fs = system_context.substitution_expanded("IMAGE_FS", None) image_device = _device_ify( system_context.substitution_expanded("IMAGE_DEVICE", None)) image_options = system_context.substitution_expanded( "IMAGE_OPTIONS", "") image_name = system_context.substitution_expanded( "CLRM_IMAGE_FILENAME", "") kernel_version = system_context.substitution_expanded( "KERNEL_VERSION", "") assert kernel_version extra_modules = system_context.substitution_expanded( "INITRD_EXTRA_MODULES", "").split() initrd = args[0] assert not os.path.exists(initrd) staging_area = os.path.join(system_context.cache_directory, "clrm_extra") os.makedirs(staging_area) with TemporaryDirectory(prefix="clrm_cpio_") as tmp: _extract_initrds(system_context, tmp, cpio_command=self._binary(Binaries.CPIO)) cpio_files = _hash_directory(tmp) modules: typing.List[str] = [ *extra_modules, *system_context.substitution_expanded("INITRD_EXTRA_MODULES", "").split(","), "squashfs", *_install_image_file_support(tmp, image_fs, image_device, image_options, image_name), *_install_lvm_support(tmp, vg, image_name), *_install_sysroot_setup_support(tmp, system_context), *_install_verity_support(tmp, system_context, root_hash), *_install_volatile_support(tmp, system_context), *_install_var_mount_support(tmp, system_context), ] modules = [m for m in modules if m ] # Trim empty modules (e.g. added by the substitution) _install_extra_modules( system_context, tmp, modules, kernel_version=kernel_version, depmod_command=self._binary(Binaries.DEPMOD), ) if debug: _install_debug_support( system_context, tmp, ) updated_files = _hash_directory(tmp) for t in [ k for k, v in updated_files.items() if k not in cpio_files or cpio_files[k] != v ]: target = os.path.join(staging_area, t) src = os.path.join(tmp, t) trace( f' Installing module-related file: "{src}" => "{target}".' ) os.makedirs(os.path.dirname(target), exist_ok=True) shutil.copy2(src, target, follow_symlinks=False) trace(f"Copied {target} into {staging_area}.") # Create Initrd: run( "/bin/sh", "-c", f'"{self._binary(Binaries.FIND)}" . | "{self._binary(Binaries.CPIO)}" -o -H newc > "{initrd}"', work_directory=staging_area, ) assert os.path.exists(initrd)