Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Treecompose kernel cleanup #959

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/manual/treefile.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ It supports the following parameters:
possible values:
* "both": the default, kernel data goes in /boot and /usr/lib/ostree-boot
* "legacy": Now an alias for "both"; historically meant just "boot"
* "new": kernel data goes in /usr/lib/ostree-boot
* "new": kernel data goes in /usr/lib/ostree-boot and /usr/lib/modules

* `etc-group-members`: Array of strings, optional: Unix groups in this
list will be stored in `/etc/group` instead of `/usr/lib/group`. Use
Expand Down
15 changes: 15 additions & 0 deletions src/app/rpmostree-compose-builtin-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,21 @@ install_packages_in_root (RpmOstreeTreeComposeContext *self,
return FALSE;
}

/* Before we install packages, drop a file to suppress the kernel.rpm dracut run.
* <https://github.com/systemd/systemd/pull/4174> */
const char *kernel_installd_path = "usr/lib/kernel/install.d";
if (!glnx_shutil_mkdir_p_at (rootfs_dfd, kernel_installd_path, 0755, cancellable, error))
return FALSE;
const char skip_kernel_install_data[] = "#!/usr/bin/sh\nexit 77\n";
const char *kernel_skip_path = glnx_strjoina (kernel_installd_path, "/00-rpmostree-skip.install");
if (!glnx_file_replace_contents_with_perms_at (rootfs_dfd, kernel_skip_path,
(guint8*)skip_kernel_install_data,
strlen (skip_kernel_install_data),
0755, 0, 0,
GLNX_FILE_REPLACE_NODATASYNC,
cancellable, error))
return FALSE;

