Przeglądaj źródła

Add support for filesystem-based replica initialization.

David Boreham 21 lat temu
rodzic
commit
b323e28707

+ 50 - 19
ldap/admin/src/create_instance.c

@@ -2398,18 +2398,31 @@ char *ds_gen_scripts(char *sroot, server_config_s *cf, char *cs_path)
     if(t) return t;
     
     t = gen_script(cs_path, "bak2db", 
-           "if [ \"$#\" -ne 1 ]\nthen\n"
-           "    echo \"Usage: bak2db archivedir\"\n"
-           "    exit 1\nfi\n\n"
-           "if [ 1 = `expr $1 : \"\\/\"` ]\nthen\n"
-           "    archivedir=$1\n"
+           "if [ $# -lt 1 ] || [ $# -gt 3 ]\nthen\n"
+           "    echo \"Usage: bak2db archivedir [-n backendname]\"\n"
+           "    exit 1\n"
+           "else\n"
+           "	archivedir=$1\n"
+           "	shift\n"
+           "fi\n"
+           "while getopts \"n:\" flag\ndo\n"
+           "	case $flag in\n"
+           "		n) bename=$OPTARG;;\n"
+           "		*) echo \"Usage: bak2db archivedir [-n backendname]\"; exit 2;;\n"
+           "	esac\n"
+           "done\n\n"
+           "if [ 1 = `expr $archivedir : \"\\/\"` ]\nthen\n"
+           "    archivedir=$archivedir\n"
            "else\n"
            "    # relative\n"
-           "    cwd=`pwd`\n"
-           "    archivedir=`echo $cwd/$1`\nfi\n\n"
+           "    archivedir=`pwd`/$archivedir\nfi\n\n"
            "cd %s\n"
-           "./ns-slapd archive2db -D %s -a $archivedir\n",
-           server, cs_path);
+           "if [ \"$#\" -eq 2 ]\nthen\n"
+           "    ./ns-slapd archive2db -D %s -a $archivedir -n $bename\n"
+           "else\n"
+           "    ./ns-slapd archive2db -D %s -a $archivedir\n"		   
+           "fi\n",
+           server, cs_path, cs_path);
     if(t) return t;
 
     t = CREATE_BAK2DB();
@@ -2941,20 +2954,38 @@ char *ds_gen_scripts(char *sroot, server_config_s *cf, char *cs_path)
     if(t) return t;
     
     t = gen_script(cs_path, "bak2db.bat", 
-           "@echo off\n"
-            "setlocal\n\n"
-            "set rc=0\n"
-            "if [%%1] == [] goto usage\n\n"    
-           "\"%s\\slapd\" archive2db -D \"%s\" -a %%1\n"
-            "set rc=%%errorlevel%%\n"
-            "goto done\n\n"
+            "@echo off\n"
+            "pushd & setlocal\n\n"
+            "if [%%1] == [] (goto :usage)\n"
+            "if not [%%4] == [] (goto :usage)\n\n"
+            "set archivedir=%%1\n"
+            "set rc=0\n\n"
+            ":getopts\n"
+            "shift\n"
+            "if [%%1]==[] (goto :main)\n"
+            "if [%%1]==[-n] (if not [%%2]==[] (set bename=%%2) else (goto :usage)) else (goto :getopts)\n\n"
+            ":main\n"
+            "call :relative %%archivedir%%\n"
+            "if defined bename (\n"
+            "\"%s\\slapd\" archive2db -D \"%s\" -a %%archivedir%% -n %%bename%%\n"
+            ") else (\n"
+            "\"%s\\slapd\" archive2db -D \"%s\" -a %%archivedir%%\n"
+            ")\n"
+            "set rc=%%ERRORLEVEL%%\n"
+            "popd\n"
+            "goto :done\n\n"
+            "goto :EOF\n"
             ":usage\n"
-            "echo \"Usage: bak2db -a archivedir\"\n\n"
-            "set rc=1\n"
+            "echo %%0 archivedir [-n backendname]\n"
+            "goto :done\n\n"
+            "goto :EOF\n"
+            ":relative\n"
+            "set archivedir=%%~f1\n\n"
+            "goto :EOF\n"
             ":done\n"
             "if defined MKSARGS exit %%rc%%\n"
             "exit /b %%rc%%\n",
-           server, cs_path);
+            server, cs_path, server, cs_path);
     if(t) return t;
 
 #if defined(UPGRADEDB)

+ 13 - 1
ldap/admin/src/scripts/template-bak2db.pl

@@ -16,11 +16,13 @@ sub usage {
 	print(STDERR "     : -j filename - Read Directory Manager's password from file\n");
 	print(STDERR "     : -a dirname  - backup directory\n");
 	print(STDERR "     : -t dbtype   - database type (default: ldbm database)\n");
+	print(STDERR "     : -n backend  - name of backend instance to restore\n");
 	print(STDERR "     : -v          - verbose\n");
 }
 $taskname = "";
 $archivedir = "";
 $dbtype = "ldbm database";
+$instance = "";
 $dsroot = "{{DS-ROOT}}";
 $mydsroot = "{{MY-DS-ROOT}}";
 $verbose = 0;
