Esempio n. 1
0
    def __call__(
        self,
        location: Location,
        system_context: SystemContext,
        *args: typing.Any,
        **kwargs: typing.Any,
    ) -> None:
        """Execute command."""
        h2(
            f'Running tests for system "{system_context.system_name}"',
            verbosity=2,
        )
        env = _environment(system_context)

        for test in _find_tests(system_context):
            trace(f"{system_context.system_name}::Running test {test}...")
            test_result = run(
                test,
                system_context.system_name,
                env=env,
                returncode=None,
                work_directory=system_context.fs_directory,
            )
            if test_result.returncode == 0:
                success(
                    f'{system_context.system_name}::Test "{test}"',
                    verbosity=3,
                )
            else:
                report_completed_process(msg, test_result)
                fail(f'{system_context.system_name}::Test "{test}"')
Esempio n. 2
0
def run(
        *args: str,
        work_directory: str = "",
        check: bool = True,
        env: typing.Any = os.environ,  ## What is a better type for this?
) -> subprocess.CompletedProcess:
    env["LC_ALL"] = "en_US.UTF-8"

    cwd = work_directory or None
    trace('Running: "{}"...'.format('" "'.join(args)))
    result = subprocess.run(
        args,
        env=env,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        check=False,
        cwd=cwd,
    )
    if result.returncode != 0:
        debug(
            f'Command returned with exit code {result.returncode}:\nSTDOUT:\n{result.stdout.decode("utf-8")}\nSTDERR:\n{result.stderr.decode("utf-8")}.'
        )
    if result.returncode == 2 and check:
        raise subprocess.CalledProcessError(
            returncode=result.returncode,
            cmd=args,
            output=result.stdout,
            stderr=result.stderr,
        )
    return result
Esempio n. 3
0
def execute_with_system_mounted(to_execute: typing.Callable[[str, str], int],
                                *, image_file: str, tmp_dir: str) -> int:
    assert os.path.isfile(image_file)

    with disk.NbdDevice(image_file, disk_format="raw",
                        read_only=True) as device:
        verbose("Mounting EFI...")
        device.wait_for_device_node(partition=1)
        with mount.Mount(
                device.device(1),
                os.path.join(tmp_dir, "EFI"),
                fs_type="vfat",
                options="ro",
        ) as efi:
            verbose("Mounting root filesystem...")
            with mount.Mount(
                    device.device(2),
                    os.path.join(tmp_dir, "root"),
                    fs_type="squashfs",
                    options="ro",
            ) as root:

                trace(f'Executing with EFI "{efi}" and root "{root}".')
                result = to_execute(efi, root)

    return result
Esempio n. 4
0
def copy_efi_partition(*,
                       image_file: str,
                       efi_device, tempdir: str,
                       kernel_only: bool = True):
    verbose('Copying EFI configuration out of image file.')
    with disk.NbdDevice(image_file, disk_format='raw') \
            as internal_device:
        internal_device.wait_for_device_node(partition=1)
        with mount.Mount(internal_device.device(1),
                         os.path.join(tempdir, '_efi')) \
                as int_efi:
            with mount.Mount(efi_device,
                             os.path.join(tempdir, 'efi'), fs_type='vfat') \
                    as efi:
                if kernel_only:
                    img_dir = os.path.join(int_efi, 'EFI', 'Linux')
                    efi_dir = os.path.join(efi, 'EFI', 'Linux')
                    assert os.path.isdir(img_dir)
                    if not os.path.isdir(efi_dir):
                        os.makedirs(efi_dir)

                    for f in [f for f in os.listdir(img_dir)
                              if os.path.isfile(os.path.join(img_dir, f))]:
                        trace('Copying EFI kernel {}.'.format(f))
                        copyfile(os.path.join(img_dir, f),
                                 os.path.join(efi_dir, f))
                else:
                    trace('Copying EFI folder into system.')
                    copy_tree(int_efi, efi)
