diff options
author | jbj <devnull@localhost> | 2002-08-14 16:31:49 +0000 |
---|---|---|
committer | jbj <devnull@localhost> | 2002-08-14 16:31:49 +0000 |
commit | d481ba55c02407124c499c7800ea556786137bc5 (patch) | |
tree | e0d3fdb7906ae3290f019999e0661cfc5b1b3f58 /db/dbreg | |
parent | 9114d6ffea4ee330874ebc8febe225ce0e891eac (diff) | |
download | librpm-tizen-d481ba55c02407124c499c7800ea556786137bc5.tar.gz librpm-tizen-d481ba55c02407124c499c7800ea556786137bc5.tar.bz2 librpm-tizen-d481ba55c02407124c499c7800ea556786137bc5.zip |
Initial revision
CVS patchset: 5630
CVS date: 2002/08/14 16:31:49
Diffstat (limited to 'db/dbreg')
-rw-r--r-- | db/dbreg/dbreg.c | 449 | ||||
-rw-r--r-- | db/dbreg/dbreg.src | 49 | ||||
-rw-r--r-- | db/dbreg/dbreg_auto.c | 358 | ||||
-rw-r--r-- | db/dbreg/dbreg_rec.c | 363 | ||||
-rw-r--r-- | db/dbreg/dbreg_util.c | 689 |
5 files changed, 1908 insertions, 0 deletions
diff --git a/db/dbreg/dbreg.c b/db/dbreg/dbreg.c new file mode 100644 index 000000000..69b2fba22 --- /dev/null +++ b/db/dbreg/dbreg.c @@ -0,0 +1,449 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996-2002 + * Sleepycat Software. All rights reserved. + */ +#include "db_config.h" + +#ifndef lint +static const char revid[] = "Id: dbreg.c,v 11.66 2002/08/11 02:14:16 margo Exp "; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "dbinc/log.h" +#include "dbinc/txn.h" + +/* + * The dbreg subsystem, as its name implies, registers database handles so + * that we can associate log messages with them without logging a filename + * or a full, unique DB ID. Instead, we assign each dbp an int32_t which is + * easy and cheap to log, and use this subsystem to map back and forth. + * + * Overview of how dbreg ids are managed: + * + * OPEN + * dbreg_setup (Creates FNAME struct.) + * dbreg_new_id (Assigns new ID to dbp and logs it. May be postponed + * until we attempt to log something else using that dbp, if the dbp + * was opened on a replication client.) + * + * CLOSE + * dbreg_close_id (Logs closure of dbp/revocation of ID.) + * dbreg_revoke_id (As name implies, revokes ID.) + * dbreg_teardown (Destroys FNAME.) + * + * RECOVERY + * dbreg_setup + * dbreg_assign_id (Assigns a particular ID we have in the log to a dbp.) + * + * sometimes: dbreg_revoke_id; dbreg_teardown + * other times: normal close path + * + * A note about locking: + * + * FNAME structures are referenced only by their corresponding dbp's + * until they have a valid id. + * + * Once they have a valid id, they must get linked into the log + * region list so they can get logged on checkpoints. + * + * An FNAME that may/does have a valid id must be accessed under + * protection of the fq_mutex, with the following exception: + * + * We don't want to have to grab the fq_mutex on every log + * record, and it should be safe not to do so when we're just + * looking at the id, because once allocated, the id should + * not change under a handle until the handle is closed. + * + * If a handle is closed during an attempt by another thread to + * log with it, well, the application doing the close deserves to + * go down in flames and a lot else is about to fail anyway. + * + * When in the course of logging we encounter an invalid id + * and go to allocate it lazily, we *do* need to check again + * after grabbing the mutex, because it's possible to race with + * another thread that has also decided that it needs to allocate + * a id lazily. + * + * See SR #5623 for further discussion of the new dbreg design. + */ + +/* + * __dbreg_setup -- + * Allocate and initialize an FNAME structure. The FNAME structures + * live in the log shared region and map one-to-one with open database handles. + * When the handle needs to be logged, the FNAME should have a valid fid + * allocated. If the handle currently isn't logged, it still has an FNAME + * entry. If we later discover that the handle needs to be logged, we can + * allocate a id for it later. (This happens when the handle is on a + * replication client that later becomes a master.) + * + * PUBLIC: int __dbreg_setup __P((DB *, const char *, u_int32_t)); + */ +int +__dbreg_setup(dbp, name, create_txnid) + DB *dbp; + const char *name; + u_int32_t create_txnid; +{ + DB_ENV *dbenv; + DB_LOG *dblp; + FNAME *fnp; + LOG *lp; + int ret; + size_t len; + void *namep; + + dbenv = dbp->dbenv; + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + + fnp = NULL; + namep = NULL; + + /* Allocate an FNAME and, if necessary, a buffer for the name itself. */ + R_LOCK(dbenv, &dblp->reginfo); + if ((ret = + __db_shalloc(dblp->reginfo.addr, sizeof(FNAME), 0, &fnp)) != 0) { + R_UNLOCK(dbenv, &dblp->reginfo); + return (ret); + } + memset(fnp, 0, sizeof(FNAME)); + if (name != NULL) { + len = strlen(name) + 1; + if ((ret = __db_shalloc(dblp->reginfo.addr, + len, 0, &namep)) != 0) { + R_UNLOCK(dbenv, &dblp->reginfo); + return (ret); + } + fnp->name_off = R_OFFSET(&dblp->reginfo, namep); + memcpy(namep, name, len); + } else + fnp->name_off = INVALID_ROFF; + + R_UNLOCK(dbenv, &dblp->reginfo); + + /* + * Fill in all the remaining info that we'll need later to register + * the file, if we use it for logging. + */ + fnp->id = DB_LOGFILEID_INVALID; + fnp->s_type = dbp->type; + memcpy(fnp->ufid, dbp->fileid, DB_FILE_ID_LEN); + fnp->meta_pgno = dbp->meta_pgno; + fnp->create_txnid = create_txnid; + + dbp->log_filename = fnp; + + return (0); +} + +/* + * __dbreg_teardown -- + * Destroy a DB handle's FNAME struct. + * + * PUBLIC: int __dbreg_teardown __P((DB *)); + */ +int +__dbreg_teardown(dbp) + DB *dbp; +{ + DB_ENV *dbenv; + DB_LOG *dblp; + FNAME *fnp; + + dbenv = dbp->dbenv; + dblp = dbenv->lg_handle; + fnp = dbp->log_filename; + + /* + * We may not have an FNAME if we were never opened. This is not an + * error. + */ + if (fnp == NULL) + return (0); + + DB_ASSERT(fnp->id == DB_LOGFILEID_INVALID); + + R_LOCK(dbenv, &dblp->reginfo); + if (fnp->name_off != INVALID_ROFF) + __db_shalloc_free(dblp->reginfo.addr, + R_ADDR(&dblp->reginfo, fnp->name_off)); + __db_shalloc_free(dblp->reginfo.addr, fnp); + R_UNLOCK(dbenv, &dblp->reginfo); + + dbp->log_filename = NULL; + + return (0); +} + +/* + * __dbreg_new_id -- + * Assign an unused dbreg id to this database handle. + * + * PUBLIC: int __dbreg_new_id __P((DB *, DB_TXN *)); + */ +int +__dbreg_new_id(dbp, txn) + DB *dbp; + DB_TXN *txn; +{ + DBT fid_dbt, r_name; + DB_ENV *dbenv; + DB_LOG *dblp; + DB_LSN unused; + FNAME *fnp; + LOG *lp; + int32_t id; + int ret; + + dbenv = dbp->dbenv; + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + fnp = dbp->log_filename; + + /* The fq_mutex protects the FNAME list and id management. */ + MUTEX_LOCK(dbenv, &lp->fq_mutex); + + /* + * It's possible that after deciding we needed to call this function, + * someone else allocated an ID before we grabbed the lock. Check + * to make sure there was no race and we have something useful to do. + */ + if (fnp->id != DB_LOGFILEID_INVALID) { + MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + return (0); + } + + /* Get an unused ID from the free list. */ + if ((ret = __dbreg_pop_id(dbenv, &id)) != 0) + goto err; + + /* If no ID was found, allocate a new one. */ + if (id == DB_LOGFILEID_INVALID) + id = lp->fid_max++; + + fnp->id = id; + + /* Hook the FNAME into the list of open files. */ + SH_TAILQ_INSERT_HEAD(&lp->fq, fnp, q, __fname); + + /* + * Log the registry. We should only request a new ID in situations + * where logging is reasonable. + */ + DB_ASSERT(!F_ISSET(dbp, DB_AM_RECOVER)); + + memset(&fid_dbt, 0, sizeof(fid_dbt)); + memset(&r_name, 0, sizeof(r_name)); + if (fnp->name_off != INVALID_ROFF) { + r_name.data = R_ADDR(&dblp->reginfo, fnp->name_off); + r_name.size = (u_int32_t)strlen((char *)r_name.data) + 1; + } + fid_dbt.data = dbp->fileid; + fid_dbt.size = DB_FILE_ID_LEN; + if ((ret = __dbreg_register_log(dbenv, txn, &unused, 0, LOG_OPEN, + r_name.size == 0 ? NULL : &r_name, &fid_dbt, id, fnp->s_type, + fnp->meta_pgno, fnp->create_txnid)) != 0) + goto err; + + DB_ASSERT(dbp->type == fnp->s_type); + DB_ASSERT(dbp->meta_pgno == fnp->meta_pgno); + + if ((ret = __dbreg_add_dbentry(dbenv, dblp, dbp, id)) != 0) + goto err; + +err: MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + return (ret); +} + +/* + * __dbreg_assign_id -- + * Assign a particular dbreg id to this database handle. + * + * PUBLIC: int __dbreg_assign_id __P((DB *, int32_t)); + */ +int +__dbreg_assign_id(dbp, id) + DB *dbp; + int32_t id; +{ + DB *close_dbp; + DB_ENV *dbenv; + DB_LOG *dblp; + FNAME *close_fnp, *fnp; + LOG *lp; + int ret; + + dbenv = dbp->dbenv; + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + fnp = dbp->log_filename; + + close_dbp = NULL; + close_fnp = NULL; + + /* The fq_mutex protects the FNAME list and id management. */ + MUTEX_LOCK(dbenv, &lp->fq_mutex); + + /* We should only call this on DB handles that have no ID. */ + DB_ASSERT(fnp->id == DB_LOGFILEID_INVALID); + + /* + * Make sure there isn't already a file open with this ID. There can + * be in recovery, if we're recovering across a point where an ID got + * reused. + */ + if (__dbreg_id_to_fname(dblp, id, 1, &close_fnp) == 0) { + /* + * We want to save off any dbp we have open with this id. + * We can't safely close it now, because we hold the fq_mutex, + * but we should be able to rely on it being open in this + * process, and we're running recovery, so no other thread + * should muck with it if we just put off closing it until + * we're ready to return. + * + * Once we have the dbp, revoke its id; we're about to + * reuse it. + */ + if ((ret = + __dbreg_id_to_db(dbenv, NULL, &close_dbp, id, 0)) != 0) + goto err; + + if ((ret = __dbreg_revoke_id(close_dbp, 1)) != 0) + goto err; + } + + /* + * Remove this ID from the free list, if it's there, and make sure + * we don't allocate it anew. + */ + if ((ret = __dbreg_pluck_id(dbenv, id)) != 0) + goto err; + if (id >= lp->fid_max) + lp->fid_max = id + 1; + + /* Now go ahead and assign the id to our dbp. */ + fnp->id = id; + SH_TAILQ_INSERT_HEAD(&lp->fq, fnp, q, __fname); + + if ((ret = __dbreg_add_dbentry(dbenv, dblp, dbp, id)) != 0) + goto err; + +err: MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + + /* There's nothing useful that our caller can do if this close fails. */ + if (close_dbp != NULL) + (void)close_dbp->close(close_dbp, DB_NOSYNC); + + return (ret); +} + +/* + * __dbreg_revoke_id -- + * Take a log id away from a dbp, in preparation for closing it, + * but without logging the close. + * + * PUBLIC: int __dbreg_revoke_id __P((DB *, int)); + */ +int +__dbreg_revoke_id(dbp, have_lock) + DB *dbp; + int have_lock; +{ + DB_ENV *dbenv; + DB_LOG *dblp; + FNAME *fnp; + LOG *lp; + int32_t id; + int ret; + + dbenv = dbp->dbenv; + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + fnp = dbp->log_filename; + + /* If we lack an ID, this is a null-op. */ + if (fnp == NULL || fnp->id == DB_LOGFILEID_INVALID) + return (0); + + if (!have_lock) + MUTEX_LOCK(dbenv, &lp->fq_mutex); + + id = fnp->id; + fnp->id = DB_LOGFILEID_INVALID; + + /* Remove the FNAME from the list of open files. */ + SH_TAILQ_REMOVE(&lp->fq, fnp, q, __fname); + + /* Remove this id from the dbentry table. */ + __dbreg_rem_dbentry(dblp, id); + + /* Push this id onto the free list. */ + ret = __dbreg_push_id(dbenv, id); + + if (!have_lock) + MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + return (ret); +} + +/* + * __dbreg_close_id -- + * Take a dbreg id away from a dbp that we're closing, and log + * the unregistry. + * + * PUBLIC: int __dbreg_close_id __P((DB *, DB_TXN *)); + */ +int +__dbreg_close_id(dbp, txn) + DB *dbp; + DB_TXN *txn; +{ + DBT fid_dbt, r_name, *dbtp; + DB_ENV *dbenv; + DB_LOG *dblp; + DB_LSN r_unused; + FNAME *fnp; + LOG *lp; + int ret; + + dbenv = dbp->dbenv; + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + fnp = dbp->log_filename; + + /* If we lack an ID, this is a null-op. */ + if (fnp == NULL || fnp->id == DB_LOGFILEID_INVALID) + return (0); + + MUTEX_LOCK(dbenv, &lp->fq_mutex); + + if (fnp->name_off == INVALID_ROFF) + dbtp = NULL; + else { + memset(&r_name, 0, sizeof(r_name)); + r_name.data = R_ADDR(&dblp->reginfo, fnp->name_off); + r_name.size = + (u_int32_t)strlen((char *)r_name.data) + 1; + dbtp = &r_name; + } + memset(&fid_dbt, 0, sizeof(fid_dbt)); + fid_dbt.data = fnp->ufid; + fid_dbt.size = DB_FILE_ID_LEN; + if ((ret = __dbreg_register_log(dbenv, txn, + &r_unused, 0, LOG_CLOSE, dbtp, &fid_dbt, fnp->id, + fnp->s_type, fnp->meta_pgno, TXN_INVALID)) != 0) + goto err; + + ret = __dbreg_revoke_id(dbp, 1); + +err: MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + return (ret); +} diff --git a/db/dbreg/dbreg.src b/db/dbreg/dbreg.src new file mode 100644 index 000000000..337ffcb61 --- /dev/null +++ b/db/dbreg/dbreg.src @@ -0,0 +1,49 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996-2002 + * Sleepycat Software. All rights reserved. + * + * Id: dbreg.src,v 10.22 2002/03/27 04:31:44 bostic Exp + */ + +PREFIX __dbreg +DBPRIVATE + +INCLUDE #include "db_config.h" +INCLUDE +INCLUDE #ifndef NO_SYSTEM_INCLUDES +INCLUDE #include <sys/types.h> +INCLUDE +INCLUDE #include <ctype.h> +INCLUDE #include <string.h> +INCLUDE #endif +INCLUDE +INCLUDE #include "db_int.h" +INCLUDE #include "dbinc/crypto.h" +INCLUDE #include "dbinc/db_page.h" +INCLUDE #include "dbinc/db_dispatch.h" +INCLUDE #include "dbinc/db_am.h" +INCLUDE #include "dbinc/log.h" +INCLUDE #include "dbinc/rep.h" +INCLUDE #include "dbinc/txn.h" +INCLUDE + +/* + * Used for registering name/id translations at open or close. + * opcode: register or unregister + * name: file name + * fileid: unique file id + * ftype: file type + * ftype: database type + * id: transaction id of the subtransaction that created the fs object + */ +BEGIN register 2 +ARG opcode u_int32_t lu +DBT name DBT s +DBT uid DBT s +ARG fileid int32_t ld +ARG ftype DBTYPE lx +ARG meta_pgno db_pgno_t lu +ARG id u_int32_t lx +END diff --git a/db/dbreg/dbreg_auto.c b/db/dbreg/dbreg_auto.c new file mode 100644 index 000000000..91eace3f4 --- /dev/null +++ b/db/dbreg/dbreg_auto.c @@ -0,0 +1,358 @@ +/* Do not edit: automatically built by gen_rec.awk. */ +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <ctype.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "dbinc/crypto.h" +#include "dbinc/db_page.h" +#include "dbinc/db_dispatch.h" +#include "dbinc/db_am.h" +#include "dbinc/log.h" +#include "dbinc/rep.h" +#include "dbinc/txn.h" + +/* + * PUBLIC: int __dbreg_register_log __P((DB_ENV *, DB_TXN *, + * PUBLIC: DB_LSN *, u_int32_t, u_int32_t, const DBT *, const DBT *, + * PUBLIC: int32_t, DBTYPE, db_pgno_t, u_int32_t)); + */ +int +__dbreg_register_log(dbenv, txnid, ret_lsnp, flags, + opcode, name, uid, fileid, ftype, meta_pgno, + id) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + u_int32_t opcode; + const DBT *name; + const DBT *uid; + int32_t fileid; + DBTYPE ftype; + db_pgno_t meta_pgno; + u_int32_t id; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t zero; + u_int32_t uinttmp; + u_int32_t npad, rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB___dbreg_register; + npad = 0; + + if (txnid == NULL) { + txn_num = 0; + null_lsn.file = 0; + null_lsn.offset = 0; + lsnp = &null_lsn; + } else { + if (TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid->txnid; + lsnp = &txnid->last_lsn; + } + + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(u_int32_t) + + sizeof(u_int32_t) + (name == NULL ? 0 : name->size) + + sizeof(u_int32_t) + (uid == NULL ? 0 : uid->size) + + sizeof(u_int32_t) + + sizeof(u_int32_t) + + sizeof(u_int32_t) + + sizeof(u_int32_t); + if (CRYPTO_ON(dbenv)) { + npad = + ((DB_CIPHER *)dbenv->crypto_handle)->adj_size(logrec.size); + logrec.size += npad; + } + + if ((ret = __os_malloc(dbenv, + logrec.size, &logrec.data)) != 0) + return (ret); + + if (npad > 0) + memset((u_int8_t *)logrec.data + logrec.size - npad, 0, npad); + + bp = logrec.data; + + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + + uinttmp = (u_int32_t)opcode; + memcpy(bp, &uinttmp, sizeof(uinttmp)); + bp += sizeof(uinttmp); + + if (name == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &name->size, sizeof(name->size)); + bp += sizeof(name->size); + memcpy(bp, name->data, name->size); + bp += name->size; + } + + if (uid == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &uid->size, sizeof(uid->size)); + bp += sizeof(uid->size); + memcpy(bp, uid->data, uid->size); + bp += uid->size; + } + + uinttmp = (u_int32_t)fileid; + memcpy(bp, &uinttmp, sizeof(uinttmp)); + bp += sizeof(uinttmp); + + uinttmp = (u_int32_t)ftype; + memcpy(bp, &uinttmp, sizeof(uinttmp)); + bp += sizeof(uinttmp); + + uinttmp = (u_int32_t)meta_pgno; + memcpy(bp, &uinttmp, sizeof(uinttmp)); + bp += sizeof(uinttmp); + + uinttmp = (u_int32_t)id; + memcpy(bp, &uinttmp, sizeof(uinttmp)); + bp += sizeof(uinttmp); + + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) <= logrec.size); + ret = dbenv->log_put(dbenv, + ret_lsnp, (DBT *)&logrec, flags | DB_NOCOPY); + if (txnid != NULL && ret == 0) + txnid->last_lsn = *ret_lsnp; +#ifdef LOG_DIAGNOSTIC + if (ret != 0) + (void)__dbreg_register_print(dbenv, + (DBT *)&logrec, ret_lsnp, NULL, NULL); +#endif + __os_free(dbenv, logrec.data); + return (ret); +} + +/* + * PUBLIC: int __dbreg_register_getpgnos __P((DB_ENV *, DBT *, + * PUBLIC: DB_LSN *, db_recops, void *)); + */ +int +__dbreg_register_getpgnos(dbenv, rec, lsnp, notused1, summary) + DB_ENV *dbenv; + DBT *rec; + DB_LSN *lsnp; + db_recops notused1; + void *summary; +{ + TXN_RECS *t; + int ret; + COMPQUIET(rec, NULL); + COMPQUIET(notused1, DB_TXN_ABORT); + + t = (TXN_RECS *)summary; + + if ((ret = __rep_check_alloc(dbenv, t, 1)) != 0) + return (ret); + + t->array[t->npages].flags = LSN_PAGE_NOLOCK; + t->array[t->npages].lsn = *lsnp; + t->array[t->npages].fid = DB_LOGFILEID_INVALID; + memset(&t->array[t->npages].pgdesc, 0, + sizeof(t->array[t->npages].pgdesc)); + + t->npages++; + + return (0); +} + +/* + * PUBLIC: int __dbreg_register_print __P((DB_ENV *, DBT *, DB_LSN *, + * PUBLIC: db_recops, void *)); + */ +int +__dbreg_register_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __dbreg_register_args *argp; + u_int32_t i; + int ch; + int ret; + + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __dbreg_register_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + (void)printf( + "[%lu][%lu]__dbreg_register: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + (void)printf("\topcode: %lu\n", (u_long)argp->opcode); + (void)printf("\tname: "); + for (i = 0; i < argp->name.size; i++) { + ch = ((u_int8_t *)argp->name.data)[i]; + printf(isprint(ch) || ch == 0x0a ? "%c" : "%#x ", ch); + } + (void)printf("\n"); + (void)printf("\tuid: "); + for (i = 0; i < argp->uid.size; i++) { + ch = ((u_int8_t *)argp->uid.data)[i]; + printf(isprint(ch) || ch == 0x0a ? "%c" : "%#x ", ch); + } + (void)printf("\n"); + (void)printf("\tfileid: %ld\n", (long)argp->fileid); + (void)printf("\tftype: 0x%lx\n", (u_long)argp->ftype); + (void)printf("\tmeta_pgno: %lu\n", (u_long)argp->meta_pgno); + (void)printf("\tid: 0x%lx\n", (u_long)argp->id); + (void)printf("\n"); + __os_free(dbenv, argp); + return (0); +} + +/* + * PUBLIC: int __dbreg_register_read __P((DB_ENV *, void *, + * PUBLIC: __dbreg_register_args **)); + */ +int +__dbreg_register_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __dbreg_register_args **argpp; +{ + __dbreg_register_args *argp; + u_int32_t uinttmp; + u_int8_t *bp; + int ret; + + if ((ret = __os_malloc(dbenv, + sizeof(__dbreg_register_args) + sizeof(DB_TXN), &argp)) != 0) + return (ret); + + argp->txnid = (DB_TXN *)&argp[1]; + + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + + memcpy(&uinttmp, bp, sizeof(uinttmp)); + argp->opcode = (u_int32_t)uinttmp; + bp += sizeof(uinttmp); + + memset(&argp->name, 0, sizeof(argp->name)); + memcpy(&argp->name.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->name.data = bp; + bp += argp->name.size; + + memset(&argp->uid, 0, sizeof(argp->uid)); + memcpy(&argp->uid.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->uid.data = bp; + bp += argp->uid.size; + + memcpy(&uinttmp, bp, sizeof(uinttmp)); + argp->fileid = (int32_t)uinttmp; + bp += sizeof(uinttmp); + + memcpy(&uinttmp, bp, sizeof(uinttmp)); + argp->ftype = (DBTYPE)uinttmp; + bp += sizeof(uinttmp); + + memcpy(&uinttmp, bp, sizeof(uinttmp)); + argp->meta_pgno = (db_pgno_t)uinttmp; + bp += sizeof(uinttmp); + + memcpy(&uinttmp, bp, sizeof(uinttmp)); + argp->id = (u_int32_t)uinttmp; + bp += sizeof(uinttmp); + + *argpp = argp; + return (0); +} + +/* + * PUBLIC: int __dbreg_init_print __P((DB_ENV *, int (***)(DB_ENV *, + * PUBLIC: DBT *, DB_LSN *, db_recops, void *), size_t *)); + */ +int +__dbreg_init_print(dbenv, dtabp, dtabsizep) + DB_ENV *dbenv; + int (***dtabp)__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + size_t *dtabsizep; +{ + int ret; + + if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep, + __dbreg_register_print, DB___dbreg_register)) != 0) + return (ret); + return (0); +} + +/* + * PUBLIC: int __dbreg_init_getpgnos __P((DB_ENV *, + * PUBLIC: int (***)(DB_ENV *, DBT *, DB_LSN *, db_recops, void *), + * PUBLIC: size_t *)); + */ +int +__dbreg_init_getpgnos(dbenv, dtabp, dtabsizep) + DB_ENV *dbenv; + int (***dtabp)__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + size_t *dtabsizep; +{ + int ret; + + if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep, + __dbreg_register_getpgnos, DB___dbreg_register)) != 0) + return (ret); + return (0); +} + +/* + * PUBLIC: int __dbreg_init_recover __P((DB_ENV *, int (***)(DB_ENV *, + * PUBLIC: DBT *, DB_LSN *, db_recops, void *), size_t *)); + */ +int +__dbreg_init_recover(dbenv, dtabp, dtabsizep) + DB_ENV *dbenv; + int (***dtabp)__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + size_t *dtabsizep; +{ + int ret; + + if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep, + __dbreg_register_recover, DB___dbreg_register)) != 0) + return (ret); + return (0); +} diff --git a/db/dbreg/dbreg_rec.c b/db/dbreg/dbreg_rec.c new file mode 100644 index 000000000..1deefa88c --- /dev/null +++ b/db/dbreg/dbreg_rec.c @@ -0,0 +1,363 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996-2002 + * Sleepycat Software. All rights reserved. + */ +/* + * Copyright (c) 1995, 1996 + * The President and Fellows of Harvard University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "Id: dbreg_rec.c,v 11.107 2002/08/08 15:44:46 bostic Exp "; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "dbinc/db_page.h" +#include "dbinc/db_am.h" +#include "dbinc/log.h" +#include "dbinc/txn.h" + +static int __dbreg_open_file __P((DB_ENV *, + DB_TXN *, __dbreg_register_args *, void *)); + +/* + * PUBLIC: int __dbreg_register_recover + * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__dbreg_register_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + DB_ENTRY *dbe; + DB_LOG *dblp; + DB *dbp; + __dbreg_register_args *argp; + int do_close, do_open, do_rem, ret, t_ret; + + dblp = dbenv->lg_handle; + dbp = NULL; + +#ifdef DEBUG_RECOVER + REC_PRINT(__dbreg_register_print); +#endif + do_open = do_close = 0; + if ((ret = __dbreg_register_read(dbenv, dbtp->data, &argp)) != 0) + goto out; + + switch (argp->opcode) { + case LOG_OPEN: + if ((DB_REDO(op) || + op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES)) + do_open = 1; + else + do_close = 1; + break; + + case LOG_CLOSE: + if (DB_UNDO(op)) + do_open = 1; + else + do_close = 1; + break; + case LOG_RCLOSE: + /* + * LOG_RCLOSE was generated by recover because a file + * was left open. The POPENFILES pass, which is run + * to open files to abort prepared transactions, + * may not include the open for this file so we + * open it here. Note that a normal CLOSE is + * not legal before the prepared transaction is + * committed or aborted. + */ + if (DB_UNDO(op) || op == DB_TXN_POPENFILES) + do_open = 1; + else + do_close = 1; + break; + + case LOG_CHECKPOINT: + if (DB_UNDO(op) || + op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES) + do_open = 1; + break; + } + + if (do_open) { + /* + * We must open the db even if the meta page is not + * yet written as we may be creating subdatabase. + */ + if (op == DB_TXN_OPENFILES && argp->opcode != LOG_CHECKPOINT) + F_SET(dblp, DBLOG_FORCE_OPEN); + + /* + * During an abort or an open pass to recover prepared txns, + * we need to make sure that we use the same locker id on the + * open. We pass the txnid along to ensure this. + */ + ret = __dbreg_open_file(dbenv, + op == DB_TXN_ABORT || op == DB_TXN_POPENFILES ? + argp->txnid : NULL, argp, info); + if (ret == ENOENT || ret == EINVAL) { + /* + * If this is an OPEN while rolling forward, it's + * possible that the file was recreated since last + * time we got here. In that case, we've got deleted + * set and probably shouldn't, so we need to check + * for that case and possibly retry. + */ + if (op == DB_TXN_FORWARD_ROLL && + argp->txnid != 0 && + dblp->dbentry[argp->fileid].deleted) { + dblp->dbentry[argp->fileid].deleted = 0; + ret = + __dbreg_open_file(dbenv, NULL, argp, info); + } + ret = 0; + } + F_CLR(dblp, DBLOG_FORCE_OPEN); + } + + if (do_close) { + /* + * If we are undoing an open, or redoing a close, + * then we need to close the file. + * + * If the file is deleted, then we can just ignore this close. + * Otherwise, we should usually have a valid dbp we should + * close or whose reference count should be decremented. + * However, if we shut down without closing a file, we may, in + * fact, not have the file open, and that's OK. + */ + do_rem = 0; + MUTEX_THREAD_LOCK(dbenv, dblp->mutexp); + if (argp->fileid < dblp->dbentry_cnt) { + /* + * Typically, closes should match an open which means + * that if this is a close, there should be a valid + * entry in the dbentry table when we get here, + * however there is an exception. If this is an + * OPENFILES pass, then we may have started from + * a log file other than the first, and the + * corresponding open appears in an earlier file. + * We can ignore that case, but all others are errors. + */ + dbe = &dblp->dbentry[argp->fileid]; + if (dbe->dbp == NULL && !dbe->deleted) { + /* No valid entry here. */ + if ((argp->opcode != LOG_CLOSE && + argp->opcode != LOG_RCLOSE) || + (op != DB_TXN_OPENFILES && + op !=DB_TXN_POPENFILES)) { + __db_err(dbenv, + "Improper file close at %lu/%lu", + (u_long)lsnp->file, + (u_long)lsnp->offset); + ret = EINVAL; + } + MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + goto done; + } + + /* We have either an open entry or a deleted entry. */ + if ((dbp = dbe->dbp) != NULL) { + MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + (void)__dbreg_revoke_id(dbp, 0); + + /* + * If we're a replication client, it's + * possible to get here with a dbp that + * the user opened, but which we later + * assigned a fileid to. Be sure that + * we only close dbps that we opened in + * the recovery code; they should have + * DB_AM_RECOVER set. + * + * The only exception is if we're aborting + * in a normal environment; then we might + * get here with a non-AM_RECOVER database. + */ + if (F_ISSET(dbp, DB_AM_RECOVER) || + op == DB_TXN_ABORT) + do_rem = 1; + } else if (dbe->deleted) { + MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + __dbreg_rem_dbentry(dblp, argp->fileid); + } + } else + MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + if (do_rem) { + /* + * If we are undoing a create we'd better discard + * any buffers from the memory pool. + */ + if (dbp != NULL && dbp->mpf != NULL && argp->id != 0) { + if ((ret = dbp->mpf->close(dbp->mpf, + DB_MPOOL_DISCARD)) != 0) + goto out; + dbp->mpf = NULL; + } + + /* + * During recovery, all files are closed. On an abort, + * we only close the file if we opened it during the + * abort (DB_AM_RECOVER set), otherwise we simply do + * a __db_refresh. For the close case, if remove or + * rename has closed the file, don't request a sync, + * because the NULL mpf would be a problem. + */ + if (dbp != NULL) { + if (op == DB_TXN_ABORT && + !F_ISSET(dbp, DB_AM_RECOVER)) + t_ret = + __db_refresh(dbp, NULL, DB_NOSYNC); + else + t_ret = dbp->close(dbp, DB_NOSYNC); + if (t_ret != 0 && ret == 0) + ret = t_ret; + } + } + } +done: if (ret == 0) + *lsnp = argp->prev_lsn; +out: if (argp != NULL) + __os_free(dbenv, argp); + return (ret); +} + +/* + * __dbreg_open_file -- + * Called during log_register recovery. Make sure that we have an + * entry in the dbentry table for this ndx. Returns 0 on success, + * non-zero on error. + */ +static int +__dbreg_open_file(dbenv, txn, argp, info) + DB_ENV *dbenv; + DB_TXN *txn; + __dbreg_register_args *argp; + void *info; +{ + DB_ENTRY *dbe; + DB_LOG *lp; + DB *dbp; + int ret; + u_int32_t id; + + lp = (DB_LOG *)dbenv->lg_handle; + /* + * We never re-open temporary files. Temp files are only + * useful during aborts in which case the dbp was entered + * when the file was registered. During recovery, we treat + * temp files as properly deleted files, allowing the open to + * fail and not reporting any errors when recovery fails to + * get a valid dbp from __dbreg_id_to_db. + */ + if (argp->name.size == 0) { + (void)__dbreg_add_dbentry(dbenv, lp, NULL, argp->fileid); + return (ENOENT); + } + + /* + * When we're opening, we have to check that the name we are opening + * is what we expect. If it's not, then we close the old file and + * open the new one. + */ + MUTEX_THREAD_LOCK(dbenv, lp->mutexp); + if (argp->fileid < lp->dbentry_cnt) + dbe = &lp->dbentry[argp->fileid]; + else + dbe = NULL; + + if (dbe != NULL) { + if (dbe->deleted) { + MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp); + return (ENOENT); + } + if ((dbp = dbe->dbp) != NULL) { + if (dbp->meta_pgno != argp->meta_pgno || + memcmp(dbp->fileid, + argp->uid.data, DB_FILE_ID_LEN) != 0) { + MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp); + (void)__dbreg_revoke_id(dbp, 0); + if (F_ISSET(dbp, DB_AM_RECOVER)) + dbp->close(dbp, DB_NOSYNC); + goto reopen; + } + + /* + * We should only get here if we already have the + * dbp from an openfiles pass, in which case, what's + * here had better be the same dbp. + */ + DB_ASSERT(dbe->dbp == dbp); + MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp); + + /* + * This is a successful open. We need to record that + * in the txnlist so that we know how to handle the + * subtransaction that created the file system object. + */ + if (argp->id != TXN_INVALID && + (ret = __db_txnlist_update(dbenv, info, + argp->id, TXN_EXPECTED, NULL)) == TXN_NOTFOUND) + ret = __db_txnlist_add(dbenv, + info, argp->id, TXN_EXPECTED, NULL); + return (0); + } + } + + MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp); + + /* + * We are about to pass a recovery txn pointer into the main library. + * We need to make sure that any accessed fields are set appropriately. + */ +reopen: if (txn != NULL) { + id = txn->txnid; + memset(txn, 0, sizeof(DB_TXN)); + txn->txnid = id; + txn->mgrp = dbenv->tx_handle; + } + + return (__dbreg_do_open(dbenv, txn, lp, argp->uid.data, argp->name.data, + argp->ftype, argp->fileid, argp->meta_pgno, info, argp->id)); +} diff --git a/db/dbreg/dbreg_util.c b/db/dbreg/dbreg_util.c new file mode 100644 index 000000000..f625062c7 --- /dev/null +++ b/db/dbreg/dbreg_util.c @@ -0,0 +1,689 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2002 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "Id: dbreg_util.c,v 11.13 2002/08/06 06:11:23 bostic Exp "; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "dbinc/db_page.h" +#include "dbinc/db_am.h" +#include "dbinc/log.h" +#include "dbinc/txn.h" + +static int __dbreg_check_master __P((DB_ENV *, u_int8_t *, char *)); + +/* + * __dbreg_add_dbentry -- + * Adds a DB entry to the dbreg DB entry table. + * + * PUBLIC: int __dbreg_add_dbentry __P((DB_ENV *, DB_LOG *, DB *, int32_t)); + */ +int +__dbreg_add_dbentry(dbenv, dblp, dbp, ndx) + DB_ENV *dbenv; + DB_LOG *dblp; + DB *dbp; + int32_t ndx; +{ + int32_t i; + int ret; + + ret = 0; + + MUTEX_THREAD_LOCK(dbenv, dblp->mutexp); + + /* + * Check if we need to grow the table. Note, ndx is 0-based (the + * index into the DB entry table) an dbentry_cnt is 1-based, the + * number of available slots. + */ + if (dblp->dbentry_cnt <= ndx) { + if ((ret = __os_realloc(dbenv, + (ndx + DB_GROW_SIZE) * sizeof(DB_ENTRY), + &dblp->dbentry)) != 0) + goto err; + + /* Initialize the new entries. */ + for (i = dblp->dbentry_cnt; i < ndx + DB_GROW_SIZE; i++) { + dblp->dbentry[i].dbp = NULL; + dblp->dbentry[i].deleted = 0; + } + dblp->dbentry_cnt = i; + } + + DB_ASSERT(dblp->dbentry[ndx].dbp == NULL); + dblp->dbentry[ndx].deleted = dbp == NULL; + dblp->dbentry[ndx].dbp = dbp; + +err: MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + return (ret); +} + +/* + * __dbreg_rem_dbentry + * Remove an entry from the DB entry table. Find the appropriate DB and + * unlink it from the linked list off the table. If the DB is NULL, treat + * this as a simple refcount decrement. + * + * PUBLIC: void __dbreg_rem_dbentry __P((DB_LOG *, int32_t)); + */ +void +__dbreg_rem_dbentry(dblp, ndx) + DB_LOG *dblp; + int32_t ndx; +{ + MUTEX_THREAD_LOCK(dblp->dbenv, dblp->mutexp); + dblp->dbentry[ndx].dbp = NULL; + dblp->dbentry[ndx].deleted = 0; + MUTEX_THREAD_UNLOCK(dblp->dbenv, dblp->mutexp); +} + +/* + * __dbreg_open_files -- + * Put a LOG_CHECKPOINT log record for each open database. + * + * PUBLIC: int __dbreg_open_files __P((DB_ENV *)); + */ +int +__dbreg_open_files(dbenv) + DB_ENV *dbenv; +{ + DB_LOG *dblp; + DB_LSN r_unused; + DBT *dbtp, fid_dbt, t; + FNAME *fnp; + LOG *lp; + int ret; + + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + + ret = 0; + + MUTEX_LOCK(dbenv, &lp->fq_mutex); + + for (fnp = SH_TAILQ_FIRST(&lp->fq, __fname); + fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { + if (fnp->name_off == INVALID_ROFF) + dbtp = NULL; + else { + memset(&t, 0, sizeof(t)); + t.data = R_ADDR(&dblp->reginfo, fnp->name_off); + t.size = (u_int32_t)strlen(t.data) + 1; + dbtp = &t; + } + memset(&fid_dbt, 0, sizeof(fid_dbt)); + fid_dbt.data = fnp->ufid; + fid_dbt.size = DB_FILE_ID_LEN; + /* + * Output LOG_CHECKPOINT records which will be + * processed during the OPENFILES pass of recovery. + * At the end of recovery we want to output the + * files that were open so that a future recovery + * run will have the correct files open during + * a backward pass. For this we output LOG_RCLOSE + * records so that the files will be closed on + * the forward pass. + */ + if ((ret = __dbreg_register_log(dbenv, + NULL, &r_unused, 0, + F_ISSET(dblp, DBLOG_RECOVER) ? LOG_RCLOSE : LOG_CHECKPOINT, + dbtp, &fid_dbt, fnp->id, fnp->s_type, fnp->meta_pgno, + TXN_INVALID)) != 0) + break; + } + + MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + + return (ret); +} + +/* + * __dbreg_close_files -- + * Close files that were opened by the recovery daemon. We sync the + * file, unless its mpf pointer has been NULLed by a db_remove or + * db_rename. We may not have flushed the log_register record that + * closes the file. + * + * PUBLIC: int __dbreg_close_files __P((DB_ENV *)); + */ +int +__dbreg_close_files(dbenv) + DB_ENV *dbenv; +{ + DB_LOG *dblp; + DB *dbp; + int ret, t_ret; + int32_t i; + + /* If we haven't initialized logging, we have nothing to do. */ + if (!LOGGING_ON(dbenv)) + return (0); + + dblp = dbenv->lg_handle; + ret = 0; + MUTEX_THREAD_LOCK(dbenv, dblp->mutexp); + for (i = 0; i < dblp->dbentry_cnt; i++) { + /* We only want to close dbps that recovery opened. */ + if ((dbp = dblp->dbentry[i].dbp) != NULL && + F_ISSET(dbp, DB_AM_RECOVER)) { + /* + * It's unsafe to call DB->close while holding the + * thread lock, because we'll call __dbreg_rem_dbentry + * and grab it again. + * + * Just drop it. Since dbreg ids go monotonically + * upward, concurrent opens should be safe, and the + * user should have no business closing files while + * we're in this loop anyway--we're in the process of + * making all outstanding dbps invalid. + */ + MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + if ((t_ret = dbp->close(dbp, + dbp->mpf == NULL ? DB_NOSYNC : 0)) != 0 && ret == 0) + ret = t_ret; + MUTEX_THREAD_LOCK(dbenv, dblp->mutexp); + } + dblp->dbentry[i].deleted = 0; + dblp->dbentry[i].dbp = NULL; + } + MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + return (ret); +} + +/* + * __dbreg_id_to_db -- + * Return the DB corresponding to the specified dbreg id. + * + * PUBLIC: int __dbreg_id_to_db __P((DB_ENV *, DB_TXN *, DB **, int32_t, int)); + */ +int +__dbreg_id_to_db(dbenv, txn, dbpp, ndx, inc) + DB_ENV *dbenv; + DB_TXN *txn; + DB **dbpp; + int32_t ndx; + int inc; +{ + DB_LOG *dblp; + FNAME *fname; + int ret; + char *name; + + ret = 0; + dblp = dbenv->lg_handle; + COMPQUIET(inc, 0); + + MUTEX_THREAD_LOCK(dbenv, dblp->mutexp); + + /* + * Under XA, a process different than the one issuing DB operations + * may abort a transaction. In this case, the "recovery" routines + * are run by a process that does not necessarily have the file open, + * so we we must open the file explicitly. + */ + if (ndx >= dblp->dbentry_cnt || + (!dblp->dbentry[ndx].deleted && dblp->dbentry[ndx].dbp == NULL)) { + if (F_ISSET(dblp, DBLOG_RECOVER)) { + ret = ENOENT; + goto err; + } + + /* + * __dbreg_id_to_fname acquires the region's fq_mutex, + * which we can't safely acquire while we hold the thread lock. + * We no longer need it anyway--the dbentry table didn't + * have what we needed. + */ + MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + + if (__dbreg_id_to_fname(dblp, ndx, 0, &fname) != 0) + /* + * With transactional opens, we may actually have + * closed this file in the transaction in which + * case this will fail too. Then it's up to the + * caller to reopen the file. + */ + return (ENOENT); + + /* + * Note that we're relying on fname not to change, even + * though we released the mutex that protects it (fq_mutex) + * inside __dbreg_id_to_fname. This should be a safe + * assumption, because the other process that has the file + * open shouldn't be closing it while we're trying to abort. + */ + name = R_ADDR(&dblp->reginfo, fname->name_off); + + /* + * At this point, we are not holding the thread lock, so exit + * directly instead of going through the exit code at the + * bottom. If the __dbreg_do_open succeeded, then we don't need + * to do any of the remaining error checking at the end of this + * routine. + * XXX I am sending a NULL txnlist and 0 txnid which may be + * completely broken ;( + */ + if ((ret = __dbreg_do_open(dbenv, txn, dblp, + fname->ufid, name, fname->s_type, + ndx, fname->meta_pgno, NULL, 0)) != 0) + return (ret); + + *dbpp = dblp->dbentry[ndx].dbp; + return (0); + } + + /* + * Return DB_DELETED if the file has been deleted (it's not an error). + */ + if (dblp->dbentry[ndx].deleted) { + ret = DB_DELETED; + goto err; + } + + /* It's an error if we don't have a corresponding writeable DB. */ + if ((*dbpp = dblp->dbentry[ndx].dbp) == NULL) + ret = ENOENT; + +err: MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp); + return (ret); +} + +/* + * __dbreg_id_to_fname -- + * Traverse the shared-memory region looking for the entry that + * matches the passed dbreg id. Returns 0 on success; -1 on error. + * + * PUBLIC: int __dbreg_id_to_fname __P((DB_LOG *, int32_t, int, FNAME **)); + */ +int +__dbreg_id_to_fname(dblp, lid, have_lock, fnamep) + DB_LOG *dblp; + int32_t lid; + int have_lock; + FNAME **fnamep; +{ + DB_ENV *dbenv; + FNAME *fnp; + LOG *lp; + int ret; + + dbenv = dblp->dbenv; + lp = dblp->reginfo.primary; + + ret = -1; + + if (!have_lock) + MUTEX_LOCK(dbenv, &lp->fq_mutex); + for (fnp = SH_TAILQ_FIRST(&lp->fq, __fname); + fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { + if (fnp->id == lid) { + *fnamep = fnp; + ret = 0; + break; + } + } + if (!have_lock) + MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + + return (ret); +} +/* + * __dbreg_fid_to_fname -- + * Traverse the shared-memory region looking for the entry that + * matches the passed file unique id. Returns 0 on success; -1 on error. + * + * PUBLIC: int __dbreg_fid_to_fname __P((DB_LOG *, u_int8_t *, int, FNAME **)); + */ +int +__dbreg_fid_to_fname(dblp, fid, have_lock, fnamep) + DB_LOG *dblp; + u_int8_t *fid; + int have_lock; + FNAME **fnamep; +{ + DB_ENV *dbenv; + FNAME *fnp; + LOG *lp; + int ret; + + dbenv = dblp->dbenv; + lp = dblp->reginfo.primary; + + ret = -1; + + if (!have_lock) + MUTEX_LOCK(dbenv, &lp->fq_mutex); + for (fnp = SH_TAILQ_FIRST(&lp->fq, __fname); + fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { + if (memcmp(fnp->ufid, fid, DB_FILE_ID_LEN) == 0) { + *fnamep = fnp; + ret = 0; + break; + } + } + if (!have_lock) + MUTEX_UNLOCK(dbenv, &lp->fq_mutex); + + return (ret); +} + +/* + * __dbreg_get_name + * + * Interface to get name of registered files. This is mainly diagnostic + * and the name passed could be transient unless there is something + * ensuring that the file cannot be closed. + * + * PUBLIC: int __dbreg_get_name __P((DB_ENV *, u_int8_t *, char **)); + */ +int +__dbreg_get_name(dbenv, fid, namep) + DB_ENV *dbenv; + u_int8_t *fid; + char **namep; +{ + DB_LOG *dblp; + FNAME *fname; + + dblp = dbenv->lg_handle; + + if (dblp != NULL && __dbreg_fid_to_fname(dblp, fid, 0, &fname) == 0) { + *namep = R_ADDR(&dblp->reginfo, fname->name_off); + return (0); + } + + return (-1); +} + +/* + * __dbreg_do_open -- + * Open files referenced in the log. This is the part of the open that + * is not protected by the thread mutex. + * PUBLIC: int __dbreg_do_open __P((DB_ENV *, DB_TXN *, DB_LOG *, u_int8_t *, + * PUBLIC: char *, DBTYPE, int32_t, db_pgno_t, void *, u_int32_t)); + */ +int +__dbreg_do_open(dbenv, + txn, lp, uid, name, ftype, ndx, meta_pgno, info, id) + DB_ENV *dbenv; + DB_TXN *txn; + DB_LOG *lp; + u_int8_t *uid; + char *name; + DBTYPE ftype; + int32_t ndx; + db_pgno_t meta_pgno; + void *info; + u_int32_t id; +{ + DB *dbp; + int ret; + u_int32_t cstat; + + if ((ret = db_create(&dbp, lp->dbenv, 0)) != 0) + return (ret); + + /* + * We can open files under a number of different scenarios. + * First, we can open a file during a normal txn_abort, if that file + * was opened and closed during the transaction (as is the master + * database of a sub-database). + * Second, we might be aborting a transaction in XA and not have + * it open in the process that is actually doing the abort. + * Third, we might be in recovery. + * In case 3, there is no locking, so there is no issue. + * In cases 1 and 2, we are guaranteed to already hold any locks + * that we need, since we're still in the same transaction, so by + * setting DB_AM_RECOVER, we guarantee that we don't log and that + * we don't try to acquire locks on behalf of a different locker id. + */ + F_SET(dbp, DB_AM_RECOVER); + if (meta_pgno != PGNO_BASE_MD) { + memcpy(dbp->fileid, uid, DB_FILE_ID_LEN); + dbp->meta_pgno = meta_pgno; + } + dbp->type = ftype; + if ((ret = __db_dbopen(dbp, txn, name, NULL, + DB_ODDFILESIZE, __db_omode("rw----"), meta_pgno)) == 0) { + /* + * Verify that we are opening the same file that we were + * referring to when we wrote this log record. + */ + if ((meta_pgno != PGNO_BASE_MD && + __dbreg_check_master(dbenv, uid, name) != 0) || + memcmp(uid, dbp->fileid, DB_FILE_ID_LEN) != 0) + cstat = TXN_IGNORE; + else + cstat = TXN_EXPECTED; + + /* Assign the specific dbreg id to this dbp. */ + if ((ret = __dbreg_assign_id(dbp, ndx)) != 0) + goto err; + + /* + * If we successfully opened this file, then we need to + * convey that information to the txnlist so that we + * know how to handle the subtransaction that created + * the file system object. + */ + if (id != TXN_INVALID) { + if ((ret = __db_txnlist_update(dbenv, + info, id, cstat, NULL)) == TXN_NOTFOUND) + ret = __db_txnlist_add(dbenv, + info, id, cstat, NULL); + else if (ret > 0) + ret = 0; + } +err: if (cstat == TXN_IGNORE) + goto not_right; + return (ret); + } else { + /* Record that the open failed in the txnlist. */ + if (id != TXN_INVALID && (ret = __db_txnlist_update(dbenv, + info, id, TXN_UNEXPECTED, NULL)) == TXN_NOTFOUND) + ret = __db_txnlist_add(dbenv, + info, id, TXN_UNEXPECTED, NULL); + } +not_right: + (void)dbp->close(dbp, 0); + /* Add this file as deleted. */ + (void)__dbreg_add_dbentry(dbenv, lp, NULL, ndx); + return (ENOENT); +} + +static int +__dbreg_check_master(dbenv, uid, name) + DB_ENV *dbenv; + u_int8_t *uid; + char *name; +{ + DB *dbp; + int ret; + + ret = 0; + if ((ret = db_create(&dbp, dbenv, 0)) != 0) + return (ret); + dbp->type = DB_BTREE; + F_SET(dbp, DB_AM_RECOVER); + ret = __db_dbopen(dbp, + NULL, name, NULL, 0, __db_omode("rw----"), PGNO_BASE_MD); + + if (ret == 0 && memcmp(uid, dbp->fileid, DB_FILE_ID_LEN) != 0) + ret = EINVAL; + + (void)dbp->close(dbp, 0); + return (ret); +} + +/* + * __dbreg_lazy_id -- + * When a replication client gets upgraded to being a replication master, + * it may have database handles open that have not been assigned an ID, but + * which have become legal to use for logging. + * + * This function lazily allocates a new ID for such a function, in a + * new transaction created for the purpose. We need to do this in a new + * transaction because we definitely wish to commit the dbreg_register, but + * at this point we have no way of knowing whether the log record that incited + * us to call this will be part of a committed transaction. + * + * PUBLIC: int __dbreg_lazy_id __P((DB *)); + */ +int +__dbreg_lazy_id(dbp) + DB *dbp; +{ + DB_ENV *dbenv; + DB_TXN *txn; + int ret; + + dbenv = dbp->dbenv; + + DB_ASSERT(F_ISSET(dbenv, DB_ENV_REP_MASTER)); + + if ((ret = dbenv->txn_begin(dbenv, NULL, &txn, 0)) != 0) + return (ret); + + if ((ret = __dbreg_new_id(dbp, txn)) != 0) { + (void)txn->abort(txn); + return (ret); + } + + return (txn->commit(txn, DB_TXN_NOSYNC)); +} + +/* + * __dbreg_push_id and __dbreg_pop_id -- + * Dbreg ids from closed files are kept on a stack in shared memory + * for recycling. (We want to reuse them as much as possible because each + * process keeps open files in an array by ID.) Push them to the stack and + * pop them from it, managing memory as appropriate. + * + * The stack is protected by the fq_mutex, and in both functions we assume + * that this is already locked. + * + * PUBLIC: int __dbreg_push_id __P((DB_ENV *, int32_t)); + * PUBLIC: int __dbreg_pop_id __P((DB_ENV *, int32_t *)); + */ +int +__dbreg_push_id(dbenv, id) + DB_ENV *dbenv; + int32_t id; +{ + DB_LOG *dblp; + LOG *lp; + int32_t *stack, *newstack; + int ret; + + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + + if (lp->free_fid_stack != INVALID_ROFF) + stack = R_ADDR(&dblp->reginfo, lp->free_fid_stack); + else + stack = NULL; + + /* Check if we have room on the stack. */ + if (lp->free_fids_alloced <= lp->free_fids + 1) { + R_LOCK(dbenv, &dblp->reginfo); + if ((ret = __db_shalloc(dblp->reginfo.addr, + (lp->free_fids_alloced + 20) * sizeof(u_int32_t), 0, + &newstack)) != 0) { + R_UNLOCK(dbenv, &dblp->reginfo); + return (ret); + } + + memcpy(newstack, stack, + lp->free_fids_alloced * sizeof(u_int32_t)); + lp->free_fid_stack = R_OFFSET(&dblp->reginfo, newstack); + lp->free_fids_alloced += 20; + + if (stack != NULL) + __db_shalloc_free(dblp->reginfo.addr, stack); + + stack = newstack; + R_UNLOCK(dbenv, &dblp->reginfo); + } + + DB_ASSERT(stack != NULL); + stack[lp->free_fids++] = id; + return (0); +} + +int +__dbreg_pop_id(dbenv, id) + DB_ENV *dbenv; + int32_t *id; +{ + DB_LOG *dblp; + LOG *lp; + int32_t *stack; + + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + + /* Do we have anything to pop? */ + if (lp->free_fid_stack != INVALID_ROFF && lp->free_fids > 0) { + stack = R_ADDR(&dblp->reginfo, lp->free_fid_stack); + *id = stack[--lp->free_fids]; + } else + *id = DB_LOGFILEID_INVALID; + + return (0); +} + +/* + * __dbreg_pluck_id -- + * Remove a particular dbreg id from the stack of free ids. This is + * used when we open a file, as in recovery, with a specific ID that might + * be on the stack. + * + * Returns success whether or not the particular id was found, and like + * push and pop, assumes that the fq_mutex is locked. + * + * PUBLIC: int __dbreg_pluck_id __P((DB_ENV *, int32_t)); + */ +int +__dbreg_pluck_id(dbenv, id) + DB_ENV *dbenv; + int32_t id; +{ + DB_LOG *dblp; + LOG *lp; + int32_t *stack; + int i; + + dblp = dbenv->lg_handle; + lp = dblp->reginfo.primary; + + /* Do we have anything to look at? */ + if (lp->free_fid_stack != INVALID_ROFF) { + stack = R_ADDR(&dblp->reginfo, lp->free_fid_stack); + for (i = 0; i < lp->free_fids; i++) + if (id == stack[i]) { + /* + * Found it. Overwrite it with the top + * id (which may harmlessly be itself), + * and shorten the stack by one. + */ + stack[i] = stack[lp->free_fids - 1]; + lp->free_fids--; + return (0); + } + } + + return (0); +} |