mdbx: rework mdbx_read_header().

This commit is contained in:
Leo Yuriev 2017-04-27 15:17:30 +03:00
parent fd078ee163
commit 40dee6f05f
2 changed files with 38 additions and 26 deletions

View File

@ -278,8 +278,10 @@ typedef struct MDB_meta {
#define MDB_DATASIGN_NONE 0u #define MDB_DATASIGN_NONE 0u
#define MDB_DATASIGN_WEAK 1u #define MDB_DATASIGN_WEAK 1u
volatile uint64_t mm_datasync_sign; volatile uint64_t mm_datasync_sign;
#define META_IS_WEAK(meta) ((meta)->mm_datasync_sign == MDB_DATASIGN_WEAK) #define SIGN_IS_WEAK(sign) ((sign) == MDB_DATASIGN_WEAK)
#define META_IS_STEADY(meta) ((meta)->mm_datasync_sign > MDB_DATASIGN_WEAK) #define SIGN_IS_STEADY(sign) ((sign) > MDB_DATASIGN_WEAK)
#define META_IS_WEAK(meta) SIGN_IS_WEAK((meta)->mm_datasync_sign)
#define META_IS_STEADY(meta) SIGN_IS_STEADY((meta)->mm_datasync_sign)
volatile mdbx_canary mm_canary; volatile mdbx_canary mm_canary;
} MDB_meta; } MDB_meta;

View File

@ -3264,38 +3264,32 @@ fail:
return rc; return rc;
} }
/** Read the environment parameters of a DB environment before /* Read the environment parameters of a DB environment
* mapping it into memory. * before mapping it into memory. */
* @param[in] env the environment handle
* @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure. */
static int __cold mdbx_read_header(MDB_env *env, MDB_meta *meta) { static int __cold mdbx_read_header(MDB_env *env, MDB_meta *meta) {
MDB_metabuf pbuf;
MDB_page *p;
MDB_meta *m;
int i, rc, off;
assert(offsetof(MDB_metabuf, mb_metabuf.mm_meta) == PAGEHDRSZ); assert(offsetof(MDB_metabuf, mb_metabuf.mm_meta) == PAGEHDRSZ);
/* We don't know the page size yet, so use a minimum value.
* Read both meta pages so we can use the latest one. */
meta->mm_datasync_sign = MDB_DATASIGN_WEAK; meta->mm_datasync_sign = MDB_DATASIGN_WEAK;
meta->mm_txnid = 0; meta->mm_txnid = 0;
for (i = off = 0; i < NUM_METAS; i++, off += meta->mm_psize) { off_t offset = 0;
rc = mdbx_pread(env->me_fd, &pbuf, sizeof(pbuf), off);
/* Read both meta pages so we can use the latest one. */
for (int loops_left = 2; --loops_left >= 0;) {
MDB_metabuf buf;
/* We don't know the page size on first time, so use a minimum value. */
int rc = mdbx_pread(env->me_fd, &buf, sizeof(buf), offset);
if (rc != MDB_SUCCESS) { if (rc != MDB_SUCCESS) {
mdbx_debug("read: %s", mdbx_strerror(rc)); mdbx_debug("read: %s", mdbx_strerror(rc));
return rc; return rc;
} }
p = (MDB_page *)&pbuf; MDB_page *p = (MDB_page *)&buf;
if (!F_ISSET(p->mp_flags, P_META)) { if (!F_ISSET(p->mp_flags, P_META)) {
mdbx_debug("page %zu not a meta page", p->mp_pgno); mdbx_debug("page %zu not a meta page", p->mp_pgno);
return MDB_INVALID; return MDB_INVALID;
} }
m = PAGEDATA(p); MDB_meta *m = PAGEDATA(p);
if (m->mm_magic != MDB_MAGIC) { if (m->mm_magic != MDB_MAGIC) {
mdbx_debug("meta has invalid magic"); mdbx_debug("meta has invalid magic");
return MDB_INVALID; return MDB_INVALID;
@ -3307,22 +3301,38 @@ static int __cold mdbx_read_header(MDB_env *env, MDB_meta *meta) {
return MDB_VERSION_MISMATCH; return MDB_VERSION_MISMATCH;
} }
if (m->mm_datasync_sign > MDB_DATASIGN_WEAK && /* LY: check signature as a checksum */
m->mm_datasync_sign != mdbx_meta_sign(m)) if (META_IS_STEADY(m) && m->mm_datasync_sign != mdbx_meta_sign(m)) {
mdbx_debug("steady-meta has invalid checksum");
continue; continue;
if (mdbx_meta_lt(meta, m))
*meta = *m;
} }
if (meta->mm_datasync_sign == MDB_DATASIGN_WEAK) if (mdbx_meta_lt(meta, m)) {
/* LY: Both meta-pages are weak. */ *meta = *m;
if (META_IS_WEAK(meta))
loops_left += 1; /* LY: should re-read to avoid race */
}
if (offset)
offset = 0;
else {
offset = meta->mm_psize;
if (!offset)
offset = m->mm_psize;
if (!offset)
offset = env->me_os_psize;
}
}
if (META_IS_WEAK(meta)) {
mdbx_debug("both meta-pages are weak, database is corrupted");
return MDB_CORRUPTED; return MDB_CORRUPTED;
}
return MDB_SUCCESS; return MDB_SUCCESS;
} }
/** Fill in most of the zeroed #MDB_meta for an empty database environment */ /* Fill in most of the zeroed MDB_meta for an empty database environment */
static void __cold mdbx_env_init_meta0(MDB_env *env, MDB_meta *meta) { static void __cold mdbx_env_init_meta0(MDB_env *env, MDB_meta *meta) {
meta->mm_magic = MDB_MAGIC; meta->mm_magic = MDB_MAGIC;
meta->mm_version = MDB_DATA_VERSION; meta->mm_version = MDB_DATA_VERSION;