#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/lock.h"
#include "dbinc/log.h"
#include "dbinc/mp.h"
#include "dbinc/qam.h"
#include "dbinc/txn.h"
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;
{
ENV *env;
int ret;
u_int32_t id;
env = dbp->env;
id = TXN_INVALID;
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) {
if (dname == NULL) {
if (!LF_ISSET(DB_CREATE)) {
__db_errx(env,
"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,
"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);
} else {
if ((ret = __fop_subdb_setup(dbp, ip,
txn, fname, dname, mode, flags)) != 0)
return (ret);
meta_pgno = dbp->meta_pgno;
}
if (dname == NULL && F_ISSET(dbp, DB_AM_CREATED))
LF_SET(DB_TRUNCATE);
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;
if ((ret = __fop_file_setup(dbp, ip,
txn, dname, mode, flags, &id)) == 0 &&
DBENV_LOGGING(env) && !F_ISSET(dbp, DB_AM_RECOVER)
#if !defined(DEBUG_ROP) && !defined(DEBUG_WOP) && !defined(DIAGNOSTIC)
&& txn != NULL
#endif
#if !defined(DEBUG_ROP)
&& !F_ISSET(dbp, DB_AM_RDONLY)
#endif
)
ret = __dbreg_log_id(dbp,
txn, dbp->log_filename->id, 1);
}
if (ret != 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_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;
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))
ret = __lock_downgrade(env,
&dbp->handle_lock, DB_LOCK_READ, 0);
}
DB_TEST_RECOVERY_LABEL
err:
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;
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_QUEUE:
ret = __qam_new_file(dbp, ip, txn, fhp, name);
break;
case DB_UNKNOWN:
default:
__db_errx(dbp->env,
"%s: Invalid type %d specified", 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);
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,
"Invalid subdatabase type %d specified", 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 (ret);
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
ret = __crypto_decrypt_meta(env,
dbp, (u_int8_t *)meta, LF_ISSET(DB_CHK_META));
#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_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_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 ((ret = __db_chk_meta(env, dbp, meta, flags)) != 0) {
if (ret == -1)
__db_errx(env,
"%s: metadata page checksum error", 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_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;
}
return (0);
bad_format:
if (F_ISSET(dbp, DB_AM_RECOVER))
ret = ENOENT;
else
__db_errx(env, "%s: unexpected file type or format", name);
return (ret == 0 ? EINVAL : ret);
}