Add mdb_copy for backing up a DB environment
authorHoward Chu <hyc@symas.com>
Thu, 11 Oct 2012 19:21:40 +0000 (12:21 -0700)
committerHoward Chu <hyc@symas.com>
Thu, 11 Oct 2012 19:24:01 +0000 (12:24 -0700)
libraries/libmdb/Makefile
libraries/libmdb/mdb.c
libraries/libmdb/mdb.h
libraries/libmdb/mdb_copy.c [new file with mode: 0644]

index 796be07..67a2007 100644 (file)
@@ -5,7 +5,7 @@ CFLAGS  = -pthread $(OPT) $(W) $(XCFLAGS)
 LDLIBS =
 SOLIBS =
 
-PROGS  = mdb_stat mtest mtest2 mtest3 mtest4 mtest5
+PROGS  = mdb_stat mdb_copy mtest mtest2 mtest3 mtest4 mtest5
 all:   libmdb.a libmdb.so $(PROGS)
 
 clean:
@@ -22,6 +22,7 @@ libmdb.so:    mdb.o midl.o
        gcc -pthread -shared -o $@ mdb.o midl.o $(SOLIBS)
 
 mdb_stat: mdb_stat.o libmdb.a
+mdb_copy: mdb_copy.o libmdb.a
 mtest:    mtest.o    libmdb.a
 mtest2:        mtest2.o libmdb.a
 mtest3:        mtest3.o libmdb.a
index 262919a..7d9ea5c 100644 (file)
@@ -32,6 +32,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#define _GNU_SOURCE
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/param.h>
@@ -3280,6 +3281,101 @@ mdb_env_close0(MDB_env *env, int excl)
        env->me_lfd = INVALID_HANDLE_VALUE;     /* Mark env as reset */
 }
 
+int
+mdb_env_copy(MDB_env *env, const char *path)
+{
+       MDB_txn *txn = NULL;
+       int rc, len, oflags;
+       size_t wsize;
+       char *lpath, *ptr;
+       HANDLE newfd = INVALID_HANDLE_VALUE;
+
+       if (env->me_flags & MDB_NOSUBDIR) {
+               lpath = path;
+       } else {
+               len = strlen(path);
+               len += sizeof(DATANAME);
+               lpath = malloc(len);
+               if (!lpath)
+                       return ENOMEM;
+               sprintf(lpath, "%s" DATANAME, path);
+       }
+
+       /* The destination path must exist, but the destination file must not.
+        * We don't want the OS to cache the writes, since the source data is
+        * already in the OS cache.
+        */
+#ifdef _WIN32
+       newfd = CreateFile(lpath, GENERIC_WRITE, 0, NULL, CREATE_NEW,
+                               FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH, NULL);
+#else
+       newfd = open(lpath, O_WRONLY|O_CREAT|O_EXCL
+#ifdef O_DIRECT
+               |O_DIRECT
+#endif
+               , 0666);
+#endif
+       if (!(env->me_flags & MDB_NOSUBDIR))
+               free(lpath);
+       if (newfd == INVALID_HANDLE_VALUE) {
+               rc = ErrCode();
+               goto leave;
+       }
+
+#ifdef F_NOCACHE       /* __APPLE__ */
+       rc = fcntl(newfd, F_NOCACHE, 1);
+       if (rc) {
+               rc = ErrCode();
+               goto leave;
+       }
+#endif
+
+       /* Temporarily block writers until we snapshot the meta pages */
+       LOCK_MUTEX_W(env);
+
+       rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
+       if (rc) {
+               UNLOCK_MUTEX_W(env);
+               goto leave;
+       }
+
+       wsize = env->me_psize * 2;
+#ifdef _WIN32
+       {
+               DWORD len;
+               rc = WriteFile(newfd, env->me_map, wsize, &len, NULL);
+               rc = (len == wsize) ? MDB_SUCCESS : ErrCode();
+       }
+#else
+       rc = write(newfd, env->me_map, wsize);
+       rc = (rc == (int)wsize) ? MDB_SUCCESS : ErrCode();
+#endif
+       UNLOCK_MUTEX_W(env);
+
+       if (rc)
+               goto leave;
+
+       ptr = env->me_map + wsize;
+       wsize = txn->mt_next_pgno * env->me_psize - wsize;
+#ifdef _WIN32
+       {
+               DWORD len;
+               rc = WriteFile(newfd, ptr, wsize, &len, NULL);
+               rc = (len == wsize) ? MDB_SUCCESS : ErrCode();
+       }
+#else
+       rc = write(newfd, ptr, wsize);
+       rc = (rc == (int)wsize) ? MDB_SUCCESS : ErrCode();
+#endif
+       mdb_txn_abort(txn);
+
+leave:
+       if (newfd != INVALID_HANDLE_VALUE)
+               close(newfd);
+
+       return rc;
+}
+
 void
 mdb_env_close(MDB_env *env)
 {
index 8ded737..1735fbd 100644 (file)
@@ -450,6 +450,18 @@ int  mdb_env_create(MDB_env **env);
         */
 int  mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mode_t mode);
 
+       /** @brief Copy an MDB environment to the specified path.
+        *
+        * This function may be used to make a backup of an existing environment.
+        * @param[in] env An environment handle returned by #mdb_env_create(). It
+        * must have already been opened successfully.
+        * @param[in] path The directory in which the copy will reside. This
+        * directory must already exist and be writable but must otherwise be
+        * empty.
+        * @return A non-zero error value on failure and 0 on success.
+        */
+int  mdb_env_copy(MDB_env *env, const char *path);
+
        /** @brief Return statistics about the MDB environment.
         *
         * @param[in] env An environment handle returned by #mdb_env_create()
diff --git a/libraries/libmdb/mdb_copy.c b/libraries/libmdb/mdb_copy.c
new file mode 100644 (file)
index 0000000..c5eb6b5
--- /dev/null
@@ -0,0 +1,43 @@
+/* mdb_copy.c - memory-mapped database backup tool */
+/*
+ * Copyright 2012 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "mdb.h"
+
+int main(int argc,char * argv[])
+{
+       int rc;
+       MDB_env *env;
+       char *envname = argv[1];
+
+       if (argc != 3) {
+               fprintf(stderr, "usage: %s srcpath dstpath\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       rc = mdb_env_create(&env);
+
+       rc = mdb_env_open(env, envname, MDB_RDONLY, 0);
+       if (rc) {
+               printf("mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
+       } else {
+               rc = mdb_env_copy(env, argv[2]);
+               if (rc)
+                       printf("mdb_env_copy failed, error %d %s\n", rc, mdb_strerror(rc));
+       }
+       mdb_env_close(env);
+
+       return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}