mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-06 18:44:13 +08:00
mdbx: refine checking inside page_get()
.
This commit is contained in:
parent
5afc5c4e8c
commit
fa854e40c3
193
src/core.c
193
src/core.c
@ -13595,8 +13595,94 @@ static __inline int mdbx_cursor_push(MDBX_cursor *mc, MDBX_page *mp) {
|
|||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
__hot static __noinline MDBX_page *page_lookup_spilled(MDBX_txn *const txn,
|
__hot static __always_inline int page_get_checker_lite(const uint16_t ILL,
|
||||||
const pgno_t pgno) {
|
const MDBX_page *page,
|
||||||
|
MDBX_txn *const txn,
|
||||||
|
const txnid_t front) {
|
||||||
|
if (unlikely(page->mp_flags & ILL)) {
|
||||||
|
if (ILL == P_ILL_BITS || (page->mp_flags & P_ILL_BITS))
|
||||||
|
return bad_page(page, "invalid page's flags (%u)\n", page->mp_flags);
|
||||||
|
else if (ILL & P_OVERFLOW) {
|
||||||
|
assert((ILL & (P_BRANCH | P_LEAF | P_LEAF2)) == 0);
|
||||||
|
assert(page->mp_flags & (P_BRANCH | P_LEAF | P_LEAF2));
|
||||||
|
return bad_page(page, "unexpected %s instead of %s (%u)\n",
|
||||||
|
"large/overlow", "branch/leaf/leaf2", page->mp_flags);
|
||||||
|
} else if (ILL & (P_BRANCH | P_LEAF | P_LEAF2)) {
|
||||||
|
assert((ILL & P_BRANCH) && (ILL & P_LEAF) && (ILL & P_LEAF2));
|
||||||
|
assert(page->mp_flags & (P_BRANCH | P_LEAF | P_LEAF2));
|
||||||
|
return bad_page(page, "unexpected %s instead of %s (%u)\n",
|
||||||
|
"branch/leaf/leaf2", "large/overlow", page->mp_flags);
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(page->mp_txnid > front) &&
|
||||||
|
unlikely(page->mp_txnid > txn->mt_front || front < txn->mt_txnid))
|
||||||
|
return bad_page(
|
||||||
|
page,
|
||||||
|
"invalid page' txnid (%" PRIaTXN ") for %s' txnid (%" PRIaTXN ")\n",
|
||||||
|
page->mp_txnid,
|
||||||
|
(front == txn->mt_front && front != txn->mt_txnid) ? "front-txn"
|
||||||
|
: "parent-page",
|
||||||
|
front);
|
||||||
|
|
||||||
|
if (((ILL & P_OVERFLOW) || !IS_OVERFLOW(page)) &&
|
||||||
|
(ILL & (P_BRANCH | P_LEAF | P_LEAF2)) == 0) {
|
||||||
|
if (unlikely(page->mp_upper < page->mp_lower ||
|
||||||
|
((page->mp_lower | page->mp_upper) & 1) ||
|
||||||
|
PAGEHDRSZ + page->mp_upper > txn->mt_env->me_psize))
|
||||||
|
return bad_page(page, "invalid page' lower(%u)/upper(%u) with limit %u\n",
|
||||||
|
page->mp_lower, page->mp_upper, page_space(txn->mt_env));
|
||||||
|
|
||||||
|
} else if ((ILL & P_OVERFLOW) == 0) {
|
||||||
|
const pgno_t npages = page->mp_pages;
|
||||||
|
if (unlikely(npages < 1) || unlikely(npages >= MAX_PAGENO / 2))
|
||||||
|
return bad_page(page, "invalid n-pages (%u) for large-page\n", npages);
|
||||||
|
if (unlikely(page->mp_pgno + npages > txn->mt_next_pgno))
|
||||||
|
return bad_page(
|
||||||
|
page,
|
||||||
|
"end of large-page beyond (%u) allocated space (%u next-pgno)\n",
|
||||||
|
page->mp_pgno + npages, txn->mt_next_pgno);
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
__cold static __noinline pgr_t page_get_checker_full(const uint16_t ILL,
|
||||||
|
MDBX_page *page,
|
||||||
|
MDBX_cursor *const mc,
|
||||||
|
const txnid_t front) {
|
||||||
|
pgr_t r = {page, page_get_checker_lite(ILL, page, mc->mc_txn, front)};
|
||||||
|
if (likely(r.err == MDBX_SUCCESS))
|
||||||
|
r.err = mdbx_page_check(mc, page);
|
||||||
|
if (unlikely(r.err != MDBX_SUCCESS))
|
||||||
|
mc->mc_txn->mt_flags |= MDBX_TXN_ERROR;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
__hot static __always_inline pgr_t page_get_inline(const uint16_t ILL,
|
||||||
|
MDBX_cursor *const mc,
|
||||||
|
const pgno_t pgno,
|
||||||
|
const txnid_t front) {
|
||||||
|
MDBX_txn *const txn = mc->mc_txn;
|
||||||
|
mdbx_tassert(txn, front <= txn->mt_front);
|
||||||
|
|
||||||
|
pgr_t r;
|
||||||
|
if (unlikely(pgno >= txn->mt_next_pgno)) {
|
||||||
|
mdbx_error("page #%" PRIaPGNO " beyond next-pgno", pgno);
|
||||||
|
r.page = nullptr;
|
||||||
|
r.err = MDBX_PAGE_NOTFOUND;
|
||||||
|
bailout:
|
||||||
|
txn->mt_flags |= MDBX_TXN_ERROR;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
mdbx_assert(txn->mt_env,
|
||||||
|
((txn->mt_flags ^ txn->mt_env->me_flags) & MDBX_WRITEMAP) == 0);
|
||||||
|
r.page = pgno2page(txn->mt_env, pgno);
|
||||||
|
if ((txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP)) == 0) {
|
||||||
const MDBX_txn *spiller = txn;
|
const MDBX_txn *spiller = txn;
|
||||||
do {
|
do {
|
||||||
/* Spilled pages were dirtied in this txn and flushed
|
/* Spilled pages were dirtied in this txn and flushed
|
||||||
@ -13611,114 +13697,31 @@ __hot static __noinline MDBX_page *page_lookup_spilled(MDBX_txn *const txn,
|
|||||||
mdbx_tassert(txn, (int)i > 0);
|
mdbx_tassert(txn, (int)i > 0);
|
||||||
if (spiller->tw.dirtylist->items[i].pgno == pgno) {
|
if (spiller->tw.dirtylist->items[i].pgno == pgno) {
|
||||||
spiller->tw.dirtylist->items[i].lru = txn->tw.dirtylru++;
|
spiller->tw.dirtylist->items[i].lru = txn->tw.dirtylru++;
|
||||||
return spiller->tw.dirtylist->items[i].ptr;
|
r.page = spiller->tw.dirtylist->items[i].ptr;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
spiller = spiller->mt_parent;
|
spiller = spiller->mt_parent;
|
||||||
} while (spiller);
|
} while (spiller);
|
||||||
|
|
||||||
return pgno2page(txn->mt_env, pgno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__hot static __always_inline pgr_t page_get_inline(const uint16_t ILL,
|
|
||||||
MDBX_cursor *const mc,
|
|
||||||
const pgno_t pgno,
|
|
||||||
const txnid_t front) {
|
|
||||||
MDBX_txn *const txn = mc->mc_txn;
|
|
||||||
mdbx_tassert(txn, front <= txn->mt_front);
|
|
||||||
|
|
||||||
pgr_t r;
|
|
||||||
if (unlikely(pgno >= txn->mt_next_pgno)) {
|
|
||||||
mdbx_error("page #%" PRIaPGNO " beyond next-pgno", pgno);
|
|
||||||
notfound:
|
|
||||||
r.page = nullptr;
|
|
||||||
r.err = MDBX_PAGE_NOTFOUND;
|
|
||||||
bailout:
|
|
||||||
mc->mc_txn->mt_flags |= MDBX_TXN_ERROR;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.page = pgno2page(txn->mt_env, pgno);
|
|
||||||
if (unlikely((txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP)) == 0))
|
|
||||||
r.page = page_lookup_spilled(txn, pgno);
|
|
||||||
|
|
||||||
MDBX_env *const env = txn->mt_env;
|
|
||||||
mdbx_assert(env, ((txn->mt_flags ^ env->me_flags) & MDBX_WRITEMAP) == 0);
|
|
||||||
|
|
||||||
if (unlikely(r.page->mp_pgno != pgno)) {
|
if (unlikely(r.page->mp_pgno != pgno)) {
|
||||||
r.err = bad_page(
|
r.err = bad_page(
|
||||||
r.page, "pgno mismatch (%" PRIaPGNO ") != expected (%" PRIaPGNO ")\n",
|
r.page, "pgno mismatch (%" PRIaPGNO ") != expected (%" PRIaPGNO ")\n",
|
||||||
r.page->mp_pgno, pgno);
|
r.page->mp_pgno, pgno);
|
||||||
goto notfound;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !MDBX_DISABLE_VALIDATION
|
|
||||||
if (unlikely(r.page->mp_flags & ILL)) {
|
|
||||||
if (ILL == P_ILL_BITS || (r.page->mp_flags & P_ILL_BITS))
|
|
||||||
r.err = bad_page(r.page, "invalid page's flags (%u)\n", r.page->mp_flags);
|
|
||||||
else if (ILL & P_OVERFLOW) {
|
|
||||||
assert((ILL & (P_BRANCH | P_LEAF | P_LEAF2)) == 0);
|
|
||||||
assert(r.page->mp_flags & (P_BRANCH | P_LEAF | P_LEAF2));
|
|
||||||
r.err = bad_page(r.page, "unexpected %s instead of %s (%u)\n",
|
|
||||||
"large/overlow", "branch/leaf/leaf2", r.page->mp_flags);
|
|
||||||
} else if (ILL & (P_BRANCH | P_LEAF | P_LEAF2)) {
|
|
||||||
assert((ILL & P_BRANCH) && (ILL & P_LEAF) && (ILL & P_LEAF2));
|
|
||||||
assert(r.page->mp_flags & (P_BRANCH | P_LEAF | P_LEAF2));
|
|
||||||
r.err = bad_page(r.page, "unexpected %s instead of %s (%u)\n",
|
|
||||||
"branch/leaf/leaf2", "large/overlow", r.page->mp_flags);
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
goto bailout;
|
goto bailout;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(r.page->mp_txnid > front) &&
|
if (unlikely(mc->mc_checking & CC_PAGECHECK))
|
||||||
unlikely(r.page->mp_txnid > txn->mt_front || front < txn->mt_txnid)) {
|
return page_get_checker_full(ILL, r.page, mc, front);
|
||||||
r.err = bad_page(
|
|
||||||
r.page,
|
|
||||||
"invalid page' txnid (%" PRIaTXN ") for %s' txnid (%" PRIaTXN ")\n",
|
|
||||||
r.page->mp_txnid,
|
|
||||||
(front == txn->mt_front && front != txn->mt_txnid) ? "front-txn"
|
|
||||||
: "parent-page",
|
|
||||||
front);
|
|
||||||
goto bailout;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((ILL & P_OVERFLOW) || !IS_OVERFLOW(r.page)) &&
|
|
||||||
(ILL & (P_BRANCH | P_LEAF | P_LEAF2)) == 0) {
|
|
||||||
if (unlikely(r.page->mp_upper < r.page->mp_lower ||
|
|
||||||
((r.page->mp_lower | r.page->mp_upper) & 1) ||
|
|
||||||
PAGEHDRSZ + r.page->mp_upper > env->me_psize)) {
|
|
||||||
r.err =
|
|
||||||
bad_page(r.page, "invalid page' lower(%u)/upper(%u) with limit %u\n",
|
|
||||||
r.page->mp_lower, r.page->mp_upper, page_space(env));
|
|
||||||
goto bailout;
|
|
||||||
}
|
|
||||||
} else if ((ILL & P_OVERFLOW) == 0) {
|
|
||||||
const pgno_t npages = r.page->mp_pages;
|
|
||||||
if (unlikely(npages < 1 || npages >= MAX_PAGENO / 2)) {
|
|
||||||
r.err = bad_page(r.page, "invalid n-pages (%u) for large-page\n", npages);
|
|
||||||
goto bailout;
|
|
||||||
}
|
|
||||||
if (unlikely(r.page->mp_pgno + npages > txn->mt_next_pgno)) {
|
|
||||||
r.err = bad_page(
|
|
||||||
r.page,
|
|
||||||
"end of large-page beyond (%u) allocated space (%u next-pgno)\n",
|
|
||||||
r.page->mp_pgno + npages, txn->mt_next_pgno);
|
|
||||||
goto bailout;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
(void)ILL;
|
|
||||||
#endif /* MDBX_DISABLE_VALIDATION */
|
|
||||||
|
|
||||||
if (unlikely(mc->mc_checking & CC_PAGECHECK) &&
|
|
||||||
unlikely(MDBX_SUCCESS != (r.err = mdbx_page_check(mc, r.page))))
|
|
||||||
goto bailout;
|
|
||||||
|
|
||||||
|
#if MDBX_DISABLE_VALIDATION
|
||||||
r.err = MDBX_SUCCESS;
|
r.err = MDBX_SUCCESS;
|
||||||
|
#else
|
||||||
|
r.err = page_get_checker_lite(ILL, r.page, txn, front);
|
||||||
|
if (unlikely(r.err != MDBX_SUCCESS))
|
||||||
|
goto bailout;
|
||||||
|
#endif /* MDBX_DISABLE_VALIDATION */
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user