db.c 41.6 KB
Newer Older
unknown's avatar
unknown committed
1 2 3
/*-
 * See the file LICENSE for redistribution information.
 *
unknown's avatar
unknown committed
4
 * Copyright (c) 1996-2005
unknown's avatar
unknown committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 *	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. 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.
unknown's avatar
unknown committed
38
 *
unknown's avatar
unknown committed
39
 * $Id: db.c,v 12.22 2005/11/12 17:41:44 bostic Exp $
unknown's avatar
unknown committed
40 41 42 43 44 45 46 47 48 49 50
 */

#include "db_config.h"

#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>

#include <string.h>
#endif

#include "db_int.h"
unknown's avatar
unknown committed
51 52 53 54
#include "dbinc/db_page.h"
#include "dbinc/db_shash.h"
#include "dbinc/db_swap.h"
#include "dbinc/btree.h"
unknown's avatar
unknown committed
55
#include "dbinc/fop.h"
unknown's avatar
unknown committed
56 57 58 59 60 61 62 63
#include "dbinc/hash.h"
#include "dbinc/lock.h"
#include "dbinc/log.h"
#include "dbinc/mp.h"
#include "dbinc/qam.h"
#include "dbinc/txn.h"

static int __db_disassociate __P((DB *));
unknown's avatar
unknown committed
64 65 66

#ifdef CONFIG_TEST
static void __db_makecopy __P((DB_ENV *, const char *, const char *));
unknown's avatar
unknown committed
67 68
static int  __db_testdocopy __P((DB_ENV *, const char *));
static int  __qam_testdocopy __P((DB *, const char *));
unknown's avatar
unknown committed
69 70 71
#endif

/*
unknown's avatar
unknown committed
72 73
 * DB.C --
 *	This file contains the utility functions for the DBP layer.
unknown's avatar
unknown committed
74 75 76 77 78 79 80
 */

/*
 * __db_master_open --
 *	Open up a handle on a master database.
 *
 * PUBLIC: int __db_master_open __P((DB *,
unknown's avatar
unknown committed
81
 * PUBLIC:     DB_TXN *, const char *, u_int32_t, int, DB **));
unknown's avatar
unknown committed
82 83
 */
int
unknown's avatar
unknown committed
84
__db_master_open(subdbp, txn, name, flags, mode, dbpp)
unknown's avatar
unknown committed
85
	DB *subdbp;
unknown's avatar
unknown committed
86
	DB_TXN *txn;
unknown's avatar
unknown committed
87 88 89 90 91 92 93 94
	const char *name;
	u_int32_t flags;
	int mode;
	DB **dbpp;
{
	DB *dbp;
	int ret;

unknown's avatar
unknown committed
95 96
	*dbpp = NULL;

unknown's avatar
unknown committed
97 98 99 100 101 102 103 104 105 106 107 108
	/* Open up a handle on the main database. */
	if ((ret = db_create(&dbp, subdbp->dbenv, 0)) != 0)
		return (ret);

	/*
	 * It's always a btree.
	 * Run in the transaction we've created.
	 * Set the pagesize in case we're creating a new database.
	 * Flag that we're creating a database with subdatabases.
	 */
	dbp->pgsize = subdbp->pgsize;
	F_SET(dbp, DB_AM_SUBDB);
unknown's avatar
unknown committed
109
	F_SET(dbp, F_ISSET(subdbp,
unknown's avatar
unknown committed
110 111
	    DB_AM_RECOVER | DB_AM_SWAP |
	    DB_AM_ENCRYPT | DB_AM_CHKSUM | DB_AM_NOT_DURABLE));
unknown's avatar
unknown committed
112

unknown's avatar
unknown committed
113 114 115 116 117 118 119
	/*
	 * If there was a subdb specified, then we only want to apply
	 * DB_EXCL to the subdb, not the actual file.  We only got here
	 * because there was a subdb specified.
	 */
	LF_CLR(DB_EXCL);
	LF_SET(DB_RDWRMASTER);
unknown's avatar
unknown committed
120 121
	if ((ret = __db_open(dbp,
	    txn, name, NULL, DB_BTREE, flags, mode, PGNO_BASE_MD)) != 0)
unknown's avatar
unknown committed
122
		goto err;
unknown's avatar
unknown committed
123

unknown's avatar
unknown committed
124
	/*
unknown's avatar
unknown committed
125 126 127 128 129 130 131
	 * Verify that pagesize is the same on both.  The items in dbp were now
	 * initialized from the meta page.  The items in dbp were set in
	 * __db_dbopen when we either read or created the master file.  Other
	 * items such as checksum and encryption are checked when we read the
	 * meta-page.  So we do not check those here.  However, if the
	 * meta-page caused checksumming to be turned on and it wasn't already,
	 * set it here.
unknown's avatar
unknown committed
132 133 134 135 136 137 138 139 140 141 142
	 */
	if (F_ISSET(dbp, DB_AM_CHKSUM))
		F_SET(subdbp, DB_AM_CHKSUM);
	if (subdbp->pgsize != 0 && dbp->pgsize != subdbp->pgsize) {
		ret = EINVAL;
		__db_err(dbp->dbenv,
		    "Different pagesize specified on existent file");
		goto err;
	}
err:
	if (ret != 0 && !F_ISSET(dbp, DB_AM_DISCARD))
unknown's avatar
unknown committed
143
		(void)__db_close(dbp, txn, 0);
unknown's avatar
unknown committed
144 145 146
	else
		*dbpp = dbp;
	return (ret);
unknown's avatar
unknown committed
147 148 149 150
}

/*
 * __db_master_update --
unknown's avatar
unknown committed
151 152 153 154
 *	Add/Open/Remove a subdatabase from a master database.
 *
 * PUBLIC: int __db_master_update __P((DB *, DB *, DB_TXN *, const char *,
 * PUBLIC:     DBTYPE, mu_action, const char *, u_int32_t));
unknown's avatar
unknown committed
155
 */
unknown's avatar
unknown committed
156 157 158 159
int
__db_master_update(mdbp, sdbp, txn, subdb, type, action, newname, flags)
	DB *mdbp, *sdbp;
	DB_TXN *txn;
unknown's avatar
unknown committed
160
	const char *subdb;
unknown's avatar
unknown committed
161
	DBTYPE type;