Esempio n. 5
0
    def _create_complete_kernel(
        self,
        location: Location,
        system_context: SystemContext,
        cmdline: str,
        *,
        kernel_file: str,
        efi_key: str,
        efi_cert: str,
    ):
        self._create_efi_kernel(
            location,
            system_context,
            cmdline,
            kernel_file=kernel_file,
        )

        if efi_key and efi_cert:
            debug("Signing EFI kernel.")
            location.set_description("Sign EFI kernel")
            self._execute(
                location.next_line(),
                system_context,
                "sign_efi_binary",
                kernel_file,
                key=efi_key,
                cert=efi_cert,
                outside=True,
                keep_unsigned=False,
            )

        trace(f"Validating existence of {kernel_file}.")
        assert os.path.isfile(kernel_file)
Esempio n. 6
0
def run(*args,
        work_directory: str = "",
        check: bool = True,
        env: typing.Dict[str,
                         str] = os.environ) -> subprocess.CompletedProcess:
    env["LC_ALL"] = "en_US.UTF-8"

    cwd = work_directory or None
    trace('Running borg: "{}"...'.format('" "'.join(args)))
    result = subprocess.run(args,
                            env=env,
                            capture_output=True,
                            check=False,
                            cwd=cwd)
    if result.returncode != 0:
        debug("Borg returned with exit code {}:\nSTDOUT:\n{}\nSTDERR:\n{}.".
              format(result.returncode, result.stdout, result.stderr))
    if result.returncode == 2 and check:
        raise subprocess.CalledProcessError(
            returncode=result.returncode,
            cmd=args,
            output=result.stdout,
            stderr=result.stderr,
        )

    return result
def _install_sysroot_setup_support(staging_area: str) -> typing.List[str]:
    write_file(
        os.path.join(staging_area,
                     "usr/lib/systemd/system/initrd-sysroot-setup.service"),
        textwrap.dedent("""\
                [Unit]
                Description=Set up root fs in /sysroot
                DefaultDependencies=no
                ConditionPathExists=/sysroot/usr/lib/boot/root-fs.tar
                Requires=sysroot.mount
                After=sysroot.mount systemd-volatile-root.service
                Before=initrd-root-fs.target shutdown.target
                Conflicts=shutdown.target
                AssertPathExists=/etc/initrd-release
                
                [Service]
                Type=oneshot
                RemainAfterExit=yes
                ExecStart=/usr/bin/tar -C /sysroot -xf /sysroot/usr/lib/boot/root-fs.tar
            """).encode("utf-8"),
        mode=0o644,
    )
    trace("Wrote initrd-sysroot-setup.service")
    symlink(
        os.path.join(
            staging_area,
            "usr/lib/systemd/system/initrd.target.wants/initrd-sysroot-setup.service",
        ),
        "../initrd-sysroot-setup.service",
    )

    return []
def _install_volatile_support(
        staging_area: str, system_context: SystemContext) -> typing.List[str]:
    shutil.copyfile(
        system_context.file_name(
            "/usr/lib/systemd/system/systemd-volatile-root.service"),
        os.path.join(staging_area,
                     "usr/lib/systemd/system/systemd-volatile-root.service"),
    )
    trace("Installed systemd-volatile-root.service")
    symlink(
        os.path.join(
            staging_area,
            "usr/lib/systemd/system/initrd.target.wants/systemd-volatile-root.service",
        ),
        "../systemd-volatile-root.service",
    )
    # Installing binaries is not a good idea in general, as dependencies are not handled!
    # These binaries are probably safe: systemd binaries tend to have few dependencies and those
    # that are included are most likely already in the image due to other systemd binaries!
    shutil.copyfile(
        system_context.file_name("/usr/lib/systemd/systemd-volatile-root"),
        os.path.join(staging_area, "usr/lib/systemd/systemd-volatile-root"),
    )
    os.chmod(
        os.path.join(staging_area, "usr/lib/systemd/systemd-volatile-root"),
        0o755)
    trace("Installed systemd-volatile-root binary.")

    return []
