compose: Support "preserve-passwd" option (enabled by default)

The checking code from #56 landed, and started triggering for me on
the `dockerroot` user. It's nice to know it works. Then the issue
is... "what now"?

It turns out in the case of `dockerroot` it's actually unused, so we
could fix this by deleting it. But in general we need to support
dynamic uids/gids/. And we can't yet take a hard dep on #49.

So this patch changes things so we take a copy of the passwd/group
data from the previous commit.  Any users subsequently added in the
*new* commit will be additive.

Closes: https://github.com/projectatomic/rpm-ostree/issues/78
This commit is contained in:
Colin Walters 2014-12-23 16:28:53 -05:00
parent fc1a4b05fa
commit f9e9c06648
6 changed files with 165 additions and 0 deletions

View File

@ -61,6 +61,13 @@ Treefile
Note this does not alter the RPM database, so `rpm -V` will complain.
* `preserve-passwd`: boolean, optional: Defaults to `true`. If enabled,
copy the `/etc/passwd` (and `/usr/lib/passwd`) files from the previous commit
if they exist. This helps ensure consistent uid/gid allocations across
builds. However, it does mean that removed users will exist in the `passwd`
database forever. It also does not help clients switch between unrelated
trees.
* `check-passwd`: Object, optional: Checks to run against the new passwd file
before accepting the tree. All the entries specified should exist (unless
ignored) and have the same values or the compose will fail. There are four

View File

@ -940,6 +940,23 @@ rpmostree_compose_builtin_tree (int argc,
self->serialized_treefile = g_bytes_new_take (treefile_buf, len);
}
{
gboolean generate_from_previous = TRUE;
if (!_rpmostree_jsonutil_object_get_optional_boolean_member (treefile,
"preserve-passwd",
&generate_from_previous,
error))
goto out;
if (generate_from_previous)
{
if (!rpmostree_generate_passwd_from_previous (repo, yumroot, ref,
cancellable, error))
goto out;
}
}
if (!yuminstall (self, treefile, yumroot,
(char**)packages->pdata,
cancellable, error))

View File

@ -133,6 +133,31 @@ _rpmostree_jsonutil_object_require_int_member (JsonObject *object,
return TRUE;
}
gboolean
_rpmostree_jsonutil_object_get_optional_boolean_member (JsonObject *object,
const char *member_name,
gboolean *out_value,
GError **error)
{
gboolean ret = FALSE;
JsonNode *node = json_object_get_member (object, member_name);
if (node != NULL)
{
if (json_node_get_value_type (node) != G_TYPE_BOOLEAN)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Member '%s' is not a boolean", member_name);
goto out;
}
*out_value = json_node_get_boolean (node);
}
ret = TRUE;
out:
return ret;
}
const char *
_rpmostree_jsonutil_array_require_string_element (JsonArray *array,
guint i,

View File

@ -47,6 +47,12 @@ _rpmostree_jsonutil_object_require_int_member (JsonObject *object,
gint64 *out_val,
GError **error);
gboolean
_rpmostree_jsonutil_object_get_optional_boolean_member (JsonObject *object,
const char *member_name,
gboolean *out_value,
GError **error);
const char *
_rpmostree_jsonutil_array_require_string_element (JsonArray *array,
guint i,

View File

@ -667,3 +667,106 @@ rpmostree_check_groups (OstreeRepo *repo,
return rpmostree_check_passwd_groups (TRUE, repo, yumroot, treefile_dirpath,
treedata, cancellable, error);
}
static gboolean
concat_passwd_file (GFile *yumroot,
GFile *previous_commit,
const char *filename,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_free char *etc_subpath = g_strconcat ("etc/", filename, NULL);
gs_free char *usrlib_subpath = g_strconcat ("usr/lib/", filename, NULL);
gs_unref_object GFile *yumroot_etc = g_file_resolve_relative_path (yumroot, "etc");
gs_unref_object GFile *yumroot_dest = g_file_resolve_relative_path (yumroot, etc_subpath);
gs_unref_object GFile *orig_etc_content = g_file_resolve_relative_path (previous_commit, etc_subpath);
gs_unref_object GFile *orig_usrlib_content = g_file_resolve_relative_path (previous_commit, usrlib_subpath);
gs_unref_object GFileOutputStream *out = NULL;
gboolean have_etc, have_usr;
if (!gs_file_ensure_directory (yumroot_etc, TRUE, cancellable, error))
goto out;
have_etc = g_file_query_exists (orig_etc_content, NULL);
have_usr = g_file_query_exists (orig_usrlib_content, NULL);
if (!(have_etc || have_usr))
{
ret = TRUE;
goto out;
}
out = g_file_replace (yumroot_dest, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION,
cancellable, error);
if (!out)
goto out;
if (have_etc)
{
gs_unref_object GInputStream *src =
(GInputStream*)g_file_read (orig_etc_content, cancellable, error);
if (g_output_stream_splice ((GOutputStream*)out, src,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
cancellable, error ) < 0)
goto out;
}
if (have_usr)
{
gs_unref_object GInputStream *src =
(GInputStream*)g_file_read (orig_usrlib_content, cancellable, error);
if (g_output_stream_splice ((GOutputStream*)out, src,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
cancellable, error ) < 0)
goto out;
}
if (!g_output_stream_flush ((GOutputStream*)out, cancellable, error))
goto out;
ret = TRUE;
out:
return ret;
}
gboolean
rpmostree_generate_passwd_from_previous (OstreeRepo *repo,
GFile *yumroot,
const char *ref,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GError *temp_error = NULL;
gs_unref_object GFile *previous_root = NULL;
gs_unref_object GFile *yumroot_etc_group = g_file_resolve_relative_path (yumroot, "etc/group");
gs_unref_object GFile *out = NULL;
if (!ostree_repo_read_commit (repo, ref, &previous_root, NULL, NULL, &temp_error))
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&temp_error);
ret = TRUE;
}
else
{
g_propagate_error (error, temp_error);
}
goto out;
}
if (!concat_passwd_file (yumroot, previous_root, "passwd",
cancellable, error))
goto out;
if (!concat_passwd_file (yumroot, previous_root, "group",
cancellable, error))
goto out;
ret = TRUE;
out:
return ret;
}

View File

@ -39,3 +39,10 @@ rpmostree_check_groups (OstreeRepo *repo,
JsonObject *treedata,
GCancellable *cancellable,
GError **error);
gboolean
rpmostree_generate_passwd_from_previous (OstreeRepo *repo,
GFile *yumroot,
const char *ref,
GCancellable *cancellable,
GError **error);