1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1997,2008 Oracle. All rights reserved. 5 * 6 * $Id: dbreg_util.c,v 12.40 2008/04/02 17:09:26 sue Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/db_page.h" 13#include "dbinc/db_am.h" 14#include "dbinc/fop.h" 15#include "dbinc/log.h" 16#include "dbinc/mp.h" 17#include "dbinc/txn.h" 18 19static int __dbreg_check_master __P((ENV *, u_int8_t *, char *)); 20 21/* 22 * __dbreg_add_dbentry -- 23 * Adds a DB entry to the dbreg DB entry table. 24 * 25 * PUBLIC: int __dbreg_add_dbentry __P((ENV *, DB_LOG *, DB *, int32_t)); 26 */ 27int 28__dbreg_add_dbentry(env, dblp, dbp, ndx) 29 ENV *env; 30 DB_LOG *dblp; 31 DB *dbp; 32 int32_t ndx; 33{ 34 int32_t i; 35 int ret; 36 37 ret = 0; 38 39 MUTEX_LOCK(env, dblp->mtx_dbreg); 40 41 /* 42 * Check if we need to grow the table. Note, ndx is 0-based (the 43 * index into the DB entry table) an dbentry_cnt is 1-based, the 44 * number of available slots. 45 */ 46 if (dblp->dbentry_cnt <= ndx) { 47 if ((ret = __os_realloc(env, 48 (size_t)(ndx + DB_GROW_SIZE) * sizeof(DB_ENTRY), 49 &dblp->dbentry)) != 0) 50 goto err; 51 52 /* Initialize the new entries. */ 53 for (i = dblp->dbentry_cnt; i < ndx + DB_GROW_SIZE; i++) { 54 dblp->dbentry[i].dbp = NULL; 55 dblp->dbentry[i].deleted = 0; 56 } 57 dblp->dbentry_cnt = i; 58 } 59 60 DB_ASSERT(env, dblp->dbentry[ndx].dbp == NULL); 61 dblp->dbentry[ndx].deleted = dbp == NULL; 62 dblp->dbentry[ndx].dbp = dbp; 63 64err: MUTEX_UNLOCK(env, dblp->mtx_dbreg); 65 return (ret); 66} 67 68/* 69 * __dbreg_rem_dbentry 70 * Remove an entry from the DB entry table. 71 * 72 * PUBLIC: int __dbreg_rem_dbentry __P((DB_LOG *, int32_t)); 73 */ 74int 75__dbreg_rem_dbentry(dblp, ndx) 76 DB_LOG *dblp; 77 int32_t ndx; 78{ 79 MUTEX_LOCK(dblp->env, dblp->mtx_dbreg); 80 if (dblp->dbentry_cnt > ndx) { 81 dblp->dbentry[ndx].dbp = NULL; 82 dblp->dbentry[ndx].deleted = 0; 83 } 84 MUTEX_UNLOCK(dblp->env, dblp->mtx_dbreg); 85 86 return (0); 87} 88 89/* 90 * __dbreg_log_files -- 91 * Put a DBREG_CHKPNT/CLOSE log record for each open database. 92 * 93 * PUBLIC: int __dbreg_log_files __P((ENV *, u_int32_t)); 94 */ 95int 96__dbreg_log_files(env, opcode) 97 ENV *env; 98 u_int32_t opcode; 99{ 100 DBT *dbtp, fid_dbt, t; 101 DB_LOG *dblp; 102 DB_LSN r_unused; 103 FNAME *fnp; 104 LOG *lp; 105 int ret; 106 107 dblp = env->lg_handle; 108 lp = dblp->reginfo.primary; 109 110 ret = 0; 111 112 MUTEX_LOCK(env, lp->mtx_filelist); 113 114 SH_TAILQ_FOREACH(fnp, &lp->fq, q, __fname) { 115 /* This id was revoked by a switch in replication master. */ 116 if (fnp->id == DB_LOGFILEID_INVALID) 117 continue; 118 if (fnp->fname_off == INVALID_ROFF) 119 dbtp = NULL; 120 else { 121 memset(&t, 0, sizeof(t)); 122 t.data = R_ADDR(&dblp->reginfo, fnp->fname_off); 123 t.size = (u_int32_t)strlen(t.data) + 1; 124 dbtp = &t; 125 } 126 memset(&fid_dbt, 0, sizeof(fid_dbt)); 127 fid_dbt.data = fnp->ufid; 128 fid_dbt.size = DB_FILE_ID_LEN; 129 /* 130 * Output DBREG_CHKPNT records which will be processed during 131 * the OPENFILES pass of recovery. At the end of recovery we 132 * want to output the files that were open so a future recovery 133 * run will have the correct files open during a backward pass. 134 * For this we output DBREG_RCLOSE records so the files will be 135 * closed on the forward pass. 136 */ 137 if ((ret = __dbreg_register_log(env, NULL, &r_unused, 138 F_ISSET(fnp, DB_FNAME_DURABLE) ? 0 : DB_LOG_NOT_DURABLE, 139 opcode, 140 dbtp, &fid_dbt, fnp->id, fnp->s_type, fnp->meta_pgno, 141 TXN_INVALID)) != 0) 142 break; 143 } 144 145 MUTEX_UNLOCK(env, lp->mtx_filelist); 146 147 return (ret); 148} 149 150/* 151 * __dbreg_close_files -- 152 * Remove the id's of open files and actually close those 153 * files that were opened by the recovery daemon. We sync the 154 * file, unless its mpf pointer has been NULLed by a db_remove or 155 * db_rename. We may not have flushed the log_register record that 156 * closes the file. 157 * 158 * PUBLIC: int __dbreg_close_files __P((ENV *, int)); 159 */ 160int 161__dbreg_close_files(env, do_restored) 162 ENV *env; 163 int do_restored; 164{ 165 DB *dbp; 166 DB_LOG *dblp; 167 int ret, t_ret; 168 int32_t i; 169 170 /* If we haven't initialized logging, we have nothing to do. */ 171 if (!LOGGING_ON(env)) 172 return (0); 173 174 dblp = env->lg_handle; 175 ret = 0; 176 177 MUTEX_LOCK(env, dblp->mtx_dbreg); 178 for (i = 0; i < dblp->dbentry_cnt; i++) { 179 /* 180 * We only want to close dbps that recovery opened. Any 181 * dbps that weren't opened by recovery but show up here 182 * are about to be unconditionally removed from the table. 183 * Before doing so, we need to revoke their log fileids 184 * so that we don't end up leaving around FNAME entries 185 * for dbps that shouldn't have them. 186 */ 187 if ((dbp = dblp->dbentry[i].dbp) != NULL) { 188 /* 189 * It's unsafe to call DB->close or revoke_id 190 * while holding the thread lock, because 191 * we'll call __dbreg_rem_dbentry and grab it again. 192 * 193 * Just drop it. Since dbreg ids go monotonically 194 * upward, concurrent opens should be safe, and the 195 * user should have no business closing files while 196 * we're in this loop anyway--we're in the process of 197 * making all outstanding dbps invalid. 198 */ 199 /* 200 * If we only want to close those FNAMES marked 201 * as restored, check now. 202 */ 203 if (do_restored && 204 !F_ISSET(dbp->log_filename, DB_FNAME_RESTORED)) 205 continue; 206 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 207 if (F_ISSET(dbp, DB_AM_RECOVER)) 208 t_ret = __db_close(dbp, NULL, DB_NOSYNC); 209 else 210 t_ret = __dbreg_revoke_id( 211 dbp, 0, DB_LOGFILEID_INVALID); 212 if (ret == 0) 213 ret = t_ret; 214 MUTEX_LOCK(env, dblp->mtx_dbreg); 215 } 216 217 dblp->dbentry[i].deleted = 0; 218 dblp->dbentry[i].dbp = NULL; 219 } 220 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 221 return (ret); 222} 223 224/* 225 * __dbreg_close_file -- 226 * Close a database file opened by recovery. 227 * PUBLIC: int __dbreg_close_file __P((ENV *, FNAME *)); 228 */ 229int 230__dbreg_close_file(env, fnp) 231 ENV *env; 232 FNAME *fnp; 233{ 234 DB *dbp; 235 DB_LOG *dblp; 236 237 dblp = env->lg_handle; 238 239 dbp = dblp->dbentry[fnp->id].dbp; 240 if (dbp == NULL) 241 return (0); 242 DB_ASSERT(env, dbp->log_filename == fnp); 243 DB_ASSERT(env, F_ISSET(dbp, DB_AM_RECOVER)); 244 return (__db_close(dbp, NULL, DB_NOSYNC)); 245} 246 247/* 248 * __dbreg_mark_restored -- 249 * Mark files when we change replication roles and there are outstanding 250 * prepared txns that may use these files. These will be invalidated later 251 * when all outstanding prepared txns are resolved. 252 * 253 * PUBLIC: int __dbreg_mark_restored __P((ENV *)); 254 */ 255int 256__dbreg_mark_restored(env) 257 ENV *env; 258{ 259 DB_LOG *dblp; 260 FNAME *fnp; 261 LOG *lp; 262 263 /* If we haven't initialized logging, we have nothing to do. */ 264 if (!LOGGING_ON(env)) 265 return (0); 266 267 dblp = env->lg_handle; 268 lp = dblp->reginfo.primary; 269 270 MUTEX_LOCK(env, lp->mtx_filelist); 271 SH_TAILQ_FOREACH(fnp, &lp->fq, q, __fname) 272 if (fnp->id != DB_LOGFILEID_INVALID) 273 F_SET(fnp, DB_FNAME_RESTORED); 274 275 MUTEX_UNLOCK(env, lp->mtx_filelist); 276 return (0); 277} 278 279/* 280 * __dbreg_invalidate_files -- 281 * Invalidate files when we change replication roles. Save the 282 * id so that another process will be able to clean up the information 283 * when it notices. 284 * 285 * PUBLIC: int __dbreg_invalidate_files __P((ENV *, int)); 286 */ 287int 288__dbreg_invalidate_files(env, do_restored) 289 ENV *env; 290 int do_restored; 291{ 292 DB_LOG *dblp; 293 FNAME *fnp; 294 LOG *lp; 295 int ret; 296 297 /* If we haven't initialized logging, we have nothing to do. */ 298 if (!LOGGING_ON(env)) 299 return (0); 300 301 dblp = env->lg_handle; 302 lp = dblp->reginfo.primary; 303 304 ret = 0; 305 MUTEX_LOCK(env, lp->mtx_filelist); 306 SH_TAILQ_FOREACH(fnp, &lp->fq, q, __fname) { 307 /* 308 * Normally, skip any file with DB_FNAME_RESTORED 309 * set. If do_restored is set, only invalidate 310 * those files with the flag set and skip all others. 311 */ 312 if (F_ISSET(fnp, DB_FNAME_RESTORED) && !do_restored) 313 continue; 314 if (!F_ISSET(fnp, DB_FNAME_RESTORED) && do_restored) 315 continue; 316 if (fnp->id != DB_LOGFILEID_INVALID) { 317 if ((ret = __dbreg_log_close(env, 318 fnp, NULL, DBREG_RCLOSE)) != 0) 319 goto err; 320 fnp->old_id = fnp->id; 321 fnp->id = DB_LOGFILEID_INVALID; 322 } 323 } 324err: MUTEX_UNLOCK(env, lp->mtx_filelist); 325 return (ret); 326} 327 328/* 329 * __dbreg_id_to_db -- 330 * Return the DB corresponding to the specified dbreg id. 331 * 332 * PUBLIC: int __dbreg_id_to_db __P((ENV *, DB_TXN *, DB **, int32_t, int)); 333 */ 334int 335__dbreg_id_to_db(env, txn, dbpp, ndx, tryopen) 336 ENV *env; 337 DB_TXN *txn; 338 DB **dbpp; 339 int32_t ndx; 340 int tryopen; 341{ 342 DB_LOG *dblp; 343 FNAME *fname; 344 int ret; 345 char *name; 346 347 dblp = env->lg_handle; 348 ret = 0; 349 350 MUTEX_LOCK(env, dblp->mtx_dbreg); 351 352 /* 353 * We take a final parameter that indicates whether we should attempt 354 * to open the file if no mapping is found. During recovery, the 355 * recovery routines all want to try to open the file (and this is 356 * called from __dbreg_id_to_db), however, if we have a multi-process 357 * environment where some processes may not have the files open (e.g., 358 * XA), then we also get called from __dbreg_assign_id and it's OK if 359 * there is no mapping. 360 * 361 * Under XA, a process different than the one issuing DB operations 362 * may abort a transaction. In this case, the "recovery" routines 363 * are run by a process that does not necessarily have the file open, 364 * so we we must open the file explicitly. 365 */ 366 if (ndx >= dblp->dbentry_cnt || 367 (!dblp->dbentry[ndx].deleted && dblp->dbentry[ndx].dbp == NULL)) { 368 if (!tryopen || F_ISSET(dblp, DBLOG_RECOVER)) { 369 ret = ENOENT; 370 goto err; 371 } 372 373 /* 374 * __dbreg_id_to_fname acquires the mtx_filelist mutex, which 375 * we can't safely acquire while we hold the thread lock. We 376 * no longer need it anyway--the dbentry table didn't have what 377 * we needed. 378 */ 379 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 380 381 if (__dbreg_id_to_fname(dblp, ndx, 0, &fname) != 0) 382 /* 383 * With transactional opens, we may actually have 384 * closed this file in the transaction in which 385 * case this will fail too. Then it's up to the 386 * caller to reopen the file. 387 */ 388 return (ENOENT); 389 390 /* 391 * Note that we're relying on fname not to change, even though 392 * we released the mutex that protects it (mtx_filelist) inside 393 * __dbreg_id_to_fname. This should be a safe assumption, the 394 * other process that has the file open shouldn't be closing it 395 * while we're trying to abort. 396 */ 397 name = fname->fname_off == INVALID_ROFF ? 398 NULL : R_ADDR(&dblp->reginfo, fname->fname_off); 399 400 /* 401 * At this point, we are not holding the thread lock, so exit 402 * directly instead of going through the exit code at the 403 * bottom. If the __dbreg_do_open succeeded, then we don't need 404 * to do any of the remaining error checking at the end of this 405 * routine. 406 * If TXN_INVALID is passed then no txnlist is needed. 407 */ 408 if ((ret = __dbreg_do_open(env, txn, dblp, 409 fname->ufid, name, fname->s_type, ndx, fname->meta_pgno, 410 NULL, TXN_INVALID, F_ISSET(fname, DB_FNAME_INMEM) ? 411 DBREG_REOPEN : DBREG_OPEN)) != 0) 412 return (ret); 413 414 *dbpp = dblp->dbentry[ndx].dbp; 415 return (*dbpp == NULL ? DB_DELETED : 0); 416 } 417 418 /* 419 * Return DB_DELETED if the file has been deleted (it's not an error). 420 */ 421 if (dblp->dbentry[ndx].deleted) { 422 ret = DB_DELETED; 423 goto err; 424 } 425 426 /* It's an error if we don't have a corresponding writeable DB. */ 427 if ((*dbpp = dblp->dbentry[ndx].dbp) == NULL) 428 ret = ENOENT; 429 else 430 /* 431 * If we are in recovery, then set that the file has 432 * been written. It is possible to run recovery, 433 * find all the pages in their post update state 434 * in the OS buffer pool, put a checkpoint in the log 435 * and then crash the system without forcing the pages 436 * to disk. If this is an in-memory file, we may not have 437 * an mpf yet. 438 */ 439 if ((*dbpp)->mpf != NULL && (*dbpp)->mpf->mfp != NULL) 440 (*dbpp)->mpf->mfp->file_written = 1; 441 442err: MUTEX_UNLOCK(env, dblp->mtx_dbreg); 443 return (ret); 444} 445 446/* 447 * __dbreg_id_to_fname -- 448 * Traverse the shared-memory region looking for the entry that 449 * matches the passed dbreg id. Returns 0 on success; -1 on error. 450 * 451 * PUBLIC: int __dbreg_id_to_fname __P((DB_LOG *, int32_t, int, FNAME **)); 452 */ 453int 454__dbreg_id_to_fname(dblp, id, have_lock, fnamep) 455 DB_LOG *dblp; 456 int32_t id; 457 int have_lock; 458 FNAME **fnamep; 459{ 460 ENV *env; 461 FNAME *fnp; 462 LOG *lp; 463 int ret; 464 465 env = dblp->env; 466 lp = dblp->reginfo.primary; 467 468 ret = -1; 469 470 if (!have_lock) 471 MUTEX_LOCK(env, lp->mtx_filelist); 472 SH_TAILQ_FOREACH(fnp, &lp->fq, q, __fname) 473 if (fnp->id == id) { 474 *fnamep = fnp; 475 ret = 0; 476 break; 477 } 478 if (!have_lock) 479 MUTEX_UNLOCK(env, lp->mtx_filelist); 480 481 return (ret); 482} 483/* 484 * __dbreg_fid_to_fname -- 485 * Traverse the shared-memory region looking for the entry that 486 * matches the passed file unique id. Returns 0 on success; -1 on error. 487 * 488 * PUBLIC: int __dbreg_fid_to_fname __P((DB_LOG *, u_int8_t *, int, FNAME **)); 489 */ 490int 491__dbreg_fid_to_fname(dblp, fid, have_lock, fnamep) 492 DB_LOG *dblp; 493 u_int8_t *fid; 494 int have_lock; 495 FNAME **fnamep; 496{ 497 ENV *env; 498 FNAME *fnp; 499 LOG *lp; 500 int ret; 501 502 env = dblp->env; 503 lp = dblp->reginfo.primary; 504 505 ret = -1; 506 507 if (!have_lock) 508 MUTEX_LOCK(env, lp->mtx_filelist); 509 SH_TAILQ_FOREACH(fnp, &lp->fq, q, __fname) 510 if (memcmp(fnp->ufid, fid, DB_FILE_ID_LEN) == 0) { 511 *fnamep = fnp; 512 ret = 0; 513 break; 514 } 515 if (!have_lock) 516 MUTEX_UNLOCK(env, lp->mtx_filelist); 517 518 return (ret); 519} 520 521/* 522 * __dbreg_get_name 523 * 524 * Interface to get name of registered files. This is mainly diagnostic 525 * and the name passed could be transient unless there is something 526 * ensuring that the file cannot be closed. 527 * 528 * PUBLIC: int __dbreg_get_name __P((ENV *, u_int8_t *, char **, char **)); 529 */ 530int 531__dbreg_get_name(env, fid, fnamep, dnamep) 532 ENV *env; 533 u_int8_t *fid; 534 char **fnamep, **dnamep; 535{ 536 DB_LOG *dblp; 537 FNAME *fnp; 538 539 dblp = env->lg_handle; 540 541 if (dblp != NULL && __dbreg_fid_to_fname(dblp, fid, 0, &fnp) == 0) { 542 *fnamep = fnp->fname_off == INVALID_ROFF ? 543 NULL : R_ADDR(&dblp->reginfo, fnp->fname_off); 544 *dnamep = fnp->dname_off == INVALID_ROFF ? 545 NULL : R_ADDR(&dblp->reginfo, fnp->dname_off); 546 return (0); 547 } 548 549 *fnamep = *dnamep = NULL; 550 return (-1); 551} 552 553/* 554 * __dbreg_do_open -- 555 * Open files referenced in the log. This is the part of the open that 556 * is not protected by the thread mutex. 557 * PUBLIC: int __dbreg_do_open __P((ENV *, 558 * PUBLIC: DB_TXN *, DB_LOG *, u_int8_t *, char *, DBTYPE, 559 * PUBLIC: int32_t, db_pgno_t, void *, u_int32_t, u_int32_t)); 560 */ 561int 562__dbreg_do_open(env, 563 txn, lp, uid, name, ftype, ndx, meta_pgno, info, id, opcode) 564 ENV *env; 565 DB_TXN *txn; 566 DB_LOG *lp; 567 u_int8_t *uid; 568 char *name; 569 DBTYPE ftype; 570 int32_t ndx; 571 db_pgno_t meta_pgno; 572 void *info; 573 u_int32_t id, opcode; 574{ 575 DB *dbp; 576 u_int32_t cstat, ret_stat; 577 int ret, try_inmem; 578 char *dname, *fname; 579 580 cstat = TXN_EXPECTED; 581 fname = name; 582 dname = NULL; 583 try_inmem = 0; 584 if ((ret = __db_create_internal(&dbp, lp->env, 0)) != 0) 585 return (ret); 586 587 /* 588 * We can open files under a number of different scenarios. 589 * First, we can open a file during a normal txn_abort, if that file 590 * was opened and closed during the transaction (as is the master 591 * database of a sub-database). 592 * Second, we might be aborting a transaction in XA and not have 593 * it open in the process that is actually doing the abort. 594 * Third, we might be in recovery. 595 * In case 3, there is no locking, so there is no issue. 596 * In cases 1 and 2, we are guaranteed to already hold any locks 597 * that we need, since we're still in the same transaction, so by 598 * setting DB_AM_RECOVER, we guarantee that we don't log and that 599 * we don't try to acquire locks on behalf of a different locker id. 600 */ 601 F_SET(dbp, DB_AM_RECOVER); 602 if (meta_pgno != PGNO_BASE_MD) { 603 memcpy(dbp->fileid, uid, DB_FILE_ID_LEN); 604 dbp->meta_pgno = meta_pgno; 605 } 606 if (opcode == DBREG_PREOPEN) { 607 dbp->type = ftype; 608 if ((ret = __dbreg_setup(dbp, name, NULL, id)) != 0) 609 goto err; 610 MAKE_INMEM(dbp); 611 goto skip_open; 612 } 613 614 if (opcode == DBREG_REOPEN) { 615 MAKE_INMEM(dbp); 616 fname = NULL; 617 dname = name; 618 } 619 620retry_inmem: 621 if ((ret = __db_open(dbp, NULL, txn, fname, dname, ftype, 622 DB_DURABLE_UNKNOWN | DB_ODDFILESIZE, 623 DB_MODE_600, meta_pgno)) == 0) { 624skip_open: 625 /* 626 * Verify that we are opening the same file that we were 627 * referring to when we wrote this log record. 628 */ 629 if ((meta_pgno != PGNO_BASE_MD && 630 __dbreg_check_master(env, uid, name) != 0) || 631 memcmp(uid, dbp->fileid, DB_FILE_ID_LEN) != 0) 632 cstat = TXN_UNEXPECTED; 633 else 634 cstat = TXN_EXPECTED; 635 636 /* Assign the specific dbreg id to this dbp. */ 637 if ((ret = __dbreg_assign_id(dbp, ndx)) != 0) 638 goto err; 639 640 /* 641 * If we successfully opened this file, then we need to 642 * convey that information to the txnlist so that we 643 * know how to handle the subtransaction that created 644 * the file system object. 645 */ 646 if (id != TXN_INVALID) 647 ret = __db_txnlist_update(env, 648 info, id, cstat, NULL, &ret_stat, 1); 649 650err: if (cstat == TXN_UNEXPECTED) 651 goto not_right; 652 return (ret); 653 } else if (ret == ENOENT) { 654 /* 655 * If we are on a checkpoint record and open failed, try 656 * it as a named in-mem database. Currently checkpoint 657 * records do not distinguish between a named in-memory 658 * database and one on-disk. Therefore, an internal init 659 * via replication that is trying to open and access this 660 * as a named in-men database will not find it on-disk, and 661 * we need to try to open it in-memory too. 662 */ 663 if (try_inmem == 0 && opcode == DBREG_CHKPNT) { 664 MAKE_INMEM(dbp); 665 fname = NULL; 666 dname = name; 667 try_inmem = 1; 668 goto retry_inmem; 669 } else if (try_inmem != 0) 670 CLR_INMEM(dbp); 671 672 /* Record that the open failed in the txnlist. */ 673 if (id != TXN_INVALID) 674 ret = __db_txnlist_update(env, info, 675 id, TXN_UNEXPECTED, NULL, &ret_stat, 1); 676 } 677not_right: 678 (void)__db_close(dbp, NULL, DB_NOSYNC); 679 /* Add this file as deleted. */ 680 (void)__dbreg_add_dbentry(env, lp, NULL, ndx); 681 return (ret); 682} 683 684static int 685__dbreg_check_master(env, uid, name) 686 ENV *env; 687 u_int8_t *uid; 688 char *name; 689{ 690 DB *dbp; 691 int ret; 692 693 ret = 0; 694 if ((ret = __db_create_internal(&dbp, env, 0)) != 0) 695 return (ret); 696 F_SET(dbp, DB_AM_RECOVER); 697 ret = __db_open(dbp, NULL, NULL, 698 name, NULL, DB_BTREE, 0, DB_MODE_600, PGNO_BASE_MD); 699 700 if (ret == 0 && memcmp(uid, dbp->fileid, DB_FILE_ID_LEN) != 0) 701 ret = EINVAL; 702 703 (void)__db_close(dbp, NULL, 0); 704 return (ret); 705} 706 707/* 708 * __dbreg_lazy_id -- 709 * When a replication client gets upgraded to being a replication master, 710 * it may have database handles open that have not been assigned an ID, but 711 * which have become legal to use for logging. 712 * 713 * This function lazily allocates a new ID for such a function, in a 714 * new transaction created for the purpose. We need to do this in a new 715 * transaction because we definitely wish to commit the dbreg_register, but 716 * at this point we have no way of knowing whether the log record that incited 717 * us to call this will be part of a committed transaction. 718 * 719 * We first revoke any old id this handle may have had. That can happen 720 * if a master becomes a client and then becomes a master again and 721 * there are other processes with valid open handles to this env. 722 * 723 * PUBLIC: int __dbreg_lazy_id __P((DB *)); 724 */ 725int 726__dbreg_lazy_id(dbp) 727 DB *dbp; 728{ 729 DB_LOG *dblp; 730 DB_TXN *txn; 731 ENV *env; 732 FNAME *fnp; 733 LOG *lp; 734 int32_t id; 735 int ret; 736 737 env = dbp->env; 738 739 DB_ASSERT(env, IS_REP_MASTER(env)); 740 741 env = dbp->env; 742 dblp = env->lg_handle; 743 lp = dblp->reginfo.primary; 744 fnp = dbp->log_filename; 745 746 /* The mtx_filelist protects the FNAME list and id management. */ 747 MUTEX_LOCK(env, lp->mtx_filelist); 748 if (fnp->id != DB_LOGFILEID_INVALID) { 749 MUTEX_UNLOCK(env, lp->mtx_filelist); 750 return (0); 751 } 752 id = DB_LOGFILEID_INVALID; 753 /* 754 * When we became master we moved the fnp->id to old_id in 755 * every FNAME structure that was open. If our id was changed, 756 * we need to revoke and give back that id. 757 */ 758 if (fnp->old_id != DB_LOGFILEID_INVALID && 759 (ret = __dbreg_revoke_id(dbp, 1, DB_LOGFILEID_INVALID)) != 0) 760 goto err; 761 if ((ret = __txn_begin(env, NULL, NULL, &txn, 0)) != 0) 762 goto err; 763 764 if ((ret = __dbreg_get_id(dbp, txn, &id)) != 0) { 765 (void)__txn_abort(txn); 766 goto err; 767 } 768 769 if ((ret = __txn_commit(txn, DB_TXN_NOSYNC)) != 0) 770 goto err; 771 772 /* 773 * All DB related logging routines check the id value *without* 774 * holding the mtx_filelist to know whether we need to call 775 * dbreg_lazy_id to begin with. We must set the ID after a 776 * *successful* commit so that there is no possibility of a second 777 * modification call finding a valid ID in the dbp before the 778 * dbreg_register and commit records are in the log. 779 * If there was an error, then we call __dbreg_revoke_id to 780 * remove the entry from the lists. 781 */ 782 fnp->id = id; 783err: 784 if (ret != 0 && id != DB_LOGFILEID_INVALID) 785 (void)__dbreg_revoke_id(dbp, 1, id); 786 MUTEX_UNLOCK(env, lp->mtx_filelist); 787 return (ret); 788} 789