]> git.sur5r.net Git - openldap/blobdiff - libraries/liblmdb/mdb.c
ITS#8321 reorganize page_split fixups
[openldap] / libraries / liblmdb / mdb.c
index 0bff10e75933687912cb62909bf0f3e6bbac2efd..c5174e983acb0923da159fe7874f8337de8d3169 100644 (file)
@@ -238,7 +238,9 @@ typedef SSIZE_T     ssize_t;
 #define MDB_OWNERDEAD  EOWNERDEAD      /**< #LOCK_MUTEX0() result if dead owner */
 #endif
 
-
+#ifdef __GLIBC__
+#define        GLIBC_VER       ((__GLIBC__ << 16 )| __GLIBC_MINOR__)
+#endif
 /** Some platforms define the EOWNERDEAD error code
  * even though they don't support Robust Mutexes.
  * Compile with -DMDB_USE_ROBUST=0, or use some other
@@ -248,12 +250,19 @@ typedef SSIZE_T   ssize_t;
  * either.)
  */
 #ifndef MDB_USE_ROBUST
-/* Android currently lacks Robust Mutex support */
-#if defined(ANDROID) && defined(MDB_USE_POSIX_MUTEX) && !defined(MDB_USE_ROBUST)
-#define MDB_USE_ROBUST 0
-#else
-#define MDB_USE_ROBUST 1
-#endif
+/* Android currently lacks Robust Mutex support. So does glibc < 2.4. */
+# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \
+       (defined(__GLIBC__) && GLIBC_VER < 0x020004))
+#  define MDB_USE_ROBUST       0
+# else
+#  define MDB_USE_ROBUST       1
+/* glibc < 2.10 only provided _np API */
+#  if defined(__GLIBC__) && GLIBC_VER < 0x02000a
+#   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 */
 
 #if defined(MDB_OWNERDEAD) && MDB_USE_ROBUST
@@ -394,10 +403,13 @@ static int mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc);
  *
  *     @note If O_DSYNC is undefined but exists in /usr/include,
  * preferably set some compiler flag to get the definition.
- * Otherwise compile with the less efficient -DMDB_DSYNC=O_SYNC.
  */
 #ifndef MDB_DSYNC
+# ifdef O_DSYNC
 # define MDB_DSYNC     O_DSYNC
+# else
+# define MDB_DSYNC     O_SYNC
+# endif
 #endif
 #endif
 
@@ -1149,7 +1161,6 @@ struct MDB_cursor {
 #define C_EOF  0x02                    /**< No more data */
 #define C_SUB  0x04                    /**< Cursor is a sub-cursor */
 #define C_DEL  0x08                    /**< last op was a cursor_del */
-#define C_SPLITTING    0x20            /**< Cursor is in page_split */
 #define C_UNTRACK      0x40            /**< Un-track cursor when closing */
 /** @} */
        unsigned int    mc_flags;       /**< @ref mdb_cursor */
@@ -1315,7 +1326,7 @@ static int  mdb_node_add(MDB_cursor *mc, indx_t indx,
                            MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags);
 static void mdb_node_del(MDB_cursor *mc, int ksize);
 static void mdb_node_shrink(MDB_page *mp, indx_t indx);
-static int     mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst);
+static int     mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft);
 static int  mdb_node_read(MDB_txn *txn, MDB_node *leaf, MDB_val *data);
 static size_t  mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data);
 static size_t  mdb_branch_size(MDB_env *env, MDB_val *key);
@@ -1360,6 +1371,8 @@ static MDB_cmp_func       mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int, mdb_cmp_cint, mdb_
 static SECURITY_DESCRIPTOR mdb_null_sd;
 static SECURITY_ATTRIBUTES mdb_all_sa;
 static int mdb_sec_inited;
+
+static int utf8_to_utf16(const char *src, int srcsize, wchar_t **dst, int *dstsize);
 #endif
 
 /** Return the library version info. */
