]> git.sur5r.net Git - openldap/blobdiff - libraries/liblmdb/mdb.c
ITS#8489 reset cursor EOF flag in cursor_set
[openldap] / libraries / liblmdb / mdb.c
index 41f7e25fcc2fdeab11affee3a0e161a3c875fb19..0110402c5b5bbbdcfb860efa783c759cb4480f52 100644 (file)
@@ -5,7 +5,7 @@
  *     BerkeleyDB API, but much simplified.
  */
 /*
- * Copyright 2011-2015 Howard Chu, Symas Corp.
+ * Copyright 2011-2016 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -228,7 +228,7 @@ union semun {
 
 #if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN)
 # error "Unknown or unsupported endianness (BYTE_ORDER)"
-#elif (-6 & 5) || CHAR_BIT != 8 || UINT_MAX < 0xffffffff || ULONG_MAX % 0xFFFF
+#elif (-6 & 5) || CHAR_BIT!=8 || UINT_MAX!=0xffffffff || MDB_SIZE_MAX%UINT_MAX
 # error "Two's complement, reasonably sized integer types, please"
 #endif
 
@@ -303,16 +303,20 @@ union semun {
 #  define MDB_USE_ROBUST       0
 # else
 #  define MDB_USE_ROBUST       1
+# endif
+#endif /* !MDB_USE_ROBUST */
+
+#if defined(MDB_USE_POSIX_MUTEX) && (MDB_USE_ROBUST)
 /* glibc < 2.12 only provided _np API */
-#  if defined(__GLIBC__) && GLIBC_VER < 0x02000c
+#  if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \
+       (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST))
 #   define PTHREAD_MUTEX_ROBUST        PTHREAD_MUTEX_ROBUST_NP
 #   define pthread_mutexattr_setrobust(attr, flag)     pthread_mutexattr_setrobust_np(attr, flag)
 #   define pthread_mutex_consistent(mutex)     pthread_mutex_consistent_np(mutex)
 #  endif
-# endif
-#endif /* MDB_USE_ROBUST */
+#endif /* MDB_USE_POSIX_MUTEX && MDB_USE_ROBUST */
 
-#if defined(MDB_OWNERDEAD) && MDB_USE_ROBUST
+#if defined(MDB_OWNERDEAD) && (MDB_USE_ROBUST)
 #define MDB_ROBUST_SUPPORTED   1
 #endif
 
@@ -335,8 +339,10 @@ typedef HANDLE mdb_mutex_t, mdb_mutexref_t;
 #define pthread_mutex_lock(x)  WaitForSingleObject(*x, INFINITE)
 #define pthread_cond_signal(x) SetEvent(*x)
 #define pthread_cond_wait(cond,mutex)  do{SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE);}while(0)
-#define THREAD_CREATE(thr,start,arg)   thr=CreateThread(NULL,0,start,arg,0,NULL)
-#define THREAD_FINISH(thr)     WaitForSingleObject(thr, INFINITE)
+#define THREAD_CREATE(thr,start,arg) \
+       (((thr) = CreateThread(NULL, 0, start, arg, 0, NULL)) ? 0 : ErrCode())
+#define THREAD_FINISH(thr) \
+       (WaitForSingleObject(thr, INFINITE) ? ErrCode() : 0)
 #define LOCK_MUTEX0(mutex)             WaitForSingleObject(mutex, INFINITE)
 #define UNLOCK_MUTEX(mutex)            ReleaseMutex(mutex)
 #define mdb_mutex_consistent(mutex)    0
@@ -352,12 +358,10 @@ typedef HANDLE mdb_mutex_t, mdb_mutexref_t;
 #else
 #define MDB_PROCESS_QUERY_LIMITED_INFORMATION 0x1000
 #endif
-#define        Z       "I"
 #else
 #define THREAD_RET     void *
 #define THREAD_CREATE(thr,start,arg)   pthread_create(&thr,NULL,start,arg)
 #define THREAD_FINISH(thr)     pthread_join(thr,NULL)
-#define        Z       "z"                     /**< printf format modifier for size_t */
 
        /** For MDB_LOCK_FORMAT: True if readers take a pid lock in the lockfile */
 #define MDB_PIDLOCK                    1
@@ -455,15 +459,9 @@ typedef pthread_mutex_t mdb_mutex_t[1], *mdb_mutexref_t;
 #define        GET_PAGESIZE(x) ((x) = sysconf(_SC_PAGE_SIZE))
 #endif
 
-#ifdef MDB_VL32
-#ifdef _WIN32
-#define        Y       "I64"
-#else
-#define        Y       "ll"
-#endif
-#else
-#define Y      Z
-#endif
+#define        Z       MDB_FMT_Z       /**< printf/scanf format modifier for size_t */
+#define        Yu      MDB_PRIy(u)     /**< printf format for #mdb_size_t */
+#define        Yd      MDB_PRIy(d)     /**< printf format for "signed #mdb_size_t" */
 
 #if defined(_WIN32) || defined(MDB_USE_POSIX_SEM)
 #define MNAME_LEN      32
@@ -873,9 +871,23 @@ typedef struct MDB_txninfo {
          + (((MDB_PIDLOCK) != 0) << 16)))
 /** @} */
 
-/** Common header for all page types.
- * Overflow records occupy a number of contiguous pages with no
- * headers on any page after the first.
+/** Common header for all page types. The page type depends on #mp_flags.
+ *
+ * #P_BRANCH and #P_LEAF pages have unsorted '#MDB_node's at the end, with
+ * sorted #mp_ptrs[] entries referring to them. Exception: #P_LEAF2 pages
+ * omit mp_ptrs and pack sorted #MDB_DUPFIXED values after the page header.
+ *
+ * #P_OVERFLOW records occupy one or more contiguous pages where only the
+ * first has a page header. They hold the real data of #F_BIGDATA nodes.
+ *
+ * #P_SUBP sub-pages are small leaf "pages" with duplicate data.
+ * A node with flag #F_DUPDATA but not #F_SUBDATA contains a sub-page.
+ * (Duplicate data can also go in sub-databases, which use normal pages.)
+ *
+ * #P_META pages contain #MDB_meta, the start point of an LMDB snapshot.
+ *
+ * Each non-metapage up to #MDB_meta.%mm_last_pg is reachable exactly once
+ * in the snapshot: Either used by a database or listed in a freeDB record.
  */
 typedef struct MDB_page {
 #define        mp_pgno mp_p.p_pgno
@@ -884,7 +896,7 @@ typedef struct MDB_page {
                pgno_t          p_pgno; /**< page number */
                struct MDB_page *p_next; /**< for in-memory list of freed pages */
        } mp_p;
-       uint16_t        mp_pad;
+       uint16_t        mp_pad;                 /**< key size if this is a LEAF2 page */
 /**    @defgroup mdb_page      Page Flags
  *     @ingroup internal
  *     Flags for the page headers.
@@ -951,7 +963,9 @@ typedef struct MDB_page {
        /** The number of overflow pages needed to store the given size. */
 #define OVPAGES(size, psize)   ((PAGEHDRSZ-1 + (size)) / (psize) + 1)
 
-       /** Link in #MDB_txn.%mt_loose_pgs list */
+       /** Link in #MDB_txn.%mt_loose_pgs list.
+        *  Kept outside the page header, which is needed when reusing the page.
+        */
 #define NEXT_LOOSE_PAGE(p)             (*(MDB_page **)((p) + 2))
 
        /** Header for a single key/data pair within a page.
@@ -1034,7 +1048,7 @@ typedef struct MDB_node {
 #ifdef MISALIGNED_OK
 #define COPY_PGNO(dst,src)     dst = src
 #else
-#if SIZE_MAX > 4294967295UL
+#if MDB_SIZE_MAX > 0xffffffffU
 #define COPY_PGNO(dst,src)     do { \
        unsigned short *s, *d;  \
        s = (unsigned short *)&(src);   \
@@ -1075,13 +1089,13 @@ typedef struct MDB_db {
        pgno_t          md_branch_pages;        /**< number of internal pages */
        pgno_t          md_leaf_pages;          /**< number of leaf pages */
        pgno_t          md_overflow_pages;      /**< number of overflow pages */
-       pgno_t          md_entries;             /**< number of data items */
+       mdb_size_t      md_entries;             /**< number of data items */
        pgno_t          md_root;                /**< the root page of this tree */
 } MDB_db;
 
-       /** mdb_dbi_open flags */
 #define MDB_VALID      0x8000          /**< DB handle is valid, for me_dbflags */
 #define PERSISTENT_FLAGS       (0xffff & ~(MDB_VALID))
+       /** #mdb_dbi_open() flags */
 #define VALID_FLAGS    (MDB_REVERSEKEY|MDB_DUPSORT|MDB_INTEGERKEY|MDB_DUPFIXED|\
        MDB_INTEGERDUP|MDB_REVERSEDUP|MDB_CREATE)
 
@@ -1120,7 +1134,10 @@ typedef struct MDB_meta {
 #define        mm_psize        mm_dbs[FREE_DBI].md_pad
        /** Any persistent environment flags. @ref mdb_env */
 #define        mm_flags        mm_dbs[FREE_DBI].md_flags
-       pgno_t          mm_last_pg;                     /**< last used page in file */
+       /** Last used page in the datafile.
+        *      Actually the file may be shorter if the freeDB lists the final pages.
+        */
+       pgno_t          mm_last_pg;
        volatile txnid_t        mm_txnid;       /**< txnid that committed this page */
 } MDB_meta;
 
@@ -1173,7 +1190,7 @@ struct MDB_txn {
         *      in this transaction, linked through #NEXT_LOOSE_PAGE(page).
         */
        MDB_page        *mt_loose_pgs;
-       /* #Number of loose pages (#mt_loose_pgs) */
+       /*Number of loose pages (#mt_loose_pgs) */
        int                     mt_loose_count;
        /** The sorted list of dirty pages we temporarily wrote to disk
         *      because the dirty list was full. page numbers in here are
@@ -1310,6 +1327,11 @@ struct MDB_cursor {
        indx_t          mc_ki[CURSOR_STACK];    /**< stack of page indices */
 #ifdef MDB_VL32
        MDB_page        *mc_ovpg;               /**< a referenced overflow page */
+#      define MC_OVPG(mc)                      ((mc)->mc_ovpg)
+#      define MC_SET_OVPG(mc, pg)      ((mc)->mc_ovpg = (pg))
+#else
+#      define MC_OVPG(mc)                      ((MDB_page *)0)
+#      define MC_SET_OVPG(mc, pg)      ((void)0)
 #endif
 };
 
