#include "db_config.h"
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/db_swap.h"
#include "dbinc/btree.h"
#include "dbinc/crypto.h"
#include "dbinc/hmac.h"
#include "dbinc/fop.h"
#include "dbinc/hash.h"
#include "dbinc/heap.h"
#include "dbinc/lock.h"
#include "dbinc/mp.h"
#include "dbinc/qam.h"
#include "dbinc/txn.h"
static int __db_handle_lock __P((DB *));
int
__db_open(dbp, ip, txn, fname, dname, type, flags, mode, meta_pgno)
DB *dbp;
DB_THREAD_INFO *ip;
DB_TXN *txn;
const char *fname, *dname;
DBTYPE type;
u_int32_t flags;
int mode;
db_pgno_t meta_pgno;
{
DB *tdbp;
ENV *env;
int ret;
u_int32_t id;
env = dbp->env;
id = TXN_INVALID;
if (LF_ISSET(DB_TRUNCATE)) {
if ((ret = __db_create_internal(&tdbp, dbp->env, 0)) != 0)
goto err;
ret = __db_open(tdbp, ip, txn, fname, dname, DB_UNKNOWN,
DB_NOERROR | (flags & ~(DB_TRUNCATE|DB_CREATE)),
mode, meta_pgno);
if (ret == 0)
ret = __memp_ftruncate(tdbp->mpf, txn, ip, 0, 0);
(void)__db_close(tdbp, txn, DB_NOSYNC);
if (ret != 0 && ret != ENOENT && ret != EINVAL)
goto err;
ret = 0;
}
DB_TEST_RECOVERY(dbp, DB_TEST_PREOPEN, ret, fname);
if (F_ISSET(env, ENV_THREAD))
LF_SET(DB_THREAD);
if (LF_ISSET(DB_RDONLY))
F_SET(dbp, DB_AM_RDONLY);
if (LF_ISSET(DB_READ_UNCOMMITTED))
F_SET(dbp, DB_AM_READ_UNCOMMITTED);
if (IS_REAL_TXN(txn))
F_SET(dbp, DB_AM_TXN);
dbp->type = type;
if ((fname != NULL &&
(ret = __os_strdup(env, fname, &dbp->fname)) != 0))
goto err;
if ((dname != NULL &&
(ret = __os_strdup(env, dname, &dbp->dname)) != 0))
goto err;
if (fname == NULL) {
if (dbp->p_internal != NULL) {
__db_errx(env, DB_STR("0634",
"Partitioned databases may not be in memory."));
return (ENOENT);
}
if (dname == NULL) {
if (!LF_ISSET(DB_CREATE)) {
__db_errx(env, DB_STR("0635",
"DB_CREATE must be specified to create databases."));
return (ENOENT);
}
F_SET(dbp, DB_AM_INMEM);
F_SET(dbp, DB_AM_CREATED);
if (dbp->type == DB_UNKNOWN) {
__db_errx(env, DB_STR("0636",
"DBTYPE of unknown without existing file"));
return (EINVAL);
}
if (dbp->pgsize == 0)
dbp->pgsize = DB_DEF_IOSIZE;
if (LOCKING_ON(env) && (ret = __lock_id(env,
(u_int32_t *)dbp->fileid, NULL)) != 0)
return (ret);
} else
MAKE_INMEM(dbp);
} else if (dname == NULL && meta_pgno == PGNO_BASE_MD) {
if ((ret = __fop_file_setup(dbp, ip,
txn, fname, mode, flags, &id)) != 0)
return (ret);
if (!F_ISSET(dbp, DB_AM_RDONLY))
LF_CLR(DB_RDONLY);
} else {
if (dbp->p_internal != NULL) {
__db_errx(env, DB_STR("0637",
"Partitioned databases may not be included with multiple databases."));
return (ENOENT);
}
if ((ret = __fop_subdb_setup(dbp, ip,
txn, fname, dname, mode, flags)) != 0)
return (ret);
meta_pgno = dbp->meta_pgno;
}
if ((ret = __env_setup(dbp, txn, fname, dname, id, flags)) != 0)
return (ret);
if (F_ISSET(dbp, DB_AM_INMEM)) {
if (dname == NULL)
ret = __db_new_file(dbp, ip, txn, NULL, NULL);
else {
id = TXN_INVALID;
ret = __fop_file_setup(dbp,
ip, txn, dname, mode, flags, &id);
}
if (ret != 0)
goto err;
}
if (F2_ISSET(dbp, DB2_AM_INTEXCL) &&
(ret = __db_handle_lock(dbp)) != 0)
goto err;
switch (dbp->type) {
case DB_BTREE:
ret = __bam_open(dbp, ip, txn, fname, meta_pgno, flags);
break;
case DB_HASH:
ret = __ham_open(dbp, ip, txn, fname, meta_pgno, flags);
break;
case DB_HEAP:
ret = __heap_open(dbp,
ip, txn, fname, meta_pgno, flags);
break;
case DB_RECNO:
ret = __ram_open(dbp, ip, txn, fname, meta_pgno, flags);
break;
case DB_QUEUE:
ret = __qam_open(
dbp, ip, txn, fname, meta_pgno, mode, flags);
break;
case DB_UNKNOWN:
return (
__db_unknown_type(env, "__db_dbopen", dbp->type));
}
if (ret != 0)
goto err;
#ifdef HAVE_PARTITION
if (dbp->p_internal != NULL && (ret =
__partition_open(dbp, ip, txn, fname, type, flags, mode, 1)) != 0)
goto err;
#endif
DB_TEST_RECOVERY(dbp, DB_TEST_POSTOPEN, ret, fname);
if (!F_ISSET(dbp, DB_AM_RECOVER) && (fname != NULL || dname != NULL) &&
LOCK_ISSET(dbp->handle_lock)) {
if (IS_REAL_TXN(txn))
ret = __txn_lockevent(env,
txn, dbp, &dbp->handle_lock, dbp->locker);
else if (LOCKING_ON(env) && !F2_ISSET(dbp, DB2_AM_EXCL))
ret = __lock_downgrade(env,
&dbp->handle_lock, DB_LOCK_READ, 0);
}
DB_TEST_RECOVERY_LABEL
err:
PERFMON4(env,
db, open, (char *) fname, (char *) dname, flags, &dbp->fileid[0]);
return (ret);
}
int
__db_get_open_flags(dbp, flagsp)
DB *dbp;
u_int32_t *flagsp;
{
DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->get_open_flags");
*flagsp = dbp->open_flags;
return (0);
}
int
__db_new_file(dbp, ip, txn, fhp, name)
DB *dbp;
DB_THREAD_INFO *ip;
DB_TXN *txn;
DB_FH *fhp;
const char *name;
{
int ret;
if (F_ISSET(dbp, DB_AM_INMEM))
LOCK_CHECK_OFF(ip);
switch (dbp->type) {
case DB_BTREE:
case DB_RECNO:
ret = __bam_new_file(dbp, ip, txn, fhp, name);
break;
case DB_HASH:
ret = __ham_new_file(dbp, ip, txn, fhp, name);
break;
case DB_HEAP:
ret = __heap_new_file(dbp, ip, txn, fhp, name);
break;
case DB_QUEUE:
ret = __qam_new_file(dbp, ip, txn, fhp, name);
break;
case DB_UNKNOWN:
default:
__db_errx(dbp->env, DB_STR_A("0638",
"%s: Invalid type %d specified", "%s %d"),
name, dbp->type);
ret = EINVAL;
break;
}
DB_TEST_RECOVERY(dbp, DB_TEST_POSTLOGMETA, ret, name);
if (ret == 0 && fhp != NULL)
ret = __os_fsync(dbp->env, fhp);
DB_TEST_RECOVERY(dbp, DB_TEST_POSTSYNC, ret, name);
if (F_ISSET(dbp, DB_AM_INMEM))
LOCK_CHECK_ON(ip);
DB_TEST_RECOVERY_LABEL
return (ret);
}
int
__db_init_subdb(mdbp, dbp, name, ip, txn)
DB *mdbp, *dbp;
const char *name;
DB_THREAD_INFO *ip;
DB_TXN *txn;
{
DBMETA *meta;
DB_MPOOLFILE *mpf;
int ret, t_ret;
ret = 0;
if (!F_ISSET(dbp, DB_AM_CREATED)) {
mpf = mdbp->mpf;
if ((ret = __memp_fget(mpf, &dbp->meta_pgno,
ip, txn, 0, &meta)) != 0)
goto err;
ret = __db_meta_setup(mdbp->env, dbp, name, meta, 0, 0);
if ((t_ret = __memp_fput(mpf,
ip, meta, dbp->priority)) != 0 && ret == 0)
ret = t_ret;
if (ret == ENOENT)
ret = 0;
goto err;
}
switch (dbp->type) {
case DB_BTREE:
case DB_RECNO:
ret = __bam_new_subdb(mdbp, dbp, ip, txn);
break;
case DB_HASH:
ret = __ham_new_subdb(mdbp, dbp, ip, txn);
break;
case DB_QUEUE:
ret = EINVAL;
break;
case DB_UNKNOWN:
default:
__db_errx(dbp->env, DB_STR_A("0639",
"Invalid subdatabase type %d specified", "%d"),
dbp->type);
return (EINVAL);
}
err: return (ret);
}
int
__db_chk_meta(env, dbp, meta, flags)
ENV *env;
DB *dbp;
DBMETA *meta;
u_int32_t flags;
{
DB_LSN swap_lsn;
int is_hmac, ret, swapped;
u_int32_t magic, orig_chk;
u_int8_t *chksum;
ret = 0;
swapped = 0;
if (FLD_ISSET(meta->metaflags, DBMETA_CHKSUM)) {
if (dbp != NULL)
F_SET(dbp, DB_AM_CHKSUM);
is_hmac = meta->encrypt_alg == 0 ? 0 : 1;
chksum = ((BTMETA *)meta)->chksum;
orig_chk = *(u_int32_t *)chksum;
if (LF_ISSET(DB_CHK_META)) {
swapped = 0;
chk_retry: if ((ret =
__db_check_chksum(env, NULL, env->crypto_handle,
chksum, meta, DBMETASIZE, is_hmac)) != 0) {
if (is_hmac || swapped)
return (DB_CHKSUM_FAIL);
M_32_SWAP(orig_chk);
swapped = 1;
*(u_int32_t *)chksum = orig_chk;
goto chk_retry;
}
}
} else if (dbp != NULL)
F_CLR(dbp, DB_AM_CHKSUM);
#ifdef HAVE_CRYPTO
if (__crypto_decrypt_meta(env,
dbp, (u_int8_t *)meta, LF_ISSET(DB_CHK_META)) != 0)
ret = DB_CHKSUM_FAIL;
else
#endif
if (LOGGING_ON(env) && !LF_ISSET(DB_CHK_NOLSN)) {
swap_lsn = meta->lsn;
magic = meta->magic;
lsn_retry:
if (swapped) {
M_32_SWAP(swap_lsn.file);
M_32_SWAP(swap_lsn.offset);
M_32_SWAP(magic);
}
switch (magic) {
case DB_BTREEMAGIC:
case DB_HASHMAGIC:
case DB_HEAPMAGIC:
case DB_QAMMAGIC:
case DB_RENAMEMAGIC:
break;
default:
if (swapped)
return (EINVAL);
swapped = 1;
goto lsn_retry;
}
if (!IS_REP_CLIENT(env) &&
!IS_NOT_LOGGED_LSN(swap_lsn) && !IS_ZERO_LSN(swap_lsn))
ret = __log_check_page_lsn(env, dbp, &swap_lsn);
}
return (ret);
}
int
__db_meta_setup(env, dbp, name, meta, oflags, flags)
ENV *env;
DB *dbp;
const char *name;
DBMETA *meta;
u_int32_t oflags;
u_int32_t flags;
{
u_int32_t magic;
int ret;
ret = 0;
F_CLR(dbp, DB_AM_SWAP | DB_AM_IN_RENAME);
magic = meta->magic;
swap_retry:
switch (magic) {
case DB_BTREEMAGIC:
case DB_HASHMAGIC:
case DB_HEAPMAGIC:
case DB_QAMMAGIC:
case DB_RENAMEMAGIC:
break;
case 0:
if (F_ISSET(dbp, DB_AM_SUBDB) && ((IS_RECOVERING(env) &&
F_ISSET(env->lg_handle, DBLOG_FORCE_OPEN)) ||
meta->pgno != PGNO_INVALID))
return (ENOENT);
goto bad_format;
default:
if (F_ISSET(dbp, DB_AM_SWAP))
goto bad_format;
M_32_SWAP(magic);
F_SET(dbp, DB_AM_SWAP);
goto swap_retry;
}
if (!LF_ISSET(DB_SKIP_CHK) &&
(ret = __db_chk_meta(env, dbp, meta, flags)) != 0) {
if (ret == DB_CHKSUM_FAIL)
__db_errx(env, DB_STR_A("0640",
"%s: metadata page checksum error", "%s"), name);
goto bad_format;
}
switch (magic) {
case DB_BTREEMAGIC:
if (dbp->type != DB_UNKNOWN &&
dbp->type != DB_RECNO && dbp->type != DB_BTREE)
goto bad_format;
flags = meta->flags;
if (F_ISSET(dbp, DB_AM_SWAP))
M_32_SWAP(flags);
if (LF_ISSET(BTM_RECNO))
dbp->type = DB_RECNO;
else
dbp->type = DB_BTREE;
if ((oflags & DB_TRUNCATE) == 0 && (ret =
__bam_metachk(dbp, name, (BTMETA *)meta)) != 0)
return (ret);
break;
case DB_HASHMAGIC:
if (dbp->type != DB_UNKNOWN && dbp->type != DB_HASH)
goto bad_format;
dbp->type = DB_HASH;
if ((oflags & DB_TRUNCATE) == 0 && (ret =
__ham_metachk(dbp, name, (HMETA *)meta)) != 0)
return (ret);
break;
case DB_HEAPMAGIC:
if (dbp->type != DB_UNKNOWN && dbp->type != DB_HEAP)
goto bad_format;
dbp->type = DB_HEAP;
if ((oflags & DB_TRUNCATE) == 0 && (ret =
__heap_metachk(dbp, name, (HEAPMETA *)meta)) != 0)
return (ret);
break;
case DB_QAMMAGIC:
if (dbp->type != DB_UNKNOWN && dbp->type != DB_QUEUE)
goto bad_format;
dbp->type = DB_QUEUE;
if ((oflags & DB_TRUNCATE) == 0 && (ret =
__qam_metachk(dbp, name, (QMETA *)meta)) != 0)
return (ret);
break;
case DB_RENAMEMAGIC:
F_SET(dbp, DB_AM_IN_RENAME);
memcpy(dbp->fileid, ((DBMETA *)meta)->uid, DB_FILE_ID_LEN);
break;
default:
goto bad_format;
}
if (FLD_ISSET(meta->metaflags,
DBMETA_PART_RANGE | DBMETA_PART_CALLBACK))
if ((ret =
__partition_init(dbp, meta->metaflags)) != 0)
return (ret);
return (0);
bad_format:
if (F_ISSET(dbp, DB_AM_RECOVER))
ret = ENOENT;
else
__db_errx(env, DB_STR_A("0641",
"__db_meta_setup: %s: unexpected file type or format",
"%s"), name);
return (ret == 0 ? EINVAL : ret);
}
int
__db_reopen(arg_dbc)
DBC *arg_dbc;
{
BTREE *bt;
DBC *dbc;
DB_TXN *txn;
HASH *ht;
DB *dbp, *mdbp;
DB_LOCK new_lock, old_lock;
PAGE *new_page, *old_page;
db_pgno_t newpgno, oldpgno;
int ret, t_ret;
dbc = arg_dbc;
dbp = dbc->dbp;
old_page = new_page = NULL;
mdbp = NULL;
COMPQUIET(bt, NULL);
COMPQUIET(ht, NULL);
COMPQUIET(txn, NULL);
LOCK_INIT(new_lock);
LOCK_INIT(old_lock);
if (TXN_ON(dbp->env) && (txn = dbc->txn) == NULL) {
if ((ret = __txn_begin(dbp->env,
dbc->thread_info, NULL, &txn, 0)) != 0)
return (ret);
if ((ret = __db_cursor(dbp,
dbc->thread_info, txn, &dbc, 0)) != 0) {
(void)__txn_abort(txn);
return (ret);
}
}
if (dbp->type == DB_HASH) {
ht = (HASH*)dbp->h_internal;
oldpgno = ht->meta_pgno;
} else {
bt = (BTREE *)dbp->bt_internal;
oldpgno = bt->bt_root;
}
if (STD_LOCKING(dbc) && (ret = __db_lget(dbc,
0, oldpgno, DB_LOCK_READ, 0, &old_lock)) != 0)
goto err;
if ((ret = __memp_fget(dbp->mpf, &oldpgno,
dbc->thread_info, dbc->txn, 0, &old_page)) != 0 &&
ret != DB_PAGE_NOTFOUND)
goto err;
if (ret == DB_PAGE_NOTFOUND || TYPE(old_page) == P_INVALID) {
if ((ret = __LPUT(dbc, old_lock)) != 0)
goto err;
if (old_page != NULL && (ret = __memp_fput(dbp->mpf,
dbc->thread_info, old_page, dbc->priority)) != 0)
goto err;
old_page = NULL;
}
if ((ret = __db_master_open(dbp,
dbc->thread_info, dbc->txn, dbp->fname, 0, 0, &mdbp)) != 0)
goto err;
if ((ret = __db_master_update(mdbp, dbp, dbc->thread_info,
dbc->txn, dbp->dname, dbp->type, MU_OPEN, NULL, 0)) != 0)
goto err;
if (dbp->type == DB_HASH)
newpgno = ht->meta_pgno = dbp->meta_pgno;
else {
bt->bt_meta = dbp->meta_pgno;
if ((ret = __bam_read_root(dbp,
dbc->thread_info, dbc->txn, bt->bt_meta, 0)) != 0)
goto err;
newpgno = bt->bt_root;
}
if (oldpgno == newpgno)
goto done;
if (STD_LOCKING(dbc) && (ret = __db_lget(dbc,
0, newpgno, DB_LOCK_READ, 0, &new_lock)) != 0)
goto err;
if ((ret = __memp_fget(dbp->mpf, &newpgno,
dbc->thread_info, dbc->txn, 0, &new_page)) != 0)
goto err;
done: if (dbp->type == DB_HASH)
ht->revision = dbp->mpf->mfp->revision;
else
bt->revision = dbp->mpf->mfp->revision;
err: if (old_page != NULL && (t_ret = __memp_fput(dbp->mpf,
dbc->thread_info, old_page, dbc->priority)) != 0 && ret == 0)
ret = t_ret;
if (new_page != NULL && (t_ret = __memp_fput(dbp->mpf,
dbc->thread_info, new_page, dbc->priority)) != 0 && ret == 0)
ret = t_ret;
if (mdbp != NULL &&
(t_ret = __db_close(mdbp, dbc->txn, DB_NOSYNC)) != 0 && ret == 0)
ret = t_ret;
if (dbc != arg_dbc) {
if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0)
ret = t_ret;
if ((t_ret = __txn_commit(txn, 0)) != 0 && ret == 0)
ret = t_ret;
}
return (ret);
}
static int
__db_handle_lock(dbp)
DB *dbp;
{
ENV *env;
int ret;
u_int32_t old_flags;
env = dbp->env;
ret = 0;
old_flags = dbp->flags;
F_CLR(dbp, DB_AM_RECOVER);
F_SET(dbp, DB_AM_NOT_DURABLE);
dbp->mpf->mfp->excl_lockout = 1;
if ((ret = __lock_id(env, NULL, &dbp->locker)) != 0)
goto err;
LOCK_INIT(dbp->handle_lock);
if ((ret = __fop_lock_handle(env, dbp, dbp->locker, DB_LOCK_WRITE,
NULL, 0))!= 0)
goto err;
err:
dbp->mpf->mfp->excl_lockout = 0;
dbp->flags = old_flags;
return (ret);
}