From b870f8a690ceb888e03b102aebb7780a3fecfa70 Mon Sep 17 00:00:00 2001
From: Noriko Hosoi <nhosoi@redhat.com>
Date: Fri, 27 Sep 2013 17:58:03 -0700
Subject: [PATCH] Ticket #47463 - IDL-style can become mismatched during
 partial restoration

Description by telackey:
When doing a partial/FRI restoration the database files are copied
key-by-key. This is necessary to reset the LSNs so the restored
files can merge into the existing DB environment (cf. dblayer_copy_
file_resetlsns()) in a recoverable way.

However, dblayer_copy_file_keybykey() does not set the comparison
function before calling DB->put(), which means that the restored
files will not necessarily be using the proper function.

While all the keys are technically present, indexed searches can
still fail to return relevant results because the key order is
significant when intersecting the results with the other indices.

Note: The bug was reported and the patch was provided by telackey.
(Thank you, Thomas!)

Additional fix by nhosoi:
The entryrdn index uses its special dup compare function.  The dup
compare function is set when the restoring index file is entryrdn.
---
 ldap/servers/slapd/back-ldbm/dbhelp.c  |  41 ++++++++++--
 ldap/servers/slapd/back-ldbm/dblayer.c | 110 ++++++++++++++++-----------------
 ldap/servers/slapd/back-ldbm/dblayer.h |   2 +-
 3 files changed, 92 insertions(+), 61 deletions(-)

diff --git a/ldap/servers/slapd/back-ldbm/dbhelp.c b/ldap/servers/slapd/back-ldbm/dbhelp.c
index cf20187..78449f1 100644
--- a/ldap/servers/slapd/back-ldbm/dbhelp.c
+++ b/ldap/servers/slapd/back-ldbm/dbhelp.c
@@ -49,7 +49,13 @@
 #include "back-ldbm.h"
 #include "dblayer.h"
 
