From c68e5ae9be63d9464efcf37ace117881e63b7d9a Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 30 May 2013 13:06:12 -0700 Subject: [PATCH] Add mdb_env_copyfd() Allow writing backup to an already opened file handle, for piping to tar/gzip/ssh/whatever. --- libraries/liblmdb/lmdb.h | 19 +++++++ libraries/liblmdb/mdb.c | 107 +++++++++++++++++++---------------- libraries/liblmdb/mdb_copy.1 | 8 ++- libraries/liblmdb/mdb_copy.c | 9 ++- 4 files changed, 89 insertions(+), 54 deletions(-) diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h index 53dab20461..9776366c7c 100644 --- a/libraries/liblmdb/lmdb.h +++ b/libraries/liblmdb/lmdb.h @@ -144,6 +144,14 @@ typedef int mdb_mode_t; typedef mode_t mdb_mode_t; #endif +#ifndef _WIN32 + /** An abstraction for a file handle. + * On POSIX systems file handles are small integers. On Windows + * they're opaque pointers. + */ +#define HANDLE int +#endif + /** @defgroup mdb MDB API * @{ * @brief OpenLDAP Lightning Memory-Mapped Database Manager @@ -533,6 +541,17 @@ int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t */ int mdb_env_copy(MDB_env *env, const char *path); + /** @brief Copy an MDB environment to the specified file descriptor. + * + * 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] fd The filedescriptor to write the copy to. It must + * have already been opened for Write access. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copyfd(MDB_env *env, HANDLE fd); + /** @brief Return statistics about the MDB environment. * * @param[in] env An environment handle returned by #mdb_env_create() diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c index bc25bb39f0..eeb0540894 100644 --- a/libraries/liblmdb/mdb.c +++ b/libraries/liblmdb/mdb.c @@ -199,12 +199,6 @@ mdb_sem_wait(sem_t *sem) */ #define ErrCode() errno - /** An abstraction for a file handle. - * On POSIX systems file handles are small integers. On Windows - * they're opaque pointers. - */ -#define HANDLE int - /** A value for an invalid file handle. * Mainly used to initialize file variables and signify that they are * unused. @@ -3631,60 +3625,20 @@ mdb_env_close0(MDB_env *env, int excl) } int -mdb_env_copy(MDB_env *env, const char *path) +mdb_env_copyfd(MDB_env *env, int fd) { MDB_txn *txn = NULL; - int rc, len; + int rc; size_t wsize; - char *lpath, *ptr; + char *ptr; HANDLE newfd = INVALID_HANDLE_VALUE; - if (env->me_flags & MDB_NOSUBDIR) { - lpath = (char *)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 - /* Do the lock/unlock of the reader mutex before starting the * write txn. Otherwise other read txns could block writers. */ rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); if (rc) - goto leave; + return rc; if (env->me_txns) { /* We must start the actual read txn after blocking writers */ @@ -3751,6 +3705,59 @@ mdb_env_copy(MDB_env *env, const char *path) leave: mdb_txn_abort(txn); + return rc; +} + +int +mdb_env_copy(MDB_env *env, const char *path) +{ + int rc, len; + char *lpath; + HANDLE newfd = INVALID_HANDLE_VALUE; + + if (env->me_flags & MDB_NOSUBDIR) { + lpath = (char *)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 + + rc = mdb_env_copyfd(env, newfd); + +leave: if (newfd != INVALID_HANDLE_VALUE) close(newfd); diff --git a/libraries/liblmdb/mdb_copy.1 b/libraries/liblmdb/mdb_copy.1 index 2b3d421e78..b759f68786 100644 --- a/libraries/liblmdb/mdb_copy.1 +++ b/libraries/liblmdb/mdb_copy.1 @@ -5,12 +5,18 @@ mdb_copy \- LMDB environment copy tool .SH SYNOPSIS .B mdb_copy -.I srcpath\ dstpath +.I srcpath\ [dstpath] .SH DESCRIPTION The .B mdb_copy utility copies an LMDB environment. The environment can be copied regardless of whether it is currently in use. + +If +.I dstpath +is specified it must be the path of an empty directory +for storing the backup. Otherwise, the backup will be +written to stdout. .SH DIAGNOSTICS Exit status is zero if no errors occur. Errors result in a non-zero exit status and diff --git a/libraries/liblmdb/mdb_copy.c b/libraries/liblmdb/mdb_copy.c index bd0b859110..a2ac4cc7c1 100644 --- a/libraries/liblmdb/mdb_copy.c +++ b/libraries/liblmdb/mdb_copy.c @@ -21,8 +21,8 @@ int main(int argc,char * argv[]) MDB_env *env; char *envname = argv[1]; - if (argc != 3) { - fprintf(stderr, "usage: %s srcpath dstpath\n", argv[0]); + if (argc<2 || argc>3) { + fprintf(stderr, "usage: %s srcpath [dstpath]\n", argv[0]); exit(EXIT_FAILURE); } @@ -32,7 +32,10 @@ int main(int argc,char * argv[]) if (rc) { printf("mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); } else { - rc = mdb_env_copy(env, argv[2]); + if (argc == 2) + rc = mdb_env_copyfd(env, 1); + else + rc = mdb_env_copy(env, argv[2]); if (rc) printf("mdb_env_copy failed, error %d %s\n", rc, mdb_strerror(rc)); }