From c6bf9c22407a6f299049761bdc77ae4e75ee4263 Mon Sep 17 00:00:00 2001 From: Ruixin Bao Date: Mon, 11 Jun 2018 10:49:38 +0000 Subject: [PATCH] wip: finished conversion to pointer array - Finished conversion to pointer array, the only reamining is the sorting logic and written to the file logic. Then the testing ofc --- Makefile-rpm-ostree.am | 2 +- Makefile-tests.am | 5 + src/app/main.c | 3 + src/app/rpmostree-builtins.h | 1 + src/app/rpmostree-compose-builtin-tree.c | 16 ++- src/libpriv/rpmostree-passwd-util.c | 141 +++++++++++++++++++++-- src/libpriv/rpmostree-passwd-util.h | 32 +++++ tests/check/test-sysusers.c | 141 +++++++++++++++++++++++ 8 files changed, 331 insertions(+), 10 deletions(-) create mode 100644 tests/check/test-sysusers.c diff --git a/Makefile-rpm-ostree.am b/Makefile-rpm-ostree.am index 924478ca93..a22ad44589 100644 --- a/Makefile-rpm-ostree.am +++ b/Makefile-rpm-ostree.am @@ -57,7 +57,7 @@ rpm_ostree_SOURCES = src/app/main.c \ src/app/rpmostree-polkit-agent.c \ src/app/rpmostree-polkit-agent.h \ src/app/rpmostree-builtin-kargs.c \ - $(NULL) + $(NULL) if BUILDOPT_COMPOSE_TOOLING rpm_ostree_SOURCES += \ diff --git a/Makefile-tests.am b/Makefile-tests.am index b18974cb3f..b279771793 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -46,11 +46,16 @@ tests_check_test_kargs_CPPFLAGS = $(testbin_cppflags) tests_check_test_kargs_CFLAGS = $(testbin_cflags) tests_check_test_kargs_LDADD = $(testbin_ldadd) libtest.la +tests_check_test_sysusers_CPPFLAGS = $(testbin_cppflags) +tests_check_test_sysusers_CFLAGS = $(testbin_cflags) +tests_check_test_sysusers_LDADD = $(testbin_ldadd) libtest.la + uninstalled_test_programs = \ tests/check/jsonutil \ tests/check/postprocess \ tests/check/test-utils \ tests/check/test-kargs \ + tests/check/test-sysusers \ $(NULL) uninstalled_test_scripts = \ diff --git a/src/app/main.c b/src/app/main.c index 7c1ac82194..0b35cb3650 100644 --- a/src/app/main.c +++ b/src/app/main.c @@ -89,6 +89,9 @@ static RpmOstreeCommand commands[] = { { "refresh-md", 0, "Generate rpm repo metadata", rpmostree_builtin_refresh_md }, + //{ "temp-convert", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, + //"temp command to convert passwd/group conversion", + // rpmostree_builtin_temp_convert }, /* Legacy aliases */ { "pkg-add", RPM_OSTREE_BUILTIN_FLAG_HIDDEN, NULL, rpmostree_builtin_install }, diff --git a/src/app/rpmostree-builtins.h b/src/app/rpmostree-builtins.h index 99b6793381..5979b96b01 100644 --- a/src/app/rpmostree-builtins.h +++ b/src/app/rpmostree-builtins.h @@ -50,6 +50,7 @@ BUILTINPROTO(uninstall); BUILTINPROTO(override); BUILTINPROTO(start_daemon); BUILTINPROTO(ex); +// BUILTINPROTO(temp_convert); #undef BUILTINPROTO diff --git a/src/app/rpmostree-compose-builtin-tree.c b/src/app/rpmostree-compose-builtin-tree.c index 73a3c1d319..45913b8907 100644 --- a/src/app/rpmostree-compose-builtin-tree.c +++ b/src/app/rpmostree-compose-builtin-tree.c @@ -1259,15 +1259,27 @@ impl_commit_tree (RpmOstreeTreeComposeContext *self, if (self->treefile) { g_autoptr(GFile) treefile_dirpath = g_file_get_parent (self->treefile_path); + g_autoptr(GHashTable) sysusers_table = NULL; if (!rpmostree_check_passwd (self->repo, self->rootfs_dfd, treefile_dirpath, self->treefile, - self->previous_checksum, + self->previous_checksum, &sysusers_table, cancellable, error)) return glnx_prefix_error (error, "Handling passwd db"); if (!rpmostree_check_groups (self->repo, self->rootfs_dfd, treefile_dirpath, self->treefile, - self->previous_checksum, + self->previous_checksum, &sysusers_table, cancellable, error)) return glnx_prefix_error (error, "Handling group db"); + if (sysusers_table) + { + /* Do conversion and write files here */ + g_autofree gchar* sysuser_content = NULL; + //if (!rpmostree_passwd_sysusers2char (sysusers_table, + // &sysuser_content, error)) + // return glnx_prefix_error (error, "Handling sysusers conversion"); + /* Write to the sysusers directory */ + ; + /* May be delete the /usr/lib/passwd generated? */ + } } /* See comment above */ diff --git a/src/libpriv/rpmostree-passwd-util.c b/src/libpriv/rpmostree-passwd-util.c index 2e1762bc36..540f59c543 100644 --- a/src/libpriv/rpmostree-passwd-util.c +++ b/src/libpriv/rpmostree-passwd-util.c @@ -138,6 +138,7 @@ conv_passwd_ent_free (void *vptr) g_free (ptr->name); g_free (ptr->pw_gecos); g_free (ptr->pw_dir); + g_free (ptr->pw_shell); g_free (ptr); } @@ -159,8 +160,9 @@ rpmostree_passwd_data2passwdents (const char *data) convent->uid = ent->pw_uid; convent->gid = ent->pw_gid; /* Want to add anymore, like dir? */ - convent->pw_gecos = g_strdup(ent->pw_gecos); - convent->pw_dir = g_strdup(ent->pw_dir); + convent->pw_gecos = g_strdup (ent->pw_gecos); + convent->pw_dir = g_strdup (ent->pw_dir); + convent->pw_shell = g_strdup (ent->pw_shell); g_ptr_array_add (ret, convent); } @@ -185,6 +187,17 @@ conv_group_ent_free (void *vptr) g_free (ptr); } +static void +sysuser_ent_free (void *vptr) +{ + struct sysuser_ent *ptr = vptr; + g_free (ptr->name); + g_free (ptr->id); + g_free (ptr->gecos); + g_free (ptr->dir); + g_free (ptr->shell); +} + GPtrArray * rpmostree_passwd_data2groupents (const char *data) { @@ -216,6 +229,112 @@ compare_group_ents (gconstpointer a, gconstpointer b) return strcmp ((*sa)->name, (*sb)->name); } +rpmostree_passwd_ents2sysusers (gboolean is_passwd, + GPtrArray *input_ents, + GHashTable **out_sysusers_table, + GError **error) +{ + gboolean ret; + if (is_passwd) + ret = FALSE; + //ret = rpmostree_passwdents2sysusers (input_ents, out_sysusers_table, error); + else + ret = FALSE; + return ret; +} + +gboolean +rpmostree_passwdents2sysusers (GPtrArray *passwd_ents, + GPtrArray **out_sysusers_entries, + GError **error) +{ + /* Do the assignment inside the function so we don't need to handle visibility + * issues from different files any more */ + GPtrArray *sysusers_array = NULL; + sysusers_array = *out_sysusers_entries ?: g_ptr_array_new_with_free_func (sysuser_ent_free); + for (int counter=0; counter < passwd_ents->len; counter++) + { + struct conv_passwd_ent *convent = passwd_ents->pdata[counter]; + struct sysuser_ent *sysent = g_new (struct sysuser_ent, 1); + + // Note, sysuser support uid:gid format when uid is not equal + // to gid, and that allows sysusers to add both group and user entries + if (convent->uid != convent->gid) + sysent->id = g_strdup_printf ("%u:%u", convent->uid, convent->gid); + else + sysent->id = g_strdup_printf ("%u", convent->uid); + + sysent->type = "u"; + sysent->name = g_strdup (convent->name); + + sysent->gecos = (g_str_equal (convent->pw_gecos, "")) ? NULL : + g_strdup_printf ("\"%s\"", convent->pw_gecos); + sysent->dir = (g_str_equal (convent->pw_dir, ""))? NULL : + g_steal_pointer (&convent->pw_dir); + sysent->shell = g_steal_pointer (&convent->pw_shell); + + g_ptr_array_add (sysusers_array, sysent); + } + /* Do the assignment at the end if the sysusers_table was not initialized */ + if (*out_sysusers_entries == NULL) + *out_sysusers_entries = g_steal_pointer (&sysusers_array); + + return TRUE; +} + +gboolean +rpmostree_groupents2sysusers (GPtrArray *group_ents, + GPtrArray **out_sysusers_entries, + GError **error) +{ + /* Do the assignment inside the function so we don't need to handle visibility + * issues from different files any more */ + GPtrArray *sysusers_array = NULL; + sysusers_array = *out_sysusers_entries ?: g_ptr_array_new_with_free_func (sysuser_ent_free); + for (int counter=0; counter < group_ents->len; counter++) + { + struct conv_group_ent *convent = group_ents->pdata[counter]; + struct sysuser_ent *sysent = g_new (struct sysuser_ent, 1); + + sysent->type = "g"; + sysent->name = g_steal_pointer (&convent->name); + sysent->id = g_strdup_printf ("%u", convent->gid); + sysent->gecos = NULL; + sysent->dir = NULL; + sysent->shell = NULL; + + g_ptr_array_add (sysusers_array, sysent); + } + /* Do the assignment at the end if the sysusers_table was not initialized */ + if (*out_sysusers_entries == NULL) + *out_sysusers_entries = g_steal_pointer (&sysusers_array); + + return TRUE; +} + +gboolean +rpmostree_passwd_sysusers2char (GPtrArray *sysusers_entries, + char **out_content, + GError **error) +{ + + GString* sysuser_content = g_string_new (NULL); + for (int counter = 0; counter < sysusers_entries->len; counter++) + { + struct sysuser_ent *sysent = sysusers_entries->pdata[counter]; + const char *shell = sysent->shell ?: "-"; + const char *gecos = sysent->gecos ?: "-"; + const char *dir = sysent->dir ?: "-"; + g_autofree gchar* line_content = g_strjoin (" ", sysent->type, sysent->name, + sysent->id, gecos, dir, shell, NULL); + g_string_append_printf (sysuser_content, "%s\n", line_content); + } + if (out_content) + *out_content = g_string_free(sysuser_content, FALSE); + + return TRUE; +} + /* See "man 5 passwd" We just make sure the name and uid/gid match, and that none are missing. don't care about GECOS/dir/shell. */ @@ -226,6 +345,7 @@ rpmostree_check_passwd_groups (gboolean passwd, GFile *treefile_dirpath, JsonObject *treedata, const char *previous_commit, + GHashTable **out_hashtable, GCancellable *cancellable, GError **error) { @@ -253,7 +373,7 @@ rpmostree_check_passwd_groups (gboolean passwd, return TRUE; /* Note early return */ else if (g_str_equal (chk_type, "previous")) ; /* Handled below */ - else if (g_str_equal (chk_type, "file")) + else if (g_str_equal (chk_type, "file") || g_str_equal (chk_type, "sysusers")) { direct = _rpmostree_jsonutil_object_require_string_member (chk, "filename", @@ -358,7 +478,7 @@ rpmostree_check_passwd_groups (gboolean passwd, return TRUE; } } - else if (g_str_equal (chk_type, "file")) + else if (g_str_equal (chk_type, "file") || g_str_equal (chk_type, "sysusers")) { old_path = g_file_resolve_relative_path (treefile_dirpath, direct); old_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (old_path), NULL, @@ -367,7 +487,7 @@ rpmostree_check_passwd_groups (gboolean passwd, return FALSE; } - if (g_str_equal (chk_type, "previous") || g_str_equal (chk_type, "file")) + if (g_str_equal (chk_type, "previous") || g_str_equal (chk_type, "file") || g_str_equal (chk_type, "sysusers")) { if (passwd) old_ents = rpmostree_passwd_data2passwdents (old_contents); @@ -548,6 +668,11 @@ rpmostree_check_passwd_groups (gboolean passwd, } } + /* Now, at the end of checking, if all goes correctly, we put entries into + * hashtable for sysusers */ + if (g_str_equal (chk_type, "sysusers") && + !rpmostree_passwd_ents2sysusers (passwd, new_ents, out_hashtable, error)) + return FALSE; return TRUE; } @@ -560,11 +685,12 @@ rpmostree_check_passwd (OstreeRepo *repo, GFile *treefile_dirpath, JsonObject *treedata, const char *previous_commit, + GHashTable **out_hashtable, GCancellable *cancellable, GError **error) { return rpmostree_check_passwd_groups (TRUE, repo, rootfs_fd, treefile_dirpath, - treedata, previous_commit, + treedata, previous_commit, out_hashtable, cancellable, error); } @@ -577,11 +703,12 @@ rpmostree_check_groups (OstreeRepo *repo, GFile *treefile_dirpath, JsonObject *treedata, const char *previous_commit, + GHashTable **out_hashtable, GCancellable *cancellable, GError **error) { return rpmostree_check_passwd_groups (TRUE, repo, rootfs_fd, treefile_dirpath, - treedata, previous_commit, + treedata, previous_commit, out_hashtable, cancellable, error); } diff --git a/src/libpriv/rpmostree-passwd-util.h b/src/libpriv/rpmostree-passwd-util.h index 9302581610..b396807c26 100644 --- a/src/libpriv/rpmostree-passwd-util.h +++ b/src/libpriv/rpmostree-passwd-util.h @@ -30,6 +30,7 @@ rpmostree_check_passwd (OstreeRepo *repo, GFile *treefile_path, JsonObject *treedata, const char *previous_commit, + GHashTable **out_hashtable, GCancellable *cancellable, GError **error); @@ -39,6 +40,7 @@ rpmostree_check_groups (OstreeRepo *repo, GFile *treefile_path, JsonObject *treedata, const char *previous_commit, + GHashTable **out_hashtable, GCancellable *cancellable, GError **error); @@ -89,12 +91,22 @@ gboolean rpmostree_passwd_complete_rpm_layering (int rootfs_dfd, GError **error); +struct sysuser_ent { + const char *type; /* type of sysuser entry, can be 1: u (user) 2: g (group) 3: m (mixed) 4: r (ranged ids) */ + char *name; + char *id; /* id in sysuser entry, can be in the form of 1: uid 2:gid 3: uid:gid, or we can separate this into uid_t and gid_t*/ + char *gecos; /* user information */ + char *dir; /* home directory */ + char *shell; /* Login shell, defaulted to /sbin/nologin */ +}; + struct conv_passwd_ent { char *name; uid_t uid; gid_t gid; char *pw_gecos; /* user information */ char *pw_dir; /* home directory */ + char *pw_shell; /* login shell */ }; struct conv_group_ent { @@ -107,3 +119,23 @@ rpmostree_passwd_data2passwdents (const char *data); GPtrArray * rpmostree_passwd_data2groupents (const char *data); + +gboolean +rpmostree_passwdents2sysusers (GPtrArray *passwd_ents, + GPtrArray **out_sysusers_entries, + GError **error); +gboolean +rpmostree_groupents2sysusers (GPtrArray *group_ents, + GPtrArray **out_sysusers_entries, + GError **error); + +gboolean +rpmostree_passwd_sysusers2char (GPtrArray *sysusers_entries, + char **out_content, + GError **error); + +gboolean +rpmostree_passwd_ents2sysusers (gboolean is_passwd, + GPtrArray *input_ents, + GHashTable **out_sysusers_table, + GError **error); diff --git a/tests/check/test-sysusers.c b/tests/check/test-sysusers.c new file mode 100644 index 0000000000..5df67f05c3 --- /dev/null +++ b/tests/check/test-sysusers.c @@ -0,0 +1,141 @@ +#include "config.h" +#include "libglnx.h" +#include "rpmostree-util.h" +#include "rpmostree-json-parsing.h" +#include "rpmostree-passwd-util.h" + +static const gchar *test_passwd[] = { + "chrony:x:994:992::/var/lib/chrony:/sbin/nologin", + "tcpdump:x:72:72::/:/sbin/nologin", + "systemd-timesync:x:993:991:a:/:/sbin/nologin", + "cockpit-ws:x:988:987:b:/:/sbin/nologin", + NULL +}; + +static const gchar *expected_sysuser_passwd_content[] ={ + "u chrony 994:992 - /var/lib/chrony /sbin/nologin", + "u tcpdump 72 - / /sbin/nologin", + "u systemd-timesync 993:991 \"a\" / /sbin/nologin", + "u cockpit-ws 988:987 \"b\" / /sbin/nologin", + NULL +}; + +static const gchar *test_group[] = { + "chrony:x:992:", + "tcpdump:x:72:", + "systemd-timesync:x:991:", + "cockpit-ws:x:987:", + "test:x:111:", + NULL +}; + +static const gchar *expected_sysuser_group_content[] = { + "g chrony 992 - - -", + "g tcpdump 72 - - -", + "g systemd-timesync 991 - - -", + "g cockpit-ws 987 - - -", + "g test 111 - - -", + NULL +}; + +static void +verify_sysuser_ent_content (GPtrArray *sysusers_entries, + const gchar **expected_content) +{ + /* Now we do the comparison between the converted / generated output to expected */ + for (int counter = 0; counter < sysusers_entries->len; counter++) + { + g_autofree gchar** sysent_list = g_strsplit (expected_content[counter], " ", -1); + struct sysuser_ent *sysuser_ent = sysusers_entries->pdata[counter]; + const char *shell = sysuser_ent->shell ?: "-"; + const char *gecos = sysuser_ent->gecos ?: "-"; + const char *dir = sysuser_ent->dir ?: "-"; + g_assert (g_str_equal (sysuser_ent->type, (char **)sysent_list[0])); + g_assert (g_str_equal (sysuser_ent->name, (char **)sysent_list[1])); + g_assert (g_str_equal (sysuser_ent->id, (char **)sysent_list[2])); + g_assert (g_str_equal (shell, (char **)sysent_list[5])); + g_assert (g_str_equal (gecos, (char **)sysent_list[3])); + g_assert (g_str_equal (dir, (char **)sysent_list[4])); + } +} + +static void +setup_sysuser_group (gchar **test_content, + GPtrArray **out_entries, + GError **error) +{ + gboolean ret; + g_autofree char* group_data = g_strjoinv ("\n", test_content); + g_autoptr(GPtrArray) group_ents = rpmostree_passwd_data2groupents (group_data); + ret = rpmostree_groupents2sysusers (group_ents, out_entries, error); + g_assert (ret); +} + +static void +setup_sysuser_passwd(gchar **test_content, + GPtrArray **out_entries, + GError **error) +{ + gboolean ret; + g_autofree char* passwd_data = g_strjoinv ("\n", test_content); + g_autoptr(GPtrArray) passwd_ents = rpmostree_passwd_data2passwdents (passwd_data); + ret = rpmostree_passwdents2sysusers (passwd_ents, out_entries, error); + g_assert(ret); +} + +// ".*'([^']*)'.* ---> regex string" +static void +test_passwd_conversion(void) +{ + g_autoptr(GPtrArray) sysusers_entries = NULL; + g_autoptr(GError) error = NULL; + + setup_sysuser_passwd((gchar**) test_passwd, &sysusers_entries, &error); + /* Check Hashtable properties */ + g_assert (sysusers_entries); + + verify_sysuser_ent_content (sysusers_entries, expected_sysuser_passwd_content); +} + +static void +test_group_conversion(void) +{ + g_autoptr(GPtrArray) sysusers_entries = NULL; + g_autoptr(GError) error = NULL; + + setup_sysuser_group ((gchar**) test_group, &sysusers_entries, &error); + /* Check Hashtable properties */ + g_assert (sysusers_entries); + + verify_sysuser_ent_content (sysusers_entries, expected_sysuser_group_content); +} + +static void +test_sysuser_conversion(void) +{ + g_autoptr(GPtrArray) sysusers_entries = NULL; + g_autoptr(GError) error = NULL; + gboolean ret; + + setup_sysuser_group ((gchar**) test_group, &sysusers_entries, &error); + setup_sysuser_passwd ((gchar**) test_passwd, &sysusers_entries, &error); + + g_autofree gchar* output_content = NULL; + ret = rpmostree_passwd_sysusers2char (sysusers_entries, &output_content, &error); + + g_assert (ret); + g_print("%s\n", output_content); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/sysusers/passwd_conversion", test_passwd_conversion); + g_test_add_func ("/sysusers/group_conversion", test_group_conversion); + g_test_add_func ("/sysusers/sysuser_conversion", test_sysuser_conversion); + return g_test_run (); +} +