]> git.sur5r.net Git - openldap/blobdiff - libraries/liblmdb/mdb.c
ITS#7589 Fix delete(uneven-sized MDB_DUPFIXED data).
[openldap] / libraries / liblmdb / mdb.c
index 8ea7e6e0989502ed3d1805b118084db09e282df8..5a1c1fd8df89a8c97ccd3d1a6d2644d237d655d8 100644 (file)
@@ -428,6 +428,9 @@ static txnid_t mdb_debug_start;
        /** Test if the flags \b f are set in a flag word \b w. */
 #define F_ISSET(w, f)   (((w) & (f)) == (f))
 
+       /** Round \b n up to an even number. */
+#define EVEN(n)                (((n) + 1U) & -2) /* sign-extending -2 to match n+1U */
+
        /**     Used for offsets within a single page.
         *      Since memory pages are typically 4 or 8KB in size, 12-13 bits,
         *      this is plenty.
@@ -1222,7 +1225,7 @@ void
 mdb_page_list(MDB_page *mp)
 {
        MDB_node *node;
-       unsigned int i, nkeys, nsize;
+       unsigned int i, nkeys, nsize, total = 0;
        MDB_val key;
        DKBUF;
 
@@ -1232,18 +1235,23 @@ mdb_page_list(MDB_page *mp)
                node = NODEPTR(mp, i);
                key.mv_size = node->mn_ksize;
                key.mv_data = node->mn_data;
-               nsize = NODESIZE + NODEKSZ(node) + sizeof(indx_t);
+               nsize = NODESIZE + key.mv_size;
                if (IS_BRANCH(mp)) {
                        fprintf(stderr, "key %d: page %"Z"u, %s\n", i, NODEPGNO(node),
                                DKEY(&key));
+                       total += nsize;
                } else {
                        if (F_ISSET(node->mn_flags, F_BIGDATA))
                                nsize += sizeof(pgno_t);
                        else
                                nsize += NODEDSZ(node);
+                       total += nsize;
+                       nsize += sizeof(indx_t);
                        fprintf(stderr, "key %d: nsize %d, %s\n", i, nsize, DKEY(&key));
                }
+               total = EVEN(total);
        }
+       fprintf(stderr, "Total: %d\n", total);
 }
 
 void
@@ -1669,11 +1677,11 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
 #else
        enum { Paranoid = 0, Max_retries = INT_MAX /*infinite*/ };
 #endif
-       int rc, n2 = num-1, retry = Max_retries;
+       int rc, retry = Max_retries;
        MDB_txn *txn = mc->mc_txn;
        MDB_env *env = txn->mt_env;
        pgno_t pgno, *mop = env->me_pghead;
-       unsigned i, j, k, mop_len = mop ? mop[0] : 0;
+       unsigned i, j, k, mop_len = mop ? mop[0] : 0, n2 = num-1;
        MDB_page *np;
        txnid_t oldest = 0, last;
        MDB_cursor_op op;
@@ -1693,13 +1701,13 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
                /* Seek a big enough contiguous page range. Prefer
                 * pages at the tail, just truncating the list.
                 */
-               if (mop_len >= (unsigned)num) {
+               if (mop_len > n2) {
                        i = mop_len;
                        do {
                                pgno = mop[i];
                                if (mop[i-n2] == pgno+n2)
                                        goto search_done;
-                       } while (--i >= (unsigned)num);
+                       } while (--i > n2);
                        if (Max_retries < INT_MAX && --retry < 0)
                                break;
                }
@@ -5353,6 +5361,7 @@ set1:
                                if (op == MDB_GET_BOTH || rc > 0)
                                        return MDB_NOTFOUND;
                                rc = 0;