@@ -39,6 +41,8 @@ while ($i <= $#ARGV) {
 		$i++; $passwdfile = $ARGV[$i];
 	} elsif ("$ARGV[$i]" eq "-t") {	# database type
 		$i++; $dbtype = $ARGV[$i];
+	} elsif ("$ARGV[$i]" eq "-n") {	# backend instance name
+		$i++; $instance = $ARGV[$i];
 	} elsif ("$ARGV[$i]" eq "-v") {	# verbose
 		$verbose = 1;
 	} else {
@@ -75,12 +79,20 @@ $taskname = "restore_${yr}_${mn}_${dy}_${h}_${m}_${s}";
 if ($archivedir eq "") {
 	&usage; exit(1);
 }
+use File::Spec;
+$isabs = File::Spec->file_name_is_absolute( $archivedir );
+if (!$isabs) {
+    $archivedir = File::Spec->rel2abs( $archivedir );
+}
 $dn = "dn: cn=$taskname, cn=restore, cn=tasks, cn=config\n";
 $misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n";
 $cn = "cn: $taskname\n";
+if ($instance ne "") {
+	$nsinstance = "nsInstance: ${instance}\n";
+}
 $nsarchivedir = "nsArchiveDir: $archivedir\n";
 $nsdbtype = "nsDatabaseType: $dbtype\n";
-$entry = "${dn}${misc}${cn}${nsarchivedir}${nsdbtype}";
+$entry = "${dn}${misc}${cn}${nsinstance}${nsarchivedir}${nsdbtype}";
 $vstr = "";
 if ($verbose != 0) { $vstr = "-v"; }
 chdir("$dsroot{{SEP}}shared{{SEP}}bin");

+ 2 - 1
ldap/servers/slapd/back-ldbm/Makefile

@@ -55,7 +55,8 @@ LIBBACK_LDBM_OBJS=	idl.o idl_shim.o idl_new.o idl_common.o cache.o dn2entry.o \
 	ldbm_bind.o \
 	ldbm_unbind.o \
 	ancestorid.o \
-	ldbm_attrcrypt.o
+	ldbm_attrcrypt.o \
+	dbhelp.o
 
 OBJS = $(addprefix $(OBJDEST)/, $(LIBBACK_LDBM_OBJS))
 

+ 3 - 1
ldap/servers/slapd/back-ldbm/archive.c

@@ -11,6 +11,7 @@ int ldbm_back_archive2ldbm( Slapi_PBlock *pb )
 {
     struct ldbminfo    *li;
     char *directory = NULL;
+	char *backendname = NULL;
     int return_value = -1;
     int task_flags = 0;
     int run_from_cmdline = 0;
@@ -19,6 +20,7 @@ int ldbm_back_archive2ldbm( Slapi_PBlock *pb )
 
     slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
     slapi_pblock_get( pb, SLAPI_SEQ_VAL, &directory );
+	slapi_pblock_get( pb, SLAPI_BACKEND_INSTANCE_NAME, &backendname);
     slapi_pblock_get( pb, SLAPI_BACKEND_TASK, &task );
     slapi_pblock_get( pb, SLAPI_TASK_FLAGS, &task_flags );
     li->li_flags = run_from_cmdline = (task_flags & TASK_RUNNING_FROM_COMMANDLINE);
@@ -122,7 +124,7 @@ int ldbm_back_archive2ldbm( Slapi_PBlock *pb )
     }
 
     /* tell the database to restore */
-    return_value = dblayer_restore(li, directory, task);
+    return_value = dblayer_restore(li, directory, task, backendname);
     if (0 != return_value) {
         LDAPDebug( LDAP_DEBUG_ANY,
                   "archive2db: Failed to read backup file set. "

+ 271 - 0
ldap/servers/slapd/back-ldbm/dbhelp.c

@@ -0,0 +1,271 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2005 Red Hat Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* 
+ * File for helper functions related to BerkeleyDB.
+ * This exists because dblayer.c is 5k+ lines long, 
+ * so it seems time to move code to a new file.
+ */
+
+#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)
+{
+	int retval = 0;
+	int retval_cleanup = 0;
+	DB *source_file = NULL;
+	DB *destination_file = NULL;
+	DBC *source_cursor = NULL;
+	int dbtype = 0;
+	int dbflags = 0;
+	int dbpagesize = 0;
+	int cursor_flag = 0;
+	int finished = 0;
+	int mode = 0;
+
+	if (priv->dblayer_file_mode)
+		mode = priv->dblayer_file_mode;
+	dblayer_set_env_debugging(env, priv);
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> dblayer_copy_file_keybykey\n", 0, 0, 0 );
+	/* Open the source file */
+	retval = db_create(&source_file, env, 0);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Create error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	retval = source_file->open(source_file, NULL, source_file_name, NULL, DB_UNKNOWN, DB_RDONLY, 0);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Open error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	/* Get the info we need from the source file */
+	retval = source_file->get_flags(source_file, &dbflags);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, get_flags error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	retval = source_file->get_type(source_file, &dbtype);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, get_type error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	retval = source_file->get_pagesize(source_file, &dbpagesize);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, get_pagesize error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	/* Open the destination file
+	 * and make sure that it has the correct page size, the correct access method, and the correct flags (dup etc)
+	 */
+	retval = db_create(&destination_file, env, 0);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Create error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	retval = destination_file->set_flags(destination_file,dbflags);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, set_flags error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	retval = destination_file->set_pagesize(destination_file,dbpagesize);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, set_pagesize error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	retval = destination_file->open(destination_file, NULL, destination_file_name, NULL, dbtype, DB_CREATE | DB_EXCL, mode);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Open error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	/* Open a cursor on the source file */
+	retval = source_file->cursor(source_file,NULL,&source_cursor,0);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Create cursor error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	/* Seek to the first key */
+	cursor_flag = DB_FIRST;
+	/* Loop seeking to the next key until they're all done */
+	while (!finished) {
+		DBT key = {0};
+		DBT data = {0};
+		retval = source_cursor->c_get(source_cursor, &key, &data, cursor_flag);
+		if (retval) {
+			/* DB_NOTFOUND is expected when we find the end, log a message for any other error.
+			 * In either case, set finished=1 so we can hop down and close the cursor. */
+			if ( DB_NOTFOUND != retval )
+			{
+				LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, c_get error %d: %s\n", retval, db_strerror(retval), 0);
+				goto error;
+			}
+			retval = 0; /* DB_NOTFOUND was OK... */
+			finished = 1;
+		} else {
+			/* For each key, insert into the destination file */
+			retval = destination_file->put(destination_file, NULL, &key, &data, 0);
+			if (retval) {
+				LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, put error %d: %s\n", retval, db_strerror(retval), 0);
+				goto error;
+			}
+			cursor_flag = DB_NEXT;
+		}
+	}
+
+error:
+	/* Close the cursor */
+	if (source_cursor) {
+		retval_cleanup = source_cursor->c_close(source_cursor);
+		if (retval_cleanup) {
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Close cursor error %d: %s\n", retval_cleanup, db_strerror(retval_cleanup), 0);
+			retval += retval_cleanup;
+		}
+	}
+	/* Close the source file */
+	if (source_file) {
+		retval_cleanup = source_file->close(source_file,0);
+		source_file = NULL;
+		if (retval_cleanup) {
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Close error %d: %s\n", retval_cleanup, db_strerror(retval_cleanup), 0); 
+			retval += retval_cleanup;
+		}
+	}
+	/* Close the destination file */
+	if (destination_file) {
+		retval_cleanup = destination_file->close(destination_file,0);
+		destination_file = NULL;
+		if (retval_cleanup) {
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_keybykey, Close error %d: %s\n", retval_cleanup, db_strerror(retval_cleanup), 0);
+			retval += retval_cleanup;
+		}
+	}
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= dblayer_copy_file_keybykey\n", 0, 0, 0 );
+	return retval;
+}
+
+int dblayer_copy_file_resetlsns(char *home_dir ,char *source_file_name, char *destination_file_name, int overwrite, dblayer_private *priv)
+{
+	int retval = 0;
+	int mode = 0;
+	DB_ENV *env = NULL;
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> dblayer_copy_file_resetlsns\n", 0, 0, 0 );
+	/* Make the environment */
+
+	if (priv->dblayer_file_mode)
+		mode = priv->dblayer_file_mode;
+	retval = dblayer_make_private_simple_env(home_dir,&env);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_resetlsns: Call to dblayer_make_private_simple_env failed!\n" 
+			"Unable to open an environment.", 0, 0, 0);
+	}
+	/* Do the copy */
+	retval = dblayer_copy_file_keybykey(env, source_file_name, destination_file_name, overwrite, priv);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_resetlsns: Copy not completed successfully.", 0, 0, 0);
+	}
+	/* Close the environment */
+	if (env) {
+		int retval2 = 0;
+		retval2 = env->close(env,0);
+		if (retval2) {
+			if (0 == retval) {
+				retval = retval2;
+				LDAPDebug(LDAP_DEBUG_ANY, "dblayer_copy_file_resetlsns, error %d: %s\n", retval, db_strerror(retval), 0);
+			}
+		}
+	}
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= dblayer_copy_file_resetlsns\n", 0, 0, 0 );
+	return retval;
+}
+
+void dblayer_set_env_debugging(DB_ENV *pEnv, dblayer_private *priv)
+{
+	pEnv->set_errpfx(pEnv, "ns-slapd");
+    if (priv->dblayer_verbose) {
+        pEnv->set_verbose(pEnv, DB_VERB_CHKPOINT, 1);    /* 1 means on */
+        pEnv->set_verbose(pEnv, DB_VERB_DEADLOCK, 1);    /* 1 means on */
+        pEnv->set_verbose(pEnv, DB_VERB_RECOVERY, 1);    /* 1 means on */
+        pEnv->set_verbose(pEnv, DB_VERB_WAITSFOR, 1);    /* 1 means on */
+    }
+    if (priv->dblayer_debug) {
+        pEnv->set_errcall(pEnv, dblayer_log_print);
+    }
+
+}
+
+/* Make an environment to be used for isolated recovery (e.g. during a partial restore operation) */
+int dblayer_make_private_recovery_env(char *db_home_dir, dblayer_private *priv, DB_ENV **env)
+{
+	int retval = 0;
+	DB_ENV *ret_env = NULL;
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> dblayer_make_private_recovery_env\n", 0, 0, 0 );
+	if (NULL == env) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_make_private_recovery_env: Null environment.  Cannot continue.", 0, 0, 0);
+		return -1;
+	}
+	*env = NULL;
+
+	retval = db_env_create(&ret_env,0);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_make_private_recovery_env, Create error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+	dblayer_set_env_debugging(ret_env, priv);
+
+	retval = ret_env->open(ret_env,db_home_dir, DB_INIT_TXN | DB_RECOVER_FATAL | DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE,0);
+	if (0 == retval) {
+		*env = ret_env;
+	} else {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_make_private_recovery_env, Open error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+
+error:
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= dblayer_make_private_recovery_env\n", 0, 0, 0 );
+	return retval;
+}
+
+/* Make an environment to be used for simple non-transacted database operations, e.g. fixup during upgrade */
+int dblayer_make_private_simple_env(char *db_home_dir, DB_ENV **env)
+{
+	int retval = 0;
+	DB_ENV *ret_env = NULL;
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> dblayer_make_private_simple_env\n", 0, 0, 0 );
+	if (NULL == env) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_make_private_simple_env: Null environment.  Cannot continue.", 0, 0, 0);
+		return -1;
+	}
+	*env = NULL;
+
+	retval = db_env_create(&ret_env,0);
+	if (retval) {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_make_private_simple_env, error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+
+	retval = ret_env->open(ret_env,db_home_dir,DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE,0);
+	if (0 == retval) {
+		*env = ret_env;
+	} else {
+		LDAPDebug(LDAP_DEBUG_ANY, "dblayer_make_private_simple_env, error %d: %s\n", retval, db_strerror(retval), 0);
+		goto error;
+	}
+
+error:
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= dblayer_make_private_simple_env\n", 0, 0, 0 );
+	return retval;
+}
+
+char* last_four_chars(const char* s)
+{
+    size_t l = strlen(s);
+    return ((char*)s + (l - 4));
+}