unknown's avatar
unknown committed
162 163 164 165 166 167 168
	mu_action action;
	const char *newname;
	u_int32_t flags;
{
	DB_ENV *dbenv;
	DBC *dbc, *ndbc;
	DBT key, data, ndata;
unknown's avatar
unknown committed
169
	PAGE *p, *r;
unknown's avatar
unknown committed
170 171 172 173 174 175 176 177 178 179
	db_pgno_t t_pgno;
	int modify, ret, t_ret;

	dbenv = mdbp->dbenv;
	dbc = ndbc = NULL;
	p = NULL;

	memset(&key, 0, sizeof(key));
	memset(&data, 0, sizeof(data));

unknown's avatar
unknown committed
180 181 182
	/* Might we modify the master database?  If so, we'll need to lock. */
	modify = (action != MU_OPEN || LF_ISSET(DB_CREATE)) ? 1 : 0;

unknown's avatar
unknown committed
183 184 185 186
	/*
	 * Open up a cursor.  If this is CDB and we're creating the database,
	 * make it an update cursor.
	 */
unknown's avatar
unknown committed
187
	if ((ret = __db_cursor(mdbp, txn, &dbc,
unknown's avatar
unknown committed
188 189 190 191
	    (CDB_LOCKING(dbenv) && modify) ? DB_WRITECURSOR : 0)) != 0)
		goto err;

	/*
unknown's avatar
unknown committed
192
	 * Point the cursor at the record.
unknown's avatar
unknown committed
193 194 195 196
	 *
	 * If we're removing or potentially creating an entry, lock the page
	 * with DB_RMW.
	 *
unknown's avatar
unknown committed
197 198 199 200
	 * We do multiple cursor operations with the cursor in some cases and
	 * subsequently access the data DBT information.  Set DB_DBT_MALLOC so
	 * we don't risk modification of the data between our uses of it.
	 *
unknown's avatar
unknown committed
201 202 203
	 * !!!
	 * We don't include the name's nul termination in the database.
	 */
unknown's avatar
unknown committed
204 205
	key.data = (void *)subdb;
	key.size = (u_int32_t)strlen(subdb);
unknown's avatar
unknown committed
206
	F_SET(&data, DB_DBT_MALLOC);
unknown's avatar
unknown committed
207

unknown's avatar
unknown committed
208
	ret = __db_c_get(dbc, &key, &data,
unknown's avatar
unknown committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
	    DB_SET | ((STD_LOCKING(dbc) && modify) ? DB_RMW : 0));

	/*
	 * What we do next--whether or not we found a record for the
	 * specified subdatabase--depends on what the specified action is.
	 * Handle ret appropriately as the first statement of each case.
	 */
	switch (action) {
	case MU_REMOVE:
		/*
		 * We should have found something if we're removing it.  Note
		 * that in the common case where the DB we're asking to remove
		 * doesn't exist, we won't get this far;  __db_subdb_remove
		 * will already have returned an error from __db_open.
		 */
		if (ret != 0)
			goto err;

		/*
		 * Delete the subdatabase entry first;  if this fails,
		 * we don't want to touch the actual subdb pages.
		 */
unknown's avatar
unknown committed
231
		if ((ret = __db_c_del(dbc, 0)) != 0)
unknown's avatar
unknown committed
232 233 234 235 236 237 238
			goto err;

		/*
		 * We're handling actual data, not on-page meta-data,
		 * so it hasn't been converted to/from opposite
		 * endian architectures.  Do it explicitly, now.
		 */
unknown's avatar
unknown committed
239 240 241
		memcpy(&sdbp->meta_pgno, data.data, sizeof(db_pgno_t));
		DB_NTOHL(&sdbp->meta_pgno);
		if ((ret =
unknown's avatar
unknown committed
242
		    __memp_fget(mdbp->mpf, &sdbp->meta_pgno, 0, &p)) != 0)
unknown's avatar
unknown committed
243 244
			goto err;

unknown's avatar
unknown committed
245 246 247
		/* Free the root on the master db if it was created. */
		if (TYPE(p) == P_BTREEMETA &&
		    ((BTMETA *)p)->root != PGNO_INVALID) {
unknown's avatar
unknown committed
248 249 250 251 252 253 254 255 256 257
			if ((ret = __memp_fget(mdbp->mpf,
			     &((BTMETA *)p)->root, 0, &r)) != 0)
				goto err;

			/* Free and put the page. */
			if ((ret = __db_free(dbc, r)) != 0) {
				r = NULL;
				goto err;
			}
		}
unknown's avatar
unknown committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
		/* Free and put the page. */
		if ((ret = __db_free(dbc, p)) != 0) {
			p = NULL;
			goto err;
		}
		p = NULL;
		break;
	case MU_RENAME:
		/* We should have found something if we're renaming it. */
		if (ret != 0)
			goto err;

		/*
		 * Before we rename, we need to make sure we're not
		 * overwriting another subdatabase, or else this operation
		 * won't be undoable.  Open a second cursor and check
		 * for the existence of newname;  it shouldn't appear under
		 * us since we hold the metadata lock.
		 */
unknown's avatar
unknown committed
277
		if ((ret = __db_cursor(mdbp, txn, &ndbc, 0)) != 0)
unknown's avatar
unknown committed
278
			goto err;
unknown's avatar
unknown committed
279 280
		key.data = (void *)newname;
		key.size = (u_int32_t)strlen(newname);
unknown's avatar
unknown committed
281 282 283 284 285 286 287 288

		/*
		 * We don't actually care what the meta page of the potentially-
		 * overwritten DB is;  we just care about existence.
		 */
		memset(&ndata, 0, sizeof(ndata));
		F_SET(&ndata, DB_DBT_USERMEM | DB_DBT_PARTIAL);

unknown's avatar
unknown committed
289
		if ((ret = __db_c_get(ndbc, &key, &ndata, DB_SET)) == 0) {
unknown's avatar
unknown committed
290 291 292 293 294 295 296 297 298 299 300 301
			/* A subdb called newname exists.  Bail. */
			ret = EEXIST;
			__db_err(dbenv, "rename: database %s exists", newname);
			goto err;
		} else if (ret != DB_NOTFOUND)
			goto err;

		/*
		 * Now do the put first;  we don't want to lose our
		 * sole reference to the subdb.  Use the second cursor
		 * so that the first one continues to point to the old record.
		 */
unknown's avatar
unknown committed
302
		if ((ret = __db_c_put(ndbc, &key, &data, DB_KEYFIRST)) != 0)
unknown's avatar
unknown committed
303
			goto err;
unknown's avatar
unknown committed
304
		if ((ret = __db_c_del(dbc, 0)) != 0) {
unknown's avatar
unknown committed
305 306 307 308
			/*
			 * If the delete fails, try to delete the record
			 * we just put, in case we're not txn-protected.
			 */
unknown's avatar
unknown committed
309
			(void)__db_c_del(ndbc, 0);
unknown's avatar
unknown committed
310 311 312 313 314 315 316 317 318 319 320
			goto err;
		}

		break;
	case MU_OPEN:
		/*
		 * Get the subdatabase information.  If it already exists,
		 * copy out the page number and we're done.
		 */
		switch (ret) {
		case 0:
unknown's avatar
unknown committed
321 322 323 324 325 326
			if (LF_ISSET(DB_CREATE) && LF_ISSET(DB_EXCL)) {
				ret = EEXIST;
				goto err;
			}
			memcpy(&sdbp->meta_pgno, data.data, sizeof(db_pgno_t));
			DB_NTOHL(&sdbp->meta_pgno);
unknown's avatar
unknown committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340
			goto done;
		case DB_NOTFOUND:
			if (LF_ISSET(DB_CREATE))
				break;
			/*
			 * No db_err, it is reasonable to remove a
			 * nonexistent db.
			 */
			ret = ENOENT;
			goto err;
		default:
			goto err;
		}

unknown's avatar
unknown committed
341
		/* Create a subdatabase. */
unknown's avatar
unknown committed
342 343 344
		if ((ret = __db_new(dbc,
		    type == DB_HASH ? P_HASHMETA : P_BTREEMETA, &p)) != 0)
			goto err;
unknown's avatar
unknown committed
345
		sdbp->meta_pgno = PGNO(p);
unknown's avatar
unknown committed
346 347 348 349 350 351 352 353 354 355 356 357

		/*
		 * XXX
		 * We're handling actual data, not on-page meta-data, so it
		 * hasn't been converted to/from opposite endian architectures.
		 * Do it explicitly, now.
		 */
		t_pgno = PGNO(p);
		DB_HTONL(&t_pgno);
		memset(&ndata, 0, sizeof(ndata));
		ndata.data = &t_pgno;
		ndata.size = sizeof(db_pgno_t);
unknown's avatar
unknown committed
358
		if ((ret = __db_c_put(dbc, &key, &ndata, DB_KEYLAST)) != 0)
unknown's avatar
unknown committed
359
			goto err;
unknown's avatar
unknown committed
360
		F_SET(sdbp, DB_AM_CREATED);
unknown's avatar
unknown committed
361 362 363 364 365 366 367 368 369 370 371
		break;
	}

err:
done:	/*
	 * If we allocated a page: if we're successful, mark the page dirty
	 * and return it to the cache, otherwise, discard/free it.
	 */
	if (p != NULL) {
		if (ret == 0) {
			if ((t_ret =
unknown's avatar
unknown committed
372
			    __memp_fput(mdbp->mpf, p, DB_MPOOL_DIRTY)) != 0)
unknown's avatar
unknown committed
373 374
				ret = t_ret;
		} else
unknown's avatar
unknown committed
375
			(void)__memp_fput(mdbp->mpf, p, 0);
unknown's avatar
unknown committed
376 377 378 379
	}

	/* Discard the cursor(s) and data. */
	if (data.data != NULL)
unknown's avatar
unknown committed
380
		__os_ufree(dbenv, data.data);
unknown's avatar
unknown committed
381
	if (dbc != NULL && (t_ret = __db_c_close(dbc)) != 0 && ret == 0)
unknown's avatar
unknown committed
382
		ret = t_ret;
unknown's avatar
unknown committed
383
	if (ndbc != NULL && (t_ret = __db_c_close(ndbc)) != 0 && ret == 0)
unknown's avatar
unknown committed
384 385 386 387 388 389 390 391 392
		ret = t_ret;

	return (ret);
}

/*
 * __db_dbenv_setup --
 *	Set up the underlying environment during a db_open.
 *
unknown's avatar
unknown committed
393
 * PUBLIC: int __db_dbenv_setup __P((DB *,
unknown's avatar
unknown committed
394
 * PUBLIC:     DB_TXN *, const char *, const char *, u_int32_t, u_int32_t));
unknown's avatar
unknown committed
395 396
 */
int
unknown's avatar
unknown committed
397
__db_dbenv_setup(dbp, txn, fname, dname, id, flags)
unknown's avatar
unknown committed
398
	DB *dbp;
unknown's avatar
unknown committed
399
	DB_TXN *txn;
unknown's avatar
unknown committed
400
	const char *fname, *dname;
unknown's avatar
unknown committed
401
	u_int32_t id, flags;