/* Now actually run through librpm to install the packages. Note this bit
* will be replaced in the future with a unified core:
* https://github.com/projectatomic/rpm-ostree/issues/729
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/rpmostree-sysroot-upgrader.c
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,7 @@ perform_local_assembly (RpmOstreeSysrootUpgrader *self,
return FALSE;

if (!rpmostree_finalize_kernel (self->tmprootfs_dfd, bootdir, kver, kernel_path,
&initramfs_tmpf,
&initramfs_tmpf, RPMOSTREE_FINALIZE_KERNEL_AUTO,
cancellable, error))
return FALSE;

Expand Down
165 changes: 127 additions & 38 deletions src/libpriv/rpmostree-kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
#include "rpmostree-bwrap.h"
#include "rpmostree-util.h"

static const char usrlib_ostreeboot[] = "usr/lib/ostree-boot";

/* Keep this in sync with ostree/src/libostree/ostree-sysroot-deploy.c:get_kernel_from_tree().
* Note they are of necessity slightly different since rpm-ostree needs
* to support grabbing wherever the Fedora kernel RPM dropped files as well.
*/
static gboolean
find_kernel_and_initramfs_in_bootdir (int rootfs_dfd,
const char *bootdir,
Expand All @@ -57,7 +63,7 @@ find_kernel_and_initramfs_in_bootdir (int rootfs_dfd,
if (dfd < 0)
{
if (errno == ENOENT)
return TRUE;
return TRUE; /* Note early return */
else
return glnx_throw_errno_prefix (error, "opendir(%s)", bootdir);
}
Expand All @@ -80,25 +86,16 @@ find_kernel_and_initramfs_in_bootdir (int rootfs_dfd,
name = dent->d_name;

/* Current Fedora 23 kernel.spec installs as just vmlinuz */
if (strcmp (name, "vmlinuz") == 0 || g_str_has_prefix (name, "vmlinuz-"))
if (g_str_equal (name, "vmlinuz") || g_str_has_prefix (name, "vmlinuz-"))
{
if (ret_kernel)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Multiple vmlinuz- in %s",
bootdir);
return FALSE;
}
return glnx_throw (error, "Multiple vmlinuz- in %s", bootdir);
ret_kernel = g_strconcat (bootdir, "/", name, NULL);
}
else if (g_str_has_prefix (name, "initramfs-"))
else if (g_str_equal (name, "initramfs.img") || g_str_has_prefix (name, "initramfs-"))
{
if (ret_initramfs)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Multiple initramfs- in %s", bootdir);
return FALSE;
}
return glnx_throw (error, "Multiple initramfs- in %s", bootdir);
ret_initramfs = g_strconcat (bootdir, "/", name, NULL);
}
}
Expand Down Expand Up @@ -153,8 +150,8 @@ find_ensure_one_subdirectory (int rootfs_dfd,
/* Given a root filesystem, return a GVariant of format (sssms):
* - kver: uname -r equivalent
* - bootdir: Path to the boot directory
* - kernel_path: Relative path to kernel
* - initramfs_path: Relative path to initramfs (may be NULL if no initramfs)
* - kernel_path: Relative (to rootfs) path to kernel
* - initramfs_path: Relative (to rootfs) path to initramfs (may be NULL if no initramfs)
*/
GVariant *
rpmostree_find_kernel (int rootfs_dfd,
Expand All @@ -175,7 +172,7 @@ rpmostree_find_kernel (int rootfs_dfd,
/* First, look for the kernel in the canonical ostree directory */
g_autofree char* kernel_path = NULL;
g_autofree char* initramfs_path = NULL;
g_autofree char *bootdir = g_strdup ("usr/lib/ostree-boot");
g_autofree char *bootdir = g_strdup (usrlib_ostreeboot);
if (!find_kernel_and_initramfs_in_bootdir (rootfs_dfd, bootdir,
&kernel_path, &initramfs_path,
cancellable, error))
Expand Down Expand Up @@ -211,52 +208,140 @@ rpmostree_find_kernel (int rootfs_dfd,
return g_variant_ref_sink (g_variant_new ("(sssms)", kver, bootdir, kernel_path, initramfs_path));
}

/* Given a kernel path and a temporary initramfs, compute their checksum and put
* them in their final locations.
/* Given a @rootfs_dfd and path to kernel/initramfs that live in
* usr/lib/modules/$kver, possibly update @bootdir to use them.
* @bootdir should be one of either /usr/lib/ostree-boot or /boot.
* If @copy_if_not_found is set, we do the copy unconditionally,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter is still called is_auto, though I prefer copy_if_not_found as well. :) Or maybe keep it reversed and name it something like only_if_found?

* otherwise only update if found. (This way we avoid e.g. touching /boot
* if it isn't being used).
*/
static gboolean
copy_kernel_into (int rootfs_dfd,
const char *kver,
const char *boot_checksum_str,
const char *kernel_modules_path,
const char *initramfs_modules_path,
gboolean is_auto,
const char *bootdir,
GCancellable *cancellable,
GError **error)
{
g_autofree char *legacy_kernel_path = NULL;
g_autofree char* legacy_initramfs_path = NULL;
if (!find_kernel_and_initramfs_in_bootdir (rootfs_dfd, bootdir,
&legacy_kernel_path, &legacy_initramfs_path,
cancellable, error))
return FALSE;

/* No kernel found? Skip to the next if we're in "auto"
* mode i.e. only update if found.
*/
if (!legacy_kernel_path && is_auto)
return TRUE;

/* Update kernel */
if (legacy_kernel_path)
{
if (!glnx_unlinkat (rootfs_dfd, legacy_kernel_path, 0, error))
return FALSE;
g_free (legacy_kernel_path);
}
legacy_kernel_path = g_strconcat (bootdir, "/", "vmlinuz-", kver, "-", boot_checksum_str, NULL);
if (linkat (rootfs_dfd, kernel_modules_path, rootfs_dfd, legacy_kernel_path, 0) < 0)
return glnx_throw_errno_prefix (error, "linkat(%s)", legacy_kernel_path);

/* Update initramfs */
if (legacy_initramfs_path)
{
if (!glnx_unlinkat (rootfs_dfd, legacy_initramfs_path, 0, error))
return FALSE;
g_free (legacy_initramfs_path);
}
legacy_initramfs_path = g_strconcat (bootdir, "/", "initramfs-", kver, ".img-", boot_checksum_str, NULL);
if (linkat (rootfs_dfd, initramfs_modules_path, rootfs_dfd, legacy_initramfs_path, 0) < 0)
return glnx_throw_errno_prefix (error, "linkat(%s)", legacy_initramfs_path);

return TRUE;
}

/* Given a kernel path and a temporary initramfs, place them in their final
* location. We handle /usr/lib/modules as well as the /usr/lib/ostree-boot and
* /boot paths where we need to pre-compute their checksum.
*/
gboolean
rpmostree_finalize_kernel (int rootfs_dfd,
const char *bootdir,
const char *kver,
const char *kernel_path,
GLnxTmpfile *initramfs_tmpf,
RpmOstreeFinalizeKernelDestination dest,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GChecksum) boot_checksum = NULL;
g_autofree char *kernel_final_path = NULL;
g_autofree char *initramfs_final_path = NULL;
const char *boot_checksum_str = NULL;

/* Now, calculate the combined sha256sum of the two. We checksum the initramfs
* from the tmpfile fd (via mmap()) to avoid writing it to disk in another
* temporary location.
const char slash_bootdir[] = "boot";
g_autofree char *modules_bootdir = g_strconcat ("usr/lib/modules/", kver, NULL);

/* Calculate the sha256sum of the kernel+initramfs (called the "boot
* checksum"). We checksum the initramfs from the tmpfile fd (via mmap()) to
* avoid writing it to disk in another temporary location.
*/
boot_checksum = g_checksum_new (G_CHECKSUM_SHA256);
g_autoptr(GChecksum) boot_checksum = g_checksum_new (G_CHECKSUM_SHA256);
if (!_rpmostree_util_update_checksum_from_file (boot_checksum, rootfs_dfd, kernel_path,
cancellable, error))
return FALSE;

{ g_autoptr(GMappedFile) mfile = g_mapped_file_new_from_fd (initramfs_tmpf->fd, FALSE, error);
if (!mfile)
return FALSE;
g_checksum_update (boot_checksum, (guint8*)g_mapped_file_get_contents (mfile),
g_mapped_file_get_length (mfile));
}
boot_checksum_str = g_checksum_get_string (boot_checksum);

kernel_final_path = g_strconcat (bootdir, "/", "vmlinuz-", kver, "-", boot_checksum_str, NULL);
initramfs_final_path = g_strconcat (bootdir, "/", "initramfs-", kver, ".img-", boot_checksum_str, NULL);
const char *boot_checksum_str = g_checksum_get_string (boot_checksum);

/* Put the kernel in the final location */
if (!glnx_renameat (rootfs_dfd, kernel_path, rootfs_dfd, kernel_final_path, error))
return FALSE;
/* Link the initramfs directly to its final destination */
g_autofree char *kernel_modules_path = g_strconcat (modules_bootdir, "/vmlinuz", NULL);;
/* It's possible the bootdir is already the modules directory; in that case,
* we don't need to rename.
*/
if (!g_str_equal (kernel_path, kernel_modules_path))
{
g_assert_cmpstr (bootdir, !=, modules_bootdir);
/* Ensure that the /usr/lib/modules kernel is the same as the source.
* Right now we don't support overriding the kernel, but to be
* conservative let's relink (unlink/link). We don't just rename() because
* for _AUTO mode we still want to find the kernel in the old path
* (probably /usr/lib/ostree-boot) and update as appropriate.
*/
if (unlinkat (rootfs_dfd, kernel_modules_path, 0) < 0)
{
if (errno != ENOENT)
return glnx_throw_errno_prefix (error, "unlinkat(%s)", kernel_modules_path);
}
if (linkat (rootfs_dfd, kernel_path, rootfs_dfd, kernel_modules_path, 0) < 0)
return glnx_throw_errno_prefix (error, "linkat(%s)", kernel_modules_path);
}
g_autofree char *initramfs_modules_path = g_strconcat (modules_bootdir, "/initramfs.img", NULL);
if (!glnx_link_tmpfile_at (initramfs_tmpf, GLNX_LINK_TMPFILE_NOREPLACE,
rootfs_dfd, initramfs_final_path,
rootfs_dfd, initramfs_modules_path,
error))
return FALSE;

/* Update /usr/lib/ostree-boot and /boot (if desired) */
const gboolean is_auto = (dest == RPMOSTREE_FINALIZE_KERNEL_AUTO);
if (is_auto || dest >= RPMOSTREE_FINALIZE_KERNEL_USRLIB_OSTREEBOOT)
{
if (!copy_kernel_into (rootfs_dfd, kver, boot_checksum_str,
kernel_modules_path, initramfs_modules_path,
is_auto, usrlib_ostreeboot,
cancellable, error))
return FALSE;
}
if (is_auto || dest >= RPMOSTREE_FINALIZE_KERNEL_SLASH_BOOT)
{
if (!copy_kernel_into (rootfs_dfd, kver, boot_checksum_str,
kernel_modules_path, initramfs_modules_path,
is_auto, slash_bootdir,
cancellable, error))
return FALSE;
}
return TRUE;
}

Expand Down Expand Up @@ -341,12 +426,16 @@ rpmostree_run_dracut (int rootfs_dfd,
&tmpf, error))
goto out;

/* If we're rebuilding, we use the *current* /etc so we pick up any modified
* config files. Otherwise, we use the usr/etc defaults.
*/
if (rebuild_from_initramfs)
bwrap = rpmostree_bwrap_new (rootfs_dfd, RPMOSTREE_BWRAP_IMMUTABLE, error,
"--ro-bind", "/etc", "/etc",
NULL);
else
bwrap = rpmostree_bwrap_new (rootfs_dfd, RPMOSTREE_BWRAP_IMMUTABLE, error,
"--ro-bind", "./usr/etc", "/etc",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor/optional: this can just be usr/etc, right? (To be more consistent with other relative bindings we do elsewhere).

NULL);
if (!bwrap)
return FALSE;
Expand Down
8 changes: 8 additions & 0 deletions src/libpriv/rpmostree-kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@

#include <ostree.h>

typedef enum {
RPMOSTREE_FINALIZE_KERNEL_AUTO,
RPMOSTREE_FINALIZE_KERNEL_USRLIB_MODULES,
RPMOSTREE_FINALIZE_KERNEL_USRLIB_OSTREEBOOT,
RPMOSTREE_FINALIZE_KERNEL_SLASH_BOOT,
} RpmOstreeFinalizeKernelDestination;

GVariant *
rpmostree_find_kernel (int rootfs_dfd,
GCancellable *cancellable,
Expand All @@ -33,6 +40,7 @@ rpmostree_finalize_kernel (int rootfs_dfd,
const char *kver,
const char *kernel_path,
GLnxTmpfile *initramfs_tmpf,
RpmOstreeFinalizeKernelDestination dest,
GCancellable *cancellable,
GError **error);

Expand Down
Loading