@@ -1515,13 +1537,18 @@ static int mdb_reader_check0(MDB_env *env, int rlocked, int *dead);
 static MDB_cmp_func    mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int, mdb_cmp_cint, mdb_cmp_long;
 /** @endcond */
 
-/** Compare two items pointing at size_t's of unknown alignment. */
+/** Compare two items pointing at '#mdb_size_t's of unknown alignment. */
 #ifdef MISALIGNED_OK
 # define mdb_cmp_clong mdb_cmp_long
 #else
 # define mdb_cmp_clong mdb_cmp_cint
 #endif
 
+/** True if we need #mdb_cmp_clong() instead of \b cmp for #MDB_INTEGERDUP */
+#define NEED_CMP_CLONG(cmp, ksize) \
+       (UINT_MAX < MDB_SIZE_MAX && \
+        (cmp) == mdb_cmp_int && (ksize) == sizeof(mdb_size_t))
+
 #ifdef _WIN32
 static SECURITY_DESCRIPTOR mdb_null_sd;
 static SECURITY_ATTRIBUTES mdb_all_sa;
@@ -1562,6 +1589,7 @@ static char *const mdb_errstr[] = {
        "MDB_BAD_TXN: Transaction must abort, has a child, or is invalid",
        "MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size",
        "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly",
+       "MDB_PROBLEM: Unexpected problem - txn should abort",
 };
 
 char *
@@ -1572,8 +1600,9 @@ mdb_strerror(int err)
         *      This works as long as no function between the call to mdb_strerror
         *      and the actual use of the message uses more than 4K of stack.
         */
-       char pad[4096];
-       char buf[1024], *ptr = buf;
+#define MSGSIZE        1024
+#define PADSIZE        4096
+       char buf[MSGSIZE+PADSIZE], *ptr = buf;
 #endif
        int i;
        if (!err)
@@ -1605,7 +1634,7 @@ mdb_strerror(int err)
        buf[0] = 0;
        FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
-               NULL, err, 0, ptr, sizeof(buf), (va_list *)pad);
+               NULL, err, 0, ptr, MSGSIZE, (va_list *)buf+MSGSIZE);
        return ptr;
 #else
        return strerror(err);
@@ -1705,20 +1734,20 @@ mdb_page_list(MDB_page *mp)
        case P_LEAF|P_LEAF2:        type = "LEAF2 page";                break;
        case P_LEAF|P_LEAF2|P_SUBP: type = "LEAF2 sub-page";    break;
        case P_OVERFLOW:
-               fprintf(stderr, "Overflow page %"Y"u pages %u%s\n",
+               fprintf(stderr, "Overflow page %"Yu" pages %u%s\n",
                        pgno, mp->mp_pages, state);
                return;
        case P_META:
-               fprintf(stderr, "Meta-page %"Y"u txnid %"Y"u\n",
+               fprintf(stderr, "Meta-page %"Yu" txnid %"Yu"\n",
                        pgno, ((MDB_meta *)METADATA(mp))->mm_txnid);
                return;
        default:
-               fprintf(stderr, "Bad page %"Y"u flags 0x%u\n", pgno, mp->mp_flags);
+               fprintf(stderr, "Bad page %"Yu" flags 0x%X\n", pgno, mp->mp_flags);
                return;
        }
 
        nkeys = NUMKEYS(mp);
-       fprintf(stderr, "%s %"Y"u numkeys %d%s\n", type, pgno, nkeys, state);
+       fprintf(stderr, "%s %"Yu" numkeys %d%s\n", type, pgno, nkeys, state);
 
        for (i=0; i<nkeys; i++) {
                if (IS_LEAF2(mp)) {     /* LEAF2 pages have no mp_ptrs[] or node headers */
@@ -1733,7 +1762,7 @@ mdb_page_list(MDB_page *mp)
                key.mv_data = node->mn_data;
                nsize = NODESIZE + key.mv_size;
                if (IS_BRANCH(mp)) {
-                       fprintf(stderr, "key %d: page %"Y"u, %s\n", i, NODEPGNO(node),
+                       fprintf(stderr, "key %d: page %"Yu", %s\n", i, NODEPGNO(node),
                                DKEY(&key));
                        total += nsize;
                } else {
@@ -1829,7 +1858,7 @@ static void mdb_audit(MDB_txn *txn)
                }
        }
        if (freecount + count + NUM_METAS != txn->mt_next_pgno) {
-               fprintf(stderr, "audit: %"Y"u freecount: %"Y"u count: %"Y"u total: %"Y"u next_pgno: %"Y"u\n",
+               fprintf(stderr, "audit: %"Yu" freecount: %"Yu" count: %"Yu" total: %"Yu" next_pgno: %"Yu"\n",
                        txn->mt_txnid, freecount, count+NUM_METAS,
                        freecount+count+NUM_METAS, txn->mt_next_pgno);
        }
@@ -1846,10 +1875,8 @@ int
 mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
 {
        MDB_cmp_func *dcmp = txn->mt_dbxs[dbi].md_dcmp;
-#if UINT_MAX < SIZE_MAX || defined(MDB_VL32)
-       if (dcmp == mdb_cmp_int && a->mv_size == sizeof(mdb_size_t))
+       if (NEED_CMP_CLONG(dcmp, a->mv_size))
                dcmp = mdb_cmp_clong;
-#endif
        return dcmp(a, b);
 }
 
@@ -1964,8 +1991,14 @@ mdb_cursor_unref(MDB_cursor *mc)
        mc->mc_pg[0] = NULL;
        mc->mc_flags &= ~C_INITIALIZED;
 }
+#define MDB_CURSOR_UNREF(mc, force) \
+       (((force) || ((mc)->mc_flags & C_INITIALIZED)) \
+        ? mdb_cursor_unref(mc) \
+        : (void)0)
+
 #else
 #define MDB_PAGE_UNREF(txn, mp)
+#define MDB_CURSOR_UNREF(mc, force) ((void)0)
 #endif /* MDB_VL32 */
 
 /** Loosen or free a single page.
@@ -1997,7 +2030,7 @@ mdb_page_loose(MDB_cursor *mc, MDB_page *mp)
                                        if (mp != dl[x].mptr) { /* bad cursor? */
                                                mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
                                                txn->mt_flags |= MDB_TXN_ERROR;
-                                               return MDB_CORRUPTED;
+                                               return MDB_PROBLEM;
                                        }
                                        /* ok, it's ours */
                                        loose = 1;
@@ -2009,8 +2042,7 @@ mdb_page_loose(MDB_cursor *mc, MDB_page *mp)
                }
        }
        if (loose) {
-               DPRINTF(("loosen db %d page %"Y"u", DDBI(mc),
-                       mp->mp_pgno));
+               DPRINTF(("loosen db %d page %"Yu, DDBI(mc), mp->mp_pgno));
                NEXT_LOOSE_PAGE(mp) = txn->mt_loose_pgs;
                txn->mt_loose_pgs = mp;
                txn->mt_loose_count++;
@@ -2307,8 +2339,7 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
                np = txn->mt_loose_pgs;
                txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np);
                txn->mt_loose_count--;
-               DPRINTF(("db %d use loose page %"Y"u", DDBI(mc),
-                               np->mp_pgno));
+               DPRINTF(("db %d use loose page %"Yu, DDBI(mc), np->mp_pgno));
                *mp = np;
                return MDB_SUCCESS;
        }
@@ -2410,10 +2441,10 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
                }
                env->me_pglast = last;
 #if (MDB_DEBUG) > 1
