diff options
Diffstat (limited to 'db2/db/db.c')
-rw-r--r-- | db2/db/db.c | 818 |
1 files changed, 818 insertions, 0 deletions
diff --git a/db2/db/db.c b/db2/db/db.c new file mode 100644 index 0000000000..df3a9d2d21 --- /dev/null +++ b/db2/db/db.c @@ -0,0 +1,818 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ +/* + * Copyright (c) 1990, 1993, 1994, 1995, 1996 + * Keith Bostic. All rights reserved. + */ +/* + * Copyright (c) 1990, 1993, 1994, 1995 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)db.c 10.37 (Sleepycat) 8/23/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "db_page.h" +#include "db_shash.h" +#include "db_swap.h" +#include "btree.h" +#include "hash.h" +#include "mp.h" +#include "db_am.h" +#include "common_ext.h" + +static int db_close __P((DB *, int)); +static int db_fd __P((DB *, int *)); + +/* + * If the metadata page has the flag set, set the local flag. If the page + * does NOT have the flag set, return EINVAL if the user's dbinfo argument + * caused us to already set the local flag. + */ +#define DBINFO_FCHK(dbp, fn, meta_flags, m_name, dbp_name) { \ + if ((meta_flags) & (m_name)) \ + F_SET(dbp, dbp_name); \ + else \ + if (F_ISSET(dbp, dbp_name)) { \ + __db_err(dbenv, \ + "%s: %s specified in dbinfo argument but not set in file", \ + fname, fn); \ + goto einval; \ + } \ +} + +/* + * db_open -- + * Main library interface to the DB access methods. + */ +int +db_open(fname, type, flags, mode, dbenv, dbinfo, dbpp) + const char *fname; + DBTYPE type; + int flags, mode; + DB_ENV *dbenv; + DB_INFO *dbinfo; + DB **dbpp; +{ + BTMETA *btm; + DB *dbp; + DBT pgcookie; + DB_ENV *envp, t_dbenv; + DB_PGINFO pginfo; + HASHHDR *hashm; + off_t io; + size_t cachesize; + ssize_t nr; + int fd, ftype, need_fileid, restore, ret, retry_cnt, swapped; + char *real_name, mbuf[512]; + + /* Validate arguments. */ +#ifdef HAVE_SPINLOCKS +#define OKFLAGS (DB_CREATE | DB_NOMMAP | DB_RDONLY | DB_THREAD | DB_TRUNCATE) +#else +#define OKFLAGS (DB_CREATE | DB_NOMMAP | DB_RDONLY | DB_TRUNCATE) +#endif + if ((ret = __db_fchk(dbenv, "db_open", flags, OKFLAGS)) != 0) + return (ret); + + /* Initialize for error return. */ + fd = -1; + need_fileid = 1; + real_name = NULL; + + /* Allocate the DB structure, reference the DB_ENV structure. */ + if ((dbp = (DB *)calloc(1, sizeof(DB))) == NULL) { + __db_err(dbenv, "%s", strerror(ENOMEM)); + return (ENOMEM); + } + dbp->dbenv = dbenv; + + /* Convert the dbinfo flags. */ + if (dbinfo != NULL) { + /* + * !!! + * We can't check for illegal flags until we know what type + * of open we're doing. + */ + if (F_ISSET(dbinfo, DB_DELIMITER)) + F_SET(dbp, DB_RE_DELIMITER); + if (F_ISSET(dbinfo, DB_DUP)) + F_SET(dbp, DB_AM_DUP); + if (F_ISSET(dbinfo, DB_FIXEDLEN)) + F_SET(dbp, DB_RE_FIXEDLEN); + if (F_ISSET(dbinfo, DB_PAD)) + F_SET(dbp, DB_RE_PAD); + if (F_ISSET(dbinfo, DB_RECNUM)) + F_SET(dbp, DB_BT_RECNUM); + if (F_ISSET(dbinfo, DB_RENUMBER)) + F_SET(dbp, DB_RE_RENUMBER); + if (F_ISSET(dbinfo, DB_SNAPSHOT)) + F_SET(dbp, DB_RE_SNAPSHOT); + } + + /* Set based on the open(2) flags. */ + if (LF_ISSET(DB_RDONLY)) + F_SET(dbp, DB_AM_RDONLY); + + /* Check threading fields. */ + if (LF_ISSET(DB_THREAD)) { + if ((dbp->mutex = + (db_mutex_t *)malloc(sizeof(db_mutex_t))) == NULL) { + __db_err(dbenv, "%s", strerror(ENOMEM)); + ret = ENOMEM; + goto err; + } + __db_mutex_init(dbp->mutex, 0); + + F_SET(dbp, DB_AM_THREAD); + } + + /* + * Always set the master and initialize the queues, so we can + * use these fields without checking the thread bit. + */ + dbp->master = dbp; + LIST_INIT(&dbp->handleq); + LIST_INSERT_HEAD(&dbp->handleq, dbp, links); + TAILQ_INIT(&dbp->curs_queue); + + /* + * Set based on the dbenv fields, although no logging or transactions + * are possible for temporary files. + */ + if (dbp->dbenv != NULL) { + if (dbenv->lk_info != NULL) + F_SET(dbp, DB_AM_LOCKING); + if (fname != NULL && dbenv->lg_info != NULL) + F_SET(dbp, DB_AM_LOGGING); + } + + /* Set the common fields. */ + if (dbinfo == NULL) { + dbp->pgsize = 0; + dbp->db_malloc = NULL; + } else { + dbp->pgsize = dbinfo->db_pagesize; + dbp->db_malloc = dbinfo->db_malloc; + } + + /* Fill in the default file mode. */ + if (mode == 0) + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + + /* Check if the user wants us to swap byte order. */ + if (dbinfo != NULL) + switch (ret = __db_byteorder(dbenv, dbinfo->db_lorder)) { + case 0: + break; + case DB_SWAPBYTES: + F_SET(dbp, DB_AM_SWAP); + break; + default: + goto err; + } + + /* + * If we have a file name, try and read the first page, figure out + * what type of file it is, and initialize everything we can based + * on that file's meta-data page. + * + * XXX + * We don't actually expect zero-length strings as arguments. We + * do the check, permitting them, because scripting languages, e.g., + * the Tcl test suite, doesn't know anything about passing NULL's. + */ + if (fname != NULL && fname[0] != '\0') { + /* Get the real file name. */ + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, fname, NULL, &real_name)) != 0) + goto err; + + /* + * Open the backing file. We need to make sure that multiple + * processes attempting to create the file at the same time + * are properly ordered so that only one of them creates the + * "unique" file id, so we open it O_EXCL and O_CREAT so two + * simultaneous attempts to create the region will return + * failure in one of the attempts. If we're one of the ones + * that fail, we simply retry without the O_CREAT flag, which + * will require that the meta-data page exist. + */ +#undef OKFLAGS +#define OKFLAGS \ + DB_CREATE | DB_NOMMAP | DB_RDONLY | DB_THREAD | DB_TRUNCATE + retry_cnt = 0; +open_retry: if (LF_ISSET(DB_CREATE)) { + if ((ret = __db_fdopen(real_name, flags | DB_EXCL, + OKFLAGS | DB_EXCL, mode, &fd)) != 0) + if (ret == EEXIST) { + LF_CLR(DB_CREATE); + goto open_retry; + } else { + __db_err(dbenv, + "%s: %s", fname, strerror(ret)); + goto err; + } + } else + if ((ret = __db_fdopen(real_name, + flags, OKFLAGS, mode, &fd)) != 0) { + __db_err(dbenv, "%s: %s", fname, strerror(ret)); + goto err; + } + + /* + * Use the optimum I/O size as the pagesize if a pagesize not + * specified. Some filesystems have 64K as their optimum I/O + * size, but as that results in impossibly large default cache + * sizes, we limit the default pagesize to 16K. + */ + if (dbp->pgsize == 0) { + if ((ret = __db_stat(dbp->dbenv, + real_name, fd, NULL, &io)) != 0) + goto err; + if (io < 512) + io = 512; + if (io > 16 * 1024) + io = 16 * 1024; + dbp->pgsize = io; + F_SET(dbp, DB_AM_PGDEF); + } + + /* + * Try and read the first disk sector -- this code assumes + * that the meta-data for all access methods fits in 512 + * bytes, and that no database will be smaller than that. + */ + if ((ret = __db_read(fd, mbuf, sizeof(mbuf), &nr)) != 0) + goto err; + + /* The fd is no longer needed. */ + (void)__db_close(fd); + fd = -1; + + if (nr != sizeof(mbuf)) { + if (nr != 0) { + __db_err(dbenv, + "%s: unexpected file format", fname); + goto einval; + } + /* + * The only way we can reach here with the DB_CREATE + * flag set is if we created the file. If we didn't + * create the file, there's a chance that someone else + * is busily doing so. Sleep and give them a chance, + * because we need the metadata page their going to + * write. + */ + if (!LF_ISSET(DB_CREATE) && retry_cnt++ < 3) { + __db_sleep(1, 0); + goto open_retry; + } + if (type == DB_UNKNOWN) { + __db_err(dbenv, + "%s: DBTYPE of unknown with empty file", + fname); + goto einval; + } + goto empty; + } + + /* + * A found file overrides some user information. We'll check + * for possible error conditions based on conflicts between + * the file and the user's arguments below. + */ + swapped = 0; + F_CLR(dbp, DB_AM_SWAP); + +retry: switch (((BTMETA *)mbuf)->magic) { + case DB_BTREEMAGIC: + if (type != DB_BTREE && + type != DB_RECNO && type != DB_UNKNOWN) + goto einval; + + btm = (BTMETA *)mbuf; + if (swapped && (ret = __bam_mswap((PAGE *)btm)) != 0) + goto err; + + if (btm->version < DB_BTREEOLDVER || + btm->version > DB_BTREEVERSION) { + __db_err(dbenv, + "%s: unsupported btree version number %lu", + fname, (u_long)btm->version); + goto einval; + } + dbp->pgsize = btm->pagesize; + F_CLR(dbp, DB_AM_PGDEF); + + if ((ret = __db_fchk(dbenv, + "db_open", btm->flags, BTM_MASK)) != 0) + goto err; + DBINFO_FCHK(dbp, "DB_DUP", + btm->flags, BTM_DUP, DB_AM_DUP); + if (F_ISSET(btm, BTM_RECNO)) { + DBINFO_FCHK(dbp, "DB_FIXEDLEN", + btm->flags, BTM_FIXEDLEN, DB_RE_FIXEDLEN); + DBINFO_FCHK(dbp, "DB_RENUMBER", + btm->flags, BTM_RENUMBER, DB_RE_RENUMBER); + type = DB_RECNO; + } else { + DBINFO_FCHK(dbp, "DB_RECNUM", + btm->flags, BTM_RECNUM, DB_BT_RECNUM); + type = DB_BTREE; + } + + /* Copy the file's unique id. */ + need_fileid = 0; + memcpy(dbp->lock.fileid, btm->uid, DB_FILE_ID_LEN); + break; + case DB_HASHMAGIC: + if (type != DB_HASH && type != DB_UNKNOWN) + goto einval; + + hashm = (HASHHDR *)mbuf; + if (swapped && (ret = __ham_mswap((PAGE *)hashm)) != 0) + goto err; + + if (hashm->version < DB_HASHOLDVER || + hashm->version > DB_HASHVERSION) { + __db_err(dbenv, + "%s: unsupported hash version number %lu", + fname, hashm->version); + goto einval; + } + dbp->pgsize = hashm->pagesize; + F_CLR(dbp, DB_AM_PGDEF); + + if ((ret = __db_fchk(dbenv, + "db_open", hashm->flags, DB_HASH_DUP)) != 0) + goto err; + DBINFO_FCHK(dbp, "DB_DUP", + hashm->flags, DB_HASH_DUP, DB_AM_DUP); + type = DB_HASH; + + /* Copy the file's unique id. */ + need_fileid = 0; + memcpy(dbp->lock.fileid, hashm->uid, DB_FILE_ID_LEN); + break; + default: + if (swapped) { + __db_err(dbenv, "unrecognized file type"); + goto einval; + } + M_32_SWAP(((BTMETA *)mbuf)->magic); + F_SET(dbp, DB_AM_SWAP); + + swapped = 1; + goto retry; + } + } else { + fname = real_name = NULL; + + if (type == DB_UNKNOWN) { + __db_err(dbenv, + "DBTYPE of unknown without existing file"); + goto einval; + } + F_SET(dbp, DB_AM_INMEM); + } + +empty: /* + * By the time we get here we've either set the type or we're taking + * it from the user. + */ + dbp->type = type; + + /* + * Set the page size to the best value for I/O to this file. Don't + * overflow the page offset type. The page size must be db_indx_t + * aligned and >= MIN_PAGE_SIZE. + * + * XXX + * Should we be checking for a page size that's not a multiple of 512? + */ + if (dbp->pgsize == 0) { + F_SET(dbp, DB_AM_PGDEF); + dbp->pgsize = 8 * 1024; + } + if (dbp->pgsize < DB_MIN_PGSIZE || + dbp->pgsize > DB_MAX_PGSIZE || + dbp->pgsize & (sizeof(db_indx_t) - 1)) { + __db_err(dbenv, "illegal page size"); + goto einval; + } + + /* + * Set and/or correct the cache size; must be a multiple of the + * page size. + */ + if (dbinfo == NULL || dbinfo->db_cachesize == 0) + cachesize = dbp->pgsize * DB_MINCACHE; + else { + cachesize = dbinfo->db_cachesize; + if (cachesize & (dbp->pgsize - 1)) + cachesize += (~cachesize & (dbp->pgsize - 1)) + 1; + if (cachesize < dbp->pgsize * DB_MINCACHE) + cachesize = dbp->pgsize * DB_MINCACHE; + if (cachesize < 20 * 1024) + cachesize = 20 * 1024; + } + + /* + * If no mpool supplied by the application, attach to a local, + * created buffer pool. + * + * XXX + * If the user has a DB_ENV structure, we have to use a temporary + * one so that we don't step on their values. If the user doesn't, + * we have to create one, and keep it around until the call to the + * memp_close() function. This is all so the mpool functions get + * the error stuff right. + */ + if (dbenv == NULL || dbenv->mp_info == NULL) { + F_SET(dbp, DB_AM_MLOCAL); + + if (dbenv == NULL) { + if ((dbp->mp_dbenv = + (DB_ENV *)calloc(sizeof(DB_ENV), 1)) == NULL) { + ret = ENOMEM; + goto err; + } + + envp = dbp->mp_dbenv; + restore = 0; + } else { + t_dbenv = *dbenv; + + envp = dbenv; + restore = 1; + } + envp->mp_size = cachesize; + F_SET(envp, DB_MPOOL_PRIVATE); + if ((ret = memp_open(NULL, + DB_CREATE, S_IRUSR | S_IWUSR, envp, &dbp->mp)) != 0) + goto err; + if (restore) + *dbenv = t_dbenv; + } else + dbp->mp = dbenv->mp_info; + + /* Register DB's pgin/pgout functions. */ + if ((ret = memp_register(dbp->mp, + DB_FTYPE_BTREE, __bam_pgin, __bam_pgout)) != 0) + goto err; + if ((ret = memp_register(dbp->mp, + DB_FTYPE_HASH, __ham_pgin, __ham_pgout)) != 0) + goto err; + + /* + * If we don't already have one, get a unique file ID. If the file + * is a temporary file, then we have to create a unique file ID -- + * no backing file will be created until the mpool cache is filled + * forcing it to go to disk. The created ID must never match any + * potential real file ID -- we know it won't because real file IDs + * contain a time stamp after the dev/ino pair, and we're simply + * storing a 4-byte locker ID. + * + * XXX + * Store the file id in the locker structure -- we can get it from + * there as necessary, and it saves having two copies. + */ + if (need_fileid) + if (fname == NULL) { + memset(dbp->lock.fileid, 0, DB_FILE_ID_LEN); + if (F_ISSET(dbp, DB_AM_LOCKING) && + (ret = lock_id(dbenv->lk_info, + (u_int32_t *)dbp->lock.fileid)) != 0) + goto err; + } else + if ((ret = __db_fileid(dbenv, + real_name, 1, dbp->lock.fileid)) != 0) + goto err; + + /* No further use for the real name. */ + if (real_name != NULL) + FREES(real_name); + real_name = NULL; + + /* + * Open a backing file in the memory pool. + * + * If we need to process the file's pages on I/O, set the file type. + * If it's a hash file, always call pgin and pgout routines. This + * means that hash files can never be mapped into process memory. If + * it's a btree file and requires swapping, we need to page the file + * in and out. This has to be right -- we can't mmap files that are + * being paged in and out. + */ + if (type == DB_HASH) + ftype = DB_FTYPE_HASH; + else + ftype = F_ISSET(dbp, DB_AM_SWAP) ? DB_FTYPE_BTREE : 0; + pginfo.db_pagesize = dbp->pgsize; + pginfo.needswap = F_ISSET(dbp, DB_AM_SWAP); + pgcookie.data = &pginfo; + pgcookie.size = sizeof(DB_PGINFO); + + if ((ret = memp_fopen(dbp->mp, fname, ftype, + F_ISSET(dbp, DB_AM_RDONLY) ? DB_RDONLY : 0, 0, dbp->pgsize, + 0, &pgcookie, dbp->lock.fileid, &dbp->mpf)) != 0) + goto err; + + /* Get a log file id. */ + if (F_ISSET(dbp, DB_AM_LOGGING) && + (ret = log_register(dbenv->lg_info, + dbp, fname, type, &dbp->log_fileid)) != 0) + goto err; + + /* + * Get a locker id for this DB, and build the lock cookie: the first + * db_pgno_t bytes are the page number, the next N bytes are the file + * id. + */ + if (F_ISSET(dbp, DB_AM_LOCKING)) { + if ((ret = lock_id(dbenv->lk_info, &dbp->locker)) != 0) + goto err; + dbp->lock_dbt.size = sizeof(dbp->lock); + dbp->lock_dbt.data = &dbp->lock; + } + + /* Call the real open function. */ + switch (type) { + case DB_BTREE: + if (dbinfo != NULL && (ret = __db_fchk(dbenv, + "db_open", dbinfo->flags, DB_RECNUM | DB_DUP)) != 0) + goto err; + if (dbinfo != NULL && (ret = __db_fcchk(dbenv, + "db_open", dbinfo->flags, DB_DUP, DB_RECNUM)) != 0) + goto err; + if ((ret = __bam_open(dbp, type, dbinfo)) != 0) + goto err; + break; + case DB_HASH: + if (dbinfo != NULL && (ret = __db_fchk(dbenv, + "db_open", dbinfo->flags, DB_DUP)) != 0) + goto err; + if ((ret = __ham_open(dbp, dbinfo)) != 0) + goto err; + break; + case DB_RECNO: +#define DB_INFO_FLAGS \ + (DB_DELIMITER | DB_FIXEDLEN | DB_PAD | DB_RENUMBER | DB_SNAPSHOT) + if (dbinfo != NULL && (ret = __db_fchk(dbenv, + "db_open", dbinfo->flags, DB_INFO_FLAGS)) != 0) + goto err; + if ((ret = __ram_open(dbp, type, dbinfo)) != 0) + goto err; + break; + default: + abort(); + } + + /* Call a local close routine. */ + dbp->close = db_close; + dbp->fd = db_fd; + + *dbpp = dbp; + return (0); + +einval: ret = EINVAL; +err: /* Close the file descriptor. */ + if (fd != -1) + (void)__db_close(fd); + + /* Discard the log file id. */ + if (dbp->log_fileid != 0) + (void)log_unregister(dbenv->lg_info, dbp->log_fileid); + + /* Close the memory pool file. */ + if (dbp->mpf != NULL) + (void)memp_fclose(dbp->mpf); + + /* If the memory pool was local, close it. */ + if (F_ISSET(dbp, DB_AM_MLOCAL) && dbp->mp != NULL) + (void)memp_close(dbp->mp); + + /* If we allocated a DB_ENV, discard it. */ + if (dbp->mp_dbenv != NULL) + FREE(dbp->mp_dbenv, sizeof(DB_ENV)); + + if (real_name != NULL) + FREES(real_name); + if (dbp != NULL) + FREE(dbp, sizeof(DB)); + + return (ret); +} + +/* + * db_close -- + * Close a DB tree. + */ +static int +db_close(dbp, flags) + DB *dbp; + int flags; +{ + DBC *dbc; + DB *tdbp; + int ret, t_ret; + + ret = 0; + + /* Sync the underlying file. */ + if (!LF_ISSET(DB_NOSYNC) && + (t_ret = dbp->sync(dbp, 0)) != 0 && ret == 0) + ret = t_ret; + + /* + * Call the underlying access method close routine for all the + * cursors and handles. + */ + for (tdbp = LIST_FIRST(&dbp->handleq); + tdbp != NULL; tdbp = LIST_NEXT(tdbp, links)) { + + while ((dbc = TAILQ_FIRST(&tdbp->curs_queue)) != NULL) + if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0) + ret = t_ret; + + switch (tdbp->type) { + case DB_BTREE: + if ((t_ret = __bam_close(tdbp)) != 0 && ret == 0) + ret = t_ret; + break; + case DB_HASH: + if ((t_ret = __ham_close(tdbp)) != 0 && ret == 0) + ret = t_ret; + break; + case DB_RECNO: + if ((t_ret = __ram_close(tdbp)) != 0 && ret == 0) + ret = t_ret; + break; + default: + abort(); + } + + } + + /* Sync the memory pool. */ + if ((t_ret = memp_fsync(dbp->mpf)) != 0 && ret == 0) + ret = t_ret; + + /* Close the memory pool file. */ + if ((t_ret = memp_fclose(dbp->mpf)) != 0 && ret == 0) + ret = t_ret; + + /* If the memory pool was local, close it. */ + if (F_ISSET(dbp, DB_AM_MLOCAL) && + (t_ret = memp_close(dbp->mp)) != 0 && ret == 0) + ret = t_ret; + + /* Discard the mutex. */ + if (dbp->mutex != NULL) + FREE(dbp->mutex, sizeof(db_mutex_t)); + + /* Discard the log file id. */ + if (F_ISSET(dbp, DB_AM_LOGGING)) + (void)log_unregister(dbp->dbenv->lg_info, dbp->log_fileid); + + /* Discard the lock cookie for all handles. */ + for (tdbp = LIST_FIRST(&dbp->handleq); + tdbp != NULL; tdbp = LIST_NEXT(tdbp, links)) + if (F_ISSET(tdbp, DB_AM_LOCKING)) { +#ifdef DEBUG + DB_LOCKREQ request; + + /* + * If we're running tests, display any locks currently + * held. It's possible that some applications may hold + * locks for long periods, e.g., conference room locks, + * but the DB tests should never close holding locks. + */ + request.op = DB_LOCK_DUMP; + if ((t_ret = lock_vec(tdbp->dbenv->lk_info, + tdbp->locker, 0, &request, 1, NULL)) != 0 && + ret == 0) + ret = EAGAIN; +#endif + } + + /* If we allocated a DB_ENV, discard it. */ + if (dbp->mp_dbenv != NULL) + FREE(dbp->mp_dbenv, sizeof(DB_ENV)); + + /* Free all of the DB's. */ + LIST_REMOVE(dbp, links); + while ((tdbp = LIST_FIRST(&dbp->handleq)) != NULL) { + LIST_REMOVE(tdbp, links); + FREE(tdbp, sizeof(*tdbp)); + } + FREE(dbp, sizeof(*dbp)); + + return (ret); +} + +/* + * db_fd -- + * Return a file descriptor for flock'ing. + */ +static int +db_fd(dbp, fdp) + DB *dbp; + int *fdp; +{ + /* In-memory database can't have a file descriptor. */ + if (F_ISSET(dbp, DB_AM_INMEM)) + return (ENOENT); + + /* + * XXX + * Truly spectacular layering violation. As we don't open the + * underlying file until we need it, it may not be initialized. + */ + if ((*fdp = dbp->mpf->fd) == -1) + return (ENOENT); + return (0); +} + +/* + * __db_pgerr -- + * Error when unable to retrieve a specified page. + * + * PUBLIC: int __db_pgerr __P((DB *, db_pgno_t)); + */ +int +__db_pgerr(dbp, pgno) + DB *dbp; + db_pgno_t pgno; +{ + __db_err(dbp->dbenv, + "unable to create/retrieve page %lu", (u_long)pgno); + return (__db_panic(dbp)); +} + +/* + * __db_pgfmt -- + * Error when a page has the wrong format. + * + * PUBLIC: int __db_pgfmt __P((DB *, db_pgno_t)); + */ +int +__db_pgfmt(dbp, pgno) + DB *dbp; + db_pgno_t pgno; +{ + __db_err(dbp->dbenv, + "page %lu: illegal page type or format", (u_long)pgno); + return (__db_panic(dbp)); +} |