core,kernel: Set up /etc/passwd for dracut

This fixes a longstanding spew of error messages from the initramfs
because we don't have nss-altfiles set up there.  Rather than
trying to do it, just do the dance of re-synthesizing `/etc/passwd`
as it traditionally looks around running dracut, the same as we
do for scripts during core layering.

Yes, this is all a mess and hopefully I'll get to sysusers soon...
This commit is contained in:
Colin Walters 2020-02-07 02:26:19 +00:00 committed by OpenShift Merge Robot
parent 9c4e7590b2
commit b797a42f3e
5 changed files with 86 additions and 22 deletions

View File

@ -3798,6 +3798,49 @@ rpmostree_context_get_kernel_changed (RpmOstreeContext *self)
return self->kernel_changed;
}
/* We have a messy dance in dealing with /usr/etc and /etc; the
* current model is basically to have it be /etc whenever we're running
* any code.
*/
gboolean
rpmostree_core_undo_usretc (int rootfs_dfd,
gboolean *renamed_etc,
GError **error)
{
if (!glnx_fstatat_allow_noent (rootfs_dfd, "usr/etc", NULL, 0, error))
return FALSE;
if (errno == 0)
{
/* In general now, we place contents in /etc when running scripts */
if (!glnx_renameat (rootfs_dfd, "usr/etc", rootfs_dfd, "etc", error))
return FALSE;
/* But leave a compat symlink, as we used to bind mount, so scripts
* could still use that too.
*/
if (symlinkat ("../etc", rootfs_dfd, "usr/etc") < 0)
return glnx_throw_errno_prefix (error, "symlinkat");
*renamed_etc = TRUE;
}
else
{
*renamed_etc = FALSE;
}
return TRUE;
}
gboolean
rpmostree_core_redo_usretc (int rootfs_dfd,
GError **error)
{
/* Remove the symlink and swap back */
if (!glnx_unlinkat (rootfs_dfd, "usr/etc", 0, error))
return FALSE;
if (!glnx_renameat (rootfs_dfd, "etc", rootfs_dfd, "usr/etc", error))
return FALSE;
return TRUE;
}
static gboolean
process_one_ostree_layer (RpmOstreeContext *self,
int rootfs_dfd,
@ -4129,20 +4172,9 @@ rpmostree_context_assemble (RpmOstreeContext *self,
gboolean skip_sanity_check = FALSE;
g_variant_dict_lookup (self->spec->dict, "skip-sanity-check", "b", &skip_sanity_check);
if (!glnx_fstatat_allow_noent (tmprootfs_dfd, "usr/etc", NULL, 0, error))
gboolean renamed_etc = FALSE;
if (!rpmostree_core_undo_usretc (tmprootfs_dfd, &renamed_etc, error))
return FALSE;
gboolean renamed_etc = (errno == 0);
if (renamed_etc)
{
/* In general now, we place contents in /etc when running scripts */
if (!glnx_renameat (tmprootfs_dfd, "usr/etc", tmprootfs_dfd, "etc", error))
return FALSE;
/* But leave a compat symlink, as we used to bind mount, so scripts
* could still use that too.
*/
if (symlinkat ("../etc", tmprootfs_dfd, "usr/etc") < 0)
return glnx_throw_errno_prefix (error, "symlinkat");
}
/* NB: we're not running scripts right now for removals, so this is only for overlays and
* replacements */
@ -4388,14 +4420,8 @@ rpmostree_context_assemble (RpmOstreeContext *self,
}
/* Undo the /etc move above */
if (renamed_etc)
{
/* Remove the symlink and swap back */
if (!glnx_unlinkat (tmprootfs_dfd, "usr/etc", 0, error))
if (renamed_etc && !rpmostree_core_redo_usretc (tmprootfs_dfd, error))
return FALSE;
if (!glnx_renameat (tmprootfs_dfd, "etc", tmprootfs_dfd, "usr/etc", error))
return FALSE;
}
/* And clean up var/tmp, we don't want it in commits */
if (!glnx_shutil_rm_rf_at (tmprootfs_dfd, "var/tmp", cancellable, error))

View File

@ -230,3 +230,11 @@ gboolean rpmostree_context_commit (RpmOstreeContext *self,
char **out_commit,
GCancellable *cancellable,
GError **error);
gboolean
rpmostree_core_undo_usretc (int rootfs_dfd,
gboolean *renamed_etc,
GError **error);
gboolean
rpmostree_core_redo_usretc (int rootfs_dfd,
GError **error);

View File

@ -34,6 +34,8 @@
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include "rpmostree-passwd-util.h"
#include "rpmostree-core.h"
#include "rpmostree-kernel.h"
#include "rpmostree-bwrap.h"
#include "rpmostree-util.h"
@ -496,6 +498,21 @@ rpmostree_run_dracut (int rootfs_dfd,
g_auto(GLnxTmpfile) tmpf = { 0, };
g_autoptr(GBytes) random_cpio_data = NULL;
/* We need to have /etc/passwd since dracut doesn't have altfiles
* today. Though maybe in the future we should add it, but
* in the end we want to use systemd-sysusers of course.
**/
gboolean renamed_etc = FALSE;
if (!rpmostree_core_undo_usretc (rootfs_dfd, &renamed_etc, error))
return FALSE;
gboolean have_passwd = FALSE;
if (!rpmostree_passwd_prepare_rpm_layering (rootfs_dfd,
NULL,
&have_passwd,
cancellable,
error))
return FALSE;
/* Previously we used to error out if argv or rebuild_from_initramfs were both
* not set; now we simply use the defaults (which in Fedora today also means
* implicitly hostonly). That case is for `rpm-ostree override replace
@ -607,6 +624,12 @@ rpmostree_run_dracut (int rootfs_dfd,
if (rebuild_from_initramfs)
(void) unlinkat (rootfs_dfd, rebuild_from_initramfs, 0);
if (have_passwd && !rpmostree_passwd_complete_rpm_layering (rootfs_dfd, error))
goto out;
if (renamed_etc && !rpmostree_core_redo_usretc (rootfs_dfd, error))
goto out;
ret = TRUE;
*out_initramfs_tmpf = tmpf; tmpf.initialized = FALSE; /* Transfer */
out:

View File

@ -21,6 +21,7 @@
#pragma once
#include <gio/gio.h>
#include <json-glib/json-glib.h>
#include <sys/types.h>
#include <sys/wait.h>

View File

@ -129,5 +129,11 @@ initramfs=$(vm_cmd grep ^initrd /boot/loader/entries/ostree-2-$osname.conf | sed
test -n "${initramfs}"
vm_cmd lsinitrd $initramfs > lsinitrd.txt
assert_not_file_has_content lsinitrd.txt /etc/rpmostree-initramfs-testing
echo "ok initramfs disable"
# while we're here, sanity check we also ship /etc/passwd in the initrd to
# soothe systemd-udevd
for x in passwd group; do
assert_file_has_content lsinitrd.txt " etc/$x"
done
rm -f lsinitrd.txt
echo "ok initramfs has passwd"