Esempio n. 9
0
def _copy_efi(src: str, dest: str) -> int:
    try:
        efi_path = os.path.join(dest, "EFI")
        os.makedirs(efi_path, exist_ok=True)

        trace("Copying bootloader.")

        efi_src_path = os.path.join(src, "EFI")

        dirs = [
            d for d in os.listdir(efi_src_path)
            if os.path.isdir(os.path.join(efi_src_path, d))
        ]
        for d in dirs:
            dest = os.path.join(efi_path, d)
            trace(f"Copying {os.path.join(efi_src_path, d)} to {dest}")
            copytree(
                os.path.join(efi_src_path, d),
                dest,
                dirs_exist_ok=True,
            )

        copytree(
            os.path.join(src, "loader"),
            os.path.join(dest, "loader"),
            dirs_exist_ok=True,
        )

    except Exception as e:
        debug(f"Failed to install EFI: {e}.")
        return 1
    else:
        debug("Successfully installed EFI")
        return 0
Esempio n. 10
0
    def __call__(
        self,
        location: Location,
        system_context: SystemContext,
        *args: typing.Any,
        **kwargs: typing.Any,
    ) -> None:
        """Execute command."""
        user = args[0]
        keyfile = args[1]

        info(f"Adding ssh key to {user}'s authorized_keys file.")
        data = UserHelper.user_data(user, root_directory=system_context.fs_directory)
        if data is None:
            raise GenerateError(
                f'"{self.name}" could not find user "{user}".', location=location,
            )

        trace(f"{user} mapping: UID {data.uid}, GID {data.gid}, home: {data.home}.")
        self._check_or_create_directory(
            location,
            system_context,
            data.home,
            mode=0o750,
            user=data.uid,
            group=data.gid,
        )
        ssh_directory = os.path.join(data.home, ".ssh")
        self._check_or_create_directory(
            location,
            system_context,
            ssh_directory,
            mode=0o700,
            user=data.uid,
            group=data.gid,
        )

        key = read_file(system_context, keyfile, outside=True).decode("utf-8")

        authorized_file = os.path.join(ssh_directory, "authorized_keys")
        line = ""

        options = kwargs.get("options", "")

        if options:
            line = options + " " + key + "\n"
        else:
            line += key + "\n"

        self._execute(
            location.next_line(),
            system_context,
            "append",
            authorized_file,
            line,
            force=True,
        )
        chown(system_context, data.uid, data.gid, authorized_file)
        chmod(system_context, 0o600, authorized_file)
Esempio n. 11
0
def _setup_btrfs(mount_point: str):
    trace('Creating subvolumes.')
    run('btrfs', 'subvol', 'create', '@btrfs',
        work_directory=mount_point)
    run('btrfs', 'subvol', 'create', '@home',
        work_directory=mount_point)
    run('btrfs', 'subvol', 'create', '@var',
        work_directory=mount_point)
    run('btrfs', 'subvol', 'create', '.images',
        work_directory=mount_point)
