Example #1
0
    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
Example #2
0
    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}"))
Example #3
0
    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", "")
Example #4
0
    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
Example #6
0
    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)