@@ -1591,7 +1604,7 @@ mdb_cursor_chk(MDB_cursor *mc)
        MDB_node *node;
        MDB_page *mp;
 
-       if (!mc->mc_snum && !(mc->mc_flags & C_INITIALIZED)) return;
+       if (!mc->mc_snum || !(mc->mc_flags & C_INITIALIZED)) return;
        for (i=0; i<mc->mc_top; i++) {
                mp = mc->mc_pg[i];
                node = NODEPTR(mp, mc->mc_ki[i]);
@@ -2476,14 +2489,15 @@ mdb_cursor_shadow(MDB_txn *src, MDB_txn *dst)
                                *bk = *mc;
                                mc->mc_backup = bk;
                                mc->mc_db = &dst->mt_dbs[i];
-                               /* Kill pointers into src - and dst to reduce abuse: The
-                                * user may not use mc until dst ends. Otherwise we'd...
+                               /* Kill pointers into src to reduce abuse: The
+                                * user may not use mc until dst ends. But we need a valid
+                                * txn pointer here for cursor fixups to keep working.
                                 */
-                               mc->mc_txn    = NULL;   /* ...set this to dst */
-                               mc->mc_dbflag = NULL;   /* ...and &dst->mt_dbflags[i] */
+                               mc->mc_txn    = dst;
+                               mc->mc_dbflag = &dst->mt_dbflags[i];
                                if ((mx = mc->mc_xcursor) != NULL) {
                                        *(MDB_xcursor *)(bk+1) = *mx;
-                                       mx->mx_cursor.mc_txn = NULL; /* ...and dst. */
+                                       mx->mx_cursor.mc_txn = dst;
                                }
                                mc->mc_next = dst->mt_cursors[i];
                                dst->mt_cursors[i] = mc;
@@ -3462,7 +3476,7 @@ mdb_txn_commit(MDB_txn *txn)
                }
 
                /* Append our loose page list to parent's */
-               for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(lp))
+               for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(*lp))
                        ;
                *lp = txn->mt_loose_pgs;
                parent->mt_loose_count += txn->mt_loose_count;
@@ -4428,9 +4442,12 @@ mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl)
        off_t size, rsize;
 
 #ifdef _WIN32