+ 362 - 64
ldap/servers/slapd/back-ldbm/dblayer.c

@@ -131,6 +131,9 @@
 #endif
 #endif
 
+#define NEWDIR_MODE 0755
+
+
 static int perf_threadmain(void *param);
 static int checkpoint_threadmain(void *param);
 static int trickle_threadmain(void *param);
@@ -142,6 +145,7 @@ static int dblayer_override_libdb_functions(DB_ENV *pEnv, dblayer_private *priv)
 static int dblayer_force_checkpoint(struct ldbminfo *li);
 static int log_flush_threadmain(void *param);
 static int dblayer_delete_transaction_logs(const char * log_dir);
+static int dblayer_is_logfilename(const char* path);
 
 static int dblayer_start_log_flush_thread(dblayer_private *priv);
 static int dblayer_start_deadlock_thread(struct ldbminfo *li);
@@ -152,7 +156,6 @@ static int trans_batch_count=1;
 static int trans_batch_limit=0;
 static PRBool log_flush_thread=PR_FALSE;
 static int dblayer_db_remove_ex(dblayer_private_env *env, char const path[], char const dbName[], PRBool use_lock);
-static char* last_four_chars(const char* s);
 
 #define MEGABYTE (1024 * 1024)
 #define GIGABYTE (1024 * MEGABYTE)
@@ -285,7 +288,7 @@ static void dblayer_reset_env(struct ldbminfo *li)
 }
 
 /* Callback function for libdb to spit error info into our log */
-static void dblayer_log_print(const char* prefix, char *buffer)
+void dblayer_log_print(const char* prefix, char *buffer)
 {
     /* We ignore the prefix since we know who we are anyway */
     LDAPDebug(LDAP_DEBUG_ANY,"libdb: %s\n", buffer, 0, 0);    
@@ -605,21 +608,13 @@ static void dblayer_init_dbenv(DB_ENV *pEnv, dblayer_private *priv)
     dblayer_select_ncache(mysize, &myncache); 
     priv->dblayer_ncache = myncache;
 
-    pEnv->set_errpfx(pEnv, "ns-slapd");
+    dblayer_set_env_debugging(pEnv,priv);
+
     pEnv->set_lg_max(pEnv, priv->dblayer_logfile_size);
     pEnv->set_cachesize(pEnv, mysize / GIGABYTE, mysize % GIGABYTE, myncache);
     pEnv->set_lk_max_locks(pEnv, priv->dblayer_lock_config);
     pEnv->set_lk_max_objects(pEnv, priv->dblayer_lock_config);
     pEnv->set_lk_max_lockers(pEnv, priv->dblayer_lock_config); 
-    if (priv->dblayer_verbose) {
-        pEnv->set_verbose(pEnv, DB_VERB_CHKPOINT, 1);    /* 1 means on */
-        pEnv->set_verbose(pEnv, DB_VERB_DEADLOCK, 1);    /* 1 means on */
-        pEnv->set_verbose(pEnv, DB_VERB_RECOVERY, 1);    /* 1 means on */
-        pEnv->set_verbose(pEnv, DB_VERB_WAITSFOR, 1);    /* 1 means on */
-    }
-    if (priv->dblayer_debug) {
-        pEnv->set_errcall(pEnv, dblayer_log_print);
-    }
 
     /* shm_key required for named_regions (DB_SYSTEM_MEM) */
     pEnv->set_shm_key(pEnv, priv->dblayer_shm_key);
@@ -3972,8 +3967,12 @@ static int dblayer_force_checkpoint(struct ldbminfo *li)
   return ret;
 }
 