unknown's avatar
unknown committed
402 403
{
	DB *ldbp;
unknown's avatar
unknown committed
404
	DB_ENV *dbenv;
unknown's avatar
unknown committed
405
	u_int32_t maxid;
unknown's avatar
unknown committed
406
	int ret;
unknown's avatar
unknown committed
407 408 409 410 411 412 413 414

	dbenv = dbp->dbenv;

	/* If we don't yet have an environment, it's time to create it. */
	if (!F_ISSET(dbenv, DB_ENV_OPEN_CALLED)) {
		/* Make sure we have at least DB_MINCACHE pages in our cache. */
		if (dbenv->mp_gbytes == 0 &&
		    dbenv->mp_bytes < dbp->pgsize * DB_MINPAGECACHE &&
unknown's avatar
unknown committed
415
		    (ret = __memp_set_cachesize(
unknown's avatar
unknown committed
416 417 418
		    dbenv, 0, dbp->pgsize * DB_MINPAGECACHE, 0)) != 0)
			return (ret);

unknown's avatar
unknown committed
419
		if ((ret = __env_open(dbenv, NULL, DB_CREATE |
unknown's avatar
unknown committed
420 421 422 423
		    DB_INIT_MPOOL | DB_PRIVATE | LF_ISSET(DB_THREAD), 0)) != 0)
			return (ret);
	}

unknown's avatar
unknown committed
424
	/* Join the underlying cache. */
unknown's avatar
unknown committed
425 426
	if ((!F_ISSET(dbp, DB_AM_INMEM) || dname == NULL) &&
	    (ret = __db_dbenv_mpool(dbp, fname, flags)) != 0)
unknown's avatar
unknown committed
427 428
		return (ret);

unknown's avatar
unknown committed
429 430 431 432
	/* We may need a per-thread mutex. */
	if (LF_ISSET(DB_THREAD) && (ret = __mutex_alloc(
	    dbenv, MTX_DB_HANDLE, DB_MUTEX_THREAD, &dbp->mutex)) != 0)
		return (ret);
unknown's avatar
unknown committed
433

unknown's avatar
unknown committed
434 435 436 437 438 439
	/*
	 * Set up a bookkeeping entry for this database in the log region,
	 * if such a region exists.  Note that even if we're in recovery
	 * or a replication client, where we won't log registries, we'll
	 * still need an FNAME struct, so LOGGING_ON is the correct macro.
	 */
unknown's avatar
unknown committed
440 441 442
	if (LOGGING_ON(dbenv) && dbp->log_filename == NULL &&
	    (ret = __dbreg_setup(dbp,
	    F_ISSET(dbp, DB_AM_INMEM) ? dname : fname, id)) != 0)
unknown's avatar
unknown committed
443 444 445 446
		return (ret);

	/*
	 * If we're actively logging and our caller isn't a recovery function
unknown's avatar
unknown committed
447
	 * that already did so, then assign this dbp a log fileid.
unknown's avatar
unknown committed
448 449
	 */
	if (DBENV_LOGGING(dbenv) && !F_ISSET(dbp, DB_AM_RECOVER) &&
unknown's avatar
unknown committed
450 451 452
#if !defined(DEBUG_ROP)
	    !F_ISSET(dbp, DB_AM_RDONLY) &&
#endif
unknown's avatar
unknown committed
453
	    (ret = __dbreg_new_id(dbp, txn)) != 0)
unknown's avatar
unknown committed
454 455 456 457 458 459 460 461 462 463 464
		return (ret);

	/*
	 * Insert ourselves into the DB_ENV's dblist.  We allocate a
	 * unique ID to each {fileid, meta page number} pair, and to
	 * each temporary file (since they all have a zero fileid).
	 * This ID gives us something to use to tell which DB handles
	 * go with which databases in all the cursor adjustment
	 * routines, where we don't want to do a lot of ugly and
	 * expensive memcmps.
	 */
unknown's avatar
unknown committed
465
	MUTEX_LOCK(dbenv, dbenv->mtx_dblist);
unknown's avatar
unknown committed
466
	for (maxid = 0, ldbp = LIST_FIRST(&dbenv->dblist);
unknown's avatar
unknown committed
467
	    ldbp != NULL; ldbp = LIST_NEXT(ldbp, dblistlinks)) {
unknown's avatar
unknown committed
468 469 470 471 472 473 474 475 476
		if (!F_ISSET(dbp, DB_AM_INMEM)) {
			if (memcmp(ldbp->fileid, dbp->fileid, DB_FILE_ID_LEN)
			    == 0 && ldbp->meta_pgno == dbp->meta_pgno)
				break;
		} else if (dname != NULL) {
			if (F_ISSET(ldbp, DB_AM_INMEM) &&
			    strcmp(ldbp->dname, dname) == 0)
				break;
		}
unknown's avatar
unknown committed
477 478 479 480 481 482
		if (ldbp->adj_fileid > maxid)
			maxid = ldbp->adj_fileid;
	}

	/*
	 * If ldbp is NULL, we didn't find a match, or we weren't
unknown's avatar
unknown committed
483
	 * really looking because fname is NULL.  Assign the dbp an
unknown's avatar
unknown committed
484 485 486 487 488 489 490 491 492 493 494 495 496 497
	 * adj_fileid one higher than the largest we found, and
	 * insert it at the head of the master dbp list.
	 *
	 * If ldbp is not NULL, it is a match for our dbp.  Give dbp
	 * the same ID that ldbp has, and add it after ldbp so they're
	 * together in the list.
	 */
	if (ldbp == NULL) {
		dbp->adj_fileid = maxid + 1;
		LIST_INSERT_HEAD(&dbenv->dblist, dbp, dblistlinks);
	} else {
		dbp->adj_fileid = ldbp->adj_fileid;
		LIST_INSERT_AFTER(ldbp, dbp, dblistlinks);
	}
unknown's avatar
unknown committed
498
	MUTEX_UNLOCK(dbenv, dbenv->mtx_dblist);
unknown's avatar
unknown committed
499 500 501 502 503

	return (0);
}

/*
unknown's avatar
unknown committed
504 505
 * __db_dbenv_mpool --
 *	Set up the underlying environment cache during a db_open.
unknown's avatar
unknown committed
506
 *
unknown's avatar
unknown committed
507
 * PUBLIC: int __db_dbenv_mpool __P((DB *, const char *, u_int32_t));
unknown's avatar
unknown committed
508
 */
unknown's avatar
unknown committed
509
int
unknown's avatar
unknown committed
510
__db_dbenv_mpool(dbp, fname, flags)
unknown's avatar
unknown committed
511
	DB *dbp;
unknown's avatar
unknown committed
512
	const char *fname;
unknown's avatar
unknown committed
513 514 515
	u_int32_t flags;
{
	DB_ENV *dbenv;
unknown's avatar
unknown committed
516 517 518
	DBT pgcookie;
	DB_MPOOLFILE *mpf;
	DB_PGINFO pginfo;
unknown's avatar
unknown committed
519 520 521
	int fidset, ftype, ret;
	int32_t lsn_off;
	u_int8_t nullfid[DB_FILE_ID_LEN];
unknown's avatar
unknown committed
522 523 524
	u_int32_t clear_len;

	COMPQUIET(mpf, NULL);
unknown's avatar
unknown committed
525

unknown's avatar
unknown committed
526
	dbenv = dbp->dbenv;
unknown's avatar
unknown committed
527 528 529 530 531
	lsn_off = 0;

	/* It's possible that this database is already open. */
	if (F_ISSET(dbp, DB_AM_OPEN_CALLED))
		return (0);
unknown's avatar
unknown committed
532

unknown's avatar
unknown committed
533 534 535 536 537 538 539 540 541 542 543 544 545
	/*
	 * If we need to pre- or post-process a file's pages on I/O, set the
	 * file type.  If it's a hash file, always call the 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.
	 */
	switch (dbp->type) {
	case DB_BTREE:
	case DB_RECNO:
		ftype = F_ISSET(dbp, DB_AM_SWAP | DB_AM_ENCRYPT | DB_AM_CHKSUM)
		    ? DB_FTYPE_SET : DB_FTYPE_NOTSET;
unknown's avatar
unknown committed
546 547 548
		clear_len = CRYPTO_ON(dbenv) ?
		    (dbp->pgsize != 0 ? dbp->pgsize : DB_CLEARLEN_NOTSET) :
		    DB_PAGE_DB_LEN;
unknown's avatar
unknown committed
549 550 551
		break;
	case DB_HASH:
		ftype = DB_FTYPE_SET;
unknown's avatar
unknown committed
552 553 554
		clear_len = CRYPTO_ON(dbenv) ?
		    (dbp->pgsize != 0 ? dbp->pgsize : DB_CLEARLEN_NOTSET) :
		    DB_PAGE_DB_LEN;
unknown's avatar
unknown committed
555 556 557 558 559
		break;
	case DB_QUEUE:
		ftype = F_ISSET(dbp,
		    DB_AM_SWAP | DB_AM_ENCRYPT | DB_AM_CHKSUM) ?
		    DB_FTYPE_SET : DB_FTYPE_NOTSET;
unknown's avatar
unknown committed
560 561 562 563 564 565 566 567

		/*
		 * If we came in here without a pagesize set, then we need
		 * to mark the in-memory handle as having clear_len not
		 * set, because we don't really know the clear length or
		 * the page size yet (since the file doesn't yet exist).
		 */
		clear_len = dbp->pgsize != 0 ? dbp->pgsize : DB_CLEARLEN_NOTSET;
unknown's avatar
unknown committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
		break;
	case DB_UNKNOWN:
		/*
		 * If we're running in the verifier, our database might
		 * be corrupt and we might not know its type--but we may
		 * still want to be able to verify and salvage.
		 *
		 * If we can't identify the type, it's not going to be safe
		 * to call __db_pgin--we pretty much have to give up all
		 * hope of salvaging cross-endianness.  Proceed anyway;
		 * at worst, the database will just appear more corrupt
		 * than it actually is, but at best, we may be able
		 * to salvage some data even with no metadata page.
		 */
		if (F_ISSET(dbp, DB_AM_VERIFYING)) {
			ftype = DB_FTYPE_NOTSET;
			clear_len = DB_PAGE_DB_LEN;
			break;
		}
unknown's avatar
unknown committed
587 588 589 590 591 592 593 594 595 596 597 598

		/*
		 * This might be an in-memory file and we won't know its
		 * file type until after we open it and read the meta-data
		 * page.
		 */
		if (F_ISSET(dbp, DB_AM_INMEM)) {
			clear_len = DB_CLEARLEN_NOTSET;
			ftype = DB_FTYPE_NOTSET;
			lsn_off = DB_LSN_OFF_NOTSET;
			break;
		}
unknown's avatar
unknown committed
599 600 601 602 603 604
		/* FALLTHROUGH */
	default:
		return (__db_unknown_type(dbenv, "DB->open", dbp->type));
	}

	mpf = dbp->mpf;
unknown's avatar
unknown committed
605

unknown's avatar
unknown committed
606 607 608 609 610
	memset(nullfid, 0, DB_FILE_ID_LEN);
	fidset = memcmp(nullfid, dbp->fileid, DB_FILE_ID_LEN);
	if (fidset)
		(void)__memp_set_fileid(mpf, dbp->fileid);

unknown's avatar
unknown committed
611 612
	(void)__memp_set_clear_len(mpf, clear_len);
	(void)__memp_set_ftype(mpf, ftype);
unknown's avatar
unknown committed
613
	(void)__memp_set_lsn_offset(mpf, lsn_off);
unknown's avatar
unknown committed
614

unknown's avatar
unknown committed
615 616 617 618 619 620 621 622 623
	pginfo.db_pagesize = dbp->pgsize;
	pginfo.flags =
	    F_ISSET(dbp, (DB_AM_CHKSUM | DB_AM_ENCRYPT | DB_AM_SWAP));
	pginfo.type = dbp->type;
	pgcookie.data = &pginfo;
	pgcookie.size = sizeof(DB_PGINFO);
	(void)__memp_set_pgcookie(mpf, &pgcookie);

	if ((ret = __memp_fopen(mpf, NULL, fname,
unknown's avatar
unknown committed
624 625
	    LF_ISSET(DB_CREATE | DB_DURABLE_UNKNOWN |
		DB_NOMMAP | DB_ODDFILESIZE | DB_RDONLY | DB_TRUNCATE) |
unknown's avatar
unknown committed
626 627
	    (F_ISSET(dbenv, DB_ENV_DIRECT_DB) ? DB_DIRECT : 0) |
	    (F_ISSET(dbp, DB_AM_NOT_DURABLE) ? DB_TXN_NOT_DURABLE : 0),
unknown's avatar
unknown committed
628 629 630 631 632 633 634 635 636
	    0, dbp->pgsize)) != 0) {
		/*
		 * The open didn't work; we need to reset the mpf,
		 * retaining the in-memory semantics (if any).
		 */
		(void)__memp_fclose(dbp->mpf, 0);
		(void)__memp_fcreate(dbenv, &dbp->mpf);
		if (F_ISSET(dbp, DB_AM_INMEM))
			MAKE_INMEM(dbp);
unknown's avatar
unknown committed
637
		return (ret);
unknown's avatar
unknown committed
638 639 640 641 642 643 644 645 646 647 648 649 650 651
	}

	/*
	 * Set the open flag.  We use it to mean that the dbp has gone
	 * through mpf setup, including dbreg_register.  Also, below,
	 * the underlying access method open functions may want to do
	 * things like acquire cursors, so the open flag has to be set
	 * before calling them.
	 */
	F_SET(dbp, DB_AM_OPEN_CALLED);
	if (!fidset && fname != NULL) {
		(void)__memp_get_fileid(dbp->mpf, dbp->fileid);
		dbp->preserve_fid = 1;
	}
unknown's avatar
unknown committed
652 653

	return (0);
unknown's avatar
unknown committed
654 655 656
}