-       env->me_lfd = CreateFileA(lpath, GENERIC_READ|GENERIC_WRITE,
+       wchar_t *wlpath;
+       utf8_to_utf16(lpath, -1, &wlpath, NULL);
+       env->me_lfd = CreateFileW(wlpath, GENERIC_READ|GENERIC_WRITE,
                FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
                FILE_ATTRIBUTE_NORMAL, NULL);
+       free(wlpath);
 #else
        env->me_lfd = open(lpath, O_RDWR|O_CREAT|MDB_CLOEXEC, mode);
 #endif
@@ -4648,6 +4665,9 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
 {
        int             oflags, rc, len, excl = -1;
        char *lpath, *dpath;
+#ifdef _WIN32
+       wchar_t *wpath;
+#endif
 
        if (env->me_fd!=INVALID_HANDLE_VALUE || (flags & ~(CHANGEABLE|CHANGELESS)))
                return EINVAL;
@@ -4711,8 +4731,10 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
                len = OPEN_ALWAYS;
        }
        mode = FILE_ATTRIBUTE_NORMAL;
-       env->me_fd = CreateFileA(dpath, oflags, FILE_SHARE_READ|FILE_SHARE_WRITE,
+       utf8_to_utf16(dpath, -1, &wpath, NULL);
+       env->me_fd = CreateFileW(wpath, oflags, FILE_SHARE_READ|FILE_SHARE_WRITE,
                NULL, len, mode, NULL);
+       free(wpath);
 #else
        if (F_ISSET(flags, MDB_RDONLY))
                oflags = O_RDONLY;
@@ -4741,9 +4763,11 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
                         */
 #ifdef _WIN32
                        len = OPEN_EXISTING;
-                       env->me_mfd = CreateFileA(dpath, oflags,
+                       utf8_to_utf16(dpath, -1, &wpath, NULL);
+                       env->me_mfd = CreateFileW(wpath, oflags,
                                FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, len,
                                mode | FILE_FLAG_WRITE_THROUGH, NULL);
+                       free(wpath);
 #else
                        oflags &= ~O_CREAT;
                        env->me_mfd = open(dpath, oflags | MDB_DSYNC, mode);
@@ -5437,6 +5461,7 @@ mdb_ovpage_free(MDB_cursor *mc, MDB_page *mp)
                                return MDB_CORRUPTED;
                        }
                }
+               txn->mt_dirty_room++;
                if (!(env->me_flags & MDB_WRITEMAP))
                        mdb_dpage_free(env, mp);
 release:
@@ -6379,16 +6404,18 @@ fix_parent:
                         * update branch key if there is a parent page
                         */
                        if (mc->mc_top && !mc->mc_ki[mc->mc_top]) {
-                               unsigned short top = mc->mc_top;
+                               unsigned short dtop = 1;
                                mc->mc_top--;
                                /* slot 0 is always an empty key, find real slot */
-                               while (mc->mc_top && !mc->mc_ki[mc->mc_top])
+                               while (mc->mc_top && !mc->mc_ki[mc->mc_top]) {
                                        mc->mc_top--;
+                                       dtop++;
+                               }
                                if (mc->mc_ki[mc->mc_top])
                                        rc2 = mdb_update_key(mc, key);
                                else
                                        rc2 = MDB_SUCCESS;
-                               mc->mc_top top;
+                               mc->mc_top += dtop;
                                if (rc2)
                                        return rc2;
                        }
@@ -6569,6 +6596,7 @@ current:
                                                return ENOMEM;
                                        id2.mid = pg;
                                        id2.mptr = np;
+                                       /* 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);
                                        if (!(flags & MDB_RESERVE)) {
@@ -6682,6 +6710,7 @@ put_sub:
                                MDB_xcursor *mx = mc->mc_xcursor;
                                unsigned i = mc->mc_top;
                                MDB_page *mp = mc->mc_pg[i];
+                               int nkeys = NUMKEYS(mp);
 
                                for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
                                        if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
@@ -6689,9 +6718,9 @@ put_sub:
                                        if (m2->mc_pg[i] == mp) {
                                                if (m2->mc_ki[i] == mc->mc_ki[i]) {
                                                        mdb_xcursor_init2(m2, mx, new_dupdata);
-                                               } else if (!insert_key) {
+                                               } else if (!insert_key && m2->mc_ki[i] < nkeys) {
                                                        MDB_node *n2 = NODEPTR(mp, m2->mc_ki[i]);
-                                                       if (!(n2->mn_flags & F_SUBDATA))
+                                                       if ((n2->mn_flags & (F_SUBDATA|F_DUPDATA)) == F_DUPDATA)
                                                                m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(n2);
                                                }
                                        }
@@ -7495,10 +7524,26 @@ mdb_update_key(MDB_cursor *mc, MDB_val *key)
 static void
 mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst);
 
+/** Track a temporary cursor */
+#define CURSOR_TMP_TRACK(mc, mn, dummy, tracked) \
+       if (mc->mc_flags & C_SUB) { \
+               dummy.mc_flags =  C_INITIALIZED; \
+               dummy.mc_xcursor = (MDB_xcursor *)&mn; \
+               tracked = &dummy; \
+       } else { \
+               tracked = &mn; \
+       } \
+       tracked->mc_next = mc->mc_txn->mt_cursors[mc->mc_dbi]; \
+       mc->mc_txn->mt_cursors[mc->mc_dbi] = tracked
+
+/** Stop tracking a temporary cursor */
+#define CURSOR_TMP_UNTRACK(mc, tracked) \
+       mc->mc_txn->mt_cursors[mc->mc_dbi] = tracked->mc_next
+
 /** Move a node from csrc to cdst.
  */
 static int
-mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
+mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
 {
        MDB_node                *srcnode;
        MDB_val          key, data;
@@ -7596,32 +7641,52 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                /* Adjust other cursors pointing to mp */
                MDB_cursor *m2, *m3;
                MDB_dbi dbi = csrc->mc_dbi;
-               MDB_page *mp;
-
-               mp = cdst->mc_pg[csrc->mc_top];
-               for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
-                       if (csrc->mc_flags & C_SUB)
-                               m3 = &m2->mc_xcursor->mx_cursor;
-                       else
-                               m3 = m2;
-                       if (m3 == cdst) continue;
-                       if (m3->mc_pg[csrc->mc_top] == mp && m3->mc_ki[csrc->mc_top] >=
-                               cdst->mc_ki[csrc->mc_top]) {
-                               m3->mc_ki[csrc->mc_top]++;
+               MDB_page *mpd, *mps;
+
+               mps = csrc->mc_pg[csrc->mc_top];
+               /* If we're adding on the left, bump others up */
+               if (fromleft) {
+                       mpd = cdst->mc_pg[csrc->mc_top];
+                       for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+                               if (csrc->mc_flags & C_SUB)
+                                       m3 = &m2->mc_xcursor->mx_cursor;
+                               else
+                                       m3 = m2;
+                               if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top)
+                                       continue;
+                               if (m3 != cdst &&
+                                       m3->mc_pg[csrc->mc_top] == mpd &&
+                                       m3->mc_ki[csrc->mc_top] >= cdst->mc_ki[csrc->mc_top]) {
+                                       m3->mc_ki[csrc->mc_top]++;
+                               }
+                               if (m3 !=csrc &&
+                                       m3->mc_pg[csrc->mc_top] == mps &&
+                                       m3->mc_ki[csrc->mc_top] == csrc->mc_ki[csrc->mc_top]) {
+                                       m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
+                                       m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+                                       m3->mc_ki[csrc->mc_top-1]++;
+                               }
                        }
-               }
-
-               mp = csrc->mc_pg[csrc->mc_top];
-               for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
-                       if (csrc->mc_flags & C_SUB)
-                               m3 = &m2->mc_xcursor->mx_cursor;
-                       else
-                               m3 = m2;
-                       if (m3 == csrc) continue;
-                       if (m3->mc_pg[csrc->mc_top] == mp && m3->mc_ki[csrc->mc_top] ==
-                               csrc->mc_ki[csrc->mc_top]) {
-                               m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
-                               m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+               } else
+               /* Adding on the right, bump others down */
+               {
+                       for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+                               if (csrc->mc_flags & C_SUB)
+                                       m3 = &m2->mc_xcursor->mx_cursor;
+                               else
+                                       m3 = m2;
+                               if (m3 == csrc) continue;
+                               if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top)
+                                       continue;
+                               if (m3->mc_pg[csrc->mc_top] == mps) {
+                                       if (!m3->mc_ki[csrc->mc_top]) {
+                                               m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
+                                               m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+                                               m3->mc_ki[csrc->mc_top-1]--;
+                                       } else {
+                                               m3->mc_ki[csrc->mc_top]--;
+                                       }
+                               }
                        }
                }
        }
