From ebb6859ea5f4ca44ca528e49b17800f9992703e7 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 11 Oct 2012 12:21:40 -0700 Subject: [PATCH] Add mdb_copy for backing up a DB environment --- libraries/libmdb/Makefile | 3 +- libraries/libmdb/mdb.c | 96 +++++++++++++++++++++++++++++++++++++ libraries/libmdb/mdb.h | 12 +++++ libraries/libmdb/mdb_copy.c | 43 +++++++++++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 libraries/libmdb/mdb_copy.c diff --git a/libraries/libmdb/Makefile b/libraries/libmdb/Makefile index 796be07..67a2007 100644 --- a/libraries/libmdb/Makefile +++ b/libraries/libmdb/Makefile @@ -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 diff --git a/libraries/libmdb/mdb.c b/libraries/libmdb/mdb.c index 262919a..7d9ea5c 100644 --- a/libraries/libmdb/mdb.c +++ b/libraries/libmdb/mdb.c @@ -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 #include #include @@ -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) { diff --git a/libraries/libmdb/mdb.h b/libraries/libmdb/mdb.h index 8ded737..1735fbd 100644 --- a/libraries/libmdb/mdb.h +++ b/libraries/libmdb/mdb.h @@ -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 index 0000000..c5eb6b5 --- /dev/null +++ b/libraries/libmdb/mdb_copy.c @@ -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 + * . + */ +#include +#include +#include +#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; +}