/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996, 2010 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); } /* Seocndary 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); } /* Seocndary 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); } /* Seocndary 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, int *)); */ int __add_dbregid(lvh, freg, dbregid, opcode, lsn, dbtype, addp) DB_LOG_VRFY_INFO *lvh; VRFY_FILEREG_INFO *freg; int32_t dbregid; u_int32_t opcode; DB_LSN lsn; DBTYPE dbtype; 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) { tret = 2; goto err; } tret = 0; inarray = 1; break; } else tret = -1;/* Gonna remove 1 dbregid. */ } } 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 && freg->regcnt > 0) { 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; 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; VRFY_FILEREG_INFO *fregp; 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"); if ((ret = __lv_unpack_filereg(&data, &fregp)) != 0) goto err; memcpy(lvinfo->target_dbid, fregp->fileid.data, DB_FILE_ID_LEN); if ((ret = __free_filereg_info(fregp)) != 0) goto err; err: 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); }