+                               *data = d2;
                        }
 
                } else {
@@ -5815,9 +5824,8 @@ more:
                                if (flags == MDB_CURRENT)
                                        goto current;
 
-                               dkey = olddata;
 #if UINT_MAX < SIZE_MAX
-                               if (mc->mc_dbx->md_dcmp == mdb_cmp_int && dkey.mv_size == sizeof(size_t))
+                               if (mc->mc_dbx->md_dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t))
 #ifdef MISALIGNED_OK
                                        mc->mc_dbx->md_dcmp = mdb_cmp_long;
 #else
@@ -5825,7 +5833,7 @@ more:
 #endif
 #endif
                                /* if data matches, skip it */
-                               if (!mc->mc_dbx->md_dcmp(data, &dkey)) {
+                               if (!mc->mc_dbx->md_dcmp(data, &olddata)) {
                                        if (flags & MDB_NODUPDATA)
                                                rc = MDB_KEYEXIST;
                                        else if (flags & MDB_MULTIPLE)
@@ -5835,9 +5843,11 @@ more:
                                        return rc;
                                }
 
+                               /* Back up original data item */
+                               dkey.mv_size = olddata.mv_size;
+                               dkey.mv_data = memcpy(dbuf, olddata.mv_data, olddata.mv_size);
+
                                /* create a fake page for the dup items */
-                               memcpy(dbuf, dkey.mv_data, dkey.mv_size);
-                               dkey.mv_data = dbuf;
                                fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP;
                                fp->mp_lower = PAGEHDRSZ;
                                xdata.mv_size = PAGEHDRSZ + dkey.mv_size + data->mv_size;
@@ -5864,8 +5874,8 @@ more:
                                switch (flags) {
                                default:
                                        if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) {
-                                               offset = NODESIZE + sizeof(indx_t) + data->mv_size;
-                                               offset += offset & 1;
+                                               offset = EVEN(NODESIZE + sizeof(indx_t) +
+                                                       data->mv_size);
                                                break;
                                        }
                                        offset = fp->mp_pad;
@@ -6249,9 +6259,8 @@ mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data)
                /* put on overflow page */
                sz -= data->mv_size - sizeof(pgno_t);
        }
-       sz += sz & 1;
 
-       return sz + sizeof(indx_t);
+       return EVEN(sz + sizeof(indx_t));
 }
 
 /** Calculate the size of a branch node.
@@ -6345,7 +6354,7 @@ mdb_node_add(MDB_cursor *mc, indx_t indx,
                        /* Put data on overflow page. */
                        DPRINTF(("data size is %"Z"u, node would be %"Z"u, put data on overflow page",
                            data->mv_size, node_size+data->mv_size));
-                       node_size += sizeof(pgno_t) + (node_size & 1);
+                       node_size = EVEN(node_size + sizeof(pgno_t));
                        if ((ssize_t)node_size > room)
                                goto full;
                        if ((rc = mdb_page_new(mc, P_OVERFLOW, ovpages, &ofp)))
@@ -6357,7 +6366,7 @@ mdb_node_add(MDB_cursor *mc, indx_t indx,
                        node_size += data->mv_size;
                }
        }
-       node_size += node_size & 1;
+       node_size = EVEN(node_size);
        if ((ssize_t)node_size > room)
                goto full;
 
@@ -6458,7 +6467,7 @@ mdb_node_del(MDB_page *mp, indx_t indx, int ksize)
                else
                        sz += NODEDSZ(node);
        }
-       sz += sz & 1;
+       sz = EVEN(sz);
 
        ptr = mp->mp_ptrs[indx];
        numkeys = NUMKEYS(mp);
@@ -6488,25 +6497,22 @@ mdb_node_shrink(MDB_page *mp, indx_t indx)
        MDB_node *node;
        MDB_page *sp, *xp;
        char *base;
-       int osize, nsize;
-       int delta;
+       int nsize, delta;
        indx_t           i, numkeys, ptr;
 
        node = NODEPTR(mp, indx);
        sp = (MDB_page *)NODEDATA(node);
