389-ds-base/SOURCES/0010-Issue-6417-If-an-entry-RDN-is-identical-to-the-suffi.patch

201 lines
7.6 KiB
Diff

From 7ed791beda5d507e15c970a88289f533aa06b2d3 Mon Sep 17 00:00:00 2001
From: tbordaz <tbordaz@redhat.com>
Date: Mon, 2 Dec 2024 17:18:32 +0100
Subject: [PATCH] Issue 6417 - If an entry RDN is identical to the suffix, then
Entryrdn gets broken during a reindex (#6418)
Bug description:
During a reindex, the entryrdn index is built at the end from
each entry in the suffix.
If one entry has a RDN that is identical to the suffix DN,
then entryrdn_lookup_dn may erroneously return the suffix DN
as the DN of the entry.
Fix description:
When the lookup entry has no parent (because index is under
work) the loop lookup the entry using the RDN.
If this RDN matches the suffix DN, then it exits from the loop
with the suffix DN.
Before exiting it checks that the original lookup entryID
is equal to suffix entryID. If it does not match
the function fails and then the DN from the entry will be
built from id2enty
fixes: #6417
Reviewed by: Pierre Rogier, Simon Pichugin (Thanks !!!)
---
.../tests/suites/indexes/entryrdn_test.py | 108 +++++++++++++++++-
.../back-ldbm/db-mdb/mdb_import_threads.c | 2 +-
ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c | 11 +-
3 files changed, 118 insertions(+), 3 deletions(-)
diff --git a/dirsrvtests/tests/suites/indexes/entryrdn_test.py b/dirsrvtests/tests/suites/indexes/entryrdn_test.py
index 345955d4d..7669292ab 100644
--- a/dirsrvtests/tests/suites/indexes/entryrdn_test.py
+++ b/dirsrvtests/tests/suites/indexes/entryrdn_test.py
@@ -11,11 +11,16 @@ import os
import pytest
import ldap
import logging
+from lib389 import Entry
from lib389._constants import DEFAULT_BENAME, DEFAULT_SUFFIX
-from lib389.backend import Backends
+from lib389.backend import Backends, Backend
+from lib389.mappingTree import MappingTrees
+from lib389.configurations.sample import create_base_domain
+from lib389.idm.domain import Domain
from lib389.idm.user import UserAccounts, UserAccount
from lib389.idm.organizationalunit import OrganizationalUnits
from lib389.topologies import topology_m2 as topo_m2
+from lib389.topologies import topology_st
from lib389.agreement import Agreements
from lib389.utils import ds_is_older, ensure_bytes
from lib389.tasks import Tasks,ExportTask, ImportTask
@@ -283,6 +288,107 @@ def test_long_rdn(topo_m2):
ou.delete()
assert not ou.exists()
+def test_entry_rdn_same_as_suffix(topology_st, request):
+ """
+ Test that a reindex is successful even if an entry
+ has a RDN that is identical to the suffix
+
+ :id: 7f5a38e9-b979-4664-b132-81df0e60f38a
+ :setup: standalone
+ :steps:
+ 1. Create a new backend with suffix 'dc=dup_rdn' (ID 1)
+ 2. Create a dummy entry 'ou=my_org,dc=dup_rdn' (ID 2)
+ 3. Create the problematic entry 'dc=dup_rdn,dc=dup_rdn' (ID 3)
+ 4. Create a dummy entry 'ou=my_org,dc=dup_rdn,dc=dup_rdn' (ID 4)
+ 5. Do an offline reindex
+ 6. Check that entryrdn contains the key P3 (parent of ID 3)
+ 7. Check that error log does not contain 'entryrdn_insert_key - Same DN'
+ :expectedresults:
+ 1. Should succeed
+ 2. Should succeed
+ 3. Should succeed
+ 4. Should succeed
+ 5. Should succeed
+ 6. Should succeed
+ 7. Should succeed
+ """
+ inst = topology_st.standalone
+
+ # Create a suffix 'dc=dup_rdn'
+ be_name = 'domain'
+ dc_value = 'dup_rdn'
+ suffix = 'dc=' + dc_value
+ rdn = 'my_org'
+ be1 = Backend(inst)
+ be1.create(properties={
+ 'cn': be_name,
+ 'nsslapd-suffix': suffix,
+ },
+ create_mapping_tree=False
+ )
+
+ mts = MappingTrees(inst)
+ mt = mts.create(properties={
+ 'cn': suffix,
+ 'nsslapd-state': 'backend',
+ 'nsslapd-backend': be_name,
+ })
+
+ # Create the domain entry 'dc=dup_rdn'
+ create_base_domain(inst, suffix)
+
+ # Create the org ou=my_org,dc=dup_rdn
+ ous = OrganizationalUnits(inst, suffix)
+ ou = ous.create(properties={ 'ou': rdn })
+
+ # when reindexing entryrdn the following entry
+ # (dc=dup_rdn,dc=dup_rdn) Triggers
+ # this message.
+ # This is because its RDN (dc=dup_rdn) is also
+ # the suffix DN
+ info_message = 'entryrdn_insert_key - Same DN (dn: %s) is already in the entryrdn file with different' % (ou.dn)
+ log.info("In case if the bug still exist this line should be in the error log")
+ log.info(" --> " + info_message)
+
+ # Create the domain entry 'dc=dup_rdn,dc=dup_rdn'
+ trigger_entry = suffix + "," + suffix
+ domain = Domain(inst, dn=trigger_entry)
+ domain.create(properties={
+ 'dc': dc_value,
+ 'description': 'Entry with RDN identical to suffix'
+ })
+
+ # Create the org ou=my_org,dc=dup_rdn,dc=dup_rdn
+ ous = OrganizationalUnits(inst, trigger_entry)
+ ou = ous.create(properties={ 'ou': rdn })
+
+
+ # Trigger an offline reindex
+ log.info('Offline reindex, stopping the server')
+ topology_st.standalone.stop()
+
+ log.info('Reindex all the suffix')
+ topology_st.standalone.db2index(bename=be_name)
+
+ # make sure the key 'P3' (parent of 'dc=dup_rdn,dc=dup_rdn') exists
+ dbscanout = topology_st.standalone.dbscan(bename=be_name, index='entryrdn')
+ log.info(dbscanout)
+ assert(ensure_bytes('P3') in ensure_bytes(dbscanout))
+
+ # make sure there is no failure detected/logged in error logs
+ if topology_st.standalone.get_db_lib() == "mdb":
+ pattern_str = ".*Inconsistent id2entry database.*"
+ else:
+ pattern_str = ".*entryrdn_insert_key - Same DN.*is already in the entryrdn file with different.*$"
+ assert not topology_st.standalone.ds_error_log.match(pattern_str)
+
+
+ def fin():
+ topology_st.standalone.restart()
+ mt.delete()
+ be1.delete()
+
+ request.addfinalizer(fin)
if __name__ == "__main__":
# Run isolated
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
index 143f2c9df..65f564aff 100644
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
@@ -731,7 +731,7 @@ get_entry_type(WorkerQueueData_t *wqelmt, Slapi_DN *sdn)
int len = SLAPI_ATTR_UNIQUEID_LENGTH;
const char *ndn = slapi_sdn_get_ndn(sdn);
- if (slapi_be_issuffix(be, sdn)) {
+ if (slapi_be_issuffix(be, sdn) && (wqelmt->wait_id == 1)) {
return DNRC_SUFFIX;
}
if (PL_strncasecmp(ndn, SLAPI_ATTR_UNIQUEID, len) || ndn[len] != '=') {
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
index 267957a36..8901b790a 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
@@ -1077,7 +1077,16 @@ entryrdn_lookup_dn(backend *be,
_ENTRYRDN_DEBUG_GOTO_BAIL();
goto bail;
}
- maybesuffix = 1;
+ if (workid == 1) {
+ /* The loop (workid) iterates from the starting 'id'
+ * up to the suffix ID (i.e. '1').
+ * A corner case (#6417) is if an entry, on the path
+ * 'id' -> suffix, has the same RDN than the suffix.
+ * In order to erroneously believe the loop hits the suffix
+ * we need to check that 'workid' is '1' (suffix)
+ */
+ maybesuffix = 1;
+ }
} else {
_entryrdn_cursor_print_error("entryrdn_lookup_dn",
key.data, data.size, data.ulen, rc);
--
2.48.1