diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h
index bc3a294..a356538 100644
--- a/libraries/liblmdb/lmdb.h
+++ b/libraries/liblmdb/lmdb.h
@@ -679,6 +679,7 @@ int mdb_env_copyfd(MDB_env *env, mdb_filehandle_t fd);
*
#MDB_CP_COMPACT - Perform compaction while copying: omit free
* pages and sequentially renumber all pages in output. This option
* consumes more CPU and runs more slowly than the default.
+ * Currently it fails if the environment has suffered a page leak.
*
* @return A non-zero error value on failure and 0 on success.
*/
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
index 255e216..28ccb73 100644
--- a/libraries/liblmdb/mdb.c
+++ b/libraries/liblmdb/mdb.c
@@ -9093,6 +9093,7 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd)
mdb_copy my = {0};
MDB_txn *txn = NULL;
pthread_t thr;
+ pgno_t root, new_root;
int rc = MDB_SUCCESS;
#ifdef _WIN32
@@ -9154,10 +9155,12 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd)
*(MDB_meta *)METADATA(mp) = *mm;
mm = (MDB_meta *)METADATA(mp);
- /* Count the number of free pages, subtract from lastpg to find
- * number of active pages
- */
- {
+ /* Set metapage 1 with current main DB */
+ root = new_root = txn->mt_dbs[MAIN_DBI].md_root;
+ if (root != P_INVALID) {
+ /* Count free pages + freeDB pages. Subtract from last_pg
+ * to find the new last_pg, which also becomes the new root.
+ */
MDB_ID freecount = 0;
MDB_cursor mc;
MDB_val key, data;
@@ -9170,19 +9173,26 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd)
txn->mt_dbs[FREE_DBI].md_leaf_pages +
txn->mt_dbs[FREE_DBI].md_overflow_pages;
- /* Set metapage 1 */
- mm->mm_last_pg = txn->mt_next_pgno - freecount - 1;
+ new_root = txn->mt_next_pgno - 1 - freecount;
+ mm->mm_last_pg = new_root;
mm->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI];
- if (mm->mm_last_pg > NUM_METAS-1) {
- mm->mm_dbs[MAIN_DBI].md_root = mm->mm_last_pg;
- mm->mm_txnid = 1;
- } else {
- mm->mm_dbs[MAIN_DBI].md_root = P_INVALID;
- }
+ mm->mm_dbs[MAIN_DBI].md_root = new_root;
+ } else {
+ /* When the DB is empty, handle it specially to
+ * fix any breakage like page leaks from ITS#8174.
+ */
+ mm->mm_dbs[MAIN_DBI].md_flags = txn->mt_dbs[MAIN_DBI].md_flags;
+ }
+ if (root != P_INVALID || mm->mm_dbs[MAIN_DBI].md_flags) {
+ mm->mm_txnid = 1; /* use metapage 1 */
}
+
my.mc_wlen[0] = env->me_psize * NUM_METAS;
my.mc_txn = txn;
- rc = mdb_env_cwalk(&my, &txn->mt_dbs[MAIN_DBI].md_root, 0);
+ rc = mdb_env_cwalk(&my, &root, 0);
+ if (rc == MDB_SUCCESS && root != new_root) {
+ rc = MDB_INCOMPATIBLE; /* page leak or corrupt DB */
+ }
finish:
if (rc)
diff --git a/libraries/liblmdb/mdb_copy.1 b/libraries/liblmdb/mdb_copy.1
index 258affb..4387ac3 100644
--- a/libraries/liblmdb/mdb_copy.1
+++ b/libraries/liblmdb/mdb_copy.1
@@ -36,6 +36,7 @@ Write the library version number to the standard output, and exit.
Compact while copying. Only current data pages will be copied; freed
or unused pages will be omitted from the copy. This option will
slow down the backup process as it is more CPU-intensive.
+Currently it fails if the environment has suffered a page leak.
.TP
.BR \-n
Open LDMB environment(s) which do not use subdirectories.