@@ -7630,6 +7695,7 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
         */
        if (csrc->mc_ki[csrc->mc_top] == 0) {
                if (csrc->mc_ki[csrc->mc_top-1] != 0) {
+                       MDB_cursor dummy, *tracked;
                        if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
                                key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size);
                        } else {
@@ -7642,7 +7708,11 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                        mdb_cursor_copy(csrc, &mn);
                        mn.mc_snum--;
                        mn.mc_top--;
-                       if ((rc = mdb_update_key(&mn, &key)) != MDB_SUCCESS)
+                       /* We want mdb_rebalance to find mn when doing fixups */
+                       CURSOR_TMP_TRACK(csrc, mn, dummy, tracked);
+                       rc = mdb_update_key(&mn, &key);
+                       CURSOR_TMP_UNTRACK(csrc, tracked);
+                       if (rc)
                                return rc;
                }
                if (IS_BRANCH(csrc->mc_pg[csrc->mc_top])) {
@@ -7658,6 +7728,7 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
 
        if (cdst->mc_ki[cdst->mc_top] == 0) {
                if (cdst->mc_ki[cdst->mc_top-1] != 0) {
+                       MDB_cursor dummy, *tracked;
                        if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
                                key.mv_data = LEAF2KEY(cdst->mc_pg[cdst->mc_top], 0, key.mv_size);
                        } else {
@@ -7670,7 +7741,11 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                        mdb_cursor_copy(cdst, &mn);
                        mn.mc_snum--;
                        mn.mc_top--;
-                       if ((rc = mdb_update_key(&mn, &key)) != MDB_SUCCESS)
+                       /* We want mdb_rebalance to find mn when doing fixups */
+                       CURSOR_TMP_TRACK(cdst, mn, dummy, tracked);
+                       rc = mdb_update_key(&mn, &key);
+                       CURSOR_TMP_UNTRACK(cdst, tracked);
+                       if (rc)
                                return rc;
                }
                if (IS_BRANCH(cdst->mc_pg[cdst->mc_top])) {
@@ -7717,6 +7792,9 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
        if ((rc = mdb_page_touch(cdst)))
                return rc;
 
+       /* get dst page again now that we've touched it. */
+       pdst = cdst->mc_pg[cdst->mc_top];
+
        /* Move all nodes from src to dst.
         */
        j = nkeys = NUMKEYS(pdst);
@@ -7807,6 +7885,9 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
                                m3->mc_pg[top] = pdst;
                                m3->mc_ki[top] += nkeys;
                                m3->mc_ki[top-1] = cdst->mc_ki[top-1];
+                       } else if (m3->mc_pg[top-1] == csrc->mc_pg[top-1] &&
+                               m3->mc_ki[top-1] > csrc->mc_ki[top-1]) {
+                               m3->mc_ki[top-1]--;
                        }
                }
        }