Esempio n. 12
0
def main(*command_args: str) -> int:
    known_install_targets: typing.List[InstallTarget] = [
        ContainerFilesystemInstallTarget(),
        CopyInstallTarget(),
        ImagePartitionInstallTarget(),
        MountInstallTarget(),
        PartitionInstallTarget(),
        QemuImageInstallTarget(),
        QemuInstallTarget(),
        TarballInstallTarget(),
    ]

    parse_result = _parse_commandline(*command_args,
                                      install_targets=known_install_targets)

    # Set up printing:
    pr = Printer.instance()
    pr.set_verbosity(parse_result.verbose)
    pr.show_verbosity_level()

    trace(f"Arguments parsed from command line: {parse_result}.")

    install_target = next(x for x in known_install_targets
                          if x.name == parse_result.subcommand)
    assert install_target
    debug(f"Install target {install_target.name} found.")

    with TemporaryDirectory(prefix=f"fs_{install_target.name}") as tmp_dir:
        trace(f"Using temporary directory: {tmp_dir}.")

        image_dir = os.path.join(tmp_dir, "borg")
        os.makedirs(image_dir)

        with BorgMount(
                image_dir,
                system_name=parse_result.system_name,
                repository=parse_result.repository,
                version=parse_result.system_version,
        ) as image_file:
            trace(f"Mounted borg directory with image file: {image_file}.")
            debug(
                f"Running install target with parse_args={parse_result}, tmp_dir={tmp_dir} and image_file={image_file}."
            )
            result = install_target(
                parse_result=parse_result,
                tmp_dir=tmp_dir,
                image_file=image_file,
            )
            debug(f"Install target done: return code: {result}.")
            trace("Starting cleanup.")

    trace(f"Done, leaving with return code {result}.")
    return result
Esempio n. 13
0
def _copy_efi(src: str,
              dest: str,
              *,
              include_bootloader: bool = False,
              overwrite: bool = False) -> int:
    try:
        efi_path = os.path.join(dest, "EFI")
        os.makedirs(efi_path, exist_ok=True)

        linux_src_path = os.path.join(src, "EFI/Linux")
        kernels = [
            f for f in os.listdir(linux_src_path)
            if os.path.isfile(os.path.join(linux_src_path, f))
        ]
        kernels_str = '", "'.join(kernels)
        debug(f'Found kernel(s): "{kernels_str}".')
        assert len(kernels) == 1
        kernel = kernels[0]

        _copy_file(
            os.path.join(linux_src_path, kernel),
            os.path.join(dest, "EFI/Linux"),
            overwrite=overwrite,
        )

        if include_bootloader:
            trace("Copying bootloader.")

            efi_src_path = os.path.join(src, "EFI")

            dirs = [
                d for d in os.listdir(efi_src_path) if d != "Linux"
                and os.path.isdir(os.path.join(efi_src_path, d))
            ]
            for d in dirs:
                trace(f"Copying {os.path.join(efi_src_path, d)} to {efi_path}")
                copytree(os.path.join(efi_src_path, d),
                         efi_path,
                         dirs_exist_ok=True)

            copytree(
                os.path.join(src, "loader"),
                os.path.join(dest, "loader"),
                dirs_exist_ok=True,
            )

    except Exception as e:
        debug(f"Failed to install EFI: {e}.")
        return 1
    else:
        debug("Successfully installed EFI")
        return 0
Esempio n. 14
0
def _get_initrd_parts(location: Location, path: str) -> typing.List[str]:
    initrd_parts: typing.List[str] = []
    for f in glob(os.path.join(path, "*")):
        if os.path.isfile(f):
            initrd_parts.append(f)
    if not initrd_parts:
        raise GenerateError(
            f'No initrd-parts found in directory "{path}".', location=location
        )
    initrd_parts.sort()
    for ip in initrd_parts:
        trace(f"    Adding into initrd: {ip} ...")
    return initrd_parts
def _module_dependencies(
        module_path: str,
        dependencies: typing.Dict[str, typing.List[str]]) -> typing.Set[str]:
    assert module_path
    trace(f"Finding dependencies for: {module_path}.")
    result: typing.Set[str] = set()
    result.add(module_path)

    for d in dependencies[module_path]:
        result = result.union(_module_dependencies(d, dependencies))

    trace(f"    => {result}.")
    return result
