|
|
d69b2b |
From 6cd4b1c60dbd3d7b74adb19a2434585d50553f39 Mon Sep 17 00:00:00 2001
|
|
|
d69b2b |
From: Thierry Bordaz <tbordaz@redhat.com>
|
|
|
d69b2b |
Date: Fri, 5 Jun 2020 12:14:51 +0200
|
|
|
d69b2b |
Subject: [PATCH] Ticket 49859 - A distinguished value can be missing in an
|
|
|
d69b2b |
entry
|
|
|
d69b2b |
|
|
|
d69b2b |
Bug description:
|
|
|
d69b2b |
According to RFC 4511 (see ticket), the values of the RDN attributes
|
|
|
d69b2b |
should be present in an entry.
|
|
|
d69b2b |
With a set of replicated operations, it is possible that those values
|
|
|
d69b2b |
would be missing
|
|
|
d69b2b |
|
|
|
d69b2b |
Fix description:
|
|
|
d69b2b |
MOD and MODRDN update checks that the RDN values are presents.
|
|
|
d69b2b |
If they are missing they are added to the resulting entry. In addition
|
|
|
d69b2b |
the set of modifications to add those values are also indexed.
|
|
|
d69b2b |
The specific case of single-valued attributes, where the final and unique value
|
|
|
d69b2b |
can not be the RDN value, the attribute nsds5ReplConflict is added.
|
|
|
d69b2b |
|
|
|
d69b2b |
https://pagure.io/389-ds-base/issue/49859
|
|
|
d69b2b |
|
|
|
d69b2b |
Reviewed by: Mark Reynolds, William Brown
|
|
|
d69b2b |
|
|
|
d69b2b |
Platforms tested: F31
|
|
|
d69b2b |
---
|
|
|
d69b2b |
.../replication/conflict_resolve_test.py | 174 +++++++++++++++++-
|
|
|
d69b2b |
ldap/servers/slapd/back-ldbm/ldbm_modify.c | 136 ++++++++++++++
|
|
|
d69b2b |
ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 37 +++-
|
|
|
d69b2b |
.../servers/slapd/back-ldbm/proto-back-ldbm.h | 1 +
|
|
|
d69b2b |
4 files changed, 343 insertions(+), 5 deletions(-)
|
|
|
d69b2b |
|
|
|
d69b2b |
diff --git a/dirsrvtests/tests/suites/replication/conflict_resolve_test.py b/dirsrvtests/tests/suites/replication/conflict_resolve_test.py
|
|
|
d69b2b |
index 99a072935..48d0067db 100644
|
|
|
d69b2b |
--- a/dirsrvtests/tests/suites/replication/conflict_resolve_test.py
|
|
|
d69b2b |
+++ b/dirsrvtests/tests/suites/replication/conflict_resolve_test.py
|
|
|
d69b2b |
@@ -10,10 +10,11 @@ import time
|
|
|
d69b2b |
import logging
|
|
|
d69b2b |
import ldap
|
|
|
d69b2b |
import pytest
|
|
|
d69b2b |
+import re
|
|
|
d69b2b |
from itertools import permutations
|
|
|
d69b2b |
from lib389._constants import *
|
|
|
d69b2b |
from lib389.idm.nscontainer import nsContainers
|
|
|
d69b2b |
-from lib389.idm.user import UserAccounts
|
|
|
d69b2b |
+from lib389.idm.user import UserAccounts, UserAccount
|
|
|
d69b2b |
from lib389.idm.group import Groups
|
|
|
d69b2b |
from lib389.idm.organizationalunit import OrganizationalUnits
|
|
|
d69b2b |
from lib389.replica import ReplicationManager
|
|
|
d69b2b |
@@ -763,6 +764,177 @@ class TestTwoMasters:
|
|
|
d69b2b |
user_dns_m2 = [user.dn for user in test_users_m2.list()]
|
|
|
d69b2b |
assert set(user_dns_m1) == set(user_dns_m2)
|
|
|
d69b2b |
|
|
|
d69b2b |
+ def test_conflict_attribute_multi_valued(self, topology_m2, base_m2):
|
|
|
d69b2b |
+ """A RDN attribute being multi-valued, checks that after several operations
|
|
|
d69b2b |
+ MODRDN and MOD_REPL its RDN values are the same on both servers
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ :id: 225b3522-8ed7-4256-96f9-5fab9b7044a5
|
|
|
d69b2b |
+ :setup: Two master replication,
|
|
|
d69b2b |
+ audit log, error log for replica and access log for internal
|
|
|
d69b2b |
+ :steps:
|
|
|
d69b2b |
+ 1. Create a test entry uid=user_test_1000,...
|
|
|
d69b2b |
+ 2. Pause all replication agreements
|
|
|
d69b2b |
+ 3. On M1 rename it into uid=foo1,...
|
|
|
d69b2b |
+ 4. On M2 rename it into uid=foo2,...
|
|
|
d69b2b |
+ 5. On M1 MOD_REPL uid:foo1
|
|
|
d69b2b |
+ 6. Resume all replication agreements
|
|
|
d69b2b |
+ 7. Check that entry on M1 has uid=foo1, foo2
|
|
|
d69b2b |
+ 8. Check that entry on M2 has uid=foo1, foo2
|
|
|
d69b2b |
+ 9. Check that entry on M1 and M2 has the same uid values
|
|
|
d69b2b |
+ :expectedresults:
|
|
|
d69b2b |
+ 1. It should pass
|
|
|
d69b2b |
+ 2. It should pass
|
|
|
d69b2b |
+ 3. It should pass
|
|
|
d69b2b |
+ 4. It should pass
|
|
|
d69b2b |
+ 5. It should pass
|
|
|
d69b2b |
+ 6. It should pass
|
|
|
d69b2b |
+ 7. It should pass
|
|
|
d69b2b |
+ 8. It should pass
|
|
|
d69b2b |
+ 9. It should pass
|
|
|
d69b2b |
+ """
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ M1 = topology_m2.ms["master1"]
|
|
|
d69b2b |
+ M2 = topology_m2.ms["master2"]
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # add a test user
|
|
|
d69b2b |
+ test_users_m1 = UserAccounts(M1, base_m2.dn, rdn=None)
|
|
|
d69b2b |
+ user_1 = test_users_m1.create_test_user(uid=1000)
|
|
|
d69b2b |
+ test_users_m2 = UserAccount(M2, user_1.dn)
|
|
|
d69b2b |
+ # Waiting fo the user to be replicated
|
|
|
d69b2b |
+ for i in range(0,4):
|
|
|
d69b2b |
+ time.sleep(1)
|
|
|
d69b2b |
+ if test_users_m2.exists():
|
|
|
d69b2b |
+ break
|
|
|
d69b2b |
+ assert(test_users_m2.exists())
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # Stop replication agreements
|
|
|
d69b2b |
+ topology_m2.pause_all_replicas()
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # On M1 rename test entry in uid=foo1
|
|
|
d69b2b |
+ original_dn = user_1.dn
|
|
|
d69b2b |
+ user_1.rename('uid=foo1')
|
|
|
d69b2b |
+ time.sleep(1)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # On M2 rename test entry in uid=foo2
|
|
|
d69b2b |
+ M2.rename_s(original_dn, 'uid=foo2')
|
|
|
d69b2b |
+ time.sleep(2)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # on M1 MOD_REPL uid into foo1
|
|
|
d69b2b |
+ user_1.replace('uid', 'foo1')
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # resume replication agreements
|
|
|
d69b2b |
+ topology_m2.resume_all_replicas()
|
|
|
d69b2b |
+ time.sleep(5)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # check that on M1, the entry 'uid' has two values 'foo1' and 'foo2'
|
|
|
d69b2b |
+ final_dn = re.sub('^.*1000,', 'uid=foo2,', original_dn)
|
|
|
d69b2b |
+ final_user_m1 = UserAccount(M1, final_dn)
|
|
|
d69b2b |
+ for val in final_user_m1.get_attr_vals_utf8('uid'):
|
|
|
d69b2b |
+ log.info("Check %s is on M1" % val)
|
|
|
d69b2b |
+ assert(val in ['foo1', 'foo2'])
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # check that on M2, the entry 'uid' has two values 'foo1' and 'foo2'
|
|
|
d69b2b |
+ final_user_m2 = UserAccount(M2, final_dn)
|
|
|
d69b2b |
+ for val in final_user_m2.get_attr_vals_utf8('uid'):
|
|
|
d69b2b |
+ log.info("Check %s is on M1" % val)
|
|
|
d69b2b |
+ assert(val in ['foo1', 'foo2'])
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # check that the entry have the same uid values
|
|
|
d69b2b |
+ for val in final_user_m1.get_attr_vals_utf8('uid'):
|
|
|
d69b2b |
+ log.info("Check M1.uid %s is also on M2" % val)
|
|
|
d69b2b |
+ assert(val in final_user_m2.get_attr_vals_utf8('uid'))
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ for val in final_user_m2.get_attr_vals_utf8('uid'):
|
|
|
d69b2b |
+ log.info("Check M2.uid %s is also on M1" % val)
|
|
|
d69b2b |
+ assert(val in final_user_m1.get_attr_vals_utf8('uid'))
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ def test_conflict_attribute_single_valued(self, topology_m2, base_m2):
|
|
|
d69b2b |
+ """A RDN attribute being signle-valued, checks that after several operations
|
|
|
d69b2b |
+ MODRDN and MOD_REPL its RDN values are the same on both servers
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ :id: c38ae613-5d1e-47cf-b051-c7284e64b817
|
|
|
d69b2b |
+ :setup: Two master replication, test container for entries, enable plugin logging,
|
|
|
d69b2b |
+ audit log, error log for replica and access log for internal
|
|
|
d69b2b |
+ :steps:
|
|
|
d69b2b |
+ 1. Create a test entry uid=user_test_1000,...
|
|
|
d69b2b |
+ 2. Pause all replication agreements
|
|
|
d69b2b |
+ 3. On M1 rename it into employeenumber=foo1,...
|
|
|
d69b2b |
+ 4. On M2 rename it into employeenumber=foo2,...
|
|
|
d69b2b |
+ 5. On M1 MOD_REPL employeenumber:foo1
|
|
|
d69b2b |
+ 6. Resume all replication agreements
|
|
|
d69b2b |
+ 7. Check that entry on M1 has employeenumber=foo1
|
|
|
d69b2b |
+ 8. Check that entry on M2 has employeenumber=foo1
|
|
|
d69b2b |
+ 9. Check that entry on M1 and M2 has the same employeenumber values
|
|
|
d69b2b |
+ :expectedresults:
|
|
|
d69b2b |
+ 1. It should pass
|
|
|
d69b2b |
+ 2. It should pass
|
|
|
d69b2b |
+ 3. It should pass
|
|
|
d69b2b |
+ 4. It should pass
|
|
|
d69b2b |
+ 5. It should pass
|
|
|
d69b2b |
+ 6. It should pass
|
|
|
d69b2b |
+ 7. It should pass
|
|
|
d69b2b |
+ 8. It should pass
|
|
|
d69b2b |
+ 9. It should pass
|
|
|
d69b2b |
+ """
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ M1 = topology_m2.ms["master1"]
|
|
|
d69b2b |
+ M2 = topology_m2.ms["master2"]
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # add a test user with a dummy 'uid' extra value because modrdn removes
|
|
|
d69b2b |
+ # uid that conflict with 'account' objectclass
|
|
|
d69b2b |
+ test_users_m1 = UserAccounts(M1, base_m2.dn, rdn=None)
|
|
|
d69b2b |
+ user_1 = test_users_m1.create_test_user(uid=1000)
|
|
|
d69b2b |
+ user_1.add('objectclass', 'extensibleobject')
|
|
|
d69b2b |
+ user_1.add('uid', 'dummy')
|
|
|
d69b2b |
+ test_users_m2 = UserAccount(M2, user_1.dn)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # Waiting fo the user to be replicated
|
|
|
d69b2b |
+ for i in range(0,4):
|
|
|
d69b2b |
+ time.sleep(1)
|
|
|
d69b2b |
+ if test_users_m2.exists():
|
|
|
d69b2b |
+ break
|
|
|
d69b2b |
+ assert(test_users_m2.exists())
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # Stop replication agreements
|
|
|
d69b2b |
+ topology_m2.pause_all_replicas()
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # On M1 rename test entry in employeenumber=foo1
|
|
|
d69b2b |
+ original_dn = user_1.dn
|
|
|
d69b2b |
+ user_1.rename('employeenumber=foo1')
|
|
|
d69b2b |
+ time.sleep(1)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # On M2 rename test entry in employeenumber=foo2
|
|
|
d69b2b |
+ M2.rename_s(original_dn, 'employeenumber=foo2')
|
|
|
d69b2b |
+ time.sleep(2)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # on M1 MOD_REPL uid into foo1
|
|
|
d69b2b |
+ user_1.replace('employeenumber', 'foo1')
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # resume replication agreements
|
|
|
d69b2b |
+ topology_m2.resume_all_replicas()
|
|
|
d69b2b |
+ time.sleep(5)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # check that on M1, the entry 'employeenumber' has value 'foo1'
|
|
|
d69b2b |
+ final_dn = re.sub('^.*1000,', 'employeenumber=foo2,', original_dn)
|
|
|
d69b2b |
+ final_user_m1 = UserAccount(M1, final_dn)
|
|
|
d69b2b |
+ for val in final_user_m1.get_attr_vals_utf8('employeenumber'):
|
|
|
d69b2b |
+ log.info("Check %s is on M1" % val)
|
|
|
d69b2b |
+ assert(val in ['foo1'])
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # check that on M2, the entry 'employeenumber' has values 'foo1'
|
|
|
d69b2b |
+ final_user_m2 = UserAccount(M2, final_dn)
|
|
|
d69b2b |
+ for val in final_user_m2.get_attr_vals_utf8('employeenumber'):
|
|
|
d69b2b |
+ log.info("Check %s is on M2" % val)
|
|
|
d69b2b |
+ assert(val in ['foo1'])
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ # check that the entry have the same uid values
|
|
|
d69b2b |
+ for val in final_user_m1.get_attr_vals_utf8('employeenumber'):
|
|
|
d69b2b |
+ log.info("Check M1.uid %s is also on M2" % val)
|
|
|
d69b2b |
+ assert(val in final_user_m2.get_attr_vals_utf8('employeenumber'))
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ for val in final_user_m2.get_attr_vals_utf8('employeenumber'):
|
|
|
d69b2b |
+ log.info("Check M2.uid %s is also on M1" % val)
|
|
|
d69b2b |
+ assert(val in final_user_m1.get_attr_vals_utf8('employeenumber'))
|
|
|
d69b2b |
|
|
|
d69b2b |
class TestThreeMasters:
|
|
|
d69b2b |
def test_nested_entries(self, topology_m3, base_m3):
|
|
|
d69b2b |
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
|
|
d69b2b |
index e9d7e87e3..a507f3c31 100644
|
|
|
d69b2b |
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
|
|
d69b2b |
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
|
|
d69b2b |
@@ -213,6 +213,112 @@ error:
|
|
|
d69b2b |
return retval;
|
|
|
d69b2b |
}
|
|
|
d69b2b |
|
|
|
d69b2b |
+int32_t
|
|
|
d69b2b |
+entry_get_rdn_mods(Slapi_PBlock *pb, Slapi_Entry *entry, CSN *csn, int repl_op, Slapi_Mods **smods_ret)
|
|
|
d69b2b |
+{
|
|
|
d69b2b |
+ unsigned long op_type = SLAPI_OPERATION_NONE;
|
|
|
d69b2b |
+ char *new_rdn = NULL;
|
|
|
d69b2b |
+ char **dns = NULL;
|
|
|
d69b2b |
+ char **rdns = NULL;
|
|
|
d69b2b |
+ Slapi_Mods *smods = NULL;
|
|
|
d69b2b |
+ char *type = NULL;
|
|
|
d69b2b |
+ struct berval *bvp[2] = {0};
|
|
|
d69b2b |
+ struct berval bv;
|
|
|
d69b2b |
+ Slapi_Attr *attr = NULL;
|
|
|
d69b2b |
+ const char *entry_dn = NULL;
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ *smods_ret = NULL;
|
|
|
d69b2b |
+ entry_dn = slapi_entry_get_dn_const(entry);
|
|
|
d69b2b |
+ /* Do not bother to check that RDN is present, no one rename RUV or change its nsuniqueid */
|
|
|
d69b2b |
+ if (strcasestr(entry_dn, RUV_STORAGE_ENTRY_UNIQUEID)) {
|
|
|
d69b2b |
+ return 0;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ /* First get the RDNs of the operation */
|
|
|
d69b2b |
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
|
|
|
d69b2b |
+ switch (op_type) {
|
|
|
d69b2b |
+ case SLAPI_OPERATION_MODIFY:
|
|
|
d69b2b |
+ dns = slapi_ldap_explode_dn(entry_dn, 0);
|
|
|
d69b2b |
+ if (dns == NULL) {
|
|
|
d69b2b |
+ slapi_log_err(SLAPI_LOG_ERR, "entry_get_rdn_mods",
|
|
|
d69b2b |
+ "Fails to split DN \"%s\" into components\n", entry_dn);
|
|
|
d69b2b |
+ return -1;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ rdns = slapi_ldap_explode_rdn(dns[0], 0);
|
|
|
d69b2b |
+ slapi_ldap_value_free(dns);
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ break;
|
|
|
d69b2b |
+ case SLAPI_OPERATION_MODRDN:
|
|
|
d69b2b |
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &new_rdn);
|
|
|
d69b2b |
+ rdns = slapi_ldap_explode_rdn(new_rdn, 0);
|
|
|
d69b2b |
+ break;
|
|
|
d69b2b |
+ default:
|
|
|
d69b2b |
+ break;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ if (rdns == NULL || rdns[0] == NULL) {
|
|
|
d69b2b |
+ slapi_log_err(SLAPI_LOG_ERR, "entry_get_rdn_mods",
|
|
|
d69b2b |
+ "Fails to split RDN \"%s\" into components\n", slapi_entry_get_dn_const(entry));
|
|
|
d69b2b |
+ return -1;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ /* Update the entry to add RDNs values if they are missing */
|
|
|
d69b2b |
+ smods = slapi_mods_new();
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ bvp[0] = &bv;
|
|
|
d69b2b |
+ bvp[1] = NULL;
|
|
|
d69b2b |
+ for (size_t rdns_count = 0; rdns[rdns_count]; rdns_count++) {
|
|
|
d69b2b |
+ Slapi_Value *value;
|
|
|
d69b2b |
+ attr = NULL;
|
|
|
d69b2b |
+ slapi_rdn2typeval(rdns[rdns_count], &type, &bv;;
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ /* Check if the RDN value exists */
|
|
|
d69b2b |
+ if ((slapi_entry_attr_find(entry, type, &attr) != 0) ||
|
|
|
d69b2b |
+ (slapi_attr_value_find(attr, &bv))) {
|
|
|
d69b2b |
+ const CSN *csn_rdn_add;
|
|
|
d69b2b |
+ const CSN *adcsn = attr_get_deletion_csn(attr);
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ /* It is missing => adds it */
|
|
|
d69b2b |
+ if (slapi_attr_flag_is_set(attr, SLAPI_ATTR_FLAG_SINGLE)) {
|
|
|
d69b2b |
+ if (csn_compare(adcsn, csn) >= 0) {
|
|
|
d69b2b |
+ /* this is a single valued attribute and the current value
|
|
|
d69b2b |
+ * (that is different from RDN value) is more recent than
|
|
|
d69b2b |
+ * the RDN value we want to apply.
|
|
|
d69b2b |
+ * Keep the current value and add a conflict flag
|
|
|
d69b2b |
+ */
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ type = ATTR_NSDS5_REPLCONFLICT;
|
|
|
d69b2b |
+ bv.bv_val = "RDN value may be missing because it is single-valued";
|
|
|
d69b2b |
+ bv.bv_len = strlen(bv.bv_val);
|
|
|
d69b2b |
+ slapi_entry_add_string(entry, type, bv.bv_val);
|
|
|
d69b2b |
+ slapi_mods_add_modbvps(smods, LDAP_MOD_ADD, type, bvp);
|
|
|
d69b2b |
+ continue;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ /* if a RDN value needs to be forced, make sure it csn is ahead */
|
|
|
d69b2b |
+ slapi_mods_add_modbvps(smods, LDAP_MOD_ADD, type, bvp);
|
|
|
d69b2b |
+ csn_rdn_add = csn_max(adcsn, csn);
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ if (entry_apply_mods_wsi(entry, smods, csn_rdn_add, repl_op)) {
|
|
|
d69b2b |
+ slapi_log_err(SLAPI_LOG_ERR, "entry_get_rdn_mods",
|
|
|
d69b2b |
+ "Fails to set \"%s\" in \"%s\"\n", type, slapi_entry_get_dn_const(entry));
|
|
|
d69b2b |
+ slapi_ldap_value_free(rdns);
|
|
|
d69b2b |
+ slapi_mods_free(&smods);
|
|
|
d69b2b |
+ return -1;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ /* Make the RDN value a distinguished value */
|
|
|
d69b2b |
+ attr_value_find_wsi(attr, &bv, &value);
|
|
|
d69b2b |
+ value_update_csn(value, CSN_TYPE_VALUE_DISTINGUISHED, csn_rdn_add);
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ slapi_ldap_value_free(rdns);
|
|
|
d69b2b |
+ if (smods->num_mods == 0) {
|
|
|
d69b2b |
+ /* smods_ret already NULL, just free the useless smods */
|
|
|
d69b2b |
+ slapi_mods_free(&smods);
|
|
|
d69b2b |
+ } else {
|
|
|
d69b2b |
+ *smods_ret = smods;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ return 0;
|
|
|
d69b2b |
+}
|
|
|
d69b2b |
/**
|
|
|
d69b2b |
Apply the mods to the ec entry. Check for syntax, schema problems.
|
|
|
d69b2b |
Check for abandon.
|
|
|
d69b2b |
@@ -269,6 +375,8 @@ modify_apply_check_expand(
|
|
|
d69b2b |
goto done;
|
|
|
d69b2b |
}
|
|
|
d69b2b |
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+
|
|
|
d69b2b |
/*
|
|
|
d69b2b |
* If the objectClass attribute type was modified in any way, expand
|
|
|
d69b2b |
* the objectClass values to reflect the inheritance hierarchy.
|
|
|
d69b2b |
@@ -414,6 +522,7 @@ ldbm_back_modify(Slapi_PBlock *pb)
|
|
|
d69b2b |
int result_sent = 0;
|
|
|
d69b2b |
int32_t parent_op = 0;
|
|
|
d69b2b |
struct timespec parent_time;
|
|
|
d69b2b |
+ Slapi_Mods *smods_add_rdn = NULL;
|
|
|
d69b2b |
|
|
|
d69b2b |
slapi_pblock_get(pb, SLAPI_BACKEND, &be);
|
|
|
d69b2b |
slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &li;;
|
|
|
d69b2b |
@@ -731,6 +840,15 @@ ldbm_back_modify(Slapi_PBlock *pb)
|
|
|
d69b2b |
}
|
|
|
d69b2b |
} /* else if new_mod_count == mod_count then betxnpremod plugin did nothing */
|
|
|
d69b2b |
|
|
|
d69b2b |
+ /* time to check if applying a replicated operation removed
|
|
|
d69b2b |
+ * the RDN value from the entry. Assuming that only replicated update
|
|
|
d69b2b |
+ * can lead to that bad result
|
|
|
d69b2b |
+ */
|
|
|
d69b2b |
+ if (entry_get_rdn_mods(pb, ec->ep_entry, opcsn, repl_op, &smods_add_rdn)) {
|
|
|
d69b2b |
+ goto error_return;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+
|
|
|
d69b2b |
/*
|
|
|
d69b2b |
* Update the ID to Entry index.
|
|
|
d69b2b |
* Note that id2entry_add replaces the entry, so the Entry ID
|
|
|
d69b2b |
@@ -764,6 +882,23 @@ ldbm_back_modify(Slapi_PBlock *pb)
|
|
|
d69b2b |
MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
|
|
|
d69b2b |
goto error_return;
|
|
|
d69b2b |
}
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ if (smods_add_rdn && slapi_mods_get_num_mods(smods_add_rdn) > 0) {
|
|
|
d69b2b |
+ retval = index_add_mods(be, (LDAPMod **) slapi_mods_get_ldapmods_byref(smods_add_rdn), e, ec, &txn);
|
|
|
d69b2b |
+ if (DB_LOCK_DEADLOCK == retval) {
|
|
|
d69b2b |
+ /* Abort and re-try */
|
|
|
d69b2b |
+ slapi_mods_free(&smods_add_rdn);
|
|
|
d69b2b |
+ continue;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ if (retval != 0) {
|
|
|
d69b2b |
+ slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
|
|
|
d69b2b |
+ "index_add_mods (rdn) failed, err=%d %s\n",
|
|
|
d69b2b |
+ retval, (msg = dblayer_strerror(retval)) ? msg : "");
|
|
|
d69b2b |
+ MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
|
|
|
d69b2b |
+ slapi_mods_free(&smods_add_rdn);
|
|
|
d69b2b |
+ goto error_return;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
/*
|
|
|
d69b2b |
* Remove the old entry from the Virtual List View indexes.
|
|
|
d69b2b |
* Add the new entry to the Virtual List View indexes.
|
|
|
d69b2b |
@@ -978,6 +1113,7 @@ error_return:
|
|
|
d69b2b |
|
|
|
d69b2b |
common_return:
|
|
|
d69b2b |
slapi_mods_done(&smods);
|
|
|
d69b2b |
+ slapi_mods_free(&smods_add_rdn);
|
|
|
d69b2b |
|
|
|
d69b2b |
if (inst) {
|
|
|
d69b2b |
if (ec_locked || cache_is_in_cache(&inst->inst_cache, ec)) {
|
|
|
d69b2b |
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
|
|
d69b2b |
index fde83c99f..e97b7a5f6 100644
|
|
|
d69b2b |
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
|
|
d69b2b |
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
|
|
d69b2b |
@@ -21,7 +21,7 @@ static void moddn_unlock_and_return_entry(backend *be, struct backentry **target
|
|
|
d69b2b |
static int moddn_newrdn_mods(Slapi_PBlock *pb, const char *olddn, struct backentry *ec, Slapi_Mods *smods_wsi, int is_repl_op);
|
|
|
d69b2b |
static IDList *moddn_get_children(back_txn *ptxn, Slapi_PBlock *pb, backend *be, struct backentry *parententry, Slapi_DN *parentdn, struct backentry ***child_entries, struct backdn ***child_dns, int is_resurect_operation);
|
|
|
d69b2b |
static int moddn_rename_children(back_txn *ptxn, Slapi_PBlock *pb, backend *be, IDList *children, Slapi_DN *dn_parentdn, Slapi_DN *dn_newsuperiordn, struct backentry *child_entries[]);
|
|
|
d69b2b |
-static int modrdn_rename_entry_update_indexes(back_txn *ptxn, Slapi_PBlock *pb, struct ldbminfo *li, struct backentry *e, struct backentry **ec, Slapi_Mods *smods1, Slapi_Mods *smods2, Slapi_Mods *smods3);
|
|
|
d69b2b |
+static int modrdn_rename_entry_update_indexes(back_txn *ptxn, Slapi_PBlock *pb, struct ldbminfo *li, struct backentry *e, struct backentry **ec, Slapi_Mods *smods1, Slapi_Mods *smods2, Slapi_Mods *smods3, Slapi_Mods *smods4);
|
|
|
d69b2b |
static void mods_remove_nsuniqueid(Slapi_Mods *smods);
|
|
|
d69b2b |
|
|
|
d69b2b |
#define MOD_SET_ERROR(rc, error, count) \
|
|
|
d69b2b |
@@ -100,6 +100,7 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
|
|
d69b2b |
Connection *pb_conn = NULL;
|
|
|
d69b2b |
int32_t parent_op = 0;
|
|
|
d69b2b |
struct timespec parent_time;
|
|
|
d69b2b |
+ Slapi_Mods *smods_add_rdn = NULL;
|
|
|
d69b2b |
|
|
|
d69b2b |
if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {
|
|
|
d69b2b |
conn_id = 0; /* connection is NULL */
|
|
|
d69b2b |
@@ -842,6 +843,15 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
|
|
d69b2b |
goto error_return;
|
|
|
d69b2b |
}
|
|
|
d69b2b |
}
|
|
|
d69b2b |
+
|
|
|
d69b2b |
+ /* time to check if applying a replicated operation removed
|
|
|
d69b2b |
+ * the RDN value from the entry. Assuming that only replicated update
|
|
|
d69b2b |
+ * can lead to that bad result
|
|
|
d69b2b |
+ */
|
|
|
d69b2b |
+ if (entry_get_rdn_mods(pb, ec->ep_entry, opcsn, is_replicated_operation, &smods_add_rdn)) {
|
|
|
d69b2b |
+ goto error_return;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+
|
|
|
d69b2b |
/* check that the entry still obeys the schema */
|
|
|
d69b2b |
if (slapi_entry_schema_check(pb, ec->ep_entry) != 0) {
|
|
|
d69b2b |
ldap_result_code = LDAP_OBJECT_CLASS_VIOLATION;
|
|
|
d69b2b |
@@ -1003,7 +1013,7 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
|
|
d69b2b |
/*
|
|
|
d69b2b |
* Update the indexes for the entry.
|
|
|
d69b2b |
*/
|
|
|
d69b2b |
- retval = modrdn_rename_entry_update_indexes(&txn, pb, li, e, &ec, &smods_generated, &smods_generated_wsi, &smods_operation_wsi);
|
|
|
d69b2b |
+ retval = modrdn_rename_entry_update_indexes(&txn, pb, li, e, &ec, &smods_generated, &smods_generated_wsi, &smods_operation_wsi, smods_add_rdn);
|
|
|
d69b2b |
if (DB_LOCK_DEADLOCK == retval) {
|
|
|
d69b2b |
/* Retry txn */
|
|
|
d69b2b |
continue;
|
|
|
d69b2b |
@@ -1497,6 +1507,7 @@ common_return:
|
|
|
d69b2b |
slapi_mods_done(&smods_operation_wsi);
|
|
|
d69b2b |
slapi_mods_done(&smods_generated);
|
|
|
d69b2b |
slapi_mods_done(&smods_generated_wsi);
|
|
|
d69b2b |
+ slapi_mods_free(&smods_add_rdn);
|
|
|
d69b2b |
slapi_ch_free((void **)&child_entries);
|
|
|
d69b2b |
slapi_ch_free((void **)&child_dns);
|
|
|
d69b2b |
if (ldap_result_matcheddn && 0 != strcmp(ldap_result_matcheddn, "NULL"))
|
|
|
d69b2b |
@@ -1778,7 +1789,7 @@ mods_remove_nsuniqueid(Slapi_Mods *smods)
|
|
|
d69b2b |
* mods contains the list of attribute change made.
|
|
|
d69b2b |
*/
|
|
|
d69b2b |
static int
|
|
|
d69b2b |
-modrdn_rename_entry_update_indexes(back_txn *ptxn, Slapi_PBlock *pb, struct ldbminfo *li __attribute__((unused)), struct backentry *e, struct backentry **ec, Slapi_Mods *smods1, Slapi_Mods *smods2, Slapi_Mods *smods3)
|
|
|
d69b2b |
+modrdn_rename_entry_update_indexes(back_txn *ptxn, Slapi_PBlock *pb, struct ldbminfo *li __attribute__((unused)), struct backentry *e, struct backentry **ec, Slapi_Mods *smods1, Slapi_Mods *smods2, Slapi_Mods *smods3, Slapi_Mods *smods4)
|
|
|
d69b2b |
{
|
|
|
d69b2b |
backend *be;
|
|
|
d69b2b |
ldbm_instance *inst;
|
|
|
d69b2b |
@@ -1874,6 +1885,24 @@ modrdn_rename_entry_update_indexes(back_txn *ptxn, Slapi_PBlock *pb, struct ldbm
|
|
|
d69b2b |
goto error_return;
|
|
|
d69b2b |
}
|
|
|
d69b2b |
}
|
|
|
d69b2b |
+ if (smods4 != NULL && slapi_mods_get_num_mods(smods4) > 0) {
|
|
|
d69b2b |
+ /*
|
|
|
d69b2b |
+ * update the indexes: lastmod, rdn, etc.
|
|
|
d69b2b |
+ */
|
|
|
d69b2b |
+ retval = index_add_mods(be, slapi_mods_get_ldapmods_byref(smods4), e, *ec, ptxn);
|
|
|
d69b2b |
+ if (DB_LOCK_DEADLOCK == retval) {
|
|
|
d69b2b |
+ /* Retry txn */
|
|
|
d69b2b |
+ slapi_log_err(SLAPI_LOG_BACKLDBM, "modrdn_rename_entry_update_indexes",
|
|
|
d69b2b |
+ "index_add_mods4 deadlock\n");
|
|
|
d69b2b |
+ goto error_return;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ if (retval != 0) {
|
|
|
d69b2b |
+ slapi_log_err(SLAPI_LOG_TRACE, "modrdn_rename_entry_update_indexes",
|
|
|
d69b2b |
+ "index_add_mods 4 failed, err=%d %s\n",
|
|
|
d69b2b |
+ retval, (msg = dblayer_strerror(retval)) ? msg : "");
|
|
|
d69b2b |
+ goto error_return;
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
+ }
|
|
|
d69b2b |
/*
|
|
|
d69b2b |
* Remove the old entry from the Virtual List View indexes.
|
|
|
d69b2b |
* Add the new entry to the Virtual List View indexes.
|
|
|
d69b2b |
@@ -1991,7 +2020,7 @@ moddn_rename_child_entry(
|
|
|
d69b2b |
* Update all the indexes.
|
|
|
d69b2b |
*/
|
|
|
d69b2b |
retval = modrdn_rename_entry_update_indexes(ptxn, pb, li, e, ec,
|
|
|
d69b2b |
- smodsp, NULL, NULL);
|
|
|
d69b2b |
+ smodsp, NULL, NULL, NULL);
|
|
|
d69b2b |
/* JCMREPL - Should the children get updated modifiersname and lastmodifiedtime? */
|
|
|
d69b2b |
slapi_mods_done(&smods);
|
|
|
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 4d2524fd9..e2f1100ed 100644
|
|
|
d69b2b |
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
|
|
d69b2b |
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
|
|
d69b2b |
@@ -324,6 +324,7 @@ int get_parent_rdn(DB *db, ID parentid, Slapi_RDN *srdn);
|
|
|
d69b2b |
/*
|
|
|
d69b2b |
* modify.c
|
|
|
d69b2b |
*/
|
|
|
d69b2b |
+int32_t entry_get_rdn_mods(Slapi_PBlock *pb, Slapi_Entry *entry, CSN *csn, int repl_op, Slapi_Mods **smods_ret);
|
|
|
d69b2b |
int modify_update_all(backend *be, Slapi_PBlock *pb, modify_context *mc, back_txn *txn);
|
|
|
d69b2b |
void modify_init(modify_context *mc, struct backentry *old_entry);
|
|
|
d69b2b |
int modify_apply_mods(modify_context *mc, Slapi_Mods *smods);
|
|
|
d69b2b |
--
|
|
|
d69b2b |
2.26.2
|
|
|
d69b2b |
|