-               DPRINTF(("IDL read txn %"Y"u root %"Y"u num %u",
+               DPRINTF(("IDL read txn %"Yu" root %"Yu" num %u",
                        last, txn->mt_dbs[FREE_DBI].md_root, i));
                for (j = i; j; j--)
-                       DPRINTF(("IDL %"Y"u", idl[j]));
+                       DPRINTF(("IDL %"Yu, idl[j]));
 #endif
                /* Merge in descending sorted order */
                mdb_midl_xmerge(mop, idl);
@@ -2583,7 +2614,7 @@ mdb_page_touch(MDB_cursor *mc)
                        (rc = mdb_page_alloc(mc, 1, &np)))
                        goto fail;
                pgno = np->mp_pgno;
-               DPRINTF(("touched db %d page %"Y"u -> %"Y"u", DDBI(mc),
+               DPRINTF(("touched db %d page %"Yu" -> %"Yu, DDBI(mc),
                        mp->mp_pgno, pgno));
                mdb_cassert(mc, mp->mp_pgno != pgno);
                mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno);
@@ -2607,7 +2638,7 @@ mdb_page_touch(MDB_cursor *mc)
                                if (mp != dl[x].mptr) { /* bad cursor? */
                                        mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
                                        txn->mt_flags |= MDB_TXN_ERROR;
-                                       return MDB_CORRUPTED;
+                                       return MDB_PROBLEM;
                                }
                                return 0;
                        }
@@ -2984,7 +3015,7 @@ mdb_txn_renew(MDB_txn *txn)
 
        rc = mdb_txn_renew0(txn);
        if (rc == MDB_SUCCESS) {
-               DPRINTF(("renew txn %"Y"u%c %p on mdbenv %p, root page %"Y"u",
+               DPRINTF(("renew txn %"Yu"%c %p on mdbenv %p, root page %"Yu,
                        txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
                        (void *)txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root));
        }
@@ -3102,7 +3133,7 @@ renew:
        } else {
                txn->mt_flags |= flags; /* could not change txn=me_txn0 earlier */
                *ret = txn;
-               DPRINTF(("begin txn %"Y"u%c %p on mdbenv %p, root page %"Y"u",
+               DPRINTF(("begin txn %"Yu"%c %p on mdbenv %p, root page %"Yu,
                        txn->mt_txnid, (flags & MDB_RDONLY) ? 'r' : 'w',
                        (void *) txn, (void *) env, txn->mt_dbs[MAIN_DBI].md_root));
        }
@@ -3169,7 +3200,7 @@ mdb_txn_end(MDB_txn *txn, unsigned mode)
        /* Export or close DBI handles opened in this txn */
        mdb_dbis_update(txn, mode & MDB_END_UPDATE);
 
-       DPRINTF(("%s txn %"Y"u%c %p on mdbenv %p, root page %"Y"u",
+       DPRINTF(("%s txn %"Yu"%c %p on mdbenv %p, root page %"Yu,
                names[mode & MDB_END_OPMASK],
                txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
                (void *) txn, (void *)env, txn->mt_dbs[MAIN_DBI].md_root));
@@ -3370,10 +3401,10 @@ mdb_freelist_save(MDB_txn *txn)
 #if (MDB_DEBUG) > 1
                        {
                                unsigned int i = free_pgs[0];
-                               DPRINTF(("IDL write txn %"Y"u root %"Y"u num %u",
+                               DPRINTF(("IDL write txn %"Yu" root %"Yu" num %u",
                                        txn->mt_txnid, txn->mt_dbs[FREE_DBI].md_root, i));
                                for (; i; i--)
-                                       DPRINTF(("IDL %"Y"u", free_pgs[i]));
+                                       DPRINTF(("IDL %"Yu, free_pgs[i]));
                        }
 #endif
                        continue;
@@ -3541,7 +3572,7 @@ mdb_page_flush(MDB_txn *txn, int keep)
                 * the write offset, to at least save the overhead of a Seek
                 * system call.
                 */
-               DPRINTF(("committing page %"Z"u", pgno));
+               DPRINTF(("committing page %"Yu, pgno));
                memset(&ov, 0, sizeof(ov));
                ov.Offset = pos & 0xffffffff;
                ov.OffsetHigh = pos >> 16 >> 16;
@@ -3592,7 +3623,7 @@ retry_seek:
                        wpos = pos;
                        wsize = 0;
                }
-               DPRINTF(("committing page %"Y"u", pgno));
+               DPRINTF(("committing page %"Yu, pgno));
                next_pos = pos + size;
                iov[n].iov_len = size;
                iov[n].iov_base = (char *)dp;
@@ -3805,7 +3836,7 @@ mdb_txn_commit(MDB_txn *txn)
                !(txn->mt_flags & (MDB_TXN_DIRTY|MDB_TXN_SPILLS)))
                goto done;
 
-       DPRINTF(("committing txn %"Y"u %p on mdbenv %p, root page %"Y"u",
+       DPRINTF(("committing txn %"Yu" %p on mdbenv %p, root page %"Yu,
            txn->mt_txnid, (void*)txn, (void*)env, txn->mt_dbs[MAIN_DBI].md_root));
 
        /* Update DB root pointers */
@@ -3903,7 +3934,7 @@ mdb_env_read_header(MDB_env *env, MDB_meta *meta)
                p = (MDB_page *)&pbuf;
 
                if (!F_ISSET(p->mp_flags, P_META)) {
-                       DPRINTF(("page %"Y"u not a meta page", p->mp_pgno));
+                       DPRINTF(("page %"Yu" not a meta page", p->mp_pgno));
                        return MDB_INVALID;
                }
 
@@ -4015,7 +4046,7 @@ mdb_env_write_meta(MDB_txn *txn)
 #endif
 
        toggle = txn->mt_txnid & 1;
-       DPRINTF(("writing meta page %d for root page %"Y"u",
+       DPRINTF(("writing meta page %d for root page %"Yu,
                toggle, txn->mt_dbs[MAIN_DBI].md_root));
 
        env = txn->mt_env;
@@ -4159,6 +4190,19 @@ mdb_env_create(MDB_env **env)
        return MDB_SUCCESS;
 }
 
+#ifdef _WIN32
+/** @brief Map a result from an NTAPI call to WIN32. */
+static DWORD
+mdb_nt2win32(NTSTATUS st)
+{
+       OVERLAPPED o = {0};
+       DWORD br;
+       o.Internal = st;
+       GetOverlappedResult(NULL, &o, &br, FALSE);
+       return GetLastError();
+}
+#endif
+
 static int ESECT
 mdb_env_map(MDB_env *env, void *addr)
 {
@@ -4188,7 +4232,7 @@ mdb_env_map(MDB_env *env, void *addr)
 
        rc = NtCreateSection(&mh, access, NULL, NULL, secprot, SEC_RESERVE, env->me_fd);
        if (rc)
-               return rc;
+               return mdb_nt2win32(rc);
        map = addr;
 #ifdef MDB_VL32
        msize = NUM_METAS * env->me_psize;
@@ -4200,7 +4244,7 @@ mdb_env_map(MDB_env *env, void *addr)
        NtClose(mh);
 #endif
        if (rc)
-               return rc;
+               return mdb_nt2win32(rc);
        env->me_map = map;
 #else
 #ifdef MDB_VL32
@@ -4496,13 +4540,13 @@ mdb_env_open2(MDB_env *env)
 
                DPRINTF(("opened database version %u, pagesize %u",
                        meta->mm_version, env->me_psize));
-               DPRINTF(("using meta page %d",    (int) (meta->mm_txnid & 1)));
-               DPRINTF(("depth: %u",             db->md_depth));
-               DPRINTF(("entries: %"Y"u",        db->md_entries));
-               DPRINTF(("branch pages: %"Y"u",   db->md_branch_pages));
-               DPRINTF(("leaf pages: %"Y"u",     db->md_leaf_pages));
-               DPRINTF(("overflow pages: %"Y"u", db->md_overflow_pages));
-               DPRINTF(("root: %"Y"u",           db->md_root));
+               DPRINTF(("using meta page %d",  (int) (meta->mm_txnid & 1)));
+               DPRINTF(("depth: %u",           db->md_depth));
+               DPRINTF(("entries: %"Yu,        db->md_entries));
+               DPRINTF(("branch pages: %"Yu,   db->md_branch_pages));
+               DPRINTF(("leaf pages: %"Yu,     db->md_leaf_pages));
+               DPRINTF(("overflow pages: %"Yu, db->md_overflow_pages));
+               DPRINTF(("root: %"Yu,           db->md_root));
        }
 #endif
 
@@ -4945,18 +4989,29 @@ mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl)
                if (semctl(semid, 0, SETALL, semu) < 0)
                        goto fail_errno;
                env->me_txns->mti_semid = semid;
+               env->me_txns->mti_rlocked = 0;
+               env->me_txns->mti_wlocked = 0;
 #else  /* MDB_USE_POSIX_MUTEX: */
                pthread_mutexattr_t mattr;
 
-               if ((rc = pthread_mutexattr_init(&mattr))
-                       || (rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED))
+               /* Solaris needs this before initing a robust mutex.  Otherwise
+                * it may skip the init and return EBUSY "seems someone already
+                * inited" or EINVAL "it was inited differently".
+                */
+               memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex));
+               memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex));
+
+               if ((rc = pthread_mutexattr_init(&mattr)) != 0)
+                       goto fail;
+               rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
 #ifdef MDB_ROBUST_SUPPORTED
-                       || (rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST))
+               if (!rc) rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
 #endif
-                       || (rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr))
-                       || (rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr)))
-                       goto fail;
+               if (!rc) rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr);
+               if (!rc) rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr);
                pthread_mutexattr_destroy(&mattr);
+               if (rc)
+                       goto fail;
 #endif /* _WIN32 || ... */
 
                env->me_txns->mti_magic = MDB_MAGIC;
@@ -5381,7 +5436,7 @@ mdb_env_close(MDB_env *env)
        free(env);
 }
 
-/** Compare two items pointing at aligned mdb_size_t's */
+/** Compare two items pointing at aligned #mdb_size_t's */
 static int
 mdb_cmp_long(const MDB_val *a, const MDB_val *b)
 {
@@ -5392,7 +5447,7 @@ mdb_cmp_long(const MDB_val *a, const MDB_val *b)
 /** Compare two items pointing at aligned unsigned int's.
  *
  *     This is also set as #MDB_INTEGERDUP|#MDB_DUPFIXED's #MDB_dbx.%md_dcmp,
- *     but #mdb_cmp_clong() is called instead if the data type is mdb_size_t.
+ *     but #mdb_cmp_clong() is called instead if the data type is #mdb_size_t.
  */
 static int
 mdb_cmp_int(const MDB_val *a, const MDB_val *b)
@@ -5497,7 +5552,7 @@ mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp)
 
        nkeys = NUMKEYS(mp);
 