Esempio n. 16
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""
        user = args[0]
        keyfile = args[1]

        info('Adding ssh key to {}\'s authorized_keys file.'.format(user))
        data = UserHelper.user_data(user,
                                    root_directory=system_context.fs_directory)
        if data is None:
            raise GenerateError('"{}" could not find user "{}".'.format(
                self.name, user),
                                location=location)

        trace('{} mapping: UID {}, GID {}, home: {}.'.format(
            user, data.uid, data.gid, data.home))
        self._check_or_create_directory(location,
                                        system_context,
                                        data.home,
                                        mode=0o750,
                                        user=data.uid,
                                        group=data.gid)
        ssh_directory = os.path.join(data.home, '.ssh')
        self._check_or_create_directory(location,
                                        system_context,
                                        ssh_directory,
                                        mode=0o700,
                                        user=data.uid,
                                        group=data.gid)

        key = read_file(system_context, keyfile, outside=True).decode('utf-8')

        authorized_file = os.path.join(ssh_directory, 'authorized_keys')
        line = ''

        options = kwargs.get('options', '')

        if options:
            line = options + ' ' + key + '\n'
        else:
            line += key + '\n'

        self._execute(location.next_line(),
                      system_context,
                      'append',
                      authorized_file,
                      line,
                      force=True)
        chown(system_context, data.uid, data.gid, authorized_file)
        chmod(system_context, 0o600, authorized_file)
Esempio n. 17
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""
        old_base = system_context.file_name("/etc/systemd/system") + "/"
        new_base = system_context.file_name("/usr/lib/systemd/system") + "/"

        trace("walking:", old_base)

        for root, _dirs, files in os.walk(old_base):
            for f in files:
                full_path = os.path.join(root, f)
                trace("Checking", full_path)
                if os.path.islink(full_path):
                    trace("Moving link", full_path)
                    _move_symlink(location, system_context, old_base, new_base,
                                  full_path)
                else:
                    trace("Moving file", full_path)
                    _move_file(location, old_base, new_base, full_path)

        self._execute(
            location.next_line(),
            system_context,
            "remove",
            "/etc/systemd/system/*",
            recursive=True,
            force=True,
        )
Esempio n. 18
0
    def __call__(
        self,
        location: Location,
        system_context: SystemContext,
        *args: typing.Any,
        **kwargs: typing.Any,
    ) -> None:
        """Execute command."""
        to_sign = args[0]
        keep_unsigned = kwargs.get("keep_unsigned", False)
        if not kwargs.get("outside", False):
            to_sign = system_context.file_name(to_sign)
        systems_directory = system_context.systems_definition_directory
        key = os.path.join(systems_directory,
                           kwargs.get("key", "config/efi/sign.key"))
        cert = os.path.join(systems_directory,
                            kwargs.get("cert", "config/efi/sign.crt"))

        info(
            f"Signing EFI binary {to_sign} using key {key} and cert {cert} (keep unsigned: {keep_unsigned})."
        )

        signed = to_sign + ".signed"

        assert os.path.isfile(key)
        assert os.path.isfile(cert)
        assert os.path.isfile(to_sign)
        assert not os.path.exists(signed)

        run(
            self._binary(Binaries.SBSIGN),
            "--key",
            key,
            "--cert",
            cert,
            "--output",
            signed,
            to_sign,
        )

        if keep_unsigned:
            assert os.path.isfile(to_sign)
            assert os.path.isfile(signed)
        else:
            trace(f"Moving {signed} to {to_sign}.")
            os.remove(to_sign)
            os.rename(signed, to_sign)

            assert os.path.isfile(to_sign)
def _kernel_module_map(module_dir: str) -> typing.Dict[str, str]:
    known_modules: typing.Dict[str, str] = {}
    for root, _, files in os.walk(module_dir):
        for f in files:
            if "modules." in f:
                continue

            module_name = _strip_extensions(f).replace("_", "-")
            module_path = os.path.relpath(os.path.join(root, f), module_dir)
            assert module_name not in known_modules
            trace(
                f'Found "{module_name}" at "{os.path.join(root, f)}" ({module_path})'
            )
            known_modules[module_name] = module_path
    return known_modules