-static int dblayer_copy_file_keybykey(DB_ENV *env, char *source_file_name, char *destination_file_name, int overwrite, dblayer_private *priv)
+static int
+dblayer_copy_file_keybykey(DB_ENV *env,
+                           char *source_file_name,
+                           char *destination_file_name,
+                           int overwrite,
+                           dblayer_private *priv,
+                           ldbm_instance *inst)
 {
 	int retval = 0;
 	int retval_cleanup = 0;
@@ -62,6 +68,7 @@ static int dblayer_copy_file_keybykey(DB_ENV *env, char *source_file_name, char
 	int cursor_flag = 0;
 	int finished = 0;
 	int mode = 0;
+	char *p = NULL;
 
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> dblayer_copy_file_keybykey\n", 0, 0, 0 );
 
@@ -127,10 +134,28 @@ static int dblayer_copy_file_keybykey(DB_ENV *env, char *source_file_name, char
 	 * since the DB_DUP and DB_DUPSORT flags wouldn't have been toggled on
 	 * unless they passed the check on the source.
 	 */
-	if (idl_get_idl_new() && (dbflags & DB_DUP) && (dbflags & DB_DUPSORT)) {
+	/* Entryrdn index has its own dup compare function */
+	if ((p = PL_strcasestr(source_file_name, LDBM_ENTRYRDN_STR)) &&
+		(*(p + sizeof(LDBM_ENTRYRDN_STR) - 1) == '.')) {
+		/* entryrdn.db */
+		struct attrinfo *ai = NULL;
+		ainfo_get(inst->inst_be, LDBM_ENTRYRDN_STR, &ai);
+		if (ai->ai_dup_cmp_fn) {
+			/* If set, use the special dup compare callback */
+			retval = destination_file->set_dup_compare(destination_file, ai->ai_dup_cmp_fn);
+			if (retval) {
+				LDAPDebug2Args(LDAP_DEBUG_ANY,
+				               "dblayer_copy_file_keybykey(entryrdn), set_dup_compare error %d: %s\n",
+				               retval, db_strerror(retval));
+				goto error;
+			}
+		}
+	} else if (idl_get_idl_new() && (dbflags & DB_DUP) && (dbflags & DB_DUPSORT)) {
 		retval = destination_file->set_dup_compare(destination_file, idl_new_compare_dups); 
 		if (retval) {
-			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, set_dup_compare error %d: %s\n", retval, db_strerror(retval), 0);
+			LDAPDebug2Args(LDAP_DEBUG_ANY,
+			               "dblayer_copy_file_keybykey, set_dup_compare error %d: %s\n",
+			               retval, db_strerror(retval));
 			goto error;
 		}
 	}
@@ -206,7 +231,13 @@ error:
 	return retval;
 }
 
-int dblayer_copy_file_resetlsns(char *home_dir ,char *source_file_name, char *destination_file_name, int overwrite, dblayer_private *priv)
+int
+dblayer_copy_file_resetlsns(char *home_dir,
+                            char *source_file_name,
+                            char *destination_file_name,
+                            int overwrite,
+                            dblayer_private *priv,
+                            ldbm_instance *inst)
 {
 	/* TEL 20130416 NOTE:
 	 * DB_ENV->lsn_reset() should also provide what we need.  The open 
@@ -233,7 +264,7 @@ int dblayer_copy_file_resetlsns(char *home_dir ,char *source_file_name, char *de
 		goto out;
 	}
 	/* Do the copy */
-	retval = dblayer_copy_file_keybykey(env, source_file_name, destination_file_name, overwrite, priv);
+	retval = dblayer_copy_file_keybykey(env, source_file_name, destination_file_name, overwrite, priv, inst);
 	if (retval) {
 		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_resetlsns: Copy not completed successfully.", 0, 0, 0);
 	}
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.c b/ldap/servers/slapd/back-ldbm/dblayer.c
index 013d1c0..4102d3c 100644
--- a/ldap/servers/slapd/back-ldbm/dblayer.c
+++ b/ldap/servers/slapd/back-ldbm/dblayer.c
@@ -3617,17 +3617,17 @@ dblayer_txn_begin_ext(struct ldbminfo *li, back_txnid parent_txn, back_txn *txn,
         {
             /* this txn is now our current transaction for current operations
                and new parent for any nested transactions created */
-		if (use_lock && log_flush_thread) {
-			int txn_id = new_txn.back_txn_txn->id(new_txn.back_txn_txn);
-			PR_Lock(sync_txn_log_flush);
-			txn_in_progress_count++;
-    			LDAPDebug(LDAP_DEBUG_BACKLDBM, "txn_begin: batchcount: %d, txn_in_progress: %d, curr_txn: %x\n", trans_batch_count, txn_in_progress_count, txn_id);
-			PR_Unlock(sync_txn_log_flush);
-		}
-        	dblayer_push_pvt_txn(&new_txn);
-        	if (txn) {
-	        	txn->back_txn_txn = new_txn.back_txn_txn;
-            	}
+            if (use_lock && log_flush_thread) {
+                int txn_id = new_txn.back_txn_txn->id(new_txn.back_txn_txn);
+                PR_Lock(sync_txn_log_flush);
+                txn_in_progress_count++;
+                LDAPDebug(LDAP_DEBUG_BACKLDBM, "txn_begin: batchcount: %d, txn_in_progress: %d, curr_txn: %x\n", trans_batch_count, txn_in_progress_count, txn_id);
+                PR_Unlock(sync_txn_log_flush);
+            }
+            dblayer_push_pvt_txn(&new_txn);
+            if (txn) {
+                txn->back_txn_txn = new_txn.back_txn_txn;
+            }
         }
     } else
     {
@@ -3655,16 +3655,16 @@ dblayer_txn_begin(backend *be, back_txnid parent_txn, back_txn *txn)
     struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
     int rc = 0;
     if (DBLOCK_INSIDE_TXN(li)) {
-    	rc = dblayer_txn_begin_ext(li,parent_txn,txn,PR_TRUE);
-	if (!rc && SERIALLOCK(li)) {
+        rc = dblayer_txn_begin_ext(li,parent_txn,txn,PR_TRUE);
+        if (!rc && SERIALLOCK(li)) {
             dblayer_lock_backend(be);
-    	}
+        }
     } else {
-    	if (SERIALLOCK(li)) {
+        if (SERIALLOCK(li)) {
             dblayer_lock_backend(be);
-    	}
-    	rc = dblayer_txn_begin_ext(li,parent_txn,txn,PR_TRUE);
-    	if (rc && SERIALLOCK(li)) {
+        }
+        rc = dblayer_txn_begin_ext(li,parent_txn,txn,PR_TRUE);
+        if (rc && SERIALLOCK(li)) {
             dblayer_unlock_backend(be);
         }
     }
@@ -3678,8 +3678,8 @@ int dblayer_txn_commit_ext(struct ldbminfo *li, back_txn *txn, PRBool use_lock)
     dblayer_private *priv = NULL;
     DB_TXN *db_txn = NULL;
     back_txn *cur_txn = NULL;
-	int txn_id = 0;
-	int txn_batch_slot = 0;
+    int txn_id = 0;
+    int txn_batch_slot = 0;
 
     PR_ASSERT(NULL != li);
 
@@ -3783,15 +3783,15 @@ dblayer_txn_commit(backend *be, back_txn *txn)
     struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
     int rc;
     if (DBLOCK_INSIDE_TXN(li)) {
-    	if (SERIALLOCK(li)) {
-    	    dblayer_unlock_backend(be);
+        if (SERIALLOCK(li)) {
+            dblayer_unlock_backend(be);
         }
-	rc  = dblayer_txn_commit_ext(li,txn,PR_TRUE);
+        rc  = dblayer_txn_commit_ext(li,txn,PR_TRUE);
     } else {
-	rc  = dblayer_txn_commit_ext(li,txn,PR_TRUE);
-    	if (SERIALLOCK(li)) {
+        rc  = dblayer_txn_commit_ext(li,txn,PR_TRUE);
+        if (SERIALLOCK(li)) {
             dblayer_unlock_backend(be);
-	}
+        }
     }
     return rc;
 }
@@ -3823,13 +3823,13 @@ int dblayer_txn_abort_ext(struct ldbminfo *li, back_txn *txn, PRBool use_lock)
         priv->dblayer_env &&
         priv->dblayer_enable_transactions)
     {
-	int txn_id = db_txn->id(db_txn);
-	if ( use_lock && log_flush_thread) {
-		PR_Lock(sync_txn_log_flush);
-		txn_in_progress_count--;
-		PR_Unlock(sync_txn_log_flush);
-    		LDAPDebug(LDAP_DEBUG_BACKLDBM, "txn_abort : batchcount: %d, txn_in_progress: %d, curr_txn: %x\n", trans_batch_count, txn_in_progress_count, txn_id);
-	}
+        int txn_id = db_txn->id(db_txn);
+        if ( use_lock && log_flush_thread) {
+            PR_Lock(sync_txn_log_flush);
+            txn_in_progress_count--;
+            PR_Unlock(sync_txn_log_flush);
+            LDAPDebug(LDAP_DEBUG_BACKLDBM, "txn_abort : batchcount: %d, txn_in_progress: %d, curr_txn: %x\n", trans_batch_count, txn_in_progress_count, txn_id);
+        }
         return_value = TXN_ABORT(db_txn);
         /* if we were given a transaction, and it is the same as the
            current transaction in progress, pop it off the stack
@@ -3874,14 +3874,14 @@ dblayer_txn_abort(backend *be, back_txn *txn)
     int rc;
     if (DBLOCK_INSIDE_TXN(li)) {
         if (SERIALLOCK(li)) {
-    	    dblayer_unlock_backend(be);
-	}
-	rc = dblayer_txn_abort_ext(li, txn, PR_TRUE);
+            dblayer_unlock_backend(be);
+        }
+        rc = dblayer_txn_abort_ext(li, txn, PR_TRUE);
     } else {
-	rc = dblayer_txn_abort_ext(li, txn, PR_TRUE);
+        rc = dblayer_txn_abort_ext(li, txn, PR_TRUE);
         if (SERIALLOCK(li)) {
-    	    dblayer_unlock_backend(be);
-	}
+            dblayer_unlock_backend(be);
+        }
     }
     return rc;
 }
@@ -4567,9 +4567,9 @@ static int log_flush_threadmain(void *param)
     interval_flush = PR_MillisecondsToInterval(trans_batch_txn_min_sleep);
     interval_wait = PR_MillisecondsToInterval(trans_batch_txn_max_sleep);
     interval_def = PR_MillisecondsToInterval(300); /*used while no txn or txn batching */
-						   /* LK this is only needed if online change of
-						    * of txn config is supported ??? 
-						    */	
+    /* LK this is only needed if online change of
+     * of txn config is supported ??? 
+     */
     while ((!priv->dblayer_stop_threads) && (log_flush_thread)){
         if (priv->dblayer_enable_transactions){
             if (trans_batch_limit > 0) {
@@ -5932,6 +5932,7 @@ dblayer_copy_directory(struct ldbminfo *li,
     char            inst_dir[MAXPATHLEN];
     char            sep;
     int             suffix_len = 0;
+    ldbm_instance *inst = NULL;
 
     if (!src_dir || '\0' == *src_dir)
     {
@@ -5955,6 +5956,14 @@ dblayer_copy_directory(struct ldbminfo *li,
     else
         relative_instance_name++;
 
+    inst = ldbm_instance_find_by_name(li, relative_instance_name);
+    if (NULL == inst) {
+        LDAPDebug(LDAP_DEBUG_ANY, "Backend instance \"%s\" does not exist; "
+                  "Instance path %s could be invalid.\n",
+                  relative_instance_name, src_dir, 0);
+        return return_value;
+    }
+
     if (is_fullpath(src_dir))
     {
         new_src_dir = src_dir;
@@ -5962,15 +5971,6 @@ dblayer_copy_directory(struct ldbminfo *li,
     else
     {
         int len;
-        ldbm_instance *inst =
-                       ldbm_instance_find_by_name(li, relative_instance_name);
-        if (NULL == inst)
-        {
-            LDAPDebug(LDAP_DEBUG_ANY, "Backend instance \"%s\" does not exist; "
-                  "Instance path %s could be invalid.\n",
-                  relative_instance_name, src_dir, 0);
-            return return_value;
-        }
 
         inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
                                               inst_dir, MAXPATHLEN);
@@ -6090,13 +6090,12 @@ dblayer_copy_directory(struct ldbminfo *li,
             /* If the file is a database file, and resetlsns is set, then we need to do a key by key copy */
             /* PL_strcmp takes NULL arg */
             if (resetlsns &&
-                (PL_strcmp(LDBM_FILENAME_SUFFIX, strrchr(filename1, '.'))
-                 == 0)) {
+                (PL_strcmp(LDBM_FILENAME_SUFFIX, strrchr(filename1, '.')) == 0)) {
                 return_value = dblayer_copy_file_resetlsns(src_dir, filename1, filename2,
-                                            0, priv);
+                                                           0, priv, inst);
             } else {
                 return_value = dblayer_copyfile(filename1, filename2,
-                                            0, priv->dblayer_file_mode);
+                                                0, priv->dblayer_file_mode);
             }
             slapi_ch_free((void**)&filename1);
             slapi_ch_free((void**)&filename2);
@@ -6819,6 +6818,7 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char *
     PR_Lock(li->li_config_mutex);
     /* dblayer_home_directory is freed in dblayer_post_close.
      * li_directory needs to live beyond dblayer. */
+    slapi_ch_free_string(&priv->dblayer_home_directory);
     priv->dblayer_home_directory = slapi_ch_strdup(li->li_directory);
     priv->dblayer_cachesize = li->li_dbcachesize;
     priv->dblayer_ncache = li->li_dbncache;
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.h b/ldap/servers/slapd/back-ldbm/dblayer.h
index c382a98..e1a178e 100644
--- a/ldap/servers/slapd/back-ldbm/dblayer.h
+++ b/ldap/servers/slapd/back-ldbm/dblayer.h
@@ -202,7 +202,7 @@ int dblayer_make_private_simple_env(char *db_home_dir, DB_ENV **env);
 /* Copy a database file, preserving all its contents (used to reset the LSNs in the file in order to move 
  * it from one transacted environment to another.
  */
-int dblayer_copy_file_resetlsns(char *home_dir, char *source_file_name, char *destination_file_name, int overwrite, dblayer_private *priv);
+int dblayer_copy_file_resetlsns(char *home_dir, char *source_file_name, char *destination_file_name, int overwrite, dblayer_private *priv, ldbm_instance *inst);
 /* Turn on the various logging and debug options for DB */
 void dblayer_set_env_debugging(DB_ENV *pEnv, dblayer_private *priv);
 
-- 
1.8.1.4

