compose: Migrate content of /etc/{passwd,group} to /usr/lib more sanely
I had an epiphany today while working on https://bugzilla.redhat.com/show_bug.cgi?id=1098304 - I realized that I can just do an install, and then copy over everything except the root entries from /etc/passwd into /usr/lib/passwd. No need for a patched shadow-utils. No need to modify the /etc/nsswitch.conf before doing the install root. It totally works. I have no idea why I originally overcomplicated this. The thing that sucks a bit about this code is that I have to drop to the FILE * APIs so that I can use the glibc APIs for processing group/shadow. Also, the way I deduplicated the code paths for processing passwd/group is crappy, but I think it's better than duplicating them (as systemd-sysusers does). The good: We don't need a two-step RPM transaction, we don't need a patch for shadow-utils, it's just saner The bad: Code is not the most beautiful? Not really bad. The ugly: I didn't think of this in the first place and spent months beating my head against the wall of shadow-utils...
This commit is contained in:
parent
aa5ae877de
commit
827e711eb7
@ -158,50 +158,6 @@ append_string_array_to (JsonObject *object,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
replace_nsswitch (GFile *target_usretc,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_object GFile *nsswitch_conf =
|
||||
g_file_get_child (target_usretc, "nsswitch.conf");
|
||||
gs_free char *nsswitch_contents = NULL;
|
||||
gs_free char *new_nsswitch_contents = NULL;
|
||||
|
||||
static gsize regex_initialized;
|
||||
static GRegex *passwd_regex;
|
||||
|
||||
if (g_once_init_enter (®ex_initialized))
|
||||
{
|
||||
passwd_regex = g_regex_new ("^(passwd|group):\\s+files(.*)$",
|
||||
G_REGEX_MULTILINE, 0, NULL);
|
||||
g_assert (passwd_regex);
|
||||
g_once_init_leave (®ex_initialized, 1);
|
||||
}
|
||||
|
||||
nsswitch_contents = gs_file_load_contents_utf8 (nsswitch_conf, cancellable, error);
|
||||
if (!nsswitch_contents)
|
||||
goto out;
|
||||
|
||||
new_nsswitch_contents = g_regex_replace (passwd_regex,
|
||||
nsswitch_contents, -1, 0,
|
||||
"\\1: files altfiles\\2",
|
||||
0, error);
|
||||
if (!new_nsswitch_contents)
|
||||
goto out;
|
||||
|
||||
if (!g_file_replace_contents (nsswitch_conf, new_nsswitch_contents,
|
||||
strlen (new_nsswitch_contents),
|
||||
NULL, FALSE, 0, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GSSubprocess *process;
|
||||
GFile *reposdir_path;
|
||||
@ -452,8 +408,6 @@ yum_context_new (RpmOstreeTreeComposeContext *self,
|
||||
duped_environ = g_environ_setenv (duped_environ, "OSTREE_KERNEL_INSTALL_NOOP", "1", TRUE);
|
||||
/* See fedora's kernel.spec */
|
||||
duped_environ = g_environ_setenv (duped_environ, "HARDLINK", "no", TRUE);
|
||||
/* See https://bugzilla.redhat.com/show_bug.cgi?id=1098304 */
|
||||
duped_environ = g_environ_setenv (duped_environ, "SHADOW_USE_USRLIB", "1", TRUE);
|
||||
|
||||
gs_subprocess_context_set_environment (context, duped_environ);
|
||||
}
|
||||
@ -990,49 +944,16 @@ rpmostree_compose_builtin_tree (int argc,
|
||||
bootstrap_packages = g_ptr_array_new ();
|
||||
packages = g_ptr_array_new ();
|
||||
|
||||
if (!append_string_array_to (treefile, "bootstrap_packages", bootstrap_packages, error))
|
||||
if (!append_string_array_to (treefile, "bootstrap_packages", packages, error))
|
||||
goto out;
|
||||
g_ptr_array_add (bootstrap_packages, NULL);
|
||||
|
||||
if (!append_string_array_to (treefile, "packages", packages, error))
|
||||
goto out;
|
||||
g_ptr_array_add (packages, NULL);
|
||||
|
||||
|
||||
/* Ensure we have enough to modify NSS */
|
||||
if (!yuminstall (self, treefile, yumroot, workdir,
|
||||
(char**)bootstrap_packages->pdata,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* Prepare NSS configuration; this needs to be done
|
||||
before any invocations of "useradd" in %post */
|
||||
|
||||
{
|
||||
gs_unref_object GFile *yumroot_passwd =
|
||||
g_file_resolve_relative_path (yumroot, "usr/lib/passwd");
|
||||
gs_unref_object GFile *yumroot_group =
|
||||
g_file_resolve_relative_path (yumroot, "usr/lib/group");
|
||||
gs_unref_object GFile *yumroot_etc =
|
||||
g_file_resolve_relative_path (yumroot, "etc");
|
||||
|
||||
if (!g_file_replace_contents (yumroot_passwd, "", 0, NULL, FALSE, 0,
|
||||
NULL, cancellable, error))
|
||||
goto out;
|
||||
if (!g_file_replace_contents (yumroot_group, "", 0, NULL, FALSE, 0,
|
||||
NULL, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!replace_nsswitch (yumroot_etc, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
{
|
||||
if (!yuminstall (self, treefile, yumroot, workdir,
|
||||
(char**)packages->pdata,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
cachekey = g_strconcat ("treecompose/", ref, NULL);
|
||||
if (!cachedir_lookup_string (cachedir, cachekey,
|
||||
|
@ -24,8 +24,11 @@
|
||||
|
||||
#include <ostree.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <utime.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -534,6 +537,190 @@ workaround_selinux_cross_labeling (GFile *rootfs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static FILE *
|
||||
gfopen (const char *path,
|
||||
const char *mode,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
FILE *ret = NULL;
|
||||
|
||||
ret = fopen (path, mode);
|
||||
if (!ret)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"fopen(%s): %s", path, g_strerror (errsv));
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gfflush (FILE *f,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (fflush (f) != 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"fflush: %s", g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
MIGRATE_PASSWD,
|
||||
MIGRATE_GROUP
|
||||
} MigrateKind;
|
||||
|
||||
/*
|
||||
* This function is taking the /etc/passwd generated in the install
|
||||
* root, and splitting it into two streams: a new /etc/passwd that
|
||||
* just contains the root entry, and /usr/lib/passwd which contains
|
||||
* everything else.
|
||||
*
|
||||
* The implementation is kind of horrible because I wanted to avoid
|
||||
* duplicating the user/group code.
|
||||
*/
|
||||
static gboolean
|
||||
migrate_passwd_file_except_root (GFile *rootfs,
|
||||
MigrateKind kind,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
const char *name = kind == MIGRATE_PASSWD ? "passwd" : "group";
|
||||
gs_free char *src_path = g_strconcat (gs_file_get_path_cached (rootfs), "/etc/", name, NULL);
|
||||
gs_free char *etctmp_path = g_strconcat (gs_file_get_path_cached (rootfs), "/etc/", name, ".tmp", NULL);
|
||||
gs_free char *usrdest_path = g_strconcat (gs_file_get_path_cached (rootfs), "/usr/lib/", name, NULL);
|
||||
FILE *src_stream = NULL;
|
||||
FILE *etcdest_stream = NULL;
|
||||
FILE *usrdest_stream = NULL;
|
||||
|
||||
src_stream = gfopen (src_path, "r", cancellable, error);
|
||||
if (!src_stream)
|
||||
goto out;
|
||||
|
||||
etcdest_stream = gfopen (etctmp_path, "w", cancellable, error);
|
||||
if (!etcdest_stream)
|
||||
goto out;
|
||||
|
||||
usrdest_stream = gfopen (usrdest_path, "a", cancellable, error);
|
||||
if (!usrdest_stream)
|
||||
goto out;
|
||||
|
||||
errno = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
struct passwd *pw = NULL;
|
||||
struct group *gr = NULL;
|
||||
FILE *deststream;
|
||||
int r;
|
||||
|
||||
if (kind == MIGRATE_PASSWD)
|
||||
pw = fgetpwent (src_stream);
|
||||
else
|
||||
gr = fgetgrent (src_stream);
|
||||
|
||||
if (!(pw || gr))
|
||||
{
|
||||
if (errno != 0 && errno != ENOENT)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"fgetpwent: %s", g_strerror (errno));
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if ((pw && pw->pw_uid == 0) ||
|
||||
(gr && gr->gr_gid == 0))
|
||||
deststream = etcdest_stream;
|
||||
else
|
||||
deststream = usrdest_stream;
|
||||
|
||||
if (pw)
|
||||
r = putpwent (pw, deststream);
|
||||
else
|
||||
r = putgrent (gr, deststream);
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"putpwent: %s", g_strerror (errsv));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gfflush (etcdest_stream, cancellable, error))
|
||||
goto out;
|
||||
if (!gfflush (usrdest_stream, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (rename (etctmp_path, src_path) != 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"rename(%s, %s): %s", etctmp_path, src_path, g_strerror (errsv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (src_stream) (void) fclose (src_stream);
|
||||
if (etcdest_stream) (void) fclose (etcdest_stream);
|
||||
if (usrdest_stream) (void) fclose (usrdest_stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
replace_nsswitch (GFile *target_usretc,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_object GFile *nsswitch_conf =
|
||||
g_file_get_child (target_usretc, "nsswitch.conf");
|
||||
gs_free char *nsswitch_contents = NULL;
|
||||
gs_free char *new_nsswitch_contents = NULL;
|
||||
|
||||
static gsize regex_initialized;
|
||||
static GRegex *passwd_regex;
|
||||
|
||||
if (g_once_init_enter (®ex_initialized))
|
||||
{
|
||||
passwd_regex = g_regex_new ("^(passwd|group):\\s+files(.*)$",
|
||||
G_REGEX_MULTILINE, 0, NULL);
|
||||
g_assert (passwd_regex);
|
||||
g_once_init_leave (®ex_initialized, 1);
|
||||
}
|
||||
|
||||
nsswitch_contents = gs_file_load_contents_utf8 (nsswitch_conf, cancellable, error);
|
||||
if (!nsswitch_contents)
|
||||
goto out;
|
||||
|
||||
new_nsswitch_contents = g_regex_replace (passwd_regex,
|
||||
nsswitch_contents, -1, 0,
|
||||
"\\1: files altfiles\\2",
|
||||
0, error);
|
||||
if (!new_nsswitch_contents)
|
||||
goto out;
|
||||
|
||||
if (!g_file_replace_contents (nsswitch_conf, new_nsswitch_contents,
|
||||
strlen (new_nsswitch_contents),
|
||||
NULL, FALSE, 0, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Prepare a root filesystem, taking mainly the contents of /usr from yumroot */
|
||||
static gboolean
|
||||
@ -554,6 +741,22 @@ create_rootfs_from_yumroot_content (GFile *targetroot,
|
||||
if (!init_rootfs (targetroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
g_print ("Migrating /etc/passwd to /usr/lib/\n");
|
||||
if (!migrate_passwd_file_except_root (yumroot, MIGRATE_PASSWD, cancellable, error))
|
||||
goto out;
|
||||
g_print ("Migrating /etc/group to /usr/lib/\n");
|
||||
if (!migrate_passwd_file_except_root (yumroot, MIGRATE_GROUP, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* NSS configuration to look at the new files */
|
||||
{
|
||||
gs_unref_object GFile *yumroot_etc =
|
||||
g_file_resolve_relative_path (yumroot, "etc");
|
||||
|
||||
if (!replace_nsswitch (yumroot_etc, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We take /usr from the yum content */
|
||||
g_print ("Moving /usr to target\n");
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user