def _copy_modules(
    system_context: SystemContext,
    modules: typing.List[str],
    modules_dir: str,
    etc_dir: str,
    *,
    kernel_version: str,
):
    src_top = os.path.join(system_context.file_name("/usr/lib/modules"),
                           kernel_version)
    target_top = os.path.join(modules_dir, kernel_version)

    known_modules = _kernel_module_map(src_top)

    to_load: typing.List[str] = []
    to_install: typing.Set[str] = set()

    debug(f'Installing modules from "{src_top}" into "{target_top}"')
    for module in modules:
        m = module.replace("_", "-")
        if m not in known_modules:
            info(f"Module {m} not found. Was it built into the kernel?")
            continue
        p = known_modules[m]
        if p not in to_install:
            to_install.add(p)
            to_load.append(m)

    _write_modules_load_file(etc_dir, to_load)

    if not to_install:
        return

    # Install dependencies:
    dependencies = _parse_module_dependencies(src_top)

    to_test = to_install
    for t in to_test:
        to_install = to_install.union(_module_dependencies(t, dependencies))

    for i in to_install:
        os.makedirs(os.path.dirname(os.path.join(target_top, i)),
                    exist_ok=True)
        trace(f"Copying module {i}.")
        trace(
            f'    "{os.path.join(src_top, i)}" => "{os.path.join(target_top, i)}".'
        )
        shutil.copy2(os.path.join(src_top, i), os.path.join(target_top, i))
Esempio n. 21
0
def _find_tests(system_context: SystemContext) -> typing.Generator[str, None, None]:
    """Find tests to run."""
    tests_directory = system_context.system_tests_directory
    debug('Searching for tests in "{}".'.format(tests_directory))

    for f in sorted(os.listdir(tests_directory)):
        test = os.path.join(tests_directory, f)
        if not os.path.isfile(test):
            trace('"{}": Not a file, skipping.'.format(test))
            continue
        if not os.access(test, os.X_OK):
            trace('"{}": Not executable, skipping.'.format(test))
            continue

        info('Found test: "{}"'.format(test))
        yield test
def _install_etc_shadow(staging_area: str,
                        system_context: SystemContext) -> typing.List[str]:
    shadow_file = system_context.file_name("/etc/shadow.initramfs")
    if os.path.exists(shadow_file):
        os.makedirs(os.path.join(staging_area, "etc"), exist_ok=True)

        shutil.copyfile(
            shadow_file,
            os.path.join(
                staging_area,
                "etc/shadow",
            ),
        )
        os.chmod(os.path.join(staging_area, "etc/shadow"), 0o600)
        trace("Installed /etc/shadow.initramfs as /etc/shadow into initrd.")
    return []
Esempio n. 23
0
def create_qemu_image(
    image_path: str,
    *,
    image_size: int,
    image_format: str = "qcow2",
    system_image_file: str,
    tmp_dir: str,
) -> str:
    trace(f"Creating image file {image_path}.")
    with disk.NbdDevice.new_image_file(image_path,
                                       image_size,
                                       disk_format=image_format) as device:
        _create_hdd_image(device)

        debug("mounting data partition for further setup.")
        with mount.Mount(
                device.device(3),
                os.path.join(tmp_dir, "data"),
                fs_type="btrfs",
                options="subvolid=0",
                fallback_cwd=os.getcwd(),
        ) as data_dir:
            _setup_btrfs(data_dir)

            trace("Copying image file")
            copyfile(
                system_image_file,
                os.path.join(data_dir, ".images",
                             os.path.basename(system_image_file)),
            )

            with mount.Mount(
                    device.device(1),
                    os.path.join(tmp_dir, "efi_dest"),
                    options="defaults",
                    fs_type="vfat",
            ) as efi_dest_mnt:
                tool.execute_with_system_mounted(
                    lambda e, _: _copy_efi(
                        e,
                        efi_dest_mnt,
                    ),
                    image_file=system_image_file,
                    tmp_dir=tmp_dir,
                )

    return image_path
