Bump version to 2.9.0
- Resolves: RHEL-165978 - Replication halt caused by an incorrect setting of "nsslapd-changelogmaxage" - Resolves: RHEL-166003 - Crash in replica_config_add when manually configuring a replica with an incorrect nsds5ReplicaRoot. - Resolves: RHEL-166004 - Possible memory leak when using the Retro Changelog plugin. - Resolves: RHEL-168908 - ns-slapd fails to shutdown when deferred memberof update is in progress - Resolves: RHEL-168964 - Web console doesn't show the sub suffix of ou=foo,ou=people,dc=example,dc=com. [rhel-9] - Resolves: RHEL-168972 - Replica installation is failing with message MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED [rhel-9] - Resolves: RHEL-169530 - Rebase 389-ds-base to 2.9.x - Resolves: RHEL-170270 - DS 12 does not handle escape char in bind user [rhel-9] - Resolves: RHEL-170275 - dnaSharedConfig: "dnaPortNum: 0" [rhel-9] - Resolves: RHEL-170280 - Memory leaks in syncrepl plugin during persistent search operations [rhel-9] - Resolves: RHEL-170287 - access log - suspicious wtime optime negative and large values in internal op [rhel-9] - Resolves: RHEL-170477 - An online reinitialization with LMDB is terminating the receiving server [rhel-9] - Resolves: RHEL-170480 - dsctl healthcheck DSMOLE0001 inaccurate recommendations when there is more than 1 LDAP backend [rhel-9] - Resolves: RHEL-170651 - passwordbadwords attribute of the local password policy is not functioning [rhel-9] - Resolves: RHEL-170732 - Memory leaks in IPA context on server restart [rhel-9] - Resolves: RHEL-174525 - [RFE] Add OS-level thread names to all server threads [rhel-9] - Resolves: RHEL-180717 - Online export is failing when using the option "-s" [rhel-9]
This commit is contained in:
parent
4c921f32ff
commit
c824da947d
@ -1,80 +0,0 @@
|
||||
From 94a52cd03a6fcccf78a4d7e5c4adc65fb777fcfb Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 10 Nov 2025 13:20:28 +0100
|
||||
Subject: [PATCH] Issue 7049 - RetroCL plugin generates invalid LDIF
|
||||
|
||||
Bug Description:
|
||||
When a replicated modification marked with LDAP_MOD_IGNORE is logged,
|
||||
`changes` attribute contains invalid LDIF:
|
||||
|
||||
```
|
||||
replace: modifiersName
|
||||
modifiersName: cn=MemberOf Plugin,cn=plugins,cn=config
|
||||
-
|
||||
modifyTimestamp: 20250903092211Z
|
||||
-
|
||||
```
|
||||
Line `replace: modifyTimestamp` is missing.
|
||||
|
||||
A similar issue is present in audit log:
|
||||
```
|
||||
time: 20251031064114
|
||||
dn: ou=tuser,dc=example,dc=com
|
||||
result: 0
|
||||
changetype: modify
|
||||
add: objectClass
|
||||
objectClass: nsMemberOf
|
||||
-
|
||||
replace: modifiersName
|
||||
modifiersName: cn=MemberOf Plugin,cn=plugins,cn=config
|
||||
-
|
||||
-
|
||||
```
|
||||
Dash separator is logged, while the operation is not.
|
||||
This issue is not present wheh JSON format is used.
|
||||
|
||||
Fix Description:
|
||||
* retrocl_po.c: add a default case to skip the entire modification if it
|
||||
has LDAP_MOD_IGNORE flag.
|
||||
* auditlog.c: write the dash separator only if operation type is not
|
||||
LDAP_MOD_IGNORE
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7049
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
ldap/servers/plugins/retrocl/retrocl_po.c | 3 +++
|
||||
ldap/servers/slapd/auditlog.c | 2 +-
|
||||
2 files changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/retrocl/retrocl_po.c b/ldap/servers/plugins/retrocl/retrocl_po.c
|
||||
index e447ccdfb..277711dd3 100644
|
||||
--- a/ldap/servers/plugins/retrocl/retrocl_po.c
|
||||
+++ b/ldap/servers/plugins/retrocl/retrocl_po.c
|
||||
@@ -99,6 +99,9 @@ make_changes_string(LDAPMod **ldm, const char **includeattrs)
|
||||
addlenstr(l, ldm[i]->mod_type);
|
||||
addlenstr(l, "\n");
|
||||
break;
|
||||
+ default:
|
||||
+ /* LDAP_MOD_IGNORE or unknown - skip this mod entirely */
|
||||
+ continue;
|
||||
}
|
||||
for (j = 0; ldm[i]->mod_bvalues != NULL &&
|
||||
ldm[i]->mod_bvalues[j] != NULL;
|
||||
diff --git a/ldap/servers/slapd/auditlog.c b/ldap/servers/slapd/auditlog.c
|
||||
index 32fcea38a..e2db40722 100644
|
||||
--- a/ldap/servers/slapd/auditlog.c
|
||||
+++ b/ldap/servers/slapd/auditlog.c
|
||||
@@ -853,8 +853,8 @@ write_audit_file(
|
||||
slapi_ch_free((void **)&buf);
|
||||
}
|
||||
}
|
||||
+ addlenstr(l, "-\n");
|
||||
}
|
||||
- addlenstr(l, "-\n");
|
||||
}
|
||||
break;
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
From 71cda087462c7cef744555676603c7c5fde3ca55 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 3 Jun 2026 17:52:09 -0400
|
||||
Subject: [PATCH 1/8] Issue 7554 - deref plugin null pointer dereference if
|
||||
ber_init fails
|
||||
|
||||
Description:
|
||||
|
||||
**CWE**: CWE-476 (NULL Pointer Dereference)
|
||||
|
||||
A flaw in the 389 Directory Server's dereference control plugin allows an
|
||||
unauthenticated attacker to crash the LDAP server when the system is under
|
||||
memory pressure(OOM). The deref plugin, enabled by default, fails to check for
|
||||
a memory allocation failure before using the result, causing the server
|
||||
process to terminate.
|
||||
|
||||
CI test 'test_deref_and_access_control' already covers this fix.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7554
|
||||
|
||||
Reviewed by: tbordaz & progier(Thanks!!)
|
||||
---
|
||||
ldap/servers/plugins/deref/deref.c | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/ldap/servers/plugins/deref/deref.c b/ldap/servers/plugins/deref/deref.c
|
||||
index fc1c10f71..fc157f6d6 100644
|
||||
--- a/ldap/servers/plugins/deref/deref.c
|
||||
+++ b/ldap/servers/plugins/deref/deref.c
|
||||
@@ -357,6 +357,12 @@ deref_parse_ctrl_value(DerefSpecList *speclist, const struct berval *ctrlbv, int
|
||||
}
|
||||
|
||||
ber = ber_init((struct berval *)ctrlbv);
|
||||
+ if (!ber) {
|
||||
+ *ldapcode = LDAP_UNWILLING_TO_PERFORM;
|
||||
+ *ldaperrtext = "Deref control parsing failed to initialize BER element";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
for (tag = ber_first_element(ber, &len, &last);
|
||||
(tag != LBER_ERROR) && (tag != LBER_END_OF_SEQORSET);
|
||||
tag = ber_next_element(ber, &len, last)) {
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
From d42d7eefa937d4f12e21929210786274f9c8e849 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Thu, 4 Jun 2026 19:21:30 -0700
|
||||
Subject: [PATCH 2/8] Issue 3555 - UI - Fix audit issue with npm -
|
||||
brace-expansion (#7556)
|
||||
|
||||
Description: Run npm audit fix to address the vulnerability
|
||||
in brace-expansion.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/3555
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7527
|
||||
|
||||
Reviewed by: jchapma (Thanks!)
|
||||
---
|
||||
src/cockpit/389-console/package-lock.json | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/cockpit/389-console/package-lock.json b/src/cockpit/389-console/package-lock.json
|
||||
index 6c8ccdbd8..1efed1c1d 100644
|
||||
--- a/src/cockpit/389-console/package-lock.json
|
||||
+++ b/src/cockpit/389-console/package-lock.json
|
||||
@@ -2377,9 +2377,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
- "version": "5.0.5",
|
||||
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
- "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
+ "version": "5.0.6",
|
||||
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,318 +0,0 @@
|
||||
From 67118939bd2b51e53f7284c2058a43744d1dba76 Mon Sep 17 00:00:00 2001
|
||||
From: tbordaz <tbordaz@redhat.com>
|
||||
Date: Wed, 7 Jan 2026 11:21:12 +0100
|
||||
Subject: [PATCH] Issue 7096 - During replication online total init the
|
||||
function idl_id_is_in_idlist is not scaling with large database (#7145)
|
||||
|
||||
Bug description:
|
||||
During a online total initialization, the supplier sorts
|
||||
the candidate list of entries so that the parents are sent before
|
||||
children entries.
|
||||
With large DB the ID array used for the sorting is not
|
||||
scaling. It takes so long to build the candidate list that
|
||||
the connection gets closed
|
||||
|
||||
Fix description:
|
||||
Instead of using an ID array, uses a list of ID ranges
|
||||
|
||||
fixes: #7096
|
||||
|
||||
Reviewed by: Mark Reynolds, Pierre Rogier (Thanks !!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/back-ldbm.h | 12 ++
|
||||
ldap/servers/slapd/back-ldbm/idl_common.c | 163 ++++++++++++++++++
|
||||
ldap/servers/slapd/back-ldbm/idl_new.c | 30 ++--
|
||||
.../servers/slapd/back-ldbm/proto-back-ldbm.h | 3 +
|
||||
4 files changed, 189 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
index 36aa432e0..5e4988782 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
@@ -281,6 +281,18 @@ typedef struct _idlist_set
|
||||
#define INDIRECT_BLOCK(idl) ((idl)->b_nids == INDBLOCK)
|
||||
#define IDL_NIDS(idl) (idl ? (idl)->b_nids : (NIDS)0)
|
||||
|
||||
+/*
|
||||
+ * used by the supplier during online total init
|
||||
+ * it stores the ranges of ID that are already present
|
||||
+ * in the candidate list ('parentid>=1')
|
||||
+ */
|
||||
+typedef struct IdRange {
|
||||
+ ID first;
|
||||
+ ID last;
|
||||
+ struct IdRange *next;
|
||||
+} IdRange_t;
|
||||
+
|
||||
+
|
||||
typedef size_t idl_iterator;
|
||||
|
||||
/* small hashtable implementation used in the entry cache -- the table
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_common.c b/ldap/servers/slapd/back-ldbm/idl_common.c
|
||||
index fcb0ece4b..fdc9b4e67 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_common.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_common.c
|
||||
@@ -172,6 +172,169 @@ idl_min(IDList *a, IDList *b)
|
||||
return (a->b_nids > b->b_nids ? b : a);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * This is a faster version of idl_id_is_in_idlist.
|
||||
+ * idl_id_is_in_idlist uses an array of ID so lookup is expensive
|
||||
+ * idl_id_is_in_idlist_ranges uses a list of ranges of ID lookup is faster
|
||||
+ * returns
|
||||
+ * 1: 'id' is present in idrange_list
|
||||
+ * 0: 'id' is not present in idrange_list
|
||||
+ */
|
||||
+int
|
||||
+idl_id_is_in_idlist_ranges(IDList *idl, IdRange_t *idrange_list, ID id)
|
||||
+{
|
||||
+ IdRange_t *range = idrange_list;
|
||||
+ int found = 0;
|
||||
+
|
||||
+ if (NULL == idl || NOID == id) {
|
||||
+ return 0; /* not in the list */
|
||||
+ }
|
||||
+ if (ALLIDS(idl)) {
|
||||
+ return 1; /* in the list */
|
||||
+ }
|
||||
+
|
||||
+ for(;range; range = range->next) {
|
||||
+ if (id > range->last) {
|
||||
+ /* check if it belongs to the next range */
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (id >= range->first) {
|
||||
+ /* It belongs to that range [first..last ] */
|
||||
+ found = 1;
|
||||
+ break;
|
||||
+ } else {
|
||||
+ /* this range is after id */
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+/* This function is used during the online total initialisation
|
||||
+ * (see next function)
|
||||
+ * It frees all ranges of ID in the list
|
||||
+ */
|
||||
+void idrange_free(IdRange_t **head)
|
||||
+{
|
||||
+ IdRange_t *curr, *sav;
|
||||
+
|
||||
+ if ((head == NULL) || (*head == NULL)) {
|
||||
+ return;
|
||||
+ }
|
||||
+ curr = *head;
|
||||
+ sav = NULL;
|
||||
+ for (; curr;) {
|
||||
+ sav = curr;
|
||||
+ curr = curr->next;
|
||||
+ slapi_ch_free((void *) &sav);
|
||||
+ }
|
||||
+ if (sav) {
|
||||
+ slapi_ch_free((void *) &sav);
|
||||
+ }
|
||||
+ *head = NULL;
|
||||
+}
|
||||
+
|
||||
+/* This function is used during the online total initialisation
|
||||
+ * Because a MODRDN can move entries under a parent that
|
||||
+ * has a higher ID we need to sort the IDList so that parents
|
||||
+ * are sent, to the consumer, before the children are sent.
|
||||
+ * The sorting with a simple IDlist does not scale instead
|
||||
+ * a list of IDs ranges is much faster.
|
||||
+ * In that list we only ADD/lookup ID.
|
||||
+ */
|
||||
+IdRange_t *idrange_add_id(IdRange_t **head, ID id)
|
||||
+{
|
||||
+ if (head == NULL) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "idrange_add_id",
|
||||
+ "Can not add ID %d in non defined list\n", id);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (*head == NULL) {
|
||||
+ /* This is the first range */
|
||||
+ IdRange_t *new_range = (IdRange_t *)slapi_ch_malloc(sizeof(IdRange_t));
|
||||
+ new_range->first = id;
|
||||
+ new_range->last = id;
|
||||
+ new_range->next = NULL;
|
||||
+ *head = new_range;
|
||||
+ return *head;
|
||||
+ }
|
||||
+
|
||||
+ IdRange_t *curr = *head, *prev = NULL;
|
||||
+
|
||||
+ /* First, find if id already falls within any existing range, or it is adjacent to any */
|
||||
+ while (curr) {
|
||||
+ if (id >= curr->first && id <= curr->last) {
|
||||
+ /* inside a range, nothing to do */
|
||||
+ return curr;
|
||||
+ }
|
||||
+
|
||||
+ if (id == curr->last + 1) {
|
||||
+ /* Extend this range upwards */
|
||||
+ curr->last = id;
|
||||
+
|
||||
+ /* Check for possible merge with next range */
|
||||
+ IdRange_t *next = curr->next;
|
||||
+ if (next && curr->last + 1 >= next->first) {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) merge current with next range [%d..%d]\n", id, curr->first, curr->last);
|
||||
+ curr->last = (next->last > curr->last) ? next->last : curr->last;
|
||||
+ curr->next = next->next;
|
||||
+ slapi_ch_free((void*) &next);
|
||||
+ } else {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) extend forward current range [%d..%d]\n", id, curr->first, curr->last);
|
||||
+ }
|
||||
+ return curr;
|
||||
+ }
|
||||
+
|
||||
+ if (id + 1 == curr->first) {
|
||||
+ /* Extend this range downwards */
|
||||
+ curr->first = id;
|
||||
+
|
||||
+ /* Check for possible merge with previous range */
|
||||
+ if (prev && prev->last + 1 >= curr->first) {
|
||||
+ prev->last = curr->last;
|
||||
+ prev->next = curr->next;
|
||||
+ slapi_ch_free((void *) &curr);
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) merge current with previous range [%d..%d]\n", id, prev->first, prev->last);
|
||||
+ return prev;
|
||||
+ } else {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) extend backward current range [%d..%d]\n", id, curr->first, curr->last);
|
||||
+ return curr;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* If id is before the current range, break so we can insert before */
|
||||
+ if (id < curr->first) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ prev = curr;
|
||||
+ curr = curr->next;
|
||||
+ }
|
||||
+ /* Need to insert a new standalone IdRange */
|
||||
+ IdRange_t *new_range = (IdRange_t *)slapi_ch_malloc(sizeof(IdRange_t));
|
||||
+ new_range->first = id;
|
||||
+ new_range->last = id;
|
||||
+ new_range->next = curr;
|
||||
+
|
||||
+ if (prev) {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) add new range [%d..%d]\n", id, new_range->first, new_range->last);
|
||||
+ prev->next = new_range;
|
||||
+ } else {
|
||||
+ /* Insert at head */
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) head range [%d..%d]\n", id, new_range->first, new_range->last);
|
||||
+ *head = new_range;
|
||||
+ }
|
||||
+ return *head;
|
||||
+}
|
||||
+
|
||||
+
|
||||
int
|
||||
idl_id_is_in_idlist(IDList *idl, ID id)
|
||||
{
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
index 5fbcaff2e..2d978353f 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
@@ -417,7 +417,6 @@ idl_new_range_fetch(
|
||||
{
|
||||
int ret = 0;
|
||||
int ret2 = 0;
|
||||
- int idl_rc = 0;
|
||||
dbi_cursor_t cursor = {0};
|
||||
IDList *idl = NULL;
|
||||
dbi_val_t cur_key = {0};
|
||||
@@ -436,6 +435,7 @@ idl_new_range_fetch(
|
||||
size_t leftoverlen = 32;
|
||||
size_t leftovercnt = 0;
|
||||
char *index_id = get_index_name(be, db, ai);
|
||||
+ IdRange_t *idrange_list = NULL;
|
||||
|
||||
|
||||
if (NULL == flag_err) {
|
||||
@@ -578,10 +578,12 @@ idl_new_range_fetch(
|
||||
* found entry is the one from the suffix
|
||||
*/
|
||||
suffix = key;
|
||||
- idl_rc = idl_append_extend(&idl, id);
|
||||
- } else if ((key == suffix) || idl_id_is_in_idlist(idl, key)) {
|
||||
+ idl_append_extend(&idl, id);
|
||||
+ idrange_add_id(&idrange_list, id);
|
||||
+ } else if ((key == suffix) || idl_id_is_in_idlist_ranges(idl, idrange_list, key)) {
|
||||
/* the parent is the suffix or already in idl. */
|
||||
- idl_rc = idl_append_extend(&idl, id);
|
||||
+ idl_append_extend(&idl, id);
|
||||
+ idrange_add_id(&idrange_list, id);
|
||||
} else {
|
||||
/* Otherwise, keep the {key,id} in leftover array */
|
||||
if (!leftover) {
|
||||
@@ -596,13 +598,7 @@ idl_new_range_fetch(
|
||||
leftovercnt++;
|
||||
}
|
||||
} else {
|
||||
- idl_rc = idl_append_extend(&idl, id);
|
||||
- }
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_new_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&idl);
|
||||
- goto error;
|
||||
+ idl_append_extend(&idl, id);
|
||||
}
|
||||
|
||||
count++;
|
||||
@@ -695,21 +691,17 @@ error:
|
||||
|
||||
while(remaining > 0) {
|
||||
for (size_t i = 0; i < leftovercnt; i++) {
|
||||
- if (leftover[i].key > 0 && idl_id_is_in_idlist(idl, leftover[i].key) != 0) {
|
||||
+ if (leftover[i].key > 0 && idl_id_is_in_idlist_ranges(idl, idrange_list, leftover[i].key) != 0) {
|
||||
/* if the leftover key has its parent in the idl */
|
||||
- idl_rc = idl_append_extend(&idl, leftover[i].id);
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_new_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&idl);
|
||||
- return NULL;
|
||||
- }
|
||||
+ idl_append_extend(&idl, leftover[i].id);
|
||||
+ idrange_add_id(&idrange_list, leftover[i].id);
|
||||
leftover[i].key = 0;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
slapi_ch_free((void **)&leftover);
|
||||
+ idrange_free(&idrange_list);
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_FILTER, "idl_new_range_fetch",
|
||||
"Found %d candidates; error code is: %d\n",
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
index 9a7ffee37..f16c37d73 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
@@ -217,6 +217,9 @@ ID idl_firstid(IDList *idl);
|
||||
ID idl_nextid(IDList *idl, ID id);
|
||||
int idl_init_private(backend *be, struct attrinfo *a);
|
||||
int idl_release_private(struct attrinfo *a);
|
||||
+IdRange_t *idrange_add_id(IdRange_t **head, ID id);
|
||||
+void idrange_free(IdRange_t **head);
|
||||
+int idl_id_is_in_idlist_ranges(IDList *idl, IdRange_t *idrange_list, ID id);
|
||||
int idl_id_is_in_idlist(IDList *idl, ID id);
|
||||
|
||||
idl_iterator idl_iterator_init(const IDList *idl);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
687
0003-Issue-6922-AddressSanitizer-leaks-found-by-acl-test-.patch
Normal file
687
0003-Issue-6922-AddressSanitizer-leaks-found-by-acl-test-.patch
Normal file
@ -0,0 +1,687 @@
|
||||
From 46808f90d9026454b77a65bbab7fccdde75253a4 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 11 Jul 2025 11:43:28 +0200
|
||||
Subject: [PATCH 3/8] Issue 6922 - AddressSanitizer: leaks found by acl test
|
||||
suite
|
||||
|
||||
Description:
|
||||
Fix multiple memory leaks in ACL plugin:
|
||||
|
||||
* aclgroup.c: Fix memory leak in `aclgroup_free()` by properly cleaning
|
||||
up all user groups before destroying the rwlock and freeing the main
|
||||
structure
|
||||
* aclscan.l & acltext.y: use `PERM_FREE` macro instead of `free()`
|
||||
* Add cleanups in aclparse.c, aclinit.c, acllist.c, acllas.c
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/6922
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
ldap/servers/plugins/acl/aclgroup.c | 16 ++++-
|
||||
ldap/servers/plugins/acl/aclinit.c | 4 ++
|
||||
ldap/servers/plugins/acl/acllas.c | 7 ++
|
||||
ldap/servers/plugins/acl/acllist.c | 2 +
|
||||
ldap/servers/plugins/acl/aclparse.c | 7 ++
|
||||
lib/libaccess/aclscan.l | 104 ++++++++++++++++++++++++++--
|
||||
lib/libaccess/acltext.y | 54 +++++++--------
|
||||
7 files changed, 160 insertions(+), 34 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/acl/aclgroup.c b/ldap/servers/plugins/acl/aclgroup.c
|
||||
index a5b0426f9..43faeae58 100644
|
||||
--- a/ldap/servers/plugins/acl/aclgroup.c
|
||||
+++ b/ldap/servers/plugins/acl/aclgroup.c
|
||||
@@ -50,8 +50,20 @@ aclgroup_init()
|
||||
void
|
||||
aclgroup_free()
|
||||
{
|
||||
- slapi_destroy_rwlock(aclUserGroups->aclg_rwlock);
|
||||
- slapi_ch_free((void **)&aclUserGroups);
|
||||
+ aclUserGroup *u_group, *next_group;
|
||||
+
|
||||
+ if (aclUserGroups) {
|
||||
+ /* Clean up all user groups */
|
||||
+ u_group = aclUserGroups->aclg_first;
|
||||
+ while (u_group) {
|
||||
+ next_group = u_group->aclug_next;
|
||||
+ __aclg__delete_userGroup(u_group);
|
||||
+ u_group = next_group;
|
||||
+ }
|
||||
+
|
||||
+ slapi_destroy_rwlock(aclUserGroups->aclg_rwlock);
|
||||
+ slapi_ch_free((void **)&aclUserGroups);
|
||||
+ }
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/ldap/servers/plugins/acl/aclinit.c b/ldap/servers/plugins/acl/aclinit.c
|
||||
index e2d5eba95..925fbbe53 100644
|
||||
--- a/ldap/servers/plugins/acl/aclinit.c
|
||||
+++ b/ldap/servers/plugins/acl/aclinit.c
|
||||
@@ -409,6 +409,7 @@ __aclinit__RegisterAttributes(void)
|
||||
rv = ACL_MethodRegister(&errp, DS_METHOD, &methodinfo);
|
||||
if (rv < 0) {
|
||||
acl_print_acllib_err(&errp, NULL);
|
||||
+ nserrDispose(&errp);
|
||||
slapi_log_err(SLAPI_LOG_ERR, plugin_name,
|
||||
"__aclinit__RegisterAttributes - Unable to Register the methods\n");
|
||||
return ACL_ERR;
|
||||
@@ -416,6 +417,7 @@ __aclinit__RegisterAttributes(void)
|
||||
rv = ACL_MethodSetDefault(&errp, methodinfo);
|
||||
if (rv < 0) {
|
||||
acl_print_acllib_err(&errp, NULL);
|
||||
+ nserrDispose(&errp);
|
||||
slapi_log_err(SLAPI_LOG_ERR, plugin_name,
|
||||
"__aclinit__RegisterAttributes - Unable to Set the default method\n");
|
||||
return ACL_ERR;
|
||||
@@ -424,6 +426,7 @@ __aclinit__RegisterAttributes(void)
|
||||
methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL);
|
||||
if (rv < 0) {
|
||||
acl_print_acllib_err(&errp, NULL);
|
||||
+ nserrDispose(&errp);
|
||||
slapi_log_err(SLAPI_LOG_ERR, plugin_name,
|
||||
"__aclinit__RegisterAttributes - Unable to Register Attr ip\n");
|
||||
return ACL_ERR;
|
||||
@@ -432,6 +435,7 @@ __aclinit__RegisterAttributes(void)
|
||||
methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL);
|
||||
if (rv < 0) {
|
||||
acl_print_acllib_err(&errp, NULL);
|
||||
+ nserrDispose(&errp);
|
||||
slapi_log_err(SLAPI_LOG_ERR, plugin_name,
|
||||
"__aclinit__RegisterAttributes - Unable to Register Attr dns\n");
|
||||
return ACL_ERR;
|
||||
diff --git a/ldap/servers/plugins/acl/acllas.c b/ldap/servers/plugins/acl/acllas.c
|
||||
index 792216cca..282359ae1 100644
|
||||
--- a/ldap/servers/plugins/acl/acllas.c
|
||||
+++ b/ldap/servers/plugins/acl/acllas.c
|
||||
@@ -258,6 +258,7 @@ DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t auth_in
|
||||
rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb, subject, resource, auth_info, global_auth);
|
||||
if (rv != LAS_EVAL_TRUE || (NULL == aclpb)) {
|
||||
acl_print_acllib_err(errp, NULL);
|
||||
+ nserrDispose(errp);
|
||||
slapi_log_err(SLAPI_LOG_ACL, plugin_name,
|
||||
"DS_LASIpGetter: Unable to get the ACLPB(%d)\n", rv);
|
||||
return LAS_EVAL_FAIL;
|
||||
@@ -334,6 +335,7 @@ DS_LASDnsGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t auth_i
|
||||
subject, resource, auth_info, global_auth);
|
||||
if (rv != LAS_EVAL_TRUE || (NULL == aclpb)) {
|
||||
acl_print_acllib_err(errp, NULL);
|
||||
+ nserrDispose(errp);
|
||||
slapi_log_err(SLAPI_LOG_ACL, plugin_name,
|
||||
"DS_LASDnsGetter - Unable to get the ACLPB(%d)\n", rv);
|
||||
return LAS_EVAL_FAIL;
|
||||
@@ -3743,6 +3745,7 @@ __acllas_setup(NSErr_t *errp, char *attr_name, CmpOp_t comparator, int allow_ran
|
||||
|
||||
if (rc != LAS_EVAL_TRUE) {
|
||||
acl_print_acllib_err(errp, NULL);
|
||||
+ nserrDispose(errp);
|
||||
slapi_log_err(SLAPI_LOG_ACL, plugin_name,
|
||||
"__acllas_setup - %s:Unable to get the clientdn attribute(%d)\n", lasName, rc);
|
||||
return LAS_EVAL_FAIL;
|
||||
@@ -3762,6 +3765,7 @@ __acllas_setup(NSErr_t *errp, char *attr_name, CmpOp_t comparator, int allow_ran
|
||||
if ((rc = PListFindValue(subject, DS_ATTR_ENTRY,
|
||||
(void **)&linfo->resourceEntry, NULL)) < 0) {
|
||||
acl_print_acllib_err(errp, NULL);
|
||||
+ nserrDispose(errp);
|
||||
slapi_log_err(SLAPI_LOG_ACL, plugin_name,
|
||||
"__acllas_setup - %s:Unable to get the Slapi_Entry attr(%d)\n", lasName, rc);
|
||||
return LAS_EVAL_FAIL;
|
||||
@@ -3772,6 +3776,7 @@ __acllas_setup(NSErr_t *errp, char *attr_name, CmpOp_t comparator, int allow_ran
|
||||
subject, resource, auth_info, global_auth);
|
||||
if (rc != LAS_EVAL_TRUE) {
|
||||
acl_print_acllib_err(errp, NULL);
|
||||
+ nserrDispose(errp);
|
||||
slapi_log_err(SLAPI_LOG_ACL, plugin_name,
|
||||
"__acllas_setup - %s:Unable to get the ACLPB(%d)\n", lasName, rc);
|
||||
return LAS_EVAL_FAIL;
|
||||
@@ -3795,6 +3800,7 @@ __acllas_setup(NSErr_t *errp, char *attr_name, CmpOp_t comparator, int allow_ran
|
||||
if ((rc = PListFindValue(subject, DS_ATTR_AUTHTYPE,
|
||||
(void **)&linfo->authType, NULL)) < 0) {
|
||||
acl_print_acllib_err(errp, NULL);
|
||||
+ nserrDispose(errp);
|
||||
slapi_log_err(SLAPI_LOG_ACL, plugin_name,
|
||||
"__acllas_setup - %s:Unable to get the auth type(%d)\n", lasName, rc);
|
||||
return LAS_EVAL_FAIL;
|
||||
@@ -3804,6 +3810,7 @@ __acllas_setup(NSErr_t *errp, char *attr_name, CmpOp_t comparator, int allow_ran
|
||||
if ((rc = PListFindValue(subject, DS_ATTR_SSF,
|
||||
(void **)&linfo->ssf, NULL)) < 0) {
|
||||
acl_print_acllib_err(errp, NULL);
|
||||
+ nserrDispose(errp);
|
||||
slapi_log_err(SLAPI_LOG_ACL, plugin_name,
|
||||
"__acllas_setup - %s:Unable to get the ssf(%d)\n", lasName, rc);
|
||||
}
|
||||
diff --git a/ldap/servers/plugins/acl/acllist.c b/ldap/servers/plugins/acl/acllist.c
|
||||
index bf1168691..6d80a5835 100644
|
||||
--- a/ldap/servers/plugins/acl/acllist.c
|
||||
+++ b/ldap/servers/plugins/acl/acllist.c
|
||||
@@ -526,6 +526,8 @@ acllist_free_aci(aci_t *item)
|
||||
|
||||
slapi_sdn_free(&item->aci_sdn);
|
||||
slapi_filter_free(item->target, 1);
|
||||
+ slapi_filter_free(item->target_to, 1);
|
||||
+ slapi_filter_free(item->target_from, 1);
|
||||
|
||||
/* slapi_filter_free(item->targetAttr, 1); */
|
||||
attrArray = item->targetAttr;
|
||||
diff --git a/ldap/servers/plugins/acl/aclparse.c b/ldap/servers/plugins/acl/aclparse.c
|
||||
index 261cb69ec..882f4ef28 100644
|
||||
--- a/ldap/servers/plugins/acl/aclparse.c
|
||||
+++ b/ldap/servers/plugins/acl/aclparse.c
|
||||
@@ -423,6 +423,9 @@ __aclp__parse_aci(char *str, aci_t *aci_item, char **errbuf)
|
||||
}
|
||||
f = slapi_str2filter(tstr);
|
||||
slapi_ch_free_string(&tstr);
|
||||
+ if (rv > 0) {
|
||||
+ slapi_ch_free_string(&tmpstr);
|
||||
+ }
|
||||
} else if (strncmp(str, aci_targetdn, targetdnlen) == 0) {
|
||||
char *tstr = NULL;
|
||||
size_t LDAP_URL_prefix_len = 0;
|
||||
@@ -706,6 +709,7 @@ __aclp__sanity_check_acltxt(aci_t *aci_item, char *str)
|
||||
/* check for acl syntax error */
|
||||
if ((handle = (ACLListHandle_t *)ACL_ParseString(&errp, newstr)) == NULL) {
|
||||
acl_print_acllib_err(&errp, str);
|
||||
+ nserrDispose(&errp);
|
||||
slapi_ch_free_string(&newstr);
|
||||
return ACL_SYNTAX_ERR;
|
||||
} else {
|
||||
@@ -1144,13 +1148,16 @@ normalize_nextACERule:
|
||||
if (nextACE && *nextACE != '\0')
|
||||
aclutil_str_append(&ret_str, nextACE);
|
||||
slapi_ch_free_string(&s_aclstr);
|
||||
+ slapi_ch_free_string(&s_acestr);
|
||||
return (ret_str);
|
||||
}
|
||||
acestr = nextACE;
|
||||
+ slapi_ch_free_string(&s_acestr);
|
||||
goto normalize_nextACERule;
|
||||
}
|
||||
|
||||
slapi_ch_free_string(&s_aclstr);
|
||||
+ slapi_ch_free_string(&s_acestr);
|
||||
return (ret_str);
|
||||
|
||||
error:
|
||||
diff --git a/lib/libaccess/aclscan.l b/lib/libaccess/aclscan.l
|
||||
index 7067096c0..9c836decc 100644
|
||||
--- a/lib/libaccess/aclscan.l
|
||||
+++ b/lib/libaccess/aclscan.l
|
||||
@@ -64,11 +64,18 @@ variable [\*a-zA-Z0-9\.\-\_][\*a-zA-Z0-9\.\-\_]*
|
||||
|
||||
<<EOF>> {
|
||||
yylval.string = NULL;
|
||||
- last_string = yylval.string;
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ last_string = NULL;
|
||||
+ }
|
||||
return(0);
|
||||
}
|
||||
|
||||
{qstring} {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ last_string = NULL;
|
||||
+ }
|
||||
yylval.string = PERM_STRDUP( yytext+1 );
|
||||
last_string = yylval.string;
|
||||
if ( yylval.string[yyleng-2] != '"' )
|
||||
@@ -81,96 +88,144 @@ variable [\*a-zA-Z0-9\.\-\_][\*a-zA-Z0-9\.\-\_]*
|
||||
|
||||
|
||||
absolute {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_ABSOLUTE_TOK;
|
||||
}
|
||||
|
||||
acl {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_ACL_TOK;
|
||||
}
|
||||
|
||||
allow {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_ALLOW_TOK;
|
||||
}
|
||||
|
||||
always {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_ALWAYS_TOK;
|
||||
}
|
||||
|
||||
at {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_AT_TOK;
|
||||
}
|
||||
|
||||
authenticate {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_AUTHENTICATE_TOK;
|
||||
}
|
||||
|
||||
content {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_CONTENT_TOK;
|
||||
}
|
||||
|
||||
default {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_DEFAULT_TOK;
|
||||
}
|
||||
|
||||
deny {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_DENY_TOK;
|
||||
}
|
||||
|
||||
in {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_IN_TOK;
|
||||
}
|
||||
|
||||
inherit {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_INHERIT_TOK;
|
||||
}
|
||||
|
||||
terminal {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_TERMINAL_TOK;
|
||||
}
|
||||
|
||||
version {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_VERSION_TOK;
|
||||
}
|
||||
|
||||
with {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_WITH_TOK;
|
||||
}
|
||||
|
||||
not {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return ACL_NOT_TOK;
|
||||
}
|
||||
|
||||
and {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = ACL_EXPR_OP_AND;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -178,6 +233,9 @@ and {
|
||||
}
|
||||
|
||||
or {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = ACL_EXPR_OP_OR;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -185,6 +243,9 @@ or {
|
||||
}
|
||||
|
||||
"=" {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = CMP_OP_EQ;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -192,6 +253,9 @@ or {
|
||||
}
|
||||
|
||||
">=" {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = CMP_OP_GE;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -199,6 +263,9 @@ or {
|
||||
}
|
||||
|
||||
">" {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = CMP_OP_GT;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -206,6 +273,9 @@ or {
|
||||
}
|
||||
|
||||
"<" {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = CMP_OP_LT;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -213,6 +283,9 @@ or {
|
||||
}
|
||||
|
||||
"<=" {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = CMP_OP_LE;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -220,6 +293,9 @@ or {
|
||||
}
|
||||
|
||||
"!=" {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
yylval.ival = CMP_OP_NE;
|
||||
acl_tokenpos += yyleng;
|
||||
@@ -227,6 +303,9 @@ or {
|
||||
}
|
||||
|
||||
[(){},;] {
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ }
|
||||
last_string = NULL;
|
||||
acl_tokenpos += yyleng;
|
||||
return yytext[0];
|
||||
@@ -234,6 +313,10 @@ or {
|
||||
|
||||
{variable} {
|
||||
acl_tokenpos += yyleng;
|
||||
+ if (last_string) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ last_string = NULL;
|
||||
+ }
|
||||
yylval.string = PERM_STRDUP( yytext );
|
||||
last_string = yylval.string;
|
||||
return ACL_VARIABLE_TOK;
|
||||
@@ -272,8 +355,10 @@ char errorStr[256];
|
||||
#if defined(UTEST) || defined(ACL_COMPILER)
|
||||
printf("ACL file: %s\n", acl_filename);
|
||||
printf("Syntax error at line: %d, token: %s\n", acl_lineno, yytext);
|
||||
- if ( last_string )
|
||||
- free(last_string);
|
||||
+ if ( last_string ) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ last_string = NULL;
|
||||
+ }
|
||||
#else
|
||||
sprintf(errorStr, "%d", acl_lineno);
|
||||
if (yytext) {
|
||||
@@ -283,8 +368,10 @@ char errorStr[256];
|
||||
nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program,
|
||||
2, acl_filename, errorStr);
|
||||
}
|
||||
- if ( last_string )
|
||||
- free(last_string);
|
||||
+ if ( last_string ) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ last_string = NULL;
|
||||
+ }
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -294,6 +381,7 @@ acl_InitScanner(NSErr_t *errp, char *filename, char *buffer)
|
||||
{
|
||||
acl_errp = errp;
|
||||
acl_lineno = 1;
|
||||
+ last_string = NULL;
|
||||
acl_use_buffer = (filename == NULL) ? 1 : 0 ;
|
||||
if ( filename != NULL ) {
|
||||
PL_strncpyz(acl_filename, filename, sizeof(acl_filename));
|
||||
@@ -342,6 +430,12 @@ acl_EndScanner()
|
||||
#endif
|
||||
yyin = NULL ;
|
||||
}
|
||||
+
|
||||
+ if ( last_string ) {
|
||||
+ PERM_FREE(last_string);
|
||||
+ last_string = NULL;
|
||||
+ }
|
||||
+
|
||||
return(0);
|
||||
}
|
||||
|
||||
diff --git a/lib/libaccess/acltext.y b/lib/libaccess/acltext.y
|
||||
index 16eea355b..ed2c6109b 100644
|
||||
--- a/lib/libaccess/acltext.y
|
||||
+++ b/lib/libaccess/acltext.y
|
||||
@@ -80,7 +80,7 @@ acl_free_args(char **args_list)
|
||||
|
||||
for (ii = 0; ii < MAX_LIST_SIZE; ii++) {
|
||||
if ( args_list[ii] )
|
||||
- free(args_list[ii]);
|
||||
+ PERM_FREE(args_list[ii]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -240,7 +240,7 @@ acl_set_ip_dns(ACLExprHandle_t *expr, char **ip_dns)
|
||||
start: | start_acl_v2
|
||||
| ACL_VERSION_TOK ACL_VARIABLE_TOK
|
||||
{
|
||||
- free($<string>2);
|
||||
+ PERM_FREE($<string>2);
|
||||
}
|
||||
';' start_acl_v3
|
||||
;
|
||||
@@ -267,7 +267,7 @@ acl_v2: ACL_ACL_TOK acl_name_v2
|
||||
acl_name_v2: ACL_VARIABLE_TOK
|
||||
{
|
||||
curr_acl = ACL_AclNew(NULL, $<string>1);
|
||||
- free($<string>1);
|
||||
+ PERM_FREE($<string>1);
|
||||
if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) {
|
||||
yyerror("Couldn't add ACL to list.");
|
||||
return(-1);
|
||||
@@ -289,7 +289,7 @@ acl_name_v2: ACL_VARIABLE_TOK
|
||||
| ACL_QSTRING_TOK
|
||||
{
|
||||
curr_acl = ACL_AclNew(NULL, $<string>1);
|
||||
- free($<string>1);
|
||||
+ PERM_FREE($<string>1);
|
||||
if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) {
|
||||
yyerror("Couldn't add ACL to list.");
|
||||
return(-1);
|
||||
@@ -503,8 +503,8 @@ ip_spec_v2: ACL_VARIABLE_TOK ACL_VARIABLE_TOK
|
||||
char tmp_str[255];
|
||||
|
||||
util_sprintf(tmp_str, "%s+%s", $<string>1, $<string>2);
|
||||
- free($<string>1);
|
||||
- free($<string>2);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>2);
|
||||
acl_add_arg(curr_ip_dns_list, PERM_STRDUP(tmp_str));
|
||||
}
|
||||
;
|
||||
@@ -538,26 +538,26 @@ method_v2: ACL_VARIABLE_TOK ACL_VARIABLE_TOK ';'
|
||||
{
|
||||
acl_string_lower($<string>1);
|
||||
if (strcmp($<string>1, "database") == 0) {
|
||||
- free($<string>1);
|
||||
- free($<string>2);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>2);
|
||||
} else {
|
||||
if ( PListInitProp(curr_auth_info,
|
||||
ACL_Attr2Index($<string>1), $<string>1, $<string>2, NULL) < 0 ) {
|
||||
}
|
||||
- free($<string>1);
|
||||
+ PERM_FREE($<string>1);
|
||||
}
|
||||
}
|
||||
| ACL_VARIABLE_TOK ACL_QSTRING_TOK ';'
|
||||
{
|
||||
acl_string_lower($<string>1);
|
||||
if (strcmp($<string>1, "database") == 0) {
|
||||
- free($<string>1);
|
||||
- free($<string>2);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>2);
|
||||
} else {
|
||||
if ( PListInitProp(curr_auth_info,
|
||||
ACL_Attr2Index($<string>1), $<string>1, $<string>2, NULL) < 0 ) {
|
||||
}
|
||||
- free($<string>1);
|
||||
+ PERM_FREE($<string>1);
|
||||
}
|
||||
}
|
||||
;
|
||||
@@ -586,7 +586,7 @@ acl: named_acl ';' body_list
|
||||
named_acl: ACL_ACL_TOK ACL_VARIABLE_TOK
|
||||
{
|
||||
curr_acl = ACL_AclNew(NULL, $<string>2);
|
||||
- free($<string>2);
|
||||
+ PERM_FREE($<string>2);
|
||||
if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) {
|
||||
yyerror("Couldn't add ACL to list.");
|
||||
return(-1);
|
||||
@@ -595,7 +595,7 @@ named_acl: ACL_ACL_TOK ACL_VARIABLE_TOK
|
||||
| ACL_ACL_TOK ACL_QSTRING_TOK
|
||||
{
|
||||
curr_acl = ACL_AclNew(NULL, $<string>2);
|
||||
- free($<string>2);
|
||||
+ PERM_FREE($<string>2);
|
||||
if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) {
|
||||
yyerror("Couldn't add ACL to list.");
|
||||
return(-1);
|
||||
@@ -654,8 +654,8 @@ deny_common: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK
|
||||
yyerror("ACL_ExprSetDenyWith() failed");
|
||||
return(-1);
|
||||
}
|
||||
- free($<string>1);
|
||||
- free($<string>3);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>3);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -692,7 +692,7 @@ attribute: ACL_VARIABLE_TOK
|
||||
yyerror("ACL_ExprAddArg() failed");
|
||||
return(-1);
|
||||
}
|
||||
- free($<string>1);
|
||||
+ PERM_FREE($<string>1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -706,7 +706,7 @@ parameter: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK
|
||||
if ( PListInitProp(curr_auth_info,
|
||||
ACL_Attr2Index($<string>1), $<string>1, $<string>3, NULL) < 0 ) {
|
||||
}
|
||||
- free($<string>1);
|
||||
+ PERM_FREE($<string>1);
|
||||
}
|
||||
| ACL_VARIABLE_TOK ACL_EQ_TOK ACL_VARIABLE_TOK
|
||||
{
|
||||
@@ -714,7 +714,7 @@ parameter: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK
|
||||
if ( PListInitProp(curr_auth_info,
|
||||
ACL_Attr2Index($<string>1), $<string>1, $<string>3, NULL) < 0 ) {
|
||||
}
|
||||
- free($<string>1);
|
||||
+ PERM_FREE($<string>1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -896,12 +896,12 @@ base_expr: ACL_VARIABLE_TOK relop ACL_QSTRING_TOK
|
||||
if ( ACL_ExprTerm(NULL, curr_expr,
|
||||
$<string>1, (CmpOp_t) $<ival>2, $<string>3) < 0 ) {
|
||||
yyerror("ACL_ExprTerm() failed");
|
||||
- free($<string>1);
|
||||
- free($<string>3);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>3);
|
||||
return(-1);
|
||||
}
|
||||
- free($<string>1);
|
||||
- free($<string>3);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>3);
|
||||
}
|
||||
| ACL_VARIABLE_TOK relop ACL_VARIABLE_TOK
|
||||
{
|
||||
@@ -909,12 +909,12 @@ base_expr: ACL_VARIABLE_TOK relop ACL_QSTRING_TOK
|
||||
if ( ACL_ExprTerm(NULL, curr_expr,
|
||||
$<string>1, (CmpOp_t) $<ival>2, $<string>3) < 0 ) {
|
||||
yyerror("ACL_ExprTerm() failed");
|
||||
- free($<string>1);
|
||||
- free($<string>3);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>3);
|
||||
return(-1);
|
||||
}
|
||||
- free($<string>1);
|
||||
- free($<string>3);
|
||||
+ PERM_FREE($<string>1);
|
||||
+ PERM_FREE($<string>3);
|
||||
}
|
||||
;
|
||||
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,765 +0,0 @@
|
||||
From a0af5ab79ddad7944687e31f16bb2a667c800958 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 7 Jan 2026 16:55:27 -0500
|
||||
Subject: [PATCH] Issue - Revise paged result search locking
|
||||
|
||||
Description:
|
||||
|
||||
Move to a single lock approach verses having two locks. This will impact
|
||||
concurrency when multiple async paged result searches are done on the same
|
||||
connection, but it simplifies the code and avoids race conditions and
|
||||
deadlocks.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7118
|
||||
|
||||
Reviewed by: progier & tbordaz (Thanks!!)
|
||||
---
|
||||
ldap/servers/slapd/abandon.c | 2 +-
|
||||
ldap/servers/slapd/opshared.c | 60 ++++----
|
||||
ldap/servers/slapd/pagedresults.c | 228 +++++++++++++++++++-----------
|
||||
ldap/servers/slapd/proto-slap.h | 26 ++--
|
||||
ldap/servers/slapd/slap.h | 5 +-
|
||||
5 files changed, 187 insertions(+), 134 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/abandon.c b/ldap/servers/slapd/abandon.c
|
||||
index 2dd1ee320..d7c1f8868 100644
|
||||
--- a/ldap/servers/slapd/abandon.c
|
||||
+++ b/ldap/servers/slapd/abandon.c
|
||||
@@ -141,7 +141,7 @@ do_abandon(Slapi_PBlock *pb)
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(pb_conn->c_mutex));
|
||||
- if (0 == pagedresults_free_one_msgid(pb_conn, id, pageresult_lock_get_addr(pb_conn))) {
|
||||
+ if (0 == pagedresults_free_one_msgid(pb_conn, id, PR_NOT_LOCKED)) {
|
||||
slapi_log_access(LDAP_DEBUG_STATS, "conn=%" PRIu64
|
||||
" op=%d ABANDON targetop=Simple Paged Results msgid=%d\n",
|
||||
pb_conn->c_connid, pb_op->o_opid, id);
|
||||
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
|
||||
index 03ed60981..bb474ef3d 100644
|
||||
--- a/ldap/servers/slapd/opshared.c
|
||||
+++ b/ldap/servers/slapd/opshared.c
|
||||
@@ -545,8 +545,8 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
be = be_list[index];
|
||||
}
|
||||
}
|
||||
- pr_search_result = pagedresults_get_search_result(pb_conn, operation, 0 /*not locked*/, pr_idx);
|
||||
- estimate = pagedresults_get_search_result_set_size_estimate(pb_conn, operation, pr_idx);
|
||||
+ pr_search_result = pagedresults_get_search_result(pb_conn, operation, PR_NOT_LOCKED, pr_idx);
|
||||
+ estimate = pagedresults_get_search_result_set_size_estimate(pb_conn, operation, PR_NOT_LOCKED, pr_idx);
|
||||
/* Set operation note flags as required. */
|
||||
if (pagedresults_get_unindexed(pb_conn, operation, pr_idx)) {
|
||||
slapi_pblock_set_flag_operation_notes(pb, SLAPI_OP_NOTE_UNINDEXED);
|
||||
@@ -592,14 +592,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
int32_t tlimit;
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_TIMELIMIT, &tlimit);
|
||||
pagedresults_set_timelimit(pb_conn, operation, (time_t)tlimit, pr_idx);
|
||||
- /* When using this mutex in conjunction with the main paged
|
||||
- * result lock, you must do so in this order:
|
||||
- *
|
||||
- * --> pagedresults_lock()
|
||||
- * --> pagedresults_mutex
|
||||
- * <-- pagedresults_mutex
|
||||
- * <-- pagedresults_unlock()
|
||||
- */
|
||||
+ /* IMPORTANT: Never acquire pagedresults_mutex when holding c_mutex. */
|
||||
pagedresults_mutex = pageresult_lock_get_addr(pb_conn);
|
||||
}
|
||||
|
||||
@@ -716,17 +709,15 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
if (op_is_pagedresults(operation) && pr_search_result) {
|
||||
void *sr = NULL;
|
||||
/* PAGED RESULTS and already have the search results from the prev op */
|
||||
- pagedresults_lock(pb_conn, pr_idx);
|
||||
/*
|
||||
* In async paged result case, the search result might be released
|
||||
* by other theads. We need to double check it in the locked region.
|
||||
*/
|
||||
pthread_mutex_lock(pagedresults_mutex);
|
||||
- pr_search_result = pagedresults_get_search_result(pb_conn, operation, 1 /*locked*/, pr_idx);
|
||||
+ pr_search_result = pagedresults_get_search_result(pb_conn, operation, PR_LOCKED, pr_idx);
|
||||
if (pr_search_result) {
|
||||
- if (pagedresults_is_abandoned_or_notavailable(pb_conn, 1 /*locked*/, pr_idx)) {
|
||||
+ if (pagedresults_is_abandoned_or_notavailable(pb_conn, PR_LOCKED, pr_idx)) {
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
/* Previous operation was abandoned and the simplepaged object is not in use. */
|
||||
send_ldap_result(pb, 0, NULL, "Simple Paged Results Search abandoned", 0, NULL);
|
||||
rc = LDAP_SUCCESS;
|
||||
@@ -737,14 +728,13 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
|
||||
/* search result could be reset in the backend/dse */
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
|
||||
- pagedresults_set_search_result(pb_conn, operation, sr, 1 /*locked*/, pr_idx);
|
||||
+ pagedresults_set_search_result(pb_conn, operation, sr, PR_LOCKED, pr_idx);
|
||||
}
|
||||
} else {
|
||||
pr_stat = PAGEDRESULTS_SEARCH_END;
|
||||
rc = LDAP_SUCCESS;
|
||||
}
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
|
||||
if ((PAGEDRESULTS_SEARCH_END == pr_stat) || (0 == pnentries)) {
|
||||
/* no more entries to send in the backend */
|
||||
@@ -762,22 +752,22 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
}
|
||||
pagedresults_set_response_control(pb, 0, estimate,
|
||||
curr_search_count, pr_idx);
|
||||
- if (pagedresults_get_with_sort(pb_conn, operation, pr_idx)) {
|
||||
+ if (pagedresults_get_with_sort(pb_conn, operation, PR_NOT_LOCKED, pr_idx)) {
|
||||
sort_make_sort_response_control(pb, CONN_GET_SORT_RESULT_CODE, NULL);
|
||||
}
|
||||
pagedresults_set_search_result_set_size_estimate(pb_conn,
|
||||
operation,
|
||||
- estimate, pr_idx);
|
||||
+ estimate, PR_NOT_LOCKED, pr_idx);
|
||||
if (PAGEDRESULTS_SEARCH_END == pr_stat) {
|
||||
- pagedresults_lock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_lock(pagedresults_mutex);
|
||||
slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
|
||||
- if (!pagedresults_is_abandoned_or_notavailable(pb_conn, 0 /*not locked*/, pr_idx)) {
|
||||
- pagedresults_free_one(pb_conn, operation, pr_idx);
|
||||
+ if (!pagedresults_is_abandoned_or_notavailable(pb_conn, PR_LOCKED, pr_idx)) {
|
||||
+ pagedresults_free_one(pb_conn, operation, PR_LOCKED, pr_idx);
|
||||
}
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_unlock(pagedresults_mutex);
|
||||
if (next_be) {
|
||||
/* no more entries, but at least another backend */
|
||||
- if (pagedresults_set_current_be(pb_conn, next_be, pr_idx, 0) < 0) {
|
||||
+ if (pagedresults_set_current_be(pb_conn, next_be, pr_idx, PR_NOT_LOCKED) < 0) {
|
||||
goto free_and_return;
|
||||
}
|
||||
}
|
||||
@@ -884,7 +874,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
}
|
||||
}
|
||||
pagedresults_set_search_result(pb_conn, operation, NULL, 1, pr_idx);
|
||||
- rc = pagedresults_set_current_be(pb_conn, NULL, pr_idx, 1);
|
||||
+ rc = pagedresults_set_current_be(pb_conn, NULL, pr_idx, PR_LOCKED);
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
}
|
||||
|
||||
@@ -922,7 +912,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
pthread_mutex_lock(pagedresults_mutex);
|
||||
pagedresults_set_search_result(pb_conn, operation, NULL, 1, pr_idx);
|
||||
be->be_search_results_release(&sr);
|
||||
- rc = pagedresults_set_current_be(pb_conn, next_be, pr_idx, 1);
|
||||
+ rc = pagedresults_set_current_be(pb_conn, next_be, pr_idx, PR_LOCKED);
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
pr_stat = PAGEDRESULTS_SEARCH_END; /* make sure stat is SEARCH_END */
|
||||
if (NULL == next_be) {
|
||||
@@ -935,23 +925,23 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
} else {
|
||||
curr_search_count = pnentries;
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate);
|
||||
- pagedresults_lock(pb_conn, pr_idx);
|
||||
- if ((pagedresults_set_current_be(pb_conn, be, pr_idx, 0) < 0) ||
|
||||
- (pagedresults_set_search_result(pb_conn, operation, sr, 0, pr_idx) < 0) ||
|
||||
- (pagedresults_set_search_result_count(pb_conn, operation, curr_search_count, pr_idx) < 0) ||
|
||||
- (pagedresults_set_search_result_set_size_estimate(pb_conn, operation, estimate, pr_idx) < 0) ||
|
||||
- (pagedresults_set_with_sort(pb_conn, operation, with_sort, pr_idx) < 0)) {
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_lock(pagedresults_mutex);
|
||||
+ if ((pagedresults_set_current_be(pb_conn, be, pr_idx, PR_LOCKED) < 0) ||
|
||||
+ (pagedresults_set_search_result(pb_conn, operation, sr, PR_LOCKED, pr_idx) < 0) ||
|
||||
+ (pagedresults_set_search_result_count(pb_conn, operation, curr_search_count, PR_LOCKED, pr_idx) < 0) ||
|
||||
+ (pagedresults_set_search_result_set_size_estimate(pb_conn, operation, estimate, PR_LOCKED, pr_idx) < 0) ||
|
||||
+ (pagedresults_set_with_sort(pb_conn, operation, with_sort, PR_LOCKED, pr_idx) < 0)) {
|
||||
+ pthread_mutex_unlock(pagedresults_mutex);
|
||||
cache_return_target_entry(pb, be, operation);
|
||||
goto free_and_return;
|
||||
}
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_unlock(pagedresults_mutex);
|
||||
}
|
||||
slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
|
||||
next_be = NULL; /* to break the loop */
|
||||
if (operation->o_status & SLAPI_OP_STATUS_ABANDONED) {
|
||||
/* It turned out this search was abandoned. */
|
||||
- pagedresults_free_one_msgid(pb_conn, operation->o_msgid, pagedresults_mutex);
|
||||
+ pagedresults_free_one_msgid(pb_conn, operation->o_msgid, PR_NOT_LOCKED);
|
||||
/* paged-results-request was abandoned; making an empty cookie. */
|
||||
pagedresults_set_response_control(pb, 0, estimate, -1, pr_idx);
|
||||
send_ldap_result(pb, 0, NULL, "Simple Paged Results Search abandoned", 0, NULL);
|
||||
@@ -961,7 +951,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
}
|
||||
pagedresults_set_response_control(pb, 0, estimate, curr_search_count, pr_idx);
|
||||
if (curr_search_count == -1) {
|
||||
- pagedresults_free_one(pb_conn, operation, pr_idx);
|
||||
+ pagedresults_free_one(pb_conn, operation, PR_NOT_LOCKED, pr_idx);
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
|
||||
index 941ab97e3..0d6c4a1aa 100644
|
||||
--- a/ldap/servers/slapd/pagedresults.c
|
||||
+++ b/ldap/servers/slapd/pagedresults.c
|
||||
@@ -34,9 +34,9 @@ pageresult_lock_cleanup()
|
||||
slapi_ch_free((void**)&lock_hash);
|
||||
}
|
||||
|
||||
-/* Beware to the lock order with c_mutex:
|
||||
- * c_mutex is sometime locked while holding pageresult_lock
|
||||
- * ==> Do not lock pageresult_lock when holing c_mutex
|
||||
+/* Lock ordering constraint with c_mutex:
|
||||
+ * c_mutex is sometimes locked while holding pageresult_lock.
|
||||
+ * Therefore: DO NOT acquire pageresult_lock when holding c_mutex.
|
||||
*/
|
||||
pthread_mutex_t *
|
||||
pageresult_lock_get_addr(Connection *conn)
|
||||
@@ -44,7 +44,11 @@ pageresult_lock_get_addr(Connection *conn)
|
||||
return &lock_hash[(((size_t)conn)/sizeof (Connection))%LOCK_HASH_SIZE];
|
||||
}
|
||||
|
||||
-/* helper function to clean up one prp slot */
|
||||
+/* helper function to clean up one prp slot
|
||||
+ *
|
||||
+ * NOTE: This function must be called while holding the pageresult_lock
|
||||
+ * (via pageresult_lock_get_addr(conn)) to ensure thread-safe cleanup.
|
||||
+ */
|
||||
static void
|
||||
_pr_cleanup_one_slot(PagedResults *prp)
|
||||
{
|
||||
@@ -56,7 +60,7 @@ _pr_cleanup_one_slot(PagedResults *prp)
|
||||
prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
|
||||
}
|
||||
|
||||
- /* clean up the slot except the mutex */
|
||||
+ /* clean up the slot */
|
||||
prp->pr_current_be = NULL;
|
||||
prp->pr_search_result_set = NULL;
|
||||
prp->pr_search_result_count = 0;
|
||||
@@ -136,6 +140,8 @@ pagedresults_parse_control_value(Slapi_PBlock *pb,
|
||||
return LDAP_UNWILLING_TO_PERFORM;
|
||||
}
|
||||
|
||||
+ /* Acquire hash-based lock for paged results list access
|
||||
+ * IMPORTANT: Never acquire this lock when holding c_mutex */
|
||||
pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
/* the ber encoding is no longer needed */
|
||||
ber_free(ber, 1);
|
||||
@@ -184,10 +190,6 @@ pagedresults_parse_control_value(Slapi_PBlock *pb,
|
||||
goto bail;
|
||||
}
|
||||
|
||||
- if ((*index > -1) && (*index < conn->c_pagedresults.prl_maxlen) &&
|
||||
- !conn->c_pagedresults.prl_list[*index].pr_mutex) {
|
||||
- conn->c_pagedresults.prl_list[*index].pr_mutex = PR_NewLock();
|
||||
- }
|
||||
conn->c_pagedresults.prl_count++;
|
||||
} else {
|
||||
/* Repeated paged results request.
|
||||
@@ -327,8 +329,14 @@ bailout:
|
||||
"<= idx=%d\n", index);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Free one paged result entry by index.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
+pagedresults_free_one(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
@@ -338,7 +346,9 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_free_one",
|
||||
"=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (conn->c_pagedresults.prl_count <= 0) {
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_free_one",
|
||||
"conn=%" PRIu64 " paged requests list count is %d\n",
|
||||
@@ -349,7 +359,9 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
conn->c_pagedresults.prl_count--;
|
||||
rc = 0;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_free_one", "<= %d\n", rc);
|
||||
@@ -357,21 +369,28 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
}
|
||||
|
||||
/*
|
||||
- * Used for abandoning - pageresult_lock_get_addr(conn) is already locked in do_abandone.
|
||||
+ * Free one paged result entry by message ID.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
*/
|
||||
int
|
||||
-pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex)
|
||||
+pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, bool locked)
|
||||
{
|
||||
int rc = -1;
|
||||
int i;
|
||||
+ pthread_mutex_t *lock = NULL;
|
||||
|
||||
if (conn && (msgid > -1)) {
|
||||
if (conn->c_pagedresults.prl_maxlen <= 0) {
|
||||
; /* Not a paged result. */
|
||||
} else {
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
- "pagedresults_free_one_msgid_nolock", "=> msgid=%d\n", msgid);
|
||||
- pthread_mutex_lock(mutex);
|
||||
+ "pagedresults_free_one_msgid", "=> msgid=%d\n", msgid);
|
||||
+ lock = pageresult_lock_get_addr(conn);
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(lock);
|
||||
+ }
|
||||
for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
|
||||
if (conn->c_pagedresults.prl_list[i].pr_msgid == msgid) {
|
||||
PagedResults *prp = conn->c_pagedresults.prl_list + i;
|
||||
@@ -390,9 +409,11 @@ pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *
|
||||
break;
|
||||
}
|
||||
}
|
||||
- pthread_mutex_unlock(mutex);
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(lock);
|
||||
+ }
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
- "pagedresults_free_one_msgid_nolock", "<= %d\n", rc);
|
||||
+ "pagedresults_free_one_msgid", "<= %d\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,29 +439,43 @@ pagedresults_get_current_be(Connection *conn, int index)
|
||||
return be;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set current backend for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=false, acquires pageresult_lock. If locked=true, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, int nolock)
|
||||
+pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, bool locked)
|
||||
{
|
||||
int rc = -1;
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_current_be", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- if (!nolock)
|
||||
+ if (!locked) {
|
||||
pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
conn->c_pagedresults.prl_list[index].pr_current_be = be;
|
||||
}
|
||||
rc = 0;
|
||||
- if (!nolock)
|
||||
+ if (!locked) {
|
||||
pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_current_be", "<= %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get search result set for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
void *
|
||||
-pagedresults_get_search_result(Connection *conn, Operation *op, int locked, int index)
|
||||
+pagedresults_get_search_result(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
void *sr = NULL;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -465,8 +500,14 @@ pagedresults_get_search_result(Connection *conn, Operation *op, int locked, int
|
||||
return sr;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set search result set for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, int locked, int index)
|
||||
+pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -494,8 +535,14 @@ pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, int lo
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get search result count for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_get_search_result_count(Connection *conn, Operation *op, int index)
|
||||
+pagedresults_get_search_result_count(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
int count = 0;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -504,19 +551,29 @@ pagedresults_get_search_result_count(Connection *conn, Operation *op, int index)
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_search_result_count", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
count = conn->c_pagedresults.prl_list[index].pr_search_result_count;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_search_result_count", "<= %d\n", count);
|
||||
return count;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set search result count for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_search_result_count(Connection *conn, Operation *op, int count, int index)
|
||||
+pagedresults_set_search_result_count(Connection *conn, Operation *op, int count, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -525,11 +582,15 @@ pagedresults_set_search_result_count(Connection *conn, Operation *op, int count,
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_search_result_count", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
conn->c_pagedresults.prl_list[index].pr_search_result_count = count;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
rc = 0;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
@@ -537,9 +598,16 @@ pagedresults_set_search_result_count(Connection *conn, Operation *op, int count,
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get search result set size estimate for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
+ bool locked,
|
||||
int index)
|
||||
{
|
||||
int count = 0;
|
||||
@@ -550,11 +618,15 @@ pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
"pagedresults_get_search_result_set_size_estimate",
|
||||
"=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
count = conn->c_pagedresults.prl_list[index].pr_search_result_set_size_estimate;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_search_result_set_size_estimate", "<= %d\n",
|
||||
@@ -562,10 +634,17 @@ pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
return count;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set search result set size estimate for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
int count,
|
||||
+ bool locked,
|
||||
int index)
|
||||
{
|
||||
int rc = -1;
|
||||
@@ -576,11 +655,15 @@ pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
"pagedresults_set_search_result_set_size_estimate",
|
||||
"=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
conn->c_pagedresults.prl_list[index].pr_search_result_set_size_estimate = count;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
rc = 0;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
@@ -589,8 +672,14 @@ pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get with_sort flag for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_get_with_sort(Connection *conn, Operation *op, int index)
|
||||
+pagedresults_get_with_sort(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
int flags = 0;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -599,19 +688,29 @@ pagedresults_get_with_sort(Connection *conn, Operation *op, int index)
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_with_sort", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
flags = conn->c_pagedresults.prl_list[index].pr_flags & CONN_FLAG_PAGEDRESULTS_WITH_SORT;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_with_sort", "<= %d\n", flags);
|
||||
return flags;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set with_sort flag for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, int index)
|
||||
+pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -620,14 +719,18 @@ pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, int index
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_with_sort", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
if (flags & OP_FLAG_SERVER_SIDE_SORTING) {
|
||||
conn->c_pagedresults.prl_list[index].pr_flags |=
|
||||
CONN_FLAG_PAGEDRESULTS_WITH_SORT;
|
||||
}
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
rc = 0;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_set_with_sort", "<= %d\n", rc);
|
||||
@@ -802,10 +905,6 @@ pagedresults_cleanup(Connection *conn, int needlock)
|
||||
rc = 1;
|
||||
}
|
||||
prp->pr_current_be = NULL;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_DestroyLock(prp->pr_mutex);
|
||||
- prp->pr_mutex = NULL;
|
||||
- }
|
||||
memset(prp, '\0', sizeof(PagedResults));
|
||||
}
|
||||
conn->c_pagedresults.prl_count = 0;
|
||||
@@ -840,10 +939,6 @@ pagedresults_cleanup_all(Connection *conn, int needlock)
|
||||
i < conn->c_pagedresults.prl_maxlen;
|
||||
i++) {
|
||||
prp = conn->c_pagedresults.prl_list + i;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_DestroyLock(prp->pr_mutex);
|
||||
- prp->pr_mutex = NULL;
|
||||
- }
|
||||
if (prp->pr_current_be && prp->pr_search_result_set &&
|
||||
prp->pr_current_be->be_search_results_release) {
|
||||
prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
|
||||
@@ -1010,43 +1105,8 @@ op_set_pagedresults(Operation *op)
|
||||
op->o_flags |= OP_FLAG_PAGED_RESULTS;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * pagedresults_lock/unlock -- introduced to protect search results for the
|
||||
- * asynchronous searches. Do not call these functions while the PR conn lock
|
||||
- * is held (e.g. pageresult_lock_get_addr(conn))
|
||||
- */
|
||||
-void
|
||||
-pagedresults_lock(Connection *conn, int index)
|
||||
-{
|
||||
- PagedResults *prp;
|
||||
- if (!conn || (index < 0) || (index >= conn->c_pagedresults.prl_maxlen)) {
|
||||
- return;
|
||||
- }
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
- prp = conn->c_pagedresults.prl_list + index;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_Lock(prp->pr_mutex);
|
||||
- }
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
-}
|
||||
-
|
||||
-void
|
||||
-pagedresults_unlock(Connection *conn, int index)
|
||||
-{
|
||||
- PagedResults *prp;
|
||||
- if (!conn || (index < 0) || (index >= conn->c_pagedresults.prl_maxlen)) {
|
||||
- return;
|
||||
- }
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
- prp = conn->c_pagedresults.prl_list + index;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_Unlock(prp->pr_mutex);
|
||||
- }
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
-}
|
||||
-
|
||||
int
|
||||
-pagedresults_is_abandoned_or_notavailable(Connection *conn, int locked, int index)
|
||||
+pagedresults_is_abandoned_or_notavailable(Connection *conn, bool locked, int index)
|
||||
{
|
||||
PagedResults *prp;
|
||||
int32_t result;
|
||||
@@ -1066,7 +1126,7 @@ pagedresults_is_abandoned_or_notavailable(Connection *conn, int locked, int inde
|
||||
}
|
||||
|
||||
int
|
||||
-pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, int locked)
|
||||
+pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, bool locked)
|
||||
{
|
||||
int rc = -1;
|
||||
Connection *conn = NULL;
|
||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
||||
index 37c643ea9..742e3ee14 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -1597,20 +1597,22 @@ pthread_mutex_t *pageresult_lock_get_addr(Connection *conn);
|
||||
int pagedresults_parse_control_value(Slapi_PBlock *pb, struct berval *psbvp, ber_int_t *pagesize, int *index, Slapi_Backend *be);
|
||||
void pagedresults_set_response_control(Slapi_PBlock *pb, int iscritical, ber_int_t estimate, int curr_search_count, int index);
|
||||
Slapi_Backend *pagedresults_get_current_be(Connection *conn, int index);
|
||||
-int pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, int nolock);
|
||||
-void *pagedresults_get_search_result(Connection *conn, Operation *op, int locked, int index);
|
||||
-int pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, int locked, int index);
|
||||
-int pagedresults_get_search_result_count(Connection *conn, Operation *op, int index);
|
||||
-int pagedresults_set_search_result_count(Connection *conn, Operation *op, int cnt, int index);
|
||||
+int pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, bool locked);
|
||||
+void *pagedresults_get_search_result(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, bool locked, int index);
|
||||
+int pagedresults_get_search_result_count(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_set_search_result_count(Connection *conn, Operation *op, int cnt, bool locked, int index);
|
||||
int pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
+ bool locked,
|
||||
int index);
|
||||
int pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
int cnt,
|
||||
+ bool locked,
|
||||
int index);
|
||||
-int pagedresults_get_with_sort(Connection *conn, Operation *op, int index);
|
||||
-int pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, int index);
|
||||
+int pagedresults_get_with_sort(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, bool locked, int index);
|
||||
int pagedresults_get_unindexed(Connection *conn, Operation *op, int index);
|
||||
int pagedresults_set_unindexed(Connection *conn, Operation *op, int index);
|
||||
int pagedresults_get_sort_result_code(Connection *conn, Operation *op, int index);
|
||||
@@ -1622,15 +1624,13 @@ int pagedresults_cleanup(Connection *conn, int needlock);
|
||||
int pagedresults_is_timedout_nolock(Connection *conn);
|
||||
int pagedresults_reset_timedout_nolock(Connection *conn);
|
||||
int pagedresults_in_use_nolock(Connection *conn);
|
||||
-int pagedresults_free_one(Connection *conn, Operation *op, int index);
|
||||
-int pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex);
|
||||
+int pagedresults_free_one(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, bool locked);
|
||||
int op_is_pagedresults(Operation *op);
|
||||
int pagedresults_cleanup_all(Connection *conn, int needlock);
|
||||
void op_set_pagedresults(Operation *op);
|
||||
-void pagedresults_lock(Connection *conn, int index);
|
||||
-void pagedresults_unlock(Connection *conn, int index);
|
||||
-int pagedresults_is_abandoned_or_notavailable(Connection *conn, int locked, int index);
|
||||
-int pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, int locked);
|
||||
+int pagedresults_is_abandoned_or_notavailable(Connection *conn, bool locked, int index);
|
||||
+int pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, bool locked);
|
||||
|
||||
/*
|
||||
* sort.c
|
||||
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
|
||||
index 9aaa4fb80..f2395cfa2 100644
|
||||
--- a/ldap/servers/slapd/slap.h
|
||||
+++ b/ldap/servers/slapd/slap.h
|
||||
@@ -80,6 +80,10 @@ static char ptokPBE[34] = "Internal (Software) Token ";
|
||||
#include <stdbool.h>
|
||||
#include <time.h> /* For timespec definitions */
|
||||
|
||||
+/* Macros for paged results lock parameter */
|
||||
+#define PR_LOCKED true
|
||||
+#define PR_NOT_LOCKED false
|
||||
+
|
||||
/* Provides our int types and platform specific requirements. */
|
||||
#include <slapi_pal.h>
|
||||
|
||||
@@ -1656,7 +1660,6 @@ typedef struct _paged_results
|
||||
struct timespec pr_timelimit_hr; /* expiry time of this request rel to clock monotonic */
|
||||
int pr_flags;
|
||||
ber_int_t pr_msgid; /* msgid of the request; to abandon */
|
||||
- PRLock *pr_mutex; /* protect each conn structure */
|
||||
} PagedResults;
|
||||
|
||||
/* array of simple paged structure stashed in connection */
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,213 +0,0 @@
|
||||
From 5b6211de252f801ffc088703acc242e47f6273ab Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 9 Jan 2026 11:39:50 +0100
|
||||
Subject: [PATCH] Issue 7172 - Index ordering mismatch after upgrade (#7173)
|
||||
|
||||
Bug Description:
|
||||
Commit daf731f55071d45eaf403a52b63d35f4e699ff28 introduced a regression.
|
||||
After upgrading to a version that adds `integerOrderingMatch` matching
|
||||
rule to `parentid` and `ancestorid` indexes, searches may return empty
|
||||
or incorrect results.
|
||||
|
||||
This happens because the existing index data was created with
|
||||
lexicographic ordering, but the new compare function expects integer
|
||||
ordering. Index lookups fail because the compare function doesn't match
|
||||
the data ordering.
|
||||
The root cause is that `ldbm_instance_create_default_indexes()` calls
|
||||
`attr_index_config()` unconditionally for `parentid` and `ancestorid`
|
||||
indexes, which triggers `ainfo_dup()` to overwrite `ai_key_cmp_fn` on
|
||||
existing indexes. This breaks indexes that were created without the
|
||||
`integerOrderingMatch` matching rule.
|
||||
|
||||
Fix Description:
|
||||
* Call `attr_index_config()` for `parentid` and `ancestorid` indexes
|
||||
only if index config doesn't exist.
|
||||
|
||||
* Add `upgrade_check_id_index_matching_rule()` that logs an error on
|
||||
server startup if `parentid` or `ancestorid` indexes are missing the
|
||||
integerOrderingMatch matching rule, advising administrators to reindex.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7172
|
||||
|
||||
Reviewed by: @tbordaz, @progier389, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 25 ++++--
|
||||
ldap/servers/slapd/upgrade.c | 105 ++++++++++++++++++++++++
|
||||
2 files changed, 122 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 24c00200b..30db99c81 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -191,6 +191,7 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
char *ancestorid_indexes_limit = NULL;
|
||||
char *parentid_indexes_limit = NULL;
|
||||
struct attrinfo *ai = NULL;
|
||||
+ struct attrinfo *index_already_configured = NULL;
|
||||
struct index_idlistsizeinfo *iter;
|
||||
int cookie;
|
||||
int limit;
|
||||
@@ -255,10 +256,14 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
slapi_entry_free(e);
|
||||
}
|
||||
|
||||
- e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
+ ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
+ index_already_configured = ai;
|
||||
+ if (!index_already_configured) {
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
+ ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
+ attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
+ slapi_entry_free(e);
|
||||
+ }
|
||||
|
||||
e = ldbm_instance_init_config_entry("objectclass", "eq", 0, 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
@@ -296,10 +301,14 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* ancestorid is special, there is actually no such attr type
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
+ ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
+ index_already_configured = ai;
|
||||
+ if (!index_already_configured) {
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
+ ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
+ attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
+ slapi_entry_free(e);
|
||||
+ }
|
||||
}
|
||||
|
||||
slapi_ch_free_string(&ancestorid_indexes_limit);
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index 0d1e2abf4..43906c1af 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -279,6 +279,107 @@ upgrade_205_fixup_repl_dep(void)
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
+ * matching rule.
|
||||
+ *
|
||||
+ * This function logs a warning if we detect this condition, advising
|
||||
+ * the administrator to reindex the affected attributes.
|
||||
+ */
|
||||
+static upgrade_status
|
||||
+upgrade_check_id_index_matching_rule(void)
|
||||
+{
|
||||
+ struct slapi_pblock *pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **backends = NULL;
|
||||
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
+ const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
+ const char *attrs_to_check[] = {"parentid", "ancestorid", NULL};
|
||||
+ upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
+
|
||||
+ /* Search for all backend instances */
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ pb, be_base_dn,
|
||||
+ LDAP_SCOPE_ONELEVEL,
|
||||
+ be_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
+
|
||||
+ if (backends) {
|
||||
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
+ if (!be_name) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Check each attribute that should have integerOrderingMatch */
|
||||
+ for (size_t attr_idx = 0; attrs_to_check[attr_idx] != NULL; attr_idx++) {
|
||||
+ const char *attr_name = attrs_to_check[attr_idx];
|
||||
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **idx_entries = NULL;
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,%s",
|
||||
+ attr_name, be_name, be_base_dn);
|
||||
+ char *idx_filter = "(objectclass=nsIndex)";
|
||||
+ PRBool has_matching_rule = PR_FALSE;
|
||||
+
|
||||
+ if (!idx_dn) {
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ idx_pb, idx_dn,
|
||||
+ LDAP_SCOPE_BASE,
|
||||
+ idx_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(idx_pb);
|
||||
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
+
|
||||
+ if (idx_entries && idx_entries[0]) {
|
||||
+ /* Index exists, check if it has integerOrderingMatch */
|
||||
+ Slapi_Attr *mr_attr = NULL;
|
||||
+ if (slapi_entry_attr_find(idx_entries[0], "nsMatchingRule", &mr_attr) == 0) {
|
||||
+ Slapi_Value *sval = NULL;
|
||||
+ int idx;
|
||||
+ for (idx = slapi_attr_first_value(mr_attr, &sval);
|
||||
+ idx != -1;
|
||||
+ idx = slapi_attr_next_value(mr_attr, idx, &sval)) {
|
||||
+ const struct berval *bval = slapi_value_get_berval(sval);
|
||||
+ if (bval && bval->bv_val &&
|
||||
+ strcasecmp(bval->bv_val, "integerOrderingMatch") == 0) {
|
||||
+ has_matching_rule = PR_TRUE;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!has_matching_rule) {
|
||||
+ /* Index exists but doesn't have integerOrderingMatch, log a warning */
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_check_id_index_matching_rule",
|
||||
+ "Index '%s' in backend '%s' is missing 'nsMatchingRule: integerOrderingMatch'. "
|
||||
+ "Incorrectly configured system indexes can lead to poor search performance, replication issues, and other operational problems. "
|
||||
+ "To fix this, add the matching rule and reindex: "
|
||||
+ "dsconf <instance> backend index set --add-mr integerOrderingMatch --attr %s %s && "
|
||||
+ "dsconf <instance> backend index reindex --attr %s %s. "
|
||||
+ "WARNING: Reindexing can be resource-intensive and may impact server performance on a live system. "
|
||||
+ "Consider scheduling reindexing during maintenance windows or periods of low activity.\n",
|
||||
+ attr_name, be_name, attr_name, be_name, attr_name, be_name);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(idx_pb);
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return uresult;
|
||||
+}
|
||||
+
|
||||
upgrade_status
|
||||
upgrade_server(void)
|
||||
{
|
||||
@@ -306,6 +407,10 @@ upgrade_server(void)
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
|
||||
+ if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
+ return UPGRADE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
From 56dde3de7c90d9d81f4588a5afda2cfa05bdb20f Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 24 Apr 2026 11:19:04 +0200
|
||||
Subject: [PATCH 4/8] Issue 7437 - LeakSanitizer: memory leaks in CoS cache
|
||||
error paths (#7438)
|
||||
|
||||
Description:
|
||||
Fix memory leaks in CoS plugin when a CoS definition fails validation
|
||||
or is incomplete.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7437
|
||||
|
||||
Reviewed by: @mreynolds389 (Thanks!)
|
||||
---
|
||||
ldap/servers/plugins/cos/cos_cache.c | 16 ++++++++++++++++
|
||||
1 file changed, 16 insertions(+)
|
||||
|
||||
diff --git a/ldap/servers/plugins/cos/cos_cache.c b/ldap/servers/plugins/cos/cos_cache.c
|
||||
index d8ebbbe3d..7074cb60a 100644
|
||||
--- a/ldap/servers/plugins/cos/cos_cache.c
|
||||
+++ b/ldap/servers/plugins/cos/cos_cache.c
|
||||
@@ -1007,6 +1007,14 @@ cos_dn_defs_cb(Slapi_Entry *e, void *callback_data)
|
||||
cos_cache_del_attrval_list(&pCosSpecifier);
|
||||
if (pCosAttribute)
|
||||
cos_cache_del_attrval_list(&pCosAttribute);
|
||||
+ if (pCosOverrides)
|
||||
+ cos_cache_del_attrval_list(&pCosOverrides);
|
||||
+ if (pCosOperational)
|
||||
+ cos_cache_del_attrval_list(&pCosOperational);
|
||||
+ if (pCosMerge)
|
||||
+ cos_cache_del_attrval_list(&pCosMerge);
|
||||
+ if (pCosOpDefault)
|
||||
+ cos_cache_del_attrval_list(&pCosOpDefault);
|
||||
if (pDn)
|
||||
cos_cache_del_attrval_list(&pDn);
|
||||
}
|
||||
@@ -1430,6 +1438,14 @@ out:
|
||||
cos_cache_del_attrval_list(spec);
|
||||
if (pAttrs)
|
||||
cos_cache_del_attrval_list(pAttrs);
|
||||
+ if (pOverrides)
|
||||
+ cos_cache_del_attrval_list(pOverrides);
|
||||
+ if (pOperational)
|
||||
+ cos_cache_del_attrval_list(pOperational);
|
||||
+ if (pCosMerge)
|
||||
+ cos_cache_del_attrval_list(pCosMerge);
|
||||
+ if (pCosOpDefault)
|
||||
+ cos_cache_del_attrval_list(pCosOpDefault);
|
||||
}
|
||||
|
||||
slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "<-- cos_cache_add_defn\n");
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
From cae81cde000df9a28733c673ee7c9b8292d69ef3 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 12 Jan 2026 10:58:02 +0100
|
||||
Subject: [PATCH] Issue 7172 - (2nd) Index ordering mismatch after upgrade
|
||||
(#7180)
|
||||
|
||||
Commit 742c12e0247ab64e87da000a4de2f3e5c99044ab introduced a regression
|
||||
where the check to skip creating parentid/ancestorid indexes if they
|
||||
already exist was incorrect.
|
||||
The `ainfo_get()` function falls back to returning
|
||||
LDBM_PSEUDO_ATTR_DEFAULT attrinfo when the requested attribute is not
|
||||
found.
|
||||
Since LDBM_PSEUDO_ATTR_DEFAULT is created before the ancestorid check,
|
||||
`ainfo_get()` returns LDBM_PSEUDO_ATTR_DEFAULT instead of NULL, causing
|
||||
the ancestorid index creation to be skipped entirely.
|
||||
|
||||
When operations later try to use the ancestorid index, they fall back to
|
||||
LDBM_PSEUDO_ATTR_DEFAULT, and attempting to open the .default dbi
|
||||
mid-transaction fails with MDB_NOTFOUND (-30798).
|
||||
|
||||
Fix Description:
|
||||
Instead of just checking if `ainfo_get()` returns non-NULL, verify that
|
||||
the returned attrinfo is actually for the requested attribute.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7172
|
||||
|
||||
Reviewed by: @tbordaz (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 30db99c81..23bd70243 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -191,7 +191,7 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
char *ancestorid_indexes_limit = NULL;
|
||||
char *parentid_indexes_limit = NULL;
|
||||
struct attrinfo *ai = NULL;
|
||||
- struct attrinfo *index_already_configured = NULL;
|
||||
+ int index_already_configured = 0;
|
||||
struct index_idlistsizeinfo *iter;
|
||||
int cookie;
|
||||
int limit;
|
||||
@@ -257,7 +257,8 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
}
|
||||
|
||||
ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
- index_already_configured = ai;
|
||||
+ /* Check if the attrinfo is actually for parentid, not a fallback to .default */
|
||||
+ index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_PARENTID_STR) == 0);
|
||||
if (!index_already_configured) {
|
||||
e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
@@ -302,7 +303,8 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
- index_already_configured = ai;
|
||||
+ /* Check if the attrinfo is actually for ancestorid, not a fallback to .default */
|
||||
+ index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_ANCESTORID_STR) == 0);
|
||||
if (!index_already_configured) {
|
||||
e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
169
0005-Issue-7372-Reindex-adds-tombstones-to-ancestorid-cau.patch
Normal file
169
0005-Issue-7372-Reindex-adds-tombstones-to-ancestorid-cau.patch
Normal file
@ -0,0 +1,169 @@
|
||||
From f34eff84824f64470a8c181a0f7a8492695eaab3 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 5 May 2026 11:13:47 +0200
|
||||
Subject: [PATCH 5/8] Issue 7372 - Reindex adds tombstones to ancestorid
|
||||
causing export failures (#7373)
|
||||
|
||||
Bug Description:
|
||||
During import/reindex, tombstone entries are added to the ancestorid
|
||||
index. When those tombstones are later purged, the purge thread skips
|
||||
updates to ancestorid index. This leaves stale entry IDs in the
|
||||
ancestorid index referencing entries in id2entry that are no longer
|
||||
there.
|
||||
|
||||
Fix Description:
|
||||
Skip tombstone entries when building the ancestorid index during
|
||||
import/reindex.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7372
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
.../export/export_reindex_tombstone_test.py | 107 ++++++++++++++++++
|
||||
.../slapd/back-ldbm/db-bdb/bdb_ldif2db.c | 2 +-
|
||||
.../back-ldbm/db-mdb/mdb_import_threads.c | 2 +-
|
||||
3 files changed, 109 insertions(+), 2 deletions(-)
|
||||
create mode 100644 dirsrvtests/tests/suites/export/export_reindex_tombstone_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/export/export_reindex_tombstone_test.py b/dirsrvtests/tests/suites/export/export_reindex_tombstone_test.py
|
||||
new file mode 100644
|
||||
index 000000000..3cb6e6178
|
||||
--- /dev/null
|
||||
+++ b/dirsrvtests/tests/suites/export/export_reindex_tombstone_test.py
|
||||
@@ -0,0 +1,107 @@
|
||||
+# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+# Copyright (C) 2026 Red Hat, Inc.
|
||||
+# All rights reserved.
|
||||
+#
|
||||
+# License: GPL (version 3 or any later version).
|
||||
+# See LICENSE for details.
|
||||
+# --- END COPYRIGHT BLOCK ---
|
||||
+
|
||||
+import os
|
||||
+import time
|
||||
+import pytest
|
||||
+import ldap
|
||||
+from lib389.idm.user import UserAccounts
|
||||
+from lib389.idm.domain import Domain
|
||||
+from lib389.tombstone import Tombstones
|
||||
+from lib389.topologies import topology_m2 as topo_m2
|
||||
+from lib389._constants import DEFAULT_SUFFIX, DEFAULT_BENAME, ErrorLog
|
||||
+from lib389.utils import *
|
||||
+from lib389.backend import Backends
|
||||
+from lib389.replica import Replicas, ReplicationManager
|
||||
+
|
||||
+pytestmark = pytest.mark.tier1
|
||||
+
|
||||
+
|
||||
+def test_export_after_reindex_and_tombstone_purge(topo_m2):
|
||||
+ """Test that export with -s works after reindex and tombstone purge.
|
||||
+
|
||||
+ :id: 8c5cb603-1f43-46ad-9935-5f518d6d7fe0
|
||||
+ :setup: Two supplier replication topology
|
||||
+ :steps:
|
||||
+ 1. Add entries under ou=people on S1, wait for replication to S2
|
||||
+ 2. Delete the entries on S1, wait for replication
|
||||
+ 3. Perform additional modifications to advance the RUV
|
||||
+ 4. Reindex S1
|
||||
+ 5. Configure aggressive tombstone purging and wait for tombstones to be purged on S1
|
||||
+ 6. Export ou=people,dc=example,dc=com with -s
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. Success
|
||||
+ 6. Export completes successfully
|
||||
+ """
|
||||
+ S1 = topo_m2.ms["supplier1"]
|
||||
+ S2 = topo_m2.ms["supplier2"]
|
||||
+ PEOPLE = f"ou=people,{DEFAULT_SUFFIX}"
|
||||
+
|
||||
+ users = UserAccounts(S1, DEFAULT_SUFFIX, rdn="ou=people")
|
||||
+ test_users = []
|
||||
+ for i in range(5):
|
||||
+ user = users.create_test_user(uid=1234 + i)
|
||||
+ test_users.append(user)
|
||||
+ log.info("Added 5 test entries under ou=people")
|
||||
+
|
||||
+ repl = ReplicationManager(DEFAULT_SUFFIX)
|
||||
+ repl.wait_for_replication(S1, S2)
|
||||
+
|
||||
+ for user in test_users:
|
||||
+ user.delete()
|
||||
+ log.info("Deleted 5 test entries")
|
||||
+
|
||||
+ repl.wait_for_replication(S1, S2)
|
||||
+
|
||||
+ domain = Domain(S1, DEFAULT_SUFFIX)
|
||||
+ for i in range(10):
|
||||
+ domain.replace("description", f"advancing RUV {i}")
|
||||
+ repl.wait_for_replication(S1, S2)
|
||||
+
|
||||
+ domain2 = Domain(S2, DEFAULT_SUFFIX)
|
||||
+ for i in range(10):
|
||||
+ domain2.replace("description", f"advancing RUV from S2 {i}")
|
||||
+ repl.wait_for_replication(S2, S1)
|
||||
+ log.info("RUV advanced on both suppliers")
|
||||
+
|
||||
+ S1.stop()
|
||||
+ S1.db2index(DEFAULT_BENAME)
|
||||
+ log.info("Reindex completed on S1")
|
||||
+ S1.start()
|
||||
+
|
||||
+ replica = Replicas(S1).get(DEFAULT_SUFFIX)
|
||||
+ replica.replace("nsDS5ReplicaPurgeDelay", "1")
|
||||
+ replica.replace("nsDS5ReplicaTombstonePurgeInterval", "1")
|
||||
+
|
||||
+ S1.config.loglevel((ErrorLog.REPLICA,), "error")
|
||||
+
|
||||
+ log.info("Waiting for tombstone purge on S1...")
|
||||
+ tombstones = Tombstones(S1, PEOPLE)
|
||||
+ for attempt in range(60):
|
||||
+ time.sleep(2)
|
||||
+ ts_list = tombstones.list()
|
||||
+ if len(ts_list) == 0:
|
||||
+ log.info(f"All tombstones purged after {(attempt + 1) * 2}s")
|
||||
+ break
|
||||
+ log.info(f"Attempt {attempt + 1}: {len(ts_list)} tombstones remaining")
|
||||
+ else:
|
||||
+ pytest.fail("Tombstones not purged after 120s")
|
||||
+
|
||||
+ S1.deleteErrorLogs()
|
||||
+
|
||||
+ backends = Backends(S1)
|
||||
+ task = backends.export_ldif(be_names=[DEFAULT_BENAME], include_suffixes=[PEOPLE])
|
||||
+ task.wait()
|
||||
+ assert task.is_complete()
|
||||
+ assert task.get_exit_code() == 0
|
||||
+
|
||||
+ log.info("Export after reindex + tombstone purge succeeded")
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c
|
||||
index f5127cc66..5138646f6 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c
|
||||
@@ -2053,7 +2053,7 @@ bdb_db2index(Slapi_PBlock *pb)
|
||||
/*
|
||||
* Update the ancestorid and entryrdn index
|
||||
*/
|
||||
- if (!entryrdn_get_noancestorid() && (index_ext & DB2INDEX_ANCESTORID)) {
|
||||
+ if (!entryrdn_get_noancestorid() && (index_ext & DB2INDEX_ANCESTORID) && !istombstone) {
|
||||
rc = ldbm_ancestorid_index_entry(be, ep, BE_INDEX_ADD, NULL);
|
||||
if (rc != 0) {
|
||||
slapi_log_err(SLAPI_LOG_ERR,
|
||||
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 d37f223fd..35965df85 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
|
||||
@@ -3087,7 +3087,7 @@ process_entryrdn(backentry *ep, WorkerQueueData_t *wqelmnt)
|
||||
add_update_entry_operational_attributes(ep, 0);
|
||||
}
|
||||
|
||||
- if (ctx->ancestorid && wqelmnt->entry_info) {
|
||||
+ if (ctx->ancestorid && wqelmnt->entry_info && wqelmnt->dnrc != DNRC_TOMBSTONE) {
|
||||
/* Update ancestorids */
|
||||
wqd.dbi = ctx->ancestorid->dbi;
|
||||
for (n=0; n<wqelmnt->entry_info[INFO_IDX_NB_ANCESTORS]; n++) {
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,235 +0,0 @@
|
||||
From c5aab4f8ba822572daa9ef69a0109577bf2147e1 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 20 Jan 2026 09:52:47 +0100
|
||||
Subject: [PATCH] Issue 7189 - DSBLE0007 generates incorrect remediation
|
||||
commands for scan limits
|
||||
|
||||
Bug Description:
|
||||
|
||||
The generated dsconf commands for fixing missing system indexes had two issues:
|
||||
|
||||
1. The --add-scanlimit value was not quoted, causing the shell to interpret
|
||||
"limit=5000 type=eq flags=AND" as multiple arguments instead of a single
|
||||
value, resulting in "unrecognized arguments: type=eq flags=AND" error.
|
||||
|
||||
2. When both matching rule and scanlimit were missing, two separate commands
|
||||
were generated where the second would fail because the matching rule was
|
||||
already added by the first command.
|
||||
|
||||
Fix Description:
|
||||
|
||||
1. Quote the scanlimit value in all remediation commands
|
||||
|
||||
2. Combine matching rule and scanlimit fixes into a single command when
|
||||
both are missing for the same index instead of expected_scanlimit)
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7189
|
||||
|
||||
Reviewed by: @progier389, @droideck (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 126 ++++++++++++++++++
|
||||
src/lib389/lib389/backend.py | 39 +++---
|
||||
2 files changed, 147 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 6293340ca..5eadf6283 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -405,6 +405,132 @@ def test_retrocl_plugin_missing_matching_rule(topology_st, retrocl_plugin_enable
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
|
||||
+def test_missing_scanlimit(topology_st, log_buffering_enabled):
|
||||
+ """Check if healthcheck returns DSBLE0007 code when parentId index is missing scanlimit
|
||||
+
|
||||
+ :id: 40e1bf6a-2397-459b-bdf3-f787ca118b86
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Remove nsIndexIDListScanLimit from parentId index
|
||||
+ 3. Use healthcheck without --json option
|
||||
+ 4. Use healthcheck with --json option
|
||||
+ 5. Verify the remediation command has properly quoted scanlimit
|
||||
+ 6. Re-add the scanlimit
|
||||
+ 7. Use healthcheck without --json option
|
||||
+ 8. Use healthcheck with --json option
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. healthcheck reports DSBLE0007 code and related details
|
||||
+ 4. healthcheck reports DSBLE0007 code and related details
|
||||
+ 5. The scanlimit value is quoted in the remediation command
|
||||
+ 6. Success
|
||||
+ 7. healthcheck reports no issues found
|
||||
+ 8. healthcheck reports no issues found
|
||||
+ """
|
||||
+
|
||||
+ RET_CODE = "DSBLE0007"
|
||||
+ PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+ SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+
|
||||
+ log.info("Remove nsIndexIDListScanLimit from parentId index")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=RET_CODE)
|
||||
+
|
||||
+ # Verify the remediation command has properly quoted scanlimit
|
||||
+ args = FakeArgs()
|
||||
+ args.instance = standalone.serverid
|
||||
+ args.verbose = standalone.verbose
|
||||
+ args.list_errors = False
|
||||
+ args.list_checks = False
|
||||
+ args.exclude_check = []
|
||||
+ args.check = ["backends"]
|
||||
+ args.dry_run = False
|
||||
+ args.json = False
|
||||
+ health_check_run(standalone, topology_st.logcap.log, args)
|
||||
+ # Check that the scanlimit is quoted in the output
|
||||
+ assert topology_st.logcap.contains('--add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
+ log.info("Verified scanlimit is properly quoted in remediation command")
|
||||
+ topology_st.logcap.flush()
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=RET_CODE)
|
||||
+
|
||||
+ log.info("Re-add the nsIndexIDListScanLimit")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
+
|
||||
+
|
||||
+def test_missing_matching_rule_and_scanlimit(topology_st, log_buffering_enabled):
|
||||
+ """Check if healthcheck generates a single combined command when both matching rule and scanlimit are missing
|
||||
+
|
||||
+ :id: af8214ad-5e4c-422a-8f74-3e99227551df
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index
|
||||
+ 3. Use healthcheck and verify a single combined command is generated
|
||||
+ 4. Re-add the matching rule and scanlimit
|
||||
+ 5. Use healthcheck without --json option
|
||||
+ 6. Use healthcheck with --json option
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. healthcheck reports DSBLE0007 and generates a single command with both --add-mr and --add-scanlimit
|
||||
+ 4. Success
|
||||
+ 5. healthcheck reports no issues found
|
||||
+ 6. healthcheck reports no issues found
|
||||
+ """
|
||||
+
|
||||
+ RET_CODE = "DSBLE0007"
|
||||
+ PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+ SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+
|
||||
+ log.info("Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.remove("nsMatchingRule", "integerOrderingMatch")
|
||||
+ parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ # Run healthcheck and verify combined command
|
||||
+ args = FakeArgs()
|
||||
+ args.instance = standalone.serverid
|
||||
+ args.verbose = standalone.verbose
|
||||
+ args.list_errors = False
|
||||
+ args.list_checks = False
|
||||
+ args.exclude_check = []
|
||||
+ args.check = ["backends"]
|
||||
+ args.dry_run = False
|
||||
+ args.json = False
|
||||
+ health_check_run(standalone, topology_st.logcap.log, args)
|
||||
+
|
||||
+ # Verify DSBLE0007 is reported
|
||||
+ assert topology_st.logcap.contains(RET_CODE)
|
||||
+ log.info("healthcheck returned code: %s" % RET_CODE)
|
||||
+
|
||||
+ # Verify a single combined command is generated with both --add-mr and --add-scanlimit
|
||||
+ assert topology_st.logcap.contains('--add-mr integerOrderingMatch --add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
+ log.info("Verified combined command with both --add-mr and --add-scanlimit")
|
||||
+
|
||||
+ topology_st.logcap.flush()
|
||||
+
|
||||
+ log.info("Re-add the integerOrderingMatch matching rule and scanlimit")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.add("nsMatchingRule", "integerOrderingMatch")
|
||||
+ parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
+
|
||||
+
|
||||
def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
"""Check if healthcheck returns DSBLE0007 code when multiple system indexes are missing
|
||||
|
||||
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
||||
index 024de2adb..d3c9ccf35 100644
|
||||
--- a/src/lib389/lib389/backend.py
|
||||
+++ b/src/lib389/lib389/backend.py
|
||||
@@ -678,7 +678,7 @@ class Backend(DSLdapObject):
|
||||
if expected_config.get('matching_rule'):
|
||||
cmd += f" --matching-rule {expected_config['matching_rule']}"
|
||||
if expected_config.get('scanlimit'):
|
||||
- cmd += f" --add-scanlimit {expected_config['scanlimit']}"
|
||||
+ cmd += f" --add-scanlimit \"{expected_config['scanlimit']}\""
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name) # New index needs reindexing
|
||||
else:
|
||||
@@ -700,28 +700,31 @@ class Backend(DSLdapObject):
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name)
|
||||
|
||||
- # Check matching rules
|
||||
+ # Check matching rules and scanlimit together to generate a single combined command
|
||||
expected_mr = expected_config.get('matching_rule')
|
||||
+ expected_scanlimit = expected_config.get('scanlimit')
|
||||
+
|
||||
+ missing_mr = False
|
||||
if expected_mr:
|
||||
actual_mrs_lower = [mr.lower() for mr in actual_mrs]
|
||||
if expected_mr.lower() not in actual_mrs_lower:
|
||||
discrepancies.append(f"Index {attr_name} missing matching rule: {expected_mr}")
|
||||
- # Add the missing matching rule
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-mr {expected_mr}"
|
||||
- remediation_commands.append(cmd)
|
||||
- reindex_attrs.add(attr_name)
|
||||
-
|
||||
- # Check fine grain definitions for parentid ONLY
|
||||
- expected_scanlimit = expected_config.get('scanlimit')
|
||||
- if (attr_name.lower() == "parentid") and expected_scanlimit and (len(actual_scanlimit) == 0):
|
||||
- discrepancies.append(f"Index {attr_name} missing fine grain definition of IDs limit: {expected_mr}")
|
||||
- # Add the missing scanlimit
|
||||
- if expected_mr:
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-mr {expected_mr} --add-scanlimit {expected_scanlimit}"
|
||||
- else:
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-scanlimit {expected_scanlimit}"
|
||||
- remediation_commands.append(cmd)
|
||||
- reindex_attrs.add(attr_name)
|
||||
+ missing_mr = True
|
||||
+
|
||||
+ missing_scanlimit = False
|
||||
+ if expected_scanlimit and (len(actual_scanlimit) == 0):
|
||||
+ discrepancies.append(f"Index {attr_name} missing fine grain definition of IDs limit: {expected_scanlimit}")
|
||||
+ missing_scanlimit = True
|
||||
+
|
||||
+ # Generate a single combined command for all missing items
|
||||
+ if missing_mr or missing_scanlimit:
|
||||
+ cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name}"
|
||||
+ if missing_mr:
|
||||
+ cmd += f" --add-mr {expected_mr}"
|
||||
+ if missing_scanlimit:
|
||||
+ cmd += f" --add-scanlimit \"{expected_scanlimit}\""
|
||||
+ remediation_commands.append(cmd)
|
||||
+ reindex_attrs.add(attr_name)
|
||||
|
||||
except Exception as e:
|
||||
self._log.debug(f"_lint_system_indexes - Error checking index {attr_name}: {e}")
|
||||
--
|
||||
2.52.0
|
||||
|
||||
199
0006-Issue-7327-dsctl-healthcheck-DSMOLE0001-inaccurate-r.patch
Normal file
199
0006-Issue-7327-dsctl-healthcheck-DSMOLE0001-inaccurate-r.patch
Normal file
@ -0,0 +1,199 @@
|
||||
From af0216be875e3580f5c916c7ca9ab63db4df32f7 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Wed, 8 Apr 2026 23:56:15 +0100
|
||||
Subject: [PATCH 6/8] Issue 7327 - dsctl healthcheck DSMOLE0001 inaccurate
|
||||
recommendations with multiple backends (#7328)
|
||||
|
||||
Description:
|
||||
The dsctl healthcheck tool generates incorrect recommendations for the MO plugin when
|
||||
multiple backends are present. This commonly occurs in IPA environments where both
|
||||
userroot and ipaca backends exist. Healthcheck incorrectly suggests indexing attributes
|
||||
for backends that are not within the MemberOf plugin scope.
|
||||
|
||||
Fix:
|
||||
Determine the MO plugin scope while iterating over backends and only generate
|
||||
recommendations for backends that fall within that scope.
|
||||
|
||||
Removed references to nsslapd-plugincontainerscope as we dont use it.
|
||||
|
||||
Fixes:
|
||||
https://github.com/389ds/389-ds-base/issues/7327
|
||||
|
||||
Reviewed by: @droideck (Thank you)
|
||||
---
|
||||
.../memberof_include_scopes_test.py | 86 +++++++++++++++++++
|
||||
src/lib389/lib389/plugins.py | 30 ++++---
|
||||
2 files changed, 106 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py b/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
|
||||
index 347eb880f..8cdf94f3c 100644
|
||||
--- a/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
|
||||
@@ -10,6 +10,7 @@ import pytest
|
||||
import os
|
||||
import ldap
|
||||
import time
|
||||
+from lib389.backend import Backends
|
||||
from lib389.utils import ensure_str
|
||||
from lib389.topologies import topology_st as topo
|
||||
from lib389._constants import *
|
||||
@@ -17,6 +18,8 @@ from lib389.plugins import MemberOfPlugin, ReferentialIntegrityPlugin
|
||||
from lib389.idm.user import UserAccount, UserAccounts
|
||||
from lib389.idm.group import Group, Groups
|
||||
from lib389.idm.nscontainer import nsContainers
|
||||
+from lib389.idm.domain import Domain
|
||||
+from lib389.idm.organizationalunit import OrganizationalUnit
|
||||
|
||||
SUBTREE_1 = 'cn=sub1,%s' % SUFFIX
|
||||
SUBTREE_2 = 'cn=sub2,%s' % SUFFIX
|
||||
@@ -131,6 +134,89 @@ def test_multiple_scopes(topo):
|
||||
assert not group.present("member", INCLUDED_USER)
|
||||
assert not group.present("member", EXCLUDED_USER)
|
||||
|
||||
+def test_memberof_scope_multiple_backends(topology_st):
|
||||
+ """Test memberOf plugin correctly handles multiple backends with different scopes
|
||||
+
|
||||
+ :id: 96419128-70a4-4a81-943e-8d1e9c92f241
|
||||
+ :setup: Instance with multiple backends
|
||||
+ :steps:
|
||||
+ 1. Create out of scope backend
|
||||
+ 2. Create domain and OU's for out of scope backend
|
||||
+ 3. Create groups and users in both backends
|
||||
+ 4. Enable MO plugin and set scope to backend 1
|
||||
+ 5. Add users to groups in both backends
|
||||
+ 6. Verify in scope user has memberOf attr value of in scope group dn
|
||||
+ 7. Verify out scope user has an empty memberOf attr value
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. Success, trigger MO update
|
||||
+ 6. Success
|
||||
+ 7. Success
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+ outscope_suffix = 'dc=other,dc=org'
|
||||
+
|
||||
+ # Create a backend 2
|
||||
+ backends = Backends(inst)
|
||||
+ outscope_be = backends.create(properties={
|
||||
+ 'cn': 'testBackend',
|
||||
+ 'nsslapd-suffix': outscope_suffix
|
||||
+ })
|
||||
+
|
||||
+ # Create domain entry for backend 2
|
||||
+ domain = Domain(inst, outscope_suffix)
|
||||
+ domain.create(properties={
|
||||
+ 'dc': 'other'
|
||||
+ })
|
||||
+
|
||||
+ # Create OUs for backend 2
|
||||
+ people_ou = OrganizationalUnit(inst, f'ou=People,{outscope_suffix}')
|
||||
+ people_ou.create(properties={'ou': 'People'})
|
||||
+ groups_ou = OrganizationalUnit(inst, f'ou=Groups,{outscope_suffix}')
|
||||
+ groups_ou.create(properties={'ou': 'Groups'})
|
||||
+
|
||||
+ # Create test groups and users
|
||||
+ inscope_users = UserAccounts(inst, 'dc=example,dc=com')
|
||||
+ inscope_groups = Groups(inst, 'dc=example,dc=com')
|
||||
+ inscope_user = inscope_users.create_test_user(uid=1000)
|
||||
+ inscope_group = inscope_groups.create(properties={'cn': 'testgroup1'}) # NO MEMBERS initially
|
||||
+
|
||||
+ outscope_users = UserAccounts(inst, outscope_suffix)
|
||||
+ outscope_groups = Groups(inst, outscope_suffix)
|
||||
+ outscope_user = outscope_users.create_test_user(uid=2000)
|
||||
+ outscope_group = outscope_groups.create(properties={'cn': 'testgroup2'}) # NO MEMBERS initially
|
||||
+
|
||||
+ # Configure memberof plugin with backend 1 scope
|
||||
+ memberof = MemberOfPlugin(inst)
|
||||
+ memberof.enable()
|
||||
+ memberof.replace('memberOfEntryScope', 'dc=example,dc=com')
|
||||
+ inst.restart()
|
||||
+
|
||||
+ # Trigger memberof processing
|
||||
+ inscope_group.add('member', inscope_user.dn)
|
||||
+ outscope_group.add('member', outscope_user.dn)
|
||||
+
|
||||
+ # Sleep for a bit
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ # In scope user should have memberOf attribute
|
||||
+ inscope_user_entry = inscope_user.get_attrs_vals_utf8(['memberOf'])
|
||||
+ assert inscope_user_entry['memberOf'] == [inscope_group.dn]
|
||||
+
|
||||
+ # Out of scope user should not have memberOf attribute
|
||||
+ outscope_user_entry = outscope_user.get_attrs_vals_utf8(['memberOf'])
|
||||
+ assert outscope_user_entry['memberOf'] == []
|
||||
+
|
||||
+ # Cleanup
|
||||
+ inscope_group.delete()
|
||||
+ inscope_user.delete()
|
||||
+ outscope_group.delete()
|
||||
+ outscope_user.delete()
|
||||
+ outscope_be.delete()
|
||||
+
|
||||
if __name__ == '__main__':
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py
|
||||
index 483d86659..0c0148119 100644
|
||||
--- a/src/lib389/lib389/plugins.py
|
||||
+++ b/src/lib389/lib389/plugins.py
|
||||
@@ -794,16 +794,21 @@ class MemberOfPlugin(Plugin):
|
||||
from lib389.backend import Backends
|
||||
backends = Backends(self._instance).list()
|
||||
attrs = self.get_attr_vals_utf8_l("memberofgroupattr")
|
||||
- container = self.get_attr_val_utf8_l("nsslapd-plugincontainerscope")
|
||||
+ scopes = self.get_attr_vals_utf8_l("memberofentryscope")
|
||||
for backend in backends:
|
||||
suffix = backend.get_attr_val_utf8_l('nsslapd-suffix')
|
||||
if suffix == "cn=changelog":
|
||||
# Always skip retro changelog
|
||||
continue
|
||||
- if container is not None:
|
||||
- # Check if this backend is in the scope
|
||||
- if not container.endswith(suffix):
|
||||
- # skip this backend that is not in the scope
|
||||
+ if scopes:
|
||||
+ # Is this backend suffix in scope
|
||||
+ in_scope = False
|
||||
+ for scope in scopes:
|
||||
+ if scope.endswith(suffix) or suffix.endswith(scope):
|
||||
+ in_scope = True
|
||||
+ break
|
||||
+
|
||||
+ if not in_scope:
|
||||
continue
|
||||
indexes = backend.get_indexes()
|
||||
for attr in attrs:
|
||||
@@ -842,16 +847,21 @@ class MemberOfPlugin(Plugin):
|
||||
from lib389.backend import Backends
|
||||
backends = Backends(self._instance).list()
|
||||
membership_attrs = ['member', 'uniquemember']
|
||||
- container = self.get_attr_val_utf8_l("nsslapd-plugincontainerscope")
|
||||
+ scopes = self.get_attr_vals_utf8_l("memberofentryscope")
|
||||
for backend in backends:
|
||||
suffix = backend.get_attr_val_utf8_l('nsslapd-suffix')
|
||||
if suffix == "cn=changelog":
|
||||
# Always skip retro changelog
|
||||
continue
|
||||
- if container is not None:
|
||||
- # Check if this backend is in the scope
|
||||
- if not container.endswith(suffix):
|
||||
- # skip this backend that is not in the scope
|
||||
+ if scopes:
|
||||
+ # Is this backend suffix in scope
|
||||
+ in_scope = False
|
||||
+ for scope in scopes:
|
||||
+ if scope.endswith(suffix) or suffix.endswith(scope):
|
||||
+ in_scope = True
|
||||
+ break
|
||||
+
|
||||
+ if not in_scope:
|
||||
continue
|
||||
indexes = backend.get_indexes()
|
||||
for attr in membership_attrs:
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
From 327ebf93decaa19bd3919afaeba0dc122eef1990 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 12 Jan 2026 13:53:05 -0500
|
||||
Subject: [PATCH] Issue 7184 - argparse.HelpFormatter _format_actions_usage()
|
||||
is deprecated
|
||||
|
||||
Description:
|
||||
|
||||
_format_actions_usage() was removed in python 3.15. Instead we can use
|
||||
_get_actions_usage_parts() but it also behaves differently between
|
||||
python 3.14 and 3.15 so we need special handling.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7184
|
||||
|
||||
Reviewed by: spichugi(Thanks!)
|
||||
---
|
||||
src/lib389/lib389/cli_base/__init__.py | 15 ++++++++++++++-
|
||||
1 file changed, 14 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_base/__init__.py b/src/lib389/lib389/cli_base/__init__.py
|
||||
index 06b8f9964..f1055aadc 100644
|
||||
--- a/src/lib389/lib389/cli_base/__init__.py
|
||||
+++ b/src/lib389/lib389/cli_base/__init__.py
|
||||
@@ -413,7 +413,20 @@ class CustomHelpFormatter(argparse.HelpFormatter):
|
||||
|
||||
def _format_usage(self, usage, actions, groups, prefix):
|
||||
usage = super(CustomHelpFormatter, self)._format_usage(usage, actions, groups, prefix)
|
||||
- formatted_options = self._format_actions_usage(parent_arguments, [])
|
||||
+
|
||||
+ if sys.version_info < (3, 13):
|
||||
+ # Use _format_actions_usage() for Python 3.12 and earlier
|
||||
+ formatted_options = self._format_actions_usage(parent_arguments, [])
|
||||
+ else:
|
||||
+ # Use _get_actions_usage_parts() for Python 3.13 and later
|
||||
+ action_parts = self._get_actions_usage_parts(parent_arguments, [])
|
||||
+ if sys.version_info >= (3, 15):
|
||||
+ # Python 3.15 returns a tuple (list of actions, count of actions)
|
||||
+ formatted_options = ' '.join(action_parts[0])
|
||||
+ else:
|
||||
+ # Python 3.13 and 3.14 return a list of actions
|
||||
+ formatted_options = ' '.join(action_parts)
|
||||
+
|
||||
# If formatted_options already in usage - remove them
|
||||
if formatted_options in usage:
|
||||
usage = usage.replace(f' {formatted_options}', '')
|
||||
--
|
||||
2.52.0
|
||||
|
||||
651
0007-Issue-7267-MDB_BAD_VALSIZE-error-when-updating-index.patch
Normal file
651
0007-Issue-7267-MDB_BAD_VALSIZE-error-when-updating-index.patch
Normal file
@ -0,0 +1,651 @@
|
||||
From 50ee439bb1d2e2344ea8cef076d54585204d9fbb Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Wed, 25 Feb 2026 18:00:24 +0100
|
||||
Subject: [PATCH 7/8] Issue 7267 - MDB_BAD_VALSIZE error when updating index
|
||||
(#7268)
|
||||
|
||||
* Issue 7267 - MDB_BAD_VALSIZE error when updating index
|
||||
* Improve import log when writer fails
|
||||
* Fix Sourcery AI comments
|
||||
* Fix INDEX_KEY_LENGTH typo
|
||||
|
||||
Problem with the key prefix handling when key is too long and must be hashed.
|
||||
The issue is that the # that is prepended is not reset when iterating over the valueset values (Ending up with very long prefix)
|
||||
|
||||
Also refactored the code to avoid duplicate the code that prepare the key from the attribute value (used when updating the index or retrieving a value from an index)
|
||||
|
||||
Issue: #7267
|
||||
|
||||
Reviewed by: @tbordaz , @vashirov (Thanks!)
|
||||
|
||||
Co-authored-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
|
||||
---------
|
||||
|
||||
Co-authored-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
---
|
||||
.../tests/suites/indexes/regression_test.py | 58 +++++++
|
||||
ldap/servers/slapd/back-ldbm/attrcrypt.h | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/back-ldbm.h | 2 +
|
||||
.../slapd/back-ldbm/db-bdb/bdb_import.c | 39 +----
|
||||
.../back-ldbm/db-mdb/mdb_import_threads.c | 46 ++++-
|
||||
ldap/servers/slapd/back-ldbm/index.c | 161 +++++++-----------
|
||||
ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c | 13 +-
|
||||
.../servers/slapd/back-ldbm/proto-back-ldbm.h | 2 +-
|
||||
ldap/servers/slapd/log.c | 40 +++++
|
||||
ldap/servers/slapd/slapi-private.h | 2 +
|
||||
10 files changed, 221 insertions(+), 144 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/indexes/regression_test.py b/dirsrvtests/tests/suites/indexes/regression_test.py
|
||||
index e2c2f5c35..53d340cdd 100644
|
||||
--- a/dirsrvtests/tests/suites/indexes/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/indexes/regression_test.py
|
||||
@@ -699,6 +699,64 @@ def test_idl_range_limit(topo, add_some_entries):
|
||||
assert len(entries) == 3
|
||||
|
||||
|
||||
+def test_large_multivalued_sn_attribute(topo):
|
||||
+ """Test adding a user entry with 512 values for sn attribute, each 512 bytes
|
||||
+
|
||||
+ :id: 8f2a9b3c-e8d7-11ef-9a5f-482ae39447e5
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Create a user with 512 sn values, each 512 bytes long
|
||||
+ 2. Verify the user was created successfully
|
||||
+ 3. Search for the user and verify all sn values are present
|
||||
+ 4. Clean up the user entry
|
||||
+ :expectedresults:
|
||||
+ 1. User is created successfully
|
||||
+ 2. User entry exists
|
||||
+ 3. All 512 sn values are present and have correct length
|
||||
+ 4. User is deleted successfully
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+
|
||||
+ log.info("Creating user with 512 sn values, each 512 bytes")
|
||||
+
|
||||
+ # Generate 512 unique sn values, each 512 bytes long
|
||||
+ # Use a pattern that makes each value unique but predictable
|
||||
+ sn_values = []
|
||||
+ for i in range(512):
|
||||
+ # Create a 512-byte value with unique identifier at the start
|
||||
+ value = f'sn_value_{i:04d}_' + 'x' * (512 - len(f'sn_value_{i:04d}_'))
|
||||
+ sn_values.append(value)
|
||||
+
|
||||
+ # Create the user with first sn value
|
||||
+ user_name = 'test_user_large_sn'
|
||||
+ user = users.create(properties={
|
||||
+ 'uid': user_name,
|
||||
+ 'cn': user_name,
|
||||
+ 'sn': sn_values,
|
||||
+ 'uidNumber': '99999',
|
||||
+ 'gidNumber': '99999',
|
||||
+ 'homeDirectory': f'/home/{user_name}'
|
||||
+ })
|
||||
+
|
||||
+ # Verify the entry was created and has all sn values
|
||||
+ log.info("Verifying all sn values are present")
|
||||
+ sn_attr_values = user.get_attr_vals_utf8('sn')
|
||||
+
|
||||
+ assert len(sn_attr_values) == 512, f"Expected 512 sn values, got {len(sn_attr_values)}"
|
||||
+
|
||||
+ # Verify each value has the correct length
|
||||
+ for idx, value in enumerate(sn_attr_values):
|
||||
+ assert len(value) == 512, f"sn value {idx} has length {len(value)}, expected 512"
|
||||
+
|
||||
+ log.info("Successfully created and verified user with 512 sn values of 512 bytes each")
|
||||
+
|
||||
+ # Clean up
|
||||
+ user.delete()
|
||||
+ log.info("User entry deleted successfully")
|
||||
+
|
||||
+
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/attrcrypt.h b/ldap/servers/slapd/back-ldbm/attrcrypt.h
|
||||
index d653ba951..dcbea80fe 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/attrcrypt.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/attrcrypt.h
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
-/* Private tructures and #defines used in the attribute encryption code. */
|
||||
+/* Private structures and #defines used in the attribute encryption code. */
|
||||
|
||||
#ifndef _ATTRCRYPT_H_
|
||||
#define _ATTRCRYPT_H_
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
index 70f532538..f77924f1a 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
@@ -104,6 +104,8 @@ typedef unsigned short u_int16_t;
|
||||
*/
|
||||
#define BE_CHANGELOG_FILE "replication_changelog"
|
||||
|
||||
+#define INDEX_KEY_LENGTH(lenval,lenprefix) (lenval+lenprefix+2)
|
||||
+
|
||||
#define BDB_IMPL "bdb"
|
||||
#define BDB_BACKEND "libback-ldbm" /* This backend plugin */
|
||||
#define BDB_NEWIDL "newidl" /* new idl format */
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
index 59a472f58..bdc4ee4f6 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
@@ -75,9 +75,9 @@ static IDList *bdb_idl_union_allids(backend *be, struct attrinfo *ai, IDList *a,
|
||||
#define DEBUG_SUBCOUNT_MSG(msg, ...) { debug_subcount(__FUNCTION__, __LINE__, (msg), __VA_ARGS__); }
|
||||
#define DUMP_SUBCOUNT_KEY(msg, key, ret) { debug_subcount(__FUNCTION__, __LINE__, "ret=%d size=%u ulen=%u doff=%u dlen=%u", \
|
||||
ret, (key).size, (key).ulen, (key).doff, (key).dlen); \
|
||||
- if (ret == 0) hexadump(msg, (key).data, 0, (key).size); \
|
||||
+ if (ret == 0) slapi_log_hexadump(SLAPI_LOG_INFO, msg, (key).data, (key).size); \
|
||||
else if (ret == DB_BUFFER_SMALL) \
|
||||
- hexadump(msg, (key).data, 0, (key).ulen); }
|
||||
+ slapi_log_hexadump(SLAPI_LOG_INFO, msg, (key).data, (key).ulen); }
|
||||
|
||||
static void
|
||||
debug_subcount(const char *funcname, int line, char *msg, ...)
|
||||
@@ -90,41 +90,6 @@ debug_subcount(const char *funcname, int line, char *msg, ...)
|
||||
slapi_log_err(SLAPI_LOG_INFO, (char*)funcname, "DEBUG SUBCOUNT [%d] %s\n", line, buff);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Dump a memory buffer in hexa and ascii in error log
|
||||
- *
|
||||
- * addr - The memory buffer address.
|
||||
- * len - The memory buffer lenght.
|
||||
- */
|
||||
-static void
|
||||
-hexadump(char *msg, const void *addr, size_t offset, size_t len)
|
||||
-{
|
||||
-#define HEXADUMP_TAB 4
|
||||
-/* 4 characters per bytes: 2 hexa digits, 1 space and the ascii */
|
||||
-#define HEXADUMP_BUF_SIZE (4*16+HEXADUMP_TAB)
|
||||
- char hexdigit[] = "0123456789ABCDEF";
|
||||
-
|
||||
- const unsigned char *pt = addr;
|
||||
- char buff[HEXADUMP_BUF_SIZE+1];
|
||||
- memset (buff, ' ', HEXADUMP_BUF_SIZE);
|
||||
- buff[HEXADUMP_BUF_SIZE] = '\0';
|
||||
- while (len > 0) {
|
||||
- int dpl;
|
||||
- for (dpl = 0; dpl < 16 && len>0; dpl++, len--) {
|
||||
- buff[3*dpl] = hexdigit[((*pt) >> 4) & 0xf];
|
||||
- buff[3*dpl+1] = hexdigit[(*pt) & 0xf];
|
||||
- buff[3*16+HEXADUMP_TAB+dpl] = (*pt>=0x20 && *pt<0x7f) ? *pt : '.';
|
||||
- pt++;
|
||||
- }
|
||||
- for (;dpl < 16; dpl++) {
|
||||
- buff[3*dpl] = ' ';
|
||||
- buff[3*dpl+1] = ' ';
|
||||
- buff[3*16+HEXADUMP_TAB+dpl] = ' ';
|
||||
- }
|
||||
- slapi_log_err(SLAPI_LOG_INFO, msg, "[0x%08lx] %s\n", offset, buff);
|
||||
- offset += 16;
|
||||
- }
|
||||
-}
|
||||
#else
|
||||
#define DEBUG_SUBCOUNT_MSG(msg, ...)
|
||||
#define DUMP_SUBCOUNT_KEY(msg, key, ret)
|
||||
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 35965df85..c49412a70 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
|
||||
@@ -1122,6 +1122,21 @@ dbmdb_import_entry_info_by_backentry(mdb_privdb_t *db, BulkQueueData_t *bqdata,
|
||||
return dnrc;
|
||||
}
|
||||
|
||||
+/* Log wqelmt details */
|
||||
+void
|
||||
+log_wqelmt(int loglvl, char *fname, WorkerQueueData_t *wqelmt)
|
||||
+{
|
||||
+ if (wqelmt->dn) {
|
||||
+ slapi_log_err(loglvl, fname, "log_wqelmt: dn=%s\n", wqelmt->dn);
|
||||
+ }
|
||||
+ if (wqelmt->filename && wqelmt->lineno) {
|
||||
+ slapi_log_err(loglvl, fname, "log_wqelmt: ldif=%s[%d]\n", wqelmt->filename, wqelmt->lineno);
|
||||
+ }
|
||||
+ if (wqelmt->data) {
|
||||
+ size_t len = wqelmt->datalen ? wqelmt->datalen : strlen(wqelmt->data);
|
||||
+ slapi_log_hexadump(loglvl, "log_wqelmt:data", wqelmt->data, len);
|
||||
+ }
|
||||
+}
|
||||
|
||||
/* producer thread for ldif import case:
|
||||
* read through the given file list, parsing entries (str2entry), assigning
|
||||
@@ -1255,6 +1270,7 @@ dbmdb_import_producer(void *param)
|
||||
import_log_notice(job, SLAPI_LOG_ERR, "dbmdb_import_producer",
|
||||
"ns_slapd software error: unexpected dbmdb_import_entry_info return code: %d.",
|
||||
wqelmt.dnrc);
|
||||
+ log_wqelmt(SLAPI_LOG_ERR, "dbmdb_import_producer", &wqelmt);
|
||||
abort();
|
||||
case DNRC_OK:
|
||||
case DNRC_SUFFIX:
|
||||
@@ -1761,6 +1777,7 @@ dbmdb_index_producer(void *param)
|
||||
import_log_notice(job, SLAPI_LOG_ERR, "dbmdb_index_producer",
|
||||
"ns_slapd software error: unexpected dbmdb_import_entry_info return code: %d.",
|
||||
tmpslot.dnrc);
|
||||
+ log_wqelmt(SLAPI_LOG_ERR, "dbmdb_index_producer", &tmpslot);
|
||||
abort();
|
||||
case DNRC_OK:
|
||||
case DNRC_SUFFIX:
|
||||
@@ -3951,10 +3968,24 @@ dbmdb_import_writer(void*param)
|
||||
if (!txn) {
|
||||
MDB_STAT_STEP(stats, MDB_STAT_TXNSTART);
|
||||
rc = TXN_BEGIN(ctx->ctx->env, NULL, 0, &txn);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to begin a txn. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
+ }
|
||||
}
|
||||
if (!rc) {
|
||||
MDB_STAT_STEP(stats, MDB_STAT_WRITE);
|
||||
rc = MDB_PUT(txn, slot->dbi->dbi, &slot->key, &slot->data, 0);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to write record in dbi %s. Error is 0x%x: %s.\n",
|
||||
+ slot->dbi->dbname, rc, mdb_strerror(rc));
|
||||
+ slapi_log_hexadump(SLAPI_LOG_ERR, "dbmdb_import_writer:key",
|
||||
+ slot->key.mv_data, slot->key.mv_size);
|
||||
+ slapi_log_hexadump(SLAPI_LOG_ERR, "dbmdb_import_writer:data",
|
||||
+ slot->data.mv_data, slot->data.mv_size);
|
||||
+ }
|
||||
}
|
||||
MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
nextslot = slot->next;
|
||||
@@ -3968,6 +3999,9 @@ dbmdb_import_writer(void*param)
|
||||
rc = TXN_COMMIT(txn);
|
||||
MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to commit the txn. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
break;
|
||||
}
|
||||
count = 0;
|
||||
@@ -3980,6 +4014,10 @@ dbmdb_import_writer(void*param)
|
||||
MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
if (!rc) {
|
||||
txn = NULL;
|
||||
+ } else {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to commit the txn. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
}
|
||||
}
|
||||
if (txn) {
|
||||
@@ -3992,13 +4030,17 @@ dbmdb_import_writer(void*param)
|
||||
if (!rc) {
|
||||
/* Ensure that all data are written on disk */
|
||||
rc = mdb_env_sync(ctx->ctx->env, 1);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "mdb_env_sync failed. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
+ }
|
||||
}
|
||||
MDB_STAT_END(stats);
|
||||
|
||||
if (rc) {
|
||||
slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
- "Failed to write in the database. Error is 0x%x: %s.\n",
|
||||
- rc, mdb_strerror(rc));
|
||||
+ "Aborting import after failure.\n");
|
||||
thread_abort(info);
|
||||
} else {
|
||||
char buf[200];
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
|
||||
index ca3a866bb..3bdf8926b 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/index.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/index.c
|
||||
@@ -891,6 +891,67 @@ index_read(
|
||||
return index_read_ext(be, (char *)type, indextype, val, txn, err, NULL);
|
||||
}
|
||||
|
||||
+/* Prepare an index key (hashed if too long, encrypted if needed from attribute value */
|
||||
+int
|
||||
+prepare_key(backend *be, struct attrinfo *a, char **buf, size_t *buflen,
|
||||
+ int flags, const char *prefix, const struct berval *bvp, dbi_val_t *key)
|
||||
+{
|
||||
+ /* Key format is [Hash?] [prefix] [val] [\0] */
|
||||
+ struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
+ size_t plen = strlen(prefix);
|
||||
+ struct berval *hashed_bvp = NULL;
|
||||
+ struct berval *encrypted_bvp = NULL;
|
||||
+ int rc = 0;
|
||||
+
|
||||
+ /* Hash large index key if necessary */
|
||||
+ if (INDEX_KEY_LENGTH(bvp->bv_len,plen) >= li->li_max_key_len) {
|
||||
+ rc = attrcrypt_hash_large_index_key(be, prefix, a, bvp, &hashed_bvp);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
+ "Failed to hash large index key for %s\n", a->ai_type);
|
||||
+ return rc;
|
||||
+ } else {
|
||||
+ bvp = hashed_bvp;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Encrypt the index key if necessary */
|
||||
+ if (rc == 0 && a->ai_attrcrypt && (0 == (flags & BE_INDEX_DONT_ENCRYPT))) {
|
||||
+ rc = attrcrypt_encrypt_index_key(be, a, bvp, &encrypted_bvp);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "addordel_values_sv",
|
||||
+ "Failed to encrypt index key for %s\n", a->ai_type);
|
||||
+ } else {
|
||||
+ bvp = encrypted_bvp;
|
||||
+ }
|
||||
+ }
|
||||
+ if (hashed_bvp) {
|
||||
+ prefix = slapi_ch_smprintf("%c%s",HASH_PREFIX, prefix);
|
||||
+ plen++;
|
||||
+ }
|
||||
+ if (buf && buflen) {
|
||||
+ if (plen+bvp->bv_len+1 > *buflen) {
|
||||
+ *buflen = plen+bvp->bv_len+1;
|
||||
+ *buf = slapi_ch_realloc(*buf, *buflen);
|
||||
+ }
|
||||
+ dblayer_value_concat(be, key, *buf, *buflen, prefix, plen, bvp->bv_val, bvp->bv_len, "", 1);
|
||||
+ } else {
|
||||
+ dblayer_value_concat(be, key, NULL, 0, prefix, plen, bvp->bv_val, bvp->bv_len, "", 1);
|
||||
+ }
|
||||
+
|
||||
+ if (hashed_bvp) {
|
||||
+ ber_bvfree(hashed_bvp);
|
||||
+ hashed_bvp = NULL;
|
||||
+ slapi_ch_free_string((char**)&prefix);
|
||||
+ }
|
||||
+ if (encrypted_bvp) {
|
||||
+ ber_bvfree(encrypted_bvp);
|
||||
+ encrypted_bvp = NULL;
|
||||
+ }
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/*
|
||||
* Extended version of index_read.
|
||||
* The unindexed flag can be used to distinguish between a
|
||||
@@ -927,7 +988,6 @@ index_read_ext_allids(
|
||||
struct berval *hashed_val = NULL;
|
||||
int is_and = 0;
|
||||
unsigned int ai_flags = 0;
|
||||
- struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
|
||||
*err = 0;
|
||||
|
||||
@@ -1038,36 +1098,7 @@ index_read_ext_allids(
|
||||
}
|
||||
|
||||
if (val != NULL) {
|
||||
- size_t vlen;
|
||||
- int ret = 0;
|
||||
-
|
||||
- /* If necessary, hash this index key */
|
||||
- if (val->bv_len >= li->li_max_key_len) {
|
||||
- ret = attrcrypt_hash_large_index_key(be, &prefix, ai, val, &hashed_val);
|
||||
- if (ret) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
- "Failed to hash large index key for %s\n", basetype);
|
||||
- *err = DBI_RC_OTHER;
|
||||
- index_free_prefix(prefix);
|
||||
- slapi_ch_free_string(&basetmp);
|
||||
- return (NULL);
|
||||
- }
|
||||
- if (hashed_val) {
|
||||
- val = hashed_val;
|
||||
- }
|
||||
- }
|
||||
- /* If necessary, encrypt this index key */
|
||||
- ret = attrcrypt_encrypt_index_key(be, ai, val, &encrypted_val);
|
||||
- if (ret) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
- "Failed to encrypt index key for %s\n", basetype);
|
||||
- }
|
||||
- if (encrypted_val) {
|
||||
- val = encrypted_val;
|
||||
- }
|
||||
- vlen = val->bv_len;
|
||||
- dblayer_value_concat(be, &key, buf, sizeof(buf),
|
||||
- prefix, strlen(prefix), val->bv_val, vlen, "", 1);
|
||||
+ (void) prepare_key(be, ai, NULL, 0, 0, prefix, val, &key);
|
||||
} else {
|
||||
dblayer_value_concat(be, &key, buf, sizeof(buf), prefix, strlen(prefix),
|
||||
"", 1, NULL, 0);
|
||||
@@ -1867,6 +1898,7 @@ index_range_read(
|
||||
return index_range_read_ext(pb, be, type, indextype, operator, val, nextval, range, txn, err, 0);
|
||||
}
|
||||
|
||||
+
|
||||
static int
|
||||
addordel_values_sv(
|
||||
backend *be,
|
||||
@@ -1885,15 +1917,10 @@ addordel_values_sv(
|
||||
int i = 0;
|
||||
dbi_val_t key = {0};
|
||||
dbi_txn_t *db_txn = NULL;
|
||||
- size_t plen, vlen, len;
|
||||
char *tmpbuf = NULL;
|
||||
size_t tmpbuflen = 0;
|
||||
- char *realbuf;
|
||||
char *prefix = NULL;
|
||||
const struct berval *bvp;
|
||||
- struct berval *hashed_bvp = NULL;
|
||||
- struct berval *encrypted_bvp = NULL;
|
||||
- struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
char *index_id = get_index_name(be, db, a);
|
||||
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "addordel_values_sv", "%s_values\n",
|
||||
@@ -1932,66 +1959,14 @@ addordel_values_sv(
|
||||
return (rc);
|
||||
}
|
||||
|
||||
- plen = strlen(prefix);
|
||||
for (i = 0; vals[i] != NULL; i++) {
|
||||
bvp = slapi_value_get_berval(vals[i]);
|
||||
|
||||
- /* Hash large index key if necessary */
|
||||
- if (bvp->bv_len >= li->li_max_key_len) {
|
||||
- rc = attrcrypt_hash_large_index_key(be, &prefix, a, bvp, &hashed_bvp);
|
||||
- if (rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
- "Failed to hash large index key for %s\n", a->ai_type);
|
||||
- break;
|
||||
- } else {
|
||||
- bvp = hashed_bvp;
|
||||
- plen = strlen(prefix);
|
||||
- }
|
||||
- }
|
||||
- /* Encrypt the index key if necessary */
|
||||
- {
|
||||
- if (a->ai_attrcrypt && (0 == (flags & BE_INDEX_DONT_ENCRYPT))) {
|
||||
- rc = attrcrypt_encrypt_index_key(be, a, bvp, &encrypted_bvp);
|
||||
- if (rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "addordel_values_sv",
|
||||
- "Failed to encrypt index key for %s\n", a->ai_type);
|
||||
- } else {
|
||||
- bvp = encrypted_bvp;
|
||||
- }
|
||||
- }
|
||||
+ rc = prepare_key(be, a, &tmpbuf, &tmpbuflen, flags, prefix, bvp, &key);
|
||||
+ if (rc) {
|
||||
+ break;
|
||||
}
|
||||
|
||||
- vlen = bvp->bv_len;
|
||||
- len = plen + vlen;
|
||||
-
|
||||
- if (len < tmpbuflen) {
|
||||
- realbuf = tmpbuf;
|
||||
- } else {
|
||||
- tmpbuf = slapi_ch_realloc(tmpbuf, len + 1);
|
||||
- tmpbuflen = len + 1;
|
||||
- realbuf = tmpbuf;
|
||||
- }
|
||||
-
|
||||
- assert(realbuf); /* For coverity */
|
||||
- memcpy(realbuf, prefix, plen);
|
||||
- memcpy(realbuf + plen, bvp->bv_val, vlen);
|
||||
- realbuf[len] = '\0';
|
||||
- /* Free the encrypted berval if necessary */
|
||||
- if (hashed_bvp) {
|
||||
- ber_bvfree(hashed_bvp);
|
||||
- hashed_bvp = NULL;
|
||||
- }
|
||||
- if (encrypted_bvp) {
|
||||
- ber_bvfree(encrypted_bvp);
|
||||
- encrypted_bvp = NULL;
|
||||
- }
|
||||
- /* should be okay to use USERMEM here because we know what
|
||||
- * the key is and it should never return a different value
|
||||
- * than the one we pass in.
|
||||
- */
|
||||
- dblayer_value_set_buffer(be, &key, realbuf, plen + vlen + 1);
|
||||
- key.ulen = tmpbuflen;
|
||||
-
|
||||
if (slapi_is_loglevel_set(LDAP_DEBUG_TRACE)) {
|
||||
char encbuf[BUFSIZ];
|
||||
|
||||
@@ -2024,10 +1999,6 @@ addordel_values_sv(
|
||||
ldbm_nasty(NASTY_MSG("addordel_values_sv"), index_id, 1130, rc);
|
||||
break;
|
||||
}
|
||||
- if (NULL != key.dptr && realbuf != key.dptr) { /* realloc'ed */
|
||||
- tmpbuf = key.dptr;
|
||||
- tmpbuflen = key.size;
|
||||
- }
|
||||
}
|
||||
index_free_prefix(prefix);
|
||||
if (tmpbuf != NULL) {
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
|
||||
index 3b86a5dd7..14b3b9ce2 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
|
||||
@@ -1065,15 +1065,15 @@ attrcrypt_decrypt_index_key(backend *be,
|
||||
* : NULL - no hash or failure
|
||||
*/
|
||||
int
|
||||
-attrcrypt_hash_large_index_key(backend *be, char **prefix, struct attrinfo *ai, const struct berval *in, struct berval **out)
|
||||
+attrcrypt_hash_large_index_key(backend *be, const char *prefix, struct attrinfo *ai, const struct berval *in, struct berval **out)
|
||||
{
|
||||
int ret = 0;
|
||||
struct berval *out_berval = NULL;
|
||||
struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
- char *new_prefix;
|
||||
+ size_t final_key_len = INDEX_KEY_LENGTH(in->bv_len, strlen(prefix));
|
||||
|
||||
/* If the index key is too long (i.e mdb case) we must hash it */
|
||||
- if (in->bv_len >= li->li_max_key_len) {
|
||||
+ if (final_key_len >= li->li_max_key_len) {
|
||||
PK11Context *c = PK11_CreateDigestContext(SEC_OID_MD5);
|
||||
if (c != NULL) {
|
||||
unsigned char hash[32];
|
||||
@@ -1087,16 +1087,13 @@ attrcrypt_hash_large_index_key(backend *be, char **prefix, struct attrinfo *ai,
|
||||
return ENOMEM;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "attrcrypt_hash_large_index_key",
|
||||
- "Key lenght (%lu) >= max key lenght (%lu) so key must be hashed\n", in->bv_len, li->li_max_key_len);
|
||||
+ "Key lenght (%lu) >= max key lenght (%lu) so key must be hashed\n", final_key_len, li->li_max_key_len);
|
||||
slapi_be_set_flag(be, SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST);
|
||||
PK11_DigestBegin(c);
|
||||
/* Compute hash for the key without the prefix */
|
||||
PK11_DigestOp(c, (unsigned char *)in->bv_val, in->bv_len);
|
||||
PK11_DigestFinal(c, hash, &hashLen, sizeof hash);
|
||||
- /* Add HASH_PREFIX before the prefix */
|
||||
- new_prefix = slapi_ch_smprintf("%c%s", HASH_PREFIX, *prefix);
|
||||
- index_free_prefix(*prefix);
|
||||
- *prefix = new_prefix;
|
||||
+
|
||||
/* Build the key: hash value in hexa */
|
||||
hkey = slapi_ch_malloc(1+2*sizeof hash);
|
||||
out_berval->bv_val = hkey;
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
index 8754a4847..1884da603 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
@@ -620,7 +620,7 @@ int attrcrypt_encrypt_entry_inplace(backend *be, const struct backentry *inout);
|
||||
int attrcrypt_encrypt_entry(backend *be, const struct backentry *in, struct backentry **out);
|
||||
int attrcrypt_encrypt_index_key(backend *be, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
int attrcrypt_decrypt_index_key(backend *be, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
-int attrcrypt_hash_large_index_key(backend *be, char **prefix, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
+int attrcrypt_hash_large_index_key(backend *be, const char *prefix, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
int attrcrypt_init(ldbm_instance *li);
|
||||
int attrcrypt_cleanup_private(ldbm_instance *li);
|
||||
|
||||
diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c
|
||||
index ca8d481e5..17c7bbb7d 100644
|
||||
--- a/ldap/servers/slapd/log.c
|
||||
+++ b/ldap/servers/slapd/log.c
|
||||
@@ -92,6 +92,10 @@ static int slapi_log_map[] = {
|
||||
#define SLAPI_LOG_MAX SLAPI_LOG_DEBUG /* from slapi-plugin.h */
|
||||
#define LOG_CHUNK 16384 /* zlib compression */
|
||||
|
||||
+#define HEXADUMP_TAB 4
|
||||
+/* 4 characters per bytes: 2 hexa digits, 1 space and the ascii */
|
||||
+#define HEXADUMP_BUF_SIZE (4*16+HEXADUMP_TAB)
|
||||
+
|
||||
/**************************************************************************
|
||||
* PROTOTYPES
|
||||
*************************************************************************/
|
||||
@@ -3065,6 +3069,42 @@ slapi_log_backtrace(int loglevel)
|
||||
}
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Dump a memory buffer in hexa and ascii in error log
|
||||
+ *
|
||||
+ * addr - The memory buffer address.
|
||||
+ * len - The memory buffer lenght.
|
||||
+ */
|
||||
+void
|
||||
+slapi_log_hexadump(int loglevel, char *fname, const void *addr, size_t len)
|
||||
+{
|
||||
+ char hexdigit[] = "0123456789ABCDEF";
|
||||
+ const unsigned char *pt = addr;
|
||||
+ char buff[HEXADUMP_BUF_SIZE+1];
|
||||
+ size_t offset = 0;
|
||||
+
|
||||
+ if (!slapi_is_loglevel_set(loglevel)) {
|
||||
+ return;
|
||||
+ }
|
||||
+ memset (buff, ' ', HEXADUMP_BUF_SIZE);
|
||||
+ buff[HEXADUMP_BUF_SIZE] = '\0';
|
||||
+ while (len > 0) {
|
||||
+ int dpl;
|
||||
+ for (dpl = 0; dpl < 16 && len>0; dpl++, len--) {
|
||||
+ buff[3*dpl] = hexdigit[((*pt) >> 4) & 0xf];
|
||||
+ buff[3*dpl+1] = hexdigit[(*pt) & 0xf];
|
||||
+ buff[3*16+HEXADUMP_TAB+dpl] = (*pt>=0x20 && *pt<0x7f) ? *pt : '.';
|
||||
+ pt++;
|
||||
+ }
|
||||
+ for (;dpl < 16; dpl++) {
|
||||
+ buff[3*dpl] = ' ';
|
||||
+ buff[3*dpl+1] = ' ';
|
||||
+ buff[3*16+HEXADUMP_TAB+dpl] = ' ';
|
||||
+ }
|
||||
+ slapi_log_err(loglevel, fname, "[0x%08lx] %s\n", offset, buff);
|
||||
+ offset += 16;
|
||||
+ }
|
||||
+}
|
||||
|
||||
/******************************************************************************
|
||||
* write in the access log
|
||||
diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h
|
||||
index a55ea3ee1..97eed5a9b 100644
|
||||
--- a/ldap/servers/slapd/slapi-private.h
|
||||
+++ b/ldap/servers/slapd/slapi-private.h
|
||||
@@ -1527,6 +1527,8 @@ void slapi_pblock_set_task_warning(Slapi_PBlock *pb, task_warning warning);
|
||||
int slapi_exists_or_add_internal(Slapi_DN *dn, const char *filter, const char *entry, const char *modifier_name);
|
||||
|
||||
void slapi_log_backtrace(int loglevel);
|
||||
+void slapi_log_hexadump(int loglevel, char *fname, const void *addr, size_t len);
|
||||
+
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
--
|
||||
2.54.0
|
||||
|
||||
39
0008-Fix-test389-imports-on-older-branches.patch
Normal file
39
0008-Fix-test389-imports-on-older-branches.patch
Normal file
@ -0,0 +1,39 @@
|
||||
From 853d11cd6abc69a8e93172771b19b52950293e6f Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 5 Jun 2026 11:17:35 +0200
|
||||
Subject: [PATCH 8/8] Fix test389 imports on older branches
|
||||
|
||||
---
|
||||
dirsrvtests/tests/suites/basic/modrdn_bulk_children_test.py | 2 +-
|
||||
dirsrvtests/tests/suites/features/ldap_controls_test.py | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/basic/modrdn_bulk_children_test.py b/dirsrvtests/tests/suites/basic/modrdn_bulk_children_test.py
|
||||
index 77f839c40..adbe2861a 100644
|
||||
--- a/dirsrvtests/tests/suites/basic/modrdn_bulk_children_test.py
|
||||
+++ b/dirsrvtests/tests/suites/basic/modrdn_bulk_children_test.py
|
||||
@@ -13,7 +13,7 @@ import pytest
|
||||
from lib389._constants import DEFAULT_SUFFIX
|
||||
from lib389.idm.organizationalunit import OrganizationalUnit, OrganizationalUnits
|
||||
from lib389.idm.user import UserAccount, UserAccounts
|
||||
-from test389.topologies import topology_st as topo
|
||||
+from lib389.topologies import topology_st as topo
|
||||
|
||||
pytestmark = pytest.mark.tier1
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/features/ldap_controls_test.py b/dirsrvtests/tests/suites/features/ldap_controls_test.py
|
||||
index 59a58b21d..7d8b21c8f 100644
|
||||
--- a/dirsrvtests/tests/suites/features/ldap_controls_test.py
|
||||
+++ b/dirsrvtests/tests/suites/features/ldap_controls_test.py
|
||||
@@ -12,7 +12,7 @@ import ldap
|
||||
from ldap.controls import RequestControl
|
||||
from ldap.controls.readentry import PostReadControl
|
||||
from lib389.idm.user import UserAccounts, UserAccount
|
||||
-from test389.topologies import topology_st
|
||||
+from lib389.topologies import topology_st
|
||||
from lib389._constants import DEFAULT_SUFFIX, DN_DM, PASSWORD
|
||||
|
||||
pytestmark = pytest.mark.tier1
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
From dbabbe6a3c42b979d14960b355925c9748371cad Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 30 Jan 2026 12:00:13 +0100
|
||||
Subject: [PATCH] Issue 7027 - (2nd) 389-ds-base OpenScanHub Leaks Detected
|
||||
(#7211)
|
||||
|
||||
Fix Description:
|
||||
Update coverity annotations.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7027
|
||||
|
||||
Reviewed by: @aadhikar (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/log.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c
|
||||
index 668a02454..6f57a3d9c 100644
|
||||
--- a/ldap/servers/slapd/log.c
|
||||
+++ b/ldap/servers/slapd/log.c
|
||||
@@ -203,8 +203,8 @@ compress_log_file(char *log_name, int32_t mode)
|
||||
|
||||
if ((source = fopen(log_name, "r")) == NULL) {
|
||||
/* Failed to open log file */
|
||||
- /* coverity[leaked_storage] gzclose does close FD */
|
||||
gzclose(outfile);
|
||||
+ /* coverity[leaked_handle] gzclose does close FD */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -214,17 +214,17 @@ compress_log_file(char *log_name, int32_t mode)
|
||||
if (bytes_written == 0)
|
||||
{
|
||||
fclose(source);
|
||||
- /* coverity[leaked_storage] gzclose does close FD */
|
||||
gzclose(outfile);
|
||||
+ /* coverity[leaked_handle] gzclose does close FD */
|
||||
return -1;
|
||||
}
|
||||
bytes_read = fread(buf, 1, LOG_CHUNK, source);
|
||||
}
|
||||
- /* coverity[leaked_storage] gzclose does close FD */
|
||||
gzclose(outfile);
|
||||
fclose(source);
|
||||
PR_Delete(log_name); /* remove the old uncompressed log */
|
||||
|
||||
+ /* coverity[leaked_handle] gzclose does close FD */
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,197 +0,0 @@
|
||||
From 39d47ca91e866d30b35dca41d477ad4ee07f8a14 Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Mon, 2 Feb 2026 15:39:18 +0100
|
||||
Subject: [PATCH] Issue 7213 - MDB_BAD_VALSIZE error while handling VLV (#7214)
|
||||
|
||||
* Issue 7213 - MDB_BAD_VALSIZE error while handling VLV
|
||||
Avoid failing lmdb operation when handling VLV index by truncating the key so that key+data is small enough.
|
||||
|
||||
Issue: #7213
|
||||
|
||||
Reviewed by: @mreynolds389 , @vashirov (Thanks!)
|
||||
|
||||
Assisted by: Claude A/I
|
||||
|
||||
(cherry picked from commit 5ebce22d4214bec5ed94ad84c4448164be99389a)
|
||||
---
|
||||
.../tests/suites/vlv/regression_test.py | 110 ++++++++++++++++++
|
||||
.../slapd/back-ldbm/db-mdb/mdb_layer.c | 5 +
|
||||
ldap/servers/slapd/back-ldbm/vlv.c | 7 +-
|
||||
3 files changed, 121 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/vlv/regression_test.py b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
index c8db94b19..1cc03c303 100644
|
||||
--- a/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
@@ -1178,6 +1178,116 @@ def test_vlv_with_mr(vlv_setup_with_uid_mr):
|
||||
|
||||
|
||||
|
||||
+def test_vlv_long_attribute_value(topology_st, request):
|
||||
+ """
|
||||
+ Test VLV with an entry containing a very long attribute value (2K).
|
||||
+
|
||||
+ :id: 99126fa4-003e-11f1-b7d6-c85309d5c3e3
|
||||
+ :setup: Standalone instance.
|
||||
+ :steps:
|
||||
+ 1. Cleanup leftover from previous tests
|
||||
+ 2. Create VLV search and index on cn attribute
|
||||
+ 3. Reindex VLV
|
||||
+ 4. Add an entry with a cn attribute having 2K character value
|
||||
+ 5. Verify the entry was added successfully
|
||||
+ 6. Perform a VLV search to ensure it still works
|
||||
+ 7. Add another entry with a cn attribute having 2K character value
|
||||
+ 8. Verify the entry was added successfully
|
||||
+ 9. Perform a VLV search to ensure it still works
|
||||
+ :expectedresults:
|
||||
+ 1. Should Success.
|
||||
+ 2. Should Success.
|
||||
+ 3. Should Success.
|
||||
+ 4. Should Success.
|
||||
+ 5. Should Success.
|
||||
+ 6. Should Success.
|
||||
+ 7. Should Success.
|
||||
+ 8. Should Success.
|
||||
+ 9. Should Success.
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+ reindex_task = Tasks(inst)
|
||||
+
|
||||
+ users_to_delete = []
|
||||
+
|
||||
+ def fin():
|
||||
+ cleanup(inst)
|
||||
+ # Clean the added users
|
||||
+ for user in users_to_delete:
|
||||
+ user.delete()
|
||||
+
|
||||
+ if not DEBUGGING:
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+ # Clean previous tests leftover
|
||||
+ fin()
|
||||
+
|
||||
+ # Create VLV search and index
|
||||
+ vlv_search, vlv_index = create_vlv_search_and_index(inst)
|
||||
+ assert reindex_task.reindex(
|
||||
+ suffix=DEFAULT_SUFFIX,
|
||||
+ attrname=vlv_index.rdn,
|
||||
+ args={TASK_WAIT: True},
|
||||
+ vlv=True
|
||||
+ ) == 0
|
||||
+
|
||||
+ # Add a few regular users first
|
||||
+ add_users(inst, 10)
|
||||
+
|
||||
+ # Create a very long cn value (2K characters)
|
||||
+ long_cn_value = 'a' * 2048 + '1'
|
||||
+
|
||||
+ # Add an entry with the long cn attribute
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ user_properties = {
|
||||
+ 'uid': 'longcnuser1',
|
||||
+ 'cn': long_cn_value,
|
||||
+ 'sn': 'user1',
|
||||
+ 'uidNumber': '99999',
|
||||
+ 'gidNumber': '99999',
|
||||
+ 'homeDirectory': '/home/longcnuser1'
|
||||
+ }
|
||||
+ user = users.create(properties=user_properties)
|
||||
+ users_to_delete.append(user);
|
||||
+
|
||||
+ # Verify the entry was created and has the long cn value
|
||||
+ entry = user.get_attr_vals_utf8('cn')
|
||||
+ assert entry[0] == long_cn_value
|
||||
+ log.info(f'Successfully created user with cn length: {len(entry[0])}')
|
||||
+
|
||||
+ # Perform VLV search to ensure VLV still works with long attribute values
|
||||
+ conn = open_new_ldapi_conn(inst.serverid)
|
||||
+ count = len(conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=*)"))
|
||||
+ assert count > 0
|
||||
+ log.info(f'VLV search successful with {count} entries including entry with 2K cn value')
|
||||
+
|
||||
+ # Add another entry with the long cn attribute
|
||||
+ long_cn_value = 'a' * 2048 + '2'
|
||||
+
|
||||
+ user_properties = {
|
||||
+ 'uid': 'longcnuser2',
|
||||
+ 'cn': long_cn_value,
|
||||
+ 'sn': 'user2',
|
||||
+ 'uidNumber': '99998',
|
||||
+ 'gidNumber': '99998',
|
||||
+ 'homeDirectory': '/home/longcnuser2'
|
||||
+ }
|
||||
+ user = users.create(properties=user_properties)
|
||||
+ users_to_delete.append(user);
|
||||
+
|
||||
+ # Verify the entry was created and has the long cn value
|
||||
+ entry = user.get_attr_vals_utf8('cn')
|
||||
+ assert entry[0] == long_cn_value
|
||||
+ log.info(f'Successfully created user with cn length: {len(entry[0])}')
|
||||
+
|
||||
+ # Perform VLV search to ensure VLV still works with long attribute values
|
||||
+ conn = open_new_ldapi_conn(inst.serverid)
|
||||
+ count = len(conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=*)"))
|
||||
+ assert count > 1
|
||||
+ log.info(f'VLV search successful with {count} entries including entry with 2K cn value')
|
||||
+
|
||||
+
|
||||
+
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
|
||||
index c6e9f8b01..ffbd6609f 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
|
||||
@@ -2138,10 +2138,15 @@ void *dbmdb_recno_cache_build(void *arg)
|
||||
recno = 1;
|
||||
}
|
||||
while (rc == 0) {
|
||||
+ struct ldbminfo *li = (struct ldbminfo *)rcctx->cursor->be->be_database->plg_private;
|
||||
slapi_log_err(SLAPI_LOG_DEBUG, "dbmdb_recno_cache_build", "recno=%d\n", recno);
|
||||
if (recno % RECNO_CACHE_INTERVAL == 1) {
|
||||
/* Prepare the cache data */
|
||||
len = sizeof(*rce) + data.mv_size + key.mv_size;
|
||||
+ if (len > li->li_max_key_len) {
|
||||
+ key.mv_size = li->li_max_key_len - data.mv_size - sizeof(*rce);
|
||||
+ len = li->li_max_key_len;
|
||||
+ }
|
||||
rce = (dbmdb_recno_cache_elmt_t*)slapi_ch_malloc(len);
|
||||
rce->len = len;
|
||||
rce->recno = recno;
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/vlv.c b/ldap/servers/slapd/back-ldbm/vlv.c
|
||||
index 18431799c..a1cd226f4 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/vlv.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/vlv.c
|
||||
@@ -866,6 +866,7 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
|
||||
struct vlv_key *key = NULL;
|
||||
dbi_val_t data = {0};
|
||||
dblayer_private *priv = NULL;
|
||||
+ size_t key_size_limit = li->li_max_key_len - sizeof(entry->ep_id);
|
||||
|
||||
slapi_pblock_get(pb, SLAPI_BACKEND, &be);
|
||||
priv = (dblayer_private *)li->li_dblayer_private;
|
||||
@@ -886,6 +887,10 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
|
||||
return rc;
|
||||
}
|
||||
|
||||
+ /* Truncate the key if it is too long */
|
||||
+ if (key->key.size > key_size_limit) {
|
||||
+ key->key.size = key_size_limit;
|
||||
+ }
|
||||
if (NULL != txn) {
|
||||
db_txn = txn->back_txn_txn;
|
||||
} else {
|
||||
@@ -930,7 +935,7 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
|
||||
if (txn && txn->back_special_handling_fn) {
|
||||
rc = txn->back_special_handling_fn(be, BTXNACT_VLV_DEL, db, &key->key, &data, txn);
|
||||
} else {
|
||||
- rc = dblayer_db_op(be, db, db_txn, DBI_OP_DEL, &key->key, NULL);
|
||||
+ rc = dblayer_db_op(be, db, db_txn, DBI_OP_DEL, &key->key, &data);
|
||||
}
|
||||
if (rc == 0) {
|
||||
if (txn && txn->back_special_handling_fn) {
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
From 70d323d4d9ca4d1d682ab4a273178946922c6929 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 24 Jan 2025 11:10:45 +0100
|
||||
Subject: [PATCH] Issue 6542 - RPM build errors on Fedora 42
|
||||
|
||||
Bug Description:
|
||||
Fedora 42 has unified `/bin` and `/sbin`: https://fedoraproject.org/wiki/Changes/Unify_bin_and_sbin
|
||||
https://docs.fedoraproject.org/en-US/packaging-guidelines/#_merged_file_system_layout
|
||||
|
||||
This change causes RPM build to fail with:
|
||||
```
|
||||
RPM build errors:
|
||||
File not found: /builddir/build/BUILD/389-ds-base-3.1.2-build/BUILDROOT/usr/bin/openldap_to_ds
|
||||
```
|
||||
|
||||
Fix Description:
|
||||
Patch `setup.py.in` based on Fedora or RHEL version that support unified
|
||||
`/bin` and `/sbin`.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/6542
|
||||
|
||||
Reviewed by: @mreynolds389, @droideck (Thanks!)
|
||||
---
|
||||
rpm/389-ds-base.spec.in | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
|
||||
index 258d94698..44a158ce5 100644
|
||||
--- a/rpm/389-ds-base.spec.in
|
||||
+++ b/rpm/389-ds-base.spec.in
|
||||
@@ -522,6 +522,9 @@ autoreconf -fiv
|
||||
|
||||
%if 0%{?rhel} > 7 || 0%{?fedora}
|
||||
# lib389
|
||||
+%if 0%{?fedora} >= 42 || 0%{?rhel} >= 11
|
||||
+ sed -i "/prefix/s@sbin@bin@g" src/lib389/setup.py.in
|
||||
+%endif
|
||||
make src/lib389/setup.py
|
||||
pushd ./src/lib389
|
||||
%py3_build
|
||||
--
|
||||
2.52.0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,785 +0,0 @@
|
||||
From 7a45b50bf3d9cfc8f7e805379fcb39d5669d6c8b Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Revert index scan limits for system indexes
|
||||
|
||||
This reverts changes introduced by the following commits:
|
||||
c6f458b42 Issue 7189 - DSBLE0007 generates incorrect remediation commands for scan limits
|
||||
8b6b3a9f9 Issue 6966 - On large DB, unlimited IDL scan limit reduce the SRCH performance
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
.../tests/suites/config/config_test.py | 27 +---
|
||||
.../healthcheck/health_system_indexes_test.py | 135 +-----------------
|
||||
.../paged_results/paged_results_test.py | 25 +---
|
||||
ldap/servers/slapd/back-ldbm/back-ldbm.h | 1 -
|
||||
ldap/servers/slapd/back-ldbm/index.c | 2 -
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 106 +++-----------
|
||||
ldap/servers/slapd/back-ldbm/ldbm_config.c | 30 ----
|
||||
ldap/servers/slapd/back-ldbm/ldbm_config.h | 1 -
|
||||
.../slapd/back-ldbm/ldbm_index_config.c | 8 --
|
||||
src/lib389/lib389/backend.py | 50 ++-----
|
||||
src/lib389/lib389/cli_conf/backend.py | 20 ---
|
||||
11 files changed, 41 insertions(+), 364 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/config/config_test.py b/dirsrvtests/tests/suites/config/config_test.py
|
||||
index b9ba684d9..6f11e7fc8 100644
|
||||
--- a/dirsrvtests/tests/suites/config/config_test.py
|
||||
+++ b/dirsrvtests/tests/suites/config/config_test.py
|
||||
@@ -715,19 +715,17 @@ def test_ndn_cache_size_enforcement(topo, request):
|
||||
|
||||
request.addfinalizer(fin)
|
||||
|
||||
-def test_require_index(topo, request):
|
||||
+def test_require_index(topo):
|
||||
"""Validate that unindexed searches are rejected
|
||||
|
||||
:id: fb6e31f2-acc2-4e75-a195-5c356faeb803
|
||||
:setup: Standalone instance
|
||||
:steps:
|
||||
1. Set "nsslapd-require-index" to "on"
|
||||
- 2. ancestorid/idlscanlimit to 100
|
||||
- 3. Test an unindexed search is rejected
|
||||
+ 2. Test an unindexed search is rejected
|
||||
:expectedresults:
|
||||
1. Success
|
||||
2. Success
|
||||
- 3. Success
|
||||
"""
|
||||
|
||||
# Set the config
|
||||
@@ -738,10 +736,6 @@ def test_require_index(topo, request):
|
||||
|
||||
db_cfg = DatabaseConfig(topo.standalone)
|
||||
db_cfg.set([('nsslapd-idlistscanlimit', '100')])
|
||||
- backend = Backends(topo.standalone).get_backend(DEFAULT_SUFFIX)
|
||||
- ancestorid_index = backend.get_index('ancestorid')
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=100 type=eq flags=AND"))
|
||||
- topo.standalone.restart()
|
||||
|
||||
users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
|
||||
for i in range(101):
|
||||
@@ -752,15 +746,10 @@ def test_require_index(topo, request):
|
||||
with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||
raw_objects.filter("(description=test*)")
|
||||
|
||||
- def fin():
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=5000 type=eq flags=AND"))
|
||||
-
|
||||
- request.addfinalizer(fin)
|
||||
-
|
||||
|
||||
|
||||
@pytest.mark.skipif(ds_is_older('1.4.2'), reason="The config setting only exists in 1.4.2 and higher")
|
||||
-def test_require_internal_index(topo, request):
|
||||
+def test_require_internal_index(topo):
|
||||
"""Ensure internal operations require indexed attributes
|
||||
|
||||
:id: 22b94f30-59e3-4f27-89a1-c4f4be036f7f
|
||||
@@ -791,10 +780,6 @@ def test_require_internal_index(topo, request):
|
||||
# Create a bunch of users
|
||||
db_cfg = DatabaseConfig(topo.standalone)
|
||||
db_cfg.set([('nsslapd-idlistscanlimit', '100')])
|
||||
- backend = Backends(topo.standalone).get_backend(DEFAULT_SUFFIX)
|
||||
- ancestorid_index = backend.get_index('ancestorid')
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=100 type=eq flags=AND"))
|
||||
- topo.standalone.restart()
|
||||
users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
|
||||
for i in range(102, 202):
|
||||
users.create_test_user(uid=i)
|
||||
@@ -819,12 +804,6 @@ def test_require_internal_index(topo, request):
|
||||
with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||
user.delete()
|
||||
|
||||
- def fin():
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=5000 type=eq flags=AND"))
|
||||
-
|
||||
- request.addfinalizer(fin)
|
||||
-
|
||||
-
|
||||
|
||||
def get_pstack(pid):
|
||||
"""Get a pstack of the pid."""
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 5eadf6283..61972d60c 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -171,8 +171,7 @@ def test_missing_parentid(topology_st, log_buffering_enabled):
|
||||
|
||||
log.info("Re-add the parentId index")
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
- backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"],
|
||||
- idlistscanlimit=['limit=5000 type=eq flags=AND'])
|
||||
+ backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
@@ -260,8 +259,7 @@ def test_usn_plugin_missing_entryusn(topology_st, usn_plugin_enabled, log_buffer
|
||||
|
||||
log.info("Re-add the entryusn index")
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
- backend.add_index("entryusn", ["eq"], matching_rules=["integerOrderingMatch"],
|
||||
- idlistscanlimit=['limit=5000 type=eq flags=AND'])
|
||||
+ backend.add_index("entryusn", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
@@ -405,132 +403,6 @@ def test_retrocl_plugin_missing_matching_rule(topology_st, retrocl_plugin_enable
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
|
||||
-def test_missing_scanlimit(topology_st, log_buffering_enabled):
|
||||
- """Check if healthcheck returns DSBLE0007 code when parentId index is missing scanlimit
|
||||
-
|
||||
- :id: 40e1bf6a-2397-459b-bdf3-f787ca118b86
|
||||
- :setup: Standalone instance
|
||||
- :steps:
|
||||
- 1. Create DS instance
|
||||
- 2. Remove nsIndexIDListScanLimit from parentId index
|
||||
- 3. Use healthcheck without --json option
|
||||
- 4. Use healthcheck with --json option
|
||||
- 5. Verify the remediation command has properly quoted scanlimit
|
||||
- 6. Re-add the scanlimit
|
||||
- 7. Use healthcheck without --json option
|
||||
- 8. Use healthcheck with --json option
|
||||
- :expectedresults:
|
||||
- 1. Success
|
||||
- 2. Success
|
||||
- 3. healthcheck reports DSBLE0007 code and related details
|
||||
- 4. healthcheck reports DSBLE0007 code and related details
|
||||
- 5. The scanlimit value is quoted in the remediation command
|
||||
- 6. Success
|
||||
- 7. healthcheck reports no issues found
|
||||
- 8. healthcheck reports no issues found
|
||||
- """
|
||||
-
|
||||
- RET_CODE = "DSBLE0007"
|
||||
- PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
- SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
-
|
||||
- standalone = topology_st.standalone
|
||||
-
|
||||
- log.info("Remove nsIndexIDListScanLimit from parentId index")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=RET_CODE)
|
||||
-
|
||||
- # Verify the remediation command has properly quoted scanlimit
|
||||
- args = FakeArgs()
|
||||
- args.instance = standalone.serverid
|
||||
- args.verbose = standalone.verbose
|
||||
- args.list_errors = False
|
||||
- args.list_checks = False
|
||||
- args.exclude_check = []
|
||||
- args.check = ["backends"]
|
||||
- args.dry_run = False
|
||||
- args.json = False
|
||||
- health_check_run(standalone, topology_st.logcap.log, args)
|
||||
- # Check that the scanlimit is quoted in the output
|
||||
- assert topology_st.logcap.contains('--add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
- log.info("Verified scanlimit is properly quoted in remediation command")
|
||||
- topology_st.logcap.flush()
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=RET_CODE)
|
||||
-
|
||||
- log.info("Re-add the nsIndexIDListScanLimit")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
-
|
||||
-
|
||||
-def test_missing_matching_rule_and_scanlimit(topology_st, log_buffering_enabled):
|
||||
- """Check if healthcheck generates a single combined command when both matching rule and scanlimit are missing
|
||||
-
|
||||
- :id: af8214ad-5e4c-422a-8f74-3e99227551df
|
||||
- :setup: Standalone instance
|
||||
- :steps:
|
||||
- 1. Create DS instance
|
||||
- 2. Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index
|
||||
- 3. Use healthcheck and verify a single combined command is generated
|
||||
- 4. Re-add the matching rule and scanlimit
|
||||
- 5. Use healthcheck without --json option
|
||||
- 6. Use healthcheck with --json option
|
||||
- :expectedresults:
|
||||
- 1. Success
|
||||
- 2. Success
|
||||
- 3. healthcheck reports DSBLE0007 and generates a single command with both --add-mr and --add-scanlimit
|
||||
- 4. Success
|
||||
- 5. healthcheck reports no issues found
|
||||
- 6. healthcheck reports no issues found
|
||||
- """
|
||||
-
|
||||
- RET_CODE = "DSBLE0007"
|
||||
- PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
- SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
-
|
||||
- standalone = topology_st.standalone
|
||||
-
|
||||
- log.info("Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.remove("nsMatchingRule", "integerOrderingMatch")
|
||||
- parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- # Run healthcheck and verify combined command
|
||||
- args = FakeArgs()
|
||||
- args.instance = standalone.serverid
|
||||
- args.verbose = standalone.verbose
|
||||
- args.list_errors = False
|
||||
- args.list_checks = False
|
||||
- args.exclude_check = []
|
||||
- args.check = ["backends"]
|
||||
- args.dry_run = False
|
||||
- args.json = False
|
||||
- health_check_run(standalone, topology_st.logcap.log, args)
|
||||
-
|
||||
- # Verify DSBLE0007 is reported
|
||||
- assert topology_st.logcap.contains(RET_CODE)
|
||||
- log.info("healthcheck returned code: %s" % RET_CODE)
|
||||
-
|
||||
- # Verify a single combined command is generated with both --add-mr and --add-scanlimit
|
||||
- assert topology_st.logcap.contains('--add-mr integerOrderingMatch --add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
- log.info("Verified combined command with both --add-mr and --add-scanlimit")
|
||||
-
|
||||
- topology_st.logcap.flush()
|
||||
-
|
||||
- log.info("Re-add the integerOrderingMatch matching rule and scanlimit")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.add("nsMatchingRule", "integerOrderingMatch")
|
||||
- parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
-
|
||||
-
|
||||
def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
"""Check if healthcheck returns DSBLE0007 code when multiple system indexes are missing
|
||||
|
||||
@@ -571,8 +443,7 @@ def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
|
||||
log.info("Re-add the missing system indexes")
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
- backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"],
|
||||
- idlistscanlimit=['limit=5000 type=eq flags=AND'])
|
||||
+ backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
backend.add_index("nsuniqueid", ["eq"])
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
diff --git a/dirsrvtests/tests/suites/paged_results/paged_results_test.py b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
index 61d6702da..1bb94b53a 100644
|
||||
--- a/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
+++ b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
@@ -306,19 +306,19 @@ def test_search_success(topology_st, create_user, page_size, users_num):
|
||||
del_users(users_list)
|
||||
|
||||
|
||||
-@pytest.mark.parametrize("page_size,users_num,suffix,attr_name,attr_value,expected_err, restart", [
|
||||
+@pytest.mark.parametrize("page_size,users_num,suffix,attr_name,attr_value,expected_err", [
|
||||
(50, 200, 'cn=config,%s' % DN_LDBM, 'nsslapd-idlistscanlimit', '100',
|
||||
- ldap.UNWILLING_TO_PERFORM, True),
|
||||
+ ldap.UNWILLING_TO_PERFORM),
|
||||
(5, 15, DN_CONFIG, 'nsslapd-timelimit', '20',
|
||||
- ldap.UNAVAILABLE_CRITICAL_EXTENSION, False),
|
||||
+ ldap.UNAVAILABLE_CRITICAL_EXTENSION),
|
||||
(21, 50, DN_CONFIG, 'nsslapd-sizelimit', '20',
|
||||
- ldap.SIZELIMIT_EXCEEDED, False),
|
||||
+ ldap.SIZELIMIT_EXCEEDED),
|
||||
(21, 50, DN_CONFIG, 'nsslapd-pagedsizelimit', '5',
|
||||
- ldap.SIZELIMIT_EXCEEDED, False),
|
||||
+ ldap.SIZELIMIT_EXCEEDED),
|
||||
(5, 50, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', '20',
|
||||
- ldap.ADMINLIMIT_EXCEEDED, False)])
|
||||
+ ldap.ADMINLIMIT_EXCEEDED)])
|
||||
def test_search_limits_fail(topology_st, create_user, page_size, users_num,
|
||||
- suffix, attr_name, attr_value, expected_err, restart):
|
||||
+ suffix, attr_name, attr_value, expected_err):
|
||||
"""Verify that search with a simple paged results control
|
||||
throws expected exceptoins when corresponding limits are
|
||||
exceeded.
|
||||
@@ -341,15 +341,6 @@ def test_search_limits_fail(topology_st, create_user, page_size, users_num,
|
||||
|
||||
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
|
||||
attr_value_bck = change_conf_attr(topology_st, suffix, attr_name, attr_value)
|
||||
- ancestorid_index = None
|
||||
- if attr_name == 'nsslapd-idlistscanlimit':
|
||||
- backend = Backends(topology_st.standalone).get_backend(DEFAULT_SUFFIX)
|
||||
- ancestorid_index = backend.get_index('ancestorid')
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=100 type=eq flags=AND"))
|
||||
-
|
||||
- if (restart):
|
||||
- log.info('Instance restarted')
|
||||
- topology_st.standalone.restart()
|
||||
conf_param_dict = {attr_name: attr_value}
|
||||
search_flt = r'(uid=test*)'
|
||||
searchreq_attrlist = ['dn', 'sn']
|
||||
@@ -402,8 +393,6 @@ def test_search_limits_fail(topology_st, create_user, page_size, users_num,
|
||||
else:
|
||||
break
|
||||
finally:
|
||||
- if ancestorid_index:
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=5000 type=eq flags=AND"))
|
||||
del_users(users_list)
|
||||
change_conf_attr(topology_st, suffix, attr_name, attr_value_bck)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
index 5e4988782..a4993208c 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
@@ -561,7 +561,6 @@ struct ldbminfo
|
||||
int li_mode;
|
||||
int li_lookthroughlimit;
|
||||
int li_allidsthreshold;
|
||||
- int li_system_allidsthreshold;
|
||||
char *li_directory;
|
||||
int li_reslimit_lookthrough_handle;
|
||||
uint64_t li_dbcachesize;
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
|
||||
index e06a8ee5f..90129b682 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/index.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/index.c
|
||||
@@ -1007,8 +1007,6 @@ index_read_ext_allids(
|
||||
}
|
||||
if (pb) {
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_IS_AND, &is_and);
|
||||
- } else if (strcasecmp(type, LDBM_ANCESTORID_STR) == 0) {
|
||||
- is_and = 1;
|
||||
}
|
||||
ai_flags = is_and ? INDEX_ALLIDS_FLAG_AND : 0;
|
||||
/* the caller can pass in a value of 0 - just ignore those - but if the index
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 23bd70243..f9a546661 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/* Forward declarations */
|
||||
static void ldbm_instance_destructor(void **arg);
|
||||
-Slapi_Entry *ldbm_instance_init_config_entry(char *cn_val, char *v1, char *v2, char *v3, char *v4, char *mr, char *scanlimit);
|
||||
+Slapi_Entry *ldbm_instance_init_config_entry(char *cn_val, char *v1, char *v2, char *v3, char *v4, char *mr);
|
||||
|
||||
|
||||
/* Creates and initializes a new ldbm_instance structure.
|
||||
@@ -127,7 +127,7 @@ done:
|
||||
* Take a bunch of strings, and create a index config entry
|
||||
*/
|
||||
Slapi_Entry *
|
||||
-ldbm_instance_init_config_entry(char *cn_val, char *val1, char *val2, char *val3, char *val4, char *mr, char *scanlimit)
|
||||
+ldbm_instance_init_config_entry(char *cn_val, char *val1, char *val2, char *val3, char *val4, char *mr)
|
||||
{
|
||||
Slapi_Entry *e = slapi_entry_alloc();
|
||||
struct berval *vals[2];
|
||||
@@ -168,11 +168,6 @@ ldbm_instance_init_config_entry(char *cn_val, char *val1, char *val2, char *val3
|
||||
slapi_entry_add_values(e, "nsMatchingRule", vals);
|
||||
}
|
||||
|
||||
- if (scanlimit) {
|
||||
- val.bv_val = scanlimit;
|
||||
- val.bv_len = strlen(scanlimit);
|
||||
- slapi_entry_add_values(e, "nsIndexIDListScanLimit", vals);
|
||||
- }
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -185,60 +180,8 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
{
|
||||
Slapi_Entry *e;
|
||||
ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
|
||||
- struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
/* write the dse file only on the final index */
|
||||
int flags = LDBM_INSTANCE_CONFIG_DONT_WRITE;
|
||||
- char *ancestorid_indexes_limit = NULL;
|
||||
- char *parentid_indexes_limit = NULL;
|
||||
- struct attrinfo *ai = NULL;
|
||||
- int index_already_configured = 0;
|
||||
- struct index_idlistsizeinfo *iter;
|
||||
- int cookie;
|
||||
- int limit;
|
||||
-
|
||||
- ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
- if (ai && ai->ai_idlistinfo) {
|
||||
- iter = (struct index_idlistsizeinfo *)dl_get_first(ai->ai_idlistinfo, &cookie);
|
||||
- if (iter) {
|
||||
- limit = iter->ai_idlistsizelimit;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set ancestorid limit to %d from attribute index\n",
|
||||
- limit);
|
||||
- } else {
|
||||
- limit = li->li_system_allidsthreshold;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set ancestorid limit to %d from default (fail to read limit)\n",
|
||||
- limit);
|
||||
- }
|
||||
- ancestorid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", limit);
|
||||
- } else {
|
||||
- ancestorid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", li->li_system_allidsthreshold);
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set ancestorid limit to %d from default (no attribute or limit)\n",
|
||||
- li->li_system_allidsthreshold);
|
||||
- }
|
||||
-
|
||||
- ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
- if (ai && ai->ai_idlistinfo) {
|
||||
- iter = (struct index_idlistsizeinfo *)dl_get_first(ai->ai_idlistinfo, &cookie);
|
||||
- if (iter) {
|
||||
- limit = iter->ai_idlistsizelimit;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set parentid limit to %d from attribute index\n",
|
||||
- limit);
|
||||
- } else {
|
||||
- limit = li->li_system_allidsthreshold;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set parentid limit to %d from default (fail to read limit)\n",
|
||||
- limit);
|
||||
- }
|
||||
- parentid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", limit);
|
||||
- } else {
|
||||
- parentid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", li->li_system_allidsthreshold);
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set parentid limit to %d from default (no attribute or limit)\n",
|
||||
- li->li_system_allidsthreshold);
|
||||
- }
|
||||
|
||||
/*
|
||||
* Always index (entrydn or entryrdn), parentid, objectclass,
|
||||
@@ -247,53 +190,47 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* ACL routines.
|
||||
*/
|
||||
if (entryrdn_get_switch()) { /* subtree-rename: on */
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ENTRYRDN_STR, "subtree", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ENTRYRDN_STR, "subtree", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
} else {
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ENTRYDN_STR, "eq", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ENTRYDN_STR, "eq", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
}
|
||||
|
||||
- ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
- /* Check if the attrinfo is actually for parentid, not a fallback to .default */
|
||||
- index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_PARENTID_STR) == 0);
|
||||
- if (!index_already_configured) {
|
||||
- e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
- }
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch");
|
||||
+ ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
+ slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry("objectclass", "eq", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry("objectclass", "eq", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry("aci", "pres", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry("aci", "pres", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry(LDBM_NUMSUBORDINATES_STR, "pres", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_NUMSUBORDINATES_STR, "pres", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry(SLAPI_ATTR_UNIQUEID, "eq", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(SLAPI_ATTR_UNIQUEID, "eq", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
/* For MMR, we need this attribute (to replace use of dncomp in delete). */
|
||||
- e = ldbm_instance_init_config_entry(ATTR_NSDS5_REPLCONFLICT, "eq", "pres", 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(ATTR_NSDS5_REPLCONFLICT, "eq", "pres", 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
/* write the dse file only on the final index */
|
||||
- e = ldbm_instance_init_config_entry(SLAPI_ATTR_NSCP_ENTRYDN, "eq", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(SLAPI_ATTR_NSCP_ENTRYDN, "eq", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
/* ldbm_instance_config_add_index_entry(inst, 2, argv); */
|
||||
- e = ldbm_instance_init_config_entry(LDBM_PSEUDO_ATTR_DEFAULT, "none", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_PSEUDO_ATTR_DEFAULT, "none", 0, 0, 0, 0);
|
||||
attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
slapi_entry_free(e);
|
||||
|
||||
@@ -302,20 +239,11 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* ancestorid is special, there is actually no such attr type
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
- ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
- /* Check if the attrinfo is actually for ancestorid, not a fallback to .default */
|
||||
- index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_ANCESTORID_STR) == 0);
|
||||
- if (!index_already_configured) {
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
- }
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch");
|
||||
+ attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
+ slapi_entry_free(e);
|
||||
}
|
||||
|
||||
- slapi_ch_free_string(&ancestorid_indexes_limit);
|
||||
- slapi_ch_free_string(&parentid_indexes_limit);
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.c b/ldap/servers/slapd/back-ldbm/ldbm_config.c
|
||||
index d8ffc4479..5cce0dbd3 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_config.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.c
|
||||
@@ -386,35 +386,6 @@ ldbm_config_allidsthreshold_set(void *arg, void *value, char *errorbuf __attribu
|
||||
return retval;
|
||||
}
|
||||
|
||||
-static void *
|
||||
-ldbm_config_system_allidsthreshold_get(void *arg)
|
||||
-{
|
||||
- struct ldbminfo *li = (struct ldbminfo *)arg;
|
||||
-
|
||||
- return (void *)((uintptr_t)(li->li_system_allidsthreshold));
|
||||
-}
|
||||
-
|
||||
-static int
|
||||
-ldbm_config_system_allidsthreshold_set(void *arg, void *value, char *errorbuf __attribute__((unused)), int phase __attribute__((unused)), int apply)
|
||||
-{
|
||||
- struct ldbminfo *li = (struct ldbminfo *)arg;
|
||||
- int retval = LDAP_SUCCESS;
|
||||
- int val = (int)((uintptr_t)value);
|
||||
-
|
||||
- /* Do whatever we can to make sure the data is ok. */
|
||||
-
|
||||
- /* Catch attempts to configure a stupidly low ancestorid allidsthreshold */
|
||||
- if ((val > -1) && (val < 5000)) {
|
||||
- val = 5000;
|
||||
- }
|
||||
-
|
||||
- if (apply) {
|
||||
- li->li_system_allidsthreshold = val;
|
||||
- }
|
||||
-
|
||||
- return retval;
|
||||
-}
|
||||
-
|
||||
static void *
|
||||
ldbm_config_pagedallidsthreshold_get(void *arg)
|
||||
{
|
||||
@@ -1133,7 +1104,6 @@ static config_info ldbm_config[] = {
|
||||
{CONFIG_LOOKTHROUGHLIMIT, CONFIG_TYPE_INT, "5000", &ldbm_config_lookthroughlimit_get, &ldbm_config_lookthroughlimit_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_MODE, CONFIG_TYPE_INT_OCTAL, "0600", &ldbm_config_mode_get, &ldbm_config_mode_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_IDLISTSCANLIMIT, CONFIG_TYPE_INT, "2147483646", &ldbm_config_allidsthreshold_get, &ldbm_config_allidsthreshold_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
- {CONFIG_SYSTEMIDLISTSCANLIMIT, CONFIG_TYPE_INT, "5000", &ldbm_config_system_allidsthreshold_get, &ldbm_config_system_allidsthreshold_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_DIRECTORY, CONFIG_TYPE_STRING, "", &ldbm_config_directory_get, &ldbm_config_directory_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE | CONFIG_FLAG_SKIP_DEFAULT_SETTING},
|
||||
{CONFIG_MAXPASSBEFOREMERGE, CONFIG_TYPE_INT, "100", &ldbm_config_maxpassbeforemerge_get, &ldbm_config_maxpassbeforemerge_set, 0},
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.h b/ldap/servers/slapd/back-ldbm/ldbm_config.h
|
||||
index 30e3477ed..e1f05d7a2 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_config.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.h
|
||||
@@ -60,7 +60,6 @@ struct config_info
|
||||
#define CONFIG_RANGELOOKTHROUGHLIMIT "nsslapd-rangelookthroughlimit"
|
||||
#define CONFIG_PAGEDLOOKTHROUGHLIMIT "nsslapd-pagedlookthroughlimit"
|
||||
#define CONFIG_IDLISTSCANLIMIT "nsslapd-idlistscanlimit"
|
||||
-#define CONFIG_SYSTEMIDLISTSCANLIMIT "nsslapd-systemidlistscanlimit"
|
||||
#define CONFIG_PAGEDIDLISTSCANLIMIT "nsslapd-pagedidlistscanlimit"
|
||||
#define CONFIG_DIRECTORY "nsslapd-directory"
|
||||
#define CONFIG_MODE "nsslapd-mode"
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
|
||||
index bae2a64b9..38e7368e1 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
|
||||
@@ -384,14 +384,6 @@ ldbm_instance_config_add_index_entry(
|
||||
}
|
||||
}
|
||||
|
||||
- /* get nsIndexIDListScanLimit and its values, and add them */
|
||||
- if (0 == slapi_entry_attr_find(e, "nsIndexIDListScanLimit", &attr)) {
|
||||
- for (j = slapi_attr_first_value(attr, &sval); j != -1; j = slapi_attr_next_value(attr, j, &sval)) {
|
||||
- attrValue = slapi_value_get_berval(sval);
|
||||
- eBuf = PR_sprintf_append(eBuf, "nsIndexIDListScanLimit: %s\n", attrValue->bv_val);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
ldbm_config_add_dse_entry(li, eBuf, flags);
|
||||
if (eBuf) {
|
||||
PR_smprintf_free(eBuf);
|
||||
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
||||
index d3c9ccf35..1d9be4683 100644
|
||||
--- a/src/lib389/lib389/backend.py
|
||||
+++ b/src/lib389/lib389/backend.py
|
||||
@@ -615,10 +615,11 @@ class Backend(DSLdapObject):
|
||||
indexes = self.get_indexes()
|
||||
|
||||
# Default system indexes taken from ldap/servers/slapd/back-ldbm/instance.c
|
||||
+ # Note: entryrdn and ancestorid are internal system indexes that are not
|
||||
+ # exposed in cn=config - they are managed internally by the server.
|
||||
+ # Only parentid has a DSE config entry (for the integerOrderingMatch rule).
|
||||
expected_system_indexes = {
|
||||
- 'entryrdn': {'types': ['subtree'], 'matching_rule': None},
|
||||
- 'parentid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch', 'scanlimit': 'limit=5000 type=eq flags=AND'},
|
||||
- 'ancestorid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch', 'scanlimit': 'limit=5000 type=eq flags=AND'},
|
||||
+ 'parentid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch'},
|
||||
'objectClass': {'types': ['eq'], 'matching_rule': None},
|
||||
'aci': {'types': ['pres'], 'matching_rule': None},
|
||||
'nscpEntryDN': {'types': ['eq'], 'matching_rule': None},
|
||||
@@ -675,17 +676,14 @@ class Backend(DSLdapObject):
|
||||
# Generate remediation command
|
||||
index_types = ' '.join([f"--index-type {t}" for t in expected_config['types']])
|
||||
cmd = f"dsconf YOUR_INSTANCE backend index add {bename} --attr {attr_name} {index_types}"
|
||||
- if expected_config.get('matching_rule'):
|
||||
+ if expected_config['matching_rule']:
|
||||
cmd += f" --matching-rule {expected_config['matching_rule']}"
|
||||
- if expected_config.get('scanlimit'):
|
||||
- cmd += f" --add-scanlimit \"{expected_config['scanlimit']}\""
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name) # New index needs reindexing
|
||||
else:
|
||||
# Index exists, check configuration
|
||||
actual_types = index.get_attr_vals_utf8('nsIndexType') or []
|
||||
actual_mrs = index.get_attr_vals_utf8('nsMatchingRule') or []
|
||||
- actual_scanlimit = index.get_attr_vals_utf8('nsIndexIDListScanLimit') or []
|
||||
|
||||
# Normalize to lowercase for comparison
|
||||
actual_types = [t.lower() for t in actual_types]
|
||||
@@ -700,31 +698,16 @@ class Backend(DSLdapObject):
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name)
|
||||
|
||||
- # Check matching rules and scanlimit together to generate a single combined command
|
||||
+ # Check matching rules
|
||||
expected_mr = expected_config.get('matching_rule')
|
||||
- expected_scanlimit = expected_config.get('scanlimit')
|
||||
-
|
||||
- missing_mr = False
|
||||
if expected_mr:
|
||||
actual_mrs_lower = [mr.lower() for mr in actual_mrs]
|
||||
if expected_mr.lower() not in actual_mrs_lower:
|
||||
discrepancies.append(f"Index {attr_name} missing matching rule: {expected_mr}")
|
||||
- missing_mr = True
|
||||
-
|
||||
- missing_scanlimit = False
|
||||
- if expected_scanlimit and (len(actual_scanlimit) == 0):
|
||||
- discrepancies.append(f"Index {attr_name} missing fine grain definition of IDs limit: {expected_scanlimit}")
|
||||
- missing_scanlimit = True
|
||||
-
|
||||
- # Generate a single combined command for all missing items
|
||||
- if missing_mr or missing_scanlimit:
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name}"
|
||||
- if missing_mr:
|
||||
- cmd += f" --add-mr {expected_mr}"
|
||||
- if missing_scanlimit:
|
||||
- cmd += f" --add-scanlimit \"{expected_scanlimit}\""
|
||||
- remediation_commands.append(cmd)
|
||||
- reindex_attrs.add(attr_name)
|
||||
+ # Add the missing matching rule
|
||||
+ cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-mr {expected_mr}"
|
||||
+ remediation_commands.append(cmd)
|
||||
+ reindex_attrs.add(attr_name)
|
||||
|
||||
except Exception as e:
|
||||
self._log.debug(f"_lint_system_indexes - Error checking index {attr_name}: {e}")
|
||||
@@ -963,13 +946,12 @@ class Backend(DSLdapObject):
|
||||
return
|
||||
raise ValueError("Can not delete index because it does not exist")
|
||||
|
||||
- def add_index(self, attr_name, types, matching_rules=None, idlistscanlimit=None, reindex=False):
|
||||
+ def add_index(self, attr_name, types, matching_rules=None, reindex=False):
|
||||
""" Add an index.
|
||||
|
||||
:param attr_name - name of the attribute to index
|
||||
:param types - a List of index types(eq, pres, sub, approx)
|
||||
:param matching_rules - a List of matching rules for the index
|
||||
- :param idlistscanlimit - a List of fine grain definitions for scanning limit
|
||||
:param reindex - If set to True then index the attribute after creating it.
|
||||
"""
|
||||
|
||||
@@ -999,15 +981,6 @@ class Backend(DSLdapObject):
|
||||
# Only add if there are actually rules present in the list.
|
||||
if len(mrs) > 0:
|
||||
props['nsMatchingRule'] = mrs
|
||||
-
|
||||
- if idlistscanlimit is not None:
|
||||
- scanlimits = []
|
||||
- for scanlimit in idlistscanlimit:
|
||||
- scanlimits.append(scanlimit)
|
||||
- # Only add if there are actually limits in the list.
|
||||
- if len(scanlimits) > 0:
|
||||
- props['nsIndexIDListScanLimit'] = scanlimits
|
||||
-
|
||||
new_index.create(properties=props, basedn="cn=index," + self._dn)
|
||||
|
||||
if reindex:
|
||||
@@ -1314,7 +1287,6 @@ class DatabaseConfig(DSLdapObject):
|
||||
'nsslapd-lookthroughlimit',
|
||||
'nsslapd-mode',
|
||||
'nsslapd-idlistscanlimit',
|
||||
- 'nsslapd-systemidlistscanlimit',
|
||||
'nsslapd-directory',
|
||||
'nsslapd-import-cachesize',
|
||||
'nsslapd-idl-switch',
|
||||
diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py
|
||||
index dcf57d006..80008d22d 100644
|
||||
--- a/src/lib389/lib389/cli_conf/backend.py
|
||||
+++ b/src/lib389/lib389/cli_conf/backend.py
|
||||
@@ -39,7 +39,6 @@ arg_to_attr = {
|
||||
'mode': 'nsslapd-mode',
|
||||
'state': 'nsslapd-state',
|
||||
'idlistscanlimit': 'nsslapd-idlistscanlimit',
|
||||
- 'systemidlistscanlimit': 'nsslapd-systemidlistscanlimit',
|
||||
'directory': 'nsslapd-directory',
|
||||
'dbcachesize': 'nsslapd-dbcachesize',
|
||||
'logdirectory': 'nsslapd-db-logdirectory',
|
||||
@@ -626,21 +625,6 @@ def backend_set_index(inst, basedn, log, args):
|
||||
except ldap.NO_SUCH_ATTRIBUTE:
|
||||
raise ValueError('Can not delete matching rule type because it does not exist')
|
||||
|
||||
- if args.replace_scanlimit is not None:
|
||||
- for replace_scanlimit in args.replace_scanlimit:
|
||||
- index.replace('nsIndexIDListScanLimit', replace_scanlimit)
|
||||
-
|
||||
- if args.add_scanlimit is not None:
|
||||
- for add_scanlimit in args.add_scanlimit:
|
||||
- index.add('nsIndexIDListScanLimit', add_scanlimit)
|
||||
-
|
||||
- if args.del_scanlimit is not None:
|
||||
- for del_scanlimit in args.del_scanlimit:
|
||||
- try:
|
||||
- index.remove('nsIndexIDListScanLimit', del_scanlimit)
|
||||
- except ldap.NO_SUCH_ATTRIBUTE:
|
||||
- raise ValueError('Can not delete a fine grain limit definition because it does not exist')
|
||||
-
|
||||
if args.reindex:
|
||||
be.reindex(attrs=[args.attr])
|
||||
log.info("Index successfully updated")
|
||||
@@ -962,9 +946,6 @@ def create_parser(subparsers):
|
||||
edit_index_parser.add_argument('--del-type', action='append', help='Removes an index type from the index: (eq, sub, pres, or approx)')
|
||||
edit_index_parser.add_argument('--add-mr', action='append', help='Adds a matching-rule to the index')
|
||||
edit_index_parser.add_argument('--del-mr', action='append', help='Removes a matching-rule from the index')
|
||||
- edit_index_parser.add_argument('--add-scanlimit', action='append', help='Adds a fine grain limit definiton to the index')
|
||||
- edit_index_parser.add_argument('--replace-scanlimit', action='append', help='Replaces a fine grain limit definiton to the index')
|
||||
- edit_index_parser.add_argument('--del-scanlimit', action='append', help='Removes a fine grain limit definiton to the index')
|
||||
edit_index_parser.add_argument('--reindex', action='store_true', help='Re-indexes the database after editing the index')
|
||||
edit_index_parser.add_argument('be_name', help='The backend name or suffix')
|
||||
|
||||
@@ -1091,7 +1072,6 @@ def create_parser(subparsers):
|
||||
'will check when examining candidate entries in response to a search request')
|
||||
set_db_config_parser.add_argument('--mode', help='Specifies the permissions used for newly created index files')
|
||||
set_db_config_parser.add_argument('--idlistscanlimit', help='Specifies the number of entry IDs that are searched during a search operation')
|
||||
- set_db_config_parser.add_argument('--systemidlistscanlimit', help='Specifies the number of entry IDs that are fetch from ancestorid/parentid indexes')
|
||||
set_db_config_parser.add_argument('--directory', help='Specifies absolute path to database instance')
|
||||
set_db_config_parser.add_argument('--dbcachesize', help='Specifies the database index cache size in bytes')
|
||||
set_db_config_parser.add_argument('--logdirectory', help='Specifies the path to the directory that contains the database transaction logs')
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,212 +0,0 @@
|
||||
From a58212943da2604091e7d5a20829ab54970d41c9 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Add upgrade function to remove
|
||||
nsIndexIDListScanLimit from parentid
|
||||
|
||||
Description:
|
||||
Add `upgrade_remove_index_scanlimit()` function that removes the
|
||||
nsIndexIDListScanLimit attribute from parentid index configuration
|
||||
if present.
|
||||
|
||||
This attribute was incorrectly added by a previous version and can
|
||||
cause issues with index configuration. The upgrade function runs
|
||||
automatically on server startup and removes the attribute if found.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 52 +++++++++
|
||||
ldap/servers/slapd/upgrade.c | 105 ++++++++++++++++++
|
||||
2 files changed, 157 insertions(+)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 61972d60c..b0d7a99ec 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -450,6 +450,58 @@ def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
|
||||
+def test_upgrade_removes_parentid_scanlimit(topology_st):
|
||||
+ """Check if upgrade function removes nsIndexIDListScanLimit from parentid index
|
||||
+
|
||||
+ :id: 2808886e-c1c1-441d-b3a3-299c4ef1ab4a
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Stop the server
|
||||
+ 3. Use DSEldif to add nsIndexIDListScanLimit to parentid index
|
||||
+ 4. Start the server (triggers upgrade)
|
||||
+ 5. Verify nsIndexIDListScanLimit is removed from parentid index
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. nsIndexIDListScanLimit is no longer present
|
||||
+ """
|
||||
+ from lib389.dseldif import DSEldif
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+ PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+ SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
+
|
||||
+ log.info("Stop the server")
|
||||
+ standalone.stop()
|
||||
+
|
||||
+ log.info("Add nsIndexIDListScanLimit to parentid index using DSEldif")
|
||||
+ dse_ldif = DSEldif(standalone)
|
||||
+ dse_ldif.add(PARENTID_DN, "nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ # Verify it was added
|
||||
+ scanlimit = dse_ldif.get(PARENTID_DN, "nsIndexIDListScanLimit")
|
||||
+ assert scanlimit is not None, "Failed to add nsIndexIDListScanLimit"
|
||||
+ log.info(f"Added nsIndexIDListScanLimit: {scanlimit}")
|
||||
+
|
||||
+ log.info("Start the server (triggers upgrade)")
|
||||
+ standalone.start()
|
||||
+
|
||||
+ log.info("Verify nsIndexIDListScanLimit was removed by upgrade")
|
||||
+ # Check via LDAP - the upgrade should have removed it
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ scanlimit_after = parentid_index.get_attr_vals_utf8("nsIndexIDListScanLimit")
|
||||
+ log.info(f"nsIndexIDListScanLimit after upgrade: {scanlimit_after}")
|
||||
+
|
||||
+ # The upgrade function should have removed nsIndexIDListScanLimit
|
||||
+ assert not scanlimit_after, \
|
||||
+ f"nsIndexIDListScanLimit should have been removed but found: {scanlimit_after}"
|
||||
+
|
||||
+ log.info("Upgrade successfully removed nsIndexIDListScanLimit from parentid index")
|
||||
+
|
||||
+
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index 43906c1af..adfec63de 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -279,6 +279,107 @@ upgrade_205_fixup_repl_dep(void)
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Remove nsIndexIDListScanLimit from parentid index configuration.
|
||||
+ *
|
||||
+ * This attribute was incorrectly added by a previous version and can
|
||||
+ * cause issues with index configuration. Remove it if present.
|
||||
+ */
|
||||
+static upgrade_status
|
||||
+upgrade_remove_index_scanlimit(void)
|
||||
+{
|
||||
+ struct slapi_pblock *pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **backends = NULL;
|
||||
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
+ const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
+ const char *attrs_to_check[] = {"parentid", NULL};
|
||||
+ upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
+
|
||||
+ /* Search for all backend instances */
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ pb, be_base_dn,
|
||||
+ LDAP_SCOPE_ONELEVEL,
|
||||
+ be_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
+
|
||||
+ if (backends) {
|
||||
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
+ if (!be_dn || !be_name) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ for (size_t attr_idx = 0; attrs_to_check[attr_idx] != NULL; attr_idx++) {
|
||||
+ const char *attr_name = attrs_to_check[attr_idx];
|
||||
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **idx_entries = NULL;
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
|
||||
+ attr_name, be_dn);
|
||||
+ char *idx_filter = "(objectclass=nsIndex)";
|
||||
+
|
||||
+ if (!idx_dn) {
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ idx_pb, idx_dn,
|
||||
+ LDAP_SCOPE_BASE,
|
||||
+ idx_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(idx_pb);
|
||||
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
+
|
||||
+ if (idx_entries && idx_entries[0]) {
|
||||
+ /* Check if nsIndexIDListScanLimit is present */
|
||||
+ if (slapi_entry_attr_get_ref(idx_entries[0], "nsIndexIDListScanLimit") != NULL) {
|
||||
+ /* Remove nsIndexIDListScanLimit */
|
||||
+ Slapi_PBlock *mod_pb = slapi_pblock_new();
|
||||
+ Slapi_Mods smods;
|
||||
+ int rc;
|
||||
+
|
||||
+ slapi_mods_init(&smods, 1);
|
||||
+ slapi_mods_add(&smods, LDAP_MOD_DELETE, "nsIndexIDListScanLimit", 0, NULL);
|
||||
+
|
||||
+ slapi_modify_internal_set_pb(
|
||||
+ mod_pb, idx_dn,
|
||||
+ slapi_mods_get_ldapmods_byref(&smods),
|
||||
+ NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_modify_internal_pb(mod_pb);
|
||||
+ slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
||||
+
|
||||
+ if (rc == LDAP_SUCCESS) {
|
||||
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_index_scanlimit",
|
||||
+ "Removed 'nsIndexIDListScanLimit' from index '%s' in backend '%s'\n",
|
||||
+ attr_name, be_name);
|
||||
+ } else if (rc != LDAP_NO_SUCH_ATTRIBUTE) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_index_scanlimit",
|
||||
+ "Failed to remove 'nsIndexIDListScanLimit' from index '%s' in backend '%s': error %d\n",
|
||||
+ attr_name, be_name, rc);
|
||||
+ }
|
||||
+
|
||||
+ slapi_mods_done(&smods);
|
||||
+ slapi_pblock_destroy(mod_pb);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(idx_pb);
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return uresult;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
* matching rule.
|
||||
@@ -407,6 +508,10 @@ upgrade_server(void)
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
|
||||
+ if (upgrade_remove_index_scanlimit() != UPGRADE_SUCCESS) {
|
||||
+ return UPGRADE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,313 +0,0 @@
|
||||
From 9a9446b9bafe25eacf97039e66d6ef19d366315c Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Add upgrade function to remove ancestorid index
|
||||
config entry
|
||||
|
||||
Description:
|
||||
Add `upgrade_remove_ancestorid_index_config()` function that removes:
|
||||
* ancestorid from `cn=default indexes`
|
||||
* ancestorid index config entries from each backend's `cn=index`
|
||||
|
||||
Also remove ancestorid index configuration from template-dse.ldif.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 85 +++++++++++
|
||||
ldap/ldif/template-dse.ldif.in | 8 --
|
||||
ldap/servers/slapd/upgrade.c | 133 +++++++++++++++++-
|
||||
3 files changed, 214 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index b0d7a99ec..4b0c58835 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -501,6 +501,91 @@ def test_upgrade_removes_parentid_scanlimit(topology_st):
|
||||
|
||||
log.info("Upgrade successfully removed nsIndexIDListScanLimit from parentid index")
|
||||
|
||||
+ # Verify idempotency - restart again and ensure no errors
|
||||
+ log.info("Restart server again to verify idempotency (no errors on second run)")
|
||||
+ standalone.restart()
|
||||
+ # Verify the attribute is still absent
|
||||
+ scanlimit_after_second = parentid_index.get_attr_vals_utf8("nsIndexIDListScanLimit")
|
||||
+ assert not scanlimit_after_second, \
|
||||
+ f"nsIndexIDListScanLimit should still be absent after second restart but found: {scanlimit_after_second}"
|
||||
+ log.info("Idempotency verified - no issues on second restart")
|
||||
+
|
||||
+
|
||||
+def test_upgrade_removes_ancestorid_index_config(topology_st):
|
||||
+ """Check if upgrade function removes ancestorid index config entry
|
||||
+
|
||||
+ :id: 3f3d6e9b-75ac-4f0d-b2ce-7204e6eacd0a
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Stop the server
|
||||
+ 3. Use DSEldif to add an ancestorid index config entry
|
||||
+ 4. Start the server (triggers upgrade)
|
||||
+ 5. Verify ancestorid index config entry is removed
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. ancestorid index config entry is no longer present
|
||||
+ """
|
||||
+ from lib389.dseldif import DSEldif
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+ ANCESTORID_DN = "cn=ancestorid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+
|
||||
+ log.info("Stop the server")
|
||||
+ standalone.stop()
|
||||
+
|
||||
+ log.info("Add ancestorid index config entry using DSEldif")
|
||||
+ dse_ldif = DSEldif(standalone)
|
||||
+
|
||||
+ # Create a fake ancestorid index entry
|
||||
+ ancestorid_entry = [
|
||||
+ "dn: {}\n".format(ANCESTORID_DN),
|
||||
+ "objectClass: top\n",
|
||||
+ "objectClass: nsIndex\n",
|
||||
+ "cn: ancestorid\n",
|
||||
+ "nsSystemIndex: true\n",
|
||||
+ "nsIndexType: eq\n",
|
||||
+ "nsMatchingRule: integerOrderingMatch\n",
|
||||
+ "\n"
|
||||
+ ]
|
||||
+ dse_ldif.add_entry(ancestorid_entry)
|
||||
+
|
||||
+ # Verify it was added by re-reading dse.ldif
|
||||
+ dse_ldif2 = DSEldif(standalone)
|
||||
+ cn_value = dse_ldif2.get(ANCESTORID_DN, "cn")
|
||||
+ assert cn_value is not None, "Failed to add ancestorid index config entry"
|
||||
+ log.info(f"Added ancestorid index entry with cn: {cn_value}")
|
||||
+
|
||||
+ log.info("Start the server (triggers upgrade)")
|
||||
+ standalone.start()
|
||||
+
|
||||
+ log.info("Verify ancestorid index config entry was removed by upgrade")
|
||||
+ # Check via LDAP - the upgrade should have removed the entry
|
||||
+ try:
|
||||
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
|
||||
+ # If we can get the entry, it wasn't removed - this is a failure
|
||||
+ cn_after = ancestorid_index.get_attr_vals_utf8("cn")
|
||||
+ assert False, f"ancestorid index config entry should have been removed but still exists: {cn_after}"
|
||||
+ except Exception as e:
|
||||
+ # Entry should not exist - this is expected
|
||||
+ log.info(f"ancestorid index config entry correctly removed (got exception: {e})")
|
||||
+
|
||||
+ log.info("Upgrade successfully removed ancestorid index config entry")
|
||||
+
|
||||
+ # Verify idempotency - restart again and ensure no errors
|
||||
+ log.info("Restart server again to verify idempotency (no errors on second run)")
|
||||
+ standalone.restart()
|
||||
+ # Verify the entry is still absent
|
||||
+ try:
|
||||
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
|
||||
+ cn_after_second = ancestorid_index.get_attr_vals_utf8("cn")
|
||||
+ assert False, f"ancestorid index config entry should still be absent after second restart but found: {cn_after_second}"
|
||||
+ except Exception as e:
|
||||
+ log.info(f"Idempotency verified - ancestorid still absent after second restart (got exception: {e})")
|
||||
+
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
|
||||
index 6f97d492b..70e7a85ac 100644
|
||||
--- a/ldap/ldif/template-dse.ldif.in
|
||||
+++ b/ldap/ldif/template-dse.ldif.in
|
||||
@@ -990,14 +990,6 @@ cn: aci
|
||||
nssystemindex: true
|
||||
nsindextype: pres
|
||||
|
||||
-dn: cn=ancestorid,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
|
||||
-objectclass: top
|
||||
-objectclass: nsIndex
|
||||
-cn: ancestorid
|
||||
-nssystemindex: true
|
||||
-nsindextype: eq
|
||||
-nsmatchingrule: integerOrderingMatch
|
||||
-
|
||||
dn: cn=cn,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
|
||||
objectclass: top
|
||||
objectclass: nsIndex
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index adfec63de..d9156cae9 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -380,6 +380,126 @@ upgrade_remove_index_scanlimit(void)
|
||||
return uresult;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Remove ancestorid index configuration entry if present.
|
||||
+ *
|
||||
+ * The ancestorid index is special - it has no corresponding attribute type
|
||||
+ * and should not have a DSE config entry. If an entry exists, remove it.
|
||||
+ *
|
||||
+ * This function removes:
|
||||
+ * 1. The ancestorid entry from cn=default indexes (to prevent re-creation on startup)
|
||||
+ * 2. The ancestorid entry from each backend's cn=index (if it exists)
|
||||
+ */
|
||||
+static upgrade_status
|
||||
+upgrade_remove_ancestorid_index_config(void)
|
||||
+{
|
||||
+ struct slapi_pblock *pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **backends = NULL;
|
||||
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
+ const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
+ upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
+ int rc;
|
||||
+
|
||||
+ /*
|
||||
+ * First, remove ancestorid from cn=default indexes to prevent
|
||||
+ * ldbm_instance_create_default_user_indexes() from re-creating it.
|
||||
+ */
|
||||
+ {
|
||||
+ Slapi_PBlock *def_pb = slapi_pblock_new();
|
||||
+ char *def_idx_dn = slapi_create_dn_string(
|
||||
+ "cn=ancestorid,cn=default indexes,cn=config,%s", be_base_dn);
|
||||
+
|
||||
+ if (def_idx_dn) {
|
||||
+ slapi_delete_internal_set_pb(
|
||||
+ def_pb, def_idx_dn, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_delete_internal_pb(def_pb);
|
||||
+ slapi_pblock_get(def_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
||||
+
|
||||
+ if (rc == LDAP_SUCCESS) {
|
||||
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Removed 'ancestorid' from default indexes.\n");
|
||||
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Failed to remove 'ancestorid' from default indexes: error %d\n", rc);
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&def_idx_dn);
|
||||
+ }
|
||||
+ slapi_pblock_destroy(def_pb);
|
||||
+ }
|
||||
+
|
||||
+ /* Search for all backend instances */
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ pb, be_base_dn,
|
||||
+ LDAP_SCOPE_ONELEVEL,
|
||||
+ be_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
+
|
||||
+ if (backends) {
|
||||
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
+ if (!be_dn || !be_name) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **idx_entries = NULL;
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=ancestorid,cn=index,%s",
|
||||
+ be_dn);
|
||||
+ char *idx_filter = "(objectclass=nsIndex)";
|
||||
+
|
||||
+ if (!idx_dn) {
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ idx_pb, idx_dn,
|
||||
+ LDAP_SCOPE_BASE,
|
||||
+ idx_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(idx_pb);
|
||||
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
+
|
||||
+ if (idx_entries && idx_entries[0]) {
|
||||
+ /* ancestorid index entry exists - delete it */
|
||||
+ Slapi_PBlock *del_pb = slapi_pblock_new();
|
||||
+
|
||||
+ slapi_delete_internal_set_pb(
|
||||
+ del_pb, idx_dn, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_delete_internal_pb(del_pb);
|
||||
+ slapi_pblock_get(del_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
||||
+
|
||||
+ if (rc == LDAP_SUCCESS) {
|
||||
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Removed 'ancestorid' index config entry in backend '%s'.\n",
|
||||
+ be_name);
|
||||
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Failed to remove 'ancestorid' index config entry in backend '%s': error %d\n",
|
||||
+ be_name, rc);
|
||||
+ }
|
||||
+
|
||||
+ slapi_pblock_destroy(del_pb);
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(idx_pb);
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return uresult;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
* matching rule.
|
||||
@@ -394,7 +514,7 @@ upgrade_check_id_index_matching_rule(void)
|
||||
Slapi_Entry **backends = NULL;
|
||||
const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
- const char *attrs_to_check[] = {"parentid", "ancestorid", NULL};
|
||||
+ const char *attrs_to_check[] = {"parentid", NULL};
|
||||
upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
|
||||
/* Search for all backend instances */
|
||||
@@ -408,8 +528,9 @@ upgrade_check_id_index_matching_rule(void)
|
||||
|
||||
if (backends) {
|
||||
for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
- if (!be_name) {
|
||||
+ if (!be_dn || !be_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -418,8 +539,8 @@ upgrade_check_id_index_matching_rule(void)
|
||||
const char *attr_name = attrs_to_check[attr_idx];
|
||||
struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
Slapi_Entry **idx_entries = NULL;
|
||||
- char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,%s",
|
||||
- attr_name, be_name, be_base_dn);
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
|
||||
+ attr_name, be_dn);
|
||||
char *idx_filter = "(objectclass=nsIndex)";
|
||||
PRBool has_matching_rule = PR_FALSE;
|
||||
|
||||
@@ -512,6 +633,10 @@ upgrade_server(void)
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
|
||||
+ if (upgrade_remove_ancestorid_index_config() != UPGRADE_SUCCESS) {
|
||||
+ return UPGRADE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,300 +0,0 @@
|
||||
From b5bee921b7f4cfbf7a2edbe55d4291f487f4a140 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Detect and log index ordering mismatch during
|
||||
backend startup
|
||||
|
||||
Description:
|
||||
Add `ldbm_instance_check_index_config()` function that checks on-disk
|
||||
index data and logs a message in case of a mismatch with DSE config entry.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 262 ++++++++++++++++++++++++
|
||||
1 file changed, 262 insertions(+)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index f9a546661..3fcdb5554 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -248,6 +248,266 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
}
|
||||
|
||||
|
||||
+/*
|
||||
+ * Check if an index has integerOrderingMatch configured in DSE.
|
||||
+ *
|
||||
+ * This function performs an internal LDAP search to check if the index
|
||||
+ * configuration entry has nsMatchingRule: integerOrderingMatch.
|
||||
+ *
|
||||
+ * Parameters:
|
||||
+ * inst_name - backend instance name (e.g., "userRoot")
|
||||
+ * index_name - name of the index to check (e.g., "parentid", "ancestorid")
|
||||
+ *
|
||||
+ * Returns:
|
||||
+ * PR_TRUE if integerOrderingMatch is configured
|
||||
+ * PR_FALSE if not configured or index entry doesn't exist
|
||||
+ */
|
||||
+static PRBool
|
||||
+ldbm_instance_index_has_int_order_in_dse(const char *inst_name, const char *index_name)
|
||||
+{
|
||||
+ Slapi_PBlock *pb = NULL;
|
||||
+ Slapi_Entry **entries = NULL;
|
||||
+ char *idx_dn = NULL;
|
||||
+ PRBool has_int_order = PR_FALSE;
|
||||
+
|
||||
+ idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,cn=ldbm database,cn=plugins,cn=config",
|
||||
+ index_name, inst_name);
|
||||
+ if (idx_dn == NULL) {
|
||||
+ return PR_FALSE;
|
||||
+ }
|
||||
+
|
||||
+ pb = slapi_pblock_new();
|
||||
+ slapi_search_internal_set_pb(pb, idx_dn, LDAP_SCOPE_BASE,
|
||||
+ "(objectclass=nsIndex)", NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
|
||||
+
|
||||
+ if (entries && entries[0]) {
|
||||
+ Slapi_Attr *mr_attr = NULL;
|
||||
+ if (slapi_entry_attr_find(entries[0], "nsMatchingRule", &mr_attr) == 0) {
|
||||
+ Slapi_Value *sval = NULL;
|
||||
+ int idx;
|
||||
+ for (idx = slapi_attr_first_value(mr_attr, &sval);
|
||||
+ idx != -1;
|
||||
+ idx = slapi_attr_next_value(mr_attr, idx, &sval)) {
|
||||
+ const struct berval *bval = slapi_value_get_berval(sval);
|
||||
+ if (bval && bval->bv_val &&
|
||||
+ strcasecmp(bval->bv_val, "integerOrderingMatch") == 0) {
|
||||
+ has_int_order = PR_TRUE;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return has_int_order;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Check a system index for ordering mismatch between config and on-disk data.
|
||||
+ *
|
||||
+ * This function compares what's configured in DSE (nsMatchingRule) with
|
||||
+ * what's actually on disk. A mismatch can occur in two scenarios:
|
||||
+ * 1. Ordering rule is configured but disk has lexicographic order
|
||||
+ * (rule was added after index was created)
|
||||
+ * 2. No ordering rule configured but disk has integer order
|
||||
+ * (rule was removed after index was created with it)
|
||||
+ *
|
||||
+ * This function reads the first keys from the specified index and checks
|
||||
+ * if they are stored in lexicographic order (string: "1" < "10" < "2") or
|
||||
+ * integer order (numeric: "1" < "2" < "10").
|
||||
+ *
|
||||
+ * Parameters:
|
||||
+ * be - backend
|
||||
+ * index_name - name of the index to check (e.g., "parentid", "ancestorid")
|
||||
+ *
|
||||
+ */
|
||||
+static void
|
||||
+ldbm_instance_check_index_config(backend *be, const char *index_name)
|
||||
+{
|
||||
+ ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
|
||||
+ struct attrinfo *ai = NULL;
|
||||
+ dbi_db_t *db = NULL;
|
||||
+ dbi_cursor_t dbc = {0};
|
||||
+ dbi_val_t key = {0};
|
||||
+ dbi_val_t data = {0};
|
||||
+ int ret = 0;
|
||||
+ PRBool config_has_int_order = PR_FALSE;
|
||||
+ PRBool disk_has_int_order = PR_TRUE; /* Assume integer order until proven otherwise */
|
||||
+ ID prev_id = 0;
|
||||
+ int key_count = 0;
|
||||
+ PRBool first_key = PR_TRUE;
|
||||
+ PRBool found_ordering_evidence = PR_FALSE;
|
||||
+
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': checking %s index ordering...\n",
|
||||
+ inst->inst_name, index_name);
|
||||
+
|
||||
+ /* Check if integerOrderingMatch is configured in DSE */
|
||||
+ config_has_int_order = ldbm_instance_index_has_int_order_in_dse(inst->inst_name, index_name);
|
||||
+
|
||||
+ /* Get attrinfo for the index */
|
||||
+ ainfo_get(be, (char *)index_name, &ai);
|
||||
+ if (ai == NULL || strcmp(ai->ai_type, index_name) != 0) {
|
||||
+ /* No index config found */
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': no %s attrinfo found, skipping check\n",
|
||||
+ inst->inst_name, index_name);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Open the index file */
|
||||
+ ret = dblayer_get_index_file(be, ai, &db, 0);
|
||||
+ if (ret != 0 || db == NULL) {
|
||||
+ /* Index file doesn't exist or can't be opened - this is fine for new instances */
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': could not open %s index file (ret=%d), skipping order check\n",
|
||||
+ inst->inst_name, index_name, ret);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Create a cursor to read keys */
|
||||
+ ret = dblayer_new_cursor(be, db, NULL, &dbc);
|
||||
+ if (ret != 0) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': could not create cursor on %s index (ret=%d)\n",
|
||||
+ inst->inst_name, index_name, ret);
|
||||
+ dblayer_release_index_file(be, ai, db);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ dblayer_value_init(be, &key);
|
||||
+ dblayer_value_init(be, &data);
|
||||
+
|
||||
+ /*
|
||||
+ * Read up to 100 unique keys and check their ordering.
|
||||
+ * With lexicographic ordering: "1" < "10" < "100" < "2" < "20" < "3"
|
||||
+ * With integer ordering: "1" < "2" < "3" < "10" < "20" < "100"
|
||||
+ *
|
||||
+ * If we find a case where prev_id > current_id (numerically), but the
|
||||
+ * keys are still in order (lexicographically), then the index uses
|
||||
+ * lexicographic ordering.
|
||||
+ */
|
||||
+ while (key_count < 100) {
|
||||
+ ID current_id;
|
||||
+
|
||||
+ ret = dblayer_cursor_op(&dbc, first_key ? DBI_OP_MOVE_TO_FIRST : DBI_OP_NEXT_KEY, &key, &data);
|
||||
+ first_key = PR_FALSE; /* Always advance cursor on next iteration */
|
||||
+ if (ret != 0) {
|
||||
+ break; /* No more keys or error */
|
||||
+ }
|
||||
+
|
||||
+ /* Skip non-equality keys */
|
||||
+ if (key.size < 2 || *(char *)key.data != EQ_PREFIX) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Parse the ID from the key (format: "=<id>") */
|
||||
+ current_id = (ID)strtoul((char *)key.data + 1, NULL, 10);
|
||||
+ if (current_id == 0) {
|
||||
+ continue; /* Invalid ID, skip */
|
||||
+ }
|
||||
+
|
||||
+ key_count++;
|
||||
+
|
||||
+ if (prev_id != 0) {
|
||||
+ /*
|
||||
+ * Check ordering: if prev_id > current_id numerically,
|
||||
+ * but we got this key after prev in DB order, then
|
||||
+ * the index is using lexicographic ordering.
|
||||
+ *
|
||||
+ * Example: if we see "10" followed by "2", that's lexicographic
|
||||
+ * because "10" < "2" as strings, but 10 > 2 as integers.
|
||||
+ */
|
||||
+ if (prev_id > current_id) {
|
||||
+ /* Found evidence of lexicographic ordering */
|
||||
+ disk_has_int_order = PR_FALSE;
|
||||
+ found_ordering_evidence = PR_TRUE;
|
||||
+ break;
|
||||
+ } else if (prev_id < current_id) {
|
||||
+ /*
|
||||
+ * This is consistent with integer ordering, but we need
|
||||
+ * to find a case that proves lexicographic ordering.
|
||||
+ * For example, seeing "1" followed by "2" is ambiguous,
|
||||
+ * but seeing "1" followed by "10" (not "2") proves lexicographic.
|
||||
+ *
|
||||
+ * A definitive test: if we see an ID followed by a smaller
|
||||
+ * ID, that's lexicographic. If all IDs are strictly increasing,
|
||||
+ * it could be either (or the index only has sequential IDs).
|
||||
+ */
|
||||
+ found_ordering_evidence = PR_TRUE;
|
||||
+ }
|
||||
+ }
|
||||
+ prev_id = current_id;
|
||||
+ }
|
||||
+
|
||||
+ /* Close the cursor and free values */
|
||||
+ dblayer_cursor_op(&dbc, DBI_OP_CLOSE, NULL, NULL);
|
||||
+ dblayer_value_free(be, &key);
|
||||
+ dblayer_value_free(be, &data);
|
||||
+
|
||||
+ /* Release the index file */
|
||||
+ dblayer_release_index_file(be, ai, db);
|
||||
+
|
||||
+ /*
|
||||
+ * Report findings and check for config/disk mismatch.
|
||||
+ * Log an error if there's a discrepancy between what's configured
|
||||
+ * in DSE and what's actually on disk.
|
||||
+ */
|
||||
+ if (!found_ordering_evidence) {
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': %s index ordering check - "
|
||||
+ "could not determine on-disk ordering (index may be empty or have sequential IDs only). "
|
||||
+ "Config has integerOrderingMatch: %s\n",
|
||||
+ inst->inst_name, index_name, config_has_int_order ? "yes" : "no");
|
||||
+ } else if (config_has_int_order && !disk_has_int_order) {
|
||||
+ /* Config expects integer ordering, but disk has lexicographic - MISMATCH */
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': MISMATCH - %s index has integerOrderingMatch configured, "
|
||||
+ "but on-disk data uses lexicographic ordering. "
|
||||
+ "This will cause searches to return incorrect or incomplete results. "
|
||||
+ "Please reindex the %s attribute: "
|
||||
+ "dsconf <instance> backend index reindex --attr %s %s\n",
|
||||
+ inst->inst_name, index_name, index_name, index_name, inst->inst_name);
|
||||
+ } else if (!config_has_int_order && disk_has_int_order) {
|
||||
+ /* Config expects lexicographic ordering, but disk has integer - MISMATCH */
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': MISMATCH - %s index does not have integerOrderingMatch configured, "
|
||||
+ "but on-disk data uses integer ordering. "
|
||||
+ "This will cause searches to return incorrect or incomplete results. "
|
||||
+ "Please reindex the %s attribute: "
|
||||
+ "dsconf <instance> backend index reindex --attr %s %s\n",
|
||||
+ inst->inst_name, index_name, index_name, index_name, inst->inst_name);
|
||||
+ } else {
|
||||
+ /* Config and disk ordering match - no action needed */
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': %s index ordering check passed - "
|
||||
+ "config has integerOrderingMatch: %s, on-disk data matches.\n",
|
||||
+ inst->inst_name, index_name, config_has_int_order ? "yes" : "no");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Check system indexes for ordering mismatches.
|
||||
+ * If a mismatch is detected, log an error advising the administrator
|
||||
+ * to reindex the affected attribute.
|
||||
+ *
|
||||
+ * Note: We only check parentid here. The ancestorid index is a special
|
||||
+ * system index that has no DSE config entry - its ordering is hardcoded
|
||||
+ * in ldbm_instance_init_config_entry() and cannot be changed by users.
|
||||
+ */
|
||||
+static void
|
||||
+ldbm_instance_check_indexes(backend *be)
|
||||
+{
|
||||
+ /* Check parentid index */
|
||||
+ ldbm_instance_check_index_config(be, LDBM_PARENTID_STR);
|
||||
+}
|
||||
+
|
||||
/* Starts a backend instance */
|
||||
int
|
||||
ldbm_instance_start(backend *be)
|
||||
@@ -319,6 +579,8 @@ ldbm_instance_startall(struct ldbminfo *li)
|
||||
ldbm_instance_register_modify_callback(inst);
|
||||
vlv_init(inst);
|
||||
slapi_mtn_be_started(inst->inst_be);
|
||||
+ /* Check index configuration for potential issues */
|
||||
+ ldbm_instance_check_indexes(inst->inst_be);
|
||||
}
|
||||
if (slapi_exist_referral(inst->inst_be)) {
|
||||
slapi_be_set_flag(inst->inst_be, SLAPI_BE_FLAG_CONTAINS_REFERRAL);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,135 +0,0 @@
|
||||
From c1f0756b453b7ea219add236f60a72b3fc660af0 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 27 Jan 2026 14:26:29 +0100
|
||||
Subject: [PATCH] Issue 7096 - (2nd) During replication online total init the
|
||||
function idl_id_is_in_idlist is not scaling with large database (#7205)
|
||||
|
||||
Bug Description:
|
||||
The fix for #7096 optimized the BDB backend's `idl_new_range_fetch()`
|
||||
function to use ID ranges instead of checking the full ID list during
|
||||
online total initialization. However, the LMDB backend's
|
||||
`idl_lmdb_range_fetch()` function and its callback
|
||||
`idl_range_add_id_cb()` were not updated and still use the non-scaling
|
||||
`idl_id_is_in_idlist()` function.
|
||||
|
||||
Fix Description:
|
||||
Apply the same optimization to the LMDB backend.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7096
|
||||
|
||||
Reviewed by: @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/idl_new.c | 39 ++++++++++----------------
|
||||
1 file changed, 15 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
index 2d978353f..613d53815 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
@@ -66,6 +66,7 @@ typedef struct {
|
||||
size_t leftoverlen;
|
||||
size_t leftovercnt;
|
||||
IDList *idl;
|
||||
+ IdRange_t *idrange_list;
|
||||
int flag_err;
|
||||
ID lastid;
|
||||
ID suffix;
|
||||
@@ -700,9 +701,9 @@ error:
|
||||
}
|
||||
}
|
||||
}
|
||||
- slapi_ch_free((void **)&leftover);
|
||||
- idrange_free(&idrange_list);
|
||||
}
|
||||
+ slapi_ch_free((void **)&leftover);
|
||||
+ idrange_free(&idrange_list);
|
||||
slapi_log_err(SLAPI_LOG_FILTER, "idl_new_range_fetch",
|
||||
"Found %d candidates; error code is: %d\n",
|
||||
idl ? idl->b_nids : 0, *flag_err);
|
||||
@@ -716,7 +717,6 @@ static int
|
||||
idl_range_add_id_cb(dbi_val_t *key, dbi_val_t *data, void *ctx)
|
||||
{
|
||||
idl_range_ctx_t *rctx = ctx;
|
||||
- int idl_rc = 0;
|
||||
ID id = 0;
|
||||
|
||||
if (key->data == NULL) {
|
||||
@@ -779,10 +779,12 @@ idl_range_add_id_cb(dbi_val_t *key, dbi_val_t *data, void *ctx)
|
||||
* found entry is the one from the suffix
|
||||
*/
|
||||
rctx->suffix = keyval;
|
||||
- idl_rc = idl_append_extend(&rctx->idl, id);
|
||||
- } else if ((keyval == rctx->suffix) || idl_id_is_in_idlist(rctx->idl, keyval)) {
|
||||
+ idl_append_extend(&rctx->idl, id);
|
||||
+ idrange_add_id(&rctx->idrange_list, id);
|
||||
+ } else if ((keyval == rctx->suffix) || idl_id_is_in_idlist_ranges(rctx->idl, rctx->idrange_list, keyval)) {
|
||||
/* the parent is the suffix or already in idl. */
|
||||
- idl_rc = idl_append_extend(&rctx->idl, id);
|
||||
+ idl_append_extend(&rctx->idl, id);
|
||||
+ idrange_add_id(&rctx->idrange_list, id);
|
||||
} else {
|
||||
/* Otherwise, keep the {keyval,id} in leftover array */
|
||||
if (!rctx->leftover) {
|
||||
@@ -797,14 +799,7 @@ idl_range_add_id_cb(dbi_val_t *key, dbi_val_t *data, void *ctx)
|
||||
rctx->leftovercnt++;
|
||||
}
|
||||
} else {
|
||||
- idl_rc = idl_append_extend(&rctx->idl, id);
|
||||
- }
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_lmdb_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&rctx->idl);
|
||||
- rctx->flag_err = LDAP_UNWILLING_TO_PERFORM;
|
||||
- return DBI_RC_NOTFOUND;
|
||||
+ idl_append_extend(&rctx->idl, id);
|
||||
}
|
||||
#if defined(DB_ALLIDS_ON_READ)
|
||||
/* enforce the allids read limit */
|
||||
@@ -841,7 +836,6 @@ idl_lmdb_range_fetch(
|
||||
{
|
||||
int ret = 0;
|
||||
int ret2 = 0;
|
||||
- int idl_rc = 0;
|
||||
dbi_cursor_t cursor = {0};
|
||||
back_txn s_txn;
|
||||
struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
@@ -891,6 +885,7 @@ idl_lmdb_range_fetch(
|
||||
idl_range_ctx.lastid = 0;
|
||||
idl_range_ctx.count = 0;
|
||||
idl_range_ctx.index_id = index_id;
|
||||
+ idl_range_ctx.idrange_list = NULL;
|
||||
if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
|
||||
struct _back_info_index_key bck_info;
|
||||
/* We are doing a bulk import
|
||||
@@ -966,22 +961,18 @@ error:
|
||||
while(remaining > 0) {
|
||||
for (size_t i = 0; i < idl_range_ctx.leftovercnt; i++) {
|
||||
if (idl_range_ctx.leftover[i].key > 0 &&
|
||||
- idl_id_is_in_idlist(idl_range_ctx.idl, idl_range_ctx.leftover[i].key) != 0) {
|
||||
+ idl_id_is_in_idlist_ranges(idl_range_ctx.idl, idl_range_ctx.idrange_list, idl_range_ctx.leftover[i].key) != 0) {
|
||||
/* if the leftover key has its parent in the idl */
|
||||
- idl_rc = idl_append_extend(&idl_range_ctx.idl, idl_range_ctx.leftover[i].id);
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_lmdb_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&idl_range_ctx.idl);
|
||||
- break;
|
||||
- }
|
||||
+ idl_append_extend(&idl_range_ctx.idl, idl_range_ctx.leftover[i].id);
|
||||
+ idrange_add_id(&idl_range_ctx.idrange_list, idl_range_ctx.leftover[i].id);
|
||||
idl_range_ctx.leftover[i].key = 0;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
- slapi_ch_free((void **)&idl_range_ctx.leftover);
|
||||
}
|
||||
+ slapi_ch_free((void **)&idl_range_ctx.leftover);
|
||||
+ idrange_free(&idl_range_ctx.idrange_list);
|
||||
*flag_err = idl_range_ctx.flag_err;
|
||||
slapi_log_err(SLAPI_LOG_FILTER, "idl_lmdb_range_fetch",
|
||||
"Found %d candidates; error code is: %d\n",
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,877 +0,0 @@
|
||||
From 35237d57daf6fdf20a42c3cef27130ba70f0cdc2 Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Tue, 18 Nov 2025 21:57:10 +0530
|
||||
Subject: [PATCH] Issue 7076, 6992, 6784, 6214 - Fix CI test failures (#7077)
|
||||
|
||||
- Fixed import test bugs in regression_test.py (cleanup handler, LDIF permissions) -
|
||||
https://github.com/389ds/389-ds-base/issues/6992
|
||||
- Fixed ModRDN cache corruption on failed operations (parent update check, cache cleanup)
|
||||
- Fixed attribute uniqueness test fixture cleanup in attruniq_test.py
|
||||
- mproved test stability by fixing race conditions in replication, healthcheck,
|
||||
web UI, memberOf, and basic tests.
|
||||
- Fixed entrycache_eviction_test.py to track incremental log counts instead of cumulative -
|
||||
https://github.com/389ds/389-ds-base/issues/6784
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7076
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/6992
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/6784
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/6214
|
||||
|
||||
Reviewed by: @vashirov, @progier389 (Thanks!)
|
||||
---
|
||||
dirsrvtests/tests/suites/basic/basic_test.py | 4 +-
|
||||
.../healthcheck/health_system_indexes_test.py | 3 +
|
||||
.../tests/suites/import/regression_test.py | 388 +++++++++++++++++-
|
||||
.../suites/memberof_plugin/fixup_test.py | 8 +-
|
||||
.../tests/suites/plugins/attruniq_test.py | 84 ++--
|
||||
.../suites/replication/regression_m2_test.py | 4 +
|
||||
.../replication/repl_log_monitoring_test.py | 8 +-
|
||||
.../suites/webui/database/database_test.py | 1 +
|
||||
ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 44 +-
|
||||
9 files changed, 481 insertions(+), 63 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/basic/basic_test.py b/dirsrvtests/tests/suites/basic/basic_test.py
|
||||
index be825efe9..e9b611439 100644
|
||||
--- a/dirsrvtests/tests/suites/basic/basic_test.py
|
||||
+++ b/dirsrvtests/tests/suites/basic/basic_test.py
|
||||
@@ -593,7 +593,7 @@ def test_basic_import_export(topology_st, import_example_ldif):
|
||||
#
|
||||
# Test online/offline LDIF imports
|
||||
#
|
||||
- topology_st.standalone.start()
|
||||
+ topology_st.standalone.restart()
|
||||
# topology_st.standalone.config.set('nsslapd-errorlog-level', '1')
|
||||
|
||||
# Generate a test ldif (50k entries)
|
||||
@@ -691,6 +691,8 @@ def test_basic_backup(topology_st, import_example_ldif):
|
||||
|
||||
log.info('Running test_basic_backup...')
|
||||
|
||||
+ topology_st.standalone.restart()
|
||||
+
|
||||
backup_dir = topology_st.standalone.get_bak_dir() + '/backup_test_online'
|
||||
log.info(f'Backup directory is {backup_dir}')
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 571465562..4cd4d0c70 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -172,6 +172,7 @@ def test_missing_parentid(topology_st, log_buffering_enabled):
|
||||
log.info("Re-add the parentId index")
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
+ standalone.restart()
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
@@ -215,6 +216,7 @@ def test_missing_matching_rule(topology_st, log_buffering_enabled):
|
||||
log.info("Re-add the integerOrderingMatch matching rule")
|
||||
parentid_index = Index(standalone, PARENTID_DN)
|
||||
parentid_index.add("nsMatchingRule", "integerOrderingMatch")
|
||||
+ standalone.restart()
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
@@ -445,6 +447,7 @@ def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
backend.add_index("nsuniqueid", ["eq"])
|
||||
+ standalone.restart()
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
diff --git a/dirsrvtests/tests/suites/import/regression_test.py b/dirsrvtests/tests/suites/import/regression_test.py
|
||||
index 18611de35..bdbb516e8 100644
|
||||
--- a/dirsrvtests/tests/suites/import/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/import/regression_test.py
|
||||
@@ -5,6 +5,7 @@
|
||||
# See LICENSE for details.
|
||||
# --- END COPYRIGHT BLOCK ---
|
||||
#
|
||||
+from abc import ABC, abstractmethod
|
||||
from decimal import *
|
||||
import ldap
|
||||
import logging
|
||||
@@ -16,9 +17,9 @@ from lib389.backend import Backends
|
||||
from lib389.properties import TASK_WAIT
|
||||
from lib389.topologies import topology_st as topo
|
||||
from lib389.dbgen import dbgen_users
|
||||
-from lib389._constants import DEFAULT_SUFFIX
|
||||
+from lib389._constants import DEFAULT_SUFFIX, DEFAULT_BENAME
|
||||
from lib389.tasks import *
|
||||
-from lib389.idm.user import UserAccounts
|
||||
+from lib389.idm.user import UserAccounts, UserAccount
|
||||
from lib389.idm.directorymanager import DirectoryManager
|
||||
from lib389.dbgen import *
|
||||
from lib389.utils import *
|
||||
@@ -92,7 +93,203 @@ class AddDelUsers(threading.Thread):
|
||||
return self._ran
|
||||
|
||||
|
||||
-def test_replay_import_operation(topo):
|
||||
+def get_backend_by_name(inst, bename):
|
||||
+ bes = Backends(inst)
|
||||
+ bename = bename.lower()
|
||||
+ be = [ be for be in bes if be.get_attr_val_utf8_l('cn') == bename ]
|
||||
+ return be[0] if len(be) == 1 else None
|
||||
+
|
||||
+
|
||||
+class LogHandler():
|
||||
+ def __init__(self, logfd, patterns):
|
||||
+ self.logfd = logfd
|
||||
+ self.patterns = [ p.lower() for p in patterns ]
|
||||
+ self.pos = logfd.tell()
|
||||
+ self.last_result = None
|
||||
+
|
||||
+ def zero(self):
|
||||
+ return [0 for _ in range(len(self.patterns))]
|
||||
+
|
||||
+ def countCaptures(self):
|
||||
+ res = self.zero()
|
||||
+ self.logfd.seek(self.pos)
|
||||
+ for line in iter(self.logfd.readline, ''):
|
||||
+ # Ignore autotune messages that may confuse the counts
|
||||
+ if 'bdb_start_autotune' in line:
|
||||
+ continue
|
||||
+ # Ignore LMDB size warnings that may confuse the counts
|
||||
+ if 'dbmdb_ctx_t_db_max_size_set' in line:
|
||||
+ continue
|
||||
+ log.info(f'ERROR LOG line is {line.strip()}')
|
||||
+ for idx,pattern in enumerate(self.patterns):
|
||||
+ if pattern in line.lower():
|
||||
+ res[idx] += 1
|
||||
+ self.pos = self.logfd.tell()
|
||||
+ self.last_result = res
|
||||
+ log.info(f'ERROR LOG counts are: {res}')
|
||||
+ return res
|
||||
+
|
||||
+ def seek2end(self):
|
||||
+ self.pos = os.fstat(self.logfd.fileno()).st_size
|
||||
+
|
||||
+ def check(self, idx, val):
|
||||
+ count = self.last_result[idx]
|
||||
+ assert count == val , f"Should have {val} '{self.patterns[idx]}' messages but got: {count} - idx = {idx}"
|
||||
+
|
||||
+
|
||||
+class IEHandler(ABC):
|
||||
+ def __init__(self, inst, errlog, ldifname, bename=DEFAULT_BENAME, suffix=None):
|
||||
+ self.inst = inst
|
||||
+ self.errlog = errlog
|
||||
+ self.ldifname = ldifname
|
||||
+ self.bename = bename
|
||||
+ self.suffix = suffix
|
||||
+ self.ldif = ldifname if ldifname.startswith('/') else f'{inst.get_ldif_dir()}/{ldifname}.ldif'
|
||||
+
|
||||
+ @abstractmethod
|
||||
+ def get_name(self):
|
||||
+ pass
|
||||
+
|
||||
+ @abstractmethod
|
||||
+ def _run_task_b(self):
|
||||
+ pass
|
||||
+
|
||||
+ @abstractmethod
|
||||
+ def _run_task_s(self):
|
||||
+ pass
|
||||
+
|
||||
+ @abstractmethod
|
||||
+ def _run_offline(self):
|
||||
+ pass
|
||||
+
|
||||
+ @abstractmethod
|
||||
+ def _set_log_pattern(self, success):
|
||||
+ pass
|
||||
+
|
||||
+ def run(self, extra_checks, success=True):
|
||||
+ if self.errlog:
|
||||
+ self._set_log_pattern(success)
|
||||
+ self.errlog.seek2end()
|
||||
+
|
||||
+ if self.inst.status():
|
||||
+ if self.bename:
|
||||
+ log.info(f"Performing online {self.get_name()} of backend {self.bename} into LDIF file {self.ldif}")
|
||||
+ r = self._run_task_b()
|
||||
+ else:
|
||||
+ log.info(f"Performing online {self.get_name()} of suffix {self.suffix} into LDIF file {self.ldif}")
|
||||
+ r = self._run_task_s()
|
||||
+ r.wait()
|
||||
+ time.sleep(1)
|
||||
+ else:
|
||||
+ if self.bename:
|
||||
+ log.info(f"Performing offline {self.get_name()} of backend {self.bename} into LDIF file {self.ldif}")
|
||||
+ else:
|
||||
+ log.info(f"Performing offline {self.get_name()} of suffix {self.suffix} into LDIF file {self.ldif}")
|
||||
+ self._run_offline()
|
||||
+ if self.errlog:
|
||||
+ expected_counts = ['*' for _ in range(len(self.errlog.patterns))]
|
||||
+ for (idx, val) in extra_checks:
|
||||
+ expected_counts[idx] = val
|
||||
+ res = self.errlog.countCaptures()
|
||||
+ log.info(f'Expected errorlog counts are: {expected_counts}')
|
||||
+ if success is True or success is False:
|
||||
+ log.info(f'Number of {self.errlog.patterns[0]} in errorlog is: {res[0]}')
|
||||
+ assert res[0] >= 1
|
||||
+ for (idx, val) in extra_checks:
|
||||
+ self.errlog.check(idx, val)
|
||||
+
|
||||
+ def check_db(self):
|
||||
+ assert self.inst.dbscan(bename=self.bename, index='id2entry')
|
||||
+
|
||||
+
|
||||
+class Importer(IEHandler):
|
||||
+ def get_name(self):
|
||||
+ return "import"
|
||||
+
|
||||
+ def _set_log_pattern(self, success):
|
||||
+ if success is True:
|
||||
+ self.errlog.patterns[0] = 'import complete'
|
||||
+ elif success is False:
|
||||
+ self.errlog.patterns[0] = 'import failed'
|
||||
+
|
||||
+ def _run_task_b(self):
|
||||
+ bes = Backends(self.inst)
|
||||
+ r = bes.import_ldif(self.bename, [self.ldif,], include_suffixes=self.suffix)
|
||||
+ return r
|
||||
+
|
||||
+ def _run_task_s(self):
|
||||
+ r = ImportTask(self.inst)
|
||||
+ r.import_suffix_from_ldif(self.ldif, self.suffix)
|
||||
+ return r
|
||||
+
|
||||
+ def _run_offline(self):
|
||||
+ log.info(f'self.inst.ldif2db({self.bename}, {self.suffix}, ...)')
|
||||
+ if self.suffix is None:
|
||||
+ self.inst.ldif2db(self.bename, self.suffix, None, False, self.ldif)
|
||||
+ else:
|
||||
+ self.inst.ldif2db(self.bename, [self.suffix, ], None, False, self.ldif)
|
||||
+
|
||||
+
|
||||
+class Exporter(IEHandler):
|
||||
+ def get_name(self):
|
||||
+ return "export"
|
||||
+
|
||||
+ def _set_log_pattern(self, success):
|
||||
+ if success is True:
|
||||
+ self.errlog.patterns[0] = 'export finished'
|
||||
+ elif success is False:
|
||||
+ self.errlog.patterns[0] = 'export failed'
|
||||
+
|
||||
+ def _run_task_b(self):
|
||||
+ bes = Backends(self.inst)
|
||||
+ r = bes.export_ldif(self.bename, self.ldif, include_suffixes=self.suffix)
|
||||
+ return r
|
||||
+
|
||||
+ def _run_task_s(self):
|
||||
+ r = ExportTask(self.inst)
|
||||
+ r.export_suffix_to_ldif(self.ldif, self.suffix)
|
||||
+ return r
|
||||
+
|
||||
+ def _run_offline(self):
|
||||
+ self.inst.db2ldif(self.bename, self.suffix, None, False, False, self.ldif)
|
||||
+
|
||||
+
|
||||
+def preserve_func(topo, request, restart):
|
||||
+ # Ensure that topology get preserved helper
|
||||
+ inst = topo.standalone
|
||||
+
|
||||
+ def fin():
|
||||
+ if restart:
|
||||
+ inst.restart()
|
||||
+ Importer(inst, None, "save").run(())
|
||||
+
|
||||
+ r = Exporter(inst, None, "save")
|
||||
+ if not os.path.isfile(r.ldif):
|
||||
+ r.run(())
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope="function")
|
||||
+def preserve(topo, request):
|
||||
+ # Ensure that topology get preserved (no restart)
|
||||
+ preserve_func(topo, request, False)
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope="function")
|
||||
+def preserve_r(topo, request):
|
||||
+ # Ensure that topology get preserved (with restart)
|
||||
+ preserve_func(topo, request, True)
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope="function")
|
||||
+def verify(topo):
|
||||
+ # Check that backend is not broken
|
||||
+ inst = topo.standalone
|
||||
+ dn=f'uid=demo_user,ou=people,{DEFAULT_SUFFIX}'
|
||||
+ assert UserAccount(inst,dn).exists()
|
||||
+
|
||||
+
|
||||
+def test_replay_import_operation(topo, preserve_r, verify):
|
||||
""" Check after certain failed import operation, is it
|
||||
possible to replay an import operation
|
||||
|
||||
@@ -487,7 +684,190 @@ def test_ldif2db_after_backend_create(topo):
|
||||
import_time_2 = create_backend_and_import(instance, ldif_file_2, 'o=test_2', 'test_2')
|
||||
|
||||
log.info('Import times should be approximately the same')
|
||||
- assert abs(import_time_1 - import_time_2) < 5
|
||||
+ assert abs(import_time_1 - import_time_2) < 15
|
||||
+
|
||||
+
|
||||
+def test_ldif_missing_suffix_entry(topo, request, verify):
|
||||
+ """Test that ldif2db/import aborts if suffix entry is not in the ldif
|
||||
+
|
||||
+ :id: 731bd0d6-8cc8-11f0-8ef2-c85309d5c3e3
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Prepare final cleanup
|
||||
+ 2. Add a few users
|
||||
+ 3. Export ou=people subtree
|
||||
+ 4. Online import using backend name ou=people subtree
|
||||
+ 5. Online import using suffix name ou=people subtree
|
||||
+ 6. Stop the instance
|
||||
+ 7. Offline import using backend name ou=people subtree
|
||||
+ 8. Offline import using suffix name ou=people subtree
|
||||
+ 9. Generate ldif with a far away suffix
|
||||
+ 10. Offline import using backend name and "far" ldif
|
||||
+ 11. Offline import using suffix name and "far" ldif
|
||||
+ 12. Start the instance
|
||||
+ 13. Online import using backend name and "far" ldif
|
||||
+ 14. Online import using suffix name and "far" ldif
|
||||
+ :expectedresults:
|
||||
+ 1. Operation successful
|
||||
+ 2. Operation successful
|
||||
+ 3. Operation successful
|
||||
+ 4. Import should success, skip all entries, db should exists
|
||||
+ 5. Import should success, skip all entries, db should exists
|
||||
+ 6. Operation successful
|
||||
+ 7. Import should success, skip all entries, db should exists
|
||||
+ 8. Import should success, skip all entries, db should exists
|
||||
+ 9. Operation successful
|
||||
+ 10. Import should success, skip all entries, db should exists
|
||||
+ 11. Import should success, 10 entries skipped, db should exists
|
||||
+ 12. Operation successful
|
||||
+ 13. Import should success, skip all entries, db should exists
|
||||
+ 14. Import should success, 10 entries skipped, db should exists
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ inst.config.set('nsslapd-errorlog-level', '266354688')
|
||||
+ no_suffix_on = (
|
||||
+ (1, 0), # no errors are expected.
|
||||
+ (2, 1), # 1 warning is expected.
|
||||
+ (3, 0), # no 'no parent' warning is expected.
|
||||
+ (4, 1), # 1 'all entries were skipped' warning
|
||||
+ (5, 0), # no 'returning task warning' info message
|
||||
+ )
|
||||
+ no_suffix_off = (
|
||||
+ (1, 0), # no errors are expected.
|
||||
+ (2, 1), # 1 warning is expected.
|
||||
+ (3, 0), # no 'no parent' warning is expected.
|
||||
+ (4, 1), # 1 'all entries were skipped' warning
|
||||
+ (5, 1), # 1 'returning task warning' info message
|
||||
+ )
|
||||
+
|
||||
+ far_suffix_on = (
|
||||
+ (1, 0), # no errors are expected.
|
||||
+ (2, 1), # 1 warning (consolidated, pre-check aborts after 4 entries)
|
||||
+ (3, 0), # 0 'no parent' warnings (pre-check aborts before processing)
|
||||
+ (4, 1), # 1 'all entries were skipped' warning (from pre-check)
|
||||
+ (5, 0), # 0 'returning task warning' info message (online import)
|
||||
+ )
|
||||
+ # Backend-specific behavior for orphan detection when suffix parameter is provided
|
||||
+ nbw = 0 if get_default_db_lib() == "bdb" else 10
|
||||
+ far_suffix_with_suffix_on = (
|
||||
+ (1, 0), # no errors are expected.
|
||||
+ (2, nbw), # 0 (BDB early filtering) or 10 (LMDB orphan detection) warnings
|
||||
+ (3, nbw), # 0 (BDB early filtering) or 10 (LMDB orphan detection) 'no parent' warnings
|
||||
+ (4, 0), # 0 'all entries were skipped' warning (no pre-check abort)
|
||||
+ (5, 0), # 0 'returning task warning' info message (online import)
|
||||
+ )
|
||||
+ far_suffix_off = (
|
||||
+ (1, 0), # no errors are expected.
|
||||
+ (2, 1), # 1 warning (consolidated, pre-check detects missing suffix)
|
||||
+ (3, 0), # 0 'no parent' warnings (pre-check aborts before processing)
|
||||
+ (4, 1), # 1 'all entries were skipped' warning (from pre-check)
|
||||
+ (5, 1), # 1 'returning task warning' info message (offline import)
|
||||
+ )
|
||||
+ far_suffix_with_suffix_off = (
|
||||
+ (1, 0), # no errors are expected.
|
||||
+ (2, nbw), # 0 (BDB early filtering) or 10 (LMDB orphan detection) warnings
|
||||
+ (3, nbw), # 0 (BDB early filtering) or 10 (LMDB orphan detection) 'no parent' warnings
|
||||
+ (4, 0), # 0 'all entries were skipped' warning (no pre-check abort)
|
||||
+ (5, 0), # 0 'returning task warning' (rc=0, successful import of suffix)
|
||||
+ )
|
||||
+
|
||||
+ with open(inst.ds_paths.error_log, 'at+') as fd:
|
||||
+ patterns = (
|
||||
+ "Reserved for IEHandler",
|
||||
+ " ERR ",
|
||||
+ " WARN ",
|
||||
+ "has no parent",
|
||||
+ "all entries were skipped",
|
||||
+ "returning task warning",
|
||||
+ )
|
||||
+ errlog = LogHandler(fd, patterns)
|
||||
+ no_errors = ((1, 0), (2, 0)) # no errors nor warnings are expected.
|
||||
+
|
||||
+
|
||||
+ # 1. Prepare final cleanup
|
||||
+ Exporter(inst, errlog, "full").run(no_errors)
|
||||
+
|
||||
+ def fin():
|
||||
+ inst.start()
|
||||
+ with open(inst.ds_paths.error_log, 'at+') as cleanup_fd:
|
||||
+ cleanup_errlog = LogHandler(cleanup_fd, patterns)
|
||||
+ Importer(inst, cleanup_errlog, "full").run(no_errors)
|
||||
+
|
||||
+ if not DEBUGGING:
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+ # 2. Add a few users
|
||||
+ user = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ users = [ user.create_test_user(uid=i) for i in range(10) ]
|
||||
+
|
||||
+ # 3. Export ou=people subtree
|
||||
+ e = Exporter(inst, errlog, "people", suffix=f'ou=people,{DEFAULT_SUFFIX}')
|
||||
+ e.run(no_errors) # no errors nor warnings are expected.
|
||||
+
|
||||
+ # 4. Online import using backend name ou=people subtree
|
||||
+ e = Importer(inst, errlog, "people")
|
||||
+ e.run(no_suffix_on)
|
||||
+ e.check_db()
|
||||
+
|
||||
+ # 5. Online import using suffix name ou=people subtree
|
||||
+ e = Importer(inst, errlog, "people", suffix=DEFAULT_SUFFIX)
|
||||
+ e.run(no_suffix_on)
|
||||
+ e.check_db()
|
||||
+
|
||||
+ # 6. Stop the instance
|
||||
+ inst.stop()
|
||||
+
|
||||
+ # 7. Offline import using backend name ou=people subtree
|
||||
+ e = Importer(inst, errlog, "people")
|
||||
+ e.run(no_suffix_off)
|
||||
+ e.check_db()
|
||||
+
|
||||
+ # 8. Offline import using suffix name ou=people subtree
|
||||
+ e = Importer(inst, errlog, "people", suffix=DEFAULT_SUFFIX)
|
||||
+ e.run(no_suffix_off)
|
||||
+ e.check_db()
|
||||
+
|
||||
+ # 9. Generate ldif with a far away suffix
|
||||
+ e = Importer(inst, errlog, "full")
|
||||
+ people_ldif = e.ldif
|
||||
+ e = Importer(inst, errlog, "far")
|
||||
+ with open(e.ldif, "wt") as fout:
|
||||
+ with open(people_ldif, "rt") as fin:
|
||||
+ # Copy version
|
||||
+ line = fin.readline()
|
||||
+ fout.write(line)
|
||||
+ line = fin.readline()
|
||||
+ fout.write(line)
|
||||
+ # Generate fake entries
|
||||
+ for idx in range(10):
|
||||
+ fout.write(f"dn: uid=id{idx},dc=foo\nobjectclasses: extensibleObject\n\n")
|
||||
+ for line in iter(fin.readline, ''):
|
||||
+ fout.write(line)
|
||||
+
|
||||
+ os.chmod(e.ldif, 0o644)
|
||||
+
|
||||
+ # 10. Offline import using backend name ou=people subtree
|
||||
+ e.run(far_suffix_off)
|
||||
+ e.check_db()
|
||||
+
|
||||
+ # 11. Offline import using suffix name ou=people subtree
|
||||
+ e = Importer(inst, errlog, "far", suffix=DEFAULT_SUFFIX)
|
||||
+ e.run(far_suffix_with_suffix_off)
|
||||
+ e.check_db()
|
||||
+
|
||||
+ # 12. Start the instance
|
||||
+ inst.start()
|
||||
+
|
||||
+ # 13. Online import using backend name ou=people subtree
|
||||
+ e = Importer(inst, errlog, "far")
|
||||
+ e.run(far_suffix_on)
|
||||
+ e.check_db()
|
||||
+
|
||||
+ # 14. Online import using suffix name ou=people subtree
|
||||
+ e = Importer(inst, errlog, "far", suffix=DEFAULT_SUFFIX)
|
||||
+ e.run(far_suffix_with_suffix_on)
|
||||
+ e.check_db()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
diff --git a/dirsrvtests/tests/suites/memberof_plugin/fixup_test.py b/dirsrvtests/tests/suites/memberof_plugin/fixup_test.py
|
||||
index 5aac40d2b..44804bd1c 100644
|
||||
--- a/dirsrvtests/tests/suites/memberof_plugin/fixup_test.py
|
||||
+++ b/dirsrvtests/tests/suites/memberof_plugin/fixup_test.py
|
||||
@@ -44,7 +44,10 @@ def test_fixup_task_limit(topo):
|
||||
group = groups.create(properties={'cn': 'test'})
|
||||
|
||||
users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
|
||||
- for idx in range(400):
|
||||
+ # Turn on access log buffering to speed up user creation
|
||||
+ buffering = topo.standalone.config.get_attr_val_utf8('nsslapd-accesslog-logbuffering')
|
||||
+ topo.standalone.config.set('nsslapd-accesslog-logbuffering', 'on')
|
||||
+ for idx in range(6000):
|
||||
user = users.create(properties={
|
||||
'uid': 'testuser%s' % idx,
|
||||
'cn' : 'testuser%s' % idx,
|
||||
@@ -55,6 +58,9 @@ def test_fixup_task_limit(topo):
|
||||
})
|
||||
group.add('member', user.dn)
|
||||
|
||||
+ # Restore access log buffering
|
||||
+ topo.standalone.config.set('nsslapd-accesslog-logbuffering', buffering)
|
||||
+
|
||||
# Configure memberOf plugin
|
||||
memberof = MemberOfPlugin(topo.standalone)
|
||||
memberof.enable()
|
||||
diff --git a/dirsrvtests/tests/suites/plugins/attruniq_test.py b/dirsrvtests/tests/suites/plugins/attruniq_test.py
|
||||
index 6eaee08a4..a2be413c8 100644
|
||||
--- a/dirsrvtests/tests/suites/plugins/attruniq_test.py
|
||||
+++ b/dirsrvtests/tests/suites/plugins/attruniq_test.py
|
||||
@@ -84,14 +84,23 @@ def containers(topology_st, request):
|
||||
def attruniq(topology_st, request):
|
||||
log.info('Setup attribute uniqueness plugin')
|
||||
attruniq = AttributeUniquenessPlugin(topology_st.standalone, dn="cn=attruniq,cn=plugins,cn=config")
|
||||
- attruniq.create(properties={'cn': 'attruniq'})
|
||||
- attruniq.add_unique_attribute('cn')
|
||||
+
|
||||
+ if attruniq.exists():
|
||||
+ attruniq.delete()
|
||||
+ topology_st.standalone.restart()
|
||||
+
|
||||
+ attruniq.create(properties={
|
||||
+ 'cn': 'attruniq',
|
||||
+ 'uniqueness-attribute-name': 'cn',
|
||||
+ 'uniqueness-subtrees': 'cn=config',
|
||||
+ 'nsslapd-pluginEnabled': 'on'
|
||||
+ })
|
||||
topology_st.standalone.restart()
|
||||
|
||||
def fin():
|
||||
if attruniq.exists():
|
||||
- attruniq.disable()
|
||||
attruniq.delete()
|
||||
+ topology_st.standalone.restart()
|
||||
|
||||
request.addfinalizer(fin)
|
||||
|
||||
@@ -250,8 +259,8 @@ def test_modrdn_attr_uniqueness(topology_st, attruniq):
|
||||
group1 = groups.create(properties={'cn': 'group1'})
|
||||
group2 = groups.create(properties={'cn': 'group2'})
|
||||
|
||||
- attruniq.add_unique_attribute('mail')
|
||||
- attruniq.add_unique_subtree(group2.dn)
|
||||
+ attruniq.replace('uniqueness-attribute-name', 'mail')
|
||||
+ attruniq.replace('uniqueness-subtrees', group2.dn)
|
||||
attruniq.enable_all_subtrees()
|
||||
log.debug(f'Enable PLUGIN_ATTR_UNIQUENESS plugin as "ON"')
|
||||
attruniq.enable()
|
||||
@@ -274,9 +283,6 @@ def test_modrdn_attr_uniqueness(topology_st, attruniq):
|
||||
assert 'attribute value already exist' in str(excinfo.value)
|
||||
log.debug(excinfo.value)
|
||||
|
||||
- log.debug('Move user2 to group1')
|
||||
- user2.rename(f'uid={user2.rdn}', group1.dn)
|
||||
-
|
||||
user1.delete()
|
||||
user2.delete()
|
||||
|
||||
@@ -302,17 +308,11 @@ def test_multiple_attr_uniqueness(topology_st, attruniq):
|
||||
6. Should raise CONSTRAINT_VIOLATION
|
||||
"""
|
||||
|
||||
- try:
|
||||
- attruniq.add_unique_attribute('mail')
|
||||
- attruniq.add_unique_attribute('mailAlternateAddress')
|
||||
- attruniq.add_unique_subtree(DEFAULT_SUFFIX)
|
||||
- attruniq.enable_all_subtrees()
|
||||
- log.debug(f'Enable PLUGIN_ATTR_UNIQUENESS plugin as "ON"')
|
||||
- attruniq.enable()
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('test_multiple_attribute_uniqueness: Failed to configure plugin for "mail": error {}'.format(e.args[0]['desc']))
|
||||
- assert False
|
||||
-
|
||||
+ attruniq.replace('uniqueness-attribute-name', ['mail', 'mailAlternateAddress'])
|
||||
+ attruniq.replace('uniqueness-subtrees', DEFAULT_SUFFIX)
|
||||
+ attruniq.enable_all_subtrees()
|
||||
+ log.debug(f'Enable PLUGIN_ATTR_UNIQUENESS plugin as "ON"')
|
||||
+ attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
|
||||
users = UserAccounts(topology_st.standalone, DEFAULT_SUFFIX)
|
||||
@@ -383,8 +383,9 @@ def test_exclude_subtrees(topology_st, attruniq):
|
||||
16. Success
|
||||
17. Success
|
||||
"""
|
||||
- attruniq.add_unique_attribute('telephonenumber')
|
||||
- attruniq.add_unique_subtree(DEFAULT_SUFFIX)
|
||||
+ # Replace dummy config with actual test config
|
||||
+ attruniq.replace('uniqueness-attribute-name', 'telephonenumber')
|
||||
+ attruniq.replace('uniqueness-subtrees', DEFAULT_SUFFIX)
|
||||
attruniq.enable_all_subtrees()
|
||||
attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
@@ -517,10 +518,18 @@ def test_matchingrule_attr(topology_st):
|
||||
"""
|
||||
|
||||
inst = topology_st.standalone
|
||||
-
|
||||
attruniq = AttributeUniquenessPlugin(inst,
|
||||
dn="cn=attribute uniqueness,cn=plugins,cn=config")
|
||||
- attruniq.add_unique_attribute('cn:CaseExactMatch:')
|
||||
+
|
||||
+ if attruniq.exists():
|
||||
+ attruniq.delete()
|
||||
+ inst.restart()
|
||||
+
|
||||
+ attruniq.create(properties={
|
||||
+ 'cn': 'attribute uniqueness',
|
||||
+ 'uniqueness-attribute-name': 'cn:CaseExactMatch:',
|
||||
+ 'uniqueness-subtrees': DEFAULT_SUFFIX
|
||||
+ })
|
||||
attruniq.enable_all_subtrees()
|
||||
attruniq.enable()
|
||||
inst.restart()
|
||||
@@ -595,7 +604,7 @@ def test_one_container_add(topology_st, attruniq, containers, active_user_1):
|
||||
active_2.delete()
|
||||
|
||||
log.info('Setup attribute uniqueness plugin for "cn" attribute')
|
||||
- attruniq.add_unique_subtree(ACTIVE_DN)
|
||||
+ attruniq.replace('uniqueness-subtrees', ACTIVE_DN)
|
||||
attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
|
||||
@@ -628,7 +637,7 @@ def test_one_container_mod(topology_st, attruniq, containers,
|
||||
"""
|
||||
|
||||
log.info('Setup attribute uniqueness plugin for "cn" attribute')
|
||||
- attruniq.add_unique_subtree(ACTIVE_DN)
|
||||
+ attruniq.replace('uniqueness-subtrees', ACTIVE_DN)
|
||||
attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
|
||||
@@ -656,7 +665,7 @@ def test_one_container_modrdn(topology_st, attruniq, containers,
|
||||
"""
|
||||
|
||||
log.info('Setup attribute uniqueness plugin for "cn" attribute')
|
||||
- attruniq.add_unique_subtree(ACTIVE_DN)
|
||||
+ attruniq.replace('uniqueness-subtrees', ACTIVE_DN)
|
||||
attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
|
||||
@@ -690,8 +699,7 @@ def test_multiple_containers_add(topology_st, attruniq, containers,
|
||||
"""
|
||||
|
||||
log.info('Setup attribute uniqueness plugin for "cn" attribute')
|
||||
- attruniq.add_unique_subtree(ACTIVE_DN)
|
||||
- attruniq.add_unique_subtree(STAGE_DN)
|
||||
+ attruniq.replace('uniqueness-subtrees', [ACTIVE_DN, STAGE_DN])
|
||||
attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
|
||||
@@ -789,8 +797,7 @@ def test_multiple_containers_mod(topology_st, attruniq, containers,
|
||||
"""
|
||||
|
||||
log.info('Setup attribute uniqueness plugin for "cn" attribute')
|
||||
- attruniq.add_unique_subtree(ACTIVE_DN)
|
||||
- attruniq.add_unique_subtree(STAGE_DN)
|
||||
+ attruniq.replace('uniqueness-subtrees', [ACTIVE_DN, STAGE_DN])
|
||||
attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
|
||||
@@ -874,8 +881,8 @@ def test_multiple_containers_modrdn(topology_st, attruniq, containers,
|
||||
"""
|
||||
|
||||
log.info('Setup attribute uniqueness plugin for "cn" attribute')
|
||||
- attruniq.add_unique_subtree(ACTIVE_DN)
|
||||
- attruniq.add_unique_subtree(STAGE_DN)
|
||||
+ # Replace dummy subtree with actual test subtrees
|
||||
+ attruniq.replace('uniqueness-subtrees', [ACTIVE_DN, STAGE_DN])
|
||||
attruniq.enable()
|
||||
topology_st.standalone.restart()
|
||||
|
||||
@@ -993,7 +1000,10 @@ def test_invalid_config_missing_attr_name(topology_st):
|
||||
_config_file(topology_st, action='save')
|
||||
|
||||
attruniq = AttributeUniquenessPlugin(topology_st.standalone, dn="cn=attruniq,cn=plugins,cn=config")
|
||||
- attruniq.create(properties={'cn': 'attruniq'})
|
||||
+ attruniq.create(properties={
|
||||
+ 'cn': 'attruniq',
|
||||
+ 'uniqueness-subtrees': DEFAULT_SUFFIX
|
||||
+ })
|
||||
attruniq.enable()
|
||||
|
||||
topology_st.standalone.errorlog_file = open(topology_st.standalone.errlog, "r")
|
||||
@@ -1040,9 +1050,11 @@ def test_invalid_config_invalid_subtree(topology_st):
|
||||
_config_file(topology_st, action='save')
|
||||
|
||||
attruniq = AttributeUniquenessPlugin(topology_st.standalone, dn="cn=attruniq,cn=plugins,cn=config")
|
||||
- attruniq.create(properties={'cn': 'attruniq'})
|
||||
- attruniq.add_unique_attribute('cn')
|
||||
- attruniq.add_unique_subtree('invalid_subtree')
|
||||
+ attruniq.create(properties={
|
||||
+ 'cn': 'attruniq',
|
||||
+ 'uniqueness-attribute-name': 'cn',
|
||||
+ 'uniqueness-subtrees': 'invalid_subtree'
|
||||
+ })
|
||||
attruniq.enable()
|
||||
|
||||
topology_st.standalone.errorlog_file = open(topology_st.standalone.errlog, "r")
|
||||
diff --git a/dirsrvtests/tests/suites/replication/regression_m2_test.py b/dirsrvtests/tests/suites/replication/regression_m2_test.py
|
||||
index ba1ffcc9c..db5140b0b 100644
|
||||
--- a/dirsrvtests/tests/suites/replication/regression_m2_test.py
|
||||
+++ b/dirsrvtests/tests/suites/replication/regression_m2_test.py
|
||||
@@ -1221,6 +1221,10 @@ def test_rid_starting_with_0(topo_m2, request):
|
||||
for replica,rid in zip(replicas, ['010', '020']):
|
||||
replica.replace('nsDS5ReplicaId', rid)
|
||||
|
||||
+ # Restart required - replica IDs are loaded at startup and cached in memory
|
||||
+ S1.restart()
|
||||
+ S2.restart()
|
||||
+
|
||||
# Restore replica id in finalizer
|
||||
def fin():
|
||||
for replica,rid in zip(replicas, ['1', '2']):
|
||||
diff --git a/dirsrvtests/tests/suites/replication/repl_log_monitoring_test.py b/dirsrvtests/tests/suites/replication/repl_log_monitoring_test.py
|
||||
index 665fcb96f..b2b2d25a2 100644
|
||||
--- a/dirsrvtests/tests/suites/replication/repl_log_monitoring_test.py
|
||||
+++ b/dirsrvtests/tests/suites/replication/repl_log_monitoring_test.py
|
||||
@@ -369,11 +369,13 @@ def test_replication_log_monitoring_multi_suffix(topo_m4):
|
||||
repl.ensure_agreement(s1, s2)
|
||||
repl.ensure_agreement(s2, s1)
|
||||
|
||||
- # Allow initial topology to settle before capturing metrics
|
||||
+ # Wait for all the setup replication to settle, then clear the logs
|
||||
for suffix in all_suffixes:
|
||||
repl = ReplicationManager(suffix)
|
||||
- repl.test_replication_topology(topo_m4)
|
||||
-
|
||||
+ for s1 in suppliers:
|
||||
+ for s2 in suppliers:
|
||||
+ if s1 != s2:
|
||||
+ repl.wait_for_replication(s1, s2)
|
||||
for supplier in suppliers:
|
||||
supplier.deleteAccessLogs(restart=True)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/webui/database/database_test.py b/dirsrvtests/tests/suites/webui/database/database_test.py
|
||||
index ef105d262..05ddb6b00 100644
|
||||
--- a/dirsrvtests/tests/suites/webui/database/database_test.py
|
||||
+++ b/dirsrvtests/tests/suites/webui/database/database_test.py
|
||||
@@ -138,6 +138,7 @@ def test_chaining_configuration_availability(topology_st, page, browser_name):
|
||||
log.info('Click on Chaining Configuration and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Database', exact=True).click()
|
||||
frame.locator('#chaining-config').click()
|
||||
+ frame.locator('#chaining-page').wait_for()
|
||||
frame.locator('#defSizeLimit').wait_for()
|
||||
assert frame.locator('#defSizeLimit').is_visible()
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
index e3b7e5783..b1a29ff7f 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
@@ -1080,7 +1080,9 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
||||
}
|
||||
}
|
||||
}
|
||||
- if (slapi_sdn_get_dn(dn_newsuperiordn) != NULL) {
|
||||
+ /* Only update parent if we're actually moving to a NEW parent (not the same parent) */
|
||||
+ if (slapi_sdn_get_dn(dn_newsuperiordn) != NULL &&
|
||||
+ slapi_sdn_compare(dn_newsuperiordn, &dn_parentdn) != 0) {
|
||||
/* Push out the db modifications from the parent entry */
|
||||
retval = modify_update_all(be, pb, &parent_modify_context, &txn);
|
||||
if (DBI_RC_RETRY == retval) {
|
||||
@@ -1335,11 +1337,6 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
||||
goto common_return;
|
||||
|
||||
error_return:
|
||||
- /* Revert the caches if this is the parent operation */
|
||||
- if (parent_op && betxn_callback_fails) {
|
||||
- revert_cache(inst, &parent_time);
|
||||
- }
|
||||
-
|
||||
/* result already sent above - just free stuff */
|
||||
if (postentry) {
|
||||
slapi_entry_free(postentry);
|
||||
@@ -1417,13 +1414,6 @@ error_return:
|
||||
slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
|
||||
}
|
||||
slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
|
||||
-
|
||||
- /* As it is a BETXN plugin failure then
|
||||
- * revert the caches if this is the parent operation
|
||||
- */
|
||||
- if (parent_op) {
|
||||
- revert_cache(inst, &parent_time);
|
||||
- }
|
||||
}
|
||||
retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN);
|
||||
|
||||
@@ -1437,6 +1427,15 @@ error_return:
|
||||
}
|
||||
}
|
||||
|
||||
+ /* Revert the caches if this is the parent operation and cache modifications were made.
|
||||
+ * Cache modifications (via modify_switch_entries) only happen after BETXN PRE plugins succeed,
|
||||
+ * so we should only revert if we got past that point (i.e., BETXN POST plugin failures).
|
||||
+ * For BETXN PRE failures, no cache modifications were made to parent/newparent entries.
|
||||
+ */
|
||||
+ if (parent_op && betxn_callback_fails && postentry) {
|
||||
+ revert_cache(inst, &parent_time);
|
||||
+ }
|
||||
+
|
||||
common_return:
|
||||
|
||||
/* result code could be used in the bepost plugin functions. */
|
||||
@@ -1482,12 +1481,22 @@ common_return:
|
||||
"operation failed, the target entry is cleared from dncache (%s)\n", slapi_entry_get_dn(ec->ep_entry));
|
||||
CACHE_REMOVE(&inst->inst_dncache, bdn);
|
||||
CACHE_RETURN(&inst->inst_dncache, &bdn);
|
||||
+
|
||||
+ /* Also remove ec from entry cache and free it since the operation failed */
|
||||
+ if (inst && cache_is_in_cache(&inst->inst_cache, ec)) {
|
||||
+ CACHE_REMOVE(&inst->inst_cache, ec);
|
||||
+ CACHE_RETURN(&inst->inst_cache, &ec);
|
||||
+ } else {
|
||||
+ /* ec was not in cache, just free it */
|
||||
+ backentry_free(&ec);
|
||||
+ }
|
||||
+ ec = NULL;
|
||||
}
|
||||
|
||||
if (ec && inst) {
|
||||
CACHE_RETURN(&inst->inst_cache, &ec);
|
||||
+ ec = NULL;
|
||||
}
|
||||
- ec = NULL;
|
||||
}
|
||||
|
||||
if (inst) {
|
||||
@@ -1817,6 +1826,8 @@ moddn_get_newdn(Slapi_PBlock *pb, Slapi_DN *dn_olddn, Slapi_DN *dn_newrdn, Slapi
|
||||
|
||||
/*
|
||||
* Return the entries to the cache.
|
||||
+ * For the original entry 'e', we should NOT remove it from cache on failure,
|
||||
+ * as it's still a valid entry in the directory.
|
||||
*/
|
||||
static void
|
||||
moddn_unlock_and_return_entry(
|
||||
@@ -1825,12 +1836,9 @@ moddn_unlock_and_return_entry(
|
||||
{
|
||||
ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
|
||||
|
||||
- /* Something bad happened so we should give back all the entries */
|
||||
+ /* Unlock and return the entry to the cache */
|
||||
if (*targetentry != NULL) {
|
||||
cache_unlock_entry(&inst->inst_cache, *targetentry);
|
||||
- if (cache_is_in_cache(&inst->inst_cache, *targetentry)) {
|
||||
- CACHE_REMOVE(&inst->inst_cache, *targetentry);
|
||||
- }
|
||||
CACHE_RETURN(&inst->inst_cache, targetentry);
|
||||
*targetentry = NULL;
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
From 04a0f5560ea741972b9c264af23b377893ab7133 Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 15:41:09 +0530
|
||||
Subject: [PATCH] Issue 7076 - Fix revert_cache() never called in modrdn
|
||||
(#7220)
|
||||
|
||||
Description: The postentry check in PR #7077 was broken - postentry is always NULL
|
||||
at that point, fixed by removing the check.
|
||||
|
||||
Relates: #7076
|
||||
|
||||
Reviewed by: @vashirov, @mreynolds389, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 12 +++++++-----
|
||||
1 file changed, 7 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
index b1a29ff7f..36377fc01 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
@@ -103,6 +103,7 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
||||
Connection *pb_conn = NULL;
|
||||
int32_t parent_op = 0;
|
||||
int32_t betxn_callback_fails = 0; /* if a BETXN fails we need to revert entry cache */
|
||||
+ int32_t cache_mod_phase = 0; /* set when we reach the cache modification phase */
|
||||
struct timespec parent_time;
|
||||
Slapi_Mods *smods_add_rdn = NULL;
|
||||
|
||||
@@ -1229,6 +1230,8 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
+ /* We're now past the BETXN PRE phase and entering the cache modification phase */
|
||||
+ cache_mod_phase = 1;
|
||||
postentry = slapi_entry_dup(ec->ep_entry);
|
||||
|
||||
if (parententry != NULL) {
|
||||
@@ -1427,12 +1430,11 @@ error_return:
|
||||
}
|
||||
}
|
||||
|
||||
- /* Revert the caches if this is the parent operation and cache modifications were made.
|
||||
- * Cache modifications (via modify_switch_entries) only happen after BETXN PRE plugins succeed,
|
||||
- * so we should only revert if we got past that point (i.e., BETXN POST plugin failures).
|
||||
- * For BETXN PRE failures, no cache modifications were made to parent/newparent entries.
|
||||
+ /* Revert the caches if this is the parent operation AND we reached the
|
||||
+ * cache modification phase. If BETXN PRE fails, cache_mod_phase is 0
|
||||
+ * and we don't need to revert since no cache modifications were made.
|
||||
*/
|
||||
- if (parent_op && betxn_callback_fails && postentry) {
|
||||
+ if (parent_op && betxn_callback_fails && cache_mod_phase) {
|
||||
revert_cache(inst, &parent_time);
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
From fb8acf6141b069e250be3821c1e70ee7ed448720 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Wed, 11 Feb 2026 19:53:44 +0100
|
||||
Subject: [PATCH] Issue 6947 - Fix health_system_indexes_test.py
|
||||
|
||||
---
|
||||
.../tests/suites/healthcheck/health_system_indexes_test.py | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 4cd4d0c70..3b3651b38 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -99,6 +99,7 @@ def run_healthcheck_and_flush_log(topology, instance, searched_code, json, searc
|
||||
"memberof",
|
||||
]
|
||||
args.dry_run = False
|
||||
+ args.exclude_check = []
|
||||
|
||||
# If we are using BDB as a backend, we will get error DSBLE0006 on new versions
|
||||
if (
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
From 8761b28fd2f2cf3d2e2119bdf1b348bc7d2d1834 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 9 Feb 2026 13:18:09 +0100
|
||||
Subject: [PATCH] Issue 7121 - (2nd) LeakSanitizer: various leaks during
|
||||
replication (#7212)
|
||||
|
||||
Bug Description:
|
||||
With the previous fix 75e0e487545893a7b0d83f94f9264c10f8bb0353 applied,
|
||||
server can crash in ber_bvcpy.
|
||||
|
||||
```
|
||||
Program terminated with signal SIGSEGV, Segmentation fault.
|
||||
#0 ber_bvcpy (bvs=0x7f1d00000000, bvd=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:47
|
||||
47 len = bvs->bv_len;
|
||||
[Current thread is 1 (Thread 0x7f1db47fe640 (LWP 36576))]
|
||||
(gdb) bt
|
||||
#0 ber_bvcpy (bvs=0x7f1d00000000, bvd=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:47
|
||||
#1 ber_bvcpy (bvs=0x7f1d00000000, bvd=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:40
|
||||
#2 slapi_value_set_berval (bval=0x7f1d00000000, value=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:322
|
||||
#3 slapi_value_set_berval (value=value@entry=0x7f1da2cd73c0, bval=bval@entry=0x7f1d00000000) at ldap/servers/slapd/value.c:317
|
||||
#4 0x00007f1e48b7d787 in value_init (v=v@entry=0x7f1da2cd73c0, bval=bval@entry=0x7f1d00000000, t=t@entry=0 '\000', csn=csn@entry=0x0)
|
||||
at ldap/servers/slapd/value.c:179
|
||||
#5 0x00007f1e48b7d884 in value_new (bval=bval@entry=0x7f1d00000000, t=t@entry=0 '\000', csn=csn@entry=0x0) at ldap/servers/slapd/value.c:158
|
||||
#6 0x00007f1e48b7ddb7 in slapi_value_dup (v=0x7f1d00000000) at ldap/servers/slapd/value.c:147
|
||||
#7 0x00007f1e48b7e262 in valueset_set_valueset (vs2=0x7f1d502b5218, vs1=0x7f1da2c5b358) at ldap/servers/slapd/valueset.c:1244
|
||||
#8 valueset_set_valueset (vs1=0x7f1da2c5b358, vs2=0x7f1d502b5218) at ldap/servers/slapd/valueset.c:1220
|
||||
#9 0x00007f1e48add4af in slapi_attr_dup (attr=0x7f1d502b51e0) at ldap/servers/slapd/attr.c:396
|
||||
#10 0x00007f1e48af0f60 in slapi_entry_dup (e=0x7f1da2c19000) at ldap/servers/slapd/entry.c:2036
|
||||
#11 0x00007f1e442c734e in ldbm_back_modify (pb=0x7f1da2c00000) at ldap/servers/slapd/back-ldbm/ldbm_modify.c:741
|
||||
#12 0x00007f1e48b30076 in op_shared_modify (pb=pb@entry=0x7f1da2c00000, pw_change=pw_change@entry=0, old_pw=0x0)
|
||||
at ldap/servers/slapd/modify.c:1079
|
||||
#13 0x00007f1e48b30ced in do_modify (pb=pb@entry=0x7f1da2c00000) at ldap/servers/slapd/modify.c:377
|
||||
#14 0x000055e990e2fd1c in connection_dispatch_operation (pb=0x7f1da2c00000, op=<optimized out>, conn=<optimized out>)
|
||||
at ldap/servers/slapd/connection.c:672
|
||||
#15 connection_threadmain (arg=<optimized out>) at ldap/servers/slapd/connection.c:1955
|
||||
#16 0x00007f1e48839bd4 in _pt_root (arg=0x7f1e439d9500) at pthreads/../../../../nspr/pr/src/pthreads/ptthread.c:191
|
||||
#17 0x00007f1e4868a19a in start_thread (arg=<optimized out>) at pthread_create.c:443
|
||||
#18 0x00007f1e4870f100 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
|
||||
```
|
||||
|
||||
The fix changed from always setting `v_csnset = NULL` to only freeing it
|
||||
inside the if-block.
|
||||
|
||||
Fix Description:
|
||||
Keep `csnset_free()` outside the if-block to handle all values, not just
|
||||
those matching the condtion.
|
||||
|
||||
Related: https://github.com/389ds/389-ds-base/issues/7121
|
||||
|
||||
Reviewed by: @progier389, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/entrywsi.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/entrywsi.c b/ldap/servers/slapd/entrywsi.c
|
||||
index e1bdc1bab..0d044092d 100644
|
||||
--- a/ldap/servers/slapd/entrywsi.c
|
||||
+++ b/ldap/servers/slapd/entrywsi.c
|
||||
@@ -1185,8 +1185,8 @@ resolve_attribute_state_deleted_to_present(Slapi_Entry *e, Slapi_Attr *a, Slapi_
|
||||
if ((csn_compare(vucsn, deletedcsn) >= 0) ||
|
||||
value_distinguished_at_csn(e, a, valuestoupdate[i], deletedcsn)) {
|
||||
entry_deleted_value_to_present_value(a, valuestoupdate[i]);
|
||||
- csnset_free(&valuestoupdate[i]->v_csnset);
|
||||
}
|
||||
+ csnset_free(&valuestoupdate[i]->v_csnset);
|
||||
}
|
||||
}
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,551 +0,0 @@
|
||||
From 86b15d688949197a9efe3d5fc7a7eadcdd46e115 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Tue, 16 Dec 2025 15:48:35 -0800
|
||||
Subject: [PATCH] Issue 7150 - Compressed access log rotations skipped,
|
||||
accesslog-list out of sync (#7151)
|
||||
|
||||
Description: Accept `.gz`-suffixed rotated log filenames when
|
||||
rebuilding rotation info and checking previous logs, preventing
|
||||
compressed rotations from being dropped from the internal list.
|
||||
|
||||
Add regression tests to stress log rotation with compression,
|
||||
verify `nsslapd-accesslog-list` stays in sync, and guard against
|
||||
crashes when flushing buffered logs during rotation.
|
||||
Minor doc fix in test.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7150
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
.../suites/logging/log_flush_rotation_test.py | 341 +++++++++++++++++-
|
||||
ldap/servers/slapd/log.c | 99 +++--
|
||||
2 files changed, 402 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py b/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py
|
||||
index b33a622e1..864ba9c5d 100644
|
||||
--- a/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py
|
||||
+++ b/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py
|
||||
@@ -6,6 +6,7 @@
|
||||
# See LICENSE for details.
|
||||
# --- END COPYRIGHT BLOCK ---
|
||||
#
|
||||
+import glob
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
@@ -13,14 +14,351 @@ import pytest
|
||||
from lib389._constants import DEFAULT_SUFFIX, PW_DM
|
||||
from lib389.tasks import ImportTask
|
||||
from lib389.idm.user import UserAccounts
|
||||
+from lib389.idm.domain import Domain
|
||||
+from lib389.idm.directorymanager import DirectoryManager
|
||||
from lib389.topologies import topology_st as topo
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
+def remove_rotated_access_logs(inst):
|
||||
+ """
|
||||
+ Remove all rotated access log files to start fresh for each test.
|
||||
+ This prevents log files from previous tests affecting current test results.
|
||||
+ """
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+ patterns = [
|
||||
+ f'{log_dir}/access.2*', # Uncompressed rotated logs
|
||||
+ f'{log_dir}/access.*.gz', # Compressed rotated logs
|
||||
+ ]
|
||||
+ for pattern in patterns:
|
||||
+ for log_file in glob.glob(pattern):
|
||||
+ try:
|
||||
+ os.remove(log_file)
|
||||
+ log.info(f"Removed old log file: {log_file}")
|
||||
+ except OSError as e:
|
||||
+ log.warning(f"Could not remove {log_file}: {e}")
|
||||
+
|
||||
+
|
||||
+def reset_access_log_config(inst):
|
||||
+ """
|
||||
+ Reset access log configuration to default values.
|
||||
+ """
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '100')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', '10')
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'on')
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+ inst.config.set('nsslapd-accesslog-logminfreediskspace', '5')
|
||||
+
|
||||
+
|
||||
+def generate_heavy_load(inst, suffix, iterations=50):
|
||||
+ """
|
||||
+ Generate heavy LDAP load to fill access log quickly.
|
||||
+ Performs multiple operations: searches, modifies, binds to populate logs.
|
||||
+ """
|
||||
+ for i in range(iterations):
|
||||
+ suffix.replace('description', f'iteration_{i}')
|
||||
+ suffix.get_attr_val('description')
|
||||
+
|
||||
+
|
||||
+def count_access_logs(log_dir, compressed_only=False):
|
||||
+ """
|
||||
+ Count access log files in the log directory.
|
||||
+ Returns count of rotated access logs (not including the active 'access' file).
|
||||
+ """
|
||||
+ if compressed_only:
|
||||
+ pattern = f'{log_dir}/access.*.gz'
|
||||
+ else:
|
||||
+ pattern = f'{log_dir}/access.2*'
|
||||
+ log_files = glob.glob(pattern)
|
||||
+ return len(log_files)
|
||||
+
|
||||
+
|
||||
+def test_log_pileup_with_compression(topo):
|
||||
+ """Test that log rotation properly deletes old logs when compression is enabled.
|
||||
+
|
||||
+ :id: fa1bfce8-b6d3-4520-a0a8-bead14fa5838
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Clean up existing rotated logs and reset configuration
|
||||
+ 2. Enable access log compression
|
||||
+ 3. Set strict log limits (small maxlogsperdir)
|
||||
+ 4. Disable log expiration to test count-based deletion
|
||||
+ 5. Generate heavy load to create many log rotations
|
||||
+ 6. Verify log count does not exceed maxlogsperdir limit
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. Success
|
||||
+ 6. Log count should be at or below maxlogsperdir + small buffer
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ suffix = Domain(inst, DEFAULT_SUFFIX)
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+
|
||||
+ # Clean up before test
|
||||
+ remove_rotated_access_logs(inst)
|
||||
+ reset_access_log_config(inst)
|
||||
+ inst.restart()
|
||||
+
|
||||
+ max_logs = 5
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'on')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', str(max_logs))
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '1') # 1MB to trigger rotation
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'off')
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-logminfreediskspace', '5')
|
||||
+
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ target_logs = max_logs * 3
|
||||
+ for i in range(target_logs):
|
||||
+ log.info(f"Generating load for log rotation {i+1}/{target_logs}")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1) # Wait for rotation
|
||||
+
|
||||
+ time.sleep(3)
|
||||
+
|
||||
+ logs_on_disk = count_access_logs(log_dir)
|
||||
+ log.info(f"Configured maxlogsperdir: {max_logs}")
|
||||
+ log.info(f"Actual rotated logs on disk: {logs_on_disk}")
|
||||
+
|
||||
+ all_access_logs = glob.glob(f'{log_dir}/access*')
|
||||
+ log.info(f"All access log files: {all_access_logs}")
|
||||
+
|
||||
+ max_allowed = max_logs + 2
|
||||
+ assert logs_on_disk <= max_allowed, (
|
||||
+ f"Log rotation failed to delete old files! "
|
||||
+ f"Expected at most {max_allowed} rotated logs (maxlogsperdir={max_logs} + 2 buffer), "
|
||||
+ f"but found {logs_on_disk}. The server has lost track of the file list."
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize("compress_enabled", ["on", "off"])
|
||||
+def test_accesslog_list_mismatch(topo, compress_enabled):
|
||||
+ """Test that nsslapd-accesslog-list stays synchronized with actual log files.
|
||||
+
|
||||
+ :id: 0a8a46a6-cae7-43bd-8b64-5e3481480cd3
|
||||
+ :parametrized: yes
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Clean up existing rotated logs and reset configuration
|
||||
+ 2. Configure log rotation with compression enabled/disabled
|
||||
+ 3. Generate activity to trigger multiple rotations
|
||||
+ 4. Get the nsslapd-accesslog-list attribute
|
||||
+ 5. Compare with actual files on disk
|
||||
+ 6. Verify they match (accounting for .gz extension when enabled)
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. Success
|
||||
+ 6. The list attribute should match actual files on disk
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ suffix = Domain(inst, DEFAULT_SUFFIX)
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+ compression_on = compress_enabled == "on"
|
||||
+
|
||||
+ # Clean up before test
|
||||
+ remove_rotated_access_logs(inst)
|
||||
+ reset_access_log_config(inst)
|
||||
+ inst.restart()
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-compress', compress_enabled)
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '1')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', '10')
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ for i in range(15):
|
||||
+ suffix_note = "(no compression)" if not compression_on else ""
|
||||
+ log.info(f"Generating load for rotation {i+1}/15 {suffix_note}")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ time.sleep(3)
|
||||
+
|
||||
+ accesslog_list = inst.config.get_attr_vals_utf8('nsslapd-accesslog-list')
|
||||
+ log.info(f"nsslapd-accesslog-list entries (compress={compress_enabled}): {len(accesslog_list)}")
|
||||
+ log.info(f"nsslapd-accesslog-list (compress={compress_enabled}): {accesslog_list}")
|
||||
+
|
||||
+ disk_files = glob.glob(f'{log_dir}/access.2*')
|
||||
+ log.info(f"Actual files on disk (compress={compress_enabled}): {len(disk_files)}")
|
||||
+ log.info(f"Disk files (compress={compress_enabled}): {disk_files}")
|
||||
+
|
||||
+ disk_files_for_compare = set()
|
||||
+ for fpath in disk_files:
|
||||
+ if compression_on and fpath.endswith('.gz'):
|
||||
+ disk_files_for_compare.add(fpath[:-3])
|
||||
+ else:
|
||||
+ disk_files_for_compare.add(fpath)
|
||||
+
|
||||
+ list_files_set = set(accesslog_list)
|
||||
+ missing_from_disk = list_files_set - disk_files_for_compare
|
||||
+ extra_on_disk = disk_files_for_compare - list_files_set
|
||||
+
|
||||
+ if missing_from_disk:
|
||||
+ log.error(
|
||||
+ f"[compress={compress_enabled}] Files in list but NOT on disk: {missing_from_disk}"
|
||||
+ )
|
||||
+ if extra_on_disk:
|
||||
+ log.warning(
|
||||
+ f"[compress={compress_enabled}] Files on disk but NOT in list: {extra_on_disk}"
|
||||
+ )
|
||||
+
|
||||
+ assert not missing_from_disk, (
|
||||
+ f"nsslapd-accesslog-list mismatch (compress={compress_enabled})! "
|
||||
+ f"Files listed but missing from disk: {missing_from_disk}. "
|
||||
+ f"This indicates the server's internal list is out of sync with actual files."
|
||||
+ )
|
||||
+
|
||||
+ if len(extra_on_disk) > 2:
|
||||
+ log.warning(
|
||||
+ f"Potential log tracking issue (compress={compress_enabled}): "
|
||||
+ f"{len(extra_on_disk)} files on disk are not tracked in the accesslog-list: "
|
||||
+ f"{extra_on_disk}"
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+def test_accesslog_list_mixed_compression(topo):
|
||||
+ """Test that nsslapd-accesslog-list correctly tracks both compressed and uncompressed logs.
|
||||
+
|
||||
+ :id: 11b088cd-23be-407d-ad16-4ce2e12da09e
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Clean up existing rotated logs and reset configuration
|
||||
+ 2. Create rotated logs with compression OFF
|
||||
+ 3. Enable compression and create more rotated logs
|
||||
+ 4. Get the nsslapd-accesslog-list attribute
|
||||
+ 5. Compare with actual files on disk
|
||||
+ 6. Verify all files are correctly tracked (uncompressed and compressed)
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success - uncompressed rotated logs created
|
||||
+ 3. Success - compressed rotated logs created
|
||||
+ 4. Success
|
||||
+ 5. Success
|
||||
+ 6. The list should contain base filenames (without .gz) that
|
||||
+ correspond to files on disk (either as-is or with .gz suffix)
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ suffix = Domain(inst, DEFAULT_SUFFIX)
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+
|
||||
+ # Clean up before test
|
||||
+ remove_rotated_access_logs(inst)
|
||||
+ reset_access_log_config(inst)
|
||||
+ inst.restart()
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '1')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', '20')
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ for i in range(15):
|
||||
+ log.info(f"Generating load for uncompressed rotation {i+1}/15")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ # Check what we have so far
|
||||
+ uncompressed_files = glob.glob(f'{log_dir}/access.2*')
|
||||
+ log.info(f"Files on disk after uncompressed phase: {uncompressed_files}")
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'on')
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ for i in range(15):
|
||||
+ log.info(f"Generating load for compressed rotation {i+1}/15")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ time.sleep(3)
|
||||
+
|
||||
+ accesslog_list = inst.config.get_attr_vals_utf8('nsslapd-accesslog-list')
|
||||
+
|
||||
+ disk_files = glob.glob(f'{log_dir}/access.2*')
|
||||
+
|
||||
+ log.info(f"nsslapd-accesslog-list entries: {len(accesslog_list)}")
|
||||
+ log.info(f"nsslapd-accesslog-list: {sorted(accesslog_list)}")
|
||||
+ log.info(f"Actual files on disk: {len(disk_files)}")
|
||||
+ log.info(f"Disk files: {sorted(disk_files)}")
|
||||
+
|
||||
+ compressed_on_disk = [f for f in disk_files if f.endswith('.gz')]
|
||||
+ uncompressed_on_disk = [f for f in disk_files if not f.endswith('.gz')]
|
||||
+ log.info(f"Compressed files on disk: {compressed_on_disk}")
|
||||
+ log.info(f"Uncompressed files on disk: {uncompressed_on_disk}")
|
||||
+
|
||||
+ list_files_set = set(accesslog_list)
|
||||
+
|
||||
+ disk_files_base = set()
|
||||
+ for fpath in disk_files:
|
||||
+ if fpath.endswith('.gz'):
|
||||
+ disk_files_base.add(fpath[:-3]) # Strip .gz
|
||||
+ else:
|
||||
+ disk_files_base.add(fpath)
|
||||
+
|
||||
+ missing_from_disk = list_files_set - disk_files_base
|
||||
+
|
||||
+ extra_on_disk = disk_files_base - list_files_set
|
||||
+
|
||||
+ if missing_from_disk:
|
||||
+ log.error(f"Files in list but NOT on disk: {missing_from_disk}")
|
||||
+ if extra_on_disk:
|
||||
+ log.warning(f"Files on disk but NOT in list: {extra_on_disk}")
|
||||
+
|
||||
+ assert not missing_from_disk, (
|
||||
+ f"nsslapd-accesslog-list contains stale entries! "
|
||||
+ f"Files in list but not on disk (as base or .gz): {missing_from_disk}"
|
||||
+ )
|
||||
+
|
||||
+ for list_file in accesslog_list:
|
||||
+ exists_uncompressed = os.path.exists(list_file)
|
||||
+ exists_compressed = os.path.exists(list_file + '.gz')
|
||||
+ assert exists_uncompressed or exists_compressed, (
|
||||
+ f"File in accesslog-list does not exist on disk: {list_file} "
|
||||
+ f"(checked both {list_file} and {list_file}.gz)"
|
||||
+ )
|
||||
+ if exists_compressed and not exists_uncompressed:
|
||||
+ log.info(f" {list_file} -> exists as .gz (compressed)")
|
||||
+ elif exists_uncompressed:
|
||||
+ log.info(f" {list_file} -> exists (uncompressed)")
|
||||
+
|
||||
+ if len(extra_on_disk) > 1:
|
||||
+ log.warning(
|
||||
+ f"Some files on disk are not tracked in accesslog-list: {extra_on_disk}"
|
||||
+ )
|
||||
+
|
||||
+ log.info("Mixed compression test completed successfully")
|
||||
+
|
||||
+
|
||||
def test_log_flush_and_rotation_crash(topo):
|
||||
- """Make sure server does not crash whening flushing a buffer and rotating
|
||||
+ """Make sure server does not crash when flushing a buffer and rotating
|
||||
the log at the same time
|
||||
|
||||
:id: d4b0af2f-48b2-45f5-ae8b-f06f692c3133
|
||||
@@ -36,6 +374,7 @@ def test_log_flush_and_rotation_crash(topo):
|
||||
3. Success
|
||||
4. Success
|
||||
"""
|
||||
+ # NOTE: This test is placed last as it may affect the suffix state.
|
||||
|
||||
inst = topo.standalone
|
||||
|
||||
diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c
|
||||
index 6f57a3d9c..ca8d481e5 100644
|
||||
--- a/ldap/servers/slapd/log.c
|
||||
+++ b/ldap/servers/slapd/log.c
|
||||
@@ -135,6 +135,7 @@ static void vslapd_log_emergency_error(LOGFD fp, const char *msg, int locked);
|
||||
static int get_syslog_loglevel(int loglevel);
|
||||
static void log_external_libs_debug_openldap_print(char *buffer);
|
||||
static int log__fix_rotationinfof(char *pathname);
|
||||
+static int log__validate_rotated_logname(const char *timestamp_str, PRBool *is_compressed);
|
||||
|
||||
static int
|
||||
get_syslog_loglevel(int loglevel)
|
||||
@@ -410,7 +411,7 @@ g_log_init()
|
||||
loginfo.log_security_fdes = NULL;
|
||||
loginfo.log_security_file = NULL;
|
||||
loginfo.log_securityinfo_file = NULL;
|
||||
- loginfo.log_numof_access_logs = 1;
|
||||
+ loginfo.log_numof_security_logs = 1;
|
||||
loginfo.log_security_logchain = NULL;
|
||||
loginfo.log_security_buffer = log_create_buffer(LOG_BUFFER_MAXSIZE);
|
||||
loginfo.log_security_compress = cfg->securitylog_compress;
|
||||
@@ -3311,7 +3312,7 @@ log__open_accesslogfile(int logfile_state, int locked)
|
||||
}
|
||||
} else if (loginfo.log_access_compress) {
|
||||
if (compress_log_file(newfile, loginfo.log_access_mode) != 0) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "log__open_auditfaillogfile",
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "log__open_accesslogfile",
|
||||
"failed to compress rotated access log (%s)\n",
|
||||
newfile);
|
||||
} else {
|
||||
@@ -4710,6 +4711,50 @@ log__delete_rotated_logs()
|
||||
loginfo.log_error_logchain = NULL;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * log__validate_rotated_logname
|
||||
+ *
|
||||
+ * Validates that a log filename timestamp suffix matches the expected format:
|
||||
+ * YYYYMMDD-HHMMSS (15 chars) or YYYYMMDD-HHMMSS.gz (18 chars) for compressed files.
|
||||
+ * Uses regex pattern: ^[0-9]{8}-[0-9]{6}(\.gz)?$
|
||||
+ *
|
||||
+ * \param timestamp_str The timestamp portion of the log filename (after the first '.')
|
||||
+ * \param is_compressed Output parameter set to PR_TRUE if the file has .gz suffix
|
||||
+ * \return 1 if valid, 0 if invalid
|
||||
+ */
|
||||
+static int
|
||||
+log__validate_rotated_logname(const char *timestamp_str, PRBool *is_compressed)
|
||||
+{
|
||||
+ Slapi_Regex *re = NULL;
|
||||
+ char *re_error = NULL;
|
||||
+ int rc = 0;
|
||||
+
|
||||
+ /* Match YYYYMMDD-HHMMSS with optional .gz suffix */
|
||||
+ static const char *pattern = "^[0-9]{8}-[0-9]{6}(\\.gz)?$";
|
||||
+
|
||||
+ *is_compressed = PR_FALSE;
|
||||
+
|
||||
+ re = slapi_re_comp(pattern, &re_error);
|
||||
+ if (re == NULL) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "log__validate_rotated_logname",
|
||||
+ "Failed to compile regex: %s\n", re_error ? re_error : "unknown error");
|
||||
+ slapi_ch_free_string(&re_error);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ rc = slapi_re_exec_nt(re, timestamp_str);
|
||||
+ if (rc == 1) {
|
||||
+ /* Check if compressed by looking for .gz suffix */
|
||||
+ size_t len = strlen(timestamp_str);
|
||||
+ if (len >= 3 && strcmp(timestamp_str + len - 3, ".gz") == 0) {
|
||||
+ *is_compressed = PR_TRUE;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_re_free(re);
|
||||
+ return rc == 1 ? 1 : 0;
|
||||
+}
|
||||
+
|
||||
#define ERRORSLOG 1
|
||||
#define ACCESSLOG 2
|
||||
#define AUDITLOG 3
|
||||
@@ -4792,31 +4837,19 @@ log__fix_rotationinfof(char *pathname)
|
||||
}
|
||||
} else if (0 == strncmp(log_type, dirent->name, strlen(log_type)) &&
|
||||
(p = strchr(dirent->name, '.')) != NULL &&
|
||||
- NULL != strchr(p, '-')) /* e.g., errors.20051123-165135 */
|
||||
+ NULL != strchr(p, '-')) /* e.g., errors.20051123-165135 or errors.20051123-165135.gz */
|
||||
{
|
||||
struct logfileinfo *logp;
|
||||
- char *q;
|
||||
- int ignoreit = 0;
|
||||
-
|
||||
- for (q = ++p; q && *q; q++) {
|
||||
- if (*q != '-' &&
|
||||
- *q != '.' && /* .gz */
|
||||
- *q != 'g' &&
|
||||
- *q != 'z' &&
|
||||
- !isdigit(*q))
|
||||
- {
|
||||
- ignoreit = 1;
|
||||
- }
|
||||
- }
|
||||
- if (ignoreit || (q - p != 15)) {
|
||||
+ PRBool is_compressed = PR_FALSE;
|
||||
+
|
||||
+ /* Skip the '.' to get the timestamp portion */
|
||||
+ p++;
|
||||
+ if (!log__validate_rotated_logname(p, &is_compressed)) {
|
||||
continue;
|
||||
}
|
||||
logp = (struct logfileinfo *)slapi_ch_malloc(sizeof(struct logfileinfo));
|
||||
logp->l_ctime = log_reverse_convert_time(p);
|
||||
- logp->l_compressed = PR_FALSE;
|
||||
- if (strcmp(p + strlen(p) - 3, ".gz") == 0) {
|
||||
- logp->l_compressed = PR_TRUE;
|
||||
- }
|
||||
+ logp->l_compressed = is_compressed;
|
||||
PR_snprintf(rotated_log, rotated_log_len, "%s/%s",
|
||||
logsdir, dirent->name);
|
||||
|
||||
@@ -4982,23 +5015,15 @@ log__check_prevlogs(FILE *fp, char *pathname)
|
||||
for (dirent = PR_ReadDir(dirptr, dirflags); dirent;
|
||||
dirent = PR_ReadDir(dirptr, dirflags)) {
|
||||
if (0 == strncmp(log_type, dirent->name, strlen(log_type)) &&
|
||||
- (p = strrchr(dirent->name, '.')) != NULL &&
|
||||
- NULL != strchr(p, '-')) { /* e.g., errors.20051123-165135 */
|
||||
- char *q;
|
||||
- int ignoreit = 0;
|
||||
-
|
||||
- for (q = ++p; q && *q; q++) {
|
||||
- if (*q != '-' &&
|
||||
- *q != '.' && /* .gz */
|
||||
- *q != 'g' &&
|
||||
- *q != 'z' &&
|
||||
- !isdigit(*q))
|
||||
- {
|
||||
- ignoreit = 1;
|
||||
- }
|
||||
- }
|
||||
- if (ignoreit || (q - p != 15))
|
||||
+ (p = strchr(dirent->name, '.')) != NULL &&
|
||||
+ NULL != strchr(p, '-')) { /* e.g., errors.20051123-165135 or errors.20051123-165135.gz */
|
||||
+ PRBool is_compressed = PR_FALSE;
|
||||
+
|
||||
+ /* Skip the '.' to get the timestamp portion */
|
||||
+ p++;
|
||||
+ if (!log__validate_rotated_logname(p, &is_compressed)) {
|
||||
continue;
|
||||
+ }
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
buf[BUFSIZ - 1] = '\0';
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
From 524cae18721234e1bc8d958c89684008d62e6b30 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 15:33:08 +0000
|
||||
Subject: [PATCH] Issue 7224 - CI Test - Simplify
|
||||
test_reserve_descriptor_validation (#7225)
|
||||
|
||||
Description:
|
||||
Previously, the test_reserve_descriptor_validation CItest calculated
|
||||
the expected number of file descriptors based on backends, indexes,
|
||||
SSL/FIPS mode, and compared it to the value returned by the server.
|
||||
This approach is fragile, especially in FIPS mode.
|
||||
|
||||
Fix:
|
||||
The test has been updated to simply verify that the server corrects
|
||||
the configured nsslapd-reservedescriptors value if it is set too low,
|
||||
instead of calculating the expected total.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7224
|
||||
|
||||
Reviewed by: @bsimonova (Thank you)
|
||||
---
|
||||
.../suites/resource_limits/fdlimits_test.py | 36 +++++++------------
|
||||
1 file changed, 13 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
|
||||
index edcae28a5..9d8fdec52 100644
|
||||
--- a/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
|
||||
+++ b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
|
||||
@@ -27,7 +27,7 @@ RESRV_FD_ATTR = "nsslapd-reservedescriptors"
|
||||
GLOBAL_LIMIT = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
|
||||
SYSTEMD_LIMIT = ensure_str(check_output("systemctl show -p LimitNOFILE dirsrv@standalone1".split(" ")).strip()).split('=')[1]
|
||||
CUSTOM_VAL = str(int(SYSTEMD_LIMIT) - 10)
|
||||
-RESRV_DESC_VAL = str(10)
|
||||
+RESRV_DESC_VAL_LOW = 10
|
||||
TOO_HIGH_VAL = str(GLOBAL_LIMIT * 2)
|
||||
TOO_HIGH_VAL2 = str(int(SYSTEMD_LIMIT) * 2)
|
||||
TOO_LOW_VAL = "0"
|
||||
@@ -86,40 +86,30 @@ def test_reserve_descriptor_validation(topology_st):
|
||||
:id: 9bacdbcc-7754-4955-8a56-1d8c82bce274
|
||||
:setup: Standalone Instance
|
||||
:steps:
|
||||
- 1. Set attr nsslapd-reservedescriptors to a low value of RESRV_DESC_VAL (10)
|
||||
+ 1. Set attr nsslapd-reservedescriptors to a low value (10)
|
||||
2. Verify low value has been set
|
||||
3. Restart instance (On restart the reservedescriptor attr will be validated)
|
||||
- 4. Check updated value for nsslapd-reservedescriptors attr
|
||||
+ 4. Verify corrected value for nsslapd-reservedescriptors > low value
|
||||
:expectedresults:
|
||||
1. Success
|
||||
- 2. A value of RESRV_DESC_VAL (10) is returned
|
||||
+ 2. A value of RESRV_DESC_VAL_LOW (10) is returned
|
||||
3. Success
|
||||
- 4. A value of STANDALONE_INST_RESRV_DESCS (55) is returned
|
||||
+ 4. Corrected value for nsslapd-reservedescriptors > low value
|
||||
"""
|
||||
|
||||
- # Set nsslapd-reservedescriptors to a low value (RESRV_DESC_VAL:10)
|
||||
- topology_st.standalone.config.set(RESRV_FD_ATTR, RESRV_DESC_VAL)
|
||||
- resrv_fd = topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR)
|
||||
- assert resrv_fd == RESRV_DESC_VAL
|
||||
+ # Set nsslapd-reservedescriptors to a low value (10)
|
||||
+ topology_st.standalone.config.set(RESRV_FD_ATTR, str(RESRV_DESC_VAL_LOW))
|
||||
+ resrv_fd = int(topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR))
|
||||
+ assert resrv_fd == RESRV_DESC_VAL_LOW
|
||||
|
||||
# An instance restart triggers a validation of the configured nsslapd-reservedescriptors attribute
|
||||
topology_st.standalone.restart()
|
||||
|
||||
- """
|
||||
- A standalone instance contains a single backend with default indexes
|
||||
- so we only check these. TODO add tests for repl, chaining, PTA, SSL
|
||||
- """
|
||||
- STANDALONE_INST_RESRV_DESCS = 20 # 20 = Reserve descriptor constant
|
||||
- backends = Backends(topology_st.standalone)
|
||||
- STANDALONE_INST_RESRV_DESCS += (len(backends.list()) * 4) # 4 = Backend descriptor constant
|
||||
- for be in backends.list() :
|
||||
- STANDALONE_INST_RESRV_DESCS += len(be.get_indexes().list())
|
||||
-
|
||||
- # Varify reservedescriptors has been updated
|
||||
- resrv_fd = topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR)
|
||||
- assert resrv_fd == str(STANDALONE_INST_RESRV_DESCS)
|
||||
+ # Get the corrected value
|
||||
+ corrected_fd = int(topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR))
|
||||
+ assert corrected_fd > RESRV_DESC_VAL_LOW
|
||||
|
||||
- log.info("test_reserve_descriptor_validation PASSED")
|
||||
+ log.info(f"test_reserve_descriptor_validation PASSED (corrected from {RESRV_DESC_VAL_LOW} to {corrected_fd})")
|
||||
|
||||
@pytest.mark.skipif(ds_is_older("1.4.1.2"), reason="Not implemented")
|
||||
def test_reserve_descriptors_high(topology_st):
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,92 +0,0 @@
|
||||
From 91e03f431d8ee0b1d316ebd7add232bf49360db2 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 10:42:09 +0000
|
||||
Subject: [PATCH] Issue 7231 - Sync repl tests fail in FIPS mode due to non
|
||||
FIPS compliant crypto (#7232)
|
||||
|
||||
Description:
|
||||
Several sync_repl tests fail when running on a FIPS enabled system. The failures
|
||||
are caused by the sync repl client (Sync_persist), using TLS options and ciphers
|
||||
that are not FIPS compatible.
|
||||
|
||||
Fix:
|
||||
Update the sync repl client to use FIPS approved TLS version.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7231
|
||||
|
||||
Reviewed by: @progier389, @droideck (Thank you)
|
||||
---
|
||||
.../tests/suites/syncrepl_plugin/basic_test.py | 15 +++++++++++++--
|
||||
1 file changed, 13 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
index c22331eb5..1e9a530bb 100644
|
||||
--- a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
+++ b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
@@ -20,7 +20,7 @@ from lib389.idm.group import Groups
|
||||
from lib389.topologies import topology_st as topology
|
||||
from lib389.topologies import topology_m2 as topo_m2
|
||||
from lib389.paths import Paths
|
||||
-from lib389.utils import ds_is_older
|
||||
+from lib389.utils import ds_is_older, is_fips
|
||||
from lib389.plugins import RetroChangelogPlugin, ContentSyncPlugin, AutoMembershipPlugin, MemberOfPlugin, MemberOfSharedConfig, AutoMembershipDefinitions, MEPTemplates, MEPConfigs, ManagedEntriesPlugin, MEPTemplate
|
||||
from lib389._constants import *
|
||||
|
||||
@@ -214,6 +214,12 @@ class Sync_persist(threading.Thread, ReconnectLDAPObject, SyncreplConsumer):
|
||||
def run(self):
|
||||
"""Start a sync repl client"""
|
||||
ldap_connection = TestSyncer(self.inst.toLDAPURL())
|
||||
+ ldap_connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
|
||||
+ ldap_connection.set_option(ldap.OPT_X_TLS_CACERTFILE, os.path.join(self.inst.get_config_dir(), "ca.crt"))
|
||||
+ if is_fips():
|
||||
+ ldap_connection.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_X_TLS_PROTOCOL_TLS1_2)
|
||||
+ ldap_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
|
||||
+
|
||||
ldap_connection.simple_bind_s('cn=directory manager', 'password')
|
||||
ldap_search = ldap_connection.syncrepl_search(
|
||||
"dc=example,dc=com",
|
||||
@@ -253,6 +259,7 @@ def test_sync_repl_mep(topology, request):
|
||||
5. Success
|
||||
"""
|
||||
inst = topology[0]
|
||||
+ inst.enable_tls()
|
||||
|
||||
# Enable/configure retroCL
|
||||
plugin = RetroChangelogPlugin(inst)
|
||||
@@ -338,6 +345,7 @@ def test_sync_repl_cookie(topology, init_sync_repl_plugins, request):
|
||||
5.: succeeds
|
||||
"""
|
||||
inst = topology[0]
|
||||
+ inst.enable_tls()
|
||||
|
||||
# create a sync repl client and wait 5 seconds to be sure it is running
|
||||
sync_repl = Sync_persist(inst)
|
||||
@@ -404,6 +412,8 @@ def test_sync_repl_cookie_add_del(topology, init_sync_repl_plugins, request):
|
||||
6.: succeeds
|
||||
"""
|
||||
inst = topology[0]
|
||||
+ inst.enable_tls()
|
||||
+
|
||||
# create a sync repl client and wait 5 seconds to be sure it is running
|
||||
sync_repl = Sync_persist(inst)
|
||||
sync_repl.start()
|
||||
@@ -547,6 +557,7 @@ def test_sync_repl_cenotaph(topo_m2, request):
|
||||
5. Should succeeds
|
||||
"""
|
||||
m1 = topo_m2.ms["supplier1"]
|
||||
+ m1.enable_tls()
|
||||
# Enable/configure retroCL
|
||||
plugin = RetroChangelogPlugin(m1)
|
||||
plugin.disable()
|
||||
@@ -605,7 +616,7 @@ def test_sync_repl_dynamic_plugin(topology, request):
|
||||
3. Should succeeds
|
||||
4. Should succeeds
|
||||
"""
|
||||
-
|
||||
+ topology.standalone.enable_tls()
|
||||
# Reset the instance in a default config
|
||||
# Disable content sync plugin
|
||||
topology.standalone.plugins.disable(name=PLUGIN_REPL_SYNC)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
From bd57467f6efd8e398eb2e608331711bdd571d3ac Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 09:58:54 -0500
|
||||
Subject: [PATCH] Issue 7248 - CLI - attribute uniqueness - fix usage for
|
||||
exclude subtree option
|
||||
|
||||
Description:
|
||||
|
||||
Fix typo in usage message for the exclude subtree option
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7248
|
||||
|
||||
Reviewed by: progier (Thanks!)
|
||||
---
|
||||
src/lib389/lib389/cli_conf/plugins/attruniq.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_conf/plugins/attruniq.py b/src/lib389/lib389/cli_conf/plugins/attruniq.py
|
||||
index bc925eb1c..26ca5d819 100644
|
||||
--- a/src/lib389/lib389/cli_conf/plugins/attruniq.py
|
||||
+++ b/src/lib389/lib389/cli_conf/plugins/attruniq.py
|
||||
@@ -127,7 +127,7 @@ def _add_parser_args(parser):
|
||||
help='Sets the DN under which the plug-in checks for uniqueness of '
|
||||
'the attributes value. This attribute is multi-valued (uniqueness-subtrees)')
|
||||
parser.add_argument('--exclude-subtree', nargs='+',
|
||||
- help='Sets subtrees that should not excludedfrom attribute uniqueness. '
|
||||
+ help='Sets subtrees that should be excluded from attribute uniqueness checks. '
|
||||
'This attribute is multi-valued (uniqueness-exclude-subtrees)')
|
||||
parser.add_argument('--across-all-subtrees', choices=['on', 'off'], type=str.lower,
|
||||
help='If enabled (on), the plug-in checks that the attribute is unique across all subtrees '
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,201 +0,0 @@
|
||||
From 180329d9ca672306f8e90d599890be3a6a8aa95c Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 11:13:45 -0500
|
||||
Subject: [PATCH] Issue - CLI - dsctl db2index needs some hardening with MBD
|
||||
|
||||
Description:
|
||||
|
||||
The usage for dsctl db2index was confusing. The way the attr options and
|
||||
backend name were displayed it looks like the backend name could come after
|
||||
the attributes, but instead the backend name was treated as an attribute.
|
||||
|
||||
Instead make the backend name required, and change the attribute naming to
|
||||
require individual options instead of a list of values.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7250
|
||||
|
||||
Reviewed by: progier(Thanks!)
|
||||
---
|
||||
.../tests/suites/import/import_test.py | 2 +-
|
||||
src/lib389/lib389/__init__.py | 40 ++++++-------------
|
||||
src/lib389/lib389/cli_ctl/dbtasks.py | 37 +++++++----------
|
||||
3 files changed, 27 insertions(+), 52 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/import/import_test.py b/dirsrvtests/tests/suites/import/import_test.py
|
||||
index 18caec633..058a4b3c7 100644
|
||||
--- a/dirsrvtests/tests/suites/import/import_test.py
|
||||
+++ b/dirsrvtests/tests/suites/import/import_test.py
|
||||
@@ -512,7 +512,7 @@ def test_entry_with_escaped_characters_fails_to_import_and_index(topo, _import_c
|
||||
count += 1
|
||||
# Now re-index the database
|
||||
topo.standalone.stop()
|
||||
- topo.standalone.db2index()
|
||||
+ topo.standalone.db2index(bename="userroot")
|
||||
topo.standalone.start()
|
||||
# Should not return error.
|
||||
assert not topo.standalone.searchErrorsLog('error')
|
||||
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
|
||||
index 39a2852e5..ec47987be 100644
|
||||
--- a/src/lib389/lib389/__init__.py
|
||||
+++ b/src/lib389/lib389/__init__.py
|
||||
@@ -2985,7 +2985,7 @@ class DirSrv(SimpleLDAPObject, object):
|
||||
|
||||
return True
|
||||
|
||||
- def db2index(self, bename=None, suffixes=None, attrs=None, vlvTag=None):
|
||||
+ def db2index(self, bename, suffixes=None, attrs=None, vlvTag=None):
|
||||
"""
|
||||
@param bename - The backend name to reindex
|
||||
@param suffixes - List/tuple of suffixes to reindex, currently unused
|
||||
@@ -2998,34 +2998,18 @@ class DirSrv(SimpleLDAPObject, object):
|
||||
if self.status():
|
||||
self.log.error("db2index: Can not operate while directory server is running")
|
||||
return False
|
||||
- cmd = [prog, ]
|
||||
- # No backend specified, do an upgrade on all backends
|
||||
- # Backend and no attrs specified, reindex with all backend indexes
|
||||
- # Backend and attr/s specified, reindex backend with attr/s
|
||||
- if bename:
|
||||
- cmd.append('db2index')
|
||||
- cmd.append('-n')
|
||||
- cmd.append(bename)
|
||||
- if attrs:
|
||||
- for attr in attrs:
|
||||
- cmd.append('-t')
|
||||
- cmd.append(attr)
|
||||
- else:
|
||||
- dse_ldif = DSEldif(self)
|
||||
- indexes = dse_ldif.get_indexes(bename)
|
||||
- if indexes:
|
||||
- for idx in indexes:
|
||||
- cmd.append('-t')
|
||||
- cmd.append(idx)
|
||||
+ cmd = [prog, 'db2index', '-n', bename, '-D', self.get_config_dir()]
|
||||
+ if attrs:
|
||||
+ for attr in attrs:
|
||||
+ cmd.append('-t')
|
||||
+ cmd.append(attr)
|
||||
else:
|
||||
- cmd.append('upgradedb')
|
||||
- cmd.append('-a')
|
||||
- now = datetime.now().isoformat()
|
||||
- cmd.append(os.path.join(self.get_bak_dir(), 'reindex_%s' % now))
|
||||
- cmd.append('-f')
|
||||
-
|
||||
- cmd.append('-D')
|
||||
- cmd.append(self.get_config_dir())
|
||||
+ dse_ldif = DSEldif(self)
|
||||
+ indexes = dse_ldif.get_indexes(bename)
|
||||
+ if indexes:
|
||||
+ for idx in indexes:
|
||||
+ cmd.append('-t')
|
||||
+ cmd.append(idx)
|
||||
|
||||
try:
|
||||
result = subprocess.check_output(cmd, encoding='utf-8')
|
||||
diff --git a/src/lib389/lib389/cli_ctl/dbtasks.py b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
index 16da966d1..cd96cdaf7 100644
|
||||
--- a/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
+++ b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
@@ -26,32 +26,18 @@ class IndexOrdering(Enum):
|
||||
|
||||
|
||||
def dbtasks_db2index(inst, log, args):
|
||||
- rtn = False
|
||||
- if not args.backend:
|
||||
- if not inst.db2index():
|
||||
- rtn = False
|
||||
- else:
|
||||
- rtn = True
|
||||
- elif args.backend and not args.attr:
|
||||
- if not inst.db2index(bename=args.backend):
|
||||
- rtn = False
|
||||
- else:
|
||||
- rtn = True
|
||||
+ inst.log = log
|
||||
+ if not inst.db2index(bename=args.backend, attrs=args.attr):
|
||||
+ log.fatal("db2index failed")
|
||||
+ return False
|
||||
else:
|
||||
- if not inst.db2index(bename=args.backend, attrs=args.attr):
|
||||
- rtn = False
|
||||
- else:
|
||||
- rtn = True
|
||||
- if rtn:
|
||||
log.info("db2index successful")
|
||||
- return rtn
|
||||
- else:
|
||||
- log.fatal("db2index failed")
|
||||
- return rtn
|
||||
+ return True
|
||||
|
||||
|
||||
def dbtasks_db2bak(inst, log, args):
|
||||
# Needs an output name?
|
||||
+ inst.log = log
|
||||
if not inst.db2bak(args.archive):
|
||||
log.fatal("db2bak failed")
|
||||
return False
|
||||
@@ -61,6 +47,7 @@ def dbtasks_db2bak(inst, log, args):
|
||||
|
||||
def dbtasks_bak2db(inst, log, args):
|
||||
# Needs the archive to restore.
|
||||
+ inst.log = log
|
||||
if not inst.bak2db(args.archive):
|
||||
log.fatal("bak2db failed")
|
||||
return False
|
||||
@@ -70,6 +57,7 @@ def dbtasks_bak2db(inst, log, args):
|
||||
|
||||
def dbtasks_db2ldif(inst, log, args):
|
||||
# If export filename is provided, check if file path exists
|
||||
+ inst.log = log
|
||||
if args.ldif:
|
||||
path = Path(args.ldif)
|
||||
parent = path.parent.absolute()
|
||||
@@ -88,6 +76,7 @@ def dbtasks_db2ldif(inst, log, args):
|
||||
|
||||
def dbtasks_ldif2db(inst, log, args):
|
||||
# Check if ldif file exists
|
||||
+ inst.log = log
|
||||
if not os.path.exists(args.ldif):
|
||||
raise ValueError("The LDIF file does not exist: " + args.ldif)
|
||||
|
||||
@@ -103,6 +92,7 @@ def dbtasks_ldif2db(inst, log, args):
|
||||
|
||||
|
||||
def dbtasks_backups(inst, log, args):
|
||||
+ inst.log = log
|
||||
if args.delete:
|
||||
# Delete backup
|
||||
inst.del_backup(args.delete[0])
|
||||
@@ -117,6 +107,7 @@ def dbtasks_backups(inst, log, args):
|
||||
|
||||
|
||||
def dbtasks_ldifs(inst, log, args):
|
||||
+ inst.log = log
|
||||
if args.delete:
|
||||
# Delete LDIF file
|
||||
inst.del_ldif(args.delete[0])
|
||||
@@ -131,6 +122,7 @@ def dbtasks_ldifs(inst, log, args):
|
||||
|
||||
|
||||
def dbtasks_verify(inst, log, args):
|
||||
+ inst.log = log
|
||||
if not inst.dbverify(bename=args.backend):
|
||||
log.fatal("dbverify failed")
|
||||
return False
|
||||
@@ -521,9 +513,8 @@ def dbtasks_index_check(inst, log, args):
|
||||
|
||||
def create_parser(subcommands):
|
||||
db2index_parser = subcommands.add_parser('db2index', help="Initialise a reindex of the server database. The server must be stopped for this to proceed.", formatter_class=CustomHelpFormatter)
|
||||
- # db2index_parser.add_argument('suffix', help="The suffix to reindex. IE dc=example,dc=com.")
|
||||
- db2index_parser.add_argument('backend', nargs="?", help="The backend to reindex. IE userRoot", default=False)
|
||||
- db2index_parser.add_argument('--attr', nargs="*", help="The attribute's to reindex. IE --attr aci cn givenname", default=False)
|
||||
+ db2index_parser.add_argument('backend', help="The backend to reindex. IE userRoot")
|
||||
+ db2index_parser.add_argument('--attr', action='append', help="An attribute to reindex. IE: --attr member --attr cn ...")
|
||||
db2index_parser.set_defaults(func=dbtasks_db2index)
|
||||
|
||||
db2bak_parser = subcommands.add_parser('db2bak', help="Initialise a BDB backup of the database. The server must be stopped for this to proceed.", formatter_class=CustomHelpFormatter)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
From c831a7af2afb7cfd2eb958476a7a8d421ac5d339 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 15:38:52 +0100
|
||||
Subject: [PATCH] Issue 7184 - (2nd) argparse.HelpFormatter
|
||||
_format_actions_usage() is deprecated (#7257)
|
||||
|
||||
Description:
|
||||
`_format_actions_usage()` was also removed in Python 3.14.3.
|
||||
Replace version check with `isinstance()` to handle the return type of
|
||||
`_get_actions_usage_parts()` more robustly across Python versions.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7184
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7253
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
src/lib389/lib389/cli_base/__init__.py | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_base/__init__.py b/src/lib389/lib389/cli_base/__init__.py
|
||||
index f1055aadc..3af8a46e6 100644
|
||||
--- a/src/lib389/lib389/cli_base/__init__.py
|
||||
+++ b/src/lib389/lib389/cli_base/__init__.py
|
||||
@@ -420,11 +420,11 @@ class CustomHelpFormatter(argparse.HelpFormatter):
|
||||
else:
|
||||
# Use _get_actions_usage_parts() for Python 3.13 and later
|
||||
action_parts = self._get_actions_usage_parts(parent_arguments, [])
|
||||
- if sys.version_info >= (3, 15):
|
||||
- # Python 3.15 returns a tuple (list of actions, count of actions)
|
||||
+ if isinstance(action_parts, tuple):
|
||||
+ # Python 3.14.3+ and 3.15+ return a tuple (list of actions, count of actions)
|
||||
formatted_options = ' '.join(action_parts[0])
|
||||
else:
|
||||
- # Python 3.13 and 3.14 return a list of actions
|
||||
+ # Earlier versions return a list of actions
|
||||
formatted_options = ' '.join(action_parts)
|
||||
|
||||
# If formatted_options already in usage - remove them
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
From a7f8eb79dde03ac771de2335a2acb4aed48483a2 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 16:27:25 +0100
|
||||
Subject: [PATCH] Issue 7213 - (2nd) MDB_BAD_VALSIZE error while handling VLV
|
||||
(#7258)
|
||||
|
||||
Decription:
|
||||
Disable test_vlv_long_attribute_value on BDB as it hangs sometimes in
|
||||
CI, blocking other pipelines.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7213
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
dirsrvtests/tests/suites/vlv/regression_test.py | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/vlv/regression_test.py b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
index 1cc03c303..94001af62 100644
|
||||
--- a/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
@@ -1178,6 +1178,7 @@ def test_vlv_with_mr(vlv_setup_with_uid_mr):
|
||||
|
||||
|
||||
|
||||
+@pytest.mark.skipif(get_default_db_lib() == "bdb", reason="Hangs on BDB")
|
||||
def test_vlv_long_attribute_value(topology_st, request):
|
||||
"""
|
||||
Test VLV with an entry containing a very long attribute value (2K).
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
From 75b1213dfb94495160a735c2688cee03d6be5a9a Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 16:58:24 +0100
|
||||
Subject: [PATCH] Issue 7223 - Use lexicographical order for ancestorid (#7256)
|
||||
|
||||
Description:
|
||||
`ldbm_instance_create_default_indexes()` configured ancestorid with
|
||||
integerOrderingMatch in the in-memory attrinfo, but ancestorid on disk
|
||||
might be using lexicographic ordering (data before the upgrade or after
|
||||
ldif2db import).
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @tbordaz (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 3fcdb5554..563cf96db 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -239,7 +239,7 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* ancestorid is special, there is actually no such attr type
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch");
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, 0);
|
||||
attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
slapi_entry_free(e);
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
From 7809104b9edb2d05ceee7ca819152f365cc1beb0 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 11 Feb 2026 15:51:47 -0500
|
||||
Subject: [PATCH] Issue 7066/7052 - allow password history to be set to zero
|
||||
and remove history
|
||||
|
||||
Description:
|
||||
|
||||
For local password policies the server was incorrectly rejecting updates that
|
||||
set the value to zero. When password history is set to zero the old passwords
|
||||
in the entry history are not cleaned as expected.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7052
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7066
|
||||
|
||||
Reviewed by: progier(Thanks!)
|
||||
---
|
||||
.../tests/suites/password/pwp_history_test.py | 9 ++++++---
|
||||
ldap/servers/slapd/modify.c | 2 +-
|
||||
ldap/servers/slapd/pw.c | 13 ++++++++++++-
|
||||
3 files changed, 19 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/password/pwp_history_test.py b/dirsrvtests/tests/suites/password/pwp_history_test.py
|
||||
index 8a15a1986..387824bc5 100644
|
||||
--- a/dirsrvtests/tests/suites/password/pwp_history_test.py
|
||||
+++ b/dirsrvtests/tests/suites/password/pwp_history_test.py
|
||||
@@ -108,8 +108,12 @@ def test_history_is_not_overwritten(topology_st, user):
|
||||
user.set('userpassword', USER_PWD)
|
||||
|
||||
|
||||
-def test_basic(topology_st, user):
|
||||
- """Test basic password policy history feature functionality
|
||||
+@pytest.mark.parametrize('policy',
|
||||
+ [(pytest.param('global')),
|
||||
+ (pytest.param('subtree')),
|
||||
+ (pytest.param('user'))])
|
||||
+def test_basic(topology_st, user, policy):
|
||||
+ """Test basic password policy history feature functionality with dynamic count reduction
|
||||
|
||||
:id: 83d74f7d-3036-4944-8839-1b40bbf265ff
|
||||
:setup: Standalone instance, a test user
|
||||
@@ -238,7 +242,6 @@ def test_basic(topology_st, user):
|
||||
|
||||
#
|
||||
# Reset password by Directory Manager(admin reset)
|
||||
- #
|
||||
dm = DirectoryManager(topology_st.standalone)
|
||||
dm.rebind()
|
||||
time.sleep(.5)
|
||||
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
|
||||
index b0066faf8..d76427886 100644
|
||||
--- a/ldap/servers/slapd/modify.c
|
||||
+++ b/ldap/servers/slapd/modify.c
|
||||
@@ -87,7 +87,7 @@ static struct attr_value_check
|
||||
{CONFIG_PW_WARNING_ATTRIBUTE, check_pw_duration_value, 0, -1},
|
||||
{CONFIG_PW_MINLENGTH_ATTRIBUTE, attr_check_minmax, 2, 512},
|
||||
{CONFIG_PW_MAXFAILURE_ATTRIBUTE, attr_check_minmax, 1, 32767},
|
||||
- {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 1, 24},
|
||||
+ {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 0, 24},
|
||||
{CONFIG_PW_LOCKDURATION_ATTRIBUTE, check_pw_duration_value, -1, -1},
|
||||
{CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE, check_pw_resetfailurecount_value, -1, -1},
|
||||
{CONFIG_PW_GRACELIMIT_ATTRIBUTE, attr_check_minmax, 0, -1},
|
||||
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
|
||||
index cda1c404f..12cf759ce 100644
|
||||
--- a/ldap/servers/slapd/pw.c
|
||||
+++ b/ldap/servers/slapd/pw.c
|
||||
@@ -1532,7 +1532,18 @@ update_pw_history(Slapi_PBlock *pb, const Slapi_DN *sdn, char *old_pw)
|
||||
pwpolicy = new_passwdPolicy(pb, dn);
|
||||
|
||||
if (pwpolicy->pw_inhistory == 0){
|
||||
- /* We are only enforcing the current password, just return */
|
||||
+ /* We are only enforcing the current password, just return but first
|
||||
+ * cleanup any old passwords in the history */
|
||||
+ attribute.mod_type = "passwordHistory";
|
||||
+ attribute.mod_op = LDAP_MOD_REPLACE;
|
||||
+ attribute.mod_values = NULL;
|
||||
+ list_of_mods[0] = &attribute;
|
||||
+ list_of_mods[1] = NULL;
|
||||
+ mod_pb = slapi_pblock_new();
|
||||
+ slapi_modify_internal_set_pb_ext(mod_pb, sdn, list_of_mods, NULL, NULL, pw_get_componentID(), 0);
|
||||
+ slapi_modify_internal_pb(mod_pb);
|
||||
+ slapi_pblock_destroy(mod_pb);
|
||||
+
|
||||
return res;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,260 +0,0 @@
|
||||
From 4209ff65310c54b6ab55594984201b1214fb75b2 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 16 Feb 2026 16:52:52 -0500
|
||||
Subject: [PATCH] Issue 7243 - UI - fix certificate table and modal
|
||||
|
||||
Description:
|
||||
|
||||
The certificate table was not handling sorting and pagination correctly, and
|
||||
the add cert modal was not correctly selecting certs on disk
|
||||
|
||||
This was fixed upstream for the hot cetificate features, but only backporting
|
||||
the table/modal changes to older branches
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7243
|
||||
|
||||
Reviewed by: spichugi(Thanks!)
|
||||
---
|
||||
.../src/lib/security/securityModals.jsx | 4 +-
|
||||
.../src/lib/security/securityTables.jsx | 85 ++++++-------------
|
||||
2 files changed, 29 insertions(+), 60 deletions(-)
|
||||
|
||||
diff --git a/src/cockpit/389-console/src/lib/security/securityModals.jsx b/src/cockpit/389-console/src/lib/security/securityModals.jsx
|
||||
index a42aa50d6..c4af753c0 100644
|
||||
--- a/src/cockpit/389-console/src/lib/security/securityModals.jsx
|
||||
+++ b/src/cockpit/389-console/src/lib/security/securityModals.jsx
|
||||
@@ -298,7 +298,9 @@ export class SecurityAddCertModal extends React.Component {
|
||||
<FormSelect
|
||||
value={selectCertName}
|
||||
id="selectCertName"
|
||||
- onChange={handleCertSelect}
|
||||
+ onChange={(e, str) => {
|
||||
+ handleCertSelect(str);
|
||||
+ }}
|
||||
aria-label="FormSelect Input"
|
||||
className="ds-cert-select"
|
||||
validated={selectValidated}
|
||||
diff --git a/src/cockpit/389-console/src/lib/security/securityTables.jsx b/src/cockpit/389-console/src/lib/security/securityTables.jsx
|
||||
index fce4cb04e..a6c64e6c1 100644
|
||||
--- a/src/cockpit/389-console/src/lib/security/securityTables.jsx
|
||||
+++ b/src/cockpit/389-console/src/lib/security/securityTables.jsx
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Grid,
|
||||
GridItem,
|
||||
Pagination,
|
||||
- PaginationVariant,
|
||||
SearchInput,
|
||||
Tooltip,
|
||||
} from '@patternfly/react-core';
|
||||
@@ -94,10 +93,10 @@ class KeyTable extends React.Component {
|
||||
];
|
||||
|
||||
handleSort(_event, index, direction) {
|
||||
- const sortedRows = [...this.state.rows].sort((a, b) =>
|
||||
+ const sortedRows = [...this.state.rows].sort((a, b) =>
|
||||
(a[index] < b[index] ? -1 : a[index] > b[index] ? 1 : 0)
|
||||
);
|
||||
-
|
||||
+
|
||||
this.setState({
|
||||
sortBy: {
|
||||
index,
|
||||
@@ -127,7 +126,7 @@ class KeyTable extends React.Component {
|
||||
>
|
||||
<a className="ds-font-size-sm">{_("What is an orphan key?")}</a>
|
||||
</Tooltip>
|
||||
- <Table
|
||||
+ <Table
|
||||
className="ds-margin-top"
|
||||
aria-label="orph key table"
|
||||
variant="compact"
|
||||
@@ -135,7 +134,7 @@ class KeyTable extends React.Component {
|
||||
<Thead>
|
||||
<Tr>
|
||||
{columns.map((column, idx) => (
|
||||
- <Th
|
||||
+ <Th
|
||||
key={idx}
|
||||
sort={column.sortable ? {
|
||||
sortBy,
|
||||
@@ -163,7 +162,7 @@ class KeyTable extends React.Component {
|
||||
)}
|
||||
{hasRows && (
|
||||
<Td isActionCell>
|
||||
- <ActionsColumn
|
||||
+ <ActionsColumn
|
||||
items={this.getActionsForRow(row)}
|
||||
/>
|
||||
</Td>
|
||||
@@ -224,7 +223,7 @@ class CSRTable extends React.Component {
|
||||
}
|
||||
|
||||
handleSort(_event, index, direction) {
|
||||
- const sortedRows = [...this.state.rows].sort((a, b) =>
|
||||
+ const sortedRows = [...this.state.rows].sort((a, b) =>
|
||||
(a[index] < b[index] ? -1 : a[index] > b[index] ? 1 : 0)
|
||||
);
|
||||
this.setState({
|
||||
@@ -316,7 +315,7 @@ class CSRTable extends React.Component {
|
||||
onClear={(evt) => this.handleSearchChange(evt, '')}
|
||||
/>
|
||||
}
|
||||
- <Table
|
||||
+ <Table
|
||||
className="ds-margin-top"
|
||||
aria-label="csr table"
|
||||
variant="compact"
|
||||
@@ -324,7 +323,7 @@ class CSRTable extends React.Component {
|
||||
<Thead>
|
||||
<Tr>
|
||||
{columns.map((column, idx) => (
|
||||
- <Th
|
||||
+ <Th
|
||||
key={idx}
|
||||
sort={column.sortable ? {
|
||||
sortBy,
|
||||
@@ -352,7 +351,7 @@ class CSRTable extends React.Component {
|
||||
)}
|
||||
{hasRows && (
|
||||
<Td isActionCell>
|
||||
- <ActionsColumn
|
||||
+ <ActionsColumn
|
||||
items={this.getActionsForRow(row)}
|
||||
/>
|
||||
</Td>
|
||||
@@ -418,41 +417,11 @@ class CertTable extends React.Component {
|
||||
}
|
||||
|
||||
handleSort(_event, columnIndex, direction) {
|
||||
- const sorted_rows = [];
|
||||
- const rows = [];
|
||||
- let count = 0;
|
||||
-
|
||||
- // Convert the rows pairings into a sortable array
|
||||
- for (let idx = 0; idx < this.state.rows.length; idx += 2) {
|
||||
- sorted_rows.push({
|
||||
- expandedRow: this.state.rows[idx + 1],
|
||||
- 1: this.state.rows[idx].cells[0].content,
|
||||
- 2: this.state.rows[idx].cells[1].content,
|
||||
- 3: this.state.rows[idx].cells[2].content,
|
||||
- issuer: this.state.rows[idx].issuer,
|
||||
- flags: this.state.rows[idx].flags
|
||||
- });
|
||||
- }
|
||||
+ const rows = [...this.state.rows];
|
||||
|
||||
- sorted_rows.sort((a, b) => (a[columnIndex + 1] > b[columnIndex + 1]) ? 1 : -1);
|
||||
+ rows.sort((a, b) => (a.cells[columnIndex].content > b.cells[columnIndex].content) ? 1 : -1);
|
||||
if (direction !== SortByDirection.asc) {
|
||||
- sorted_rows.reverse();
|
||||
- }
|
||||
-
|
||||
- for (const srow of sorted_rows) {
|
||||
- rows.push({
|
||||
- isOpen: false,
|
||||
- cells: [
|
||||
- { content: srow[1] },
|
||||
- { content: srow[2] },
|
||||
- { content: srow[3] }
|
||||
- ],
|
||||
- issuer: srow.issuer,
|
||||
- flags: srow.flags,
|
||||
- });
|
||||
- srow.expandedRow.parent = count;
|
||||
- rows.push(srow.expandedRow);
|
||||
- count += 2;
|
||||
+ rows.reverse();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -534,18 +503,16 @@ class CertTable extends React.Component {
|
||||
rows.push(
|
||||
{
|
||||
isOpen: false,
|
||||
- cells: [cert.attrs.nickname, cert.attrs.subject, cert.attrs.expires],
|
||||
+ cells: [
|
||||
+ { content: cert.attrs.nickname },
|
||||
+ { content: cert.attrs.subject },
|
||||
+ { content: cert.attrs.expires }
|
||||
+ ],
|
||||
issuer: cert.attrs.issuer,
|
||||
flags: cert.attrs.flags,
|
||||
-
|
||||
- },
|
||||
- {
|
||||
- parent: count,
|
||||
- fullWidth: true,
|
||||
- cells: [{ title: this.getExpandedRow(cert.attrs.issuer, cert.attrs.flags) }]
|
||||
- },
|
||||
+ }
|
||||
);
|
||||
- count += 2;
|
||||
+ count += 1;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -587,7 +554,7 @@ class CertTable extends React.Component {
|
||||
onChange={this.handleSearchChange}
|
||||
onClear={(evt) => this.handleSearchChange(evt, '')}
|
||||
/>}
|
||||
- <Table
|
||||
+ <Table
|
||||
aria-label="cert table"
|
||||
variant='compact'
|
||||
>
|
||||
@@ -626,7 +593,7 @@ class CertTable extends React.Component {
|
||||
</Td>
|
||||
))}
|
||||
<Td isActionCell>
|
||||
- <ActionsColumn
|
||||
+ <ActionsColumn
|
||||
items={this.getActionsForRow(row)}
|
||||
/>
|
||||
</Td>
|
||||
@@ -646,7 +613,7 @@ class CertTable extends React.Component {
|
||||
</Table>
|
||||
{hasRows &&
|
||||
<Pagination
|
||||
- itemCount={this.state.rows.length / 2}
|
||||
+ itemCount={this.state.rows.length}
|
||||
widgetId="pagination-options-menu-bottom"
|
||||
perPage={perPage}
|
||||
page={page}
|
||||
@@ -728,7 +695,7 @@ class CRLTable extends React.Component {
|
||||
if (direction !== SortByDirection.asc) {
|
||||
sorted_rows.reverse();
|
||||
}
|
||||
-
|
||||
+
|
||||
for (const srow of sorted_rows) {
|
||||
rows.push({
|
||||
isOpen: false,
|
||||
@@ -806,14 +773,14 @@ class CRLTable extends React.Component {
|
||||
onChange={this.handleSearchChange}
|
||||
onClear={(evt) => this.handleSearchChange(evt, '')}
|
||||
/>
|
||||
- <Table
|
||||
+ <Table
|
||||
aria-label="CRL Table"
|
||||
variant="compact"
|
||||
>
|
||||
<Thead>
|
||||
<Tr>
|
||||
{this.state.columns.map((column, idx) => (
|
||||
- <Th
|
||||
+ <Th
|
||||
key={idx}
|
||||
sort={column.sortable ? {
|
||||
sortBy: this.state.sortBy,
|
||||
@@ -836,7 +803,7 @@ class CRLTable extends React.Component {
|
||||
))}
|
||||
{this.state.hasRows && (
|
||||
<Td isActionCell>
|
||||
- <ActionsColumn
|
||||
+ <ActionsColumn
|
||||
items={this.getActionsForRow(row)}
|
||||
/>
|
||||
</Td>
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,538 +0,0 @@
|
||||
From 09b6f770f15ee8d333377b7902e18b78f4f7008c Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Wed, 18 Feb 2026 09:26:57 +0100
|
||||
Subject: [PATCH] Issue 7223 - Remove integerOrderingMatch requirement for
|
||||
parentid (#7264)
|
||||
|
||||
Description:
|
||||
integerOrderingMatch was introduced as a requirement for parentid and
|
||||
ancestorid indexes for performance reasons. But after #7096 the order
|
||||
for parentid doesn't make a lot of difference.
|
||||
|
||||
Fix Description:
|
||||
* Remove integerOrderingMatch requirement for parentid.
|
||||
* Read only first 100 keys from dbscan in index ordering check
|
||||
* Do not run dsctl index-check during RPM upgrade
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/pull/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 83 ++++----------
|
||||
ldap/servers/slapd/upgrade.c | 105 ------------------
|
||||
rpm/389-ds-base.spec.in | 3 -
|
||||
src/lib389/lib389/backend.py | 5 +-
|
||||
src/lib389/lib389/cli_ctl/dbtasks.py | 99 ++++++++---------
|
||||
5 files changed, 73 insertions(+), 222 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 3b3651b38..71a9f590a 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -180,7 +180,8 @@ def test_missing_parentid(topology_st, log_buffering_enabled):
|
||||
|
||||
|
||||
def test_missing_matching_rule(topology_st, log_buffering_enabled):
|
||||
- """Check if healthcheck returns DSBLE0007 code when parentId index is missing integerOrderingMatch
|
||||
+ """Check that healthcheck does NOT report DSBLE0007 when parentId index is missing integerOrderingMatch.
|
||||
+ Both lexicographic and integer orderings are valid for parentid.
|
||||
|
||||
:id: 7ffa71db-8995-430a-bed8-59bce944221c
|
||||
:setup: Standalone instance
|
||||
@@ -190,19 +191,14 @@ def test_missing_matching_rule(topology_st, log_buffering_enabled):
|
||||
3. Use healthcheck without --json option
|
||||
4. Use healthcheck with --json option
|
||||
5. Re-add the matching rule
|
||||
- 6. Use healthcheck without --json option
|
||||
- 7. Use healthcheck with --json option
|
||||
:expectedresults:
|
||||
1. Success
|
||||
2. Success
|
||||
- 3. healthcheck reports DSBLE0007 code and related details
|
||||
- 4. healthcheck reports DSBLE0007 code and related details
|
||||
+ 3. healthcheck reports no issues found
|
||||
+ 4. healthcheck reports no issues found
|
||||
5. Success
|
||||
- 6. healthcheck reports no issues found
|
||||
- 7. healthcheck reports no issues found
|
||||
"""
|
||||
|
||||
- RET_CODE = "DSBLE0007"
|
||||
PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
|
||||
standalone = topology_st.standalone
|
||||
@@ -211,17 +207,14 @@ def test_missing_matching_rule(topology_st, log_buffering_enabled):
|
||||
parentid_index = Index(standalone, PARENTID_DN)
|
||||
parentid_index.remove("nsMatchingRule", "integerOrderingMatch")
|
||||
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=RET_CODE)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=RET_CODE)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
log.info("Re-add the integerOrderingMatch matching rule")
|
||||
parentid_index = Index(standalone, PARENTID_DN)
|
||||
parentid_index.add("nsMatchingRule", "integerOrderingMatch")
|
||||
standalone.restart()
|
||||
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
-
|
||||
|
||||
def test_usn_plugin_missing_entryusn(topology_st, usn_plugin_enabled, log_buffering_enabled):
|
||||
"""Check if healthcheck returns DSBLE0007 code when USN plugin is enabled but entryusn index is missing
|
||||
@@ -911,7 +904,9 @@ def test_index_check_fixes_ancestorid_config(topology_st):
|
||||
|
||||
|
||||
def test_index_check_fixes_missing_matching_rule(topology_st):
|
||||
- """Check if dsctl index-check --fix adds missing integerOrderingMatch
|
||||
+ """Check that removing integerOrderingMatch from parentid config is not
|
||||
+ flagged as an issue when disk ordering cannot be determined.
|
||||
+ Both lexicographic and integer orderings are valid for parentid.
|
||||
|
||||
:id: 6c1d4e9f-0a3b-4d5c-1e7f-8a9b0c2d3e4f
|
||||
:setup: Standalone instance
|
||||
@@ -919,18 +914,14 @@ def test_index_check_fixes_missing_matching_rule(topology_st):
|
||||
1. Create DS instance
|
||||
2. Stop the server
|
||||
3. Remove integerOrderingMatch from parentid index using DSEldif
|
||||
- 4. Run dsctl index-check (should detect issue)
|
||||
- 5. Run dsctl index-check --fix
|
||||
- 6. Verify integerOrderingMatch was added back
|
||||
- 7. Start the server
|
||||
+ 4. Run dsctl index-check (should NOT detect issue since disk ordering is unknown)
|
||||
+ 5. Start the server
|
||||
:expectedresults:
|
||||
1. Success
|
||||
2. Success
|
||||
3. Success
|
||||
- 4. index-check returns False and detects missing matching rule
|
||||
- 5. index-check returns True after fix
|
||||
- 6. integerOrderingMatch is present
|
||||
- 7. Success
|
||||
+ 4. index-check returns True (no issues, disk ordering unknown)
|
||||
+ 5. Success
|
||||
"""
|
||||
from lib389.cli_ctl.dbtasks import dbtasks_index_check
|
||||
from lib389.dseldif import DSEldif
|
||||
@@ -964,34 +955,20 @@ def test_index_check_fixes_missing_matching_rule(topology_st):
|
||||
f"integerOrderingMatch should be removed, but found: {mr}"
|
||||
log.info("integerOrderingMatch removed from parentid index")
|
||||
|
||||
- log.info("Run index-check without --fix (should detect issue)")
|
||||
+ log.info("Run index-check (should NOT detect issue - disk ordering unknown)")
|
||||
args = FakeArgs()
|
||||
args.backend = "userRoot"
|
||||
args.fix = False
|
||||
|
||||
result = dbtasks_index_check(standalone, topology_st.logcap.log, args)
|
||||
- assert result is False, "index-check should detect missing matching rule"
|
||||
- assert topology_st.logcap.contains("missing integerOrderingMatch")
|
||||
+ assert result is True, \
|
||||
+ "index-check should not flag missing integerOrderingMatch when disk ordering is unknown"
|
||||
+ assert topology_st.logcap.contains("could not determine disk ordering")
|
||||
topology_st.logcap.flush()
|
||||
|
||||
- log.info("Run index-check with --fix")
|
||||
- args.fix = True
|
||||
- result = dbtasks_index_check(standalone, topology_st.logcap.log, args)
|
||||
- assert result is True, "index-check --fix should succeed"
|
||||
- assert topology_st.logcap.contains("integerOrderingMatch")
|
||||
- topology_st.logcap.flush()
|
||||
-
|
||||
- log.info("Verify integerOrderingMatch was added back")
|
||||
- dse_ldif = DSEldif(standalone) # Reload to get fresh data
|
||||
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
|
||||
- assert matching_rules is not None, "nsMatchingRule should be present"
|
||||
- found_int_order = False
|
||||
- for mr in matching_rules:
|
||||
- if "integerorderingmatch" in mr.lower():
|
||||
- found_int_order = True
|
||||
- break
|
||||
- assert found_int_order, f"integerOrderingMatch should be present, got: {matching_rules}"
|
||||
- log.info("integerOrderingMatch successfully added back")
|
||||
+ log.info("Restore integerOrderingMatch and start the server")
|
||||
+ dse_ldif = DSEldif(standalone)
|
||||
+ dse_ldif.add(parentid_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
|
||||
log.info("Start the server")
|
||||
standalone.start()
|
||||
@@ -1081,7 +1058,7 @@ def test_index_check_fixes_multiple_issues(topology_st):
|
||||
:steps:
|
||||
1. Create DS instance
|
||||
2. Stop the server
|
||||
- 3. Add multiple issues: scanlimit, ancestorid config, missing matching rule
|
||||
+ 3. Add multiple issues: scanlimit and ancestorid config
|
||||
4. Run dsctl index-check (should detect all issues)
|
||||
5. Run dsctl index-check --fix
|
||||
6. Verify all issues were fixed
|
||||
@@ -1123,14 +1100,6 @@ def test_index_check_fixes_multiple_issues(topology_st):
|
||||
]
|
||||
dse_ldif.add_entry(ancestorid_entry)
|
||||
|
||||
- log.info("Add issue 3: Remove integerOrderingMatch from parentid")
|
||||
- dse_ldif = DSEldif(standalone) # Reload
|
||||
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
|
||||
- if matching_rules:
|
||||
- for mr in matching_rules:
|
||||
- if "integerorderingmatch" in mr.lower():
|
||||
- dse_ldif.delete(parentid_dn, "nsMatchingRule", mr)
|
||||
-
|
||||
log.info("Run index-check without --fix (should detect all issues)")
|
||||
args = FakeArgs()
|
||||
args.backend = "userRoot"
|
||||
@@ -1161,16 +1130,6 @@ def test_index_check_fixes_multiple_issues(topology_st):
|
||||
cn_value = dse_ldif.get(ancestorid_dn, "cn", single=True)
|
||||
assert cn_value is None, f"ancestorid config should be removed, got: {cn_value}"
|
||||
|
||||
- # Check matching rule added back
|
||||
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
|
||||
- found_int_order = False
|
||||
- if matching_rules:
|
||||
- for mr in matching_rules:
|
||||
- if "integerorderingmatch" in mr.lower():
|
||||
- found_int_order = True
|
||||
- break
|
||||
- assert found_int_order, f"integerOrderingMatch should be present, got: {matching_rules}"
|
||||
-
|
||||
log.info("All issues verified as fixed")
|
||||
|
||||
log.info("Run index-check again to confirm all clear")
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index d9156cae9..537c38feb 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -500,107 +500,6 @@ upgrade_remove_ancestorid_index_config(void)
|
||||
return uresult;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
- * matching rule.
|
||||
- *
|
||||
- * This function logs a warning if we detect this condition, advising
|
||||
- * the administrator to reindex the affected attributes.
|
||||
- */
|
||||
-static upgrade_status
|
||||
-upgrade_check_id_index_matching_rule(void)
|
||||
-{
|
||||
- struct slapi_pblock *pb = slapi_pblock_new();
|
||||
- Slapi_Entry **backends = NULL;
|
||||
- const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
- const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
- const char *attrs_to_check[] = {"parentid", NULL};
|
||||
- upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
-
|
||||
- /* Search for all backend instances */
|
||||
- slapi_search_internal_set_pb(
|
||||
- pb, be_base_dn,
|
||||
- LDAP_SCOPE_ONELEVEL,
|
||||
- be_filter, NULL, 0, NULL, NULL,
|
||||
- plugin_get_default_component_id(), 0);
|
||||
- slapi_search_internal_pb(pb);
|
||||
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
-
|
||||
- if (backends) {
|
||||
- for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
- const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
- const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
- if (!be_dn || !be_name) {
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- /* Check each attribute that should have integerOrderingMatch */
|
||||
- for (size_t attr_idx = 0; attrs_to_check[attr_idx] != NULL; attr_idx++) {
|
||||
- const char *attr_name = attrs_to_check[attr_idx];
|
||||
- struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
- Slapi_Entry **idx_entries = NULL;
|
||||
- char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
|
||||
- attr_name, be_dn);
|
||||
- char *idx_filter = "(objectclass=nsIndex)";
|
||||
- PRBool has_matching_rule = PR_FALSE;
|
||||
-
|
||||
- if (!idx_dn) {
|
||||
- slapi_pblock_destroy(idx_pb);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- slapi_search_internal_set_pb(
|
||||
- idx_pb, idx_dn,
|
||||
- LDAP_SCOPE_BASE,
|
||||
- idx_filter, NULL, 0, NULL, NULL,
|
||||
- plugin_get_default_component_id(), 0);
|
||||
- slapi_search_internal_pb(idx_pb);
|
||||
- slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
-
|
||||
- if (idx_entries && idx_entries[0]) {
|
||||
- /* Index exists, check if it has integerOrderingMatch */
|
||||
- Slapi_Attr *mr_attr = NULL;
|
||||
- if (slapi_entry_attr_find(idx_entries[0], "nsMatchingRule", &mr_attr) == 0) {
|
||||
- Slapi_Value *sval = NULL;
|
||||
- int idx;
|
||||
- for (idx = slapi_attr_first_value(mr_attr, &sval);
|
||||
- idx != -1;
|
||||
- idx = slapi_attr_next_value(mr_attr, idx, &sval)) {
|
||||
- const struct berval *bval = slapi_value_get_berval(sval);
|
||||
- if (bval && bval->bv_val &&
|
||||
- strcasecmp(bval->bv_val, "integerOrderingMatch") == 0) {
|
||||
- has_matching_rule = PR_TRUE;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (!has_matching_rule) {
|
||||
- /* Index exists but doesn't have integerOrderingMatch, log a warning */
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "upgrade_check_id_index_matching_rule",
|
||||
- "Index '%s' in backend '%s' is missing 'nsMatchingRule: integerOrderingMatch'. "
|
||||
- "Incorrectly configured system indexes can lead to poor search performance, replication issues, and other operational problems. "
|
||||
- "To fix this, add the matching rule and reindex: "
|
||||
- "dsconf <instance> backend index set --add-mr integerOrderingMatch --attr %s %s && "
|
||||
- "dsconf <instance> backend index reindex --attr %s %s. "
|
||||
- "WARNING: Reindexing can be resource-intensive and may impact server performance on a live system. "
|
||||
- "Consider scheduling reindexing during maintenance windows or periods of low activity.\n",
|
||||
- attr_name, be_name, attr_name, be_name, attr_name, be_name);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- slapi_ch_free_string(&idx_dn);
|
||||
- slapi_free_search_results_internal(idx_pb);
|
||||
- slapi_pblock_destroy(idx_pb);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- slapi_free_search_results_internal(pb);
|
||||
- slapi_pblock_destroy(pb);
|
||||
-
|
||||
- return uresult;
|
||||
-}
|
||||
|
||||
upgrade_status
|
||||
upgrade_server(void)
|
||||
@@ -637,10 +536,6 @@ upgrade_server(void)
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
|
||||
- if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
- return UPGRADE_FAILURE;
|
||||
- }
|
||||
-
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
|
||||
index 0175dfa7c..f78258f6a 100644
|
||||
--- a/rpm/389-ds-base.spec.in
|
||||
+++ b/rpm/389-ds-base.spec.in
|
||||
@@ -667,9 +667,6 @@ for dir in "$instbase"/slapd-* ; do
|
||||
else
|
||||
echo "instance $inst is not running" >> "$output" 2>&1 || :
|
||||
fi
|
||||
- # Run index-check on all instances (running or not)
|
||||
- # This fixes index ordering mismatches from older versions
|
||||
- dsctl "$inst_name" index-check --fix >> "$output2" 2>&1 || :
|
||||
ninst=$((ninst + 1))
|
||||
done
|
||||
|
||||
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
||||
index 1d9be4683..29a1796ac 100644
|
||||
--- a/src/lib389/lib389/backend.py
|
||||
+++ b/src/lib389/lib389/backend.py
|
||||
@@ -617,9 +617,10 @@ class Backend(DSLdapObject):
|
||||
# Default system indexes taken from ldap/servers/slapd/back-ldbm/instance.c
|
||||
# Note: entryrdn and ancestorid are internal system indexes that are not
|
||||
# exposed in cn=config - they are managed internally by the server.
|
||||
- # Only parentid has a DSE config entry (for the integerOrderingMatch rule).
|
||||
+ # parentid works correctly with both lexicographic and integer ordering,
|
||||
+ # so integerOrderingMatch is not required.
|
||||
expected_system_indexes = {
|
||||
- 'parentid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch'},
|
||||
+ 'parentid': {'types': ['eq'], 'matching_rule': None},
|
||||
'objectClass': {'types': ['eq'], 'matching_rule': None},
|
||||
'aci': {'types': ['pres'], 'matching_rule': None},
|
||||
'nscpEntryDN': {'types': ['eq'], 'matching_rule': None},
|
||||
diff --git a/src/lib389/lib389/cli_ctl/dbtasks.py b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
index cd96cdaf7..b02de203f 100644
|
||||
--- a/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
+++ b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
@@ -10,6 +10,7 @@
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
+import signal
|
||||
import subprocess
|
||||
from enum import Enum
|
||||
from lib389._constants import TaskWarning
|
||||
@@ -263,45 +264,53 @@ def _check_disk_ordering(db_dir, backend, index_name, dbscan_path, is_mdb, log):
|
||||
if not index_file:
|
||||
return IndexOrdering.UNKNOWN
|
||||
|
||||
+ # Only read the first 100 lines from dbscan to avoid scanning the
|
||||
+ # entire index (which can take hours on large databases).
|
||||
try:
|
||||
- result = subprocess.run(
|
||||
+ proc = subprocess.Popen(
|
||||
[dbscan_path, "-f", index_file],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
- timeout=60,
|
||||
)
|
||||
|
||||
- if result.returncode != 0:
|
||||
- log.warning(" dbscan returned non-zero exit code for %s", index_file)
|
||||
- return IndexOrdering.UNKNOWN
|
||||
-
|
||||
- # Parse keys from dbscan output
|
||||
keys = []
|
||||
- for line in result.stdout.split("\n"):
|
||||
+ line_count = 0
|
||||
+ assert proc.stdout is not None
|
||||
+ for line in proc.stdout:
|
||||
+ line_count += 1
|
||||
+ if line_count > 100:
|
||||
+ break
|
||||
line = line.strip()
|
||||
if line.startswith("="):
|
||||
match = re.match(r"^=(\d+)", line)
|
||||
if match:
|
||||
keys.append(int(match.group(1)))
|
||||
|
||||
+ proc.terminate()
|
||||
+ try:
|
||||
+ proc.wait(timeout=5)
|
||||
+ except subprocess.TimeoutExpired:
|
||||
+ proc.kill()
|
||||
+ proc.wait()
|
||||
+
|
||||
+ if proc.returncode not in (0, -signal.SIGTERM):
|
||||
+ log.warning(" dbscan returned non-zero exit code for %s", index_file)
|
||||
+ return IndexOrdering.UNKNOWN
|
||||
+
|
||||
if len(keys) < 2:
|
||||
return IndexOrdering.UNKNOWN
|
||||
|
||||
# Check if keys are in integer order by looking for decreasing numeric values
|
||||
# (which would indicate lexicographic ordering, e.g., "3" < "30" < "4")
|
||||
prev_id = keys[0]
|
||||
- for i in range(1, min(len(keys), 100)):
|
||||
- current_id = keys[i]
|
||||
+ for current_id in keys[1:]:
|
||||
if prev_id > current_id:
|
||||
return IndexOrdering.LEXICOGRAPHIC
|
||||
prev_id = current_id
|
||||
|
||||
return IndexOrdering.INTEGER
|
||||
|
||||
- except subprocess.TimeoutExpired:
|
||||
- log.warning(" dbscan timed out for %s", index_file)
|
||||
- return IndexOrdering.UNKNOWN
|
||||
except OSError as e:
|
||||
log.warning(" Error running dbscan: %s", e)
|
||||
return IndexOrdering.UNKNOWN
|
||||
@@ -375,8 +384,7 @@ def dbtasks_index_check(inst, log, args):
|
||||
|
||||
# Track all issues found
|
||||
all_ok = True
|
||||
- mismatches = [] # (backend, index_name) tuples needing reindex
|
||||
- missing_matching_rules = [] # (backend, index_name) tuples missing integerOrderingMatch
|
||||
+ config_fixes = [] # (backend, index_name, action) tuples: action is "add_mr" or "remove_mr"
|
||||
scan_limits_to_remove = [] # (backend, index_name) tuples with nsIndexIDListScanLimit
|
||||
ancestorid_configs_to_remove = [] # backend names with ancestorid config entries
|
||||
remove_ancestorid_from_defaults = False # Flag to remove from cn=default indexes
|
||||
@@ -409,13 +417,6 @@ def dbtasks_index_check(inst, log, args):
|
||||
|
||||
if disk_ordering == IndexOrdering.UNKNOWN:
|
||||
log.info(" %s - could not determine disk ordering, skipping", index_name)
|
||||
- # For parentid, still check if matching rule is missing
|
||||
- if index_name == "parentid":
|
||||
- config_has_int_order = _has_integer_ordering_match(dse_ldif, backend, index_name)
|
||||
- if not config_has_int_order:
|
||||
- log.warning(" %s - missing integerOrderingMatch in config", index_name)
|
||||
- missing_matching_rules.append((backend, index_name))
|
||||
- all_ok = False
|
||||
continue
|
||||
|
||||
config_has_int_order = _has_integer_ordering_match(dse_ldif, backend, index_name)
|
||||
@@ -423,18 +424,15 @@ def dbtasks_index_check(inst, log, args):
|
||||
log.info(" %s - config: %s, disk: %s",
|
||||
index_name, config_desc, disk_ordering.value)
|
||||
|
||||
- # For parentid, the desired state is always integer ordering
|
||||
+ # Both orderings are valid for parentid, but config must match disk.
|
||||
if index_name == "parentid":
|
||||
- if not config_has_int_order:
|
||||
- log.warning(" %s - missing integerOrderingMatch in config", index_name)
|
||||
- if (backend, index_name) not in missing_matching_rules:
|
||||
- missing_matching_rules.append((backend, index_name))
|
||||
+ if config_has_int_order and disk_ordering == IndexOrdering.LEXICOGRAPHIC:
|
||||
+ log.warning(" %s - MISMATCH: config has integerOrderingMatch but disk is lexicographic", index_name)
|
||||
+ config_fixes.append((backend, index_name, "remove_mr"))
|
||||
all_ok = False
|
||||
-
|
||||
- if disk_ordering == IndexOrdering.LEXICOGRAPHIC:
|
||||
- log.warning(" %s - disk ordering is lexicographic, needs reindex", index_name)
|
||||
- if (backend, index_name) not in mismatches:
|
||||
- mismatches.append((backend, index_name))
|
||||
+ elif not config_has_int_order and disk_ordering == IndexOrdering.INTEGER:
|
||||
+ log.warning(" %s - MISMATCH: config is lexicographic but disk has integer ordering", index_name)
|
||||
+ config_fixes.append((backend, index_name, "add_mr"))
|
||||
all_ok = False
|
||||
|
||||
# Handle issues
|
||||
@@ -480,26 +478,27 @@ def dbtasks_index_check(inst, log, args):
|
||||
log.error(" Failed to remove ancestorid config from backend %s: %s", backend, e)
|
||||
return False
|
||||
|
||||
- # Add missing matching rules to dse.ldif
|
||||
- for backend, index_name in missing_matching_rules:
|
||||
+ # Fix config-vs-disk ordering mismatches by adjusting config to match disk
|
||||
+ for backend, index_name, action in config_fixes:
|
||||
index_dn = "cn={},cn=index,cn={},cn=ldbm database,cn=plugins,cn=config".format(
|
||||
index_name, backend
|
||||
)
|
||||
- log.info(" Adding integerOrderingMatch to %s in backend %s...", index_name, backend)
|
||||
- try:
|
||||
- dse_ldif.add(index_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
- log.info(" Updated dse.ldif with integerOrderingMatch for %s", index_name)
|
||||
- except Exception as e:
|
||||
- log.error(" Failed to update dse.ldif for %s: %s", index_name, e)
|
||||
- return False
|
||||
-
|
||||
- # Reindex indexes with disk ordering issues
|
||||
- for backend, index_name in mismatches:
|
||||
- log.info(" Reindexing %s in backend %s...", index_name, backend)
|
||||
- if not inst.db2index(bename=backend, attrs=[index_name]):
|
||||
- log.error(" Failed to reindex %s", index_name)
|
||||
- return False
|
||||
- log.info(" Reindex of %s completed successfully", index_name)
|
||||
+ if action == "add_mr":
|
||||
+ log.info(" Adding integerOrderingMatch to %s in backend %s...", index_name, backend)
|
||||
+ try:
|
||||
+ dse_ldif.add(index_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
+ log.info(" Updated dse.ldif with integerOrderingMatch for %s", index_name)
|
||||
+ except Exception as e:
|
||||
+ log.error(" Failed to update dse.ldif for %s: %s", index_name, e)
|
||||
+ return False
|
||||
+ elif action == "remove_mr":
|
||||
+ log.info(" Removing integerOrderingMatch from %s in backend %s...", index_name, backend)
|
||||
+ try:
|
||||
+ dse_ldif.delete(index_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
+ log.info(" Removed integerOrderingMatch from %s", index_name)
|
||||
+ except Exception as e:
|
||||
+ log.error(" Failed to remove integerOrderingMatch from %s: %s", index_name, e)
|
||||
+ return False
|
||||
|
||||
log.info("All issues fixed")
|
||||
return True
|
||||
@@ -563,5 +562,5 @@ def create_parser(subcommands):
|
||||
index_check_parser.add_argument('backend', nargs='?', default=None,
|
||||
help="Backend to check. If not specified, all backends are checked.")
|
||||
index_check_parser.add_argument('--fix', action='store_true', default=False,
|
||||
- help="Fix mismatches by reindexing affected indexes")
|
||||
+ help="Fix mismatches by adjusting config to match on-disk data")
|
||||
index_check_parser.set_defaults(func=dbtasks_index_check)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,202 +0,0 @@
|
||||
From 31bef3852f7064c73f66974d7c41262d2a724eab Mon Sep 17 00:00:00 2001
|
||||
From: Alex Kulberg <vectinx@yandex.ru>
|
||||
Date: Tue, 9 Dec 2025 17:11:56 +0300
|
||||
Subject: [PATCH] Issue 7053 - Remove memberof_del_dn_from_groups from MemberOf
|
||||
plugin (#7064)
|
||||
|
||||
Bug Description:
|
||||
|
||||
The member plugin creates redundant changes to the member attribute
|
||||
in groups when deleting a user, although the referential integrity
|
||||
of the member attribute should be controlled by the Referential Integrity plugin.
|
||||
Furthermore, memberof doesn't take replication of operations into account
|
||||
and performs the change on every server instance in the topology.
|
||||
|
||||
Fix Description:
|
||||
|
||||
Remove the `memberof_del_dn_from_groups` function from the MemberOf plugin,
|
||||
completely transferring responsibility for deleting users from groups
|
||||
to the Referential Integrity plugin.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7053
|
||||
|
||||
Reviewed by: @tbordaz
|
||||
---
|
||||
.../memberof_include_scopes_test.py | 13 ++-
|
||||
ldap/servers/plugins/memberof/memberof.c | 79 -------------------
|
||||
2 files changed, 9 insertions(+), 83 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py b/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
|
||||
index e1d3b0a96..347eb880f 100644
|
||||
--- a/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
|
||||
@@ -13,7 +13,7 @@ import time
|
||||
from lib389.utils import ensure_str
|
||||
from lib389.topologies import topology_st as topo
|
||||
from lib389._constants import *
|
||||
-from lib389.plugins import MemberOfPlugin
|
||||
+from lib389.plugins import MemberOfPlugin, ReferentialIntegrityPlugin
|
||||
from lib389.idm.user import UserAccount, UserAccounts
|
||||
from lib389.idm.group import Group, Groups
|
||||
from lib389.idm.nscontainer import nsContainers
|
||||
@@ -77,6 +77,13 @@ def test_multiple_scopes(topo):
|
||||
|
||||
inst = topo.standalone
|
||||
|
||||
+ EXCLUDED_SUBTREE = 'cn=exclude,%s' % SUFFIX
|
||||
+ # enable Referential Integrity plugin
|
||||
+ # to correctly process the 'member' attribute
|
||||
+ refint = ReferentialIntegrityPlugin(inst)
|
||||
+ refint.add_excludescope(EXCLUDED_SUBTREE)
|
||||
+ refint.enable()
|
||||
+
|
||||
# configure plugin
|
||||
memberof = MemberOfPlugin(inst)
|
||||
memberof.enable()
|
||||
@@ -106,7 +113,6 @@ def test_multiple_scopes(topo):
|
||||
check_membership(inst, f'uid=test_m3,{SUBTREE_3}', f'cn=g3,{SUBTREE_3}', False)
|
||||
|
||||
# Set exclude scope
|
||||
- EXCLUDED_SUBTREE = 'cn=exclude,%s' % SUFFIX
|
||||
EXCLUDED_USER = f"uid=test_m1,{EXCLUDED_SUBTREE}"
|
||||
INCLUDED_USER = f"uid=test_m1,{SUBTREE_1}"
|
||||
GROUP_DN = f'cn=g1,{SUBTREE_1}'
|
||||
@@ -122,9 +128,8 @@ def test_multiple_scopes(topo):
|
||||
# Check memberOf and group are cleaned up
|
||||
check_membership(inst, EXCLUDED_USER, GROUP_DN, False)
|
||||
group = Group(topo.standalone, dn=GROUP_DN)
|
||||
- assert not group.present("member", EXCLUDED_USER)
|
||||
assert not group.present("member", INCLUDED_USER)
|
||||
-
|
||||
+ assert not group.present("member", EXCLUDED_USER)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Run isolated
|
||||
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
|
||||
index c4b5afee7..5de2e9e72 100644
|
||||
--- a/ldap/servers/plugins/memberof/memberof.c
|
||||
+++ b/ldap/servers/plugins/memberof/memberof.c
|
||||
@@ -151,7 +151,6 @@ static void memberof_set_plugin_id(void *plugin_id);
|
||||
static int memberof_compare(MemberOfConfig *config, const void *a, const void *b);
|
||||
static int memberof_qsort_compare(const void *a, const void *b);
|
||||
static void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr);
|
||||
-static int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_Entry *e, Slapi_DN *sdn);
|
||||
static int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_DN *sdn, MemberOfConfig *config, char **types, plugin_search_entry_callback callback, void *callback_data, int *cached, PRBool use_grp_cache);
|
||||
static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn, Slapi_Value *memberdn);
|
||||
static int memberof_is_grouping_attr(char *type, MemberOfConfig *config);
|
||||
@@ -540,21 +539,6 @@ deferred_modrdn_func(MemberofDeferredModrdnTask *task)
|
||||
* attributes to refer to the new name. */
|
||||
if (ret == LDAP_SUCCESS && pre_sdn && post_sdn) {
|
||||
if (!memberof_entry_in_scope(&configCopy, &post_entry_info)) {
|
||||
- /*
|
||||
- * After modrdn the group contains both the pre and post DN's as
|
||||
- * members, so we need to cleanup both in this case.
|
||||
- */
|
||||
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, pre_e, pre_sdn))) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
|
||||
- "deferred_modrdn_func - Delete dn failed for preop entry(%s), error (%d)\n",
|
||||
- slapi_sdn_get_dn(pre_sdn), ret);
|
||||
- }
|
||||
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, post_e, post_sdn))) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
|
||||
- "deferred_modrdn_func - Delete dn failed for postop entry(%s), error (%d)\n",
|
||||
- slapi_sdn_get_dn(post_sdn), ret);
|
||||
- }
|
||||
-
|
||||
if (ret == LDAP_SUCCESS && pre_e && configCopy.group_filter &&
|
||||
0 == slapi_filter_test_simple(pre_e, configCopy.group_filter))
|
||||
{
|
||||
@@ -638,16 +622,6 @@ deferred_del_func(MemberofDeferredDelTask *task)
|
||||
free_configCopy = PR_TRUE;
|
||||
memberof_unlock_config();
|
||||
|
||||
- /* remove this DN from the
|
||||
- * membership lists of groups
|
||||
- */
|
||||
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, e, sdn))) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
|
||||
- "deferred_del_func - Error deleting dn (%s) from group. Error (%d)\n",
|
||||
- slapi_sdn_get_dn(sdn), ret);
|
||||
- goto bail;
|
||||
- }
|
||||
-
|
||||
/* is the entry of interest as a group? */
|
||||
if (e && configCopy.group_filter && 0 == slapi_filter_test_simple(e, configCopy.group_filter)) {
|
||||
Slapi_Attr *attr = 0;
|
||||
@@ -1456,16 +1430,6 @@ memberof_postop_del(Slapi_PBlock *pb)
|
||||
memberof_copy_config(&configCopy, memberof_get_config());
|
||||
memberof_unlock_config();
|
||||
|
||||
- /* remove this DN from the
|
||||
- * membership lists of groups
|
||||
- */
|
||||
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, e, sdn))) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
|
||||
- "memberof_postop_del - Error deleting dn (%s) from group. Error (%d)\n",
|
||||
- slapi_sdn_get_dn(sdn), ret);
|
||||
- goto bail;
|
||||
- }
|
||||
-
|
||||
/* is the entry of interest as a group? */
|
||||
if (e && configCopy.group_filter && 0 == slapi_filter_test_simple(e, configCopy.group_filter)) {
|
||||
Slapi_Attr *attr = 0;
|
||||
@@ -1496,34 +1460,6 @@ done:
|
||||
}
|
||||
|
||||
|
||||
-/* Deletes a member dn from all groups that refer to it. */
|
||||
-static int
|
||||
-memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_Entry *e, Slapi_DN *sdn)
|
||||
-{
|
||||
- char *groupattrs[2] = {0, 0};
|
||||
- int rc = LDAP_SUCCESS;
|
||||
- int cached = 0;
|
||||
-
|
||||
- /* Loop through each grouping attribute to find groups that have
|
||||
- * dn as a member. For any matches, delete the dn value from the
|
||||
- * same grouping attribute. */
|
||||
- for (size_t i = 0; config->groupattrs && config->groupattrs[i] && rc == LDAP_SUCCESS; i++) {
|
||||
- memberof_del_dn_data data = {(char *)slapi_sdn_get_dn(sdn),
|
||||
- config->groupattrs[i], config};
|
||||
-
|
||||
- groupattrs[0] = config->groupattrs[i];
|
||||
-
|
||||
- slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM,
|
||||
- "memberof_del_dn_from_groups: Ancestors of %s attr: %s\n",
|
||||
- slapi_sdn_get_dn(sdn),
|
||||
- groupattrs[0]);
|
||||
- rc = memberof_call_foreach_dn(pb, e, sdn, config, groupattrs,
|
||||
- memberof_del_dn_type_callback, &data, &cached, PR_FALSE);
|
||||
- }
|
||||
-
|
||||
- return rc;
|
||||
-}
|
||||
-
|
||||
int
|
||||
memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
|
||||
{
|
||||
@@ -1904,21 +1840,6 @@ memberof_postop_modrdn(Slapi_PBlock *pb)
|
||||
* attributes to refer to the new name. */
|
||||
if (ret == LDAP_SUCCESS && pre_sdn && post_sdn) {
|
||||
if (!memberof_entry_in_scope(&configCopy, &post_entry_info)) {
|
||||
- /*
|
||||
- * After modrdn the group contains both the pre and post DN's as
|
||||
- * members, so we need to cleanup both in this case.
|
||||
- */
|
||||
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, pre_e, pre_sdn))) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
|
||||
- "memberof_postop_modrdn - Delete dn failed for preop entry(%s), error (%d)\n",
|
||||
- slapi_sdn_get_dn(pre_sdn), ret);
|
||||
- }
|
||||
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, post_e, post_sdn))) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
|
||||
- "memberof_postop_modrdn - Delete dn failed for postop entry(%s), error (%d)\n",
|
||||
- slapi_sdn_get_dn(post_sdn), ret);
|
||||
- }
|
||||
-
|
||||
if (ret == LDAP_SUCCESS && pre_e && configCopy.group_filter &&
|
||||
0 == slapi_filter_test_simple(pre_e, configCopy.group_filter)) {
|
||||
/* is the entry of interest as a group? */
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,952 +0,0 @@
|
||||
From 0c5f7b9909ababc53e73e9469da6e30b81637cd0 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 23 Feb 2026 09:49:52 +0100
|
||||
Subject: [PATCH] Issue 5853 - Update concread to 0.5.10
|
||||
|
||||
Description:
|
||||
Update concread to 0.5.10 and update Cargo.lock
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/5853
|
||||
|
||||
Reviewed by: @droideck (Thanks!)
|
||||
---
|
||||
src/Cargo.lock | 515 +++++++++++++++++++++++----------------
|
||||
src/librslapd/Cargo.toml | 2 +-
|
||||
2 files changed, 312 insertions(+), 205 deletions(-)
|
||||
|
||||
diff --git a/src/Cargo.lock b/src/Cargo.lock
|
||||
index 87aeee852..425371478 100644
|
||||
--- a/src/Cargo.lock
|
||||
+++ b/src/Cargo.lock
|
||||
@@ -2,27 +2,18 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
-[[package]]
|
||||
-name = "addr2line"
|
||||
-version = "0.24.2"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
-dependencies = [
|
||||
- "gimli",
|
||||
-]
|
||||
-
|
||||
-[[package]]
|
||||
-name = "adler2"
|
||||
-version = "2.0.1"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
-
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
+[[package]]
|
||||
+name = "anyhow"
|
||||
+version = "1.0.102"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
+
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -40,21 +31,6 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
-[[package]]
|
||||
-name = "backtrace"
|
||||
-version = "0.3.75"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
-dependencies = [
|
||||
- "addr2line",
|
||||
- "cfg-if",
|
||||
- "libc",
|
||||
- "miniz_oxide",
|
||||
- "object",
|
||||
- "rustc-demangle",
|
||||
- "windows-targets",
|
||||
-]
|
||||
-
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
@@ -69,9 +45,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
-version = "2.9.1"
|
||||
+version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -86,8 +62,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49"
|
||||
dependencies = [
|
||||
"clap",
|
||||
- "heck",
|
||||
- "indexmap",
|
||||
+ "heck 0.4.1",
|
||||
+ "indexmap 1.9.3",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -100,10 +76,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
-version = "1.2.27"
|
||||
+version = "1.2.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
+checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||
dependencies = [
|
||||
+ "find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
@@ -111,9 +88,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
-version = "1.0.1"
|
||||
+version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
@@ -124,7 +101,7 @@ dependencies = [
|
||||
"atty",
|
||||
"bitflags 1.3.2",
|
||||
"clap_lex",
|
||||
- "indexmap",
|
||||
+ "indexmap 1.9.3",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
@@ -141,14 +118,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "concread"
|
||||
-version = "0.5.6"
|
||||
+version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "7b639eeaa550eba0c8be45b292d5e272e6d29bfdffb4df6925d651ed9ed10fd6"
|
||||
+checksum = "6588e9e68e11207fb9a5aabd88765187969e6bcba98763c40bcad87b2a73e9f5"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
- "foldhash",
|
||||
+ "foldhash 0.2.0",
|
||||
"lru",
|
||||
"smallvec",
|
||||
"sptr",
|
||||
@@ -210,9 +187,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
-version = "0.3.12"
|
||||
+version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
@@ -232,17 +209,29 @@ checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
- "getrandom 0.2.16",
|
||||
+ "getrandom 0.2.17",
|
||||
"openssl",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "find-msvc-tools"
|
||||
+version = "0.1.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
+
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
+[[package]]
|
||||
+name = "foldhash"
|
||||
+version = "0.2.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
+
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
@@ -260,32 +249,39 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
-version = "0.2.16"
|
||||
+version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
- "wasi 0.11.1+wasi-snapshot-preview1",
|
||||
+ "wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
-version = "0.3.3"
|
||||
+version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
- "wasi 0.14.2+wasi-0.2.4",
|
||||
+ "wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
-name = "gimli"
|
||||
-version = "0.31.1"
|
||||
+name = "getrandom"
|
||||
+version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
+checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
+dependencies = [
|
||||
+ "cfg-if",
|
||||
+ "libc",
|
||||
+ "r-efi",
|
||||
+ "wasip2",
|
||||
+ "wasip3",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
@@ -295,13 +291,22 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
-version = "0.15.4"
|
||||
+version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
+dependencies = [
|
||||
+ "foldhash 0.1.5",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "hashbrown"
|
||||
+version = "0.16.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
- "foldhash",
|
||||
+ "foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -310,6 +315,12 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
+[[package]]
|
||||
+name = "heck"
|
||||
+version = "0.5.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
+
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@@ -319,6 +330,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "id-arena"
|
||||
+version = "2.3.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
+
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -329,27 +346,45 @@ dependencies = [
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "indexmap"
|
||||
+version = "2.13.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
+dependencies = [
|
||||
+ "equivalent",
|
||||
+ "hashbrown 0.16.1",
|
||||
+ "serde",
|
||||
+ "serde_core",
|
||||
+]
|
||||
+
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
-version = "1.0.15"
|
||||
+version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
-version = "0.1.33"
|
||||
+version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
- "getrandom 0.3.3",
|
||||
+ "getrandom 0.3.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "leb128fmt"
|
||||
+version = "0.1.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
+
|
||||
[[package]]
|
||||
name = "libc"
|
||||
-version = "0.2.174"
|
||||
+version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
+checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "librnsslapd"
|
||||
@@ -372,48 +407,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
-version = "0.9.4"
|
||||
+version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
-version = "0.4.27"
|
||||
+version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
-version = "0.13.0"
|
||||
+version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465"
|
||||
+checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||
dependencies = [
|
||||
- "hashbrown 0.15.4",
|
||||
+ "hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
-version = "2.7.5"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "miniz_oxide"
|
||||
-version = "0.8.9"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
-dependencies = [
|
||||
- "adler2",
|
||||
-]
|
||||
-
|
||||
-[[package]]
|
||||
-name = "object"
|
||||
-version = "0.36.7"
|
||||
+version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
-dependencies = [
|
||||
- "memchr",
|
||||
-]
|
||||
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@@ -423,11 +440,11 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
-version = "0.10.73"
|
||||
+version = "0.10.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||
+checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
|
||||
dependencies = [
|
||||
- "bitflags 2.9.1",
|
||||
+ "bitflags 2.11.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
@@ -444,14 +461,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
-version = "0.9.109"
|
||||
+version = "0.9.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
+checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -496,6 +513,16 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
+[[package]]
|
||||
+name = "prettyplease"
|
||||
+version = "0.2.37"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+ "syn 2.0.117",
|
||||
+]
|
||||
+
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
@@ -504,9 +531,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
-version = "1.0.95"
|
||||
+version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -526,9 +553,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
-version = "1.0.40"
|
||||
+version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -539,19 +566,13 @@ version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
-[[package]]
|
||||
-name = "rustc-demangle"
|
||||
-version = "0.1.25"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
-
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
-version = "1.0.7"
|
||||
+version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
+checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
- "bitflags 2.9.1",
|
||||
+ "bitflags 2.11.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -559,41 +580,52 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
-name = "ryu"
|
||||
-version = "1.0.20"
|
||||
+name = "semver"
|
||||
+version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
-version = "1.0.219"
|
||||
+version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
+dependencies = [
|
||||
+ "serde_core",
|
||||
+ "serde_derive",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde_core"
|
||||
+version = "1.0.228"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
-version = "1.0.219"
|
||||
+version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
-version = "1.0.140"
|
||||
+version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
- "ryu",
|
||||
"serde",
|
||||
+ "serde_core",
|
||||
+ "zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -649,9 +681,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
-version = "2.0.103"
|
||||
+version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -660,12 +692,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
-version = "3.20.0"
|
||||
+version = "3.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
+checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
- "getrandom 0.3.3",
|
||||
+ "getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
@@ -688,11 +720,10 @@ checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
-version = "1.45.1"
|
||||
+version = "1.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
+checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
||||
dependencies = [
|
||||
- "backtrace",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@@ -707,9 +738,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
-version = "0.1.41"
|
||||
+version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
@@ -718,29 +749,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
-version = "0.1.30"
|
||||
+version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
-version = "0.1.34"
|
||||
+version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
-version = "1.0.18"
|
||||
+version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-xid"
|
||||
+version = "0.2.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
@@ -748,7 +785,7 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
- "getrandom 0.2.16",
|
||||
+ "getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -764,12 +801,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
-name = "wasi"
|
||||
-version = "0.14.2+wasi-0.2.4"
|
||||
+name = "wasip2"
|
||||
+version = "1.0.2+wasi-0.2.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
+dependencies = [
|
||||
+ "wit-bindgen",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasip3"
|
||||
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
+dependencies = [
|
||||
+ "wit-bindgen",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasm-encoder"
|
||||
+version = "0.244.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
+dependencies = [
|
||||
+ "leb128fmt",
|
||||
+ "wasmparser",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasm-metadata"
|
||||
+version = "0.244.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "wasm-encoder",
|
||||
+ "wasmparser",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasmparser"
|
||||
+version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
- "wit-bindgen-rt",
|
||||
+ "bitflags 2.11.0",
|
||||
+ "hashbrown 0.15.5",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -790,9 +870,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
-version = "0.1.9"
|
||||
+version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
@@ -804,103 +884,130 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
-name = "windows-sys"
|
||||
-version = "0.59.0"
|
||||
+name = "windows-link"
|
||||
+version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
-dependencies = [
|
||||
- "windows-targets",
|
||||
-]
|
||||
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
-name = "windows-targets"
|
||||
-version = "0.52.6"
|
||||
+name = "windows-sys"
|
||||
+version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
- "windows_aarch64_gnullvm",
|
||||
- "windows_aarch64_msvc",
|
||||
- "windows_i686_gnu",
|
||||
- "windows_i686_gnullvm",
|
||||
- "windows_i686_msvc",
|
||||
- "windows_x86_64_gnu",
|
||||
- "windows_x86_64_gnullvm",
|
||||
- "windows_x86_64_msvc",
|
||||
+ "windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_aarch64_gnullvm"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "windows_aarch64_msvc"
|
||||
-version = "0.52.6"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "windows_i686_gnu"
|
||||
-version = "0.52.6"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "windows_i686_gnullvm"
|
||||
-version = "0.52.6"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
+dependencies = [
|
||||
+ "wit-bindgen-rust-macro",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_i686_msvc"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen-core"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "heck 0.5.0",
|
||||
+ "wit-parser",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_x86_64_gnu"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen-rust"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "heck 0.5.0",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "prettyplease",
|
||||
+ "syn 2.0.117",
|
||||
+ "wasm-metadata",
|
||||
+ "wit-bindgen-core",
|
||||
+ "wit-component",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_x86_64_gnullvm"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen-rust-macro"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "prettyplease",
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "syn 2.0.117",
|
||||
+ "wit-bindgen-core",
|
||||
+ "wit-bindgen-rust",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_x86_64_msvc"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-component"
|
||||
+version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "bitflags 2.11.0",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "log",
|
||||
+ "serde",
|
||||
+ "serde_derive",
|
||||
+ "serde_json",
|
||||
+ "wasm-encoder",
|
||||
+ "wasm-metadata",
|
||||
+ "wasmparser",
|
||||
+ "wit-parser",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "wit-bindgen-rt"
|
||||
-version = "0.39.0"
|
||||
+name = "wit-parser"
|
||||
+version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
- "bitflags 2.9.1",
|
||||
+ "anyhow",
|
||||
+ "id-arena",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "log",
|
||||
+ "semver",
|
||||
+ "serde",
|
||||
+ "serde_derive",
|
||||
+ "serde_json",
|
||||
+ "unicode-xid",
|
||||
+ "wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
-version = "1.8.1"
|
||||
+version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
-version = "1.4.2"
|
||||
+version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "zmij"
|
||||
+version = "1.0.21"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
diff --git a/src/librslapd/Cargo.toml b/src/librslapd/Cargo.toml
|
||||
index f38f93f4c..61100f381 100644
|
||||
--- a/src/librslapd/Cargo.toml
|
||||
+++ b/src/librslapd/Cargo.toml
|
||||
@@ -16,7 +16,7 @@ crate-type = ["staticlib", "lib"]
|
||||
[dependencies]
|
||||
slapd = { path = "../slapd" }
|
||||
libc = "0.2"
|
||||
-concread = "0.5.6"
|
||||
+concread = "0.5.10"
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.26"
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
From a3fd3897ede4166625194d48b010b6dbdcdf9f70 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 23 Feb 2026 12:37:26 -0500
|
||||
Subject: [PATCH] Issue 7271 - plugins that create threads need to update
|
||||
active thread count
|
||||
|
||||
Description:
|
||||
|
||||
Plugins that create threads need to up to the global active thread count.
|
||||
Otherwise when the server is being stopped the plugin's close function gets
|
||||
called while these threads are still running and still using the plugin
|
||||
configuration. This can lead to crashes.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: progier & tbordaz (Thanks!!)
|
||||
---
|
||||
ldap/servers/plugins/replication/repl5_protocol.c | 5 +++++
|
||||
ldap/servers/plugins/retrocl/retrocl_trim.c | 7 ++++++-
|
||||
2 files changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_protocol.c b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
index 5d4a0e455..bc9580319 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "repl5.h"
|
||||
#include "repl5_prot_private.h"
|
||||
+#include "slap.h"
|
||||
|
||||
#define PROTOCOL_5_INCREMENTAL 1
|
||||
#define PROTOCOL_5_TOTAL 2
|
||||
@@ -237,6 +238,8 @@ prot_thread_main(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
+ g_incr_active_threadcnt();
|
||||
+
|
||||
set_thread_private_agmtname(agmt_get_long_name(agmt));
|
||||
|
||||
done = 0;
|
||||
@@ -301,6 +304,8 @@ prot_thread_main(void *arg)
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ g_decr_active_threadcnt();
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/ldap/servers/plugins/retrocl/retrocl_trim.c b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
index 8fcd3d32b..8fdc75c62 100644
|
||||
--- a/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
+++ b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
@@ -243,6 +243,8 @@ trim_changelog(void)
|
||||
|
||||
now_interval = slapi_current_rel_time_t(); /* monotonic time for interval */
|
||||
|
||||
+ g_incr_active_threadcnt();
|
||||
+
|
||||
PR_Lock(ts.ts_s_trim_mutex);
|
||||
max_age = ts.ts_c_max_age;
|
||||
trim_interval = ts.ts_c_trim_interval;
|
||||
@@ -257,7 +259,7 @@ trim_changelog(void)
|
||||
*/
|
||||
done = 0;
|
||||
now_maxage = slapi_current_utc_time(); /* real time for trim candidates */
|
||||
- while (!done && retrocl_trimming == 1) {
|
||||
+ while (!done && retrocl_trimming == 1 && !slapi_is_shutting_down()) {
|
||||
int did_delete;
|
||||
|
||||
did_delete = 0;
|
||||
@@ -309,6 +311,9 @@ trim_changelog(void)
|
||||
"trim_changelog: removed %d change records\n",
|
||||
num_deleted);
|
||||
}
|
||||
+
|
||||
+ g_decr_active_threadcnt();
|
||||
+
|
||||
return rc;
|
||||
}
|
||||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
From f4817944e43e080b1ab15c3fa13359252429e500 Mon Sep 17 00:00:00 2001
|
||||
From: tbordaz <tbordaz@redhat.com>
|
||||
Date: Wed, 25 Feb 2026 14:06:42 +0100
|
||||
Subject: [PATCH] Security fix for CVE-2025-14905
|
||||
|
||||
Description:
|
||||
A vulnerability was found in the 389 Directory Server.
|
||||
The 389 Directory Server present a risk of heap buffer overflow that
|
||||
can be exploited to excute a Denial of Service and potential Remote
|
||||
Code Execution
|
||||
|
||||
References:
|
||||
- https://access.redhat.com/security/cve/CVE-2025-14905
|
||||
- https://bugzilla.redhat.com/show_bug.cgi?id=2423624
|
||||
---
|
||||
ldap/servers/slapd/schema.c | 47 ++++++++++++++++++++++++++++++-------
|
||||
1 file changed, 38 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
|
||||
index 9ef4ee4bf..7712a720d 100644
|
||||
--- a/ldap/servers/slapd/schema.c
|
||||
+++ b/ldap/servers/slapd/schema.c
|
||||
@@ -1410,6 +1410,7 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
|
||||
const char *attr_desc, *syntaxoid;
|
||||
char *outp, syntaxlengthbuf[128];
|
||||
int i;
|
||||
+ int nb_aliases = 0;
|
||||
|
||||
vals[0] = &val;
|
||||
|
||||
@@ -1435,6 +1436,7 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
|
||||
if (asip->asi_aliases != NULL) {
|
||||
for (i = 0; asip->asi_aliases[i] != NULL; ++i) {
|
||||
aliaslen += strlen(asip->asi_aliases[i]);
|
||||
+ nb_aliases++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1452,15 +1454,42 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
|
||||
* XXX: 256 is a magic number... it must be big enough to account for
|
||||
* all of the fixed sized items we output.
|
||||
*/
|
||||
- sizedbuffer_allocate(aew->psbAttrTypes, 256 + strlen(asip->asi_oid) +
|
||||
- strlen(asip->asi_name) +
|
||||
- aliaslen + strlen_null_ok(attr_desc) +
|
||||
- strlen(syntaxoid) +
|
||||
- strlen_null_ok(asip->asi_superior) +
|
||||
- strlen_null_ok(asip->asi_mr_equality) +
|
||||
- strlen_null_ok(asip->asi_mr_ordering) +
|
||||
- strlen_null_ok(asip->asi_mr_substring) +
|
||||
- strcat_extensions(NULL, asip->asi_extensions));
|
||||
+ {
|
||||
+ int asi_oid_strlen = strlen(asip->asi_oid) + 8; /* "( %s NAME " */
|
||||
+ int asi_name_strlen = strlen(asip->asi_name) + 6; /* "( '%s' ...)" */
|
||||
+ int asi_aliases_strlen = aliaslen + nb_aliases * 3; /* "'%s' " */
|
||||
+ int asi_desc_strlen = strlen_null_ok(attr_desc) + 7; /* "DESC '%s'" */
|
||||
+ int asi_syntaxoid_strlen = strlen("SYNTAX ") + strlen(syntaxoid) + strlen(syntaxlengthbuf);
|
||||
+ int asi_superior_strlen = strlen("SUP ") + strlen_null_ok(asip->asi_superior);
|
||||
+ int asi_mr_equality_strlen = strlen("EQUALITY ") + strlen_null_ok(asip->asi_mr_equality);
|
||||
+ int asi_mr_ordering_strlen = strlen("ORDERING ") + strlen_null_ok(asip->asi_mr_ordering);
|
||||
+ int asi_mr_substring_strlen = strlen("SUBSTR ") + strlen_null_ok(asip->asi_mr_substring);
|
||||
+ int asi_flags_strlen = strlen("SINGLE-VALUE ") +
|
||||
+ strlen(schema_obsolete_with_spaces) +
|
||||
+ strlen(schema_collective_with_spaces) +
|
||||
+ strlen(schema_nousermod_with_spaces) +
|
||||
+ strlen("USAGE distributedOperation ") +
|
||||
+ strlen("USAGE dSAOperation ") +
|
||||
+ strlen("USAGE directoryOperation ");
|
||||
+ int asi_extension_strlen = strcat_extensions(NULL, asip->asi_extensions);
|
||||
+
|
||||
+ if (aew->enquote_sup_oc) {
|
||||
+ /* it enquote the syntax oid */
|
||||
+ asi_syntaxoid_strlen += 2;
|
||||
+ }
|
||||
+
|
||||
+ sizedbuffer_allocate(aew->psbAttrTypes, 256 + asi_oid_strlen +
|
||||
+ asi_name_strlen +
|
||||
+ asi_aliases_strlen +
|
||||
+ asi_desc_strlen +
|
||||
+ asi_syntaxoid_strlen +
|
||||
+ asi_superior_strlen +
|
||||
+ asi_mr_equality_strlen +
|
||||
+ asi_mr_ordering_strlen +
|
||||
+ asi_mr_substring_strlen +
|
||||
+ asi_extension_strlen +
|
||||
+ asi_flags_strlen);
|
||||
+ }
|
||||
|
||||
/*
|
||||
* Overall strategy is to maintain a pointer to the next location in
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -1,262 +0,0 @@
|
||||
From c6bb96c95dcbff0052fc2decab7020b0f833f313 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 2 Mar 2026 14:31:29 -0500
|
||||
Subject: [PATCH] Issue 7271 - implement a pre-close plugin function
|
||||
|
||||
Description:
|
||||
|
||||
replication protocol could benefit from being notifioed the shutdown process
|
||||
has started before calling the "close" plugin function. This would allow the
|
||||
replication protocol to wake up and adjust its active thread count to prevent
|
||||
a hang during shutdown.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: progier, spichugi, and vashirov(Thanks!!!)
|
||||
---
|
||||
ldap/servers/plugins/replication/repl5_init.c | 19 ++++++++----
|
||||
ldap/servers/slapd/daemon.c | 6 +++-
|
||||
ldap/servers/slapd/pblock.c | 30 +++++++++++++------
|
||||
ldap/servers/slapd/plugin.c | 24 ++++++++++++++-
|
||||
ldap/servers/slapd/proto-slap.h | 3 +-
|
||||
ldap/servers/slapd/slap.h | 3 +-
|
||||
ldap/servers/slapd/slapi-plugin.h | 3 +-
|
||||
7 files changed, 69 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_init.c b/ldap/servers/plugins/replication/repl5_init.c
|
||||
index 5047fb8dc..395744db8 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_init.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_init.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2005 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -122,6 +122,8 @@ static PRUintn thread_private_agmtname; /* thread private index for logging*/
|
||||
static PRUintn thread_private_cache;
|
||||
static PRUintn thread_primary_csn;
|
||||
|
||||
+static int multisupplier_pre_stop(Slapi_PBlock *pb __attribute__((unused)));
|
||||
+
|
||||
char *
|
||||
get_thread_private_agmtname()
|
||||
{
|
||||
@@ -836,10 +838,6 @@ multisupplier_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
int rc = 0; /* OK */
|
||||
|
||||
if (!multisupplier_stopped_flag) {
|
||||
- if (!is_ldif_dump) {
|
||||
- /* Shut down replication agreements */
|
||||
- agmtlist_shutdown();
|
||||
- }
|
||||
/* if we are cleaning a ruv, stop */
|
||||
stop_ruv_cleaning();
|
||||
/* unregister backend state change notification */
|
||||
@@ -853,6 +851,16 @@ multisupplier_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
return rc;
|
||||
}
|
||||
|
||||
+static int
|
||||
+multisupplier_pre_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
+{
|
||||
+ if (!multisupplier_stopped_flag) {
|
||||
+ /* Shut down replication agreements which will stop all the
|
||||
+ * replication protocol threads */
|
||||
+ agmtlist_shutdown();
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
PRBool
|
||||
multisupplier_started()
|
||||
@@ -900,6 +908,7 @@ replication_multisupplier_plugin_init(Slapi_PBlock *pb)
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multisupplierdesc);
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)multisupplier_start);
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, (void *)multisupplier_stop);
|
||||
+ rc = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_CLOSE_FN, (void *)multisupplier_pre_stop);
|
||||
|
||||
/* Register the plugin interfaces we implement */
|
||||
/* preop acquires csn generator handle */
|
||||
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
|
||||
index d6911f03c..7fc1a16f1 100644
|
||||
--- a/ldap/servers/slapd/daemon.c
|
||||
+++ b/ldap/servers/slapd/daemon.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -1216,6 +1216,10 @@ slapd_daemon(daemon_ports_t *ports)
|
||||
task_cancel_all();
|
||||
}
|
||||
|
||||
+ /* Call plugin pre close functions */
|
||||
+ plugin_pre_closeall();
|
||||
+
|
||||
+ /* Now wait for active threads to terminate */
|
||||
threads = g_get_active_threadcnt();
|
||||
if (threads > 0) {
|
||||
slapi_log_err(SLAPI_LOG_INFO, "slapd_daemon",
|
||||
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
|
||||
index 76e26cb86..90114dee1 100644
|
||||
--- a/ldap/servers/slapd/pblock.c
|
||||
+++ b/ldap/servers/slapd/pblock.c
|
||||
@@ -1,12 +1,12 @@
|
||||
-/** BEGIN COPYRIGHT BLOCK
|
||||
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2005-2025 Red Hat, Inc.
|
||||
- * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
- * All rights reserved.
|
||||
- *
|
||||
- * License: GPL (version 3 or any later version).
|
||||
- * See LICENSE for details.
|
||||
- * END COPYRIGHT BLOCK **/
|
||||
+ /** BEGIN COPYRIGHT BLOCK
|
||||
+ * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
+ * All rights reserved.
|
||||
+ *
|
||||
+ * License: GPL (version 3 or any later version).
|
||||
+ * See LICENSE for details.
|
||||
+ * END COPYRIGHT BLOCK **/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
@@ -2524,6 +2524,14 @@ slapi_pblock_get(Slapi_PBlock *pblock, int arg, void *value)
|
||||
}
|
||||
break;
|
||||
|
||||
+ case SLAPI_PLUGIN_PRE_CLOSE_FN:
|
||||
+ if (pblock->pb_plugin != NULL) {
|
||||
+ (*(IFP *)value) = pblock->pb_plugin->plg_pre_close;
|
||||
+ } else {
|
||||
+ (*(IFP *)value) = NULL;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
default:
|
||||
slapi_log_err(SLAPI_LOG_ERR, "slapi_pblock_get", "Unknown parameter block argument %d\n", arg);
|
||||
PR_ASSERT(0);
|
||||
@@ -4209,6 +4217,10 @@ slapi_pblock_set(Slapi_PBlock *pblock, int arg, void *value)
|
||||
pblock->pb_misc->pb_aci_target_check = *((int *)value);
|
||||
break;
|
||||
|
||||
+ case SLAPI_PLUGIN_PRE_CLOSE_FN:
|
||||
+ pblock->pb_plugin->plg_pre_close = (IFP)value;
|
||||
+ break;
|
||||
+
|
||||
default:
|
||||
slapi_log_err(SLAPI_LOG_ERR, "slapi_pblock_set",
|
||||
"Unknown parameter block argument %d\n", arg);
|
||||
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
|
||||
index 52d25e19e..95b8de894 100644
|
||||
--- a/ldap/servers/slapd/plugin.c
|
||||
+++ b/ldap/servers/slapd/plugin.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -1847,6 +1847,28 @@ plugin_dependency_closeall(void)
|
||||
}
|
||||
}
|
||||
|
||||
+/* Call the pre close functions of all the plugins */
|
||||
+void
|
||||
+plugin_pre_closeall(void)
|
||||
+{
|
||||
+ Slapi_PBlock *pb = NULL;
|
||||
+ int plugins_pre_closed = 0;
|
||||
+ int index = 0;
|
||||
+
|
||||
+ while (plugins_pre_closed < global_plugins_started) {
|
||||
+ if (global_plugin_shutdown_order[index].name) {
|
||||
+ if (!global_plugin_shutdown_order[index].removed) {
|
||||
+ pb = slapi_pblock_new();
|
||||
+ plugin_call_one(global_plugin_shutdown_order[index].plugin,
|
||||
+ SLAPI_PLUGIN_PRE_CLOSE_FN, pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+ }
|
||||
+ plugins_pre_closed++;
|
||||
+ }
|
||||
+ index++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void
|
||||
plugin_freeall(void)
|
||||
{
|
||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
||||
index 742e3ee14..fd5bc6e71 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021-2025 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -982,6 +982,7 @@ int plugin_call_exop_plugins(Slapi_PBlock *pb, struct slapdplugin *p);
|
||||
Slapi_Backend *plugin_extended_op_getbackend(Slapi_PBlock *pb, struct slapdplugin *p);
|
||||
const char *plugin_extended_op_oid2string(const char *oid);
|
||||
void plugin_closeall(int close_backends, int close_globals);
|
||||
+void plugin_pre_closeall(void);
|
||||
void plugin_dependency_freeall(void);
|
||||
void plugin_startall(int argc, char **argv, char **plugin_list);
|
||||
void plugin_get_plugin_dependencies(char *plugin_name, char ***names);
|
||||
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
|
||||
index fee5a6ab5..64f9f465b 100644
|
||||
--- a/ldap/servers/slapd/slap.h
|
||||
+++ b/ldap/servers/slapd/slap.h
|
||||
@@ -1,7 +1,7 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
- * Copyright (C) 2009-2025 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributors:
|
||||
@@ -1035,6 +1035,7 @@ struct slapdplugin
|
||||
char *plg_libpath; /* library path for dll/so */
|
||||
char *plg_initfunc; /* init symbol */
|
||||
IFP plg_close; /* close function */
|
||||
+ IFP plg_pre_close; /* pre close function */
|
||||
Slapi_PluginDesc plg_desc; /* vendor's info */
|
||||
char *plg_name; /* used for plugin rdn in cn=config */
|
||||
struct slapdplugin *plg_next; /* for plugin lists */
|
||||
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
|
||||
index 5d9ef289d..4661d2cf0 100644
|
||||
--- a/ldap/servers/slapd/slapi-plugin.h
|
||||
+++ b/ldap/servers/slapd/slapi-plugin.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/* BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -7082,6 +7082,7 @@ typedef struct slapi_plugindesc
|
||||
|
||||
/* miscellaneous plugin functions */
|
||||
#define SLAPI_PLUGIN_CLOSE_FN 210
|
||||
+#define SLAPI_PLUGIN_PRE_CLOSE_FN 211
|
||||
#define SLAPI_PLUGIN_START_FN 212
|
||||
#define SLAPI_PLUGIN_CLEANUP_FN 232
|
||||
#define SLAPI_PLUGIN_POSTSTART_FN 233
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
From 21b9e00264437dff3b6b81355cd159485bfdaf37 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 4 Mar 2026 14:47:01 -0500
|
||||
Subject: [PATCH] Issue 7271 - Add new plugin pre-close function check to
|
||||
plugin_invoke_plugin_pb
|
||||
|
||||
Description:
|
||||
|
||||
In plugin_invoke_plugin_pb we were not checking for the new pre-close function
|
||||
which led to an error in the logs: pb_op is NULL. In a debug build this leads
|
||||
to an assertion error at shutdown.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: vashirov(Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/plugin.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
|
||||
index 95b8de894..c1b0571eb 100644
|
||||
--- a/ldap/servers/slapd/plugin.c
|
||||
+++ b/ldap/servers/slapd/plugin.c
|
||||
@@ -3647,6 +3647,7 @@ plugin_invoke_plugin_pb(struct slapdplugin *plugin, int operation, Slapi_PBlock
|
||||
if (operation == SLAPI_PLUGIN_START_FN ||
|
||||
operation == SLAPI_PLUGIN_POSTSTART_FN ||
|
||||
operation == SLAPI_PLUGIN_CLOSE_FN ||
|
||||
+ operation == SLAPI_PLUGIN_PRE_CLOSE_FN ||
|
||||
operation == SLAPI_PLUGIN_CLEANUP_FN ||
|
||||
operation == SLAPI_PLUGIN_BE_PRE_CLOSE_FN ||
|
||||
operation == SLAPI_PLUGIN_BE_POST_OPEN_FN ||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
155
389-ds-base.spec
155
389-ds-base.spec
@ -46,8 +46,8 @@ ExcludeArch: i686
|
||||
|
||||
Summary: 389 Directory Server (base)
|
||||
Name: 389-ds-base
|
||||
Version: 2.8.0
|
||||
Release: 6%{?dist}
|
||||
Version: 2.9.0
|
||||
Release: 1%{?dist}
|
||||
License: GPL-3.0-or-later WITH GPL-3.0-389-ds-base-exception AND (Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT) AND (Apache-2.0 OR LGPL-2.1-or-later OR MIT) AND (Apache-2.0 OR MIT) AND (MIT OR Apache-2.0) AND Unicode-3.0 AND (MIT OR Unlicense) AND Apache-2.0 AND MIT AND MPL-2.0 AND Zlib
|
||||
URL: https://www.port389.org
|
||||
Conflicts: selinux-policy-base < 3.9.8
|
||||
@ -59,13 +59,14 @@ Provides: ldif2ldbm >= 0
|
||||
|
||||
##### Bundled cargo crates list - START #####
|
||||
Provides: bundled(crate(allocator-api2)) = 0.2.21
|
||||
Provides: bundled(crate(anyhow)) = 1.0.102
|
||||
Provides: bundled(crate(atty)) = 0.2.14
|
||||
Provides: bundled(crate(autocfg)) = 1.5.0
|
||||
Provides: bundled(crate(autocfg)) = 1.5.1
|
||||
Provides: bundled(crate(base64)) = 0.13.1
|
||||
Provides: bundled(crate(bitflags)) = 2.10.0
|
||||
Provides: bundled(crate(bitflags)) = 2.12.1
|
||||
Provides: bundled(crate(byteorder)) = 1.5.0
|
||||
Provides: bundled(crate(cbindgen)) = 0.26.0
|
||||
Provides: bundled(crate(cc)) = 1.2.52
|
||||
Provides: bundled(crate(cc)) = 1.2.63
|
||||
Provides: bundled(crate(cfg-if)) = 1.0.4
|
||||
Provides: bundled(crate(clap)) = 3.2.25
|
||||
Provides: bundled(crate(clap_lex)) = 0.2.4
|
||||
@ -75,70 +76,84 @@ Provides: bundled(crate(crossbeam-queue)) = 0.3.12
|
||||
Provides: bundled(crate(crossbeam-utils)) = 0.8.21
|
||||
Provides: bundled(crate(equivalent)) = 1.0.2
|
||||
Provides: bundled(crate(errno)) = 0.3.14
|
||||
Provides: bundled(crate(fastrand)) = 2.3.0
|
||||
Provides: bundled(crate(fastrand)) = 2.4.1
|
||||
Provides: bundled(crate(fernet)) = 0.1.4
|
||||
Provides: bundled(crate(find-msvc-tools)) = 0.1.7
|
||||
Provides: bundled(crate(find-msvc-tools)) = 0.1.9
|
||||
Provides: bundled(crate(foldhash)) = 0.2.0
|
||||
Provides: bundled(crate(foreign-types)) = 0.3.2
|
||||
Provides: bundled(crate(foreign-types-shared)) = 0.1.1
|
||||
Provides: bundled(crate(getrandom)) = 0.3.4
|
||||
Provides: bundled(crate(hashbrown)) = 0.16.1
|
||||
Provides: bundled(crate(heck)) = 0.4.1
|
||||
Provides: bundled(crate(getrandom)) = 0.4.2
|
||||
Provides: bundled(crate(hashbrown)) = 0.17.1
|
||||
Provides: bundled(crate(heck)) = 0.5.0
|
||||
Provides: bundled(crate(hermit-abi)) = 0.1.19
|
||||
Provides: bundled(crate(indexmap)) = 1.9.3
|
||||
Provides: bundled(crate(itoa)) = 1.0.17
|
||||
Provides: bundled(crate(id-arena)) = 2.3.0
|
||||
Provides: bundled(crate(indexmap)) = 2.14.0
|
||||
Provides: bundled(crate(itoa)) = 1.0.18
|
||||
Provides: bundled(crate(jobserver)) = 0.1.34
|
||||
Provides: bundled(crate(libc)) = 0.2.180
|
||||
Provides: bundled(crate(linux-raw-sys)) = 0.11.0
|
||||
Provides: bundled(crate(log)) = 0.4.29
|
||||
Provides: bundled(crate(lru)) = 0.16.3
|
||||
Provides: bundled(crate(memchr)) = 2.7.6
|
||||
Provides: bundled(crate(once_cell)) = 1.21.3
|
||||
Provides: bundled(crate(openssl)) = 0.10.75
|
||||
Provides: bundled(crate(leb128fmt)) = 0.1.0
|
||||
Provides: bundled(crate(libc)) = 0.2.186
|
||||
Provides: bundled(crate(linux-raw-sys)) = 0.12.1
|
||||
Provides: bundled(crate(log)) = 0.4.32
|
||||
Provides: bundled(crate(lru)) = 0.16.4
|
||||
Provides: bundled(crate(memchr)) = 2.8.1
|
||||
Provides: bundled(crate(once_cell)) = 1.21.4
|
||||
Provides: bundled(crate(openssl)) = 0.10.80
|
||||
Provides: bundled(crate(openssl-macros)) = 0.1.1
|
||||
Provides: bundled(crate(openssl-sys)) = 0.9.111
|
||||
Provides: bundled(crate(openssl-sys)) = 0.9.116
|
||||
Provides: bundled(crate(os_str_bytes)) = 6.6.1
|
||||
Provides: bundled(crate(paste)) = 0.1.18
|
||||
Provides: bundled(crate(paste-impl)) = 0.1.18
|
||||
Provides: bundled(crate(pin-project-lite)) = 0.2.16
|
||||
Provides: bundled(crate(pkg-config)) = 0.3.32
|
||||
Provides: bundled(crate(pin-project-lite)) = 0.2.17
|
||||
Provides: bundled(crate(pkg-config)) = 0.3.33
|
||||
Provides: bundled(crate(prettyplease)) = 0.2.37
|
||||
Provides: bundled(crate(proc-macro-hack)) = 0.5.20+deprecated
|
||||
Provides: bundled(crate(proc-macro2)) = 1.0.105
|
||||
Provides: bundled(crate(quote)) = 1.0.43
|
||||
Provides: bundled(crate(r-efi)) = 5.3.0
|
||||
Provides: bundled(crate(rustix)) = 1.1.3
|
||||
Provides: bundled(crate(proc-macro2)) = 1.0.106
|
||||
Provides: bundled(crate(quote)) = 1.0.45
|
||||
Provides: bundled(crate(r-efi)) = 6.0.0
|
||||
Provides: bundled(crate(rustix)) = 1.1.4
|
||||
Provides: bundled(crate(semver)) = 1.0.28
|
||||
Provides: bundled(crate(serde)) = 1.0.228
|
||||
Provides: bundled(crate(serde_core)) = 1.0.228
|
||||
Provides: bundled(crate(serde_derive)) = 1.0.228
|
||||
Provides: bundled(crate(serde_json)) = 1.0.149
|
||||
Provides: bundled(crate(shlex)) = 1.3.0
|
||||
Provides: bundled(crate(serde_json)) = 1.0.150
|
||||
Provides: bundled(crate(shlex)) = 2.0.1
|
||||
Provides: bundled(crate(smallvec)) = 1.15.1
|
||||
Provides: bundled(crate(sptr)) = 0.3.2
|
||||
Provides: bundled(crate(strsim)) = 0.10.0
|
||||
Provides: bundled(crate(syn)) = 2.0.114
|
||||
Provides: bundled(crate(tempfile)) = 3.24.0
|
||||
Provides: bundled(crate(syn)) = 2.0.117
|
||||
Provides: bundled(crate(tempfile)) = 3.27.0
|
||||
Provides: bundled(crate(termcolor)) = 1.4.1
|
||||
Provides: bundled(crate(textwrap)) = 0.16.2
|
||||
Provides: bundled(crate(tokio)) = 1.49.0
|
||||
Provides: bundled(crate(tokio)) = 1.52.3
|
||||
Provides: bundled(crate(toml)) = 0.5.11
|
||||
Provides: bundled(crate(tracing)) = 0.1.44
|
||||
Provides: bundled(crate(tracing-attributes)) = 0.1.31
|
||||
Provides: bundled(crate(tracing-core)) = 0.1.36
|
||||
Provides: bundled(crate(unicode-ident)) = 1.0.22
|
||||
Provides: bundled(crate(unicode-ident)) = 1.0.24
|
||||
Provides: bundled(crate(unicode-xid)) = 0.2.6
|
||||
Provides: bundled(crate(uuid)) = 0.8.2
|
||||
Provides: bundled(crate(vcpkg)) = 0.2.15
|
||||
Provides: bundled(crate(wasi)) = 0.11.1+wasi_snapshot_preview1
|
||||
Provides: bundled(crate(wasip2)) = 1.0.1+wasi_0.2.4
|
||||
Provides: bundled(crate(wasip2)) = 1.0.3+wasi_0.2.9
|
||||
Provides: bundled(crate(wasip3)) = 0.4.0+wasi_0.3.0_rc_2026_01_06
|
||||
Provides: bundled(crate(wasm-encoder)) = 0.244.0
|
||||
Provides: bundled(crate(wasm-metadata)) = 0.244.0
|
||||
Provides: bundled(crate(wasmparser)) = 0.244.0
|
||||
Provides: bundled(crate(winapi)) = 0.3.9
|
||||
Provides: bundled(crate(winapi-i686-pc-windows-gnu)) = 0.4.0
|
||||
Provides: bundled(crate(winapi-util)) = 0.1.11
|
||||
Provides: bundled(crate(winapi-x86_64-pc-windows-gnu)) = 0.4.0
|
||||
Provides: bundled(crate(windows-link)) = 0.2.1
|
||||
Provides: bundled(crate(windows-sys)) = 0.61.2
|
||||
Provides: bundled(crate(wit-bindgen)) = 0.46.0
|
||||
Provides: bundled(crate(wit-bindgen)) = 0.57.1
|
||||
Provides: bundled(crate(wit-bindgen-core)) = 0.51.0
|
||||
Provides: bundled(crate(wit-bindgen-rust)) = 0.51.0
|
||||
Provides: bundled(crate(wit-bindgen-rust-macro)) = 0.51.0
|
||||
Provides: bundled(crate(wit-component)) = 0.244.0
|
||||
Provides: bundled(crate(wit-parser)) = 0.244.0
|
||||
Provides: bundled(crate(zeroize)) = 1.8.2
|
||||
Provides: bundled(crate(zeroize_derive)) = 1.4.3
|
||||
Provides: bundled(crate(zmij)) = 1.0.12
|
||||
Provides: bundled(crate(zmij)) = 1.0.21
|
||||
##### Bundled cargo crates list - END #####
|
||||
|
||||
BuildRequires: nspr-devel >= 4.32
|
||||
@ -268,47 +283,17 @@ Source3: https://github.com/jemalloc/%{jemalloc_name}/releases/download
|
||||
%endif
|
||||
Source4: 389-ds-base.sysusers
|
||||
|
||||
Source5: vendor-%{version}-5.tar.gz
|
||||
Source6: Cargo-%{version}-5.lock
|
||||
Source5: vendor-%{version}-1.tar.gz
|
||||
Source6: Cargo-%{version}-1.lock
|
||||
|
||||
Patch: 0001-Issue-7049-RetroCL-plugin-generates-invalid-LDIF.patch
|
||||
Patch: 0002-Issue-7096-During-replication-online-total-init-the-.patch
|
||||
Patch: 0003-Issue-Revise-paged-result-search-locking.patch
|
||||
Patch: 0004-Issue-7172-Index-ordering-mismatch-after-upgrade-717.patch
|
||||
Patch: 0005-Issue-7172-2nd-Index-ordering-mismatch-after-upgrade.patch
|
||||
Patch: 0006-Issue-7189-DSBLE0007-generates-incorrect-remediation.patch
|
||||
Patch: 0007-Issue-7184-argparse.HelpFormatter-_format_actions_us.patch
|
||||
Patch: 0008-Issue-7027-2nd-389-ds-base-OpenScanHub-Leaks-Detecte.patch
|
||||
Patch: 0009-Issue-7213-MDB_BAD_VALSIZE-error-while-handling-VLV-.patch
|
||||
Patch: 0010-Issue-6542-RPM-build-errors-on-Fedora-42.patch
|
||||
Patch: 0011-Issue-6476-Fix-build-failure-with-GCC-15.patch
|
||||
Patch: 0012-Issue-7223-Revert-index-scan-limits-for-system-index.patch
|
||||
Patch: 0013-Issue-7223-Add-upgrade-function-to-remove-nsIndexIDL.patch
|
||||
Patch: 0014-Issue-7223-Add-upgrade-function-to-remove-ancestorid.patch
|
||||
Patch: 0015-Issue-7223-Detect-and-log-index-ordering-mismatch-du.patch
|
||||
Patch: 0016-Issue-7223-Add-dsctl-index-check-command-for-offline.patch
|
||||
Patch: 0017-Issue-7096-2nd-During-replication-online-total-init-.patch
|
||||
Patch: 0018-Issue-7076-6992-6784-6214-Fix-CI-test-failures-7077.patch
|
||||
Patch: 0019-Issue-7076-Fix-revert_cache-never-called-in-modrdn-7.patch
|
||||
Patch: 0020-Issue-6947-Fix-health_system_indexes_test.py.patch
|
||||
Patch: 0021-Issue-7121-2nd-LeakSanitizer-various-leaks-during-re.patch
|
||||
Patch: 0022-Issue-7150-Compressed-access-log-rotations-skipped-a.patch
|
||||
Patch: 0023-Issue-7224-CI-Test-Simplify-test_reserve_descriptor_.patch
|
||||
Patch: 0024-Issue-7231-Sync-repl-tests-fail-in-FIPS-mode-due-to-.patch
|
||||
Patch: 0025-Issue-7248-CLI-attribute-uniqueness-fix-usage-for-ex.patch
|
||||
Patch: 0026-Issue-CLI-dsctl-db2index-needs-some-hardening-with-M.patch
|
||||
Patch: 0027-Issue-7184-2nd-argparse.HelpFormatter-_format_action.patch
|
||||
Patch: 0028-Issue-7213-2nd-MDB_BAD_VALSIZE-error-while-handling-.patch
|
||||
Patch: 0029-Issue-7223-Use-lexicographical-order-for-ancestorid-.patch
|
||||
Patch: 0030-Issue-7066-7052-allow-password-history-to-be-set-to-.patch
|
||||
Patch: 0031-Issue-7243-UI-fix-certificate-table-and-modal.patch
|
||||
Patch: 0032-Issue-7223-Remove-integerOrderingMatch-requirement-f.patch
|
||||
Patch: 0033-Issue-7053-Remove-memberof_del_dn_from_groups-from-M.patch
|
||||
Patch: 0034-Issue-5853-Update-concread-to-0.5.10.patch
|
||||
Patch: 0035-Issue-7271-plugins-that-create-threads-need-to-updat.patch
|
||||
Patch: 0036-Security-fix-for-CVE-2025-14905.patch
|
||||
Patch: 0037-Issue-7271-implement-a-pre-close-plugin-function.patch
|
||||
Patch: 0038-Issue-7271-Add-new-plugin-pre-close-function-check-t.patch
|
||||
Patch: 0001-Issue-7554-deref-plugin-null-pointer-dereference-if-.patch
|
||||
Patch: 0002-Issue-3555-UI-Fix-audit-issue-with-npm-brace-expansi.patch
|
||||
Patch: 0003-Issue-6922-AddressSanitizer-leaks-found-by-acl-test-.patch
|
||||
Patch: 0004-Issue-7437-LeakSanitizer-memory-leaks-in-CoS-cache-e.patch
|
||||
Patch: 0005-Issue-7372-Reindex-adds-tombstones-to-ancestorid-cau.patch
|
||||
Patch: 0006-Issue-7327-dsctl-healthcheck-DSMOLE0001-inaccurate-r.patch
|
||||
Patch: 0007-Issue-7267-MDB_BAD_VALSIZE-error-when-updating-index.patch
|
||||
Patch: 0008-Fix-test389-imports-on-older-branches.patch
|
||||
|
||||
%description
|
||||
389 Directory Server is an LDAPv3 compliant server. The base package includes
|
||||
@ -758,6 +743,26 @@ exit 0
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Jun 05 2026 Viktor Ashirov <vashirov@redhat.com> - 2.9.0-1
|
||||
- Bump version to 2.9.0
|
||||
- Resolves: RHEL-165978 - Replication halt caused by an incorrect setting of "nsslapd-changelogmaxage"
|
||||
- Resolves: RHEL-166003 - Crash in replica_config_add when manually configuring a replica with an incorrect nsds5ReplicaRoot.
|
||||
- Resolves: RHEL-166004 - Possible memory leak when using the Retro Changelog plugin.
|
||||
- Resolves: RHEL-168908 - ns-slapd fails to shutdown when deferred memberof update is in progress
|
||||
- Resolves: RHEL-168964 - Web console doesn't show the sub suffix of ou=foo,ou=people,dc=example,dc=com. [rhel-9]
|
||||
- Resolves: RHEL-168972 - Replica installation is failing with message MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED [rhel-9]
|
||||
- Resolves: RHEL-169530 - Rebase 389-ds-base to 2.9.x
|
||||
- Resolves: RHEL-170270 - DS 12 does not handle escape char in bind user [rhel-9]
|
||||
- Resolves: RHEL-170275 - dnaSharedConfig: "dnaPortNum: 0" [rhel-9]
|
||||
- Resolves: RHEL-170280 - Memory leaks in syncrepl plugin during persistent search operations [rhel-9]
|
||||
- Resolves: RHEL-170287 - access log - suspicious wtime optime negative and large values in internal op [rhel-9]
|
||||
- Resolves: RHEL-170477 - An online reinitialization with LMDB is terminating the receiving server [rhel-9]
|
||||
- Resolves: RHEL-170480 - dsctl healthcheck DSMOLE0001 inaccurate recommendations when there is more than 1 LDAP backend [rhel-9]
|
||||
- Resolves: RHEL-170651 - passwordbadwords attribute of the local password policy is not functioning [rhel-9]
|
||||
- Resolves: RHEL-170732 - Memory leaks in IPA context on server restart [rhel-9]
|
||||
- Resolves: RHEL-174525 - [RFE] Add OS-level thread names to all server threads [rhel-9]
|
||||
- Resolves: RHEL-180717 - Online export is failing when using the option "-s" [rhel-9]
|
||||
|
||||
* Thu Mar 05 2026 Viktor Ashirov <vashirov@redhat.com> - 2.8.0-6
|
||||
- Resolves: RHEL-152335 - Crash in trim_changelog() during the Retro Changelog trimming. [rhel-9.8]
|
||||
|
||||
|
||||
2
main.fmf
2
main.fmf
@ -10,7 +10,7 @@
|
||||
package: [389-ds-base, git, pytest]
|
||||
- name: clone repo
|
||||
how: shell
|
||||
script: git clone -b 389-ds-base-2.8 https://github.com/389ds/389-ds-base /root/ds
|
||||
script: git clone -b 389-ds-base-2.9 https://github.com/389ds/389-ds-base /root/ds
|
||||
/test:
|
||||
/upstream_basic:
|
||||
test: pytest -v /root/ds/dirsrvtests/tests/suites/basic/basic_test.py
|
||||
|
||||
6
sources
6
sources
@ -1,4 +1,4 @@
|
||||
SHA512 (jemalloc-5.3.0.tar.bz2) = 22907bb052096e2caffb6e4e23548aecc5cc9283dce476896a2b1127eee64170e3562fa2e7db9571298814a7a2c7df6e8d1fbe152bd3f3b0c1abec22a2de34b1
|
||||
SHA512 (389-ds-base-2.8.0.tar.bz2) = 82a1d71618dafa2aaba9e0c54d049dfda0a404fc836887e6283a22eded00a564d66c2988f19c549041db85511a3baa4792d68e89b2de8a5b3647c703c65b75cd
|
||||
SHA512 (Cargo-2.8.0-5.lock) = efd2512a494c2d8764553b99f911065f41545d55183385889a1e60a5ba65b6514347eef50cea2cc4126abae81e35b2c683b6d99bbe111cef603bc3f04f62c7e9
|
||||
SHA512 (vendor-2.8.0-5.tar.gz) = 587b478cf1ae57d808495317660ac31a0dcd5ef9df7f7ff6b7ce80e1690de3866fa3d40fa0d67a95413bd88b323e87cea3236454fba02cf1cef673a49092e0f2
|
||||
SHA512 (389-ds-base-2.9.0.tar.bz2) = 7bbc25185a4df5624a4ec8c3c79c78eecdce03e21548bc7f906263aae5815b3fd464d05d38a2540f378a1085c8b740660b908a82757f4e839928932dcf7da8d0
|
||||
SHA512 (vendor-2.9.0-1.tar.gz) = 7cddffb4a974201e96baa635fe1c07725f7ee036ac00852fcb7c318a9037d8d87683cc7d8ea4cfe1aa5d3b1af484e56fd946ed2ec2c99cc1d5730835dd304641
|
||||
SHA512 (Cargo-2.9.0-1.lock) = 967eedd442bd30ba39fc759f32d435c2956487d7d20dd8b922c0c5a4e1c4fe98b649471b3243065ffdf8d810acb8d2b69731d71fe26f2001b787a8b7dbea89a0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user