Blob Blame History Raw
From 62a93c78500edd9c67b2300cba1134b373138258 Mon Sep 17 00:00:00 2001
From: Colin Walters <walters@verbum.org>
Date: Thu, 16 Mar 2017 11:29:21 -0400
Subject: [PATCH] postprocess: Handle f26 /etc/nsswitch.conf configuration
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

F26 put sss first, which broke our regexp. When we switch to sysusers, man it'll
be nice to dump ♲ this.

Closes: https://github.com/projectatomic/rpm-ostree/issues/685

Closes: #686
Approved by: jlebon
---
 Makefile-tests.am                   |  5 ++
 src/libpriv/rpmostree-postprocess.c | 95 +++++++++++++++++++++++++++++--------
 src/libpriv/rpmostree-postprocess.h |  5 ++
 tests/check/postprocess.c           | 93 ++++++++++++++++++++++++++++++++++++
 4 files changed, 179 insertions(+), 19 deletions(-)
 create mode 100644 tests/check/postprocess.c

diff --git a/Makefile-tests.am b/Makefile-tests.am
index ab9f9cc..b173877 100644
--- a/Makefile-tests.am
+++ b/Makefile-tests.am
@@ -75,6 +75,10 @@ tests_check_cache_branch_to_nevra_CPPFLAGS = $(AM_CPPFLAGS) -I $(srcdir)/src/lib
 tests_check_cache_branch_to_nevra_CFLAGS = $(AM_CFLAGS) $(PKGDEP_RPMOSTREE_CFLAGS)
 tests_check_cache_branch_to_nevra_LDADD = $(PKGDEP_RPMOSTREE_LIBS) librpmostreepriv.la
 
+tests_check_postprocess_CPPFLAGS = $(AM_CPPFLAGS) -I $(srcdir)/src/libpriv -I $(srcdir)/libglnx
+tests_check_postprocess_CFLAGS = $(AM_CFLAGS) $(PKGDEP_RPMOSTREE_CFLAGS)
+tests_check_postprocess_LDADD = $(PKGDEP_RPMOSTREE_LIBS) librpmostreepriv.la
+
 tests/check/test-compose.sh: tests/common/compose/test-repo.repo
 
 tests/check/test-ucontainer.sh: tests/common/compose/test-repo.repo
@@ -82,6 +86,7 @@ tests/check/test-ucontainer.sh: tests/common/compose/test-repo.repo
 uninstalled_test_programs = \
 	tests/check/jsonutil			\
 	tests/check/cache_branch_to_nevra			\
+	tests/check/postprocess			\
 	$(NULL)
 
 uninstalled_test_scripts = \
diff --git a/src/libpriv/rpmostree-postprocess.c b/src/libpriv/rpmostree-postprocess.c
index dce8ddc..cff6e8d 100644
--- a/src/libpriv/rpmostree-postprocess.c
+++ b/src/libpriv/rpmostree-postprocess.c
@@ -512,34 +512,91 @@ rpmostree_prepare_rootfs_get_sepolicy (int            dfd,
   return ret;
 }
 
-static gboolean
-replace_nsswitch (int            dfd,
-                  GCancellable  *cancellable,
-                  GError       **error)
+static char *
+replace_nsswitch_string (const char *buf,
+                         GError    **error)
 {
-  g_autofree char *nsswitch_contents = NULL;
-  g_autofree char *new_nsswitch_contents = NULL;
+  gboolean is_passwd;
+  gboolean is_group;
+
+  is_passwd = g_str_has_prefix (buf, "passwd:");
+  is_group = g_str_has_prefix (buf, "group:");
+
+  if (!(is_passwd || is_group))
+    return g_strdup (buf);
+
+  const char *colon = strchr (buf, ':');
+  g_assert (colon);
+
+  g_autoptr(GString) retbuf = g_string_new ("");
+  /* Insert the prefix */
+  g_string_append_len (retbuf, buf, (colon - buf) + 1);
+
+  /* Now parse the elements and try to insert `altfiles`
+   * after `files`.
+   */
+  g_auto(GStrv) elts = g_strsplit_set (colon + 1, " \t", -1);
+  gboolean inserted = FALSE;
+  for (char **iter = elts; iter && *iter; iter++)
+    {
+      const char *v = *iter;
+      if (!*v)
+        continue;
+      /* Already have altfiles?  We're done */
+      if (strcmp (v, "altfiles") == 0)
+        return g_strdup (buf);
+      /* We prefer `files altfiles` */
+      else if (!inserted && strcmp (v, "files") == 0)
+        {
+          g_string_append (retbuf, " files altfiles");
+          inserted = TRUE;
+        }
+      else
+        {
+          g_string_append_c (retbuf, ' ');
+          g_string_append (retbuf, v);
+        }
+    }
+  /* Last ditch effort if we didn't find `files` */
+  if (!inserted)
+    g_string_append (retbuf, " altfiles");
+  return g_string_free (g_steal_pointer (&retbuf), FALSE);
+}
 