-       osize = NODEDSZ(node);
-
-       delta = sp->mp_upper - sp->mp_lower;
-       SETDSZ(node, osize - delta);
+       delta = SIZELEFT(sp);
        xp = (MDB_page *)((char *)sp + delta);
 
        /* shift subpage upward */
        if (IS_LEAF2(sp)) {
                nsize = NUMKEYS(sp) * sp->mp_pad;
+               if (nsize & 1)
+                       return;         /* do not make the node uneven-sized */
                memmove(METADATA(xp), METADATA(sp), nsize);
        } else {
                int i;
-               nsize = osize - sp->mp_upper;
                numkeys = NUMKEYS(sp);
                for (i=numkeys-1; i>=0; i--)
                        xp->mp_ptrs[i] = sp->mp_ptrs[i] - delta;
@@ -6517,6 +6523,9 @@ mdb_node_shrink(MDB_page *mp, indx_t indx)
        xp->mp_pad = sp->mp_pad;
        COPY_PGNO(xp->mp_pgno, mp->mp_pgno);
 
+       nsize = NODEDSZ(node) - delta;
+       SETDSZ(node, nsize);
+
        /* shift lower nodes upward */
        ptr = mp->mp_ptrs[indx];
        numkeys = NUMKEYS(mp);
@@ -6752,7 +6761,7 @@ mdb_update_key(MDB_cursor *mc, MDB_val *key)
        MDB_node                *node;
        char                    *base;
        size_t                   len;
-       int                      delta, delta0;
+       int                              delta, ksize, oksize;
        indx_t                   ptr, i, numkeys, indx;
        DKBUF;
 
@@ -6774,12 +6783,12 @@ mdb_update_key(MDB_cursor *mc, MDB_val *key)
        }
 #endif
 
-       delta0 = delta = key->mv_size - node->mn_ksize;
+       /* Sizes must be 2-byte aligned. */
+       ksize = EVEN(key->mv_size);
+       oksize = EVEN(node->mn_ksize);
+       delta = ksize - oksize;
 
-       /* Must be 2-byte aligned. If new key is
-        * shorter by 1, the shift will be skipped.
-        */
-       delta += (delta & 1);
+       /* Shift node contents if EVEN(key length) changed. */
        if (delta) {
                if (delta > 0 && SIZELEFT(mp) < delta) {
                        pgno_t pgno;
@@ -6805,7 +6814,7 @@ mdb_update_key(MDB_cursor *mc, MDB_val *key)
        }
 
        /* But even if no shift was needed, update ksize */
-       if (delta0)
+       if (node->mn_ksize != key->mv_size)
                node->mn_ksize = key->mv_size;
 
        if (key->mv_size)
@@ -6837,7 +6846,6 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                return rc;
 
        if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
-               srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], 0);        /* fake */
                key.mv_size = csrc->mc_db->md_pad;
                key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top], key.mv_size);
                data.mv_size = 0;
@@ -7558,7 +7566,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                nsize = mdb_leaf_size(env, newkey, newdata);
                        else
                                nsize = mdb_branch_size(env, newkey);
-                       nsize += nsize & 1;
+                       nsize = EVEN(nsize);
 
                        /* grab a page to hold a temporary copy */
                        copy = mdb_page_malloc(mc->mc_txn, 1);
@@ -7615,7 +7623,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                                        else
                                                                psize += NODEDSZ(node);
                                                }
-                                               psize += psize & 1;
+                                               psize = EVEN(psize);
                                        }
                                        if (psize > pmax || i == k-j) {
                                                split_indx = i + (j<0);
@@ -8361,7 +8369,6 @@ int mdb_reader_check(MDB_env *env, int *dead)
                return ENOMEM;
        pids[0] = 0;
        mr = env->me_txns->mti_readers;
-       j = 0;
        for (i=0; i<rdrs; i++) {
                if (mr[i].mr_pid && mr[i].mr_pid != env->me_pid) {
                        pid = mr[i].mr_pid;