diff options
Diffstat (limited to 'src/log/log_verify_util.c')
-rw-r--r-- | src/log/log_verify_util.c | 2234 |
1 files changed, 2234 insertions, 0 deletions
diff --git a/src/log/log_verify_util.c b/src/log/log_verify_util.c new file mode 100644 index 00000000..88682921 --- /dev/null +++ b/src/log/log_verify_util.c @@ -0,0 +1,2234 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 2012 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +/* + * This file contains helper functions like data structure and in-memory db + * management, which are used to store various log verification information. + */ +#include "db_config.h" +#include "db_int.h" + +#include "dbinc/crypto.h" +#include "dbinc/db_page.h" +#include "dbinc/db_am.h" +#include "dbinc/btree.h" +#include "dbinc/hash.h" +#include "dbinc/qam.h" +#include "dbinc/mp.h" +#include "dbinc/txn.h" +#include "dbinc/fop.h" + +#include "dbinc/log_verify.h" + +#define BDBOP(op) do { \ + ret = (op); \ + if (ret != 0) { \ + __lv_on_bdbop_err(ret); \ + goto err; \ + } \ +} while (0) + +#define BDBOP2(dbenv, op, funct) do { \ + ret = (op); \ + if (ret != 0) { \ + __lv_on_bdbop_err(ret); \ + __db_err(dbenv->env, ret, "\n%s", funct); \ + return (ret); \ + } \ +} while (0) + +#define BDBOP3(dbenv, op, excpt, funct) do { \ + ret = (op); \ + if (ret != 0) { \ + __lv_on_bdbop_err(ret); \ + if (ret != excpt) { \ + __db_err(dbenv->env, ret, "\n%s", funct); \ + return (ret); \ + } \ + } \ +} while (0) + +typedef int (*btcmp_funct)(DB *, const DBT *, const DBT *); +typedef int (*dupcmp_funct)(DB *, const DBT *, const DBT *); + +static int __lv_add_recycle_handler __P(( + DB_LOG_VRFY_INFO *, VRFY_TXN_INFO *, void *)); +static int __lv_add_recycle_lsn __P((VRFY_TXN_INFO *, const DB_LSN *)); +static size_t __lv_dbt_arrsz __P((const DBT *, u_int32_t)); +static int __lv_fidpgno_cmp __P((DB *, const DBT *, const DBT *)); +static int __lv_i32_cmp __P((DB *, const DBT *, const DBT *)); +static int __lv_lsn_cmp __P((DB *, const DBT *, const DBT *)); +static void __lv_on_bdbop_err __P((int)); +static int __lv_open_db __P((DB_ENV *, DB **, DB_THREAD_INFO *, + const char *, int, btcmp_funct, u_int32_t, dupcmp_funct)); +static int __lv_pack_filereg __P((const VRFY_FILEREG_INFO *, DBT *)); +static int __lv_pack_txn_vrfy_info __P(( + const VRFY_TXN_INFO *, DBT *, DBT *data)); +static int __lv_seccbk_fname __P((DB *, const DBT *, const DBT *, DBT *)); +static int __lv_seccbk_lsn __P((DB *, const DBT *, const DBT *, DBT *)); +static int __lv_seccbk_txnpg __P((DB *, const DBT *, const DBT *, DBT *)); +static void __lv_setup_logtype_names __P((DB_LOG_VRFY_INFO *lvinfo)); +static int __lv_txnrgns_lsn_cmp __P((DB *, const DBT *, const DBT *)); +static int __lv_ui32_cmp __P((DB *, const DBT *, const DBT *)); +static int __lv_unpack_txn_vrfy_info __P((VRFY_TXN_INFO **, const DBT *)); +static int __lv_unpack_filereg __P((const DBT *, VRFY_FILEREG_INFO **)); + +static void __lv_on_bdbop_err(ret) + int ret; +{ + /* Pass lint checks. We need the ret and this function for debugging. */ + COMPQUIET(ret, 0); +} + +/* + * __create_log_vrfy_info -- + * Initialize and return a log verification handle to be used throughout + * a verification process. + * + * PUBLIC: int __create_log_vrfy_info __P((const DB_LOG_VERIFY_CONFIG *, + * PUBLIC: DB_LOG_VRFY_INFO **, DB_THREAD_INFO *)); + */ +int +__create_log_vrfy_info(cfg, lvinfopp, ip) + const DB_LOG_VERIFY_CONFIG *cfg; + DB_LOG_VRFY_INFO **lvinfopp; + DB_THREAD_INFO *ip; +{ + const char *envhome; + int inmem, ret; + u_int32_t cachesz, envflags; + const char *dbf1, *dbf2, *dbf3, *dbf4, *dbf5, *dbf6, *dbf7, *dbf8, + *dbf9, *dbf10, *dbf11; + DB_LOG_VRFY_INFO *lvinfop; + + dbf1 = "__db_log_vrfy_txninfo.db"; + dbf2 = "__db_log_vrfy_fileregs.db"; + dbf3 = "__db_log_vrfy_pgtxn.db"; + dbf4 = "__db_log_vrfy_lsntime.db"; + dbf5 = "__db_log_vrfy_timelsn.db"; + dbf6 = "__db_log_vrfy_ckps.db"; + dbf7 = "__db_log_vrfy_dbregids.db"; + dbf8 = "__db_log_vrfy_fnameuid.db"; + dbf9 = "__db_log_vrfy_timerange.db"; + dbf10 = "__db_log_vrfy_txnaborts.db"; + dbf11 = "__db_log_vrfy_txnpg.db"; + + envhome = cfg->temp_envhome; + lvinfop = NULL; + cachesz = cfg->cachesize; + if (cachesz== 0) + cachesz = 1024 * 1024 * 256; + + BDBOP(__os_malloc(NULL, sizeof(DB_LOG_VRFY_INFO), &lvinfop)); + memset(lvinfop, 0, sizeof(DB_LOG_VRFY_INFO)); + lvinfop->ip = ip; + __lv_setup_logtype_names(lvinfop); + /* Avoid the VERIFY_PARTIAL bit being cleared if no ckp_lsn exists. */ + lvinfop->valid_lsn.file = lvinfop->valid_lsn.offset = (u_int32_t)-1; + + /* + * The envhome parameter determines if we will use an in-memory + * environment and databases. + */ + if (envhome == NULL) { + envflags = DB_PRIVATE; + inmem = 1; + } else { + envflags = 0; + inmem = 0; + } + + /* Create log verify internal database environment. */ + BDBOP(db_env_create(&lvinfop->dbenv, 0)); + BDBOP(__memp_set_cachesize(lvinfop->dbenv, 0, cachesz, 1)); + /* + * Log verification internal db environment should be accessed + * single-threaded. No transaction semantics needed. + */ + BDBOP(__env_open(lvinfop->dbenv, envhome, + envflags | DB_CREATE | DB_INIT_MPOOL, 0666)); + + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txninfo, ip, dbf1, + inmem, __lv_ui32_cmp, 0, NULL)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->fileregs, ip, dbf2, + inmem, NULL, 0, NULL)); + + /* No dup allowed, always overwrite data with same key. */ + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->dbregids, ip, dbf7, + inmem, __lv_i32_cmp, 0, NULL)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->pgtxn, ip, dbf3, + inmem, __lv_fidpgno_cmp, 0, NULL)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txnpg, ip, dbf11, + inmem, __lv_ui32_cmp, DB_DUP | DB_DUPSORT, __lv_fidpgno_cmp)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->lsntime, ip, dbf4, + inmem, __lv_lsn_cmp, 0, NULL)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->timelsn, ip, dbf5, + inmem, __lv_i32_cmp, DB_DUP | DB_DUPSORT, __lv_lsn_cmp)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txnaborts, ip, dbf10, + inmem, __lv_lsn_cmp, 0, NULL)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->ckps, ip, dbf6, + inmem, __lv_lsn_cmp, 0, NULL)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->fnameuid, ip, dbf8, + inmem, NULL, 0, NULL)); + BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txnrngs, ip, dbf9, + inmem, __lv_ui32_cmp, DB_DUP | DB_DUPSORT, __lv_txnrgns_lsn_cmp)); + + BDBOP(__db_associate(lvinfop->lsntime, ip, NULL, + lvinfop->timelsn, __lv_seccbk_lsn, DB_CREATE)); + BDBOP(__db_associate(lvinfop->fileregs, ip, NULL, + lvinfop->fnameuid, __lv_seccbk_fname, DB_CREATE)); + BDBOP(__db_associate(lvinfop->pgtxn, ip, NULL, + lvinfop->txnpg, __lv_seccbk_txnpg, DB_CREATE)); + + *lvinfopp = lvinfop; + + return (0); +err: + if (lvinfop->dbenv && ret != 0) + __db_err(lvinfop->dbenv->env, ret, "__create_log_vrfy_info"); + (void)__destroy_log_vrfy_info(lvinfop); + + return (ret); +} + +/* + * __destroy_log_vrfy_info -- + * Destroy and free a log verification handle. + * + * PUBLIC: int __destroy_log_vrfy_info __P((DB_LOG_VRFY_INFO *)); + */ +int +__destroy_log_vrfy_info(lvinfop) + DB_LOG_VRFY_INFO *lvinfop; +{ + int ret; + + ret = 0; + if (lvinfop == NULL) + return (0); + + if (lvinfop->txnaborts != NULL && + (ret = __db_close(lvinfop->txnaborts, NULL, 0)) != 0) + goto err; + if (lvinfop->txninfo != NULL && + (ret = __db_close(lvinfop->txninfo, NULL, 0)) != 0) + goto err; + if (lvinfop->dbregids != NULL && + (ret = __db_close(lvinfop->dbregids, NULL, 0)) != 0) + goto err; + if (lvinfop->fileregs != NULL && + (ret = __db_close(lvinfop->fileregs, NULL, 0)) != 0) + goto err; + if (lvinfop->pgtxn != NULL && + (ret = __db_close(lvinfop->pgtxn, NULL, 0)) != 0) + goto err; + if (lvinfop->lsntime != NULL && + (ret = __db_close(lvinfop->lsntime, NULL, 0)) != 0) + goto err; + if (lvinfop->ckps != NULL && + (ret = __db_close(lvinfop->ckps, NULL, 0)) != 0) + goto err; + if (lvinfop->txnrngs != NULL && + (ret = __db_close(lvinfop->txnrngs, NULL, 0)) != 0) + goto err; + if (lvinfop->fnameuid != NULL && + (ret = __db_close(lvinfop->fnameuid, NULL, 0)) != 0) + goto err; + if (lvinfop->timelsn != NULL && + (ret = __db_close(lvinfop->timelsn, NULL, 0)) != 0) + goto err; + if (lvinfop->txnpg != NULL && + (ret = __db_close(lvinfop->txnpg, NULL, 0)) != 0) + goto err; + if (lvinfop->dbenv != NULL && + (ret = __env_close(lvinfop->dbenv, 0)) != 0) + goto err; +err: + __os_free(NULL, lvinfop); + + return (ret); +} + +/* Secondary index callback function for DB_LOG_VRFY_INFO->timelsn. */ +static int +__lv_seccbk_fname(secdb, key, data, result) + DB *secdb; + const DBT *key; + const DBT *data; + DBT *result; +{ + int ret, tret; + VRFY_FILEREG_INFO *freg; + char *buf; + size_t buflen, slen; + + ret = tret = 0; + COMPQUIET(key, NULL); + if ((ret = __lv_unpack_filereg(data, &freg)) != 0) + goto out; + if (freg->fname == NULL || (slen = strlen(freg->fname)) == 0) { + ret = DB_DONOTINDEX; + goto out; + } + + buflen = (slen + 1) * sizeof(char); + if ((ret = __os_umalloc(secdb->dbenv->env, buflen, &buf)) != 0) + goto out; + (void)strcpy(buf, freg->fname); + result->size = (u_int32_t)buflen; + result->flags |= DB_DBT_APPMALLOC; + result->data = buf; +out: + if (freg != NULL && (tret = __free_filereg_info(freg)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +/* Secondary index callback function for DB_LOG_VRFY_INFO->txnpg. */ +static int +__lv_seccbk_txnpg(secdb, key, data, result) + DB *secdb; + const DBT *key; + const DBT *data; + DBT *result; +{ + COMPQUIET(key, NULL); + COMPQUIET(secdb, NULL); + /* Txnid is the secondary key, and it's all the data dbt has. */ + result->data = data->data; + result->size = data->size; + + return (0); +} + +/* Secondary index callback function for DB_LOG_VRFY_INFO->timelsn. */ +static int +__lv_seccbk_lsn(secdb, key, data, result) + DB *secdb; + const DBT *key; + const DBT *data; + DBT *result; +{ + VRFY_TIMESTAMP_INFO *lvti; + + COMPQUIET(key, NULL); + COMPQUIET(secdb, NULL); + + lvti = (VRFY_TIMESTAMP_INFO *)data->data; + result->data = &(lvti->timestamp); + result->size = sizeof(lvti->timestamp); + + return (0); +} + +/* + * Open a BTREE database handle, optionally set the btree compare function + * and flags if any. + */ +static int +__lv_open_db(dbenv, dbpp, ip, name, inmem, cmpf, sflags, dupcmpf) + DB_ENV *dbenv; + DB **dbpp; + const char *name; + int inmem; + btcmp_funct cmpf; + u_int32_t sflags; + dupcmp_funct dupcmpf; + DB_THREAD_INFO *ip; +{ + int ret; + const char *dbfname, *dbname; + DB *dbp; + + dbp = NULL; + ret = 0; + if (inmem) { + dbfname = NULL; + dbname = name; + } else { + dbfname = name; + dbname = NULL; + } + + BDBOP(db_create(&dbp, dbenv, 0)); + + if (cmpf != NULL) + BDBOP(__bam_set_bt_compare(dbp, cmpf)); + if (dupcmpf != NULL) + dbp->dup_compare = dupcmpf; + if (sflags != 0) + BDBOP(__db_set_flags(dbp, sflags)); + /* No concurrency needed, a big page size reduces overflow pages. */ + BDBOP(__db_set_pagesize(dbp, 16 * 1024)); + + BDBOP(__db_open(dbp, ip, NULL, dbfname, dbname, DB_BTREE, DB_CREATE, + 0666, PGNO_BASE_MD)); + + *dbpp = dbp; + + return (0); +err: + if (dbenv != NULL && ret != 0) + __db_err(dbenv->env, ret, "__lv_open_db"); + if (dbp != NULL) + (void)__db_close(dbp, NULL, 0); + + return (ret); +} + +/* Btree compare function for a [fileid, pgno] key. */ +static int +__lv_fidpgno_cmp(db, dbt1, dbt2) + DB *db; + const DBT *dbt1; + const DBT *dbt2; +{ + db_pgno_t pgno1, pgno2; + int ret; + size_t len; + + COMPQUIET(db, NULL); + len = DB_FILE_ID_LEN; + ret = memcmp(dbt1->data, dbt2->data, len); + if (ret == 0) { + memcpy(&pgno1, (u_int8_t *)dbt1->data + len, + sizeof(pgno1)); + memcpy(&pgno2, (u_int8_t *)dbt2->data + len, + sizeof(pgno2)); + ret = NUMCMP(pgno1, pgno2); + } + + return (ret); +} + +/* Btree compare function for a int32_t type of key. */ +static int +__lv_i32_cmp(db, dbt1, dbt2) + DB *db; + const DBT *dbt1; + const DBT *dbt2; +{ + int32_t k1, k2; + + COMPQUIET(db, NULL); + memcpy(&k1, dbt1->data, sizeof(k1)); + memcpy(&k2, dbt2->data, sizeof(k2)); + + return (NUMCMP(k1, k2)); +} + +/* Btree compare function for a u_int32_t type of key. */ +static int +__lv_ui32_cmp(db, dbt1, dbt2) + DB *db; + const DBT *dbt1; + const DBT *dbt2; +{ + u_int32_t k1, k2; + + COMPQUIET(db, NULL); + memcpy(&k1, dbt1->data, sizeof(k1)); + memcpy(&k2, dbt2->data, sizeof(k2)); + + return (NUMCMP(k1, k2)); +} + +/* Btree compare function for a DB_LSN type of key. */ +static int +__lv_lsn_cmp(db, dbt1, dbt2) + DB *db; + const DBT *dbt1; + const DBT *dbt2; +{ + DB_LSN lsn1, lsn2; + + DB_ASSERT(db->env, dbt1->size == sizeof(DB_LSN)); + DB_ASSERT(db->env, dbt2->size == sizeof(DB_LSN)); + memcpy(&lsn1, dbt1->data, sizeof(DB_LSN)); + memcpy(&lsn2, dbt2->data, sizeof(DB_LSN)); + + return (LOG_COMPARE(&lsn1, &lsn2)); +} + +/* + * Structure management routines. We keep each structure on a + * consecutive memory chunk. + * + * The get functions will allocate memory via __os_malloc, and callers + * should free the memory after use. The update functions for VRFY_TXN_INFO + * and VRFY_FILEREG_INFO may realloc the structure. + */ + +/* + * PUBLIC: int __put_txn_vrfy_info __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: const VRFY_TXN_INFO *)); + */ +int +__put_txn_vrfy_info (lvinfo, txninfop) + const DB_LOG_VRFY_INFO *lvinfo; + const VRFY_TXN_INFO *txninfop; +{ + int ret; + DBT key, data; + + ret = __lv_pack_txn_vrfy_info(txninfop, &key, &data); + DB_ASSERT(lvinfo->dbenv->env, ret == 0); + + BDBOP2(lvinfo->dbenv, __db_put(lvinfo->txninfo, lvinfo->ip, NULL, + &key, &data, 0), "__put_txn_vrfy_info"); + __os_free(lvinfo->dbenv->env, data.data); + + return (0); +} + +/* Construct a key and data DBT from the structure. */ +static int +__lv_pack_txn_vrfy_info(txninfop, key, data) + const VRFY_TXN_INFO *txninfop; + DBT *key, *data; +{ + int ret; + char *buf, *p; + size_t bufsz, len; + u_int32_t i; + DBT *pdbt; + + memset(key, 0, sizeof(DBT)); + memset(data, 0, sizeof(DBT)); + ret = 0; + bufsz = TXN_VERIFY_INFO_TOTSIZE(*txninfop); + + if ((ret = __os_malloc(NULL, bufsz, &buf)) != 0) + goto err; + memset(buf, 0, bufsz); + memcpy(buf, txninfop, TXN_VERIFY_INFO_FIXSIZE); + p = buf + TXN_VERIFY_INFO_FIXSIZE; + memcpy(p, txninfop->recycle_lsns, len = sizeof(DB_LSN) * + txninfop->num_recycle); + p += len; + + for (i = 0; i < txninfop->filenum; i++) { + + pdbt = &(txninfop->fileups[i]); + memcpy(p, &(pdbt->size), sizeof(pdbt->size)); + p += sizeof(pdbt->size); + memcpy(p, pdbt->data, pdbt->size); + p += pdbt->size; + } + + key->data = (void *)&txninfop->txnid; + key->size = sizeof(txninfop->txnid); + data->data = buf; + data->size = (u_int32_t)bufsz; + data->flags |= DB_DBT_MALLOC; +err: + return (ret); +} + +/* Calculate a DBT array's total number of bytes to store. */ +static size_t +__lv_dbt_arrsz(arr, arrlen) + const DBT *arr; + u_int32_t arrlen; +{ + u_int32_t i; + size_t sz; + + sz = 0; + + /* For each DBT object, store its size and its data bytes. */ + for (i = 0; i < arrlen; i++) + sz += arr[i].size + sizeof(arr[i].size); + + return sz; +} + +/* + * __get_txn_vrfy_info -- + * Get a VRFY_TXN_INFO object from db by txnid. Callers should free the + * object by calling __free_txninfo. + * + * PUBLIC: int __get_txn_vrfy_info __P((const DB_LOG_VRFY_INFO *, u_int32_t, + * PUBLIC: VRFY_TXN_INFO **)); + */ +int +__get_txn_vrfy_info (lvinfo, txnid, txninfopp) + const DB_LOG_VRFY_INFO *lvinfo; + u_int32_t txnid; + VRFY_TXN_INFO **txninfopp; +{ + int ret; + DBT key, data; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = &txnid; + key.size = sizeof(txnid); + + BDBOP3(lvinfo->dbenv, __db_get(lvinfo->txninfo, lvinfo->ip, NULL, + &key, &data, 0), DB_NOTFOUND, "__get_txn_vrfy_info"); + + if (ret != DB_NOTFOUND) + ret = __lv_unpack_txn_vrfy_info(txninfopp, &data); + + return (ret); +} + +/* Construct a structure from a DBT. */ +static int +__lv_unpack_txn_vrfy_info(txninfopp, data) + VRFY_TXN_INFO **txninfopp; + const DBT *data; +{ + size_t bufsz; + VRFY_TXN_INFO *buf, *txninfop; + DB_LSN *lsns, *p; + u_int32_t i, sz; + char *pb, *q; + int ret; + + ret = 0; + i = sz = 0; + lsns = p = NULL; + pb = q = NULL; + txninfop = (VRFY_TXN_INFO *)data->data; + lsns = (DB_LSN *)((char *)data->data + TXN_VERIFY_INFO_FIXSIZE); + pb = (char *)lsns + txninfop->num_recycle * sizeof(DB_LSN); + + if ((ret = __os_malloc(NULL, bufsz = sizeof(VRFY_TXN_INFO), &buf)) != 0) + goto err; + memset(buf, 0, bufsz); + memcpy(buf, data->data, TXN_VERIFY_INFO_FIXSIZE); + + if (txninfop->num_recycle != 0) { + if ((ret = __os_malloc(NULL, + txninfop->num_recycle * sizeof(DB_LSN), &p)) != 0) + goto err; + memcpy(p, lsns, txninfop->num_recycle * sizeof(DB_LSN)); + buf->recycle_lsns = p; + } + + if (txninfop->filenum != 0) { + if ((ret = __os_malloc(NULL, + txninfop->filenum * sizeof(DBT), &q)) != 0) + goto err; + memset(q, 0, txninfop->filenum * sizeof(DBT)); + buf->fileups = (DBT *)q; + for (i = 0; i < txninfop->filenum; i++) { + memcpy(&sz, pb, sizeof(sz)); + pb += sizeof(sz); + if ((ret = __os_malloc(NULL, sz, &q)) != 0) + goto err; + memcpy(q, pb, sz); + pb += sz; + + buf->fileups[i].data = q; + buf->fileups[i].size = sz; + } + } + + *txninfopp = buf; +err: + return (ret); +} + +static int +__lv_add_recycle_lsn (txninfop, lsn) + VRFY_TXN_INFO *txninfop; + const DB_LSN *lsn; +{ + int ret; + + ret = 0; + txninfop->num_recycle++; + if ((ret = __os_realloc(NULL, txninfop->num_recycle * sizeof(DB_LSN), + &(txninfop->recycle_lsns))) != 0) + goto err; + txninfop->recycle_lsns[txninfop->num_recycle - 1] = *lsn; +err: + return (ret); +} + +/* + * __add_recycle_lsn_range -- + * Add recycle info for each txn within the recycled txnid range. + * + * PUBLIC: int __add_recycle_lsn_range __P((DB_LOG_VRFY_INFO *, + * PUBLIC: const DB_LSN *, u_int32_t, u_int32_t)); + */ +int +__add_recycle_lsn_range(lvinfo, lsn, min, max) + DB_LOG_VRFY_INFO *lvinfo; + const DB_LSN *lsn; + u_int32_t min, max; +{ + DBC *csr; + int ret, tret; + u_int32_t i; + DBT key2, data2; + struct __add_recycle_params param; + + csr = NULL; + ret = tret = 0; + memset(&key2, 0, sizeof(DBT)); + memset(&data2, 0, sizeof(DBT)); + memset(¶m, 0, sizeof(param)); + + if ((ret = __os_malloc(lvinfo->dbenv->env, sizeof(VRFY_TXN_INFO *) * + (param.ti2ul = 1024), &(param.ti2u))) != 0) + goto err; + param.ti2ui = 0; + param.recycle_lsn = *lsn; + param.min = min; + param.max = max; + + /* Iterate the specified range and process each transaction. */ + if ((ret = __iterate_txninfo(lvinfo, min, max, __lv_add_recycle_handler, + ¶m)) != 0) + goto err; + + /* + * Save updated txninfo structures. We can't do so in the above + * iteration, so we have to save them here. + */ + BDBOP(__db_cursor(lvinfo->txninfo, lvinfo->ip, NULL, &csr, DBC_BULK)); + + for (i = 0; i < param.ti2ui; i++) { + ret = __lv_pack_txn_vrfy_info(param.ti2u[i], &key2, &data2); + DB_ASSERT(lvinfo->dbenv->env, ret == 0); + BDBOP(__dbc_put(csr, &key2, &data2, DB_KEYLAST)); + /* + * key2.data refers to param.ti2u[i]'s memory, data2.data is + * freed by DB since we set DB_DBT_MALLOC. + */ + if ((ret = __free_txninfo(param.ti2u[i])) != 0) + goto err; + } + +err: + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + __os_free(lvinfo->dbenv->env, param.ti2u); + if (ret != 0) + __db_err(lvinfo->dbenv->env, ret, + "__add_recycle_lsn_range"); + + return (ret); +} + +/* + * __iterate_txninfo -- + * Iterate throught the transaction info database as fast as possible, + * and process each key/data pair using a callback handler. Break the + * iteration if the handler returns non-zero values. + * + * PUBLIC: int __iterate_txninfo __P((DB_LOG_VRFY_INFO *, u_int32_t, + * PUBLIC: u_int32_t, TXNINFO_HANDLER, void *)); + */ +int +__iterate_txninfo(lvinfo, min, max, handler, param) + DB_LOG_VRFY_INFO *lvinfo; + u_int32_t min, max; + TXNINFO_HANDLER handler; + void *param; +{ + ENV *env; + VRFY_TXN_INFO *txninfop; + int ret, tret; + u_int32_t bufsz, pgsz, txnid; + size_t retkl, retdl; + char *btbuf; + u_int8_t *retk, *retd; + DBT key, data, data2; + DBC *csr; + void *p; + + csr = NULL; + env = lvinfo->dbenv->env; + txninfop = NULL; + ret = tret = 0; + txnid = 0; + retkl = retdl = 0; + bufsz = 64 * 1024; + btbuf = NULL; + retk = retd = NULL; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + memset(&data2, 0, sizeof(DBT)); + + pgsz = lvinfo->txninfo->pgsize; + DB_ASSERT(env, ret == 0); + + if (bufsz % pgsz != 0) + bufsz = pgsz * (bufsz / pgsz); + + if ((ret = __os_malloc(env, bufsz, &btbuf)) != 0) + goto err; + + BDBOP(__db_cursor(lvinfo->txninfo, lvinfo->ip, NULL, &csr, DBC_BULK)); + + /* + * Use bulk retrieval to scan the database as fast as possible. + */ + data.data = btbuf; + data.ulen = bufsz; + data.flags |= DB_DBT_USERMEM; + + for (ret = __dbc_get(csr, &key, &data, DB_FIRST | DB_MULTIPLE_KEY) ;; + ret = __dbc_get(csr, &key, &data, DB_NEXT | DB_MULTIPLE_KEY)) { + switch (ret) { + case 0: + break; + case DB_NOTFOUND: + goto out; + /* No break statement allowed by lint here. */ + case DB_BUFFER_SMALL: + if ((ret = __os_realloc(lvinfo->dbenv->env, + bufsz *= 2, &btbuf)) != 0) + goto out; + data.ulen = bufsz; + data.data = btbuf; + continue;/* Continue the for-loop. */ + /* No break statement allowed by lint here. */ + default: + goto err; + } + + /* + * Do bulk get. Some txninfo objects may be updated by the + * handler, but we can't store them immediately in the same + * loop because we wouldn't be able to continue the bulk get + * using the same cursor; and we can't use another cursor + * otherwise we may self-block. In the handler we need to + * store the updated objects and store them to db when we get + * out of this loop. + */ + DB_MULTIPLE_INIT(p, &data); + while (1) { + DB_MULTIPLE_KEY_NEXT(p, &data, + retk, retkl, retd, retdl); + if (p == NULL) + break; + DB_ASSERT(env, retkl == sizeof(txnid) && retk != NULL); + memcpy(&txnid, retk, retkl); + /* + * Process it if txnid in range or no range specified. + * The range must be a closed one. + */ + if ((min != 0 && txnid >= min && max != 0 && + txnid <= max) || (min == 0 && max == 0)) { + data2.data = retd; + data2.size = (u_int32_t)retdl; + + if ((ret = __lv_unpack_txn_vrfy_info( + &txninfop, &data2)) != 0) + goto out; + if ((ret = handler(lvinfo, txninfop, + param)) != 0) + /* Stop the iteration on error. */ + goto out; + } + } + + } +out: + if (ret == DB_NOTFOUND) + ret = 0; +err: + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + __os_free(lvinfo->dbenv->env, btbuf); + return (ret); +} + +/* Txninfo iteration handler to add recycle info for affected txns. */ +static int +__lv_add_recycle_handler(lvinfo, txninfop, params) + DB_LOG_VRFY_INFO *lvinfo; + VRFY_TXN_INFO *txninfop; + void *params; +{ + int ret; + struct __add_recycle_params *param; + + ret = 0; + param = (struct __add_recycle_params *)params; + + /* + * If the txnid is reused, update its recycle info and note it for + * later update, otherwise free the txninfop structure. + */ + if (txninfop->txnid < param->min && txninfop->txnid > param->max) { + ret = __free_txninfo(txninfop); + return (ret); + } + + ret = __lv_add_recycle_lsn(txninfop, &(param->recycle_lsn)); + + if (ret != 0) + goto err; + /* + * Below is one way to tell if a txn is aborted without doing another + * backward pass of the log. However if the txn id is not in the + * chosen recycled txn id range, we can't tell, until all the log + * records are passed --- the remaining active txns are the aborted + * txns. + * No longer needed since we did another backward pass of the log + * and have all the txn lifetimes. + if (txninfop->status == TXN_STAT_ACTIVE) + __on_txn_abort(lvinfo, txninfop); + */ + if (txninfop->status == TXN_STAT_PREPARE) { + __db_errx(lvinfo->dbenv->env, + "[ERROR] Transaction with ID %u is prepared and not " + "committed, but its ID is recycled by log record [%u, %u].", + txninfop->txnid, param->recycle_lsn.file, + param->recycle_lsn.offset); + } + /* Note down to store later. */ + param->ti2u[(param->ti2ui)++] = txninfop; + if (param->ti2ui == param->ti2ul) + BDBOP(__os_realloc(lvinfo->dbenv->env, + sizeof(VRFY_TXN_INFO *) * (param->ti2ul *= 2), + &(param->ti2u))); +err: + return (ret); + +} +/* + * PUBLIC: int __rem_last_recycle_lsn __P((VRFY_TXN_INFO *)); + */ +int +__rem_last_recycle_lsn(txninfop) + VRFY_TXN_INFO *txninfop; +{ + int ret; + + ret = 0; + if (txninfop->num_recycle == 0) + return (0); + txninfop->num_recycle--; + if (txninfop->num_recycle > 0) + BDBOP(__os_realloc(NULL, txninfop->num_recycle * sizeof(DB_LSN), + &(txninfop->recycle_lsns))); + else { + __os_free(NULL, txninfop->recycle_lsns); + txninfop->recycle_lsns = NULL; + } +err: + return (ret); + +} + +/* + * __add_file_updated -- + * Add a file's dbregid and uid to the updating txn if it's not yet + * recorded. + * + * PUBLIC: int __add_file_updated __P((VRFY_TXN_INFO *, const DBT *, int32_t)); + */ +int +__add_file_updated (txninfop, fileid, dbregid) + VRFY_TXN_INFO *txninfop; + const DBT *fileid; + int32_t dbregid; +{ + int ret; + DBT *pdbt, *p; + u_int32_t found, i; + + ret = 0; + p = pdbt = NULL; + + for (found = 0, i = 0; i < txninfop->filenum; i++) { + p = &(txninfop->fileups[i]); + if (p->size == fileid->size && + memcmp(p->data, fileid->data, p->size) == 0) { + found = 1; + break; + } + } + + if (found) + return (0); + + /* Add file's uid into the array, deep copy from fileid. */ + txninfop->filenum++; + if ((ret = __os_realloc(NULL, txninfop->filenum * + sizeof(DBT), &(txninfop->fileups))) != 0) + goto err; + + pdbt = &(txninfop->fileups[txninfop->filenum - 1]); + memset(pdbt, 0, sizeof(DBT)); + if ((ret = __os_malloc(NULL, + pdbt->size = fileid->size, &(pdbt->data))) != 0) + goto err; + memcpy(pdbt->data, fileid->data, fileid->size); + + /* Add file dbregid into the array. */ + BDBOP(__os_realloc(NULL, txninfop->filenum * + sizeof(int32_t), &(txninfop->dbregid))); + txninfop->dbregid[txninfop->filenum - 1] = dbregid; +err: + return (ret); +} + +/* + * PUBLIC: int __del_file_updated __P((VRFY_TXN_INFO *, const DBT *)); + */ +int +__del_file_updated (txninfop, fileid) + VRFY_TXN_INFO *txninfop; + const DBT *fileid; +{ + u_int32_t found, i; + int ret; + DBT *p; + void *pdbtdata; + + ret = 0; + + if (txninfop->filenum == 0) + return (0); + + /* + * If the array has an element identical to fileid, remove it. fileid + * itself is intact after this function call. + */ + for (found = 0, i = 0, pdbtdata = NULL; i < txninfop->filenum; i++) { + p = &(txninfop->fileups[i]); + if (p->size == fileid->size && + memcmp(p->data, fileid->data, p->size) == 0) { + pdbtdata = p->data; + if (txninfop->filenum > 1) { + memmove(txninfop->fileups + i, txninfop-> + fileups + i + 1, sizeof(DBT) * (txninfop-> + filenum - (i + 1))); + memmove(txninfop->dbregid + i, txninfop-> + dbregid + i + 1, sizeof(int32_t) * + (txninfop->filenum - (i + 1))); + } else { + __os_free(NULL, txninfop->fileups); + __os_free(NULL, txninfop->dbregid); + txninfop->fileups = NULL; + txninfop->dbregid = NULL; + } + found = 1; + break; + } + } + + if (found) { + txninfop->filenum--; + if (txninfop->filenum) { + BDBOP(__os_realloc(NULL, sizeof(DBT) * + txninfop->filenum, &(txninfop->fileups))); + BDBOP(__os_realloc(NULL, sizeof(int32_t) * + txninfop->filenum, &(txninfop->dbregid))); + } + __os_free(NULL, pdbtdata); + } +err: + return (ret); +} + +/* + * PUBLIC: int __clear_fileups __P((VRFY_TXN_INFO *)); + */ +int +__clear_fileups(txninfop) + VRFY_TXN_INFO *txninfop; +{ + u_int32_t i; + + for (i = 0; i < txninfop->filenum; i++) + __os_free(NULL, txninfop->fileups[i].data); + + __os_free(NULL, txninfop->fileups); + __os_free(NULL, txninfop->dbregid); + txninfop->fileups = NULL; + txninfop->dbregid = NULL; + txninfop->filenum = 0; + + return (0); +} + +/* + * __free_txninfo_stack -- + * The object is on stack, only free its internal memory, not itself. + * PUBLIC: int __free_txninfo_stack __P((VRFY_TXN_INFO *)); + */ +int +__free_txninfo_stack (p) + VRFY_TXN_INFO *p; +{ + u_int32_t i; + + if (p == NULL) + return (0); + + if (p->fileups != NULL) { + for (i = 0; i < p->filenum; i++) + __os_free(NULL, p->fileups[i].data); + __os_free(NULL, p->fileups); + } + + if (p->dbregid != NULL) + __os_free(NULL, p->dbregid); + + if (p->recycle_lsns != NULL) + __os_free(NULL, p->recycle_lsns); + + return (0); +} +/* + * PUBLIC: int __free_txninfo __P((VRFY_TXN_INFO *)); + */ +int +__free_txninfo(p) + VRFY_TXN_INFO *p; +{ + (void)__free_txninfo_stack(p); + __os_free(NULL, p); + + return (0); +} + +/* Construct a key and data DBT from the structure. */ +static int +__lv_pack_filereg(freginfo, data) + const VRFY_FILEREG_INFO *freginfo; + DBT *data; +{ + char *buf, *p; + size_t bufsz, offset; + int ret; + + ret = 0; + if ((ret = __os_malloc(NULL, + bufsz = FILE_REG_INFO_TOTSIZE(*freginfo), &buf)) != 0) + goto err; + memset(buf, 0, bufsz); + + memcpy(buf, freginfo, FILE_REG_INFO_FIXSIZE); + p = buf + FILE_REG_INFO_FIXSIZE; + + offset = sizeof(int32_t) * freginfo->regcnt; + memcpy(p, freginfo->dbregids, offset); + p += offset; + + memcpy(p, &(freginfo->fileid.size), sizeof(freginfo->fileid.size)); + p += sizeof(freginfo->fileid.size); + memcpy(p, freginfo->fileid.data, freginfo->fileid.size); + p += freginfo->fileid.size; + (void)strcpy(p, freginfo->fname); + + data->data = buf; + data->size = (u_int32_t)bufsz; +err: + return (ret); +} + +/* + * PUBLIC: int __put_filereg_info __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: const VRFY_FILEREG_INFO *)); + */ +int __put_filereg_info (lvinfo, freginfo) + const DB_LOG_VRFY_INFO *lvinfo; + const VRFY_FILEREG_INFO *freginfo; +{ + + int ret; + DBT data; + + memset(&data, 0, sizeof(DBT)); + + if ((ret = __lv_pack_filereg(freginfo, &data)) != 0) + goto err; + + /* + * We store dbregid-filereg map into dbregids.db, but we can't make + * dbregids.db the sec db of fileregs.db, because dbregid is only + * valid when a db file is open, we want to delete data with same + * key in dbregids.db, but we want to keep all filereg_info data in + * fileregs.db to track all db file lifetime and status. + * + * Consequently we will store dbregid-file_uid in dbregs.db, so that we + * can delete dbregid when the db handle is closed, and we can + * use the dbregid to get the currently open db file's uid. + */ + + BDBOP2(lvinfo->dbenv, __db_put(lvinfo->fileregs, lvinfo->ip, NULL, + (DBT *)&(freginfo->fileid), &data, 0), "__put_filereg_info"); + +err: + if (data.data != NULL) + __os_free(lvinfo->dbenv->env, data.data); + + return (ret); +} + +/* + * PUBLIC: int __del_filelife __P((const DB_LOG_VRFY_INFO *, int32_t)); + */ +int +__del_filelife(lvinfo, dbregid) + const DB_LOG_VRFY_INFO *lvinfo; + int32_t dbregid; +{ + int ret; + DBT key; + + memset(&key, 0, sizeof(DBT)); + key.data = &(dbregid); + key.size = sizeof(dbregid); + + if ((ret = __db_del(lvinfo->dbregids, lvinfo->ip, NULL, + &key, 0)) != 0) + goto err; + +err: + return (ret); +} + +/* + * PUBLIC: int __put_filelife __P((const DB_LOG_VRFY_INFO *, VRFY_FILELIFE *)); + */ +int +__put_filelife (lvinfo, pflife) + const DB_LOG_VRFY_INFO *lvinfo; + VRFY_FILELIFE *pflife; +{ + int ret; + DBT key, data; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = &(pflife->dbregid); + key.size = sizeof(pflife->dbregid); + data.data = pflife; + data.size = sizeof(VRFY_FILELIFE); + + if ((ret = __db_put(lvinfo->dbregids, lvinfo->ip, NULL, + &key, &data, 0)) != 0) + goto err; + +err: + return (ret); +} + +/* + * PUBLIC: int __get_filelife __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: int32_t, VRFY_FILELIFE **)); + */ +int +__get_filelife (lvinfo, dbregid, flifepp) + const DB_LOG_VRFY_INFO *lvinfo; + int32_t dbregid; + VRFY_FILELIFE **flifepp; +{ + int ret; + DBT key, data; + VRFY_FILELIFE *flifep; + + ret = 0; + flifep = NULL; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data = &dbregid; + key.size = sizeof(dbregid); + if ((ret = __db_get(lvinfo->dbregids, lvinfo->ip, NULL, + &key, &data, 0)) != 0) + goto err; + if ((ret = __os_malloc(lvinfo->dbenv->env, + sizeof(VRFY_FILELIFE), &flifep)) != 0) + goto err; + DB_ASSERT(lvinfo->dbenv->env, flifep != NULL); + memcpy(flifep, data.data, sizeof(VRFY_FILELIFE)); + *flifepp = flifep; +err: + return (ret); +} + +/* + * PUBLIC: int __get_filereg_by_dbregid __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: int32_t, VRFY_FILEREG_INFO **)); + */ +int +__get_filereg_by_dbregid(lvinfo, dbregid, freginfopp) + const DB_LOG_VRFY_INFO *lvinfo; + int32_t dbregid; + VRFY_FILEREG_INFO **freginfopp; +{ + int ret; + DBT key, data; + char uid[DB_FILE_ID_LEN]; + VRFY_FILELIFE *pflife; + + memset(&data, 0, sizeof(DBT)); + memset(&key, 0, sizeof(DBT)); + key.data = &dbregid; + key.size = sizeof(dbregid); + + BDBOP3(lvinfo->dbenv, __db_get(lvinfo->dbregids, lvinfo->ip, NULL, + &key, &data, 0), DB_NOTFOUND, "__get_filereg_by_dbregid"); + if (ret == DB_NOTFOUND) + goto err; + + /* Use the file-uid as key to retrieve from fileregs.db. */ + pflife = (VRFY_FILELIFE *)data.data; + memcpy((void *)uid, (void *)pflife->fileid, key.size = DB_FILE_ID_LEN); + + key.data = (void *)uid; + memset(&data, 0, sizeof(DBT)); + + BDBOP3(lvinfo->dbenv, __db_get(lvinfo->fileregs, lvinfo->ip, NULL, + &key, &data, 0), DB_NOTFOUND, "__get_filereg_by_dbregid"); + if (ret == DB_NOTFOUND) + goto err; + if ((ret = __lv_unpack_filereg(&data, freginfopp)) != 0) + goto err; + +err: + return (ret); +} + +/* + * PUBLIC: int __add_dbregid __P((DB_LOG_VRFY_INFO *, VRFY_FILEREG_INFO *, + * PUBLIC: int32_t, u_int32_t, DB_LSN, DBTYPE, db_pgno_t, int *)); + */ +int +__add_dbregid(lvh, freg, dbregid, opcode, lsn, dbtype, meta_pgno, addp) + DB_LOG_VRFY_INFO *lvh; + VRFY_FILEREG_INFO *freg; + int32_t dbregid; + u_int32_t opcode; + DB_LSN lsn; + DBTYPE dbtype; + db_pgno_t meta_pgno; + int *addp; +{ + int inarray, ret, tret; + u_int32_t i, j; + VRFY_FILELIFE flife; + + inarray = ret = tret = 0; + for (i = 0; i < freg->regcnt; i++) { + if (freg->dbregids[i] == dbregid) { + if (!IS_DBREG_CLOSE(opcode)) { + /* Opening an open dbreg id. */ + if (IS_DBREG_OPEN(opcode) && + (opcode != DBREG_CHKPNT && + opcode != DBREG_XCHKPNT)) { + tret = 2; + goto err; + } + tret = 0; + inarray = 1; + } else + /* Found the dbregid; gonna remove it. */ + tret = -1; + break; + } + } + + if (IS_DBREG_OPEN(opcode)) + tret = 1;/* dbregid not in the array, gonna add 1. */ + + /* + * Remove closed dbregid. dbregid can be recycled, not unique to a db + * file, it's dynamically allocated for each db handle. + */ + if (tret == -1) { + for (j = i; j < freg->regcnt - 1; j++) + freg->dbregids[j] = freg->dbregids[j + 1]; + freg->regcnt--; + BDBOP(__os_realloc(lvh->dbenv->env, + sizeof(int32_t) * freg->regcnt, &(freg->dbregids))); + /* Don't remove dbregid life info from dbregids db. */ + } else if (tret == 1) { + if (!inarray) { + freg->regcnt++; + BDBOP(__os_realloc(lvh->dbenv->env, + sizeof(int32_t) * freg->regcnt, &(freg->dbregids))); + freg->dbregids[freg->regcnt - 1] = dbregid; + } + flife.dbregid = dbregid; + memcpy(flife.fileid, freg->fileid.data, freg->fileid.size); + flife.lifetime = opcode; + flife.dbtype = dbtype; + flife.lsn = lsn; + flife.meta_pgno = meta_pgno; + if ((ret = __put_filelife(lvh, &flife)) != 0) + goto err; + } + +err: + *addp = tret; + return (ret); + +} + +/* + * PUBLIC: int __get_filereg_info __P((const DB_LOG_VRFY_INFO *, const DBT *, + * PUBLIC: VRFY_FILEREG_INFO **)); + */ +int +__get_filereg_info (lvinfo, fuid, freginfopp) + const DB_LOG_VRFY_INFO *lvinfo; + const DBT *fuid; + VRFY_FILEREG_INFO **freginfopp; +{ + int ret; + DBT data; + + memset(&data, 0, sizeof(DBT)); + + BDBOP3(lvinfo->dbenv, __db_get(lvinfo->fileregs, lvinfo->ip, NULL, + (DBT *)fuid, &data, 0), DB_NOTFOUND, "__get_filereg_info"); + if (ret == DB_NOTFOUND) + goto err; + if ((ret = __lv_unpack_filereg(&data, freginfopp)) != 0) + goto err; + +err: + return (ret); +} + +static int +__lv_unpack_filereg(data, freginfopp) + const DBT *data; + VRFY_FILEREG_INFO **freginfopp; +{ + char *p, *q; + u_int32_t fidsz, arrsz; + VRFY_FILEREG_INFO *buf; + int ret; + + ret = 0; + p = q = NULL; + fidsz = arrsz = 0; + buf = NULL; + + if ((ret = __os_malloc(NULL, sizeof(VRFY_FILEREG_INFO), &buf)) != 0) + goto err; + memset(buf, 0, sizeof(VRFY_FILEREG_INFO)); + + memcpy(buf, data->data, FILE_REG_INFO_FIXSIZE); + *freginfopp = (VRFY_FILEREG_INFO *)buf; + p = ((char *)(data->data)) + FILE_REG_INFO_FIXSIZE; + + if ((ret = __os_malloc(NULL, arrsz = (*freginfopp)->regcnt * + sizeof(int32_t), &((*freginfopp)->dbregids))) != 0) + goto err; + memcpy((*freginfopp)->dbregids, p, arrsz); + p += arrsz; + + memcpy(&fidsz, p, sizeof(fidsz)); + p += sizeof(fidsz); + if ((ret = __os_malloc(NULL, fidsz, &q)) != 0) + goto err; + memcpy(q, p, fidsz); + (*freginfopp)->fileid.data = q; + (*freginfopp)->fileid.size = fidsz; + p += fidsz; + + if ((ret = __os_malloc(NULL, sizeof(char) * (strlen(p) + 1), &q)) != 0) + goto err; + (void)strcpy(q, p); + + (*freginfopp)->fname = q; +err: + return (ret); +} + +/* + * PUBLIC: int __free_filereg_info __P((VRFY_FILEREG_INFO *)); + */ +int +__free_filereg_info(p) + VRFY_FILEREG_INFO *p; +{ + if (p == NULL) + return (0); + if (p ->fname != NULL) + __os_free(NULL, (void *)(p->fname)); + if (p->fileid.data != NULL) + __os_free(NULL, p->fileid.data); + if (p->dbregids != NULL) + __os_free(NULL, p->dbregids); + __os_free(NULL, p); + + return (0); +} + +/* + * PUBLIC: int __get_ckp_info __P((const DB_LOG_VRFY_INFO *, DB_LSN, + * PUBLIC: VRFY_CKP_INFO **)); + */ +int +__get_ckp_info (lvinfo, lsn, ckpinfopp) + const DB_LOG_VRFY_INFO *lvinfo; + DB_LSN lsn; + VRFY_CKP_INFO **ckpinfopp; +{ + int ret; + DBT key, data; + VRFY_CKP_INFO *ckpinfo; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = &lsn; + key.size = sizeof(DB_LSN); + BDBOP3(lvinfo->dbenv, __db_get(lvinfo->ckps, lvinfo->ip, NULL, + &key, &data, 0), DB_NOTFOUND, "__get_ckp_info"); + + if (ret == DB_NOTFOUND) + goto err; + + if ((ret = __os_malloc(lvinfo->dbenv->env, + sizeof(VRFY_CKP_INFO), &ckpinfo)) != 0) + goto err; + memcpy(ckpinfo, data.data, sizeof(VRFY_CKP_INFO)); + *ckpinfopp = ckpinfo; +err: + return (ret); + +} + +/* + * PUBLIC: int __get_last_ckp_info __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: VRFY_CKP_INFO **)); + */ +int +__get_last_ckp_info (lvinfo, ckpinfopp) + const DB_LOG_VRFY_INFO *lvinfo; + VRFY_CKP_INFO **ckpinfopp; +{ + int ret, tret; + DBT key, data; + VRFY_CKP_INFO *ckpinfo; + DBC *csr; + + csr = NULL; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + BDBOP(__db_cursor(lvinfo->ckps, lvinfo->ip, NULL, &csr, 0)); + if ((ret = __dbc_get(csr, &key, &data, DB_LAST)) != 0) + goto err; + + if ((ret = __os_malloc(lvinfo->dbenv->env, + sizeof(VRFY_CKP_INFO), &ckpinfo)) != 0) + goto err; + DB_ASSERT(lvinfo->dbenv->env, sizeof(VRFY_CKP_INFO) == data.size); + memcpy(ckpinfo, data.data, sizeof(VRFY_CKP_INFO)); + *ckpinfopp = ckpinfo; +err: + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + if (ret != 0 && ret != DB_NOTFOUND) + __db_err(lvinfo->dbenv->env, ret, "__get_last_ckp_info"); + return (ret); +} + +/* + * PUBLIC: int __put_ckp_info __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: const VRFY_CKP_INFO *)); + */ +int __put_ckp_info (lvinfo, ckpinfo) + const DB_LOG_VRFY_INFO *lvinfo; + const VRFY_CKP_INFO *ckpinfo; +{ + int ret; + DBT key, data; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = (void *)&ckpinfo->lsn; + key.size = sizeof(DB_LSN); + data.data = (void *)ckpinfo; + data.size = sizeof(VRFY_CKP_INFO); + + BDBOP2(lvinfo->dbenv, __db_put(lvinfo->ckps, lvinfo->ip, + NULL, &key, &data, 0), "__put_ckp_info"); + return (0); +} + +/* + * PUBLIC: int __get_timestamp_info __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: DB_LSN, VRFY_TIMESTAMP_INFO **)); + */ +int __get_timestamp_info (lvinfo, lsn, tsinfopp) + const DB_LOG_VRFY_INFO *lvinfo; + DB_LSN lsn; + VRFY_TIMESTAMP_INFO **tsinfopp; +{ + int ret; + DBT key, data; + VRFY_TIMESTAMP_INFO *tsinfo; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = &lsn; + key.size = sizeof(DB_LSN); + BDBOP3(lvinfo->dbenv, __db_get(lvinfo->lsntime, lvinfo->ip, NULL, + &key, &data, 0), DB_NOTFOUND, "__get_timestamp_info"); + + if (ret == DB_NOTFOUND) + goto err; + + if ((ret = __os_malloc(lvinfo->dbenv->env, + sizeof(VRFY_TIMESTAMP_INFO), &tsinfo)) != 0) + goto err; + + memcpy(tsinfo, data.data, sizeof(VRFY_TIMESTAMP_INFO)); + *tsinfopp = tsinfo; +err: + return (ret); +} + +/* + * __get_latest_timestamp_info -- + * Get latest timestamp info before lsn. + * PUBLIC: int __get_latest_timestamp_info __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: DB_LSN, VRFY_TIMESTAMP_INFO **)); + */ +int __get_latest_timestamp_info(lvinfo, lsn, tsinfopp) + const DB_LOG_VRFY_INFO *lvinfo; + DB_LSN lsn; + VRFY_TIMESTAMP_INFO **tsinfopp; +{ + int ret, tret; + DBT key, data; + VRFY_TIMESTAMP_INFO *tsinfo; + DBC *csr; + + csr = NULL; + ret = tret = 0; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data = &lsn; + key.size = sizeof(lsn); + BDBOP(__db_cursor(lvinfo->lsntime, lvinfo->ip, NULL, &csr, 0)); + + BDBOP(__dbc_get(csr, &key, &data, DB_SET)); + BDBOP(__dbc_get(csr, &key, &data, DB_PREV)); + + if ((ret = __os_malloc(lvinfo->dbenv->env, sizeof(VRFY_TIMESTAMP_INFO), + &tsinfo)) != 0) + goto err; + + memcpy(tsinfo, data.data, sizeof(VRFY_TIMESTAMP_INFO)); + *tsinfopp = tsinfo; + +err: + if (ret != 0 && ret != DB_NOTFOUND) + __db_err(lvinfo->dbenv->env, + ret, "__get_latest_timestamp_info"); + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +/* + * PUBLIC: int __put_timestamp_info __P((const DB_LOG_VRFY_INFO *, + * PUBLIC: const VRFY_TIMESTAMP_INFO *)); + */ +int __put_timestamp_info (lvinfo, tsinfo) + const DB_LOG_VRFY_INFO *lvinfo; + const VRFY_TIMESTAMP_INFO *tsinfo; +{ + int ret; + DBT key, data; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = (void *)&(tsinfo->lsn); + key.size = sizeof(DB_LSN); + data.data = (void *)tsinfo; + data.size = sizeof(VRFY_TIMESTAMP_INFO); + BDBOP2(lvinfo->dbenv, __db_put(lvinfo->lsntime, lvinfo->ip, NULL, + &key, &data, 0), "__put_timestamp_info"); + + return (0); +} + +static int +__lv_txnrgns_lsn_cmp (db, d1, d2) + DB *db; + const DBT *d1, *d2; +{ + struct __lv_txnrange r1, r2; + + DB_ASSERT(db->env, d1->size == sizeof(r1)); + DB_ASSERT(db->env, d2->size == sizeof(r2)); + memcpy(&r1, d1->data, d1->size); + memcpy(&r2, d2->data, d2->size); + + return (LOG_COMPARE(&(r1.end), &(r2.end))); +} + +/* + * __find_lsnrg_by_timerg -- + * Find the lsn closed interval [beginlsn, endlsn] so that the + * corresponding timestamp interval fully contains interval [begin, end]. + * PUBLIC: int __find_lsnrg_by_timerg __P((DB_LOG_VRFY_INFO *, + * PUBLIC: time_t, time_t, DB_LSN *, DB_LSN *)); + */ +int +__find_lsnrg_by_timerg(lvinfo, begin, end, startlsn, endlsn) + DB_LOG_VRFY_INFO *lvinfo; + time_t begin, end; + DB_LSN *startlsn, *endlsn; +{ + int ret, tret; + DBC *csr; + struct __lv_timestamp_info *t1, *t2; + DBT key, data; + + ret = tret = 0; + csr = NULL; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + BDBOP(__db_cursor(lvinfo->timelsn, lvinfo->ip, NULL, &csr, 0)); + + /* + * We want a lsn range that completely contains [begin, end], so + * try move 1 record prev when getting the startlsn. + */ + key.data = &begin; + key.size = sizeof(begin); + BDBOP(__dbc_get(csr, &key, &data, DB_SET_RANGE)); + if ((ret = __dbc_get(csr, &key, &data, DB_PREV)) != 0 && + ret != DB_NOTFOUND) + goto err; + if (ret == DB_NOTFOUND)/* begin is smaller than the smallest key. */ + startlsn->file = startlsn->offset = 0;/* beginning. */ + else { + t1 = (struct __lv_timestamp_info *)data.data; + *startlsn = t1->lsn; + } + + /* + * Move to the last key/data pair of the duplicate set to get the + * biggest lsn having end as timestamp. + */ + key.data = &end; + key.size = sizeof(end); + if ((ret = __dbc_get(csr, &key, &data, DB_SET_RANGE)) != 0 && + ret != DB_NOTFOUND) + goto err; + if (ret == DB_NOTFOUND) { + endlsn->file = endlsn->offset = (u_int32_t)-1;/* Biggest lsn. */ + ret = 0; + goto err; /* We are done. */ + } + + /* + * Go to the biggest lsn of the dup set, if the key is the last one, + * go to the last one. + */ + if ((ret = __dbc_get(csr, &key, &data, DB_NEXT_NODUP)) != 0 && + ret != DB_NOTFOUND) + goto err; + + if (ret == DB_NOTFOUND) + BDBOP(__dbc_get(csr, &key, &data, DB_LAST)); + else + BDBOP(__dbc_get(csr, &key, &data, DB_PREV)); + + t2 = (struct __lv_timestamp_info *)data.data; + *endlsn = t2->lsn; +err: + if (ret == DB_NOTFOUND) + ret = 0; + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +/* + * PUBLIC: int __add_txnrange __P((DB_LOG_VRFY_INFO *, u_int32_t, + * PUBLIC: DB_LSN, int32_t, int)); + */ +int __add_txnrange (lvinfo, txnid, lsn, when, ishead) + DB_LOG_VRFY_INFO *lvinfo; + u_int32_t txnid; + DB_LSN lsn; + int32_t when; + int ishead; /* Whether it's the 1st log of the txn. */ +{ + int ret, tret; + DBC *csr; + struct __lv_txnrange tr, *ptr; + DBT key, data; + + csr = NULL; + ret = 0; + ptr = NULL; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + memset(&tr, 0, sizeof(tr)); + + key.data = &txnid; + key.size = sizeof(txnid); + tr.txnid = txnid; + BDBOP(__db_cursor(lvinfo->txnrngs, lvinfo->ip, NULL, &csr, 0)); + /* + * Note that we will backward play the logs to gather such information. + */ + if (!ishead) { + tr.end = lsn; + tr.when_commit = when; + data.data = &tr; + data.size = sizeof(tr); + BDBOP(__dbc_put(csr, &key, &data, DB_KEYFIRST)); + } else { + /* + * Dup data sorted by lsn, and we are backward playing logs, + * so the 1st record should be the one we want. + */ + BDBOP(__dbc_get(csr, &key, &data, DB_SET)); + ptr = (struct __lv_txnrange *)data.data; + DB_ASSERT(lvinfo->dbenv->env, IS_ZERO_LSN(ptr->begin)); + ptr->begin = lsn; + BDBOP(__dbc_put(csr, &key, &data, DB_CURRENT)); + } + +err: + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +/* + * __get_aborttxn -- + * If lsn is the last log of an aborted txn T, T's txnid is + * returned via the log verify handle. + * + * PUBLIC: int __get_aborttxn __P((DB_LOG_VRFY_INFO *, DB_LSN)); + */ +int +__get_aborttxn(lvinfo, lsn) + DB_LOG_VRFY_INFO *lvinfo; + DB_LSN lsn; +{ + int ret, tret; + u_int32_t txnid; + DBC *csr; + DBT key, data; + + csr = NULL; + txnid = 0; + ret = tret = 0; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data = &lsn; + key.size = sizeof(lsn); + BDBOP(__db_cursor(lvinfo->txnaborts, lvinfo->ip, NULL, &csr, 0)); + BDBOP(__dbc_get(csr, &key, &data, DB_SET)); + memcpy(&txnid, data.data, data.size); + /* + * The lsn is the last op of an aborted txn, call __on_txnabort + * before processing next log record. + */ + lvinfo->aborted_txnid = txnid; + lvinfo->aborted_txnlsn = lsn; + +err: + /* It's OK if can't find it. */ + if (ret == DB_NOTFOUND) + ret = 0; + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +/* + * __txn_started -- + * Whether txnid is started before lsn and ended after lsn. + * + * PUBLIC: int __txn_started __P((DB_LOG_VRFY_INFO *, + * PUBLIC: DB_LSN, u_int32_t, int *)); + */ +int +__txn_started(lvinfo, lsn, txnid, res) + DB_LOG_VRFY_INFO *lvinfo; + DB_LSN lsn; + u_int32_t txnid; + int *res; +{ + int ret, tret; + DBC *csr; + DBT key, data; + struct __lv_txnrange *ptr, tr; + + ret = *res = 0; + csr = NULL; + memset(&tr, 0, sizeof(tr)); + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = &txnid; + key.size = sizeof(txnid); + + BDBOP(__db_cursor(lvinfo->txnrngs, lvinfo->ip, NULL, &csr, 0)); + BDBOP(__dbc_get(csr, &key, &data, DB_SET)); + for (;ret == 0; ret = __dbc_get(csr, &key, &data, DB_NEXT_DUP)) { + ptr = (struct __lv_txnrange *)data.data; + if (LOG_COMPARE(&lsn, &(ptr->begin)) > 0 && + LOG_COMPARE(&lsn, &(ptr->end)) <= 0) { + *res = 1; + break; + } + } +err: + if (ret == DB_NOTFOUND) + ret = 0;/* It's OK if can't find it. */ + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +/* + * PUBLIC: int __set_logvrfy_dbfuid __P((DB_LOG_VRFY_INFO *)); + */ +int +__set_logvrfy_dbfuid(lvinfo) + DB_LOG_VRFY_INFO *lvinfo; +{ + int ret; + const char *p; + DBT key, data; + size_t buflen; + + p = NULL; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + /* So far we only support verifying a specific db file. */ + p = lvinfo->lv_config->dbfile; + buflen = sizeof(char) * (strlen(p) + 1); + key.data = (char *)p; + key.size = (u_int32_t)buflen; + + BDBOP2(lvinfo->dbenv, __db_get(lvinfo->fnameuid, lvinfo->ip, NULL, + &key, &data, 0), "__set_logvrfy_dbfuid"); + + memcpy(lvinfo->target_dbid, data.data, DB_FILE_ID_LEN); + + return (ret); +} + +/* + * __add_page_to_txn -- + * Try adding a page to a txn, result brings back if really added(0/1) + * or if there is an access violation(-1). + * PUBLIC: int __add_page_to_txn __P((DB_LOG_VRFY_INFO *, + * PUBLIC: int32_t, db_pgno_t, u_int32_t, u_int32_t *, int *)); + */ +int +__add_page_to_txn (lvinfo, dbregid, pgno, txnid, otxn, result) + DB_LOG_VRFY_INFO *lvinfo; + int32_t dbregid; + db_pgno_t pgno; + u_int32_t txnid, *otxn; + int *result; +{ + int ret; + u_int8_t *buf; + DBT key, data; + size_t buflen; + u_int32_t txnid2; + VRFY_FILELIFE *pff; + + if (txnid < TXN_MINIMUM) { + *result = 0; + return (0); + } + buf = NULL; + ret = 0; + txnid2 = 0; + pff = NULL; + buflen = sizeof(u_int8_t) * DB_FILE_ID_LEN + sizeof(db_pgno_t); + BDBOP(__os_malloc(lvinfo->dbenv->env, buflen, &buf)); + memset(buf, 0, buflen); + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + /* + * We use the file uid as key because a single db file can have + * multiple dbregid at the same time, and we may neglect the fact + * that the same db file is being updated by multiple txns if we use + * dbregid as key. + */ + key.data = &dbregid; + key.size = sizeof(dbregid); + if ((ret = __db_get(lvinfo->dbregids, lvinfo->ip, NULL, + &key, &data, 0)) != 0) { + if (ret == DB_NOTFOUND) { + if (F_ISSET(lvinfo, DB_LOG_VERIFY_PARTIAL)) { + ret = 0; + goto out; + } else + F_SET(lvinfo, DB_LOG_VERIFY_INTERR); + } + goto err; + } + pff = (VRFY_FILELIFE *)data.data; + memcpy(buf, pff->fileid, DB_FILE_ID_LEN); + memcpy(buf + DB_FILE_ID_LEN, (u_int8_t *)&pgno, sizeof(pgno)); + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = buf; + key.size = (u_int32_t)buflen; + if ((ret = __db_get(lvinfo->pgtxn, lvinfo->ip, NULL, + &key, &data, 0)) != 0) { + if (ret == DB_NOTFOUND) { + data.data = &txnid; + data.size = sizeof(txnid); + BDBOP(__db_put(lvinfo->pgtxn, lvinfo->ip, NULL, &key, + &data, 0)); + *result = 1; + ret = 0;/* This is not an error. */ + } + goto err; + } + DB_ASSERT(lvinfo->dbenv->env, data.size == sizeof(txnid2)); + memcpy(&txnid2, data.data, data.size); + if (txnid == txnid2)/* The same txn already has the page. */ + *result = 0; + else {/* Txn txnid is updating pages still held by txnid2. */ + *result = -1; + *otxn = txnid2; + } +out: + /* result is set to -1 on violation, 0 if already has it, 1 if added. */ +err: + if (buf != NULL) + __os_free(lvinfo->dbenv->env, buf); + return (ret); +} + +/* + * PUBLIC: int __del_txn_pages __P((DB_LOG_VRFY_INFO *, u_int32_t)); + */ +int +__del_txn_pages(lvinfo, txnid) + DB_LOG_VRFY_INFO *lvinfo; + u_int32_t txnid; +{ + int ret; + DBT key; + + ret = 0; + memset(&key, 0, sizeof(DBT)); + key.data = &txnid; + key.size = sizeof(txnid); + + BDBOP(__db_del(lvinfo->txnpg, lvinfo->ip, NULL, &key, 0)); + +err: + return (ret); +} + +/* + * __is_ancestor_txn -- + * Tells via res if ptxnid is txnid's parent txn at the moment of lsn. + * + * PUBLIC: int __is_ancestor_txn __P((DB_LOG_VRFY_INFO *, + * PUBLIC: u_int32_t, u_int32_t, DB_LSN, int *)); + */ +int +__is_ancestor_txn (lvinfo, ptxnid, txnid, lsn, res) + DB_LOG_VRFY_INFO *lvinfo; + u_int32_t ptxnid, txnid; + DB_LSN lsn; + int *res; +{ + u_int32_t ptid; + int ret, tret; + DBC *csr; + DB *pdb; + DBT key, data; + struct __lv_txnrange tr; + + ret = 0; + ptid = txnid; + csr = NULL; + pdb = lvinfo->txnrngs; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + *res = 0; + BDBOP(__db_cursor(pdb, lvinfo->ip, NULL, &csr, 0)); + + /* See if ptxnid is an ancestor of txnid. */ + do { + key.data = &ptid; + key.size = sizeof(ptid); + BDBOP(__dbc_get(csr, &key, &data, DB_SET)); + /* A txnid maybe reused, we want the range having lsn in it. */ + for (;ret == 0; + ret = __dbc_get(csr, &key, &data, DB_NEXT_DUP)) { + DB_ASSERT(pdb->env, sizeof(tr) == data.size); + memcpy(&tr, data.data, data.size); + if (tr.ptxnid > 0 && + LOG_COMPARE(&lsn, &(tr.begin)) >= 0 && + LOG_COMPARE(&lsn, &(tr.end)) <= 0) + break; + } + + if (tr.ptxnid == ptxnid) { + *res = 1; + goto out; + } else + ptid = tr.ptxnid; + + } while (ptid != 0); +out: + +err: + if (ret == DB_NOTFOUND) + ret = 0; + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +/* + * PUBLIC: int __return_txn_pages __P((DB_LOG_VRFY_INFO *, + * PUBLIC: u_int32_t, u_int32_t)); + */ +int __return_txn_pages(lvh, ctxn, ptxn) + DB_LOG_VRFY_INFO *lvh; + u_int32_t ctxn, ptxn; +{ + int ret, tret; + DBC *csr; + DB *pdb, *sdb; + DBT key, key2, data, data2; + char buf[DB_FILE_ID_LEN + sizeof(db_pgno_t)]; + + ret = tret = 0; + csr = NULL; + sdb = lvh->txnpg; + pdb = lvh->pgtxn; + memset(&key, 0, sizeof(DBT)); + memset(&key2, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + memset(&data2, 0, sizeof(DBT)); + + BDBOP(__db_cursor(sdb, lvh->ip, NULL, &csr, 0)); + key.data = &ctxn; + key.size = sizeof(ctxn); + key2.data = &ptxn; + key2.size = sizeof(ptxn); + data2.data = buf; + data2.ulen = DB_FILE_ID_LEN + sizeof(db_pgno_t); + data2.flags = DB_DBT_USERMEM; + + for (ret = __dbc_pget(csr, &key, &data2, &data, DB_SET); ret == 0; + ret = __dbc_pget(csr, &key, &data2, &data, DB_NEXT_DUP)) + BDBOP(__db_put(pdb, lvh->ip, NULL, &data2, &key2, 0)); + if ((ret = __del_txn_pages(lvh, ctxn)) != 0 && ret != DB_NOTFOUND) + goto err; +err: + if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0) + ret = tret; + return (ret); +} + +#define ADD_ITEM(lvh, logtype) ((lvh)->logtype_names[(logtype)] = (#logtype)) +static void +__lv_setup_logtype_names(lvinfo) + DB_LOG_VRFY_INFO *lvinfo; +{ + ADD_ITEM(lvinfo, DB___bam_irep); + ADD_ITEM(lvinfo, DB___bam_split_42); + ADD_ITEM(lvinfo, DB___bam_split); + ADD_ITEM(lvinfo, DB___bam_rsplit); + ADD_ITEM(lvinfo, DB___bam_adj); + ADD_ITEM(lvinfo, DB___bam_cadjust); + ADD_ITEM(lvinfo, DB___bam_cdel); + ADD_ITEM(lvinfo, DB___bam_repl); + ADD_ITEM(lvinfo, DB___bam_root); + ADD_ITEM(lvinfo, DB___bam_curadj); + ADD_ITEM(lvinfo, DB___bam_rcuradj); + ADD_ITEM(lvinfo, DB___bam_relink_43); + ADD_ITEM(lvinfo, DB___bam_merge_44); + ADD_ITEM(lvinfo, DB___crdel_metasub); + ADD_ITEM(lvinfo, DB___crdel_inmem_create); + ADD_ITEM(lvinfo, DB___crdel_inmem_rename); + ADD_ITEM(lvinfo, DB___crdel_inmem_remove); + ADD_ITEM(lvinfo, DB___dbreg_register); + ADD_ITEM(lvinfo, DB___db_addrem); + ADD_ITEM(lvinfo, DB___db_big); + ADD_ITEM(lvinfo, DB___db_ovref); + ADD_ITEM(lvinfo, DB___db_relink_42); + ADD_ITEM(lvinfo, DB___db_debug); + ADD_ITEM(lvinfo, DB___db_noop); + ADD_ITEM(lvinfo, DB___db_pg_alloc_42); + ADD_ITEM(lvinfo, DB___db_pg_alloc); + ADD_ITEM(lvinfo, DB___db_pg_free_42); + ADD_ITEM(lvinfo, DB___db_pg_free); + ADD_ITEM(lvinfo, DB___db_cksum); + ADD_ITEM(lvinfo, DB___db_pg_freedata_42); + ADD_ITEM(lvinfo, DB___db_pg_freedata); + ADD_ITEM(lvinfo, DB___db_pg_init); + ADD_ITEM(lvinfo, DB___db_pg_sort_44); + ADD_ITEM(lvinfo, DB___db_pg_trunc); + ADD_ITEM(lvinfo, DB___db_realloc); + ADD_ITEM(lvinfo, DB___db_relink); + ADD_ITEM(lvinfo, DB___db_merge); + ADD_ITEM(lvinfo, DB___db_pgno); +#ifdef HAVE_HASH + ADD_ITEM(lvinfo, DB___ham_insdel); + ADD_ITEM(lvinfo, DB___ham_newpage); + ADD_ITEM(lvinfo, DB___ham_splitdata); + ADD_ITEM(lvinfo, DB___ham_replace); + ADD_ITEM(lvinfo, DB___ham_copypage); + ADD_ITEM(lvinfo, DB___ham_metagroup_42); + ADD_ITEM(lvinfo, DB___ham_metagroup); + ADD_ITEM(lvinfo, DB___ham_groupalloc_42); + ADD_ITEM(lvinfo, DB___ham_groupalloc); + ADD_ITEM(lvinfo, DB___ham_changeslot); + ADD_ITEM(lvinfo, DB___ham_contract); + ADD_ITEM(lvinfo, DB___ham_curadj); + ADD_ITEM(lvinfo, DB___ham_chgpg); +#endif +#ifdef HAVE_QUEUE + ADD_ITEM(lvinfo, DB___qam_incfirst); + ADD_ITEM(lvinfo, DB___qam_mvptr); + ADD_ITEM(lvinfo, DB___qam_del); + ADD_ITEM(lvinfo, DB___qam_add); + ADD_ITEM(lvinfo, DB___qam_delext); +#endif + ADD_ITEM(lvinfo, DB___txn_regop_42); + ADD_ITEM(lvinfo, DB___txn_regop); + ADD_ITEM(lvinfo, DB___txn_ckp_42); + ADD_ITEM(lvinfo, DB___txn_ckp); + ADD_ITEM(lvinfo, DB___txn_child); + ADD_ITEM(lvinfo, DB___txn_xa_regop_42); + ADD_ITEM(lvinfo, DB___txn_prepare); + ADD_ITEM(lvinfo, DB___txn_recycle); + ADD_ITEM(lvinfo, DB___fop_create_42); + ADD_ITEM(lvinfo, DB___fop_create); + ADD_ITEM(lvinfo, DB___fop_remove); + ADD_ITEM(lvinfo, DB___fop_write_42); + ADD_ITEM(lvinfo, DB___fop_write); + ADD_ITEM(lvinfo, DB___fop_rename_42); + ADD_ITEM(lvinfo, DB___fop_rename_noundo_46); + ADD_ITEM(lvinfo, DB___fop_rename); + ADD_ITEM(lvinfo, DB___fop_rename_noundo); + ADD_ITEM(lvinfo, DB___fop_file_remove); +} |