/*
unknown's avatar
unknown committed
657 658
 * __db_close --
 *	DB->close method.
unknown's avatar
unknown committed
659
 *
unknown's avatar
unknown committed
660
 * PUBLIC: int __db_close __P((DB *, DB_TXN *, u_int32_t));
unknown's avatar
unknown committed
661 662
 */
int
unknown's avatar
unknown committed
663
__db_close(dbp, txn, flags)
unknown's avatar
unknown committed
664 665 666 667 668
	DB *dbp;
	DB_TXN *txn;
	u_int32_t flags;
{
	DB_ENV *dbenv;
unknown's avatar
unknown committed
669
	int db_ref, deferred_close, ret, t_ret;
unknown's avatar
unknown committed
670 671

	dbenv = dbp->dbenv;
unknown's avatar
unknown committed
672
	deferred_close = ret = 0;
unknown's avatar
unknown committed
673 674

	/*
unknown's avatar
unknown committed
675 676 677 678 679
	 * Validate arguments, but as a DB handle destructor, we can't fail.
	 *
	 * Check for consistent transaction usage -- ignore errors.  Only
	 * internal callers specify transactions, so it's a serious problem
	 * if we get error messages.
unknown's avatar
unknown committed
680
	 */
unknown's avatar
unknown committed
681 682 683
	if (txn != NULL)
		(void)__db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0);

unknown's avatar
unknown committed
684
	/* Refresh the structure and close any underlying resources. */
unknown's avatar
unknown committed
685
	ret = __db_refresh(dbp, txn, flags, &deferred_close, 0);
unknown's avatar
unknown committed
686 687

	/*
unknown's avatar
unknown committed
688 689
	 * If we've deferred the close because the logging of the close failed,
	 * return our failure right away without destroying the handle.
unknown's avatar
unknown committed
690
	 */
unknown's avatar
unknown committed
691 692
	if (deferred_close)
		return (ret);
unknown's avatar
unknown committed
693

unknown's avatar
unknown committed
694 695 696 697 698 699 700 701 702 703 704
	/* !!!
	 * This code has an apparent race between the moment we read and
	 * decrement dbenv->db_ref and the moment we check whether it's 0.
	 * However, if the environment is DBLOCAL, the user shouldn't have a
	 * reference to the dbenv handle anyway;  the only way we can get
	 * multiple dbps sharing a local dbenv is if we open them internally
	 * during something like a subdatabase open.  If any such thing is
	 * going on while the user is closing the original dbp with a local
	 * dbenv, someone's already badly screwed up, so there's no reason
	 * to bother engineering around this possibility.
	 */
unknown's avatar
unknown committed
705
	MUTEX_LOCK(dbenv, dbenv->mtx_dblist);
unknown's avatar
unknown committed
706
	db_ref = --dbenv->db_ref;
unknown's avatar
unknown committed
707
	MUTEX_UNLOCK(dbenv, dbenv->mtx_dblist);
unknown's avatar
unknown committed
708
	if (F_ISSET(dbenv, DB_ENV_DBLOCAL) && db_ref == 0 &&
unknown's avatar
unknown committed
709
	    (t_ret = __env_close(dbenv, 0)) != 0 && ret == 0)
unknown's avatar
unknown committed
710
		ret = t_ret;
unknown's avatar
unknown committed
711

unknown's avatar
unknown committed
712 713 714
	/* Free the database handle. */
	memset(dbp, CLEAR_BYTE, sizeof(*dbp));
	__os_free(dbenv, dbp);
unknown's avatar
unknown committed
715

unknown's avatar
unknown committed
716 717
	return (ret);
}
unknown's avatar
unknown committed
718

unknown's avatar
unknown committed
719 720 721 722 723 724 725 726
/*
 * __db_refresh --
 *	Refresh the DB structure, releasing any allocated resources.
 * This does most of the work of closing files now because refresh
 * is what is used during abort processing (since we can't destroy
 * the actual handle) and during abort processing, we may have a
 * fully opened handle.
 *
unknown's avatar
unknown committed
727
 * PUBLIC: int __db_refresh __P((DB *, DB_TXN *, u_int32_t, int *, int));
unknown's avatar
unknown committed
728 729
 */
int
unknown's avatar
unknown committed
730
__db_refresh(dbp, txn, flags, deferred_closep, reuse)
unknown's avatar
unknown committed
731 732 733
	DB *dbp;
	DB_TXN *txn;
	u_int32_t flags;
unknown's avatar
unknown committed
734
	int *deferred_closep, reuse;