@@ -7856,7 +7937,7 @@ static int
 mdb_rebalance(MDB_cursor *mc)
 {
        MDB_node        *node;
-       int rc;
+       int rc, fromleft;
        unsigned int ptop, minkeys, thresh;
        MDB_cursor      mn;
        indx_t oldki;
@@ -7907,7 +7988,8 @@ mdb_rebalance(MDB_cursor *mc)
                                                m3 = &m2->mc_xcursor->mx_cursor;
                                        else
                                                m3 = m2;
-                                       if (m3->mc_snum < mc->mc_snum) continue;
+                                       if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum))
+                                               continue;
                                        if (m3->mc_pg[0] == mp) {
                                                m3->mc_snum = 0;
                                                m3->mc_top = 0;
@@ -7942,9 +8024,11 @@ mdb_rebalance(MDB_cursor *mc)
                                                m3 = &m2->mc_xcursor->mx_cursor;
                                        else
                                                m3 = m2;
-                                       if (m3 == mc || m3->mc_snum < mc->mc_snum) continue;
+                                       if (m3 == mc) continue;
+                                       if (!(m3->mc_flags & C_INITIALIZED))
+                                               continue;
                                        if (m3->mc_pg[0] == mp) {
-                                               for (i=0; i<m3->mc_snum; i++) {
+                                               for (i=0; i<mc->mc_db->md_depth; i++) {
                                                        m3->mc_pg[i] = m3->mc_pg[i+1];
                                                        m3->mc_ki[i] = m3->mc_ki[i+1];
                                                }
@@ -7986,6 +8070,7 @@ mdb_rebalance(MDB_cursor *mc)
                        return rc;
                mn.mc_ki[mn.mc_top] = 0;
                mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]);
+               fromleft = 0;
        } else {
                /* There is at least one neighbor to the left.
                 */
@@ -7997,6 +8082,7 @@ mdb_rebalance(MDB_cursor *mc)
                        return rc;
                mn.mc_ki[mn.mc_top] = NUMKEYS(mn.mc_pg[mn.mc_top]) - 1;
                mc->mc_ki[mc->mc_top] = 0;
+               fromleft = 1;
        }
 
        DPRINTF(("found neighbor page %"Z"u (%u keys, %.1f%% full)",
@@ -8008,31 +8094,22 @@ mdb_rebalance(MDB_cursor *mc)
         * (A branch page must never have less than 2 keys.)
         */
        if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= thresh && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) {
-               rc = mdb_node_move(&mn, mc);
-               if (mc->mc_ki[mc->mc_top-1]) {
+               rc = mdb_node_move(&mn, mc, fromleft);
+               if (fromleft) {
+                       /* if we inserted on left, bump position up */
                        oldki++;
                }
        } else {
-               if (mc->mc_ki[ptop] == 0) {
+               if (!fromleft) {
                        rc = mdb_page_merge(&mn, mc);
                } else {
-                       MDB_cursor dummy;
+                       MDB_cursor dummy, *tracked;
                        oldki += NUMKEYS(mn.mc_pg[mn.mc_top]);
                        mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1;
                        /* We want mdb_rebalance to find mn when doing fixups */
-                       if (mc->mc_flags & C_SUB) {
-                               dummy.mc_next = mc->mc_txn->mt_cursors[mc->mc_dbi];
-                               mc->mc_txn->mt_cursors[mc->mc_dbi] = &dummy;
-                               dummy.mc_xcursor = (MDB_xcursor *)&mn;
-                       } else {
-                               mn.mc_next = mc->mc_txn->mt_cursors[mc->mc_dbi];
-                               mc->mc_txn->mt_cursors[mc->mc_dbi] = &mn;
-                       }
+                       CURSOR_TMP_TRACK(mc, mn, dummy, tracked);
                        rc = mdb_page_merge(mc, &mn);
-                       if (mc->mc_flags & C_SUB)
-                               mc->mc_txn->mt_cursors[mc->mc_dbi] = dummy.mc_next;
-                       else
-                               mc->mc_txn->mt_cursors[mc->mc_dbi] = mn.mc_next;
+                       CURSOR_TMP_UNTRACK(mc, tracked);
                        mdb_cursor_copy(&mn, mc);
                }
                mc->mc_flags &= ~C_EOF;
@@ -8070,7 +8147,7 @@ mdb_cursor_del0(MDB_cursor *mc)
                                        if (m3->mc_ki[mc->mc_top] > ki)
                                                m3->mc_ki[mc->mc_top]--;
                                        else if (mc->mc_db->md_flags & MDB_DUPSORT)
-                                               m3->mc_xcursor->mx_cursor.mc_flags |= C_EOF;
+                                               m3->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
                                }
                        }
                }
@@ -8217,12 +8294,19 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
        rp->mp_pad = mp->mp_pad;
        DPRINTF(("new right sibling: page %"Z"u", rp->mp_pgno));
 
-       if (mc->mc_snum < 2) {
+       /* Usually when splitting the root page, the cursor
+        * height is 1. But when called from mdb_update_key,
+        * the cursor height may be greater because it walks
+        * up the stack while finding the branch slot to update.
+        */
+       if (mc->mc_top < 1) {
                if ((rc = mdb_page_new(mc, P_BRANCH, 1, &pp)))
                        goto done;
                /* shift current top to make room for new parent */
-               mc->mc_pg[1] = mc->mc_pg[0];
-               mc->mc_ki[1] = mc->mc_ki[0];
+               for (i=mc->mc_snum; i>0; i--) {
+                       mc->mc_pg[i] = mc->mc_pg[i-1];
+                       mc->mc_ki[i] = mc->mc_ki[i-1];
+               }
                mc->mc_pg[0] = pp;
                mc->mc_ki[0] = 0;
                mc->mc_db->md_root = pp->mp_pgno;
@@ -8238,15 +8322,14 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                        mc->mc_db->md_depth--;
                        goto done;
                }
-               mc->mc_snum = 2;
-               mc->mc_top = 1;
+               mc->mc_snum++;
+               mc->mc_top++;
                ptop = 0;
        } else {
                ptop = mc->mc_top-1;
                DPRINTF(("parent branch page is %"Z"u", mc->mc_pg[ptop]->mp_pgno));
        }
 
-       mc->mc_flags |= C_SPLITTING;
        mdb_cursor_copy(mc, &mn);
        mn.mc_pg[mn.mc_top] = rp;
        mn.mc_ki[ptop] = mc->mc_ki[ptop]+1;
@@ -8297,8 +8380,6 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                rp->mp_lower += sizeof(indx_t);
                                rp->mp_upper -= ksize - sizeof(indx_t);
                                mc->mc_ki[mc->mc_top] = x;
-                               mc->mc_pg[mc->mc_top] = rp;
-                               mc->mc_ki[ptop]++;
                        }
                } else {
                        int psize, nsize, k;
@@ -8391,21 +8472,20 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
        /* Copy separator key to the parent.
         */
        if (SIZELEFT(mn.mc_pg[ptop]) < mdb_branch_size(env, &sepkey)) {
+               int snum = mc->mc_snum;
+               MDB_cursor dummy, *tracked;
                mn.mc_snum--;
                mn.mc_top--;
                did_split = 1;
+               /* We want other splits to find mn when doing fixups */
+               CURSOR_TMP_TRACK(mc, mn, dummy, tracked);
                rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0);
+               CURSOR_TMP_UNTRACK(mc, tracked);
                if (rc)
                        goto done;
 
                /* root split? */
-               if (mn.mc_snum == mc->mc_snum) {
-                       mc->mc_pg[mc->mc_snum] = mc->mc_pg[mc->mc_top];
-                       mc->mc_ki[mc->mc_snum] = mc->mc_ki[mc->mc_top];
-                       mc->mc_pg[mc->mc_top] = mc->mc_pg[ptop];
-                       mc->mc_ki[mc->mc_top] = mc->mc_ki[ptop];
-                       mc->mc_snum++;
-                       mc->mc_top++;
+               if (mc->mc_snum > snum) {
                        ptop++;
                }
                /* Right page might now have changed parent.
@@ -8431,7 +8511,6 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                rc = mdb_node_add(&mn, mn.mc_ki[ptop], &sepkey, NULL, rp->mp_pgno, 0);
                mn.mc_top++;
        }
-       mc->mc_flags ^= C_SPLITTING;
        if (rc != MDB_SUCCESS) {
                goto done;
        }
@@ -8501,11 +8580,6 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                /* reset back to original page */
                if (newindx < split_indx) {
                        mc->mc_pg[mc->mc_top] = mp;
-                       if (nflags & MDB_RESERVE) {
-                               node = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
-                               if (!(node->mn_flags & F_BIGDATA))
-                                       newdata->mv_data = NODEDATA(node);
-                       }
                } else {
                        mc->mc_pg[mc->mc_top] = rp;
                        mc->mc_ki[ptop]++;
@@ -8519,13 +8593,32 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                }
                        }
                }