-       DPRINTF(("searching %u keys in %s %spage %"Y"u",
+       DPRINTF(("searching %u keys in %s %spage %"Yu,
            nkeys, IS_LEAF(mp) ? "leaf" : "branch", IS_SUBP(mp) ? "sub-" : "",
            mdb_dbg_pgno(mp)));
 
@@ -5545,7 +5600,7 @@ mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp)
                                DPRINTF(("found leaf index %u [%s], rc = %i",
                                    i, DKEY(&nodekey), rc));
                        else
-                               DPRINTF(("found branch index %u [%s -> %"Y"u], rc = %i",
+                               DPRINTF(("found branch index %u [%s -> %"Yu"], rc = %i",
                                    i, DKEY(&nodekey), NODEPGNO(node), rc));
 #endif
                        if (rc == 0)
@@ -5593,7 +5648,7 @@ static void
 mdb_cursor_pop(MDB_cursor *mc)
 {
        if (mc->mc_snum) {
-               DPRINTF(("popping page %"Y"u off db %d cursor %p",
+               DPRINTF(("popping page %"Yu" off db %d cursor %p",
                        mc->mc_pg[mc->mc_top]->mp_pgno, DDBI(mc), (void *) mc));
 
                mc->mc_snum--;
@@ -5609,7 +5664,7 @@ mdb_cursor_pop(MDB_cursor *mc)
 static int
 mdb_cursor_push(MDB_cursor *mc, MDB_page *mp)
 {
-       DPRINTF(("pushing page %"Y"u on db %d cursor %p", mp->mp_pgno,
+       DPRINTF(("pushing page %"Yu" on db %d cursor %p", mp->mp_pgno,
                DDBI(mc), (void *) mc));
 
        if (mc->mc_snum >= CURSOR_STACK) {
@@ -5692,7 +5747,8 @@ mdb_rpage_get(MDB_txn *txn, pgno_t pg0, MDB_page **ret)
 #define MAP(rc,env,addr,len,off)       \
        addr = NULL; \
        rc = NtMapViewOfSection(env->me_fmh, GetCurrentProcess(), &addr, 0, \
-               len, &off, &len, ViewUnmap, (env->me_flags & MDB_RDONLY) ? 0 : MEM_RESERVE, PAGE_READONLY)
+               len, &off, &len, ViewUnmap, (env->me_flags & MDB_RDONLY) ? 0 : MEM_RESERVE, PAGE_READONLY); \
+       if (rc) rc = mdb_nt2win32(rc)
 #else
        off_t off;
        size_t len;
@@ -5830,7 +5886,7 @@ retry:
                                if (rc)
                                        goto fail;
                                if (!el[x].mref) {
-                                       munmap(el[x].mptr, el[x].mcnt);
+                                       munmap(el[x].mptr, env->me_psize * el[x].mcnt);
                                        el[x].mptr = id3.mptr;
                                        el[x].mcnt = id3.mcnt;
                                } else {
@@ -5882,36 +5938,15 @@ fail:
                        pthread_mutex_unlock(&env->me_rpmutex);
                        return rc;
                }
-               /* If this page is far enough from the end of the env, scan for
-                * any overflow pages that would spill onto another block.
-                * Note we must compare against mt_last_pgno, the last written
-                * page in the environment. Not mt_next_pgno, which increases
-                * for every newly allocated (but not yet written) page. If
-                * we scanned beyond the last written page we'd get a bus error.
-                */
-               if (pgno + MDB_RPAGE_CHUNK <= txn->mt_last_pgno) {
-                       int i;
-                       char *cp = (char *)id3.mptr + rem * env->me_psize;
-                       for (i=rem; i<MDB_RPAGE_CHUNK;) {
-                               p = (MDB_page *)cp;
-                               if (IS_OVERFLOW(p)) {
-                                       int nop = p->mp_pages;
-                                       if (nop + i > MDB_RPAGE_CHUNK) {
-                                               munmap(id3.mptr, len);
-                                               id3.mcnt = nop + i;
-                                               len = id3.mcnt * env->me_psize;
-                                               MAP(rc, env, id3.mptr, len, off);
-                                               if (rc)
-                                                       goto fail;
-                                               break;
-                                       }
-                                       i += nop;
-                                       cp += nop * env->me_psize;
-                               } else {
-                                       i++;
-                                       cp += env->me_psize;
-                               }
-                       }
+               /* check for overflow size */
+               p = (MDB_page *)((char *)id3.mptr + rem * env->me_psize);
+               if (IS_OVERFLOW(p) && p->mp_pages + rem > id3.mcnt) {
+                       id3.mcnt = p->mp_pages + rem;
+                       munmap(id3.mptr, len);
+                       len = id3.mcnt * env->me_psize;
+                       MAP(rc, env, id3.mptr, len, off);
+                       if (rc)
+                               goto fail;
                }
                mdb_mid3l_insert(el, &id3);
                pthread_mutex_unlock(&env->me_rpmutex);
@@ -5943,9 +5978,6 @@ static int
 mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl)
 {
        MDB_txn *txn = mc->mc_txn;
-#ifndef MDB_VL32
-       MDB_env *env = txn->mt_env;
-#endif
        MDB_page *p = NULL;
        int level;
 
@@ -5964,14 +5996,7 @@ mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl)
                                MDB_ID pn = pgno << 1;
                                x = mdb_midl_search(tx2->mt_spill_pgs, pn);
                                if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) {
-#ifdef MDB_VL32
-                                       int rc = mdb_rpage_get(txn, pgno, &p);
-                                       if (rc)
-                                               return rc;
-#else
-                                       p = (MDB_page *)(env->me_map + env->me_psize * pgno);
-#endif
-                                       goto done;
+                                       goto mapped;
                                }
                        }
                        if (dl[0].mid) {
@@ -5985,21 +6010,24 @@ mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl)
                } while ((tx2 = tx2->mt_parent) != NULL);
        }
 
-       if (pgno < txn->mt_next_pgno) {
-               level = 0;
+       if (pgno >= txn->mt_next_pgno) {
+               DPRINTF(("page %"Yu" not found", pgno));
+               txn->mt_flags |= MDB_TXN_ERROR;
+               return MDB_PAGE_NOTFOUND;
+       }
+
+       level = 0;
+
+mapped:
+       {
 #ifdef MDB_VL32
-               {
-                       int rc = mdb_rpage_get(txn, pgno, &p);
-                       if (rc)
-                               return rc;
-               }
+               int rc = mdb_rpage_get(txn, pgno, &p);
+               if (rc)
+                       return rc;
 #else
+               MDB_env *env = txn->mt_env;
                p = (MDB_page *)(env->me_map + env->me_psize * pgno);
 #endif
-       } else {
-               DPRINTF(("page %"Y"u not found", pgno));
-               txn->mt_flags |= MDB_TXN_ERROR;
-               return MDB_PAGE_NOTFOUND;
        }
 
 done:
@@ -6023,13 +6051,13 @@ mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags)
                MDB_node        *node;
                indx_t          i;
 
-               DPRINTF(("branch page %"Y"u has %u keys", mp->mp_pgno, NUMKEYS(mp)));
+               DPRINTF(("branch page %"Yu" has %u keys", mp->mp_pgno, NUMKEYS(mp)));
                /* Don't assert on branch pages in the FreeDB. We can get here
                 * while in the process of rebalancing a FreeDB branch page; we must
                 * let that proceed. ITS#8336
                 */
                mdb_cassert(mc, !mc->mc_dbi || NUMKEYS(mp) > 1);
-               DPRINTF(("found index 0 to page %"Y"u", NODEPGNO(NODEPTR(mp, 0))));
+               DPRINTF(("found index 0 to page %"Yu, NODEPGNO(NODEPTR(mp, 0))));
 
                if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) {
                        i = 0;
@@ -6074,7 +6102,7 @@ mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags)
                return MDB_CORRUPTED;
        }
 
-       DPRINTF(("found leaf page %"Y"u for key [%s]", mp->mp_pgno,
+       DPRINTF(("found leaf page %"Yu" for key [%s]", mp->mp_pgno,
            key ? DKEY(key) : "null"));
        mc->mc_flags |= C_INITIALIZED;
        mc->mc_flags &= ~C_EOF;
@@ -6189,7 +6217,7 @@ mdb_page_search(MDB_cursor *mc, MDB_val *key, int flags)
        mc->mc_snum = 1;
        mc->mc_top = 0;
 
-       DPRINTF(("db %d root page %"Y"u has flags 0x%X",
+       DPRINTF(("db %d root page %"Yu" has flags 0x%X",
                DDBI(mc), root, mc->mc_pg[0]->mp_flags));
 
        if (flags & MDB_PS_MODIFY) {
@@ -6214,7 +6242,7 @@ mdb_ovpage_free(MDB_cursor *mc, MDB_page *mp)
        MDB_ID pn = pg << 1;
        int rc;
 
-       DPRINTF(("free ov page %"Y"u (%d)", pg, ovpages));
+       DPRINTF(("free ov page %"Yu" (%d)", pg, ovpages));
        /* If the page is dirty or on the spill list we just acquired it,
         * so we should give it back to our current free list, if any.
         * Otherwise put it onto the list of pages we freed in this txn.
@@ -6255,7 +6283,7 @@ mdb_ovpage_free(MDB_cursor *mc, MDB_page *mp)
                                j = ++(dl[0].mid);
                                dl[j] = ix;             /* Unsorted. OK when MDB_TXN_ERROR. */
                                txn->mt_flags |= MDB_TXN_ERROR;
-                               return MDB_CORRUPTED;
+                               return MDB_PROBLEM;
                        }
                }
                txn->mt_dirty_room++;
@@ -6292,12 +6320,10 @@ mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data)
        pgno_t           pgno;
        int rc;
 
-#ifdef MDB_VL32
-       if (mc->mc_ovpg) {
-               MDB_PAGE_UNREF(mc->mc_txn, mc->mc_ovpg);
-               mc->mc_ovpg = 0;
+       if (MC_OVPG(mc)) {
+               MDB_PAGE_UNREF(mc->mc_txn, MC_OVPG(mc));
+               MC_SET_OVPG(mc, NULL);
        }
-#endif
        if (!F_ISSET(leaf->mn_flags, F_BIGDATA)) {
                data->mv_size = NODEDSZ(leaf);
                data->mv_data = NODEDATA(leaf);
@@ -6309,13 +6335,11 @@ mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data)
        data->mv_size = NODEDSZ(leaf);
        memcpy(&pgno, NODEDATA(leaf), sizeof(pgno));
        if ((rc = mdb_page_get(mc, pgno, &omp, NULL)) != 0) {
-               DPRINTF(("read overflow page %"Y"u failed", pgno));
+               DPRINTF(("read overflow page %"Yu" failed", pgno));
                return rc;
        }
        data->mv_data = METADATA(omp);
-#ifdef MDB_VL32
-       mc->mc_ovpg = omp;
-#endif
+       MC_SET_OVPG(mc, omp);
 
        return MDB_SUCCESS;
 }
@@ -6339,14 +6363,10 @@ mdb_get(MDB_txn *txn, MDB_dbi dbi,
 
        mdb_cursor_init(&mc, txn, dbi, &mx);
        rc = mdb_cursor_set(&mc, key, data, MDB_SET, &exact);
-#ifdef MDB_VL32
-       {
-               /* unref all the pages - caller must copy the data
-                * before doing anything else
-                */
-               mdb_cursor_unref(&mc);
-       }
-#endif
+       /* unref all the pages when MDB_VL32 - caller must copy the data
+        * before doing anything else
+        */
+       MDB_CURSOR_UNREF(&mc, 1);
        return rc;
 }
 
@@ -6376,7 +6396,7 @@ mdb_cursor_sibling(MDB_cursor *mc, int move_right)
        op = mc->mc_pg[mc->mc_top];
 #endif
        mdb_cursor_pop(mc);
-       DPRINTF(("parent page is page %"Y"u, index %u",
+       DPRINTF(("parent page is page %"Yu", index %u",
                mc->mc_pg[mc->mc_top]->mp_pgno, mc->mc_ki[mc->mc_top]));
 
        if (move_right ? (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mc->mc_pg[mc->mc_top]))
@@ -6423,11 +6443,12 @@ mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
        MDB_node        *leaf;
        int rc;
 
-       if (mc->mc_flags & C_EOF) {
+       if ((mc->mc_flags & C_EOF) ||
+               ((mc->mc_flags & C_DEL) && op == MDB_NEXT_DUP)) {
                return MDB_NOTFOUND;
        }
-
-       mdb_cassert(mc, mc->mc_flags & C_INITIALIZED);
+       if (!(mc->mc_flags & C_INITIALIZED))
+               return mdb_cursor_first(mc, key, data);
 
        mp = mc->mc_pg[mc->mc_top];
 
@@ -6442,13 +6463,9 @@ mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
                                        return rc;
                                }
                        }
-#ifdef MDB_VL32
                        else {
-                               if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
-                                       mdb_cursor_unref(&mc->mc_xcursor->mx_cursor);
-                               }
+                               MDB_CURSOR_UNREF(&mc->mc_xcursor->mx_cursor, 0);
                        }
-#endif
                } else {
                        mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
                        if (op == MDB_NEXT_DUP)
@@ -6456,7 +6473,7 @@ mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
                }
        }
 
-       DPRINTF(("cursor_next: top page is %"Y"u in cursor %p",
+       DPRINTF(("cursor_next: top page is %"Yu" in cursor %p",
                mdb_dbg_pgno(mp), (void *) mc));
        if (mc->mc_flags & C_DEL) {
                mc->mc_flags ^= C_DEL;
@@ -6470,12 +6487,12 @@ mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
                        return rc;
                }
                mp = mc->mc_pg[mc->mc_top];
-               DPRINTF(("next page is %"Y"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top]));
+               DPRINTF(("next page is %"Yu", key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top]));
        } else
                mc->mc_ki[mc->mc_top]++;
 
 skip:
-       DPRINTF(("==> cursor points to page %"Y"u with %u keys, key index %u",
+       DPRINTF(("==> cursor points to page %"Yu" with %u keys, key index %u",
            mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top]));
 
        if (IS_LEAF2(mp)) {
@@ -6513,7 +6530,12 @@ mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
        MDB_node        *leaf;
        int rc;
 
-       mdb_cassert(mc, mc->mc_flags & C_INITIALIZED);
+       if (!(mc->mc_flags & C_INITIALIZED)) {
+               rc = mdb_cursor_last(mc, key, data);
+               if (rc)
+                       return rc;
+               mc->mc_ki[mc->mc_top]++;
+       }
 
        mp = mc->mc_pg[mc->mc_top];
 
@@ -6530,13 +6552,9 @@ mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
                                        return rc;
                                }
                        }
-#ifdef MDB_VL32
                        else {
-                               if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
-                                       mdb_cursor_unref(&mc->mc_xcursor->mx_cursor);
-                               }
+                               MDB_CURSOR_UNREF(&mc->mc_xcursor->mx_cursor, 0);
                        }
-#endif
                } else {
                        mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
                        if (op == MDB_PREV_DUP)
@@ -6544,7 +6562,7 @@ mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
                }
        }
 
-       DPRINTF(("cursor_prev: top page is %"Y"u in cursor %p",
+       DPRINTF(("cursor_prev: top page is %"Yu" in cursor %p",
                mdb_dbg_pgno(mp), (void *) mc));
 
        mc->mc_flags &= ~(C_EOF|C_DEL);
@@ -6556,13 +6574,13 @@ mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
                }
                mp = mc->mc_pg[mc->mc_top];
                mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1;
-               DPRINTF(("prev page is %"Y"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top]));
+               DPRINTF(("prev page is %"Yu", key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top]));
        } else
                mc->mc_ki[mc->mc_top]--;
 
        mc->mc_flags &= ~C_EOF;
 
-       DPRINTF(("==> cursor points to page %"Y"u with %u keys, key index %u",
+       DPRINTF(("==> cursor points to page %"Yu" with %u keys, key index %u",
            mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top]));
 
        if (IS_LEAF2(mp)) {
@@ -6605,8 +6623,10 @@ mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data,
        if (key->mv_size == 0)
                return MDB_BAD_VALSIZE;
 
-       if (mc->mc_xcursor)
+       if (mc->mc_xcursor) {
+               MDB_CURSOR_UNREF(&mc->mc_xcursor->mx_cursor, 0);
                mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+       }
 
        /* See if we're already on the right page */
        if (mc->mc_flags & C_INITIALIZED) {
@@ -6672,6 +6692,7 @@ mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data,
                                                }
                                        }
                                        rc = 0;
+                                       mc->mc_flags &= ~C_EOF;
                                        goto set2;
                                }
                        }
@@ -6738,11 +6759,6 @@ set1:
                return MDB_SUCCESS;
        }
 
-#ifdef MDB_VL32
-       if (mc->mc_xcursor && mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
-               mdb_cursor_unref(&mc->mc_xcursor->mx_cursor);
-       }
-#endif
        if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
                mdb_xcursor_init1(mc, leaf);
        }
@@ -6768,17 +6784,15 @@ set1:
                        if ((rc = mdb_node_read(mc, leaf, &olddata)) != MDB_SUCCESS)
                                return rc;
                        dcmp = mc->mc_dbx->md_dcmp;
-#if UINT_MAX < SIZE_MAX || defined(MDB_VL32)
-                       if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(mdb_size_t))
+                       if (NEED_CMP_CLONG(dcmp, olddata.mv_size))
                                dcmp = mdb_cmp_clong;
-#endif
                        rc = dcmp(data, &olddata);
                        if (rc) {
                                if (op == MDB_GET_BOTH || rc > 0)
                                        return MDB_NOTFOUND;
                                rc = 0;
-                               *data = olddata;
                        }
+                       *data = olddata;
 
                } else {
                        if (mc->mc_xcursor)
@@ -6804,11 +6818,7 @@ mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data)
        MDB_node        *leaf;
 
        if (mc->mc_xcursor) {
-#ifdef MDB_VL32
-               if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
-                       mdb_cursor_unref(&mc->mc_xcursor->mx_cursor);
-               }
-#endif
+               MDB_CURSOR_UNREF(&mc->mc_xcursor->mx_cursor, 0);
                mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
        }
 
@@ -6854,11 +6864,7 @@ mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data)
        MDB_node        *leaf;
 
        if (mc->mc_xcursor) {
-#ifdef MDB_VL32
-               if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
-                       mdb_cursor_unref(&mc->mc_xcursor->mx_cursor);
-               }
-#endif
+               MDB_CURSOR_UNREF(&mc->mc_xcursor->mx_cursor, 0);
                mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
        }
 
@@ -6985,10 +6991,7 @@ mdb_cursor_get(MDB_cursor *mc, MDB_val *key, MDB_val *data,
                        rc = MDB_INCOMPATIBLE;
                        break;
                }
-               if (!(mc->mc_flags & C_INITIALIZED))
-                       rc = mdb_cursor_first(mc, key, data);
-               else
-                       rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP);
+               rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP);
                if (rc == MDB_SUCCESS) {
                        if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
                                MDB_cursor *mx;
@@ -7013,8 +7016,10 @@ fetchm:
                        break;
                }
                if (!(mc->mc_flags & C_INITIALIZED))