-  static gsize regex_initialized;
-  static GRegex *passwd_regex;
+char *
+rpmostree_postprocess_replace_nsswitch (const char *buf,
+                                        GError    **error)
+{
+  g_autoptr(GString) new_buf = g_string_new ("");
 
-  if (g_once_init_enter (&regex_initialized))
+  g_auto(GStrv) lines = g_strsplit (buf, "\n", -1);
+  for (char **iter = lines; iter && *iter; iter++)
     {
-      passwd_regex = g_regex_new ("^(passwd|group):\\s+files(.*)$",
-                                  G_REGEX_MULTILINE, 0, NULL);
-      g_assert (passwd_regex);
-      g_once_init_leave (&regex_initialized, 1);
+      const char *line = *iter;
+      g_autofree char *replaced_line = replace_nsswitch_string (line, error);
+      if (!replaced_line)
+        return NULL;
+      g_string_append (new_buf, replaced_line);
+      if (*(iter+1))
+        g_string_append_c (new_buf, '\n');
     }
+  return g_string_free (g_steal_pointer (&new_buf), FALSE);
+}
+
 
-  nsswitch_contents = glnx_file_get_contents_utf8_at (dfd, "etc/nsswitch.conf", NULL,
-                                                      cancellable, error);
+static gboolean
+replace_nsswitch (int            dfd,
+                  GCancellable  *cancellable,
+                  GError       **error)
+{
+  g_autofree char *nsswitch_contents =
+    glnx_file_get_contents_utf8_at (dfd, "etc/nsswitch.conf", NULL,
+                                    cancellable, error);
   if (!nsswitch_contents)
     return FALSE;
 
-  new_nsswitch_contents = g_regex_replace (passwd_regex,
-                                           nsswitch_contents, -1, 0,
-                                           "\\1: files altfiles\\2",
-                                           0, error);
+  g_autofree char *new_nsswitch_contents =
+    rpmostree_postprocess_replace_nsswitch (nsswitch_contents, error);
   if (!new_nsswitch_contents)
     return FALSE;
 
diff --git a/src/libpriv/rpmostree-postprocess.h b/src/libpriv/rpmostree-postprocess.h
index a270c98..1c972ac 100644
--- a/src/libpriv/rpmostree-postprocess.h
+++ b/src/libpriv/rpmostree-postprocess.h
@@ -23,6 +23,11 @@
 #include <ostree.h>
 #include "rpmostree-json-parsing.h"
 
+/* "public" for unit tests */
+char *
+rpmostree_postprocess_replace_nsswitch (const char *buf,
+                                        GError    **error);
+
 gboolean
 rpmostree_treefile_postprocessing (int            rootfs_fd,
                                    GFile         *context_directory,
diff --git a/tests/check/postprocess.c b/tests/check/postprocess.c
new file mode 100644
index 0000000..135be94
--- /dev/null
+++ b/tests/check/postprocess.c
@@ -0,0 +1,93 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-unix.h>
+#include "libglnx.h"
+#include "rpmostree-postprocess.h"
+
+typedef struct {
+  const char *input;
+  const char *output;
+} AltfilesTest;
+
+static AltfilesTest altfiles_tests[] = {
+  {
+    /* F25 */
+  .input = "# An nsswitch.conf\n" \
+  "\npasswd: files sss\n" \
+  "\ngroup: files sss\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n",
+  .output = "# An nsswitch.conf\n" \
+  "\npasswd: files altfiles sss\n" \
+  "\ngroup: files altfiles sss\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n"
+  },
+  {
+    /* F26 */
+  .input = "# An nsswitch.conf\n" \
+  "\npasswd: sss files systemd\n" \
+  "\ngroup: sss files systemd\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n",
+  .output = "# An nsswitch.conf\n" \
+  "\npasswd: sss files altfiles systemd\n" \
+  "\ngroup: sss files altfiles systemd\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n"
+  },
+  {
+    /* Already have altfiles, input/output identical */
+  .input = "# An nsswitch.conf\n" \
+  "\npasswd: sss files altfiles systemd\n" \
+  "\ngroup: sss files altfiles systemd\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n",
+  .output = "# An nsswitch.conf\n" \
+  "\npasswd: sss files altfiles systemd\n" \
+  "\ngroup: sss files altfiles systemd\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n",
+  },
+  {
+    /* Test having `files` as a substring */
+  .input = "# An nsswitch.conf\n" \
+  "\npasswd: sss foofiles files systemd\n" \
+  "\ngroup: sss foofiles files systemd\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n",
+  .output = "# An nsswitch.conf\n" \
+  "\npasswd: sss foofiles files altfiles systemd\n" \
+  "\ngroup: sss foofiles files altfiles systemd\n" \
+  "\nhosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname\n",
+  }
+};
+
+static void
+test_postprocess_altfiles (void)
+{
+  g_autoptr(GError) local_error = NULL;
+  GError **error = &local_error;
+
+  for (guint i = 0; i < G_N_ELEMENTS(altfiles_tests); i++)
+    {
+      AltfilesTest *test = &altfiles_tests[i];
+      g_autofree char *newbuf = rpmostree_postprocess_replace_nsswitch (test->input, error);
+
+      if (!newbuf)
+        goto out;
+
+      g_assert_cmpstr (newbuf, ==, test->output);
+    }
+
+ out:
+  g_assert_no_error (local_error);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/altfiles", test_postprocess_altfiles);
+
+  return g_test_run ();
+}
-- 
2.9.3