unknown's avatar
unknown committed
735 736 737 738 739
{
	DB *sdbp;
	DBC *dbc;
	DB_ENV *dbenv;
	DB_LOCKREQ lreq;
unknown's avatar
unknown committed
740 741 742
	REGENV *renv;
	REGINFO *infop;
	u_int32_t save_flags;
unknown's avatar
unknown committed
743
	int resync, ret, t_ret;
unknown's avatar
unknown committed
744

unknown's avatar
unknown committed
745
	ret = 0;
unknown's avatar
unknown committed
746 747

	dbenv = dbp->dbenv;
unknown's avatar
unknown committed
748 749 750 751 752
	infop = dbenv->reginfo;
	if (infop != NULL)
		renv = infop->primary;
	else
		renv = NULL;
unknown's avatar
unknown committed
753

unknown's avatar
unknown committed
754 755 756
	/* If never opened, or not currently open, it's easy. */
	if (!F_ISSET(dbp, DB_AM_OPEN_CALLED))
		goto never_opened;
unknown's avatar
unknown committed
757 758

	/*
unknown's avatar
unknown committed
759 760 761 762 763 764 765
	 * If we have any secondary indices, disassociate them from us.
	 * We don't bother with the mutex here;  it only protects some
	 * of the ops that will make us core-dump mid-close anyway, and
	 * if you're trying to do something with a secondary *while* you're
	 * closing the primary, you deserve what you get.  The disassociation
	 * is mostly done just so we can close primaries and secondaries in
	 * any order--but within one thread of control.
unknown's avatar
unknown committed
766
	 */
unknown's avatar
unknown committed
767 768 769 770 771
	for (sdbp = LIST_FIRST(&dbp->s_secondaries);
	    sdbp != NULL; sdbp = LIST_NEXT(sdbp, s_links)) {
		LIST_REMOVE(sdbp, s_links);
		if ((t_ret = __db_disassociate(sdbp)) != 0 && ret == 0)
			ret = t_ret;
unknown's avatar
unknown committed
772 773 774
	}

	/*
unknown's avatar
unknown committed
775 776 777
	 * Sync the underlying access method.  Do before closing the cursors
	 * because DB->sync allocates cursors in order to write Recno backing
	 * source text files.
unknown's avatar
unknown committed
778 779 780 781
	 *
	 * Sync is slow on some systems, notably Solaris filesystems where the
	 * entire buffer cache is searched.  If we're in recovery, don't flush
	 * the file, it's not necessary.
unknown's avatar
unknown committed
782
	 */
unknown's avatar
unknown committed
783 784 785
	if (!LF_ISSET(DB_NOSYNC) &&
	    !F_ISSET(dbp, DB_AM_DISCARD | DB_AM_RECOVER) &&
	    (t_ret = __db_sync(dbp)) != 0 && ret == 0)
unknown's avatar
unknown committed
786 787 788
		ret = t_ret;

	/*
unknown's avatar
unknown committed
789 790 791 792 793
	 * Go through the active cursors and call the cursor recycle routine,
	 * which resolves pending operations and moves the cursors onto the
	 * free list.  Then, walk the free list and call the cursor destroy
	 * routine.  Note that any failure on a close is considered "really
	 * bad" and we just break out of the loop and force forward.
unknown's avatar
unknown committed
794
	 */
unknown's avatar
unknown committed
795
	resync = TAILQ_FIRST(&dbp->active_queue) == NULL ? 0 : 1;
unknown's avatar
unknown committed
796
	while ((dbc = TAILQ_FIRST(&dbp->active_queue)) != NULL)
unknown's avatar
unknown committed
797
		if ((t_ret = __db_c_close(dbc)) != 0) {
unknown's avatar
unknown committed
798 799 800
			if (ret == 0)
				ret = t_ret;
			break;
unknown's avatar
unknown committed
801 802
		}

unknown's avatar
unknown committed
803 804 805 806 807
	while ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL)
		if ((t_ret = __db_c_destroy(dbc)) != 0) {
			if (ret == 0)
				ret = t_ret;
			break;
unknown's avatar
unknown committed
808 809 810
		}

	/*
unknown's avatar
unknown committed
811 812 813
	 * Close any outstanding join cursors.  Join cursors destroy themselves
	 * on close and have no separate destroy routine.  We don't have to set
	 * the resync flag here, because join cursors aren't write cursors.
unknown's avatar
unknown committed
814
	 */
unknown's avatar
unknown committed
815
	while ((dbc = TAILQ_FIRST(&dbp->join_queue)) != NULL)
unknown's avatar
unknown committed
816
		if ((t_ret = __db_join_close(dbc)) != 0) {
unknown's avatar
unknown committed
817 818 819 820
			if (ret == 0)
				ret = t_ret;
			break;
		}
unknown's avatar
unknown committed
821 822

	/*
unknown's avatar
unknown committed
823 824 825
	 * Sync the memory pool, even though we've already called DB->sync,
	 * because closing cursors can dirty pages by deleting items they
	 * referenced.
unknown's avatar
unknown committed
826 827 828 829
	 *
	 * Sync is slow on some systems, notably Solaris filesystems where the
	 * entire buffer cache is searched.  If we're in recovery, don't flush
	 * the file, it's not necessary.
unknown's avatar
unknown committed
830
	 */
unknown's avatar
unknown committed
831 832 833
	if (resync && !LF_ISSET(DB_NOSYNC) &&
	    !F_ISSET(dbp, DB_AM_DISCARD | DB_AM_RECOVER) &&
	    (t_ret = __memp_fsync(dbp->mpf)) != 0 && ret == 0)
unknown's avatar
unknown committed
834 835
		ret = t_ret;

unknown's avatar
unknown committed
836
never_opened:
unknown's avatar
unknown committed
837 838 839 840 841 842 843 844 845 846 847
	/*
	 * At this point, we haven't done anything to render the DB
	 * handle unusable, at least by a transaction abort.  Take the
	 * opportunity now to log the file close.  If this log fails
	 * and we're in a transaction, we have to bail out of the attempted
	 * close; we'll need a dbp in order to successfully abort the
	 * transaction, and we can't conjure a new one up because we haven't
	 * gotten out the dbreg_register record that represents the close.
	 * In this case, we put off actually closing the dbp until we've
	 * performed the abort.
	 */
unknown's avatar
unknown committed
848
	if (!reuse && LOGGING_ON(dbp->dbenv)) {
unknown's avatar
unknown committed
849 850
		/*
		 * Discard the log file id, if any.  We want to log the close
unknown's avatar
unknown committed
851 852
		 * if and only if this is not a recovery dbp or a client dbp,
		 * or a dead dbp handle.
unknown's avatar
unknown committed
853
		 */
unknown's avatar
unknown committed
854 855 856
		DB_ASSERT(renv != NULL);
		if (F_ISSET(dbp, DB_AM_RECOVER) || IS_REP_CLIENT(dbenv) ||
		    dbp->timestamp != renv->rep_timestamp)
unknown's avatar
unknown committed
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
			t_ret = __dbreg_revoke_id(dbp, 0, DB_LOGFILEID_INVALID);
		else {
			if ((t_ret = __dbreg_close_id(dbp,
			    txn, DBREG_CLOSE)) != 0 && txn != NULL) {
				/*
				 * We're in a txn and the attempt to log the
				 * close failed;  let the txn subsystem know
				 * that we need to destroy this dbp once we're
				 * done with the abort, then bail from the
				 * close.
				 *
				 * Note that if the attempt to put off the
				 * close -also- fails--which it won't unless
				 * we're out of heap memory--we're really
				 * screwed.  Panic.
				 */
				if ((ret =
				    __txn_closeevent(dbenv, txn, dbp)) != 0)
					return (__db_panic(dbenv, ret));
				if (deferred_closep != NULL)
					*deferred_closep = 1;
				return (t_ret);
			}
unknown's avatar
unknown committed
880 881 882 883 884 885 886 887 888 889 890
			/*
			 * If dbreg_close_id failed and we were not in a
			 * transaction, then we need to finish this close
			 * because the caller can't do anything with the
			 * handle after we return an error.  We rely on
			 * dbreg_close_id to mark the entry in some manner
			 * so that we do not do a clean shutdown of this
			 * environment.  If shutdown isn't clean, then the
			 * application *must* run recovery and that will
			 * generate the RCLOSE record.
			 */
unknown's avatar
unknown committed
891 892 893 894 895 896 897 898 899 900
		}

		if (ret == 0)
			ret = t_ret;

		/* Discard the log FNAME. */
		if ((t_ret = __dbreg_teardown(dbp)) != 0 && ret == 0)
			ret = t_ret;
	}

unknown's avatar
unknown committed
901 902 903 904
	/* Close any handle we've been holding since the open.  */
	if (dbp->saved_open_fhp != NULL &&
	    (t_ret = __os_closehandle(dbenv, dbp->saved_open_fhp)) != 0 &&
	    ret == 0)
unknown's avatar
unknown committed
905 906 907
		ret = t_ret;

	/*
unknown's avatar
unknown committed
908 909 910 911 912 913
	 * Remove this DB handle from the DB_ENV's dblist, if it's been added.
	 *
	 * Close our reference to the underlying cache while locked, we don't
	 * want to race with a thread searching for our underlying cache link
	 * while opening a DB handle.
	 */
unknown's avatar
unknown committed
914 915
	MUTEX_LOCK(dbenv, dbenv->mtx_dblist);
	if (!reuse && dbp->dblistlinks.le_prev != NULL) {
unknown's avatar
unknown committed
916 917 918 919 920 921 922 923 924 925 926
		LIST_REMOVE(dbp, dblistlinks);
		dbp->dblistlinks.le_prev = NULL;
	}

	/* Close the memory pool file handle. */
	if (dbp->mpf != NULL) {
		if ((t_ret = __memp_fclose(dbp->mpf,
		    F_ISSET(dbp, DB_AM_DISCARD) ? DB_MPOOL_DISCARD : 0)) != 0 &&
		    ret == 0)
			ret = t_ret;
		dbp->mpf = NULL;
unknown's avatar
unknown committed
927 928 929 930
		if (reuse &&
		    (t_ret = __memp_fcreate(dbenv, &dbp->mpf)) != 0 &&
		    ret == 0)
		    	ret = t_ret;
unknown's avatar
unknown committed
931 932
	}

unknown's avatar
unknown committed
933
	MUTEX_UNLOCK(dbenv, dbenv->mtx_dblist);
