From 17aada4feb87407e004a890225700e730778d692 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Thu, 20 Jun 2019 15:50:08 -0400 Subject: [PATCH 1/2] BZ1518320 - entry cache crash fix Description: THis patch is combination of all the entry cache fixes. If these fixes are not enough, there is an experimental "fix" that should prevent the crash. A message will be logged that reports the crash was averted: "(avoided crash, but cache was corrupted)" The customer should monitor the errors log for this text, and let GSS know if they see it. --- configure.ac | 3 - dirsrvtests/tests/suites/betxns/betxn_test.py | 57 ++++++ ldap/servers/slapd/back-ldbm/back-ldbm.h | 68 ++++---- ldap/servers/slapd/back-ldbm/backentry.c | 2 +- ldap/servers/slapd/back-ldbm/cache.c | 163 ++++++++++++++++-- ldap/servers/slapd/back-ldbm/ldbm_add.c | 13 ++ ldap/servers/slapd/back-ldbm/ldbm_delete.c | 12 ++ ldap/servers/slapd/back-ldbm/ldbm_modify.c | 12 ++ ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 22 ++- .../servers/slapd/back-ldbm/proto-back-ldbm.h | 1 + ldap/servers/slapd/slapi-plugin.h | 15 ++ ldap/servers/slapd/time.c | 26 +++ 12 files changed, 341 insertions(+), 53 deletions(-) diff --git a/configure.ac b/configure.ac index 91d6d398b..ea528ff2b 100644 --- a/configure.ac +++ b/configure.ac @@ -72,9 +72,6 @@ AC_FUNC_STRFTIME AC_FUNC_VPRINTF AC_CHECK_FUNCS([endpwent ftruncate getcwd gethostbyname inet_ntoa localtime_r memmove memset mkdir munmap putenv rmdir setrlimit socket strcasecmp strchr strcspn strdup strerror strncasecmp strpbrk strrchr strstr strtol tzset]) -# These functions are *required* without option. -AC_CHECK_FUNCS([clock_gettime], [], AC_MSG_ERROR([unable to locate required symbol clock_gettime])) - # This will detect if we need to add the LIBADD_DL value for us. LT_LIB_DLLOAD diff --git a/dirsrvtests/tests/suites/betxns/betxn_test.py b/dirsrvtests/tests/suites/betxns/betxn_test.py index 175496495..48181a9ea 100644 --- a/dirsrvtests/tests/suites/betxns/betxn_test.py +++ b/dirsrvtests/tests/suites/betxns/betxn_test.py @@ -8,6 +8,7 @@ # import pytest import six +import ldap from lib389.tasks import * from lib389.utils import * from lib389.topologies import topology_st @@ -248,6 +249,62 @@ def test_betxn_memberof(topology_st, dynamic_plugins): log.info('test_betxn_memberof: PASSED') +def test_betxn_modrdn_memberof(topology_st): + """Test modrdn operartions and memberOf + + :id: 70d0b96e-b693-4bf7-bbf5-102a66ac5994 + + :setup: Standalone instance + + :steps: 1. Enable and configure memberOf plugin + 2. Set memberofgroupattr="member" and memberofAutoAddOC="nsContainer" + 3. Create group and user outside of memberOf plugin scope + 4. Do modrdn to move group into scope + 5. Do modrdn to move group into scope (again) + + :expectedresults: + 1. memberOf plugin plugin should be ON + 2. Set memberofgroupattr="member" and memberofAutoAddOC="nsContainer" should PASS + 3. Creating group and user should PASS + 4. Modrdn should fail with objectclass violation + 5. Second modrdn should also fail with objectclass violation + """ + + peoplebase = 'ou=people,%s' % DEFAULT_SUFFIX + memberof = MemberOfPlugin(topology_st.standalone) + memberof.enable() + memberof.set_autoaddoc('nsContainer') # Bad OC + memberof.set('memberOfEntryScope', peoplebase) + memberof.set('memberOfAllBackends', 'on') + topology_st.standalone.restart() + + groups = Groups(topology_st.standalone, DEFAULT_SUFFIX) + group = groups.create(properties={ + 'cn': 'group', + }) + + # Create user and add it to group + users = UserAccounts(topology_st.standalone, basedn=DEFAULT_SUFFIX) + user = users.create(properties=TEST_USER_PROPERTIES) + if not ds_is_older('1.3.7'): + user.remove('objectClass', 'nsMemberOf') + + group.add_member(user.dn) + + # Attempt modrdn that should fail, but the original entry should stay in the cache + with pytest.raises(ldap.OBJECTCLASS_VIOLATION): + group.rename('cn=group_to_people', newsuperior=peoplebase) + + # Should fail, but not with NO_SUCH_OBJECT as the original entry should still be in the cache + with pytest.raises(ldap.OBJECTCLASS_VIOLATION): + group.rename('cn=group_to_people', newsuperior=peoplebase) + + # + # Done + # + log.info('test_betxn_modrdn_memberof: PASSED') + + if __name__ == '__main__': # Run isolated # -s for DEBUG mode diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h index 4727961a9..399508561 100644 --- a/ldap/servers/slapd/back-ldbm/back-ldbm.h +++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h @@ -310,36 +310,37 @@ typedef struct #define CACHE_TYPE_ENTRY 0 #define CACHE_TYPE_DN 1 -struct backcommon -{ - int ep_type; /* to distinguish backdn from backentry */ - struct backcommon *ep_lrunext; /* for the cache */ - struct backcommon *ep_lruprev; /* for the cache */ - ID ep_id; /* entry id */ - char ep_state; /* state in the cache */ -#define ENTRY_STATE_DELETED 0x1 /* entry is marked as deleted */ -#define ENTRY_STATE_CREATING 0x2 /* entry is being created; don't touch it */ -#define ENTRY_STATE_NOTINCACHE 0x4 /* cache_add failed; not in the cache */ - int ep_refcnt; /* entry reference cnt */ - size_t ep_size; /* for cache tracking */ +struct backcommon { + int ep_type; /* to distinguish backdn from backentry */ + struct backcommon *ep_lrunext; /* for the cache */ + struct backcommon *ep_lruprev; /* for the cache */ + ID ep_id; /* entry id */ + char ep_state; /* state in the cache */ +#define ENTRY_STATE_DELETED 0x1 /* entry is marked as deleted */ +#define ENTRY_STATE_CREATING 0x2 /* entry is being created; don't touch it */ +#define ENTRY_STATE_NOTINCACHE 0x4 /* cache_add failed; not in the cache */ +#define ENTRY_STATE_INVALID 0x8 /* cache entry is invalid and needs to be removed */ + int ep_refcnt; /* entry reference cnt */ + size_t ep_size; /* for cache tracking */ + struct timespec ep_create_time; /* the time the entry was added to the cache */ }; /* From ep_type through ep_size MUST be identical to backcommon */ -struct backentry -{ - int ep_type; /* to distinguish backdn from backentry */ - struct backcommon *ep_lrunext; /* for the cache */ - struct backcommon *ep_lruprev; /* for the cache */ - ID ep_id; /* entry id */ - char ep_state; /* state in the cache */ - int ep_refcnt; /* entry reference cnt */ - size_t ep_size; /* for cache tracking */ - Slapi_Entry *ep_entry; /* real entry */ - Slapi_Entry *ep_vlventry; - void *ep_dn_link; /* linkage for the 3 hash */ - void *ep_id_link; /* tables used for */ - void *ep_uuid_link; /* looking up entries */ - PRMonitor *ep_mutexp; /* protection for mods; make it reentrant */ +struct backentry { + int ep_type; /* to distinguish backdn from backentry */ + struct backcommon *ep_lrunext; /* for the cache */ + struct backcommon *ep_lruprev; /* for the cache */ + ID ep_id; /* entry id */ + char ep_state; /* state in the cache */ + int ep_refcnt; /* entry reference cnt */ + size_t ep_size; /* for cache tracking */ + struct timespec ep_create_time; /* the time the entry was added to the cache */ + Slapi_Entry *ep_entry; /* real entry */ + Slapi_Entry *ep_vlventry; + void * ep_dn_link; /* linkage for the 3 hash */ + void * ep_id_link; /* tables used for */ + void * ep_uuid_link; /* looking up entries */ + PRMonitor *ep_mutexp; /* protection for mods; make it reentrant */ }; /* From ep_type through ep_size MUST be identical to backcommon */ @@ -348,12 +349,13 @@ struct backdn int ep_type; /* to distinguish backdn from backentry */ struct backcommon *ep_lrunext; /* for the cache */ struct backcommon *ep_lruprev; /* for the cache */ - ID ep_id; /* entry id */ - char ep_state; /* state in the cache; share ENTRY_STATE_* */ - int ep_refcnt; /* entry reference cnt */ - size_t ep_size; /* for cache tracking */ - Slapi_DN *dn_sdn; - void *dn_id_link; /* for hash table */ + ID ep_id; /* entry id */ + char ep_state; /* state in the cache; share ENTRY_STATE_* */ + int ep_refcnt; /* entry reference cnt */ + size_t ep_size; /* for cache tracking */ + struct timespec ep_create_time; /* the time the entry was added to the cache */ + Slapi_DN *dn_sdn; + void *dn_id_link; /* for hash table */ }; /* for the in-core cache of entries */ diff --git a/ldap/servers/slapd/back-ldbm/backentry.c b/ldap/servers/slapd/back-ldbm/backentry.c index f2fe780db..a1f3ca1bb 100644 --- a/ldap/servers/slapd/back-ldbm/backentry.c +++ b/ldap/servers/slapd/back-ldbm/backentry.c @@ -23,7 +23,7 @@ backentry_free(struct backentry **bep) return; } ep = *bep; - PR_ASSERT(ep->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE)); + PR_ASSERT(ep->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE | ENTRY_STATE_INVALID)); if (ep->ep_entry != NULL) { slapi_entry_free(ep->ep_entry); } diff --git a/ldap/servers/slapd/back-ldbm/cache.c b/ldap/servers/slapd/back-ldbm/cache.c index 86e1f7b39..054766df2 100644 --- a/ldap/servers/slapd/back-ldbm/cache.c +++ b/ldap/servers/slapd/back-ldbm/cache.c @@ -56,6 +56,11 @@ #define LOG(...) #endif +typedef enum { + ENTRY_CACHE, + DN_CACHE, +} CacheType; + #define LRU_DETACH(cache, e) lru_detach((cache), (void *)(e)) #define CACHE_LRU_HEAD(cache, type) ((type)((cache)->c_lruhead)) @@ -185,6 +190,7 @@ new_hash(u_long size, u_long offset, HashFn hfn, HashTestFn tfn) int add_hash(Hashtable *ht, void *key, uint32_t keylen, void *entry, void **alt) { + struct backcommon *back_entry = (struct backcommon *)entry; u_long val, slot; void *e; @@ -202,6 +208,7 @@ add_hash(Hashtable *ht, void *key, uint32_t keylen, void *entry, void **alt) e = HASH_NEXT(ht, e); } /* ok, it's not already there, so add it */ + back_entry->ep_create_time = slapi_current_rel_time_hr(); HASH_NEXT(ht, entry) = ht->slot[slot]; ht->slot[slot] = entry; return 1; @@ -492,6 +499,126 @@ cache_make_hashes(struct cache *cache, int type) } } +/* + * Helper function for flush_hash() to calculate if the entry should be + * removed from the cache. + */ +static int32_t +flush_remove_entry(struct timespec *entry_time, struct timespec *start_time) +{ + struct timespec diff; + + slapi_timespec_diff(entry_time, start_time, &diff); + if (diff.tv_sec >= 0) { + return 1; + } else { + return 0; + } +} + +/* + * Flush all the cache entries that were added after the "start time" + * This is called when a backend transaction plugin fails, and we need + * to remove all the possible invalid entries in the cache. + * + * If the ref count is 0, we can straight up remove it from the cache, but + * if the ref count is greater than 1, then the entry is currently in use. + * In the later case we set the entry state to ENTRY_STATE_INVALID, and + * when the owning thread cache_returns() the cache entry is automatically + * removed so another thread can not use/lock the invalid cache entry. + */ +static void +flush_hash(struct cache *cache, struct timespec *start_time, int32_t type) +{ + Hashtable *ht = cache->c_idtable; /* start with the ID table as it's in both ENTRY and DN caches */ + void *e, *laste = NULL; + + cache_lock(cache); + + for (size_t i = 0; i < ht->size; i++) { + e = ht->slot[i]; + while (e) { + struct backcommon *entry = (struct backcommon *)e; + uint64_t remove_it = 0; + if (flush_remove_entry(&entry->ep_create_time, start_time)) { + /* Mark the entry to be removed */ + slapi_log_err(SLAPI_LOG_CACHE, "flush_hash", "[%s] Removing entry id (%d)\n", + type ? "DN CACHE" : "ENTRY CACHE", entry->ep_id); + remove_it = 1; + } + laste = e; + e = HASH_NEXT(ht, e); + + if (remove_it) { + /* since we have the cache lock we know we can trust refcnt */ + entry->ep_state |= ENTRY_STATE_INVALID; + if (entry->ep_refcnt == 0) { + entry->ep_refcnt++; + lru_delete(cache, laste); + if (type == ENTRY_CACHE) { + entrycache_remove_int(cache, laste); + entrycache_return(cache, (struct backentry **)&laste); + } else { + dncache_remove_int(cache, laste); + dncache_return(cache, (struct backdn **)&laste); + } + } else { + /* Entry flagged for removal */ + slapi_log_err(SLAPI_LOG_CACHE, "flush_hash", + "[%s] Flagging entry to be removed later: id (%d) refcnt: %d\n", + type ? "DN CACHE" : "ENTRY CACHE", entry->ep_id, entry->ep_refcnt); + } + } + } + } + + if (type == ENTRY_CACHE) { + /* Also check the DN hashtable */ + ht = cache->c_dntable; + + for (size_t i = 0; i < ht->size; i++) { + e = ht->slot[i]; + while (e) { + struct backcommon *entry = (struct backcommon *)e; + uint64_t remove_it = 0; + if (flush_remove_entry(&entry->ep_create_time, start_time)) { + /* Mark the entry to be removed */ + slapi_log_err(SLAPI_LOG_CACHE, "flush_hash", "[ENTRY CACHE] Removing entry id (%d)\n", + entry->ep_id); + remove_it = 1; + } + laste = e; + e = HASH_NEXT(ht, e); + + if (remove_it) { + /* since we have the cache lock we know we can trust refcnt */ + entry->ep_state |= ENTRY_STATE_INVALID; + if (entry->ep_refcnt == 0) { + entry->ep_refcnt++; + lru_delete(cache, laste); + entrycache_remove_int(cache, laste); + entrycache_return(cache, (struct backentry **)&laste); + } else { + /* Entry flagged for removal */ + slapi_log_err(SLAPI_LOG_CACHE, "flush_hash", + "[ENTRY CACHE] Flagging entry to be removed later: id (%d) refcnt: %d\n", + entry->ep_id, entry->ep_refcnt); + } + } + } + } + } + + cache_unlock(cache); +} + +void +revert_cache(ldbm_instance *inst, struct timespec *start_time) +{ + flush_hash(&inst->inst_cache, start_time, ENTRY_CACHE); + flush_hash(&inst->inst_dncache, start_time, DN_CACHE); +} + /* initialize the cache */ int cache_init(struct cache *cache, uint64_t maxsize, long maxentries, int type) @@ -1141,10 +1268,10 @@ entrycache_return(struct cache *cache, struct backentry **bep) backentry_free(bep); } else { ASSERT(e->ep_refcnt > 0); - if (!--e->ep_refcnt) { - if (e->ep_state & ENTRY_STATE_DELETED) { - const char *ndn = slapi_sdn_get_ndn(backentry_get_sdn(e)); - if (ndn) { + if (! --e->ep_refcnt) { + if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_INVALID)) { + const char* ndn = slapi_sdn_get_ndn(backentry_get_sdn(e)); + if (ndn){ /* * State is "deleted" and there are no more references, * so we need to remove the entry from the DN cache because @@ -1154,6 +1281,13 @@ entrycache_return(struct cache *cache, struct backentry **bep) LOG("entrycache_return -Failed to remove %s from dn table\n", ndn); } } + if (e->ep_state & ENTRY_STATE_INVALID) { + /* Remove it from the hash table before we free the back entry */ + slapi_log_err(SLAPI_LOG_CACHE, "entrycache_return", + "Finally flushing invalid entry: %d (%s)\n", + e->ep_id, backentry_get_ndn(e)); + entrycache_remove_int(cache, e); + } backentry_free(bep); } else { lru_add(cache, e); @@ -1535,11 +1669,11 @@ cache_lock_entry(struct cache *cache, struct backentry *e) /* make sure entry hasn't been deleted now */ cache_lock(cache); - if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE)) { - cache_unlock(cache); - PR_ExitMonitor(e->ep_mutexp); - LOG("<= cache_lock_entry (DELETED)\n"); - return RETRY_CACHE_LOCK; + if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE | ENTRY_STATE_INVALID)) { + cache_unlock(cache); + PR_ExitMonitor(e->ep_mutexp); + LOG("<= cache_lock_entry (DELETED)\n"); + return RETRY_CACHE_LOCK; } cache_unlock(cache); @@ -1695,8 +1829,15 @@ dncache_return(struct cache *cache, struct backdn **bdn) backdn_free(bdn); } else { ASSERT((*bdn)->ep_refcnt > 0); - if (!--(*bdn)->ep_refcnt) { - if ((*bdn)->ep_state & ENTRY_STATE_DELETED) { + if (! --(*bdn)->ep_refcnt) { + if ((*bdn)->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_INVALID)) { + if ((*bdn)->ep_state & ENTRY_STATE_INVALID) { + /* Remove it from the hash table before we free the back dn */ + slapi_log_err(SLAPI_LOG_CACHE, "dncache_return", + "Finally flushing invalid entry: %d (%s)\n", + (*bdn)->ep_id, slapi_sdn_get_dn((*bdn)->dn_sdn)); + dncache_remove_int(cache, (*bdn)); + } backdn_free(bdn); } else { lru_add(cache, (void *)*bdn); diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c index 32c8e71ff..d3c8cdab2 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_add.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c @@ -97,6 +97,8 @@ ldbm_back_add(Slapi_PBlock *pb) PRUint64 conn_id; int op_id; int result_sent = 0; + int32_t parent_op = 0; + struct timespec parent_time; if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) { conn_id = 0; /* connection is NULL */ @@ -147,6 +149,13 @@ ldbm_back_add(Slapi_PBlock *pb) slapi_entry_delete_values(e, numsubordinates, NULL); dblayer_txn_init(li, &txn); + + if (txn.back_txn_txn == NULL) { + /* This is the parent operation, get the time */ + parent_op = 1; + parent_time = slapi_current_rel_time_hr(); + } + /* the calls to perform searches require the parent txn if any so set txn to the parent_txn until we begin the child transaction */ if (parent_txn) { @@ -1239,6 +1248,10 @@ ldbm_back_add(Slapi_PBlock *pb) goto common_return; error_return: + if (parent_op) { + revert_cache(inst, &parent_time); + } + if (addingentry_id_assigned) { next_id_return(be, addingentry->ep_id); } diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c index f5f6c1e3a..80c53a3e0 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c @@ -79,6 +79,8 @@ ldbm_back_delete(Slapi_PBlock *pb) ID tomb_ep_id = 0; int result_sent = 0; Connection *pb_conn; + int32_t parent_op = 0; + struct timespec parent_time; if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) { conn_id = 0; /* connection is NULL */ @@ -98,6 +100,13 @@ ldbm_back_delete(Slapi_PBlock *pb) /* dblayer_txn_init needs to be called before "goto error_return" */ dblayer_txn_init(li, &txn); + + if (txn.back_txn_txn == NULL) { + /* This is the parent operation, get the time */ + parent_op = 1; + parent_time = slapi_current_rel_time_hr(); + } + /* the calls to perform searches require the parent txn if any so set txn to the parent_txn until we begin the child transaction */ if (parent_txn) { @@ -1356,6 +1365,9 @@ commit_return: goto common_return; error_return: + if (parent_op) { + revert_cache(inst, &parent_time); + } if (tombstone) { if (cache_is_in_cache(&inst->inst_cache, tombstone)) { tomb_ep_id = tombstone->ep_id; /* Otherwise, tombstone might have been freed. */ diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c index cc4319e5f..93ab0a9e8 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c @@ -412,6 +412,8 @@ ldbm_back_modify(Slapi_PBlock *pb) int fixup_tombstone = 0; int ec_locked = 0; int result_sent = 0; + int32_t parent_op = 0; + struct timespec parent_time; slapi_pblock_get(pb, SLAPI_BACKEND, &be); slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &li); @@ -424,6 +426,13 @@ ldbm_back_modify(Slapi_PBlock *pb) fixup_tombstone = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_FIXUP); dblayer_txn_init(li, &txn); /* must do this before first goto error_return */ + + if (txn.back_txn_txn == NULL) { + /* This is the parent operation, get the time */ + parent_op = 1; + parent_time = slapi_current_rel_time_hr(); + } + /* the calls to perform searches require the parent txn if any so set txn to the parent_txn until we begin the child transaction */ if (parent_txn) { @@ -887,6 +896,9 @@ ldbm_back_modify(Slapi_PBlock *pb) goto common_return; error_return: + if (parent_op) { + revert_cache(inst, &parent_time); + } if (postentry != NULL) { slapi_entry_free(postentry); postentry = NULL; diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c index e2e9d1b46..1ca1bdb28 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c @@ -97,6 +97,8 @@ ldbm_back_modrdn(Slapi_PBlock *pb) int op_id; int result_sent = 0; Connection *pb_conn = NULL; + int32_t parent_op = 0; + struct timespec parent_time; if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) { conn_id = 0; /* connection is NULL */ @@ -134,6 +136,13 @@ ldbm_back_modrdn(Slapi_PBlock *pb) /* dblayer_txn_init needs to be called before "goto error_return" */ dblayer_txn_init(li, &txn); + + if (txn.back_txn_txn == NULL) { + /* This is the parent operation, get the time */ + parent_op = 1; + parent_time = slapi_current_rel_time_hr(); + } + /* the calls to perform searches require the parent txn if any so set txn to the parent_txn until we begin the child transaction */ if (parent_txn) { @@ -1276,6 +1285,10 @@ ldbm_back_modrdn(Slapi_PBlock *pb) goto common_return; error_return: + /* Revert the caches if this is the parent operation */ + if (parent_op) { + revert_cache(inst, &parent_time); + } /* result already sent above - just free stuff */ if (postentry) { slapi_entry_free(postentry); @@ -1353,6 +1366,10 @@ 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); + /* 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); @@ -1413,12 +1430,7 @@ common_return: CACHE_RETURN(&inst->inst_dncache, &bdn); } - /* remove the new entry from the cache if the op failed - - otherwise, leave it in */ if (ec && inst) { - if (retval && cache_is_in_cache(&inst->inst_cache, ec)) { - CACHE_REMOVE(&inst->inst_cache, ec); - } CACHE_RETURN(&inst->inst_cache, &ec); } ec = NULL; diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h index 61c3313c5..510d38f57 100644 --- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h +++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h @@ -55,6 +55,7 @@ void cache_unlock_entry(struct cache *cache, struct backentry *e); int cache_replace(struct cache *cache, void *oldptr, void *newptr); int cache_has_otherref(struct cache *cache, void *bep); int cache_is_in_cache(struct cache *cache, void *ptr); +void revert_cache(ldbm_instance *inst, struct timespec *start_time); #ifdef CACHE_DEBUG void check_entry_cache(struct cache *cache, struct backentry *e); diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index bdad4e59e..eefe88724 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -6853,6 +6853,12 @@ void slapi_operation_time_expiry(Slapi_Operation *o, time_t timeout, struct time */ slapi_timer_result slapi_timespec_expire_check(struct timespec *expire); +/** + * Returns the current system time as a hr clock + * + * \return timespec of the current monotonic time. + */ +struct timespec slapi_current_rel_time_hr(void); /* * Plugin and parameter block related macros (remainder of this file). @@ -8296,6 +8302,15 @@ uint64_t slapi_atomic_decr_64(uint64_t *ptr, int memorder); /* helper function */ const char * fetch_attr(Slapi_Entry *e, const char *attrname, char *default_val); +/** + * Diffs two timespects a - b into *diff. This is useful with + * clock_monotonic to find time taken to perform operations. + * + * \param struct timespec a the "end" time. + * \param struct timespec b the "start" time. + * \param struct timespec c the difference. + */ +void slapi_timespec_diff(struct timespec *a, struct timespec *b, struct timespec *diff); #ifdef __cplusplus } diff --git a/ldap/servers/slapd/time.c b/ldap/servers/slapd/time.c index 584bd1e63..2a3865858 100644 --- a/ldap/servers/slapd/time.c +++ b/ldap/servers/slapd/time.c @@ -96,6 +96,32 @@ slapi_current_utc_time_hr(void) return ltnow; } +struct timespec +slapi_current_rel_time_hr(void) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now; +} + +void +slapi_timespec_diff(struct timespec *a, struct timespec *b, struct timespec *diff) +{ + /* Now diff the two */ + time_t sec = a->tv_sec - b->tv_sec; + int32_t nsec = a->tv_nsec - b->tv_nsec; + + if (nsec < 0) { + /* It's negative so take one second */ + sec -= 1; + /* And set nsec to to a whole value */ + nsec = 1000000000 - nsec; + } + + diff->tv_sec = sec; + diff->tv_nsec = nsec; +} + time_t slapi_current_utc_time(void) { -- 2.21.0