+               if (nflags & MDB_RESERVE) {
+                       node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+                       if (!(node->mn_flags & F_BIGDATA))
+                               newdata->mv_data = NODEDATA(node);
+               }
+       } else {
+               if (newindx >= split_indx) {
+                       mc->mc_pg[mc->mc_top] = rp;
+                       mc->mc_ki[ptop]++;
+                       /* Make sure mc_ki is still valid.
+                        */
+                       if (mn.mc_pg[ptop] != mc->mc_pg[ptop] &&
+                               mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) {
+                               for (i=0; i<=ptop; i++) {
+                                       mc->mc_pg[i] = mn.mc_pg[i];
+                                       mc->mc_ki[i] = mn.mc_ki[i];
+                               }
+                       }
+               }
        }
 
        {
                /* Adjust other cursors pointing to mp */
                MDB_cursor *m2, *m3;
                MDB_dbi dbi = mc->mc_dbi;
-               int fixup = NUMKEYS(mp);
+               nkeys = NUMKEYS(mp);
 
                for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
                        if (mc->mc_flags & C_SUB)
@@ -8536,16 +8629,17 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                continue;
                        if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED))
                                continue;
-                       if (m3->mc_flags & C_SPLITTING)
-                               continue;
                        if (new_root) {
                                int k;
+                               /* sub cursors may be on different DB */
+                               if (m3->mc_pg[0] != mp)
+                                       continue;
                                /* root split */
                                for (k=new_root; k>=0; k--) {
                                        m3->mc_ki[k+1] = m3->mc_ki[k];
                                        m3->mc_pg[k+1] = m3->mc_pg[k];
                                }
-                               if (m3->mc_ki[0] >= split_indx) {
+                               if (m3->mc_ki[0] > nkeys) {
                                        m3->mc_ki[0] = 1;
                                } else {
                                        m3->mc_ki[0] = 0;
@@ -8557,10 +8651,13 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                        if (m3->mc_top >= mc->mc_top && m3->mc_pg[mc->mc_top] == mp) {
                                if (m3->mc_ki[mc->mc_top] >= newindx && !(nflags & MDB_SPLIT_REPLACE))
                                        m3->mc_ki[mc->mc_top]++;
-                               if (m3->mc_ki[mc->mc_top] >= fixup) {
+                               if (m3->mc_ki[mc->mc_top] >= nkeys) {
                                        m3->mc_pg[mc->mc_top] = rp;
-                                       m3->mc_ki[mc->mc_top] -= fixup;
-                                       m3->mc_ki[ptop] = mn.mc_ki[ptop];
+                                       m3->mc_ki[mc->mc_top] -= nkeys;
+                                       for (i=0; i<mc->mc_top; i++) {
+                                               m3->mc_ki[i] = mn.mc_ki[i];
+                                               m3->mc_pg[i] = mn.mc_pg[i];
+                                       }
                                }
                        } else if (!did_split && m3->mc_top >= ptop && m3->mc_pg[ptop] == mc->mc_pg[ptop] &&
                                m3->mc_ki[ptop] >= mc->mc_ki[ptop]) {
@@ -9098,6 +9195,9 @@ mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags)
        int rc, len;
        char *lpath;
        HANDLE newfd = INVALID_HANDLE_VALUE;
+#ifdef _WIN32
+       wchar_t *wpath;
+#endif
 
        if (env->me_flags & MDB_NOSUBDIR) {
                lpath = (char *)path;
@@ -9115,8 +9215,10 @@ mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags)
         * already in the OS cache.
         */
 #ifdef _WIN32
-       newfd = CreateFileA(lpath, GENERIC_WRITE, 0, NULL, CREATE_NEW,
+       utf8_to_utf16(lpath, -1, &wpath, NULL);
+       newfd = CreateFileW(wpath, GENERIC_WRITE, 0, NULL, CREATE_NEW,
                                FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH, NULL);
+       free(wpath);
 #else
        newfd = open(lpath, O_WRONLY|O_CREAT|O_EXCL, 0666);
 #endif
@@ -9821,3 +9923,22 @@ mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc)
 }
 #endif /* MDB_ROBUST_SUPPORTED */
 /** @} */
+
+#if defined(_WIN32)
+static int utf8_to_utf16(const char *src, int srcsize, wchar_t **dst, int *dstsize)
+{
+       int need;
+       wchar_t *result;
+       need = MultiByteToWideChar(CP_UTF8, 0, src, srcsize, NULL, 0);
+       if (need == 0xFFFD)
+               return EILSEQ;
+       if (need == 0)
+               return EINVAL;
+       result = malloc(sizeof(wchar_t) * need);
+       MultiByteToWideChar(CP_UTF8, 0, src, srcsize, result, need);
+       if (dstsize)
+               *dstsize = need;
+       *dst = result;
+       return 0;
+}
+#endif /* defined(_WIN32) */