unknown's avatar
unknown committed
934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966

	/*
	 * Call the access specific close function.
	 *
	 * We do this here rather than in __db_close as we need to do this when
	 * aborting an open so that file descriptors are closed and abort of
	 * renames can succeed on platforms that lock open files (such as
	 * Windows).  In particular, we need to ensure that all the extents
	 * associated with a queue are closed so that queue renames can be
	 * aborted.
	 *
	 * It is also important that we do this before releasing the handle
	 * lock, because dbremove and dbrename assume that once they have the
	 * handle lock, it is safe to modify the underlying file(s).
	 *
	 * !!!
	 * Because of where these functions are called in the DB handle close
	 * process, these routines can't do anything that would dirty pages or
	 * otherwise affect closing down the database.  Specifically, we can't
	 * abort and recover any of the information they control.
	 */
	if ((t_ret = __bam_db_close(dbp)) != 0 && ret == 0)
		ret = t_ret;
	if ((t_ret = __ham_db_close(dbp)) != 0 && ret == 0)
		ret = t_ret;
	if ((t_ret = __qam_db_close(dbp, dbp->flags)) != 0 && ret == 0)
		ret = t_ret;

	/*
	 * !!!
	 * At this point, the access-method specific information has been
	 * freed.  From now on, we can use the dbp, but not touch any
	 * access-method specific data.
unknown's avatar
unknown committed
967
	 */
unknown's avatar
unknown committed
968

unknown's avatar
unknown committed
969
	if (!reuse && dbp->lid != DB_LOCK_INVALIDID) {
unknown's avatar
unknown committed
970 971 972 973 974 975
		/* We may have pending trade operations on this dbp. */
		if (txn != NULL)
			__txn_remlock(dbenv, txn, &dbp->handle_lock, dbp->lid);

		/* We may be holding the handle lock; release it. */
		lreq.op = DB_LOCK_PUT_ALL;
unknown's avatar
unknown committed
976
		lreq.obj = NULL;
unknown's avatar
unknown committed
977 978 979 980
		if ((t_ret = __lock_vec(dbenv,
		    dbp->lid, 0, &lreq, 1, NULL)) != 0 && ret == 0)
			ret = t_ret;

unknown's avatar
unknown committed
981
		if ((t_ret = __lock_id_free(dbenv, dbp->lid)) != 0 && ret == 0)
unknown's avatar
unknown committed
982 983 984 985
			ret = t_ret;
		dbp->lid = DB_LOCK_INVALIDID;
		LOCK_INIT(dbp->handle_lock);
	}
unknown's avatar
unknown committed
986

unknown's avatar
unknown committed
987 988 989 990 991 992 993
	/*
	 * If this is a temporary file (un-named in-memory file), then
	 * discard the locker ID allocated as the fileid.
	 */
	if (LOCKING_ON(dbenv) &&
	    F_ISSET(dbp, DB_AM_INMEM) && !dbp->preserve_fid &&
	    *(u_int32_t *)dbp->fileid != DB_LOCK_INVALIDID &&
unknown's avatar
unknown committed
994 995
	    (t_ret = __lock_id_free(dbenv, *(u_int32_t *)dbp->fileid)) != 0 &&
	    ret == 0)
unknown's avatar
unknown committed
996 997
		ret = t_ret;

unknown's avatar
unknown committed
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
	if (reuse) {
		/*
		 * If we are reusing this dbp, then we're done now. Re-init
		 * the handle, preserving important flags, and then return.
		 * This code is borrowed from __db_init, which does more
		 * than we can do here.
		 */
		save_flags = F_ISSET(dbp, DB_AM_INMEM | DB_AM_TXN);

		/*
		 * XXX If this is an XA handle, we'll want to specify 
		 * DB_XA_CREATE.
		 */
		if ((ret = __bam_db_create(dbp)) != 0)
			return (ret);
		if ((ret = __ham_db_create(dbp)) != 0)
			return (ret);
		if ((ret = __qam_db_create(dbp)) != 0)
			return (ret);

		/* Restore flags */
		dbp->flags = dbp->orig_flags | save_flags;

		if (FLD_ISSET(save_flags, DB_AM_INMEM)) {
			/*
			 * If this is inmem, then it may have a fileid
			 * even if it was never opened, and we need to
			 * clear out that fileid.
			 */
			memset(dbp->fileid, 0, sizeof(dbp->fileid));
			MAKE_INMEM(dbp);
		}
		return (ret);
	}

unknown's avatar
unknown committed
1033
	dbp->type = DB_UNKNOWN;
unknown's avatar
unknown committed
1034

unknown's avatar
unknown committed
1035
	/* Discard the thread mutex. */
unknown's avatar
unknown committed
1036 1037
	if ((t_ret = __mutex_free(dbenv, &dbp->mutex)) != 0 && ret == 0)
		ret = t_ret;
unknown's avatar
unknown committed
1038

unknown's avatar
unknown committed
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
	/* Discard any memory allocated for the file and database names. */
	if (dbp->fname != NULL) {
		__os_free(dbp->dbenv, dbp->fname);
		dbp->fname = NULL;
	}
	if (dbp->dname != NULL) {
		__os_free(dbp->dbenv, dbp->dname);
		dbp->dname = NULL;
	}

unknown's avatar
unknown committed
1049 1050 1051 1052 1053 1054 1055
	/* Discard any memory used to store returned data. */
	if (dbp->my_rskey.data != NULL)
		__os_free(dbp->dbenv, dbp->my_rskey.data);
	if (dbp->my_rkey.data != NULL)
		__os_free(dbp->dbenv, dbp->my_rkey.data);
	if (dbp->my_rdata.data != NULL)
		__os_free(dbp->dbenv, dbp->my_rdata.data);
unknown's avatar
unknown committed
1056

unknown's avatar
unknown committed
1057 1058 1059 1060
	/* For safety's sake;  we may refresh twice. */
	memset(&dbp->my_rskey, 0, sizeof(DBT));
	memset(&dbp->my_rkey, 0, sizeof(DBT));
	memset(&dbp->my_rdata, 0, sizeof(DBT));
unknown's avatar
unknown committed
1061

unknown's avatar
unknown committed
1062 1063 1064 1065 1066 1067 1068
	/* Clear out fields that normally get set during open. */
	memset(dbp->fileid, 0, sizeof(dbp->fileid));
	dbp->adj_fileid = 0;
	dbp->meta_pgno = 0;
	dbp->cur_lid = DB_LOCK_INVALIDID;
	dbp->associate_lid = DB_LOCK_INVALIDID;
	dbp->cl_id = 0;
unknown's avatar
unknown committed
1069
	dbp->open_flags = 0;
unknown's avatar
unknown committed
1070

unknown's avatar
unknown committed
1071 1072 1073 1074 1075 1076 1077 1078 1079
	/*
	 * If we are being refreshed with a txn specified, then we need
	 * to make sure that we clear out the lock handle field, because
	 * releasing all the locks for this transaction will release this
	 * lock and we don't want close to stumble upon this handle and
	 * try to close it.
	 */
	if (txn != NULL)
		LOCK_INIT(dbp->handle_lock);
unknown's avatar
unknown committed
1080

unknown's avatar
unknown committed
1081 1082
	/* Reset flags to whatever the user configured. */
	dbp->flags = dbp->orig_flags;
unknown's avatar
unknown committed
1083 1084 1085 1086 1087 1088

	return (ret);
}

/*
 * __db_log_page
unknown's avatar
unknown committed
1089
 *	Log a meta-data or root page during a subdatabase create operation.
unknown's avatar
unknown committed
1090
 *
unknown's avatar
unknown committed
1091
 * PUBLIC: int __db_log_page __P((DB *, DB_TXN *, DB_LSN *, db_pgno_t, PAGE *));
unknown's avatar
unknown committed
1092 1093
 */
int
unknown's avatar
unknown committed
1094
__db_log_page(dbp, txn, lsn, pgno, page)
unknown's avatar
unknown committed
1095
	DB *dbp;
unknown's avatar
unknown committed
1096
	DB_TXN *txn;
unknown's avatar
unknown committed
1097 1098 1099 1100
	DB_LSN *lsn;
	db_pgno_t pgno;
	PAGE *page;
{
unknown's avatar
unknown committed
1101
	DBT page_dbt;
unknown's avatar
unknown committed
1102 1103 1104
	DB_LSN new_lsn;
	int ret;

unknown's avatar
unknown committed
1105
	if (!LOGGING_ON(dbp->dbenv) || txn == NULL)
unknown's avatar
unknown committed
1106 1107 1108 1109 1110 1111
		return (0);

	memset(&page_dbt, 0, sizeof(page_dbt));
	page_dbt.size = dbp->pgsize;
	page_dbt.data = page;

unknown's avatar
unknown committed
1112
	ret = __crdel_metasub_log(dbp, txn, &new_lsn, 0, pgno, &page_dbt, lsn);
unknown's avatar
unknown committed
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123

	if (ret == 0)
		page->lsn = new_lsn;
	return (ret);
}

/*
 * __db_backup_name
 *	Create the backup file name for a given file.
 *
 * PUBLIC: int __db_backup_name __P((DB_ENV *,
unknown's avatar
unknown committed
1124
 * PUBLIC:     const char *, DB_TXN *, char **));
unknown's avatar
unknown committed
1125 1126
 */
#undef	BACKUP_PREFIX
unknown's avatar
unknown committed
1127
#define	BACKUP_PREFIX	"__db"
unknown's avatar
unknown committed
1128 1129

#undef	MAX_LSN_TO_TEXT
unknown's avatar
unknown committed
1130 1131
#define	MAX_LSN_TO_TEXT	17