Esempio n. 24
0
def _move_symlink(
    location: Location,
    system_context: SystemContext,
    old_base: str,
    new_base: str,
    link: str,
):
    """Move a symlink."""
    root_directory = system_context.fs_directory + "/"
    link_target = os.readlink(link)
    # normalize to /usr/lib...
    if link_target.startswith("/lib/"):
        link_target = f"/usr{link_target}"
    (output_link, output_link_target) = _map_host_link(root_directory,
                                                       old_base, new_base,
                                                       link, link_target)

    trace(
        f"Moving link {link}->{link_target}: {output_link} to {output_link_target}"
    )
    os.makedirs(os.path.dirname(output_link), mode=0o755, exist_ok=True)

    if not os.path.isdir(os.path.dirname(output_link)):
        raise GenerateError(
            f'"{output_link}" is no directory when trying to move "{link}" into /usr.',
            location=location,
        )

    if os.path.exists(output_link):
        if not os.path.islink(output_link):
            raise GenerateError(
                f'"{output_link}" exists and is not a link when trying to move "{link}" into /usr.',
                location=location,
            )
        else:
            old_link_target = os.readlink(output_link)
            if old_link_target != output_link_target:
                raise GenerateError(
                    f'"{link}" exists but points to "{old_link_target}" when "{output_link_target}" was expected.',
                    location=location,
                )
            else:
                os.unlink(link)
                return  # Already correct
    else:
        os.symlink(output_link_target, output_link)
        os.unlink(link)
Esempio n. 25
0
def create_qemu_image(image_path: str,
                      *,
                      image_size: str,
                      image_format: str = "qcow2",
                      system_name: str,
                      system_version: str = "",
                      repository: str,
                      tempdir: str) -> str:
    trace("Creating image file {}.".format(image_path))
    with disk.NbdDevice.new_image_file(image_path,
                                       image_size,
                                       disk_format=image_format) as device:
        _create_hdd_image(device)

        debug("mounting data partition for further setup.")
        with mount.Mount(
                device.device(3),
                os.path.join(tempdir, "data"),
                fs_type="btrfs",
                options="subvolid=0",
                fallback_cwd=os.getcwd(),
        ) as data_dir:
            _setup_btrfs(data_dir)

            extract_location = os.path.join(data_dir, ".images")
            verbose("Extracting system image to {}.".format(extract_location))
            extracted_version = tool.write_image(
                system_name,
                extract_location,
                repository=repository,
                version=system_version,
            )

            extracted_image = os.path.join(data_dir, ".images",
                                           "clrm_{}".format(extracted_version))
            assert os.path.isfile(extracted_image)

            tool.copy_efi_partition(
                image_file=extracted_image,
                efi_device=device.device(1),
                tempdir=tempdir,
                kernel_only=False,
            )

    return image_path
def _install_lvm_support(staging_area: str, vg: typing.Optional[str],
                         image_name: str) -> typing.List[str]:
    if not vg:
        return []

    device_name = f"dev-{vg}-{image_name}"
    write_file(
        os.path.join(
            staging_area,
            "usr/lib/systemd/system/initrd-find-root-lv-partitions.service",
        ),
        textwrap.dedent(f"""\
                [Unit]
                Description=Find partitions in root LV
                DefaultDependencies=no
                ConditionPathExists=/dev/{vg}/{image_name}
                After={device_name}.device
                Before=shutdown.target
                BindsTo={device_name}.device
                Requisite={device_name}.device
                Conflicts=shutdown.target

                [Service]
                WorkingDirectory=/
                Type=oneshot
                RemainAfterExit=yes
                ExecStart=/usr/bin/partprobe /dev/{vg}/{image_name}

                [Install]
                WantedBy={device_name}.device
                """).encode("utf-8"),
        mode=0o644,
    )
    trace("Wrote initrd-find-root-lv-partitions.service")
    symlink(
        os.path.join(
            staging_area,
            f"usr/lib/systemd/system/dev-{vg}-{image_name}.device.wants/initrd-find-root-lv-partitions.service",
        ),
        "../initrd-find-root-lv-partitions.service",
    )

    return []
