diff --git a/external/db_drivers/liblmdb/lmdb.h b/external/db_drivers/liblmdb/lmdb.h
index a794c9f7d..0bca3eb74 100644
--- a/external/db_drivers/liblmdb/lmdb.h
+++ b/external/db_drivers/liblmdb/lmdb.h
@@ -311,6 +311,8 @@ typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *rel
#define MDB_NORDAHEAD 0x800000
/** don't initialize malloc'd memory before writing to datafile */
#define MDB_NOMEMINIT 0x1000000
+ /** use the previous snapshot rather than the latest one */
+#define MDB_PREVSNAPSHOT 0x2000000
/** @} */
/** @defgroup mdb_dbi_open Database Flags
@@ -622,6 +624,12 @@ int mdb_env_create(MDB_env **env);
* caller is expected to overwrite all of the memory that was
* reserved in that case.
* This flag may be changed at any time using #mdb_env_set_flags().
+ *
#MDB_PREVSNAPSHOT
+ * Open the environment with the previous snapshot rather than the latest
+ * one. This loses the latest transaction, but may help work around some
+ * types of corruption. If opened with write access, this must be the
+ * only process using the environment. This flag is automatically reset
+ * after a write transaction is successfully committed.
*
* @param[in] mode The UNIX permissions to set on created files and semaphores.
* This parameter is ignored on Windows.
diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c
index 3552bd2a9..377512ebe 100644
--- a/external/db_drivers/liblmdb/mdb.c
+++ b/external/db_drivers/liblmdb/mdb.c
@@ -1468,7 +1468,7 @@ static int mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst);
static int mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata,
pgno_t newpgno, unsigned int nflags);
-static int mdb_env_read_header(MDB_env *env, MDB_meta *meta);
+static int mdb_env_read_header(MDB_env *env, int prev, MDB_meta *meta);
static MDB_meta *mdb_env_pick_meta(const MDB_env *env);
static int mdb_env_write_meta(MDB_txn *txn);
#ifdef MDB_USE_POSIX_MUTEX /* Drop unused excl arg */
@@ -3632,6 +3632,8 @@ done:
return MDB_SUCCESS;
}
+static int ESECT mdb_env_share_locks(MDB_env *env, int *excl);
+
int
mdb_txn_commit(MDB_txn *txn)
{
@@ -3854,6 +3856,15 @@ mdb_txn_commit(MDB_txn *txn)
if ((rc = mdb_env_write_meta(txn)))
goto fail;
end_mode = MDB_END_COMMITTED|MDB_END_UPDATE;
+ if (env->me_flags & MDB_PREVSNAPSHOT) {
+ if (!(env->me_flags & MDB_NOLOCK)) {
+ int excl;
+ rc = mdb_env_share_locks(env, &excl);
+ if (rc)
+ goto fail;
+ }
+ env->me_flags ^= MDB_PREVSNAPSHOT;
+ }
done:
mdb_txn_end(txn, end_mode);
@@ -3867,11 +3878,12 @@ fail:
/** Read the environment parameters of a DB environment before
* mapping it into memory.
* @param[in] env the environment handle
+ * @param[in] prev whether to read the backup meta page
* @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure.
*/
static int ESECT
-mdb_env_read_header(MDB_env *env, MDB_meta *meta)
+mdb_env_read_header(MDB_env *env, int prev, MDB_meta *meta)
{
MDB_metabuf pbuf;
MDB_page *p;
@@ -3922,7 +3934,7 @@ mdb_env_read_header(MDB_env *env, MDB_meta *meta)
return MDB_VERSION_MISMATCH;
}
- if (off == 0 || m->mm_txnid > meta->mm_txnid)
+ if (off == 0 || (prev ? m->mm_txnid < meta->mm_txnid : m->mm_txnid > meta->mm_txnid))
*meta = *m;
}
return 0;
@@ -4131,7 +4143,8 @@ static MDB_meta *
mdb_env_pick_meta(const MDB_env *env)
{
MDB_meta *const *metas = env->me_metas;
- return metas[ metas[0]->mm_txnid < metas[1]->mm_txnid ];
+ return metas[ (metas[0]->mm_txnid < metas[1]->mm_txnid) ^
+ ((env->me_flags & MDB_PREVSNAPSHOT) != 0) ];
}
int ESECT
@@ -4366,7 +4379,7 @@ mdb_fsize(HANDLE fd, mdb_size_t *size)
/** Further setup required for opening an LMDB environment
*/
static int ESECT
-mdb_env_open2(MDB_env *env)
+mdb_env_open2(MDB_env *env, int prev)
{
unsigned int flags = env->me_flags;
int i, newenv = 0, rc;
@@ -4429,7 +4442,7 @@ mdb_env_open2(MDB_env *env)
}
#endif
- if ((i = mdb_env_read_header(env, &meta)) != 0) {
+ if ((i = mdb_env_read_header(env, prev, &meta)) != 0) {
if (i != ENOENT)
return i;
DPUTS("new mdbenv");
@@ -4505,6 +4518,9 @@ mdb_env_open2(MDB_env *env)
#endif
env->me_maxpg = env->me_mapsize / env->me_psize;
+ if (env->me_txns)
+ env->me_txns->mti_txnid = meta.mm_txnid;
+
#if MDB_DEBUG
{
MDB_meta *meta = mdb_env_pick_meta(env);
@@ -4600,9 +4616,6 @@ static int ESECT
mdb_env_share_locks(MDB_env *env, int *excl)
{
int rc = 0;
- MDB_meta *meta = mdb_env_pick_meta(env);
-
- env->me_txns->mti_txnid = meta->mm_txnid;
#ifdef _WIN32
{
@@ -5056,7 +5069,7 @@ fail:
*/
#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_NOMEMINIT)
#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY| \
- MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD)
+ MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD|MDB_PREVSNAPSHOT)
#if VALID_FLAGS & PERSISTENT_FLAGS & (CHANGEABLE|CHANGELESS)
# error "Persistent DB flags & env flags overlap, but both go in mm_flags"
@@ -5178,9 +5191,13 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
rc = mdb_env_setup_locks(env, lpath, mode, &excl);
if (rc)
goto leave;
+ if ((flags & MDB_PREVSNAPSHOT) && !excl) {
+ rc = EAGAIN;
+ goto leave;
+ }
}
- if ((rc = mdb_env_open2(env)) == MDB_SUCCESS) {
+ if ((rc = mdb_env_open2(env, flags & MDB_PREVSNAPSHOT)) == MDB_SUCCESS) {
if (flags & (MDB_RDONLY|MDB_WRITEMAP)) {
env->me_mfd = env->me_fd;
} else {
@@ -5206,7 +5223,7 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
}
}
DPRINTF(("opened dbenv %p", (void *) env));
- if (excl > 0) {
+ if (excl > 0 && !(flags & MDB_PREVSNAPSHOT)) {
rc = mdb_env_share_locks(env, &excl);
if (rc)
goto leave;
diff --git a/external/db_drivers/liblmdb/mdb_copy.1 b/external/db_drivers/liblmdb/mdb_copy.1
index 1e2a97694..401e47abd 100644
--- a/external/db_drivers/liblmdb/mdb_copy.1
+++ b/external/db_drivers/liblmdb/mdb_copy.1
@@ -11,6 +11,8 @@ mdb_copy \- LMDB environment copy tool
.BR \-c ]
[\c
.BR \-n ]
+[\c
+.BR \-v ]
.B srcpath
[\c
.BR dstpath ]
@@ -39,6 +41,10 @@ slow down the backup process as it is more CPU-intensive.
.TP
.BR \-n
Open LDMB environment(s) which do not use subdirectories.
+.TP
+.BR \-v
+Use the previous environment state instead of the latest state.
+This may be useful if the latest state has been corrupted.
.SH DIAGNOSTICS
Exit status is zero if no errors occur.
diff --git a/external/db_drivers/liblmdb/mdb_copy.c b/external/db_drivers/liblmdb/mdb_copy.c
index f37ccbcc2..95a6e7130 100644
--- a/external/db_drivers/liblmdb/mdb_copy.c
+++ b/external/db_drivers/liblmdb/mdb_copy.c
@@ -38,6 +38,8 @@ int main(int argc,char * argv[])
for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) {
if (argv[1][1] == 'n' && argv[1][2] == '\0')
flags |= MDB_NOSUBDIR;
+ else if (argv[1][1] == 'v' && argv[1][2] == '\0')
+ flags |= MDB_PREVSNAPSHOT;
else if (argv[1][1] == 'c' && argv[1][2] == '\0')
cpflags |= MDB_CP_COMPACT;
else if (argv[1][1] == 'V' && argv[1][2] == '\0') {
@@ -48,7 +50,7 @@ int main(int argc,char * argv[])
}
if (argc<2 || argc>3) {
- fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname);
+ fprintf(stderr, "usage: %s [-V] [-c] [-n] [-v] srcpath [dstpath]\n", progname);
exit(EXIT_FAILURE);
}
diff --git a/external/db_drivers/liblmdb/mdb_dump.1 b/external/db_drivers/liblmdb/mdb_dump.1
index 5a647ba2e..a25fb92e9 100644
--- a/external/db_drivers/liblmdb/mdb_dump.1
+++ b/external/db_drivers/liblmdb/mdb_dump.1
@@ -14,6 +14,8 @@ mdb_dump \- LMDB environment export tool
[\c
.BR \-n ]
[\c
+.BR \-v ]
+[\c
.BR \-p ]
[\c
.BR \-a \ |
@@ -42,6 +44,10 @@ names will be listed, no data will be output.
.BR \-n
Dump an LMDB database which does not use subdirectories.
.TP
+.BR \-v
+Use the previous environment state instead of the latest state.
+This may be useful if the latest state has been corrupted.
+.TP
.BR \-p
If characters in either the key or data items are printing characters (as
defined by isprint(3)), output them directly. This option permits users to
diff --git a/external/db_drivers/liblmdb/mdb_dump.c b/external/db_drivers/liblmdb/mdb_dump.c
index 72a469052..7a42bc0b6 100644
--- a/external/db_drivers/liblmdb/mdb_dump.c
+++ b/external/db_drivers/liblmdb/mdb_dump.c
@@ -164,7 +164,7 @@ static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name)
static void usage(char *prog)
{
- fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog);
+ fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-v] [-a|-s subdb] dbpath\n", prog);
exit(EXIT_FAILURE);
}
@@ -188,6 +188,7 @@ int main(int argc, char *argv[])
* -n: use NOSUBDIR flag on env_open
* -p: use printable characters
* -f: write to file instead of stdout
+ * -v: use previous snapshot
* -V: print version and exit
* (default) dump only the main DB
*/
@@ -215,6 +216,9 @@ int main(int argc, char *argv[])
case 'n':
envflags |= MDB_NOSUBDIR;
break;
+ case 'v':
+ envflags |= MDB_PREVSNAPSHOT;
+ break;
case 'p':
mode |= PRINT;
break;
diff --git a/external/db_drivers/liblmdb/mdb_stat.1 b/external/db_drivers/liblmdb/mdb_stat.1
index 351c01757..bf49bd3bd 100644
--- a/external/db_drivers/liblmdb/mdb_stat.1
+++ b/external/db_drivers/liblmdb/mdb_stat.1
@@ -14,6 +14,8 @@ mdb_stat \- LMDB environment status tool
[\c
.BR \-n ]
[\c
+.BR \-v ]
+[\c
.BR \-r [ r ]]
[\c
.BR \-a \ |
@@ -39,6 +41,10 @@ If \fB\-fff\fP is given, display the full list of page IDs in the freelist.
.BR \-n
Display the status of an LMDB database which does not use subdirectories.
.TP
+.BR \-v
+Use the previous environment state instead of the latest state.
+This may be useful if the latest state has been corrupted.
+.TP
.BR \-r
Display information about the environment reader table.
Shows the process ID, thread ID, and transaction ID for each active
diff --git a/external/db_drivers/liblmdb/mdb_stat.c b/external/db_drivers/liblmdb/mdb_stat.c
index b785e7acf..30ec81fea 100644
--- a/external/db_drivers/liblmdb/mdb_stat.c
+++ b/external/db_drivers/liblmdb/mdb_stat.c
@@ -46,7 +46,7 @@ static void prstat(MDB_stat *ms)
static void usage(char *prog)
{
- fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n", prog);
+ fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-v] [-a|-s subdb] dbpath\n", prog);
exit(EXIT_FAILURE);
}
@@ -73,6 +73,7 @@ int main(int argc, char *argv[])
* -f: print freelist info
* -r: print reader info
* -n: use NOSUBDIR flag on env_open
+ * -v: use previous snapshot
* -V: print version and exit
* (default) print stat of only the main DB
*/
@@ -96,6 +97,9 @@ int main(int argc, char *argv[])
case 'n':
envflags |= MDB_NOSUBDIR;
break;
+ case 'v':
+ envflags |= MDB_PREVSNAPSHOT;
+ break;
case 'r':
rdrinfo++;
break;