Blame SOURCES/0015-Issue-51157-Reindex-task-may-create-abandoned-index-.patch

d69b2b
From 3b3faee01e645577ad77ff4f38429a9e0806231b Mon Sep 17 00:00:00 2001
d69b2b
From: Simon Pichugin <simon.pichugin@gmail.com>
d69b2b
Date: Tue, 16 Jun 2020 20:35:05 +0200
d69b2b
Subject: [PATCH] Issue 51157 - Reindex task may create abandoned index file
d69b2b
d69b2b
Bug Description: Recreating an index for the same attribute but changing
d69b2b
the case of for example 1 letter, results in abandoned indexfile.
d69b2b
d69b2b
Fix Decsription: Add a test case to a newly created 'indexes' test suite.
d69b2b
When we remove the index config from the backend, - remove the attribute
d69b2b
info from LDBM instance attributes.
d69b2b
d69b2b
https://pagure.io/389-ds-base/issue/51157
d69b2b
d69b2b
Reviewed by: firstyear, mreynolds (Thanks!)
d69b2b
---
d69b2b
 dirsrvtests/tests/suites/indexes/__init__.py  |   3 +
d69b2b
 .../tests/suites/indexes/regression_test.py   | 125 ++++++++++++++++++
d69b2b
 ldap/servers/slapd/back-ldbm/ldbm_attr.c      |   7 +
d69b2b
 .../slapd/back-ldbm/ldbm_index_config.c       |   3 +
d69b2b
 .../servers/slapd/back-ldbm/proto-back-ldbm.h |   1 +
d69b2b
 5 files changed, 139 insertions(+)
d69b2b
 create mode 100644 dirsrvtests/tests/suites/indexes/__init__.py
d69b2b
 create mode 100644 dirsrvtests/tests/suites/indexes/regression_test.py
d69b2b
d69b2b
diff --git a/dirsrvtests/tests/suites/indexes/__init__.py b/dirsrvtests/tests/suites/indexes/__init__.py
d69b2b
new file mode 100644
d69b2b
index 000000000..04441667e
d69b2b
--- /dev/null
d69b2b
+++ b/dirsrvtests/tests/suites/indexes/__init__.py
d69b2b
@@ -0,0 +1,3 @@
d69b2b
+"""
d69b2b
+   :Requirement: 389-ds-base: Indexes
d69b2b
+"""
d69b2b
diff --git a/dirsrvtests/tests/suites/indexes/regression_test.py b/dirsrvtests/tests/suites/indexes/regression_test.py
d69b2b
new file mode 100644
d69b2b
index 000000000..1a71f16e9
d69b2b
--- /dev/null
d69b2b
+++ b/dirsrvtests/tests/suites/indexes/regression_test.py
d69b2b
@@ -0,0 +1,125 @@
d69b2b
+# --- BEGIN COPYRIGHT BLOCK ---
d69b2b
+# Copyright (C) 2020 Red Hat, Inc.
d69b2b
+# All rights reserved.
d69b2b
+#
d69b2b
+# License: GPL (version 3 or any later version).
d69b2b
+# See LICENSE for details.
d69b2b
+# --- END COPYRIGHT BLOCK ---
d69b2b
+#
d69b2b
+import time
d69b2b
+import os
d69b2b
+import pytest
d69b2b
+import ldap
d69b2b
+from lib389._constants import DEFAULT_BENAME, DEFAULT_SUFFIX
d69b2b
+from lib389.index import Indexes
d69b2b
+from lib389.backend import Backends
d69b2b
+from lib389.idm.user import UserAccounts
d69b2b
+from lib389.topologies import topology_st as topo
d69b2b
+
d69b2b
+pytestmark = pytest.mark.tier1
d69b2b
+
d69b2b
+
d69b2b
+def test_reindex_task_creates_abandoned_index_file(topo):
d69b2b
+    """
d69b2b
+    Recreating an index for the same attribute but changing
d69b2b
+    the case of for example 1 letter, results in abandoned indexfile
d69b2b
+
d69b2b
+    :id: 07ae5274-481a-4fa8-8074-e0de50d89ac6
d69b2b
+    :setup: Standalone instance
d69b2b
+    :steps:
d69b2b
+        1. Create a user object with additional attributes:
d69b2b
+           objectClass: mozillaabpersonalpha
d69b2b
+           mozillaCustom1: xyz
d69b2b
+        2. Add an index entry mozillacustom1
d69b2b
+        3. Reindex the backend
d69b2b
+        4. Check the content of the index (after it has been flushed to disk) mozillacustom1.db
d69b2b
+        5. Remove the index
d69b2b
+        6. Notice the mozillacustom1.db is removed
d69b2b
+        7. Recreate the index but now use the exact case as mentioned in the schema
d69b2b
+        8. Reindex the backend
d69b2b
+        9. Check the content of the index (after it has been flushed to disk) mozillaCustom1.db
d69b2b
+        10. Check that an ldapsearch does not return a result (mozillacustom1=xyz)
d69b2b
+        11. Check that an ldapsearch returns the results (mozillaCustom1=xyz)
d69b2b
+        12. Restart the instance
d69b2b
+        13. Notice that an ldapsearch does not return a result(mozillacustom1=xyz)
d69b2b
+        15. Check that an ldapsearch does not return a result (mozillacustom1=xyz)
d69b2b
+        16. Check that an ldapsearch returns the results (mozillaCustom1=xyz)
d69b2b
+        17. Reindex the backend
d69b2b
+        18. Notice the second indexfile for this attribute
d69b2b
+        19. Check the content of the index (after it has been flushed to disk) no mozillacustom1.db
d69b2b
+        20. Check the content of the index (after it has been flushed to disk) mozillaCustom1.db
d69b2b
+    :expectedresults:
d69b2b
+        1. Should Success.
d69b2b
+        2. Should Success.
d69b2b
+        3. Should Success.
d69b2b
+        4. Should Success.
d69b2b
+        5. Should Success.
d69b2b
+        6. Should Success.
d69b2b
+        7. Should Success.
d69b2b
+        8. Should Success.
d69b2b
+        9. Should Success.
d69b2b
+        10. Should Success.
d69b2b
+        11. Should Success.
d69b2b
+        12. Should Success.
d69b2b
+        13. Should Success.
d69b2b
+        14. Should Success.
d69b2b
+        15. Should Success.
d69b2b
+        16. Should Success.
d69b2b
+        17. Should Success.
d69b2b
+        18. Should Success.
d69b2b
+        19. Should Success.
d69b2b
+        20. Should Success.
d69b2b
+    """
d69b2b
+
d69b2b
+    inst = topo.standalone
d69b2b
+    attr_name = "mozillaCustom1"
d69b2b
+    attr_value = "xyz"
d69b2b
+
d69b2b
+    users = UserAccounts(inst, DEFAULT_SUFFIX)
d69b2b
+    user = users.create_test_user()
d69b2b
+    user.add("objectClass", "mozillaabpersonalpha")
d69b2b
+    user.add(attr_name, attr_value)
d69b2b
+
d69b2b
+    backends = Backends(inst)
d69b2b
+    backend = backends.get(DEFAULT_BENAME)
d69b2b
+    indexes = backend.get_indexes()
d69b2b
+    index = indexes.create(properties={
d69b2b
+        'cn': attr_name.lower(),
d69b2b
+        'nsSystemIndex': 'false',
d69b2b
+        'nsIndexType': ['eq', 'pres']
d69b2b
+        })
d69b2b
+
d69b2b
+    backend.reindex()
d69b2b
+    time.sleep(3)
d69b2b
+    assert os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
d69b2b
+    index.delete()
d69b2b
+    assert not os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
d69b2b
+
d69b2b
+    index = indexes.create(properties={
d69b2b
+        'cn': attr_name,
d69b2b
+        'nsSystemIndex': 'false',
d69b2b
+        'nsIndexType': ['eq', 'pres']
d69b2b
+        })
d69b2b
+
d69b2b
+    backend.reindex()
d69b2b
+    time.sleep(3)
d69b2b
+    assert not os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
d69b2b
+    assert os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name}.db")
d69b2b
+
d69b2b
+    entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f"{attr_name}={attr_value}")
d69b2b
+    assert len(entries) > 0
d69b2b
+    inst.restart()
d69b2b
+    entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f"{attr_name}={attr_value}")
d69b2b
+    assert len(entries) > 0
d69b2b
+
d69b2b
+    backend.reindex()
d69b2b
+    time.sleep(3)
d69b2b
+    assert not os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
d69b2b
+    assert os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name}.db")
d69b2b
+
d69b2b
+
d69b2b
+if __name__ == "__main__":
d69b2b
+    # Run isolated
d69b2b
+    # -s for DEBUG mode
d69b2b
+    CURRENT_FILE = os.path.realpath(__file__)
d69b2b
+    pytest.main("-s %s" % CURRENT_FILE)
d69b2b
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attr.c b/ldap/servers/slapd/back-ldbm/ldbm_attr.c
d69b2b
index f0d418572..688c4f137 100644
d69b2b
--- a/ldap/servers/slapd/back-ldbm/ldbm_attr.c
d69b2b
+++ b/ldap/servers/slapd/back-ldbm/ldbm_attr.c
d69b2b
@@ -98,6 +98,13 @@ ainfo_cmp(
d69b2b
     return (strcasecmp(a->ai_type, b->ai_type));
d69b2b
 }
