Skip to content

Commit

Permalink
Issue 6417 - If an entry RDN is identical to the suffix, then Entryrd…
Browse files Browse the repository at this point in the history
…n 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 !!!)
  • Loading branch information
tbordaz committed Dec 2, 2024
1 parent b03d88e commit 37b4598
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 4 deletions.
109 changes: 107 additions & 2 deletions dirsrvtests/tests/suites/indexes/entryrdn_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
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.idm.user import UserAccounts
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
Expand Down Expand Up @@ -140,7 +145,107 @@ def test_tombstone(topo_m2):
assert error is False
checkdbscancount(s1, 'nsuniqueid', EXPECTED_NB_NSNIQUEID)

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
Expand Down
2 changes: 1 addition & 1 deletion ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,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] != '=') {
Expand Down
11 changes: 10 additions & 1 deletion ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,16 @@ entryrdn_lookup_dn(backend *be,
}
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);
Expand Down

0 comments on commit 37b4598

Please sign in to comment.