-
-static int _dblayer_delete_instance_dir(ldbm_instance *inst)
+/* TEL:  Added startdb flag.  If set (1), the DB environment will be started so
+ * that dblayer_db_remove_ex will be used to remove the database files instead
+ * of simply deleting them.  That is important when doing a selective restoration
+ * of a single backend (FRI).  If not set (0), the traditional remove is used.
+ */
+static int _dblayer_delete_instance_dir(ldbm_instance *inst, int startdb)
 {
     PRDir *dirhandle = NULL;
     PRDirEntry *direntry = NULL;
@@ -3987,6 +3986,16 @@ static int _dblayer_delete_instance_dir(ldbm_instance *inst)
 
     if (NULL != li)
     {
+		if (startdb)
+		{
+			rval = dblayer_start(li, DBLAYER_NORMAL_MODE);
+			if (rval)
+			{
+				LDAPDebug(LDAP_DEBUG_ANY, "_dblayer_delete_instance_dir: dblayer_start failed! %s (%d)\n",
+					dblayer_strerror(rval), rval, 0);
+				goto done;
+			}
+		}
         priv = (dblayer_private*)li->li_dblayer_private;
         if (NULL != priv)
         {
@@ -4045,6 +4054,15 @@ static int _dblayer_delete_instance_dir(ldbm_instance *inst)
         }
     }
     PR_CloseDir(dirhandle);
+	if (pEnv && startdb)
+	{
+		rval = dblayer_close(li, DBLAYER_NORMAL_MODE);
+		if (rval)
+		{
+			LDAPDebug(LDAP_DEBUG_ANY, "_dblayer_delete_instance_dir: dblayer_close failed! %s (%d)\n",
+				dblayer_strerror(rval), rval, 0);
+		}
+	}
 done:
     /* remove the directory itself too */
     if (0 == rval)
@@ -4067,20 +4085,23 @@ int dblayer_delete_instance_dir(backend *be)
     return ret;
   } else {
     ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
-    return _dblayer_delete_instance_dir(inst);
+    return _dblayer_delete_instance_dir(inst, 0);
   }
 }
 
-/* delete an entire db/ directory, including all instances under it!
+
+/* delete an entire db/ directory, including either all instances, or
+ * just a single instance (leaving the others intact), if the instance param is non-NULL !
  * this is used mostly for restores.
  * dblayer is assumed to be closed.
  */
-int dblayer_delete_database(struct ldbminfo *li)
+int dblayer_delete_database_ex(struct ldbminfo *li, char *instance)
 {
     dblayer_private *priv = NULL;
     Object *inst_obj;
     PRDir *dirhandle = NULL;
     PRDirEntry *direntry = NULL;
+	PRFileInfo fileinfo;
     char filename[MAXPATHLEN];
     char *log_dir;
     int ret;
@@ -4095,13 +4116,25 @@ int dblayer_delete_database(struct ldbminfo *li)
         ldbm_instance *inst = (ldbm_instance *)object_get_data(inst_obj);
 
         if (inst->inst_be->be_instance_info != NULL) {
-            ret = _dblayer_delete_instance_dir(inst);
-            if (ret != 0)
-            {
-                LDAPDebug(LDAP_DEBUG_ANY,
-                "dblayer_delete_database: WARNING _dblayer_delete_instance_dir failed (%d)\n", ret, 0, 0);
-                return ret;
-            }
+			if ((NULL != instance) && (strcmp(inst->inst_name,instance) != 0)) 
+			{
+				LDAPDebug(LDAP_DEBUG_ANY,
+					"dblayer_delete_database: skipping instance %s\n",inst->inst_name , 0, 0);	
+			} else 
+			{
+				if (NULL == instance)
+				{
+					ret = _dblayer_delete_instance_dir(inst, 0 /* Do not start DB environment: traditional */);
+				} else {
+					ret = _dblayer_delete_instance_dir(inst, 1 /* Start DB environment: for FRI */);
+				}
+				if (ret != 0)
+				{
+					LDAPDebug(LDAP_DEBUG_ANY,
+					"dblayer_delete_database: WARNING _dblayer_delete_instance_dir failed (%d)\n", ret, 0, 0);
+					return ret;
+				}	
+			}
         }
     }
 
@@ -4116,11 +4149,26 @@ int dblayer_delete_database(struct ldbminfo *li)
     }
     while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT |
                                           PR_SKIP_DOT_DOT))) {
+		int rval_tmp = 0;
         if (! direntry->name)
             break;
-        sprintf(filename, "%s/%s", priv->dblayer_home_directory,
-                direntry->name);
-        PR_Delete(filename);
+
+		sprintf(filename, "%s/%s", priv->dblayer_home_directory, 
+			direntry->name);
+
+		/* Do not call PR_Delete on the instance directories if they exist.
+		 * It would not work, but we still should not do it. */
+        rval_tmp = PR_GetFileInfo(filename, &fileinfo);
+        if (rval_tmp == PR_SUCCESS && fileinfo.type != PR_FILE_DIRECTORY)
+		{
+			/* Skip deleting log files; that should be handled below.
+			 * (Note, we don't want to use "filename," because that is qualified and would
+			 * not be compatibile with what dblayer_is_logfilename expects.) */
+			if (!dblayer_is_logfilename(direntry->name))
+			{       
+				PR_Delete(filename);
+			}
+		}
     }
 
     PR_CloseDir(dirhandle);
@@ -4134,16 +4182,28 @@ int dblayer_delete_database(struct ldbminfo *li)
     {
         log_dir = dblayer_get_home_dir(li, NULL);
     }
-    ret = dblayer_delete_transaction_logs(log_dir);
-    if(ret) {
-      LDAPDebug(LDAP_DEBUG_ANY,
-      "dblayer_delete_database: dblayer_delete_transaction_logs failed (%d)\n",
-      ret, 0, 0);
-      return -1;
-    }
+	if (instance == NULL)
+	{
+		ret = dblayer_delete_transaction_logs(log_dir);
+		if(ret) {
+		  LDAPDebug(LDAP_DEBUG_ANY,
+		  "dblayer_delete_database: dblayer_delete_transaction_logs failed (%d)\n",
+		  ret, 0, 0);
+		  return -1;
+		}
+	}
     return 0;
 }
 