-                       rc = mdb_cursor_first(mc, key, data);
-               else {
+                       rc = mdb_cursor_last(mc, key, data);
+               else
+                       rc = MDB_SUCCESS;
+               if (rc == MDB_SUCCESS) {
                        MDB_cursor *mx = &mc->mc_xcursor->mx_cursor;
                        if (mx->mc_flags & C_INITIALIZED) {
                                rc = mdb_cursor_sibling(mx, 0);
@@ -7028,21 +7033,11 @@ fetchm:
        case MDB_NEXT:
        case MDB_NEXT_DUP:
        case MDB_NEXT_NODUP:
-               if (!(mc->mc_flags & C_INITIALIZED))
-                       rc = mdb_cursor_first(mc, key, data);
-               else
-                       rc = mdb_cursor_next(mc, key, data, op);
+               rc = mdb_cursor_next(mc, key, data, op);
                break;
        case MDB_PREV:
        case MDB_PREV_DUP:
        case MDB_PREV_NODUP:
-               if (!(mc->mc_flags & C_INITIALIZED)) {
-                       rc = mdb_cursor_last(mc, key, data);
-                       if (rc)
-                               break;
-                       mc->mc_flags |= C_INITIALIZED;
-                       mc->mc_ki[mc->mc_top]++;
-               }
                rc = mdb_cursor_prev(mc, key, data, op);
                break;
        case MDB_FIRST:
@@ -7325,10 +7320,8 @@ more:
                                if (flags == MDB_CURRENT)
                                        goto current;
                                dcmp = mc->mc_dbx->md_dcmp;
-#if UINT_MAX < SIZE_MAX || defined(MDB_VL32)
-                               if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(mdb_size_t))
+                               if (NEED_CMP_CLONG(dcmp, olddata.mv_size))
                                        dcmp = mdb_cmp_clong;
-#endif
                                /* does data match? */
                                if (!dcmp(data, &olddata)) {
                                        if (flags & (MDB_NODUPDATA|MDB_APPENDDUP))
@@ -7479,8 +7472,13 @@ current:
                                        /* Note - this page is already counted in parent's dirty_room */
                                        rc2 = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2);
                                        mdb_cassert(mc, rc2 == 0);
+                                       /* Currently we make the page look as with put() in the
+                                        * parent txn, in case the user peeks at MDB_RESERVEd
+                                        * or unused parts. Some users treat ovpages specially.
+                                        */
                                        if (!(flags & MDB_RESERVE)) {
-                                               /* Copy end of page, adjusting alignment so
+                                               /* Skip the part where LMDB will put *data.
+                                                * Copy end of page, adjusting alignment so
                                                 * compiler may copy words instead of bytes.
                                                 */
                                                off = (PAGEHDRSZ + data->mv_size) & -sizeof(size_t);
@@ -7566,7 +7564,7 @@ new_sub:
                 */
                if (do_sub) {
                        int xflags, new_dupdata;
-                       size_t ecount;
+                       mdb_size_t ecount;
 put_sub:
                        xdata.mv_size = 0;
                        xdata.mv_data = "";
@@ -7648,7 +7646,7 @@ put_sub:
                return rc;
 bad_sub:
                if (rc == MDB_KEYEXIST) /* should not happen, we deleted that item */
-                       rc = MDB_CORRUPTED;
+                       rc = MDB_PROBLEM;
        }
        mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
        return rc;
@@ -7777,7 +7775,7 @@ mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp)
 
        if ((rc = mdb_page_alloc(mc, num, &np)))
                return rc;
-       DPRINTF(("allocated new mpage %"Y"u, page size %u",
+       DPRINTF(("allocated new mpage %"Yu", page size %u",
            np->mp_pgno, mc->mc_txn->mt_env->me_psize));
        np->mp_flags = flags | P_DIRTY;
        np->mp_lower = (PAGEHDRSZ-PAGEBASE);
@@ -7877,7 +7875,7 @@ mdb_node_add(MDB_cursor *mc, indx_t indx,
 
        mdb_cassert(mc, mp->mp_upper >= mp->mp_lower);
 
-       DPRINTF(("add to %s %spage %"Y"u index %i, data size %"Z"u key size %"Z"u [%s]",
+       DPRINTF(("add to %s %spage %"Yu" index %i, data size %"Z"u key size %"Z"u [%s]",
            IS_LEAF(mp) ? "leaf" : "branch",
                IS_SUBP(mp) ? "sub-" : "",
                mdb_dbg_pgno(mp), indx, data ? data->mv_size : 0,
@@ -7918,7 +7916,7 @@ mdb_node_add(MDB_cursor *mc, indx_t indx,
                                goto full;
                        if ((rc = mdb_page_new(mc, P_OVERFLOW, ovpages, &ofp)))
                                return rc;
-                       DPRINTF(("allocated overflow page %"Y"u", ofp->mp_pgno));
+                       DPRINTF(("allocated overflow page %"Yu, ofp->mp_pgno));
                        flags |= F_BIGDATA;
                        goto update;
                } else {
@@ -7975,7 +7973,7 @@ update:
        return MDB_SUCCESS;
 
 full:
-       DPRINTF(("not enough room in page %"Y"u, got %u ptrs",
+       DPRINTF(("not enough room in page %"Yu", got %u ptrs",
                mdb_dbg_pgno(mp), NUMKEYS(mp)));
        DPRINTF(("upper-lower = %u - %u = %"Z"d", mp->mp_upper,mp->mp_lower,room));
        DPRINTF(("node size = %"Z"u", node_size));
@@ -7998,7 +7996,7 @@ mdb_node_del(MDB_cursor *mc, int ksize)
        MDB_node        *node;
        char            *base;
 
-       DPRINTF(("delete node %u on %s page %"Y"u", indx,
+       DPRINTF(("delete node %u on %s page %"Yu, indx,
            IS_LEAF(mp) ? "leaf" : "branch", mdb_dbg_pgno(mp)));
        numkeys = NUMKEYS(mp);
        mdb_cassert(mc, indx < numkeys);
@@ -8107,9 +8105,7 @@ mdb_xcursor_init0(MDB_cursor *mc)
        mx->mx_cursor.mc_dbflag = &mx->mx_dbflag;
        mx->mx_cursor.mc_snum = 0;
        mx->mx_cursor.mc_top = 0;
-#ifdef MDB_VL32
-       mx->mx_cursor.mc_ovpg = 0;
-#endif
+       MC_SET_OVPG(&mx->mx_cursor, NULL);
        mx->mx_cursor.mc_flags = C_SUB | (mc->mc_flags & (C_ORIG_RDONLY|C_WRITEMAP));
        mx->mx_dbx.md_name.mv_size = 0;
        mx->mx_dbx.md_name.mv_data = NULL;
@@ -8157,13 +8153,11 @@ mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node)
                                mx->mx_db.md_flags |= MDB_INTEGERKEY;
                }
        }
-       DPRINTF(("Sub-db -%u root page %"Y"u", mx->mx_cursor.mc_dbi,
+       DPRINTF(("Sub-db -%u root page %"Yu, mx->mx_cursor.mc_dbi,
                mx->mx_db.md_root));
        mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
-#if UINT_MAX < SIZE_MAX || defined(MDB_VL32)
-       if (mx->mx_dbx.md_cmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(mdb_size_t))
+       if (NEED_CMP_CLONG(mx->mx_dbx.md_cmp, mx->mx_db.md_pad))
                mx->mx_dbx.md_cmp = mdb_cmp_clong;
-#endif
 }
 
 
@@ -8186,7 +8180,7 @@ mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int new_dupdata)
                mx->mx_cursor.mc_flags |= C_INITIALIZED;
                mx->mx_cursor.mc_ki[0] = 0;
                mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
-#if UINT_MAX < SIZE_MAX
+#if UINT_MAX < MDB_SIZE_MAX    /* matches mdb_xcursor_init1:NEED_CMP_CLONG() */
                mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp;
 #endif
        } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) {
@@ -8194,7 +8188,7 @@ mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int new_dupdata)
        }
        mx->mx_db = src_mx->mx_db;
        mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0];
-       DPRINTF(("Sub-db -%u root page %"Y"u", mx->mx_cursor.mc_dbi,
+       DPRINTF(("Sub-db -%u root page %"Yu, mx->mx_cursor.mc_dbi,
                mx->mx_db.md_root));
 }
 
@@ -8213,9 +8207,7 @@ mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx)
        mc->mc_top = 0;
        mc->mc_pg[0] = 0;
        mc->mc_ki[0] = 0;
-#ifdef MDB_VL32
-       mc->mc_ovpg = 0;
-#endif
+       MC_SET_OVPG(mc, NULL);
        mc->mc_flags = txn->mt_flags & (C_ORIG_RDONLY|C_WRITEMAP);
        if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
                mdb_tassert(txn, mx != NULL);
@@ -8281,7 +8273,7 @@ mdb_cursor_renew(MDB_txn *txn, MDB_cursor *mc)
 
 /* Return the count of duplicate data items for the current key */
 int
-mdb_cursor_count(MDB_cursor *mc, size_t *countp)
+mdb_cursor_count(MDB_cursor *mc, mdb_size_t *countp)
 {
        MDB_node        *leaf;
 
@@ -8315,6 +8307,9 @@ mdb_cursor_count(MDB_cursor *mc, size_t *countp)
 void
 mdb_cursor_close(MDB_cursor *mc)
 {
+       if (mc) {
+               MDB_CURSOR_UNREF(mc, 0);
+       }
        if (mc && !mc->mc_backup) {
                /* remove from txn, if tracked */
                if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_cursors) {
@@ -8366,7 +8361,7 @@ mdb_update_key(MDB_cursor *mc, MDB_val *key)
                char kbuf2[DKBUF_MAXKEYSIZE*2+1];
                k2.mv_data = NODEKEY(node);
                k2.mv_size = node->mn_ksize;
-               DPRINTF(("update key %u (ofs %u) [%s] to [%s] on page %"Y"u",
+               DPRINTF(("update key %u (ofs %u) [%s] to [%s] on page %"Yu,
                        indx, ptr,
                        mdb_dkey(&k2, kbuf2),
                        DKEY(key),
@@ -8514,7 +8509,7 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
                        return rc;
        }
 
-       DPRINTF(("moving %s node %u [%s] on page %"Y"u to node %u on page %"Y"u",
+       DPRINTF(("moving %s node %u [%s] on page %"Yu" to node %u on page %"Yu,
            IS_LEAF(csrc->mc_pg[csrc->mc_top]) ? "leaf" : "branch",
            csrc->mc_ki[csrc->mc_top],
                DKEY(&key),
@@ -8608,7 +8603,7 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
                                key.mv_size = NODEKSZ(srcnode);
                                key.mv_data = NODEKEY(srcnode);
                        }
-                       DPRINTF(("update separator for source page %"Y"u to [%s]",
+                       DPRINTF(("update separator for source page %"Yu" to [%s]",
                                csrc->mc_pg[csrc->mc_top]->mp_pgno, DKEY(&key)));
                        mdb_cursor_copy(csrc, &mn);
                        mn.mc_snum--;
@@ -8639,7 +8634,7 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
                                key.mv_size = NODEKSZ(srcnode);
                                key.mv_data = NODEKEY(srcnode);
                        }
-                       DPRINTF(("update separator for destination page %"Y"u to [%s]",
+                       DPRINTF(("update separator for destination page %"Yu" to [%s]",
                                cdst->mc_pg[cdst->mc_top]->mp_pgno, DKEY(&key)));
                        mdb_cursor_copy(cdst, &mn);
                        mn.mc_snum--;
@@ -8685,7 +8680,7 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
        psrc = csrc->mc_pg[csrc->mc_top];
        pdst = cdst->mc_pg[cdst->mc_top];
 
-       DPRINTF(("merging page %"Y"u into %"Y"u", psrc->mp_pgno, pdst->mp_pgno));
+       DPRINTF(("merging page %"Yu" into %"Yu, psrc->mp_pgno, pdst->mp_pgno));
 
        mdb_cassert(csrc, csrc->mc_snum > 1);   /* can't merge root page */
        mdb_cassert(csrc, cdst->mc_snum > 1);
@@ -8742,7 +8737,7 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
                }
        }
 
-       DPRINTF(("dst page %"Y"u now has %u keys (%.1f%% filled)",
+       DPRINTF(("dst page %"Yu" now has %u keys (%.1f%% filled)",
            pdst->mp_pgno, NUMKEYS(pdst),
                (float)PAGEFILL(cdst->mc_txn->mt_env, pdst) / 10));
 
@@ -8830,9 +8825,7 @@ mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst)
        cdst->mc_snum = csrc->mc_snum;
        cdst->mc_top = csrc->mc_top;
        cdst->mc_flags = csrc->mc_flags;
-#ifdef MDB_VL32
-       cdst->mc_ovpg = csrc->mc_ovpg;
-#endif
+       MC_SET_OVPG(cdst, MC_OVPG(csrc));
 
        for (i=0; i<csrc->mc_snum; i++) {
                cdst->mc_pg[i] = csrc->mc_pg[i];
@@ -8861,14 +8854,14 @@ mdb_rebalance(MDB_cursor *mc)
                minkeys = 1;
                thresh = FILL_THRESHOLD;
        }
-       DPRINTF(("rebalancing %s page %"Y"u (has %u keys, %.1f%% full)",
+       DPRINTF(("rebalancing %s page %"Yu" (has %u keys, %.1f%% full)",
            IS_LEAF(mc->mc_pg[mc->mc_top]) ? "leaf" : "branch",
            mdb_dbg_pgno(mc->mc_pg[mc->mc_top]), NUMKEYS(mc->mc_pg[mc->mc_top]),
                (float)PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) / 10));
 
        if (PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) >= thresh &&
                NUMKEYS(mc->mc_pg[mc->mc_top]) >= minkeys) {
-               DPRINTF(("no need to rebalance page %"Y"u, above fill threshold",
+               DPRINTF(("no need to rebalance page %"Yu", above fill threshold",
                    mdb_dbg_pgno(mc->mc_pg[mc->mc_top])));
                return MDB_SUCCESS;
        }
@@ -8997,7 +8990,7 @@ mdb_rebalance(MDB_cursor *mc)
                fromleft = 1;
        }
 
-       DPRINTF(("found neighbor page %"Y"u (%u keys, %.1f%% full)",
+       DPRINTF(("found neighbor page %"Yu" (%u keys, %.1f%% full)",
            mn.mc_pg[mn.mc_top]->mp_pgno, NUMKEYS(mn.mc_pg[mn.mc_top]),
                (float)PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) / 10));
 
@@ -9054,8 +9047,6 @@ mdb_cursor_del0(MDB_cursor *mc)
                        if (m3->mc_pg[mc->mc_top] == mp) {
                                if (m3->mc_ki[mc->mc_top] == ki) {
                                        m3->mc_flags |= C_DEL;
-                                       if (mc->mc_db->md_flags & MDB_DUPSORT)
-                                               m3->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
                                } else if (m3->mc_ki[mc->mc_top] > ki) {
                                        m3->mc_ki[mc->mc_top]--;
                                }
@@ -9089,11 +9080,21 @@ mdb_cursor_del0(MDB_cursor *mc)
                                continue;
                        if (m3->mc_pg[mc->mc_top] == mp) {
                                /* if m3 points past last node in page, find next sibling */
-                               if (m3->mc_ki[mc->mc_top] >= nkeys) {
-                                       rc = mdb_cursor_sibling(m3, 1);
-                                       if (rc == MDB_NOTFOUND) {
-                                               m3->mc_flags |= C_EOF;
-                                               rc = MDB_SUCCESS;
+                               if (m3->mc_ki[mc->mc_top] >= mc->mc_ki[mc->mc_top]) {
+                                       if (m3->mc_ki[mc->mc_top] >= nkeys) {
+                                               rc = mdb_cursor_sibling(m3, 1);
+                                               if (rc == MDB_NOTFOUND) {
+                                                       m3->mc_flags |= C_EOF;
+                                                       rc = MDB_SUCCESS;
+                                                       continue;
+                                               }
+                                       }
+                                       if (mc->mc_db->md_flags & MDB_DUPSORT) {
+                                               MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]);
+                                               if (node->mn_flags & F_DUPDATA) {
+                                                       mdb_xcursor_init1(m3, node);
+                                                       m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL;
+                                               }
                                        }
                                }
                        }
@@ -9199,7 +9200,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
        newindx = mc->mc_ki[mc->mc_top];
        nkeys = NUMKEYS(mp);
 
-       DPRINTF(("-----> splitting %s page %"Y"u and adding [%s] at index %i/%i",
+       DPRINTF(("-----> splitting %s page %"Yu" and adding [%s] at index %i/%i",
            IS_LEAF(mp) ? "leaf" : "branch", mp->mp_pgno,
            DKEY(newkey), mc->mc_ki[mc->mc_top], nkeys));
 
@@ -9207,7 +9208,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
        if ((rc = mdb_page_new(mc, mp->mp_flags, 1, &rp)))
                return rc;
        rp->mp_pad = mp->mp_pad;
-       DPRINTF(("new right sibling: page %"Y"u", rp->mp_pgno));
+       DPRINTF(("new right sibling: page %"Yu, rp->mp_pgno));
 
        /* Usually when splitting the root page, the cursor
         * height is 1. But when called from mdb_update_key,
@@ -9225,7 +9226,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                mc->mc_pg[0] = pp;
                mc->mc_ki[0] = 0;
                mc->mc_db->md_root = pp->mp_pgno;
-               DPRINTF(("root split! new root = %"Y"u", pp->mp_pgno));
+               DPRINTF(("root split! new root = %"Yu, pp->mp_pgno));
                new_root = mc->mc_db->md_depth++;
 
                /* Add left (implicit) pointer. */
@@ -9242,7 +9243,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                ptop = 0;
        } else {
                ptop = mc->mc_top-1;
-               DPRINTF(("parent branch page is %"Y"u", mc->mc_pg[ptop]->mp_pgno));
+               DPRINTF(("parent branch page is %"Yu, mc->mc_pg[ptop]->mp_pgno));
        }
 
        mdb_cursor_copy(mc, &mn);
@@ -9623,11 +9624,12 @@ mdb_put(MDB_txn *txn, MDB_dbi dbi,
 #ifndef MDB_WBUF
 #define MDB_WBUF       (1024*1024)
 #endif
+#define MDB_EOF                0x10    /**< #mdb_env_copyfd1() is done reading */
 
-       /** State needed for a compacting copy. */
+       /** State needed for a double-buffering compacting copy. */
 typedef struct mdb_copy {
        pthread_mutex_t mc_mutex;
-       pthread_cond_t mc_cond;
+       pthread_cond_t mc_cond; /**< Condition variable for #mc_new */
        char *mc_wbuf[2];
        char *mc_over[2];
        MDB_env *mc_env;
@@ -9636,10 +9638,9 @@ typedef struct mdb_copy {
        int mc_olen[2];
        pgno_t mc_next_pgno;
        HANDLE mc_fd;
-       int mc_status;
-       volatile int mc_new;
-       int mc_toggle;
-
+       int mc_toggle;                  /**< Buffer number in provider */
+       int mc_new;                             /**< (0-2 buffers to write) | (#MDB_EOF at end) */
+       volatile int mc_error;  /**< Error code, never cleared if set */
 } mdb_copy;
 
        /** Dedicated writer thread for compacting copy. */
@@ -9658,20 +9659,16 @@ mdb_env_copythr(void *arg)
 #endif
 
        pthread_mutex_lock(&my->mc_mutex);
-       my->mc_new = 0;
-       pthread_cond_signal(&my->mc_cond);
        for(;;) {
                while (!my->mc_new)
                        pthread_cond_wait(&my->mc_cond, &my->mc_mutex);
-               if (my->mc_new < 0) {
-                       my->mc_new = 0;
+               if (my->mc_new == 0 + MDB_EOF) /* 0 buffers, just EOF */
                        break;
-               }
-               my->mc_new = 0;
                wsize = my->mc_wlen[toggle];
                ptr = my->mc_wbuf[toggle];
 again:
-               while (wsize > 0) {
+               rc = MDB_SUCCESS;
+               while (wsize > 0 && !my->mc_error) {
                        DO_WRITE(rc, my->mc_fd, ptr, wsize, len);
                        if (!rc) {
                                rc = ErrCode();
@@ -9687,8 +9684,7 @@ again:
                        }
                }
                if (rc) {
-                       my->mc_status = rc;
-                       break;
+                       my->mc_error = rc;
                }
                /* If there's an overflow page tail, write it too */
                if (my->mc_olen[toggle]) {
@@ -9699,38 +9695,41 @@ again:
                }
                my->mc_wlen[toggle] = 0;
                toggle ^= 1;
+               /* Return the empty buffer to provider */
+               my->mc_new--;
                pthread_cond_signal(&my->mc_cond);
        }
-       pthread_cond_signal(&my->mc_cond);
        pthread_mutex_unlock(&my->mc_mutex);
        return (THREAD_RET)0;
 #undef DO_WRITE
 }
 
-       /** Tell the writer thread there's a buffer ready to write */
+       /** Give buffer and/or #MDB_EOF to writer thread, await unused buffer.
+        *
+        * @param[in] my control structure.
+        * @param[in] adjust (1 to hand off 1 buffer) | (MDB_EOF when ending).
+        */
 static int ESECT
-mdb_env_cthr_toggle(mdb_copy *my, int st)
+mdb_env_cthr_toggle(mdb_copy *my, int adjust)
 {
-       int toggle = my->mc_toggle ^ 1;
        pthread_mutex_lock(&my->mc_mutex);
-       if (my->mc_status) {
-               pthread_mutex_unlock(&my->mc_mutex);
-               return my->mc_status;
-       }
-       while (my->mc_new == 1)
-               pthread_cond_wait(&my->mc_cond, &my->mc_mutex);
-       my->mc_new = st;
-       my->mc_toggle = toggle;
+       my->mc_new += adjust;
        pthread_cond_signal(&my->mc_cond);
+       while (my->mc_new & 2)          /* both buffers in use */
+               pthread_cond_wait(&my->mc_cond, &my->mc_mutex);
        pthread_mutex_unlock(&my->mc_mutex);
-       return 0;
+
+       my->mc_toggle ^= (adjust & 1);
+       /* Both threads reset mc_wlen, to be safe from threading errors */
+       my->mc_wlen[my->mc_toggle] = 0;
+       return my->mc_error;
 }
 
        /** Depth-first tree traversal for compacting copy. */
 static int ESECT
 mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
 {
-       MDB_cursor mc;
+       MDB_cursor mc = {0};
        MDB_node *ni;
        MDB_page *mo, *mp, *leaf;
        char *buf, *ptr;
@@ -9742,8 +9741,8 @@ mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
                return MDB_SUCCESS;
 
        mc.mc_snum = 1;
-       mc.mc_top = 0;
        mc.mc_txn = my->mc_txn;
+       mc.mc_flags = my->mc_txn->mt_flags & (C_ORIG_RDONLY|C_WRITEMAP);
 
        rc = mdb_page_get(&mc, *pg, &mc.mc_pg[0], NULL);
        if (rc)
@@ -9789,6 +9788,7 @@ mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
                                                }
 
                                                memcpy(&pg, NODEDATA(ni), sizeof(pg));
+                                               memcpy(NODEDATA(ni), &my->mc_next_pgno, sizeof(pgno_t));
                                                rc = mdb_page_get(&mc, pg, &omp, NULL);
                                                if (rc)
                                                        goto done;
@@ -9811,7 +9811,6 @@ mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
                                                                goto done;
                                                        toggle = my->mc_toggle;
                                                }
-                                               memcpy(NODEDATA(ni), &mo->mp_pgno, sizeof(pgno_t));
                                        } else if (ni->mn_flags & F_SUBDATA) {
                                                MDB_db db;
 
@@ -9889,47 +9888,56 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd)
 {
        MDB_meta *mm;
        MDB_page *mp;
-       mdb_copy my;
+       mdb_copy my = {0};
        MDB_txn *txn = NULL;
        pthread_t thr;
-       int rc;
+       pgno_t root, new_root;
+       int rc = MDB_SUCCESS;
 
 #ifdef _WIN32
-       my.mc_mutex = CreateMutex(NULL, FALSE, NULL);
-       my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
+       if (!(my.mc_mutex = CreateMutex(NULL, FALSE, NULL)) ||
+               !(my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL))) {
+               rc = ErrCode();
+               goto done;
+       }
        my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_os_psize);
-       if (my.mc_wbuf[0] == NULL)
-               return errno;
+       if (my.mc_wbuf[0] == NULL) {
+               /* _aligned_malloc() sets errno, but we use Windows error codes */
+               rc = ERROR_NOT_ENOUGH_MEMORY;
+               goto done;
+       }
 #else
-       pthread_mutex_init(&my.mc_mutex, NULL);
-       pthread_cond_init(&my.mc_cond, NULL);
+       if ((rc = pthread_mutex_init(&my.mc_mutex, NULL)) != 0)
+               return rc;
+       if ((rc = pthread_cond_init(&my.mc_cond, NULL)) != 0)
+               goto done2;
 #ifdef HAVE_MEMALIGN
        my.mc_wbuf[0] = memalign(env->me_os_psize, MDB_WBUF*2);
-       if (my.mc_wbuf[0] == NULL)
-               return errno;
+       if (my.mc_wbuf[0] == NULL) {
+               rc = errno;
+               goto done;
+       }
 #else
-       rc = posix_memalign((void **)&my.mc_wbuf[0], env->me_os_psize, MDB_WBUF*2);
-       if (rc)
-               return rc;
+       {
+               void *p;
+               if ((rc = posix_memalign(&p, env->me_os_psize, MDB_WBUF*2)) != 0)
+                       goto done;
+               my.mc_wbuf[0] = p;
+       }
 #endif
 #endif
        memset(my.mc_wbuf[0], 0, MDB_WBUF*2);
        my.mc_wbuf[1] = my.mc_wbuf[0] + MDB_WBUF;
-       my.mc_wlen[0] = 0;
-       my.mc_wlen[1] = 0;
-       my.mc_olen[0] = 0;
-       my.mc_olen[1] = 0;
        my.mc_next_pgno = NUM_METAS;
-       my.mc_status = 0;
-       my.mc_new = 1;
-       my.mc_toggle = 0;
        my.mc_env = env;
        my.mc_fd = fd;
-       THREAD_CREATE(thr, mdb_env_copythr, &my);
+       rc = THREAD_CREATE(thr, mdb_env_copythr, &my);
+       if (rc)
+               goto done;
 
        rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
        if (rc)
-               return rc;
+               goto finish;
 
        mp = (MDB_page *)my.mc_wbuf[0];
        memset(mp, 0, NUM_METAS * env->me_psize);
@@ -9945,57 +9953,64 @@ 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;
                mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
                while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0)
                        freecount += *(MDB_ID *)data.mv_data;
+               if (rc != MDB_NOTFOUND)
+                       goto finish;
                freecount += txn->mt_dbs[FREE_DBI].md_branch_pages +
                        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;
-       pthread_mutex_lock(&my.mc_mutex);
-       while(my.mc_new)
-               pthread_cond_wait(&my.mc_cond, &my.mc_mutex);
-       pthread_mutex_unlock(&my.mc_mutex);
-       rc = mdb_env_cwalk(&my, &txn->mt_dbs[MAIN_DBI].md_root, 0);
-       if (rc == MDB_SUCCESS && my.mc_wlen[my.mc_toggle])
-               rc = mdb_env_cthr_toggle(&my, 1);
-       mdb_env_cthr_toggle(&my, -1);
-       pthread_mutex_lock(&my.mc_mutex);
-       while(my.mc_new)
-               pthread_cond_wait(&my.mc_cond, &my.mc_mutex);
-       pthread_mutex_unlock(&my.mc_mutex);
-       THREAD_FINISH(thr);
+       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)
+               my.mc_error = rc;
+       mdb_env_cthr_toggle(&my, 1 | MDB_EOF);
+       rc = THREAD_FINISH(thr);
        mdb_txn_abort(txn);
+
+done:
 #ifdef _WIN32
-       CloseHandle(my.mc_cond);
-       CloseHandle(my.mc_mutex);
-       _aligned_free(my.mc_wbuf[0]);
+       if (my.mc_wbuf[0]) _aligned_free(my.mc_wbuf[0]);
+       if (my.mc_cond)  CloseHandle(my.mc_cond);
+       if (my.mc_mutex) CloseHandle(my.mc_mutex);
 #else
+       free(my.mc_wbuf[0]);
        pthread_cond_destroy(&my.mc_cond);
+done2:
        pthread_mutex_destroy(&my.mc_mutex);
-       free(my.mc_wbuf[0]);
 #endif
-       return rc;
+       return rc ? rc : my.mc_error;
 }
 
        /** Copy environment as-is. */
@@ -10141,7 +10156,7 @@ mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags)
 #ifdef _WIN32
        rc = utf8_to_utf16(lpath, -1, &wpath, NULL);
        if (rc)
-               return rc;
+               goto leave;
        newfd = CreateFileW(wpath, GENERIC_WRITE, 0, NULL, CREATE_NEW,
                                FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH, NULL);
        free(wpath);
@@ -10505,8 +10520,11 @@ mdb_drop0(MDB_cursor *mc, int subs)
 
                /* DUPSORT sub-DBs have no ovpages/DBs. Omit scanning leaves.
                 * This also avoids any P_LEAF2 pages, which have no nodes.
+                * Also if the DB doesn't have sub-DBs and has no overflow
+                * pages, omit scanning leaves.
                 */
-               if (mc->mc_flags & C_SUB)
+               if ((mc->mc_flags & C_SUB) ||
+                       (!subs && !mc->mc_db->md_overflow_pages))
                        mdb_cursor_pop(mc);
 
                mdb_cursor_copy(mc, &mx);
@@ -10533,6 +10551,9 @@ mdb_drop0(MDB_cursor *mc, int subs)
                                                        pg, omp->mp_pages);
                                                if (rc)
                                                        goto done;
+                                               mc->mc_db->md_overflow_pages -= omp->mp_pages;
+                                               if (!mc->mc_db->md_overflow_pages && !subs)
+                                                       break;
                                        } else if (subs && (ni->mn_flags & F_SUBDATA)) {
                                                mdb_xcursor_init1(mc, ni);
                                                rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
@@ -10540,6 +10561,8 @@ mdb_drop0(MDB_cursor *mc, int subs)
                                                        goto done;
                                        }
                                }
+                               if (!subs && !mc->mc_db->md_overflow_pages)
+                                       goto pop;
                        } else {
                                if ((rc = mdb_midl_need(&txn->mt_free_pgs, n)) != 0)
                                        goto done;
@@ -10561,6 +10584,7 @@ mdb_drop0(MDB_cursor *mc, int subs)
                                /* no more siblings, go back to beginning
                                 * of previous level.
                                 */
+pop:
                                mdb_cursor_pop(mc);
                                mc->mc_ki[0] = 0;
                                for (i=1; i<mc->mc_snum; i++) {
@@ -10574,10 +10598,8 @@ mdb_drop0(MDB_cursor *mc, int subs)
 done:
                if (rc)
                        txn->mt_flags |= MDB_TXN_ERROR;
-#ifdef MDB_VL32
                /* drop refcount for mx's pages */
-               mdb_cursor_unref(&mx);
-#endif
+               MDB_CURSOR_UNREF(&mx, 0);
        } else if (rc == MDB_NOTFOUND) {
                rc = MDB_SUCCESS;
        }
@@ -10697,7 +10719,7 @@ mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
                if (mr[i].mr_pid) {
                        txnid_t txnid = mr[i].mr_txnid;
                        sprintf(buf, txnid == (txnid_t)-1 ?
-                               "%10d %"Z"x -\n" : "%10d %"Z"x %"Y"u\n",
+                               "%10d %"Z"x -\n" : "%10d %"Z"x %"Yu"\n",
                                (int)mr[i].mr_pid, (size_t)mr[i].mr_tid, txnid);
                        if (first) {
                                first = 0;
@@ -10802,7 +10824,7 @@ mdb_reader_check0(MDB_env *env, int rlocked, int *dead)
                                        }
                                        for (; j<rdrs; j++)
                                                        if (mr[j].mr_pid == pid) {
-                                                               DPRINTF(("clear stale reader pid %u txn %"Y"d",
+                                                               DPRINTF(("clear stale reader pid %u txn %"Yd,
                                                                        (unsigned) pid, mr[j].mr_txnid));
                                                                mr[j].mr_pid = 0;
                                                                count++;