90dd145
From 7703a7efe1ed4800a7676cfaac9bd00fec7de1c4 Mon Sep 17 00:00:00 2001
90dd145
From: Jakub Hrozek <jhrozek@redhat.com>
90dd145
Date: Wed, 4 Apr 2018 14:13:56 +0200
90dd145
Subject: [PATCH] FILES: Skip files that are not created yet
90dd145
MIME-Version: 1.0
90dd145
Content-Type: text/plain; charset=UTF-8
90dd145
Content-Transfer-Encoding: 8bit
90dd145
90dd145
In order to avoid complex ordering logic, even if one file is updated,
90dd145
we flush all the entries. In theory, we could only flush the individual
90dd145
file and all the files preceding it, but it's safer to just create a
90dd145
complete mirror every time.
90dd145
90dd145
And this can be problematic if one of the files we try to update is not
90dd145
created yet during the update. This can happen e.g. when a file is not
90dd145
created during early boot.
90dd145
90dd145
To solve this, try to be very defensive and always flush the whole
90dd145
database, ignore ENOENT errors, but abort on all other errors.
90dd145
90dd145
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
90dd145
(cherry picked from commit c1bce7da6c33b352dc708a5dd9712a4d96c63057)
90dd145
---
90dd145
 src/providers/files/files_ops.c       | 22 ++++++++++---
90dd145
 src/tests/intg/test_files_provider.py | 60 +++++++++++++++++++++++++++++++++++
90dd145
 2 files changed, 78 insertions(+), 4 deletions(-)