+/* delete an entire db/ directory, including all instances under it!
+ * this is used mostly for restores.
+ * dblayer is assumed to be closed.
+ */
+int dblayer_delete_database(struct ldbminfo *li)
+{
+	return dblayer_delete_database_ex(li, NULL);
+}
+
 
 /* 
  * Return the size of the database (in kilobytes).  XXXggood returning
@@ -4207,11 +4267,6 @@ int dblayer_database_size(struct ldbminfo *li, unsigned int *size)
     return return_value;
 }
 
-static char* last_four_chars(const char* s)
-{
-    size_t l = strlen(s);
-    return ((char*)s + (l - 4));
-}
 
 static int count_dbfiles_in_dir(char *directory, int *count, int recurse)
 {
@@ -4365,6 +4420,8 @@ error:
  * The argument cnt is used to count the number of files that were copied.
  *
  * This function is used during db2bak and bak2db.
+ * DBDB added resetlsns arg which is used in partial restore (because the LSNs need to be reset to avoid
+ * confusing transaction logging code).
  */
 int dblayer_copy_directory(struct ldbminfo *li,
                            Slapi_Task *task,
@@ -4373,7 +4430,8 @@ int dblayer_copy_directory(struct ldbminfo *li,
                            int restore,
                            int *cnt,
                            int instance_dir_flag,
-                           int indexonly)
+                           int indexonly,
+						   int resetlsns)
 {
     dblayer_private *priv = NULL;
     char            *new_src_dir = NULL;
@@ -4523,8 +4581,14 @@ int dblayer_copy_directory(struct ldbminfo *li,
             }
 
             /* copy filename1 to filename2 */
-            return_value = dblayer_copyfile(filename1, filename2,
+			/* If the file is a database file, and resetlsns is set, then we need to do a key by key copy */
+			if (strcmp(LDBM_FILENAME_SUFFIX, last_four_chars(filename1)) == 0 && resetlsns) {
+				return_value = dblayer_copy_file_resetlsns(src_dir, filename1, filename2,
+                                            0, priv);
+			} else {
+				return_value = dblayer_copyfile(filename1, filename2,
                                             0, priv->dblayer_file_mode);
+			}
             slapi_ch_free((void**)&filename1);
             slapi_ch_free((void**)&filename2);
             if (0 > return_value)
@@ -4640,7 +4704,7 @@ int dblayer_backup(struct ldbminfo *li, char *dest_dir, Slapi_Task *task)
             inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
                                                     inst_dir, MAXPATHLEN);
             return_value = dblayer_copy_directory(li, task, inst_dirp,
-                                        dest_dir, 0 /* backup */, &cnt, 0, 0);
+                                        dest_dir, 0 /* backup */, &cnt, 0, 0, 0);
             if (return_value != 0) {
                 LDAPDebug(LDAP_DEBUG_ANY,
                     "ERROR: error copying directory (%s -> %s): err=%d\n",
@@ -4895,9 +4959,198 @@ static int doskip(const char *filename)
     return 0;
 }
 
+static int dblayer_copy_dirand_contents(char* src_dir, char* dst_dir, int mode, Slapi_Task *task)
+{
+  int return_value  = 0;
+  int tmp_rval;
+  char filename1[MAXPATHLEN];
+  char filename2[MAXPATHLEN];
+  PRDir *dirhandle = NULL;
+  PRDirEntry *direntry = NULL;
+  PRFileInfo info;
+
+  dirhandle = PR_OpenDir(src_dir);
+  if (NULL != dirhandle) 
+  {
+
+	while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT))) 
+	{
+      if (NULL == direntry->name) {
+         /* NSPR doesn't behave like the docs say it should */
+        break;
+      }
+
+
+       sprintf(filename1, "%s/%s", src_dir, direntry->name);
+       sprintf(filename2, "%s/%s", dst_dir, direntry->name);
+       LDAPDebug(LDAP_DEBUG_ANY, "Moving file %s\n",
+                          filename2, 0, 0);
+      /* Is this entry a directory? */
+      tmp_rval = PR_GetFileInfo(filename1, &info);
+      if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) 
+	  {
+		 PR_MkDir(filename2,NEWDIR_MODE);
+		   return_value = dblayer_copy_dirand_contents(filename1, filename2,
+                                               mode,task);
+			if (return_value) 
+			{
+				if (task) 
+				{
+					slapi_task_log_notice(task,
+					"Failed to copy directory %s", filename1);
+				}
+				break;
+			}
+	   } else {
+			if (task) 
+			{
+				slapi_task_log_notice(task, "Moving file %s",
+                                          filename2);
+				slapi_task_log_status(task, "Moving file %s",
+                                          filename2);
+			}
+			return_value = dblayer_copyfile(filename1, filename2, 0,
+                                               mode);
+	   }
+       if (0 > return_value)
+         break;
+                
+    }
+  }
+  return return_value;
+}
+
+static int dblayer_fri_trim(char *fri_dir_path, char* bename)
+{
+	int retval = 0;
+	int tmp_rval;
+	char filename[MAXPATHLEN];
+	PRDir *dirhandle = NULL;
+	PRDirEntry *direntry = NULL;
+	PRFileInfo info;
+
+	dirhandle = PR_OpenDir(fri_dir_path);
+	if (NULL != dirhandle) 
+	{
+
+		while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT))) 
+		{
+			if (NULL == direntry->name) {
+			/* NSPR doesn't behave like the docs say it should */
+			break;
+			}
+
+			sprintf(filename, "%s/%s", fri_dir_path, direntry->name);
+
+			/* Is this entry a directory? */
+			tmp_rval = PR_GetFileInfo(filename, &info);
+			if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type)
+			{
+				if(strcmp(direntry->name,bename)!=0)
+				{
+					LDAPDebug(LDAP_DEBUG_ANY, "Removing file %s from staging area\n",
+                         filename, 0, 0);
+					ldbm_delete_dirs(filename);
+				}
+				continue;
+			}
+
+			if ((strcmp(direntry->name,"DBVERSION") == 0)||
+				    (strncmp(direntry->name,"__",2) == 0)||
+					(strncmp(direntry->name,"log",3) == 0)){
+				LDAPDebug(LDAP_DEBUG_ANY, "Removing file %s from staging area\n",
+                         filename, 0, 0);
+				PR_Delete(filename);
+			}
+
+		}
+	}
+	PR_CloseDir(dirhandle);
+	return retval;
+}
+
+/* Recover a stand-alone environment , used in filesystem replica intialization restore */
+static int dblayer_recover_environment_path(char *dbhome_dir, dblayer_private *priv)
+{
+	int retval = 0;
+	DB_ENV *env = NULL;
+	/* Make an environment for recovery */
+	retval = dblayer_make_private_recovery_env(dbhome_dir, priv, &env);
+	if (retval) {
+		goto error;
+	}
+	if (env) {
+		retval = env->close(env,0);
+		if (retval) {
+		}
+	}
+error:
+	return retval;
+}
+
+
+static int dblayer_fri_restore(char *home_dir, char *src_dir, dblayer_private *priv, Slapi_Task *task, char** new_src_dir, char* bename)
+{
+		int retval = 0;
+		size_t fribak_dir_length = 0;
+		char *fribak_dir_path = NULL;
+		char *fribak_dir_name = "fribak";
+		int mode = priv->dblayer_file_mode;
+
+		*new_src_dir = NULL;
+
+
+		/* First create the recovery directory */
+		fribak_dir_length = strlen(home_dir) + strlen(fribak_dir_name) + 4; /* 4 for the '/../' */
+		fribak_dir_path = (char *) slapi_ch_malloc(fribak_dir_length + 1); /* add one for the terminator */		
+		sprintf(fribak_dir_path,"%s/../%s",home_dir,fribak_dir_name);
+		if((-1 == PR_MkDir(fribak_dir_path,NEWDIR_MODE)))
+		{
+		  LDAPDebug(LDAP_DEBUG_ANY, "dblayer_fri_restore: %s exists\n",fribak_dir_path, 0, 0);
+		  LDAPDebug(LDAP_DEBUG_ANY, "dblayer_fri_restore: Removing %s.\n",fribak_dir_path, 0, 0);
+		  retval = ldbm_delete_dirs(fribak_dir_path);
+		  if (retval)
+		  {
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_fri_restore: Removal of %s failed!\n", fribak_dir_path, 0, 0);
+			goto error;
+		  }
+		  PR_MkDir(fribak_dir_path,NEWDIR_MODE);
+		  if (retval != PR_SUCCESS)
+		  {
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_fri_restore: Creation of %s failed!\n", fribak_dir_path, 0, 0);
+			goto error;
+		  }
+		}
+		/* Next copy over the entire backup file set to the recovery directory */
+		/* We do this because we want to run recovery there, and we need all the files for that */
+		retval = dblayer_copy_dirand_contents(src_dir, fribak_dir_path, mode, task);
+		if (retval) 
+		{
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_fri_restore: Copy contents to %s failed!\n", fribak_dir_path, 0, 0);
+			goto error;
+		}
+		/* Next, run recovery on the files */
+		retval = dblayer_recover_environment_path(fribak_dir_path, priv);
+		if (retval) 
+		{
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_fri_restore: Recovery failed!\n", 0, 0, 0);
+			goto error;
+		}
+		/* Files nicely recovered, next we stip out what we don't need from the backup set */
+		retval = dblayer_fri_trim(fribak_dir_path,bename);
+		if (retval) 
+		{
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_fri_restore: Trim failed!\n", 0, 0, 0);
+			goto error;
+		}
+		*new_src_dir = fribak_dir_path;
+	error:
+		return retval;
+}
+
 /* Destination Directory is an absolute pathname */
 