unknown's avatar
unknown committed
1132
int
unknown's avatar
unknown committed
1133
__db_backup_name(dbenv, name, txn, backup)
unknown's avatar
unknown committed
1134 1135
	DB_ENV *dbenv;
	const char *name;
unknown's avatar
unknown committed
1136
	DB_TXN *txn;
unknown's avatar
unknown committed
1137 1138
	char **backup;
{
unknown's avatar
unknown committed
1139
	DB_LSN lsn;
unknown's avatar
unknown committed
1140
	size_t len;
unknown's avatar
unknown committed
1141
	int ret;
unknown's avatar
unknown committed
1142 1143
	char *p, *retp;

unknown's avatar
unknown committed
1144 1145 1146 1147 1148
	/*
	 * Part of the name may be a full path, so we need to make sure that
	 * we allocate enough space for it, even in the case where we don't
	 * use the entire filename for the backup name.
	 */
unknown's avatar
unknown committed
1149
	len = strlen(name) + strlen(BACKUP_PREFIX) + 1 + MAX_LSN_TO_TEXT;
unknown's avatar
unknown committed
1150 1151 1152
	if ((ret = __os_malloc(dbenv, len, &retp)) != 0)
		return (ret);

unknown's avatar
unknown committed
1153
	/*
unknown's avatar
unknown committed
1154
	 * Create the name.  Backup file names are in one of three forms:
unknown's avatar
unknown committed
1155
	 *
unknown's avatar
unknown committed
1156 1157
	 *	In a transactional env: __db.LSN(8).LSN(8)
	 * and
unknown's avatar
unknown committed
1158 1159 1160
	 *	In VXWORKS (where we want 8.3 support)
	 * and
	 *	in any other non-transactional env: __db.FILENAME
unknown's avatar
unknown committed
1161
	 *
unknown's avatar
unknown committed
1162 1163
	 * If the transaction doesn't have a current LSN, we write a dummy
	 * log record to force it, so we ensure all tmp names are unique.
unknown's avatar
unknown committed
1164
	 *
unknown's avatar
unknown committed
1165 1166 1167
	 * In addition, the name passed may contain an env-relative path.
	 * In that case, put the __db. in the right place (in the last
	 * component of the pathname).
unknown's avatar
unknown committed
1168 1169 1170 1171 1172 1173
	 *
	 * There are four cases here:
	 *	1. simple path w/out transaction
	 *	2. simple path + transaction
	 *	3. multi-component path w/out transaction
	 *	4. multi-component path + transaction
unknown's avatar
unknown committed
1174
	 */
unknown's avatar
unknown committed
1175
	p = __db_rpath(name);
unknown's avatar
unknown committed
1176 1177 1178 1179
	if (txn == NULL) {
#ifdef HAVE_VXWORKS
	    { int i, n;
		/* On VxWorks we must support 8.3 names. */
unknown's avatar
unknown committed
1180
		if (p == NULL)				/* Case 1. */
unknown's avatar
unknown committed
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
			n = snprintf(retp,
			    len, "%s%.4s.tmp", BACKUP_PREFIX, name);
		else				/* Case 3. */
			n = snprintf(retp, len, "%.*s%s%.4s.tmp",
			    (int)(p - name) + 1, name, BACKUP_PREFIX, p + 1);

		/*
		 * Overwrite "." in the characters copied from the name.
		 * If we backup 8 characters from the end, we're guaranteed
		 * to a) include the four bytes we copied from the name
		 * and b) not run off the beginning of the string.
		 */
		for (i = 0, p = (retp + n) - 8; i < 4; p++, i++)
			if (*p == '.')
				*p = '_';
	    }
#else
		if (p == NULL)				/* Case 1. */
			snprintf(retp, len, "%s.%s", BACKUP_PREFIX, name);
unknown's avatar
unknown committed
1200
		else					/* Case 3. */
unknown's avatar
unknown committed
1201
			snprintf(retp, len, "%.*s%s.%s",
unknown's avatar
unknown committed
1202
			    (int)(p - name) + 1, name, BACKUP_PREFIX, p + 1);
unknown's avatar
unknown committed
1203 1204 1205 1206
#endif
	} else {
		lsn = ((TXN_DETAIL *)txn->td)->last_lsn;
		if (IS_ZERO_LSN(lsn)) {
unknown's avatar
unknown committed
1207
			/*
unknown's avatar
unknown committed
1208 1209 1210 1211 1212
			 * Write dummy log record.   The two choices for dummy
			 * log records are __db_noop_log and __db_debug_log;
			 * unfortunately __db_noop_log requires a valid dbp,
			 * and we aren't guaranteed to be able to pass one in
			 * here.
unknown's avatar
unknown committed
1213
			 */
unknown's avatar
unknown committed
1214 1215 1216
			if ((ret = __db_debug_log(dbenv,
			    txn, &lsn, 0, NULL, 0, NULL, NULL, 0)) != 0) {
				__os_free(dbenv, retp);
unknown's avatar
unknown committed
1217
				return (ret);
unknown's avatar
unknown committed
1218
			}
unknown's avatar
unknown committed
1219
		}
unknown's avatar
unknown committed
1220

unknown's avatar
unknown committed
1221
		if (p == NULL)				/* Case 2. */
unknown's avatar
unknown committed
1222
			snprintf(retp, len,
unknown's avatar
unknown committed
1223
			    "%s.%x.%x", BACKUP_PREFIX, lsn.file, lsn.offset);
unknown's avatar
unknown committed
1224 1225 1226
		else					/* Case 4. */
			snprintf(retp, len, "%.*s%x.%x",
			    (int)(p - name) + 1, name, lsn.file, lsn.offset);
unknown's avatar
unknown committed
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
	}

	*backup = retp;
	return (0);
}

/*
 * __dblist_get --
 *	Get the first element of dbenv->dblist with
 *	dbp->adj_fileid matching adjid.
 *
 * PUBLIC: DB *__dblist_get __P((DB_ENV *, u_int32_t));
 */
DB *
__dblist_get(dbenv, adjid)
	DB_ENV *dbenv;
	u_int32_t adjid;
{
	DB *dbp;

	for (dbp = LIST_FIRST(&dbenv->dblist);
	    dbp != NULL && dbp->adj_fileid != adjid;
	    dbp = LIST_NEXT(dbp, dblistlinks))
		;

	return (dbp);
}

unknown's avatar
unknown committed
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
/*
 * __db_disassociate --
 *	Destroy the association between a given secondary and its primary.
 */
static int
__db_disassociate(sdbp)
	DB *sdbp;
{
	DBC *dbc;
	int ret, t_ret;

	ret = 0;

	sdbp->s_callback = NULL;
	sdbp->s_primary = NULL;
	sdbp->get = sdbp->stored_get;
	sdbp->close = sdbp->stored_close;

	/*
	 * Complain, but proceed, if we have any active cursors.  (We're in
	 * the middle of a close, so there's really no turning back.)
	 */
	if (sdbp->s_refcnt != 1 ||
	    TAILQ_FIRST(&sdbp->active_queue) != NULL ||
	    TAILQ_FIRST(&sdbp->join_queue) != NULL) {
		__db_err(sdbp->dbenv,
    "Closing a primary DB while a secondary DB has active cursors is unsafe");
		ret = EINVAL;
	}
	sdbp->s_refcnt = 0;

	while ((dbc = TAILQ_FIRST(&sdbp->free_queue)) != NULL)
		if ((t_ret = __db_c_destroy(dbc)) != 0 && ret == 0)
			ret = t_ret;

	F_CLR(sdbp, DB_AM_SECONDARY);
	return (ret);
}

unknown's avatar
unknown committed
1294
#ifdef CONFIG_TEST
unknown's avatar
unknown committed
1295 1296 1297 1298
/*
 * __db_testcopy
 *	Create a copy of all backup files and our "main" DB.
 *
unknown's avatar
unknown committed
1299
 * PUBLIC: #ifdef CONFIG_TEST
unknown's avatar
unknown committed
1300 1301
 * PUBLIC: int __db_testcopy __P((DB_ENV *, DB *, const char *));
 * PUBLIC: #endif
unknown's avatar
unknown committed
1302 1303
 */
int
unknown's avatar
unknown committed
1304 1305
__db_testcopy(dbenv, dbp, name)
	DB_ENV *dbenv;
unknown's avatar
unknown committed
1306 1307 1308
	DB *dbp;
	const char *name;
{
unknown's avatar
unknown committed
1309
	DB_MPOOL *dbmp;
unknown's avatar
unknown committed
1310 1311 1312 1313 1314
	DB_MPOOLFILE *mpf;

	DB_ASSERT(dbp != NULL || name != NULL);

	if (name == NULL) {
unknown's avatar
unknown committed
1315
		dbmp = dbenv->mp_handle;
unknown's avatar
unknown committed
1316
		mpf = dbp->mpf;
unknown's avatar
unknown committed
1317
		name = R_ADDR(dbmp->reginfo, mpf->mfp->path_off);
unknown's avatar
unknown committed
1318 1319 1320
	}

	if (dbp != NULL && dbp->type == DB_QUEUE)
unknown's avatar
unknown committed
1321 1322
		return (__qam_testdocopy(dbp, name));
	else
unknown's avatar
unknown committed
1323
		return (__db_testdocopy(dbenv, name));
unknown's avatar
unknown committed
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
}