90dd145
90dd145
diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c
90dd145
index b91078417..f5a40297a 100644
90dd145
--- a/src/providers/files/files_ops.c
90dd145
+++ b/src/providers/files/files_ops.c
90dd145
@@ -734,8 +734,15 @@ static errno_t sf_enum_files(struct files_id_ctx *id_ctx,
90dd145
         /* All users were deleted, therefore we need to enumerate each file again */
90dd145
         for (size_t i = 0; id_ctx->passwd_files[i] != NULL; i++) {
90dd145
             ret = sf_enum_users(id_ctx, id_ctx->passwd_files[i]);
90dd145
-            if (ret != EOK) {
90dd145
-                DEBUG(SSSDBG_OP_FAILURE, "Cannot enumerate users\n");
90dd145
+            if (ret == ENOENT) {
90dd145
+                DEBUG(SSSDBG_MINOR_FAILURE,
90dd145
+                      "The file %s does not exist (yet), skipping\n",
90dd145
+                      id_ctx->passwd_files[i]);
90dd145
+                continue;
90dd145
+            } else if (ret != EOK) {
90dd145
+                DEBUG(SSSDBG_OP_FAILURE,
90dd145
+                      "Cannot enumerate users from %s, aborting\n",
90dd145
+                      id_ctx->passwd_files[i]);
90dd145
                 goto done;
90dd145
             }
90dd145
         }
90dd145
@@ -750,8 +757,15 @@ static errno_t sf_enum_files(struct files_id_ctx *id_ctx,
90dd145
         /* All groups were deleted, therefore we need to enumerate each file again */
90dd145
         for (size_t i = 0; id_ctx->group_files[i] != NULL; i++) {
90dd145
             ret = sf_enum_groups(id_ctx, id_ctx->group_files[i]);
90dd145
-            if (ret != EOK) {
90dd145
-                DEBUG(SSSDBG_OP_FAILURE, "Cannot enumerate groups\n");
90dd145
+            if (ret == ENOENT) {
90dd145
+                DEBUG(SSSDBG_MINOR_FAILURE,
90dd145
+                      "The file %s does not exist (yet), skipping\n",
90dd145
+                      id_ctx->group_files[i]);
90dd145
+                continue;
90dd145
+            } else if (ret != EOK) {
90dd145
+                DEBUG(SSSDBG_OP_FAILURE,
90dd145
+                      "Cannot enumerate groups from %s, aborting\n",
90dd145
+                      id_ctx->group_files[i]);
90dd145
                 goto done;
90dd145
             }
90dd145
         }
90dd145
diff --git a/src/tests/intg/test_files_provider.py b/src/tests/intg/test_files_provider.py
90dd145
index ce5c7b774..cc9c1f1c7 100644
90dd145
--- a/src/tests/intg/test_files_provider.py
90dd145
+++ b/src/tests/intg/test_files_provider.py
90dd145
@@ -187,6 +187,40 @@ def files_multiple_sources(request):
90dd145
     return alt_pwops, alt_grops
90dd145
 
90dd145
 
90dd145
+@pytest.fixture
90dd145
+def files_multiple_sources_nocreate(request):
90dd145
+    """
90dd145
+    Sets up SSSD with multiple sources, but does not actually create
90dd145
+    the files.
90dd145
+    """
90dd145
+    alt_passwd_path = tempfile.mktemp(prefix='altpasswd')
90dd145
+    request.addfinalizer(lambda: os.unlink(alt_passwd_path))
90dd145
+
90dd145
+    alt_group_path = tempfile.mktemp(prefix='altgroup')
90dd145
+    request.addfinalizer(lambda: os.unlink(alt_group_path))
90dd145
+
90dd145
+    passwd_list = ",".join([os.environ["NSS_WRAPPER_PASSWD"], alt_passwd_path])
90dd145
+    group_list = ",".join([os.environ["NSS_WRAPPER_GROUP"], alt_group_path])
90dd145
+
90dd145
+    conf = unindent("""\
90dd145
+        [sssd]
90dd145
+        domains             = files
90dd145
+        services            = nss
90dd145
+
90dd145
+        [nss]
90dd145
+        debug_level = 10
90dd145
+
90dd145
+        [domain/files]
90dd145
+        id_provider = files
90dd145
+        passwd_files = {passwd_list}
90dd145
+        group_files = {group_list}
90dd145
+        debug_level = 10
90dd145
+    """).format(**locals())
90dd145
+    create_conf_fixture(request, conf)
90dd145
+    create_sssd_fixture(request)
90dd145
+    return alt_passwd_path, alt_group_path
90dd145
+
90dd145
+
90dd145
 @pytest.fixture
90dd145
 def proxy_to_files_domain_only(request):
90dd145
     conf = unindent("""\
90dd145
@@ -1113,3 +1147,29 @@ def test_multiple_passwd_group_files(add_user_with_canary,
90dd145
 
90dd145
     check_group(GROUP1)
90dd145
     check_group(ALT_GROUP1)
90dd145
+
90dd145
+
90dd145
+def test_multiple_files_created_after_startup(add_user_with_canary,
90dd145
+                                              add_group_with_canary,
90dd145
+                                              files_multiple_sources_nocreate):
90dd145
+    """
90dd145
+    Test that users and groups can be mirrored from multiple files,
90dd145
+    but those files are not created when SSSD starts, only afterwards.
90dd145
+    """
90dd145
+    alt_passwd_path, alt_group_path = files_multiple_sources_nocreate
90dd145
+
90dd145
+    check_user(USER1)
90dd145
+    check_group(GROUP1)
90dd145
+
90dd145
+    # touch the files
90dd145
+    for fpath in (alt_passwd_path, alt_group_path):
90dd145
+        with open(fpath, "w") as f:
90dd145
+            pass
90dd145
+
90dd145
+    alt_pwops = PasswdOps(alt_passwd_path)
90dd145
+    alt_grops = GroupOps(alt_group_path)
90dd145
+    alt_pwops.useradd(**ALT_USER1)
90dd145
+    alt_grops.groupadd(**ALT_GROUP1)
90dd145
+
90dd145
+    check_user(ALT_USER1)
90dd145
+    check_group(ALT_GROUP1)
90dd145
-- 
90dd145
2.14.3
90dd145