-int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
+int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char *bename)
 {
     dblayer_private *priv = NULL;
     int return_value  = 0;
@@ -4913,6 +5166,8 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
     int dbmode;
     int action = 0;
     char *home_dir = NULL;
+	char *real_src_dir = NULL;
+	int frirestore = 0; /* Is a an FRI/single instance restore. 0 for no, 1 for yes */
 
     PR_ASSERT(NULL != li);
     priv = (dblayer_private*)li->li_dblayer_private;
@@ -4927,6 +5182,7 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
     PR_Unlock(li->li_config_mutex);
 
     home_dir = dblayer_get_home_dir(li, NULL);
+	
     if (NULL == home_dir)
     {
         LDAPDebug(LDAP_DEBUG_ANY, 
@@ -4949,6 +5205,10 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
         }
     }
 
+	/* If this is a FRI restore, the bename will be non-NULL */
+	if (bename != NULL)
+		frirestore = 1;
+
 	/*
 	 * Check if the target is a superset of the backup.
 	 * If not don't restore any db at all, otherwise
@@ -4961,19 +5221,22 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
 			&& direntry->name)
 		{
             sprintf(filename1, "%s/%s", src_dir, direntry->name);
-            tmp_rval = PR_GetFileInfo(filename1, &info);
-            if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) {
-				inst = ldbm_instance_find_by_name(li, (char *)direntry->name);
-				if ( inst == NULL)
-				{
-					LDAPDebug(LDAP_DEBUG_ANY,
-						"ERROR: target server has no %s configured\n", direntry->name, 0, 0);
-        			if (task) {
-			            slapi_task_log_notice(task,
-						"ERROR: target server has no %s configured\n", direntry->name);
-        			}
-    				PR_CloseDir(dirhandle);
-					return LDAP_UNWILLING_TO_PERFORM;
+			if(!frirestore || strcmp(direntry->name,bename)==0)
+			{
+                tmp_rval = PR_GetFileInfo(filename1, &info);
+                if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) {
+					inst = ldbm_instance_find_by_name(li, (char *)direntry->name);
+					if ( inst == NULL)
+					{
+						LDAPDebug(LDAP_DEBUG_ANY,
+						 "ERROR: target server has no %s configured\n", direntry->name, 0, 0);
+        				if (task) {
+							slapi_task_log_notice(task,
+							"ERROR: target server has no %s configured\n", direntry->name);
+        				}
+    					PR_CloseDir(dirhandle);
+						return LDAP_UNWILLING_TO_PERFORM;
+					}
 				}
 			}
 		}
@@ -4981,17 +5244,33 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
 	}
 
     /* We delete the existing database */
-    return_value = dblayer_delete_database(li);  
+
+    return_value = dblayer_delete_database_ex(li, bename);  
     if (return_value) {
         return return_value;
     }
 
+	if (frirestore) /*if we are restoring a single backend*/
+	{
+		char *new_src_dir = NULL;
+		return_value = dblayer_fri_restore(home_dir,src_dir,priv,task,&new_src_dir,bename);
+		if (return_value) {
+			return return_value;
+		}
+		/* Now modify the src_dir to point to our recovery area and carry on as if nothing had happened... */
+		real_src_dir = new_src_dir;
+	} else 
+	{
+		/* Otherwise use the src_dir from the caller */
+		real_src_dir = src_dir;
+	}
+
     /* We copy the files over from the staging area */
     /* We want to treat the logfiles specially: if there's
      * a log file directory configured, copy the logfiles there
      * rather than to the db dirctory */
     if (0 == return_value) {
-        dirhandle = PR_OpenDir(src_dir);
+        dirhandle = PR_OpenDir(real_src_dir);
         if (NULL != dirhandle) {
             char *restore_dir;
             char *prefix = NULL;
@@ -5004,8 +5283,9 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
                     break;
                 }
 
+
                 /* Is this entry a directory? */
-                sprintf(filename1, "%s/%s", src_dir, direntry->name);
+                sprintf(filename1, "%s/%s", real_src_dir, direntry->name);
                 tmp_rval = PR_GetFileInfo(filename1, &info);
                 if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) {
                     /* This is an instance directory. It contains the *.db#
@@ -5019,9 +5299,9 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
 						continue;
 
                     restore_dir = inst->inst_parent_dir_name;
-
+					/* If we're doing a partial restore, we need to reset the LSNs on the data files */
                     if (dblayer_copy_directory(li, task, filename1,
-                            restore_dir, 1 /* restore */, &cnt, 0, 0) == 0)
+						restore_dir, 1 /* restore */, &cnt, 0, 0, (bename) ? 1 : 0) == 0)
                         continue;
                     else
                     {
@@ -5054,7 +5334,7 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
                     prefix = home_dir;
                 }
                 mkdir_p(prefix, 0700);
