#include "db_config.h"
#include "db_int.h"
#include "dbinc/crypto.h"
#include "dbinc/db_page.h"
#include "dbinc/hash.h"
#include "dbinc/log.h"
#include "dbinc/lock.h"
#include "dbinc/mp.h"
#include "dbinc/btree.h"
#include "dbinc/fop.h"
static db_pgno_t __ham_init_meta __P((DB *, HMETA *, db_pgno_t, DB_LSN *));
int
__ham_open(dbp, txn, name, base_pgno, flags)
DB *dbp;
DB_TXN *txn;
const char *name;
db_pgno_t base_pgno;
u_int32_t flags;
{
DB_ENV *dbenv;
DBC *dbc;
HASH_CURSOR *hcp;
HASH *hashp;
int ret, t_ret;
dbenv = dbp->dbenv;
dbc = NULL;
if ((ret = __db_cursor(dbp,
txn, &dbc, LF_ISSET(DB_CREATE) && CDB_LOCKING(dbenv) ?
DB_WRITECURSOR : 0)) != 0)
return (ret);
hcp = (HASH_CURSOR *)dbc->internal;
hashp = dbp->h_internal;
hashp->meta_pgno = base_pgno;
if ((ret = __ham_get_meta(dbc)) != 0)
goto err;
if (hcp->hdr->dbmeta.magic == DB_HASHMAGIC) {
if (hashp->h_hash == NULL)
hashp->h_hash = hcp->hdr->dbmeta.version < 5
? __ham_func4 : __ham_func5;
hashp->h_nelem = hcp->hdr->nelem;
if (F_ISSET(&hcp->hdr->dbmeta, DB_HASH_DUP))
F_SET(dbp, DB_AM_DUP);
if (F_ISSET(&hcp->hdr->dbmeta, DB_HASH_DUPSORT))
F_SET(dbp, DB_AM_DUPSORT);
if (F_ISSET(&hcp->hdr->dbmeta, DB_HASH_SUBDB))
F_SET(dbp, DB_AM_SUBDB);
} else if (!IS_RECOVERING(dbenv) && !F_ISSET(dbp, DB_AM_RECOVER)) {
__db_errx(dbenv,
"%s: Invalid hash meta page %lu", name, (u_long)base_pgno);
ret = EINVAL;
}
if ((t_ret = __ham_release_meta(dbc)) != 0 && ret == 0)
ret = t_ret;
err: if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
int
__ham_metachk(dbp, name, hashm)
DB *dbp;
const char *name;
HMETA *hashm;
{
DB_ENV *dbenv;
u_int32_t vers;
int ret;
dbenv = dbp->dbenv;
vers = hashm->dbmeta.version;
if (F_ISSET(dbp, DB_AM_SWAP))
M_32_SWAP(vers);
switch (vers) {
case 4:
case 5:
case 6:
__db_errx(dbenv,
"%s: hash version %lu requires a version upgrade",
name, (u_long)vers);
return (DB_OLD_VERSION);
case 7:
case 8:
case 9:
break;
default:
__db_errx(dbenv,
"%s: unsupported hash version: %lu", name, (u_long)vers);
return (EINVAL);
}
if (F_ISSET(dbp, DB_AM_SWAP) && (ret = __ham_mswap((PAGE *)hashm)) != 0)
return (ret);
if (dbp->type != DB_HASH && dbp->type != DB_UNKNOWN)
return (EINVAL);
dbp->type = DB_HASH;
DB_ILLEGAL_METHOD(dbp, DB_OK_HASH);
if ((ret = __db_fchk(dbenv,
"DB->open", hashm->dbmeta.flags,
DB_HASH_DUP | DB_HASH_SUBDB | DB_HASH_DUPSORT)) != 0)
return (ret);
if (F_ISSET(&hashm->dbmeta, DB_HASH_DUP))
F_SET(dbp, DB_AM_DUP);
else
if (F_ISSET(dbp, DB_AM_DUP)) {
__db_errx(dbenv,
"%s: DB_DUP specified to open method but not set in database",
name);
return (EINVAL);
}
if (F_ISSET(&hashm->dbmeta, DB_HASH_SUBDB))
F_SET(dbp, DB_AM_SUBDB);
else
if (F_ISSET(dbp, DB_AM_SUBDB)) {
__db_errx(dbenv,
"%s: multiple databases specified but not supported in file",
name);
return (EINVAL);
}
if (F_ISSET(&hashm->dbmeta, DB_HASH_DUPSORT)) {
if (dbp->dup_compare == NULL)
dbp->dup_compare = __bam_defcmp;
} else
if (dbp->dup_compare != NULL) {
__db_errx(dbenv,
"%s: duplicate sort function specified but not set in database",
name);
return (EINVAL);
}
dbp->pgsize = hashm->dbmeta.pagesize;
memcpy(dbp->fileid, hashm->dbmeta.uid, DB_FILE_ID_LEN);
return (0);
}
static db_pgno_t
__ham_init_meta(dbp, meta, pgno, lsnp)
DB *dbp;
HMETA *meta;
db_pgno_t pgno;
DB_LSN *lsnp;
{
DB_ENV *dbenv;
HASH *hashp;
db_pgno_t nbuckets;
u_int i, l2;
dbenv = dbp->dbenv;
hashp = dbp->h_internal;
if (hashp->h_hash == NULL)
hashp->h_hash = DB_HASHVERSION < 5 ? __ham_func4 : __ham_func5;
if (hashp->h_nelem != 0 && hashp->h_ffactor != 0) {
hashp->h_nelem = (hashp->h_nelem - 1) / hashp->h_ffactor + 1;
l2 = __db_log2(hashp->h_nelem > 2 ? hashp->h_nelem : 2);
} else
l2 = 1;
nbuckets = (db_pgno_t)(1 << l2);
memset(meta, 0, sizeof(HMETA));
meta->dbmeta.lsn = *lsnp;
meta->dbmeta.pgno = pgno;
meta->dbmeta.magic = DB_HASHMAGIC;
meta->dbmeta.version = DB_HASHVERSION;
meta->dbmeta.pagesize = dbp->pgsize;
if (F_ISSET(dbp, DB_AM_CHKSUM))
FLD_SET(meta->dbmeta.metaflags, DBMETA_CHKSUM);
if (F_ISSET(dbp, DB_AM_ENCRYPT)) {
meta->dbmeta.encrypt_alg =
((DB_CIPHER *)dbenv->crypto_handle)->alg;
DB_ASSERT(dbenv, meta->dbmeta.encrypt_alg != 0);
meta->crypto_magic = meta->dbmeta.magic;
}
meta->dbmeta.type = P_HASHMETA;
meta->dbmeta.free = PGNO_INVALID;
meta->dbmeta.last_pgno = pgno;
meta->max_bucket = nbuckets - 1;
meta->high_mask = nbuckets - 1;
meta->low_mask = (nbuckets >> 1) - 1;
meta->ffactor = hashp->h_ffactor;
meta->nelem = hashp->h_nelem;
meta->h_charkey = hashp->h_hash(dbp, CHARKEY, sizeof(CHARKEY));
memcpy(meta->dbmeta.uid, dbp->fileid, DB_FILE_ID_LEN);
if (F_ISSET(dbp, DB_AM_DUP))
F_SET(&meta->dbmeta, DB_HASH_DUP);
if (F_ISSET(dbp, DB_AM_SUBDB))
F_SET(&meta->dbmeta, DB_HASH_SUBDB);
if (dbp->dup_compare != NULL)
F_SET(&meta->dbmeta, DB_HASH_DUPSORT);
meta->spares[0] = pgno + 1;
for (i = 1; i <= l2; i++)
meta->spares[i] = meta->spares[0];
for (; i < NCACHED; i++)
meta->spares[i] = PGNO_INVALID;
return (nbuckets);
}
int
__ham_new_file(dbp, txn, fhp, name)
DB *dbp;
DB_TXN *txn;
DB_FH *fhp;
const char *name;
{
DB_ENV *dbenv;
DB_LSN lsn;
DB_MPOOLFILE *mpf;
DB_PGINFO pginfo;
DBT pdbt;
HMETA *meta;
PAGE *page;
int ret;
db_pgno_t lpgno;
void *buf;
dbenv = dbp->dbenv;
mpf = dbp->mpf;
meta = NULL;
page = NULL;
buf = NULL;
if (F_ISSET(dbp, DB_AM_INMEM)) {
lpgno = PGNO_BASE_MD;
if ((ret = __memp_fget(mpf, &lpgno, txn,
DB_MPOOL_CREATE | DB_MPOOL_DIRTY, &meta)) != 0)
return (ret);
LSN_NOT_LOGGED(lsn);
lpgno = __ham_init_meta(dbp, meta, PGNO_BASE_MD, &lsn);
meta->dbmeta.last_pgno = lpgno;
if ((ret = __db_log_page(dbp,
txn, &lsn, meta->dbmeta.pgno, (PAGE *)meta)) != 0)
goto err;
ret = __memp_fput(mpf, meta, dbp->priority);
meta = NULL;
if (ret != 0)
goto err;
if ((ret = __memp_fget(mpf, &lpgno, txn,
DB_MPOOL_CREATE, &page)) != 0)
goto err;
P_INIT(page,
dbp->pgsize, lpgno, PGNO_INVALID, PGNO_INVALID, 0, P_HASH);
LSN_NOT_LOGGED(page->lsn);
if ((ret =
__db_log_page(dbp, txn, &page->lsn, lpgno, page)) != 0)
goto err;
ret = __memp_fput(mpf, page, dbp->priority);
page = NULL;
if (ret != 0)
goto err;
} else {
memset(&pdbt, 0, sizeof(pdbt));
pginfo.db_pagesize = dbp->pgsize;
pginfo.type = dbp->type;
pginfo.flags =
F_ISSET(dbp, (DB_AM_CHKSUM | DB_AM_ENCRYPT | DB_AM_SWAP));
pdbt.data = &pginfo;
pdbt.size = sizeof(pginfo);
if ((ret = __os_calloc(dbp->dbenv, 1, dbp->pgsize, &buf)) != 0)
return (ret);
meta = (HMETA *)buf;
LSN_NOT_LOGGED(lsn);
lpgno = __ham_init_meta(dbp, meta, PGNO_BASE_MD, &lsn);
meta->dbmeta.last_pgno = lpgno;
if ((ret = __db_pgout(dbenv, PGNO_BASE_MD, meta, &pdbt)) != 0)
goto err;
if ((ret = __fop_write(dbenv, txn, name, DB_APP_DATA, fhp,
dbp->pgsize, 0, 0, buf, dbp->pgsize, 1, F_ISSET(
dbp, DB_AM_NOT_DURABLE) ? DB_LOG_NOT_DURABLE : 0)) != 0)
goto err;
meta = NULL;
#ifdef DIAGNOSTIC
memset(buf, 0, dbp->pgsize);
#endif
page = (PAGE *)buf;
P_INIT(page,
dbp->pgsize, lpgno, PGNO_INVALID, PGNO_INVALID, 0, P_HASH);
LSN_NOT_LOGGED(page->lsn);
if ((ret = __db_pgout(dbenv, lpgno, buf, &pdbt)) != 0)
goto err;
if ((ret = __fop_write(dbenv, txn, name, DB_APP_DATA, fhp,
dbp->pgsize, lpgno, 0, buf, dbp->pgsize, 1, F_ISSET(
dbp, DB_AM_NOT_DURABLE) ? DB_LOG_NOT_DURABLE : 0)) != 0)
goto err;
page = NULL;
}
err: if (buf != NULL)
__os_free(dbenv, buf);
else {
if (meta != NULL)
(void)__memp_fput(mpf, meta, dbp->priority);
if (page != NULL)
(void)__memp_fput(mpf, page, dbp->priority);
}
return (ret);
}
int
__ham_new_subdb(mdbp, dbp, txn)
DB *mdbp, *dbp;
DB_TXN *txn;
{
DBC *dbc;
DB_ENV *dbenv;
DB_LOCK metalock, mmlock;
DB_LSN lsn;
DB_MPOOLFILE *mpf;
DBMETA *mmeta;
HMETA *meta;
PAGE *h;
int i, ret, t_ret;
db_pgno_t lpgno, mpgno;
dbenv = mdbp->dbenv;
mpf = mdbp->mpf;
dbc = NULL;
meta = NULL;
mmeta = NULL;
LOCK_INIT(metalock);
LOCK_INIT(mmlock);
if ((ret = __db_cursor(mdbp, txn,
&dbc, CDB_LOCKING(dbenv) ? DB_WRITECURSOR : 0)) != 0)
return (ret);
if ((ret = __db_lget(dbc,
0, dbp->meta_pgno, DB_LOCK_WRITE, 0, &metalock)) != 0)
goto err;
if ((ret = __memp_fget(mpf, &dbp->meta_pgno, dbc->txn,
DB_MPOOL_CREATE, &meta)) != 0)
goto err;
lsn = meta->dbmeta.lsn;
lpgno = __ham_init_meta(dbp, meta, dbp->meta_pgno, &lsn);
mpgno = PGNO_BASE_MD;
if ((ret = __db_lget(dbc, 0, mpgno, DB_LOCK_WRITE, 0, &mmlock)) != 0)
goto err;
if ((ret = __memp_fget(mpf, &mpgno, dbc->txn,
DB_MPOOL_DIRTY, &mmeta)) != 0)
goto err;
meta->spares[0] = mmeta->last_pgno + 1;
for (i = 0; i < NCACHED && meta->spares[i] != PGNO_INVALID; i++)
meta->spares[i] = meta->spares[0];
if ((ret = __db_log_page(mdbp,
txn, &meta->dbmeta.lsn, dbp->meta_pgno, (PAGE *)meta)) != 0)
goto err;
if (DBENV_LOGGING(dbenv)
#if !defined(DEBUG_WOP)
&& txn != NULL
#endif
)
if ((ret = __ham_groupalloc_log(mdbp, txn,
&LSN(mmeta), 0, &LSN(mmeta), meta->spares[0],
meta->max_bucket + 1, 0, mmeta->last_pgno)) != 0)
goto err;
if ((ret = __memp_fput(mpf, meta, dbc->priority)) != 0)
goto err;
meta = NULL;
lpgno += mmeta->last_pgno;
if ((ret = __memp_fget(mpf, &lpgno, dbc->txn,
DB_MPOOL_CREATE | DB_MPOOL_DIRTY, &h)) != 0)
goto err;
mmeta->last_pgno = lpgno;
P_INIT(h, dbp->pgsize, lpgno, PGNO_INVALID, PGNO_INVALID, 0, P_HASH);
LSN(h) = LSN(mmeta);
if ((ret = __memp_fput(mpf, h, dbc->priority)) != 0)
goto err;
err:
if (mmeta != NULL && (t_ret = __memp_fput(mpf,
mmeta, dbc->priority)) != 0 && ret == 0)
ret = t_ret;
if ((t_ret = __LPUT(dbc, mmlock)) != 0 && ret == 0)
ret = t_ret;
if (meta != NULL &&
(t_ret = __memp_fput(mpf, meta, dbc->priority)) != 0 && ret == 0)
ret = t_ret;
if ((t_ret = __LPUT(dbc, metalock)) != 0 && ret == 0)
ret = t_ret;
if (dbc != NULL)
if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}