def _install_shadow_file(staging_area: str, system_context: SystemContext):
    for shadow_file in (
            system_context.file_name("/etc/shadow.initramfs"),
            system_context.file_name(
                "/usr/share/defaults/etc/shadow.initramfs"),
    ):
        if os.path.exists(shadow_file):
            os.makedirs(os.path.join(staging_area, "etc"), exist_ok=True)

            shutil.copyfile(
                shadow_file,
                os.path.join(
                    staging_area,
                    "etc/shadow",
                ),
            )
            os.chmod(os.path.join(staging_area, "etc/shadow"), 0o600)
            os.remove(shadow_file)
            trace(
                "Installed /etc/shadow.initramfs as /etc/shadow into initrd.")
Esempio n. 28
0
def _move_symlink(location, system_context,
                  old_base, new_base, link):
    """Move a symlink."""
    root_directory = system_context.fs_directory + '/'
    link_target = os.readlink(link)
    # normalize to /usr/lib...
    if link_target.startswith('/lib/'):
        link_target = '/usr{}'.format(link_target)
    (output_link, output_link_target) \
        = _map_host_link(root_directory, old_base, new_base,
                         link, link_target)

    trace('Moving link {}->{}: {} to {}'
          .format(link, link_target, output_link, output_link_target))
    os.makedirs(os.path.dirname(output_link), mode=0o755, exist_ok=True)

    if not os.path.isdir(os.path.dirname(output_link)):
        raise GenerateError('"{}" is no directory when trying to move '
                            '"{}" into /usr.'.format(output_link, link),
                            location=location)

    if os.path.exists(output_link):
        if not os.path.islink(output_link):
            raise GenerateError('"{}" exists and is not a link when '
                                'trying to move "{}" into /usr.'
                                .format(output_link, link),
                                location=location)
        else:
            old_link_target = os.readlink(output_link)
            if old_link_target != output_link_target:
                raise GenerateError('"{}" exists but points to "{}" '
                                    'when "{}" was expected.'
                                    .format(link, old_link_target,
                                            output_link_target),
                                    location=location)
            else:
                os.unlink(link)
                return  # Already correct
    else:
        os.symlink(output_link_target, output_link)
        os.unlink(link)
Esempio n. 29
0
def find_archive(system_name: str, *, repository: str, version: str = '') \
        -> typing.Tuple[str, str]:
    borg_list = run_borg('list', repository)

    archive_to_use = ''
    for line in borg_list.stdout.decode('utf-8').split('\n'):
        if not line.startswith(system_name):
            continue
        trace('Borg list: {}.'.format(line))
        versioned_system_name = line.split(' ')[0]
        assert versioned_system_name[len(system_name)] == '-'
        current_version = versioned_system_name[len(system_name) + 1:]
        if version:
            if current_version == version:
                archive_to_use = versioned_system_name
                break
        else:
            if not archive_to_use or versioned_system_name > archive_to_use:
                archive_to_use = versioned_system_name

    return archive_to_use, archive_to_use[len(system_name) + 1:]
Esempio n. 30
0
def find_archive(system_name: str,
                 *,
                 repository: str,
                 version: str = "") -> typing.Tuple[str, str]:
    borg_list = run_borg("list", repository)

    archive_to_use = ""
    for line in borg_list.stdout.decode("utf-8").split("\n"):
        if not line.startswith(system_name):
            continue
        trace(f"Borg list: {line}.")
        versioned_system_name = line.split(" ")[0]
        assert versioned_system_name[len(system_name)] == "-"
        current_version = versioned_system_name[len(system_name) + 1:]
        if version:
            if current_version == version:
                archive_to_use = versioned_system_name
                break
        else:
            if not archive_to_use or versioned_system_name > archive_to_use:
                archive_to_use = versioned_system_name

    return archive_to_use, archive_to_use[len(system_name) + 1:]