-                sprintf(filename1, "%s/%s", src_dir, direntry->name);
+                sprintf(filename1, "%s/%s", real_src_dir, direntry->name);
                 sprintf(filename2, "%s/%s", prefix, direntry->name);
                 LDAPDebug(LDAP_DEBUG_ANY, "Restoring file %d (%s)\n",
                           cnt, filename2, 0);
@@ -5130,12 +5410,13 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
         if (task) {
             slapi_task_log_notice(task, "Failed to init database");
         }
-        return tmp_rval;
+        return_value = tmp_rval;
+		goto error_out;
     }
 
     if (0 == return_value) { /* only when the copyfile succeeded */
         /* check the DSE_* files, if any */
-        tmp_rval = dse_conf_verify(li, src_dir);
+        tmp_rval = dse_conf_verify(li, real_src_dir, bename);
         if (0 != tmp_rval)
             LDAPDebug(LDAP_DEBUG_ANY,
                 "Warning: Unable to verify the index configuration\n", 0, 0, 0);
@@ -5154,6 +5435,22 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
 
     return_value = tmp_rval?tmp_rval:return_value;
 
+error_out:
+	/* Free the restore src dir, but only if we allocated it above */
+	if (real_src_dir != src_dir) {
+		/* If this was an FRI restore and the staging area exists, go ahead and remove it */
+		if (frirestore && PR_Access(real_src_dir, PR_ACCESS_EXISTS) == PR_SUCCESS)
+		{
+			int ret1 = 0;
+			LDAPDebug(LDAP_DEBUG_ANY, "dblayer_restore: Removing staging area %s.\n",real_src_dir, 0, 0);
+			ret1 = ldbm_delete_dirs(real_src_dir);
+			if (ret1)
+			{
+				LDAPDebug(LDAP_DEBUG_ANY, "dblayer_restore: Removal of staging area %s failed!\n", real_src_dir, 0, 0);
+			}
+		}
+		slapi_ch_free((void**)&real_src_dir);
+	}
     return return_value;
 }
 
@@ -5393,3 +5690,4 @@ void dblayer_set_recovery_required(struct ldbminfo *li)
     }
     li->li_dblayer_private->dblayer_recovery_required = 1;
 }
+

+ 18 - 0
ldap/servers/slapd/back-ldbm/dblayer.h

@@ -133,8 +133,26 @@ struct dblayer_private
     int dblayer_lock_config;
 };
 
+void dblayer_log_print(const char* prefix, char *buffer);
+
 int dblayer_db_remove(dblayer_private_env * env, char const path[], char const dbName[]);
 
 int dblayer_delete_indices(ldbm_instance *inst);
 
+/* Helper functions in dbhelp.c */
+
+/* Make an environment to be used for isolated recovery (e.g. during a partial restore operation) */
+int dblayer_make_private_recovery_env(char *db_home_dir, dblayer_private *priv, DB_ENV **env);
+/* Make an environment to be used for simple non-transacted database operations, e.g. fixup during upgrade */
+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);
+/* Turn on the various logging and debug options for DB */
+void dblayer_set_env_debugging(DB_ENV *pEnv, dblayer_private *priv);
+
+/* Return the last four characters of a string; used for comparing extensions. */
+char* last_four_chars(const char* s);
+
 #endif /* _DBLAYER_H_ */

+ 10 - 3
ldap/servers/slapd/back-ldbm/dbversion.c