static int
__qam_testdocopy(dbp, name)
	DB *dbp;
	const char *name;
{
	QUEUE_FILELIST *filelist, *fp;
	char buf[256], *dir;
	int ret;

	filelist = NULL;
unknown's avatar
unknown committed
1336
	if ((ret = __db_testdocopy(dbp->dbenv, name)) != 0)
unknown's avatar
unknown committed
1337 1338 1339 1340 1341 1342 1343 1344 1345
		return (ret);
	if (dbp->mpf != NULL &&
	    (ret = __qam_gen_filelist(dbp, &filelist)) != 0)
		return (ret);

	if (filelist == NULL)
		return (0);
	dir = ((QUEUE *)dbp->q_internal)->dir;
	for (fp = filelist; fp->mpf != NULL; fp++) {
unknown's avatar
unknown committed
1346 1347 1348
		snprintf(buf, sizeof(buf),
		    QUEUE_EXTENT, dir, PATH_SEPARATOR[0], name, fp->id);
		if ((ret = __db_testdocopy(dbp->dbenv, buf)) != 0)
unknown's avatar
unknown committed
1349 1350 1351
			return (ret);
	}

unknown's avatar
unknown committed
1352
	__os_free(dbp->dbenv, filelist);
unknown's avatar
unknown committed
1353 1354 1355 1356 1357 1358 1359 1360 1361
	return (0);
}

/*
 * __db_testdocopy
 *	Create a copy of all backup files and our "main" DB.
 *
 */
static int
unknown's avatar
unknown committed
1362 1363
__db_testdocopy(dbenv, name)
	DB_ENV *dbenv;
unknown's avatar
unknown committed
1364 1365 1366 1367
	const char *name;
{
	size_t len;
	int dircnt, i, ret;
unknown's avatar
unknown committed
1368 1369 1370 1371 1372 1373
	char *backup, *copy, *dir, **namesp, *p, *real_name;

	dircnt = 0;
	copy = backup = NULL;
	namesp = NULL;

unknown's avatar
unknown committed
1374
	/* Get the real backing file name. */
unknown's avatar
unknown committed
1375 1376
	if ((ret = __db_appname(dbenv,
	    DB_APP_DATA, name, 0, NULL, &real_name)) != 0)
unknown's avatar
unknown committed
1377 1378 1379 1380 1381
		return (ret);

	/*
	 * Maximum size of file, including adding a ".afterop".
	 */
unknown's avatar
unknown committed
1382 1383
	len = strlen(real_name) +
	    strlen(BACKUP_PREFIX) + 1 + MAX_LSN_TO_TEXT + 9;
unknown's avatar
unknown committed
1384

unknown's avatar
unknown committed
1385
	if ((ret = __os_malloc(dbenv, len, &copy)) != 0)
unknown's avatar
unknown committed
1386
		goto err;
unknown's avatar
unknown committed
1387

unknown's avatar
unknown committed
1388
	if ((ret = __os_malloc(dbenv, len, &backup)) != 0)
unknown's avatar
unknown committed
1389
		goto err;
unknown's avatar
unknown committed
1390 1391 1392 1393 1394

	/*
	 * First copy the file itself.
	 */
	snprintf(copy, len, "%s.afterop", real_name);
unknown's avatar
unknown committed
1395
	__db_makecopy(dbenv, real_name, copy);
unknown's avatar
unknown committed
1396

unknown's avatar
unknown committed
1397
	if ((ret = __os_strdup(dbenv, real_name, &dir)) != 0)
unknown's avatar
unknown committed
1398
		goto err;
unknown's avatar
unknown committed
1399
	__os_free(dbenv, real_name);
unknown's avatar
unknown committed
1400
	real_name = NULL;
unknown's avatar
unknown committed
1401

unknown's avatar
unknown committed
1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
	/*
	 * Create the name.  Backup file names are of the form:
	 *
	 *	__db.name.0x[lsn-file].0x[lsn-offset]
	 *
	 * which guarantees uniqueness.  We want to look for the
	 * backup name, followed by a '.0x' (so that if they have
	 * files named, say, 'a' and 'abc' we won't match 'abc' when
	 * looking for 'a'.
	 */
unknown's avatar
unknown committed
1412
	snprintf(backup, len, "%s.%s.0x", BACKUP_PREFIX, name);
unknown's avatar
unknown committed
1413 1414 1415 1416 1417 1418 1419

	/*
	 * We need the directory path to do the __os_dirlist.
	 */
	p = __db_rpath(dir);
	if (p != NULL)
		*p = '\0';
unknown's avatar
unknown committed
1420
	ret = __os_dirlist(dbenv, dir, &namesp, &dircnt);
unknown's avatar
unknown committed
1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
#if DIAGNOSTIC
	/*
	 * XXX
	 * To get the memory guard code to work because it uses strlen and we
	 * just moved the end of the string somewhere sooner.  This causes the
	 * guard code to fail because it looks at one byte past the end of the
	 * string.
	 */
	*p = '/';
#endif
unknown's avatar
unknown committed
1431
	__os_free(dbenv, dir);
unknown's avatar
unknown committed
1432
	if (ret != 0)
unknown's avatar
unknown committed
1433
		goto err;
unknown's avatar
unknown committed
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
	for (i = 0; i < dircnt; i++) {
		/*
		 * Need to check if it is a backup file for this.
		 * No idea what namesp[i] may be or how long, so
		 * must use strncmp and not memcmp.  We don't want
		 * to use strcmp either because we are only matching
		 * the first part of the real file's name.  We don't
		 * know its LSN's.
		 */
		if (strncmp(namesp[i], backup, strlen(backup)) == 0) {
unknown's avatar
unknown committed
1444 1445
			if ((ret = __db_appname(dbenv, DB_APP_DATA,
			    namesp[i], 0, NULL, &real_name)) != 0)
unknown's avatar
unknown committed
1446
				goto err;
unknown's avatar
unknown committed
1447 1448 1449 1450 1451 1452 1453

			/*
			 * This should not happen.  Check that old
			 * .afterop files aren't around.
			 * If so, just move on.
			 */
			if (strstr(real_name, ".afterop") != NULL) {
unknown's avatar
unknown committed
1454
				__os_free(dbenv, real_name);
unknown's avatar
unknown committed
1455 1456 1457 1458
				real_name = NULL;
				continue;
			}
			snprintf(copy, len, "%s.afterop", real_name);
unknown's avatar
unknown committed
1459
			__db_makecopy(dbenv, real_name, copy);
unknown's avatar
unknown committed
1460
			__os_free(dbenv, real_name);
unknown's avatar
unknown committed
1461 1462 1463
			real_name = NULL;
		}
	}
unknown's avatar
unknown committed
1464 1465

err:	if (backup != NULL)
unknown's avatar
unknown committed
1466
		__os_free(dbenv, backup);
unknown's avatar
unknown committed
1467
	if (copy != NULL)
unknown's avatar
unknown committed
1468
		__os_free(dbenv, copy);
unknown's avatar
unknown committed
1469
	if (namesp != NULL)
unknown's avatar
unknown committed
1470
		__os_dirfree(dbenv, namesp, dircnt);
unknown's avatar
unknown committed
1471
	if (real_name != NULL)
unknown's avatar
unknown committed
1472
		__os_free(dbenv, real_name);
unknown's avatar
unknown committed
1473 1474 1475 1476
	return (ret);
}

static void
unknown's avatar
unknown committed
1477 1478
__db_makecopy(dbenv, src, dest)
	DB_ENV *dbenv;
unknown's avatar
unknown committed
1479 1480
	const char *src, *dest;
{
unknown's avatar
unknown committed
1481
	DB_FH *rfhp, *wfhp;
unknown's avatar
unknown committed
1482 1483 1484
	size_t rcnt, wcnt;
	char *buf;

unknown's avatar
unknown committed
1485
	rfhp = wfhp = NULL;
unknown's avatar
unknown committed
1486

unknown's avatar
unknown committed
1487
	if (__os_malloc(dbenv, 1024, &buf) != 0)
unknown's avatar
unknown committed
1488 1489
		return;

unknown's avatar
unknown committed
1490
	if (__os_open(dbenv,
unknown's avatar
unknown committed
1491
	    src, DB_OSO_RDONLY, __db_omode(OWNER_RW), &rfhp) != 0)
unknown's avatar
unknown committed
1492
		goto err;
unknown's avatar
unknown committed
1493
	if (__os_open(dbenv, dest,
unknown's avatar
unknown committed
1494
	    DB_OSO_CREATE | DB_OSO_TRUNC, __db_omode(OWNER_RW), &wfhp) != 0)
unknown's avatar
unknown committed
1495 1496 1497
		goto err;

	for (;;)
unknown's avatar
unknown committed
1498 1499
		if (__os_read(dbenv, rfhp, buf, 1024, &rcnt) < 0 || rcnt == 0 ||
		    __os_write(dbenv, wfhp, buf, rcnt, &wcnt) < 0)
unknown's avatar
unknown committed
1500 1501
			break;

unknown's avatar
unknown committed
1502 1503 1504 1505 1506 1507
err:	if (buf != NULL)
		__os_free(dbenv, buf);
	if (rfhp != NULL)
		(void)__os_closehandle(dbenv, rfhp);
	if (wfhp != NULL)
		(void)__os_closehandle(dbenv, wfhp);
unknown's avatar
unknown committed
1508 1509
}
#endif