d69b2b
 
d69b2b
+void
d69b2b
+attrinfo_delete_from_tree(backend *be, struct attrinfo *ai)
d69b2b
+{
d69b2b
+    ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
d69b2b
+    avl_delete(&inst->inst_attrs, ai, ainfo_cmp);
d69b2b
+}
d69b2b
+
d69b2b
 /*
d69b2b
  * Called when a duplicate "index" line is encountered.
d69b2b
  *
d69b2b
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
d69b2b
index 720f93036..9722d0ce7 100644
d69b2b
--- a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
d69b2b
+++ b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
d69b2b
@@ -201,7 +201,10 @@ ldbm_instance_index_config_delete_callback(Slapi_PBlock *pb,
d69b2b
             *returncode = LDAP_UNWILLING_TO_PERFORM;
d69b2b
             rc = SLAPI_DSE_CALLBACK_ERROR;
d69b2b
         }
d69b2b
+        attrinfo_delete_from_tree(inst->inst_be, ainfo);
d69b2b
     }
d69b2b
+    /* Free attrinfo structure */
d69b2b
+    attrinfo_delete(&ainfo);
d69b2b
 bail:
d69b2b
     return rc;
d69b2b
 }
d69b2b
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
d69b2b
index a07acee5e..4d2524fd9 100644
d69b2b
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
d69b2b
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
d69b2b
@@ -21,6 +21,7 @@
d69b2b
  */
d69b2b
 struct attrinfo *attrinfo_new(void);
d69b2b
 void attrinfo_delete(struct attrinfo **pp);
d69b2b
+void attrinfo_delete_from_tree(backend *be, struct attrinfo *ai);
d69b2b
 void ainfo_get(backend *be, char *type, struct attrinfo **at);
d69b2b
 void attr_masks(backend *be, char *type, int *indexmask, int *syntaxmask);
d69b2b
 void attr_masks_ex(backend *be, char *type, int *indexmask, int *syntaxmask, struct attrinfo **at);
d69b2b
-- 
d69b2b
2.26.2
d69b2b