@@ -43,7 +43,11 @@ dbversion_write(struct ldbminfo *li, const char *directory,
     PRFileDesc *prfd;
     int rc = 0;
 
-    PR_ASSERT(is_fullpath((char *)directory));
+    if (!is_fullpath((char *)directory)) {
+        rc = -1;
+        return rc;
+    }
+        
     mk_dbversion_fullpath(li, directory, filename);
   
     /* Open the file */
@@ -115,7 +119,11 @@ dbversion_read(struct ldbminfo *li, const char *directory,
     int rc = -1;
     char * iter = NULL;
 
-    PR_ASSERT(is_fullpath((char *)directory));
+    if (!is_fullpath((char *)directory)) {
+        rc = -1;
+        return rc;
+    }
+
     mk_dbversion_fullpath(li, directory, filename);
     
     ldbmversion[0]= '\0';
@@ -167,7 +175,6 @@ dbversion_exists(struct ldbminfo *li, const char *directory)
     char filename[ MAXPATHLEN*2 ];
     PRFileDesc *prfd;
 
-    PR_ASSERT(is_fullpath((char *)directory));
     mk_dbversion_fullpath(li, directory, filename);
 
     if (( prfd = PR_Open( filename, PR_RDONLY, SLAPD_DEFAULT_FILE_MODE )) ==

+ 56 - 6
ldap/servers/slapd/back-ldbm/import-threads.c

@@ -1881,7 +1881,7 @@ dse_conf_backup(struct ldbminfo *li, char *dest_dir)
  * [547427] index config must not change between backup and restore
  */
 int
-dse_conf_verify_core(struct ldbminfo *li, char *src_dir, char *file_name, char *filter, char *log_str)
+dse_conf_verify_core(struct ldbminfo *li, char *src_dir, char *file_name, char *filter, char *log_str, char *entry_filter)
 {
     char *filename = NULL;
     int rval = 0;
@@ -1890,6 +1890,7 @@ dse_conf_verify_core(struct ldbminfo *li, char *src_dir, char *file_name, char *
     int curr_lineno = 0;
     int finished = 0;
     int backup_entry_len = 256;
+	char *search_scope = NULL;
     Slapi_Entry **backup_entries = NULL;
     Slapi_Entry **bep = NULL;
     Slapi_Entry **curr_entries = NULL;
@@ -1928,6 +1929,12 @@ dse_conf_verify_core(struct ldbminfo *li, char *src_dir, char *file_name, char *
 
         if (!estr)
             break;
+		
+		if (entry_filter != NULL) /* Single instance restoration */
+		{
+			if (!(int)strstr(estr, entry_filter))
+				continue;
+		}
 
         e = slapi_str2entry(estr, 0);
         slapi_ch_free_string(&estr);
@@ -1951,11 +1958,23 @@ dse_conf_verify_core(struct ldbminfo *li, char *src_dir, char *file_name, char *
        *bep = NULL;
 
     pblock_init(&srch_pb);
-    slapi_search_internal_set_pb(&srch_pb, li->li_plugin->plg_dn,
+
+	if (entry_filter != NULL)
+	{ /* Single instance restoration */
+		int mylen = 0;
+		mylen = strlen(entry_filter) + strlen(li->li_plugin->plg_dn) + 2;
+        search_scope = slapi_ch_malloc(mylen);
+		sprintf(search_scope, "%s,%s", entry_filter, li->li_plugin->plg_dn);
+	} else { /* Normal restoration */
+        search_scope = slapi_ch_strdup(li->li_plugin->plg_dn);
+	}
+
+    slapi_search_internal_set_pb(&srch_pb, search_scope,
         LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, li->li_identity, 0);
     slapi_search_internal_pb(&srch_pb);
     slapi_pblock_get(&srch_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &curr_entries);
 
+
     if (0 != slapi_entries_diff(backup_entries, curr_entries, 1 /* test_all */,
                                 log_str, 1 /* force_update */, li->li_identity))
     {
@@ -1974,6 +1993,10 @@ out:
 
     slapi_ch_free_string(&filename);
 
+	if (search_scope)
+		slapi_ch_free(&search_scope);
+
+
     if (fd > 0)
         close(fd);
 
@@ -1981,12 +2004,39 @@ out:
 }
 
 int
-dse_conf_verify(struct ldbminfo *li, char *src_dir)
+dse_conf_verify(struct ldbminfo *li, char *src_dir, char *bename)
 {
     int rval;
-    rval  = dse_conf_verify_core(li, src_dir, DSE_INSTANCE, DSE_INSTANCE_FILTER,
-                "Instance Config");
+	char *entry_filter = NULL;
+	char *instance_entry_filter = NULL;
+	
+	if (bename != NULL) /* This was a restore of a single backend */
+	{
+		int mylen = 0;
+		/* Entry filter string */
+		mylen = strlen(bename) + strlen("cn=") + 2;
+        entry_filter = slapi_ch_malloc(mylen);
+		sprintf(entry_filter, "cn=%s", bename);
+
+		mylen = 0;
+		/* Instance search filter */
+		mylen = strlen(DSE_INSTANCE_FILTER) + strlen(bename) + strlen("(&(cn=))") + 2;
+        instance_entry_filter = slapi_ch_malloc(mylen);
+		sprintf(instance_entry_filter, "(&%s(cn=%s))", DSE_INSTANCE_FILTER, bename);
+	} else {
+	    instance_entry_filter = slapi_ch_strdup(DSE_INSTANCE_FILTER);
+	}
+
+	rval  = dse_conf_verify_core(li, src_dir, DSE_INSTANCE, instance_entry_filter,
+                "Instance Config", entry_filter);
     rval += dse_conf_verify_core(li, src_dir, DSE_INDEX, DSE_INDEX_FILTER,
-                "Index Config");
+                "Index Config", entry_filter);
+
+	if (entry_filter)
+		slapi_ch_free(&entry_filter);
+	if (instance_entry_filter)
+		slapi_ch_free(&instance_entry_filter);
+
     return rval;
 }
+

+ 2 - 2
ldap/servers/slapd/back-ldbm/ldif2ldbm.c

@@ -2066,7 +2066,7 @@ int ldbm_back_upgradedb(Slapi_PBlock *pb)
                                                   inst_dir, MAXPATHLEN);
             backup_rval = dblayer_copy_directory(li, NULL /* task */,
                                               inst_dirp, dest_dir, 0/*backup*/,
-                                              &cnt, 0, 1);
+                                              &cnt, 0, 1, 0);
             if (inst_dirp != inst_dir)
                 slapi_ch_free_string(&inst_dirp);
             if (backup_rval < 0)
@@ -2210,7 +2210,7 @@ fail1:
                 backup_rval = dblayer_copy_directory(li, NULL /* task */,
                                                      inst->inst_dir_name,
                                                      dest_dir, 1/*restore*/,
-                                                     &cnt, 0, 1);
+                                                     &cnt, 0, 1, 0);
                 if (inst_dirp != inst_dir)
                     slapi_ch_free_string(&inst_dirp);
                 if (backup_rval < 0)

+ 3 - 3
ldap/servers/slapd/back-ldbm/proto-back-ldbm.h

@@ -93,11 +93,11 @@ int dblayer_memp_stat(struct ldbminfo *li, DB_MPOOL_STAT **gsp,DB_MPOOL_FSTAT **
 int dblayer_memp_stat_instance(ldbm_instance *inst, DB_MPOOL_STAT **gsp, DB_MPOOL_FSTAT ***fsp);
 int dblayer_backup(struct ldbminfo *li, char *destination_directory,
                    Slapi_Task *task);
-int dblayer_restore(struct ldbminfo *li, char* source_directory, Slapi_Task *task);
+int dblayer_restore(struct ldbminfo *li, char* source_directory, Slapi_Task *task, char *bename);
 int dblayer_copy_directory(struct ldbminfo *li, Slapi_Task *task,
                            char *instance_dir, char *destination_dir,
                            int restore, int *cnt, int instance_dir_flag,
-                           int indexonly);
+                           int indexonly, int resetlsns);
 int dblayer_copyfile(char* source, char * destination, int overwrite, int mode);
 int dblayer_delete_instance_dir(backend *be);
 int dblayer_delete_database(struct ldbminfo *li);
@@ -570,7 +570,7 @@ int ldbm_ancestorid_move_subtree(
  * import-threads.c
  */
 int dse_conf_backup(struct ldbminfo *li, char *destination_directory);
-int dse_conf_verify(struct ldbminfo *li, char *src_dir);
+int dse_conf_verify(struct ldbminfo *li, char *src_dir, char *bename);
 
 /*
  * ldbm_attrcrypt.c

+ 6 - 1
ldap/servers/slapd/task.c

@@ -1070,7 +1070,8 @@ static int task_restore_add(Slapi_PBlock *pb, Slapi_Entry *e,
 							Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
 {
     Slapi_Backend *be = NULL;
-    const char *cn;
+    const char *cn = NULL;
+    const char *instance_name = NULL;
     const char *archive_dir = NULL;
     const char *my_database_type = NULL;
     const char *database_type = "ldbm database";
@@ -1099,6 +1100,8 @@ static int task_restore_add(Slapi_PBlock *pb, Slapi_Entry *e,
     if (NULL != my_database_type)
         database_type = my_database_type;
 
+    instance_name = fetch_attr(e, "nsInstance", NULL);
+
     /* get backend that has archive2db and the database type matches.  */
     cookie = NULL;
     be = slapi_get_first_backend (&cookie);
@@ -1148,6 +1151,8 @@ static int task_restore_add(Slapi_PBlock *pb, Slapi_Entry *e,
     }
     mypb->pb_seq_val = slapi_ch_strdup(archive_dir);
     mypb->pb_plugin = be->be_database;
+    if (NULL != instance_name)
+        mypb->pb_instance_name = slapi_ch_strdup(instance_name);
     mypb->pb_task = task;
     mypb->pb_task_flags = TASK_RUNNING_AS_TASK;