1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996, 2010 Oracle and/or its affiliates. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/db_page.h" 13#include "dbinc/log.h" 14#include "dbinc/mp.h" 15#include "dbinc/hash.h" 16 17static int __db_pg_free_recover_int __P((ENV *, DB_THREAD_INFO *, 18 __db_pg_freedata_args *, DB *, DB_LSN *, DB_MPOOLFILE *, db_recops, int)); 19static int __db_pg_free_recover_42_int __P((ENV *, DB_THREAD_INFO *, 20 __db_pg_freedata_42_args *, 21 DB *, DB_LSN *, DB_MPOOLFILE *, db_recops, int)); 22 23/* 24 * PUBLIC: int __db_addrem_recover 25 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 26 * 27 * This log message is generated whenever we add or remove a duplicate 28 * to/from a duplicate page. On recover, we just do the opposite. 29 */ 30int 31__db_addrem_recover(env, dbtp, lsnp, op, info) 32 ENV *env; 33 DBT *dbtp; 34 DB_LSN *lsnp; 35 db_recops op; 36 void *info; 37{ 38 __db_addrem_args *argp; 39 DB_THREAD_INFO *ip; 40 DB *file_dbp; 41 DBC *dbc; 42 DB_MPOOLFILE *mpf; 43 PAGE *pagep; 44 int cmp_n, cmp_p, modified, ret; 45 46 ip = ((DB_TXNHEAD *)info)->thread_info; 47 pagep = NULL; 48 REC_PRINT(__db_addrem_print); 49 REC_INTRO(__db_addrem_read, ip, 1); 50 51 REC_FGET(mpf, ip, argp->pgno, &pagep, done); 52 modified = 0; 53 54 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 55 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->pagelsn); 56 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->pagelsn); 57 CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp); 58 if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_ADD_DUP) || 59 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_REM_DUP)) { 60 /* Need to redo an add, or undo a delete. */ 61 REC_DIRTY(mpf, ip, dbc->priority, &pagep); 62 if ((ret = __db_pitem(dbc, pagep, argp->indx, argp->nbytes, 63 argp->hdr.size == 0 ? NULL : &argp->hdr, 64 argp->dbt.size == 0 ? NULL : &argp->dbt)) != 0) 65 goto out; 66 modified = 1; 67 68 } else if ((cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_ADD_DUP) || 69 (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_REM_DUP)) { 70 /* Need to undo an add, or redo a delete. */ 71 REC_DIRTY(mpf, ip, dbc->priority, &pagep); 72 if ((ret = __db_ditem(dbc, 73 pagep, argp->indx, argp->nbytes)) != 0) 74 goto out; 75 modified = 1; 76 } 77 78 if (modified) { 79 if (DB_REDO(op)) 80 LSN(pagep) = *lsnp; 81 else 82 LSN(pagep) = argp->pagelsn; 83 } 84 85 if ((ret = __memp_fput(mpf, ip, pagep, dbc->priority)) != 0) 86 goto out; 87 pagep = NULL; 88 89done: *lsnp = argp->prev_lsn; 90 ret = 0; 91 92out: if (pagep != NULL) 93 (void)__memp_fput(mpf, ip, pagep, dbc->priority); 94 REC_CLOSE; 95} 96 97/* 98 * PUBLIC: int __db_big_recover 99 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 100 */ 101int 102__db_big_recover(env, dbtp, lsnp, op, info) 103 ENV *env; 104 DBT *dbtp; 105 DB_LSN *lsnp; 106 db_recops op; 107 void *info; 108{ 109 __db_big_args *argp; 110 DB_THREAD_INFO *ip; 111 DB *file_dbp; 112 DBC *dbc; 113 DB_MPOOLFILE *mpf; 114 PAGE *pagep; 115 int cmp_n, cmp_p, modified, ret; 116 117 ip = ((DB_TXNHEAD *)info)->thread_info; 118 pagep = NULL; 119 REC_PRINT(__db_big_print); 120 REC_INTRO(__db_big_read, ip, 0); 121 122 REC_FGET(mpf, ip, argp->pgno, &pagep, ppage); 123 modified = 0; 124 125 /* 126 * There are three pages we need to check. The one on which we are 127 * adding data, the previous one whose next_pointer may have 128 * been updated, and the next one whose prev_pointer may have 129 * been updated. 130 */ 131 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 132 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->pagelsn); 133 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->pagelsn); 134 CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp); 135 if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_ADD_BIG) || 136 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_REM_BIG)) { 137 /* We are either redo-ing an add, or undoing a delete. */ 138 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 139 P_INIT(pagep, file_dbp->pgsize, argp->pgno, argp->prev_pgno, 140 argp->next_pgno, 0, P_OVERFLOW); 141 OV_LEN(pagep) = argp->dbt.size; 142 OV_REF(pagep) = 1; 143 memcpy((u_int8_t *)pagep + P_OVERHEAD(file_dbp), argp->dbt.data, 144 argp->dbt.size); 145 PREV_PGNO(pagep) = argp->prev_pgno; 146 modified = 1; 147 } else if ((cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_ADD_BIG) || 148 (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_REM_BIG)) { 149 /* 150 * We are either undo-ing an add or redo-ing a delete. 151 * The page is about to be reclaimed in either case, so 152 * there really isn't anything to do here. 153 */ 154 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 155 modified = 1; 156 } else if (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_APPEND_BIG) { 157 /* We are redoing an append. */ 158 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 159 memcpy((u_int8_t *)pagep + P_OVERHEAD(file_dbp) + 160 OV_LEN(pagep), argp->dbt.data, argp->dbt.size); 161 OV_LEN(pagep) += argp->dbt.size; 162 modified = 1; 163 } else if (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_APPEND_BIG) { 164 /* We are undoing an append. */ 165 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 166 OV_LEN(pagep) -= argp->dbt.size; 167 memset((u_int8_t *)pagep + P_OVERHEAD(file_dbp) + 168 OV_LEN(pagep), 0, argp->dbt.size); 169 modified = 1; 170 } 171 if (modified) 172 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->pagelsn; 173 174 ret = __memp_fput(mpf, ip, pagep, file_dbp->priority); 175 pagep = NULL; 176 if (ret != 0) 177 goto out; 178 179 /* 180 * We only delete a whole chain of overflow items, and appends only 181 * apply to a single page. Adding a page is the only case that 182 * needs to update the chain. 183 */ 184 if (argp->opcode != DB_ADD_BIG) 185 goto done; 186 187 /* Now check the previous page. */ 188ppage: if (argp->prev_pgno != PGNO_INVALID) { 189 REC_FGET(mpf, ip, argp->prev_pgno, &pagep, npage); 190 modified = 0; 191 192 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 193 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->prevlsn); 194 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->prevlsn); 195 CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp); 196 197 if (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_ADD_BIG) { 198 /* Redo add, undo delete. */ 199 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 200 NEXT_PGNO(pagep) = argp->pgno; 201 modified = 1; 202 } else if (cmp_n == 0 && 203 DB_UNDO(op) && argp->opcode == DB_ADD_BIG) { 204 /* Redo delete, undo add. */ 205 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 206 NEXT_PGNO(pagep) = argp->next_pgno; 207 modified = 1; 208 } 209 if (modified) 210 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->prevlsn; 211 ret = __memp_fput(mpf, ip, pagep, file_dbp->priority); 212 pagep = NULL; 213 if (ret != 0) 214 goto out; 215 } 216 pagep = NULL; 217 218 /* Now check the next page. Can only be set on a delete. */ 219npage: if (argp->next_pgno != PGNO_INVALID) { 220 REC_FGET(mpf, ip, argp->next_pgno, &pagep, done); 221 modified = 0; 222 223 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 224 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->nextlsn); 225 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->nextlsn); 226 CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp); 227 if (cmp_p == 0 && DB_REDO(op)) { 228 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 229 PREV_PGNO(pagep) = PGNO_INVALID; 230 modified = 1; 231 } else if (cmp_n == 0 && DB_UNDO(op)) { 232 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 233 PREV_PGNO(pagep) = argp->pgno; 234 modified = 1; 235 } 236 if (modified) 237 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->nextlsn; 238 ret = __memp_fput(mpf, ip, pagep, file_dbp->priority); 239 pagep = NULL; 240 if (ret != 0) 241 goto out; 242 } 243 pagep = NULL; 244 245done: *lsnp = argp->prev_lsn; 246 ret = 0; 247 248out: if (pagep != NULL) 249 (void)__memp_fput(mpf, ip, pagep, file_dbp->priority); 250 REC_CLOSE; 251} 252 253/* 254 * __db_ovref_recover -- 255 * Recovery function for __db_ovref(). 256 * 257 * PUBLIC: int __db_ovref_recover 258 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 259 */ 260int 261__db_ovref_recover(env, dbtp, lsnp, op, info) 262 ENV *env; 263 DBT *dbtp; 264 DB_LSN *lsnp; 265 db_recops op; 266 void *info; 267{ 268 __db_ovref_args *argp; 269 DB_THREAD_INFO *ip; 270 DB *file_dbp; 271 DBC *dbc; 272 DB_MPOOLFILE *mpf; 273 PAGE *pagep; 274 int cmp, ret; 275 276 ip = ((DB_TXNHEAD *)info)->thread_info; 277 pagep = NULL; 278 REC_PRINT(__db_ovref_print); 279 REC_INTRO(__db_ovref_read, ip, 0); 280 281 REC_FGET(mpf, ip, argp->pgno, &pagep, done); 282 283 cmp = LOG_COMPARE(&LSN(pagep), &argp->lsn); 284 CHECK_LSN(env, op, cmp, &LSN(pagep), &argp->lsn); 285 if (cmp == 0 && DB_REDO(op)) { 286 /* Need to redo update described. */ 287 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 288 OV_REF(pagep) += argp->adjust; 289 pagep->lsn = *lsnp; 290 } else if (LOG_COMPARE(lsnp, &LSN(pagep)) == 0 && DB_UNDO(op)) { 291 /* Need to undo update described. */ 292 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 293 OV_REF(pagep) -= argp->adjust; 294 pagep->lsn = argp->lsn; 295 } 296 ret = __memp_fput(mpf, ip, pagep, file_dbp->priority); 297 pagep = NULL; 298 if (ret != 0) 299 goto out; 300 pagep = NULL; 301 302done: *lsnp = argp->prev_lsn; 303 ret = 0; 304 305out: if (pagep != NULL) 306 (void)__memp_fput(mpf, ip, pagep, file_dbp->priority); 307 REC_CLOSE; 308} 309 310/* 311 * __db_debug_recover -- 312 * Recovery function for debug. 313 * 314 * PUBLIC: int __db_debug_recover __P((ENV *, 315 * PUBLIC: DBT *, DB_LSN *, db_recops, void *)); 316 */ 317int 318__db_debug_recover(env, dbtp, lsnp, op, info) 319 ENV *env; 320 DBT *dbtp; 321 DB_LSN *lsnp; 322 db_recops op; 323 void *info; 324{ 325 __db_debug_args *argp; 326 int ret; 327 328 COMPQUIET(op, DB_TXN_ABORT); 329 COMPQUIET(info, NULL); 330 331 REC_PRINT(__db_debug_print); 332 REC_NOOP_INTRO(__db_debug_read); 333 334 *lsnp = argp->prev_lsn; 335 ret = 0; 336 337 REC_NOOP_CLOSE; 338} 339 340/* 341 * __db_noop_recover -- 342 * Recovery function for noop. 343 * 344 * PUBLIC: int __db_noop_recover __P((ENV *, 345 * PUBLIC: DBT *, DB_LSN *, db_recops, void *)); 346 */ 347int 348__db_noop_recover(env, dbtp, lsnp, op, info) 349 ENV *env; 350 DBT *dbtp; 351 DB_LSN *lsnp; 352 db_recops op; 353 void *info; 354{ 355 __db_noop_args *argp; 356 DB_THREAD_INFO *ip; 357 DB *file_dbp; 358 DBC *dbc; 359 DB_MPOOLFILE *mpf; 360 PAGE *pagep; 361 int cmp_n, cmp_p, ret; 362 363 ip = ((DB_TXNHEAD *)info)->thread_info; 364 pagep = NULL; 365 REC_PRINT(__db_noop_print); 366 REC_INTRO(__db_noop_read, ip, 0); 367 368 REC_FGET(mpf, ip, argp->pgno, &pagep, done); 369 370 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 371 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->prevlsn); 372 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->prevlsn); 373 CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp); 374 if (cmp_p == 0 && DB_REDO(op)) { 375 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 376 LSN(pagep) = *lsnp; 377 } else if (cmp_n == 0 && DB_UNDO(op)) { 378 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 379 LSN(pagep) = argp->prevlsn; 380 } 381 ret = __memp_fput(mpf, ip, pagep, file_dbp->priority); 382 pagep = NULL; 383 384done: *lsnp = argp->prev_lsn; 385out: if (pagep != NULL) 386 (void)__memp_fput(mpf, 387 ip, pagep, file_dbp->priority); 388 REC_CLOSE; 389} 390 391/* 392 * __db_pg_alloc_recover -- 393 * Recovery function for pg_alloc. 394 * 395 * PUBLIC: int __db_pg_alloc_recover 396 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 397 */ 398int 399__db_pg_alloc_recover(env, dbtp, lsnp, op, info) 400 ENV *env; 401 DBT *dbtp; 402 DB_LSN *lsnp; 403 db_recops op; 404 void *info; 405{ 406 __db_pg_alloc_args *argp; 407 DB_THREAD_INFO *ip; 408 DB *file_dbp; 409 DBC *dbc; 410 DBMETA *meta; 411 DB_MPOOLFILE *mpf; 412 PAGE *pagep; 413 db_pgno_t pgno; 414 int cmp_n, cmp_p, created, level, ret; 415 416 ip = ((DB_TXNHEAD *)info)->thread_info; 417 meta = NULL; 418 pagep = NULL; 419 created = 0; 420 REC_PRINT(__db_pg_alloc_print); 421 REC_INTRO(__db_pg_alloc_read, ip, 0); 422 423 /* 424 * Fix up the metadata page. If we're redoing the operation, we have 425 * to get the metadata page and update its LSN and its free pointer. 426 * If we're undoing the operation and the page was ever created, we put 427 * it on the freelist. 428 */ 429 pgno = PGNO_BASE_MD; 430 if ((ret = __memp_fget(mpf, &pgno, ip, NULL, 0, &meta)) != 0) { 431 /* The metadata page must always exist on redo. */ 432 if (DB_REDO(op)) { 433 ret = __db_pgerr(file_dbp, pgno, ret); 434 goto out; 435 } else 436 goto done; 437 } 438 cmp_n = LOG_COMPARE(lsnp, &LSN(meta)); 439 cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn); 440 CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn); 441 CHECK_ABORT(env, op, cmp_n, &LSN(meta), lsnp); 442 if (cmp_p == 0 && DB_REDO(op)) { 443 /* Need to redo update described. */ 444 REC_DIRTY(mpf, ip, file_dbp->priority, &meta); 445 LSN(meta) = *lsnp; 446 meta->free = argp->next; 447 if (argp->pgno > meta->last_pgno) 448 meta->last_pgno = argp->pgno; 449 } else if (cmp_n == 0 && DB_UNDO(op)) { 450 /* Need to undo update described. */ 451 REC_DIRTY(mpf, ip, file_dbp->priority, &meta); 452 LSN(meta) = argp->meta_lsn; 453 /* 454 * If the page has a zero LSN then its newly created and 455 * will be truncated rather than go on the free list. 456 */ 457 if (!IS_ZERO_LSN(argp->page_lsn)) 458 meta->free = argp->pgno; 459 meta->last_pgno = argp->last_pgno; 460 } 461 462#ifdef HAVE_FTRUNCATE 463 /* 464 * check to see if we are keeping a sorted freelist, if so put 465 * this back in the in memory list. It must be the first element. 466 */ 467 if (op == DB_TXN_ABORT && !IS_ZERO_LSN(argp->page_lsn)) { 468 db_pgno_t *list; 469 u_int32_t nelem; 470 471 if ((ret = __memp_get_freelist(mpf, &nelem, &list)) != 0) 472 goto out; 473 if (list != NULL && (nelem == 0 || *list != argp->pgno)) { 474 if ((ret = 475 __memp_extend_freelist(mpf, nelem + 1, &list)) != 0) 476 goto out; 477 if (nelem != 0) 478 memmove(list + 1, list, nelem * sizeof(*list)); 479 *list = argp->pgno; 480 } 481 } 482#endif 483 484 /* 485 * Fix up the allocated page. If the page does not exist 486 * and we can truncate it then don't create it. 487 * Otherwise if we're redoing the operation, we have 488 * to get the page (creating it if it doesn't exist), and update its 489 * LSN. If we're undoing the operation, we have to reset the page's 490 * LSN and put it on the free list. 491 */ 492 if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) { 493 /* 494 * We have to be able to identify if a page was newly 495 * created so we can recover it properly. We cannot simply 496 * look for an empty header, because hash uses a pgin 497 * function that will set the header. Instead, we explicitly 498 * try for the page without CREATE and if that fails, then 499 * create it. 500 */ 501 if (DB_UNDO(op)) 502 goto do_truncate; 503 if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 504 DB_MPOOL_CREATE, &pagep)) != 0) { 505 if (DB_UNDO(op) && ret == ENOSPC) 506 goto do_truncate; 507 ret = __db_pgerr(file_dbp, argp->pgno, ret); 508 goto out; 509 } 510 created = 1; 511 } 512 513 /* Fix up the allocated page. */ 514 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 515 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->page_lsn); 516 517 /* 518 * If an initial allocation is aborted and then reallocated during 519 * an archival restore the log record will have an LSN for the page 520 * but the page will be empty. 521 */ 522 if (IS_ZERO_LSN(LSN(pagep))) 523 cmp_p = 0; 524 525 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->page_lsn); 526 /* 527 * Another special case we have to handle is if we ended up with a 528 * page of all 0's which can happen if we abort between allocating a 529 * page in mpool and initializing it. In that case, even if we're 530 * undoing, we need to re-initialize the page. 531 */ 532 if (DB_REDO(op) && cmp_p == 0) { 533 /* Need to redo update described. */ 534 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 535 switch (argp->ptype) { 536 case P_LBTREE: 537 case P_LRECNO: 538 case P_LDUP: 539 level = LEAFLEVEL; 540 break; 541 default: 542 level = 0; 543 break; 544 } 545 P_INIT(pagep, file_dbp->pgsize, 546 argp->pgno, PGNO_INVALID, PGNO_INVALID, level, argp->ptype); 547 548 pagep->lsn = *lsnp; 549 } else if (DB_UNDO(op) && (cmp_n == 0 || created)) { 550 /* 551 * This is where we handle the case of a 0'd page (pagep->pgno 552 * is equal to PGNO_INVALID). 553 * Undo the allocation, reinitialize the page and 554 * link its next pointer to the free list. 555 */ 556 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 557 P_INIT(pagep, file_dbp->pgsize, 558 argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID); 559 560 pagep->lsn = argp->page_lsn; 561 } 562 563do_truncate: 564 /* 565 * If the page was newly created, give it back. 566 */ 567 if ((pagep == NULL || IS_ZERO_LSN(LSN(pagep))) && 568 IS_ZERO_LSN(argp->page_lsn) && DB_UNDO(op)) { 569 /* Discard the page. */ 570 if (pagep != NULL) { 571 if ((ret = __memp_fput(mpf, ip, 572 pagep, DB_PRIORITY_VERY_LOW)) != 0) 573 goto out; 574 pagep = NULL; 575 } 576 /* Give the page back to the OS. */ 577 if (meta->last_pgno <= argp->pgno && (ret = __memp_ftruncate( 578 mpf, NULL, ip, argp->pgno, MP_TRUNC_RECOVER)) != 0) 579 goto out; 580 } 581 582 if (pagep != NULL) { 583 ret = __memp_fput(mpf, ip, pagep, file_dbp->priority); 584 pagep = NULL; 585 if (ret != 0) 586 goto out; 587 } 588 589 ret = __memp_fput(mpf, ip, meta, file_dbp->priority); 590 meta = NULL; 591 if (ret != 0) 592 goto out; 593 594done: *lsnp = argp->prev_lsn; 595 ret = 0; 596 597out: if (pagep != NULL) 598 (void)__memp_fput(mpf, ip, pagep, file_dbp->priority); 599 if (meta != NULL) 600 (void)__memp_fput(mpf, ip, meta, file_dbp->priority); 601 REC_CLOSE; 602} 603 604/* 605 * __db_pg_free_recover_int -- 606 */ 607static int 608__db_pg_free_recover_int(env, ip, argp, file_dbp, lsnp, mpf, op, data) 609 ENV *env; 610 DB_THREAD_INFO *ip; 611 __db_pg_freedata_args *argp; 612 DB *file_dbp; 613 DB_LSN *lsnp; 614 DB_MPOOLFILE *mpf; 615 db_recops op; 616 int data; 617{ 618 DBMETA *meta; 619 DB_LSN copy_lsn; 620 PAGE *pagep, *prevp; 621 int cmp_n, cmp_p, is_meta, ret; 622 623 meta = NULL; 624 pagep = prevp = NULL; 625 626 /* 627 * Get the "metapage". This will either be the metapage 628 * or the previous page in the free list if we are doing 629 * sorted allocations. If its a previous page then 630 * we will not be truncating. 631 */ 632 is_meta = argp->meta_pgno == PGNO_BASE_MD; 633 634 REC_FGET(mpf, ip, argp->meta_pgno, &meta, check_meta); 635 636 if (argp->meta_pgno != PGNO_BASE_MD) 637 prevp = (PAGE *)meta; 638 639 cmp_n = LOG_COMPARE(lsnp, &LSN(meta)); 640 cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn); 641 CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn); 642 CHECK_ABORT(env, op, cmp_n, &LSN(meta), lsnp); 643 644 /* 645 * Fix up the metadata page. If we're redoing or undoing the operation 646 * we get the page and update its LSN, last and free pointer. 647 */ 648 if (cmp_p == 0 && DB_REDO(op)) { 649 REC_DIRTY(mpf, ip, file_dbp->priority, &meta); 650 /* 651 * If we are at the end of the file truncate, otherwise 652 * put on the free list. 653 */ 654 if (argp->pgno == argp->last_pgno) 655 meta->last_pgno = argp->pgno - 1; 656 else if (is_meta) 657 meta->free = argp->pgno; 658 else 659 NEXT_PGNO(prevp) = argp->pgno; 660 LSN(meta) = *lsnp; 661 } else if (cmp_n == 0 && DB_UNDO(op)) { 662 /* Need to undo the deallocation. */ 663 REC_DIRTY(mpf, ip, file_dbp->priority, &meta); 664 if (is_meta) { 665 if (meta->last_pgno < argp->pgno) 666 meta->last_pgno = argp->pgno; 667 meta->free = argp->next; 668 } else 669 NEXT_PGNO(prevp) = argp->next; 670 LSN(meta) = argp->meta_lsn; 671 } 672 673check_meta: 674 if (ret != 0 && is_meta) { 675 /* The metadata page must always exist. */ 676 ret = __db_pgerr(file_dbp, argp->meta_pgno, ret); 677 goto out; 678 } 679 680 /* 681 * Get the freed page. Don't create the page if we are going to 682 * free it. If we're redoing the operation we get the page and 683 * explicitly discard its contents, then update its LSN. If we're 684 * undoing the operation, we get the page and restore its header. 685 */ 686 if (DB_REDO(op) || (is_meta && meta->last_pgno < argp->pgno)) { 687 if ((ret = __memp_fget(mpf, &argp->pgno, 688 ip, NULL, 0, &pagep)) != 0) { 689 if (ret != DB_PAGE_NOTFOUND) 690 goto out; 691 if (is_meta && 692 DB_REDO(op) && meta->last_pgno <= argp->pgno) 693 goto trunc; 694 goto done; 695 } 696 } else if ((ret = __memp_fget(mpf, &argp->pgno, 697 ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0) 698 goto out; 699 700 (void)__ua_memcpy(©_lsn, &LSN(argp->header.data), sizeof(DB_LSN)); 701 cmp_n = IS_ZERO_LSN(LSN(pagep)) ? 0 : LOG_COMPARE(lsnp, &LSN(pagep)); 702 cmp_p = LOG_COMPARE(&LSN(pagep), ©_lsn); 703 704 /* 705 * This page got extended by a later allocation, 706 * but its allocation was not in the scope of this 707 * recovery pass. 708 */ 709 if (IS_ZERO_LSN(LSN(pagep))) 710 cmp_p = 0; 711 712 CHECK_LSN(env, op, cmp_p, &LSN(pagep), ©_lsn); 713 if (DB_REDO(op) && 714 (cmp_p == 0 || 715 (IS_ZERO_LSN(copy_lsn) && 716 LOG_COMPARE(&LSN(pagep), &argp->meta_lsn) <= 0))) { 717 /* Need to redo the deallocation. */ 718 /* 719 * The page can be truncated if it was truncated at runtime 720 * and the current metapage reflects the truncation. 721 */ 722 if (is_meta && meta->last_pgno <= argp->pgno && 723 argp->last_pgno <= argp->pgno) { 724 if ((ret = __memp_fput(mpf, ip, 725 pagep, DB_PRIORITY_VERY_LOW)) != 0) 726 goto out; 727 pagep = NULL; 728trunc: if ((ret = __memp_ftruncate(mpf, NULL, ip, 729 argp->pgno, MP_TRUNC_RECOVER)) != 0) 730 goto out; 731 } else if (argp->last_pgno == argp->pgno) { 732 /* The page was truncated at runtime, zero it out. */ 733 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 734 P_INIT(pagep, 0, PGNO_INVALID, 735 PGNO_INVALID, PGNO_INVALID, 0, P_INVALID); 736 ZERO_LSN(pagep->lsn); 737 } else { 738 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 739 P_INIT(pagep, file_dbp->pgsize, 740 argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID); 741 pagep->lsn = *lsnp; 742 743 } 744 } else if (cmp_n == 0 && DB_UNDO(op)) { 745 /* Need to reallocate the page. */ 746 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 747 memcpy(pagep, argp->header.data, argp->header.size); 748 if (data) 749 memcpy((u_int8_t*)pagep + HOFFSET(pagep), 750 argp->data.data, argp->data.size); 751 } 752 if (pagep != NULL && 753 (ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0) 754 goto out; 755 756 pagep = NULL; 757#ifdef HAVE_FTRUNCATE 758 /* 759 * If we are keeping an in memory free list remove this 760 * element from the list. 761 */ 762 if (op == DB_TXN_ABORT && argp->pgno != argp->last_pgno) { 763 db_pgno_t *lp; 764 u_int32_t nelem, pos; 765 766 if ((ret = __memp_get_freelist(mpf, &nelem, &lp)) != 0) 767 goto out; 768 if (lp != NULL) { 769 pos = 0; 770 if (!is_meta) { 771 __db_freelist_pos(argp->pgno, lp, nelem, &pos); 772 773 /* 774 * If we aborted after logging but before 775 * updating the free list don't do anything. 776 */ 777 if (argp->pgno != lp[pos]) { 778 DB_ASSERT(env, 779 argp->meta_pgno == lp[pos]); 780 goto done; 781 } 782 DB_ASSERT(env, 783 argp->meta_pgno == lp[pos - 1]); 784 } else if (nelem != 0 && argp->pgno != lp[pos]) 785 goto done; 786 787 if (pos < nelem) 788 memmove(&lp[pos], &lp[pos + 1], 789 ((nelem - pos) - 1) * sizeof(*lp)); 790 791 /* Shrink the list */ 792 if ((ret = 793 __memp_extend_freelist(mpf, nelem - 1, &lp)) != 0) 794 goto out; 795 } 796 } 797#endif 798done: 799 if (meta != NULL && 800 (ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0) 801 goto out; 802 meta = NULL; 803 ret = 0; 804 805out: if (pagep != NULL) 806 (void)__memp_fput(mpf, ip, pagep, file_dbp->priority); 807 if (meta != NULL) 808 (void)__memp_fput(mpf, ip, meta, file_dbp->priority); 809 810 return (ret); 811} 812 813/* 814 * __db_pg_free_recover -- 815 * Recovery function for pg_free. 816 * 817 * PUBLIC: int __db_pg_free_recover 818 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 819 */ 820int 821__db_pg_free_recover(env, dbtp, lsnp, op, info) 822 ENV *env; 823 DBT *dbtp; 824 DB_LSN *lsnp; 825 db_recops op; 826 void *info; 827{ 828 __db_pg_free_args *argp; 829 DB *file_dbp; 830 DBC *dbc; 831 DB_MPOOLFILE *mpf; 832 DB_THREAD_INFO *ip; 833 int ret; 834 835 ip = ((DB_TXNHEAD *)info)->thread_info; 836 REC_PRINT(__db_pg_free_print); 837 REC_INTRO(__db_pg_free_read, ip, 0); 838 839 ret = __db_pg_free_recover_int(env, ip, 840 (__db_pg_freedata_args *)argp, file_dbp, lsnp, mpf, op, 0); 841 842done: *lsnp = argp->prev_lsn; 843out: 844 REC_CLOSE; 845} 846 847/* 848 * __db_pg_freedata_recover -- 849 * Recovery function for pg_freedata. 850 * 851 * PUBLIC: int __db_pg_freedata_recover 852 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 853 */ 854int 855__db_pg_freedata_recover(env, dbtp, lsnp, op, info) 856 ENV *env; 857 DBT *dbtp; 858 DB_LSN *lsnp; 859 db_recops op; 860 void *info; 861{ 862 __db_pg_freedata_args *argp; 863 DB *file_dbp; 864 DBC *dbc; 865 DB_MPOOLFILE *mpf; 866 DB_THREAD_INFO *ip; 867 int ret; 868 869 ip = ((DB_TXNHEAD *)info)->thread_info; 870 REC_PRINT(__db_pg_freedata_print); 871 REC_INTRO(__db_pg_freedata_read, ip, 0); 872 873 ret = __db_pg_free_recover_int(env, 874 ip, argp, file_dbp, lsnp, mpf, op, 1); 875 876done: *lsnp = argp->prev_lsn; 877out: 878 REC_CLOSE; 879} 880 881/* 882 * __db_cksum_recover -- 883 * Recovery function for checksum failure log record. 884 * 885 * PUBLIC: int __db_cksum_recover __P((ENV *, 886 * PUBLIC: DBT *, DB_LSN *, db_recops, void *)); 887 */ 888int 889__db_cksum_recover(env, dbtp, lsnp, op, info) 890 ENV *env; 891 DBT *dbtp; 892 DB_LSN *lsnp; 893 db_recops op; 894 void *info; 895{ 896 __db_cksum_args *argp; 897 int ret; 898 899 COMPQUIET(info, NULL); 900 COMPQUIET(lsnp, NULL); 901 COMPQUIET(op, DB_TXN_ABORT); 902 903 REC_PRINT(__db_cksum_print); 904 905 if ((ret = __db_cksum_read(env, dbtp->data, &argp)) != 0) 906 return (ret); 907 908 /* 909 * We had a checksum failure -- the only option is to run catastrophic 910 * recovery. 911 */ 912 if (F_ISSET(env, ENV_RECOVER_FATAL)) 913 ret = 0; 914 else { 915 __db_errx(env, 916 "Checksum failure requires catastrophic recovery"); 917 ret = __env_panic(env, DB_RUNRECOVERY); 918 } 919 920 __os_free(env, argp); 921 return (ret); 922} 923 924/* 925 * __db_pg_init_recover -- 926 * Recovery function to reinit pages after truncation. 927 * 928 * PUBLIC: int __db_pg_init_recover 929 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 930 */ 931int 932__db_pg_init_recover(env, dbtp, lsnp, op, info) 933 ENV *env; 934 DBT *dbtp; 935 DB_LSN *lsnp; 936 db_recops op; 937 void *info; 938{ 939 __db_pg_init_args *argp; 940 DB_THREAD_INFO *ip; 941 DB *file_dbp; 942 DBC *dbc; 943 DB_LSN copy_lsn; 944 DB_MPOOLFILE *mpf; 945 PAGE *pagep; 946 int cmp_n, cmp_p, ret, type; 947 948 ip = ((DB_TXNHEAD *)info)->thread_info; 949 REC_PRINT(__db_pg_init_print); 950 REC_INTRO(__db_pg_init_read, ip, 0); 951 952 mpf = file_dbp->mpf; 953 if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) { 954 if (DB_UNDO(op)) { 955 if (ret == DB_PAGE_NOTFOUND) 956 goto done; 957 else { 958 ret = __db_pgerr(file_dbp, argp->pgno, ret); 959 goto out; 960 } 961 } 962 963 /* 964 * This page was truncated and may simply not have 965 * had an item written to it yet. This should only 966 * happen on hash databases, so confirm that. 967 */ 968 DB_ASSERT(env, file_dbp->type == DB_HASH); 969 if ((ret = __memp_fget(mpf, &argp->pgno, 970 ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0) { 971 ret = __db_pgerr(file_dbp, argp->pgno, ret); 972 goto out; 973 } 974 } 975 976 (void)__ua_memcpy(©_lsn, &LSN(argp->header.data), sizeof(DB_LSN)); 977 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 978 cmp_p = LOG_COMPARE(&LSN(pagep), ©_lsn); 979 CHECK_LSN(env, op, cmp_p, &LSN(pagep), ©_lsn); 980 CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp); 981 982 if (cmp_p == 0 && DB_REDO(op)) { 983 if (TYPE(pagep) == P_HASH) 984 type = P_HASH; 985 else 986 type = file_dbp->type == DB_RECNO ? P_LRECNO : P_LBTREE; 987 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 988 P_INIT(pagep, file_dbp->pgsize, PGNO(pagep), PGNO_INVALID, 989 PGNO_INVALID, TYPE(pagep) == P_HASH ? 0 : 1, type); 990 pagep->lsn = *lsnp; 991 } else if (cmp_n == 0 && DB_UNDO(op)) { 992 /* Put the data back on the page. */ 993 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 994 memcpy(pagep, argp->header.data, argp->header.size); 995 if (argp->data.size > 0) 996 memcpy((u_int8_t*)pagep + HOFFSET(pagep), 997 argp->data.data, argp->data.size); 998 } 999 if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0) 1000 goto out; 1001 1002done: *lsnp = argp->prev_lsn; 1003out: 1004 REC_CLOSE; 1005} 1006 1007/* 1008 * __db_pg_trunc_recover -- 1009 * Recovery function for pg_trunc. 1010 * 1011 * PUBLIC: int __db_pg_trunc_recover 1012 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 1013 */ 1014int 1015__db_pg_trunc_recover(env, dbtp, lsnp, op, info) 1016 ENV *env; 1017 DBT *dbtp; 1018 DB_LSN *lsnp; 1019 db_recops op; 1020 void *info; 1021{ 1022#ifdef HAVE_FTRUNCATE 1023 __db_pg_trunc_args *argp; 1024 DB_THREAD_INFO *ip; 1025 DB *file_dbp; 1026 DBC *dbc; 1027 DBMETA *meta; 1028 DB_MPOOLFILE *mpf; 1029 PAGE *pagep; 1030 db_pglist_t *pglist, *lp; 1031 db_pgno_t last_pgno, *list; 1032 u_int32_t felem, nelem, pos; 1033 int ret; 1034 1035 ip = ((DB_TXNHEAD *)info)->thread_info; 1036 REC_PRINT(__db_pg_trunc_print); 1037 REC_INTRO(__db_pg_trunc_read, ip, 1); 1038 1039 pglist = (db_pglist_t *) argp->list.data; 1040 nelem = argp->list.size / sizeof(db_pglist_t); 1041 if (DB_REDO(op)) { 1042 /* 1043 * First call __db_pg_truncate to find the truncation 1044 * point, truncate the file and return the new last_pgno. 1045 */ 1046 last_pgno = argp->last_pgno; 1047 if ((ret = __db_pg_truncate(dbc, NULL, pglist, 1048 NULL, &nelem, argp->next_free, &last_pgno, lsnp, 1)) != 0) 1049 goto out; 1050 1051 if (argp->last_free != PGNO_INVALID) { 1052 /* 1053 * Update the next pointer of the last page in 1054 * the freelist. If the truncation point is 1055 * beyond next_free then this is still in the freelist 1056 * otherwise the last_free page is at the end. 1057 */ 1058 if ((ret = __memp_fget(mpf, 1059 &argp->last_free, ip, NULL, 0, &meta)) == 0) { 1060 if (LOG_COMPARE(&LSN(meta), 1061 &argp->last_lsn) == 0) { 1062 REC_DIRTY(mpf, 1063 ip, dbc->priority, &meta); 1064 if (pglist->pgno > last_pgno) 1065 NEXT_PGNO(meta) = PGNO_INVALID; 1066 else 1067 NEXT_PGNO(meta) = pglist->pgno; 1068 LSN(meta) = *lsnp; 1069 } 1070 if ((ret = __memp_fput(mpf, ip, 1071 meta, file_dbp->priority)) != 0) 1072 goto out; 1073 meta = NULL; 1074 } else if (ret != DB_PAGE_NOTFOUND) 1075 goto out; 1076 } 1077 if ((ret = __memp_fget(mpf, &argp->meta, ip, NULL, 1078 0, &meta)) != 0) 1079 goto out; 1080 if (LOG_COMPARE(&LSN(meta), &argp->meta_lsn) == 0) { 1081 REC_DIRTY(mpf, ip, dbc->priority, &meta); 1082 if (argp->last_free == PGNO_INVALID) { 1083 if (nelem == 0) 1084 meta->free = PGNO_INVALID; 1085 else 1086 meta->free = pglist->pgno; 1087 } 1088 meta->last_pgno = last_pgno; 1089 LSN(meta) = *lsnp; 1090 } 1091 } else { 1092 /* Put the free list back in its original order. */ 1093 for (lp = pglist; lp < &pglist[nelem]; lp++) { 1094 if ((ret = __memp_fget(mpf, &lp->pgno, ip, 1095 NULL, DB_MPOOL_CREATE, &pagep)) != 0) 1096 goto out; 1097 if (IS_ZERO_LSN(LSN(pagep)) || 1098 LOG_COMPARE(&LSN(pagep), lsnp) == 0) { 1099 REC_DIRTY(mpf, ip, dbc->priority, &pagep); 1100 P_INIT(pagep, file_dbp->pgsize, lp->pgno, 1101 PGNO_INVALID, lp->next_pgno, 0, P_INVALID); 1102 LSN(pagep) = lp->lsn; 1103 } 1104 if ((ret = __memp_fput(mpf, 1105 ip, pagep, file_dbp->priority)) != 0) 1106 goto out; 1107 } 1108 /* 1109 * Link the truncated part back into the free list. 1110 * Its either after the last_free page or direclty 1111 * linked to the metadata page. 1112 */ 1113 if (argp->last_free != PGNO_INVALID) { 1114 if ((ret = __memp_fget(mpf, &argp->last_free, 1115 ip, NULL, DB_MPOOL_EDIT, &meta)) == 0) { 1116 if (LOG_COMPARE(&LSN(meta), lsnp) == 0) { 1117 NEXT_PGNO(meta) = argp->next_free; 1118 LSN(meta) = argp->last_lsn; 1119 } 1120 if ((ret = __memp_fput(mpf, ip, 1121 meta, file_dbp->priority)) != 0) 1122 goto out; 1123 } else if (ret != DB_PAGE_NOTFOUND) 1124 goto out; 1125 meta = NULL; 1126 } 1127 if ((ret = __memp_fget(mpf, &argp->meta, 1128 ip, NULL, DB_MPOOL_EDIT, &meta)) != 0) 1129 goto out; 1130 if (LOG_COMPARE(&LSN(meta), lsnp) == 0) { 1131 REC_DIRTY(mpf, ip, dbc->priority, &meta); 1132 /* 1133 * If we had to break up the list last_pgno 1134 * may only represent the end of the block. 1135 */ 1136 if (meta->last_pgno < argp->last_pgno) 1137 meta->last_pgno = argp->last_pgno; 1138 if (argp->last_free == PGNO_INVALID) 1139 meta->free = argp->next_free; 1140 LSN(meta) = argp->meta_lsn; 1141 } 1142 } 1143 1144 if ((ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0) 1145 goto out; 1146 1147 if (op == DB_TXN_ABORT) { 1148 /* 1149 * Put the pages back on the in memory free list. 1150 * If this is part of a multi-record truncate then 1151 * we need to find this batch, it may not be at the end. 1152 * If we aborted while writing one of the log records 1153 * then this set may still be in the list. 1154 */ 1155 if ((ret = __memp_get_freelist(mpf, &felem, &list)) != 0) 1156 goto out; 1157 if (list != NULL) { 1158 if (felem != 0 && list[felem - 1] > pglist->pgno) { 1159 __db_freelist_pos( 1160 pglist->pgno, list, felem, &pos); 1161 DB_ASSERT(env, pos < felem); 1162 if (pglist->pgno == list[pos]) 1163 goto done; 1164 pos++; 1165 } else if (felem != 0 && 1166 list[felem - 1] == pglist->pgno) 1167 goto done; 1168 else 1169 pos = felem; 1170 if ((ret = __memp_extend_freelist( 1171 mpf, felem + nelem, &list)) != 0) 1172 goto out; 1173 if (pos != felem) 1174 memmove(&list[nelem + pos], &list[pos], 1175 sizeof(*list) * (felem - pos)); 1176 for (lp = pglist; lp < &pglist[nelem]; lp++) 1177 list[pos++] = lp->pgno; 1178 } 1179 } 1180 1181done: *lsnp = argp->prev_lsn; 1182 ret = 0; 1183 1184out: REC_CLOSE; 1185#else 1186 /* 1187 * If HAVE_FTRUNCATE is not defined, we'll never see pg_trunc records 1188 * to recover. 1189 */ 1190 COMPQUIET(env, NULL); 1191 COMPQUIET(dbtp, NULL); 1192 COMPQUIET(lsnp, NULL); 1193 COMPQUIET(op, DB_TXN_ABORT); 1194 COMPQUIET(info, NULL); 1195 return (EINVAL); 1196#endif 1197} 1198/* 1199 * __db_pg_sort_44_recover -- 1200 * Recovery function for pg_sort. 1201 * This is deprecated and kept for replication upgrades. 1202 * 1203 * PUBLIC: int __db_pg_sort_44_recover 1204 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 1205 */ 1206int 1207__db_pg_sort_44_recover(env, dbtp, lsnp, op, info) 1208 ENV *env; 1209 DBT *dbtp; 1210 DB_LSN *lsnp; 1211 db_recops op; 1212 void *info; 1213{ 1214#ifdef HAVE_FTRUNCATE 1215 __db_pg_sort_44_args *argp; 1216 DB_THREAD_INFO *ip; 1217 DB *file_dbp; 1218 DBC *dbc; 1219 DBMETA *meta; 1220 DB_MPOOLFILE *mpf; 1221 PAGE *pagep; 1222 db_pglist_t *pglist, *lp; 1223 db_pgno_t pgno, *list; 1224 u_int32_t felem, nelem; 1225 int ret; 1226 1227 ip = ((DB_TXNHEAD *)info)->thread_info; 1228 REC_PRINT(__db_pg_sort_44_print); 1229 REC_INTRO(__db_pg_sort_44_read, ip, 1); 1230 1231 pglist = (db_pglist_t *) argp->list.data; 1232 nelem = argp->list.size / sizeof(db_pglist_t); 1233 if (DB_REDO(op)) { 1234 pgno = argp->last_pgno; 1235 __db_freelist_sort(pglist, nelem); 1236 if ((ret = __db_pg_truncate(dbc, NULL, 1237 pglist, NULL, &nelem, PGNO_INVALID, &pgno, lsnp, 1)) != 0) 1238 goto out; 1239 1240 if (argp->last_free != PGNO_INVALID) { 1241 if ((ret = __memp_fget(mpf, 1242 &argp->last_free, ip, NULL, 0, &meta)) == 0) { 1243 if (LOG_COMPARE(&LSN(meta), 1244 &argp->last_lsn) == 0) { 1245 REC_DIRTY(mpf, 1246 ip, dbc->priority, &meta); 1247 NEXT_PGNO(meta) = PGNO_INVALID; 1248 LSN(meta) = *lsnp; 1249 } 1250 if ((ret = __memp_fput(mpf, ip, 1251 meta, file_dbp->priority)) != 0) 1252 goto out; 1253 meta = NULL; 1254 } else if (ret != DB_PAGE_NOTFOUND) 1255 goto out; 1256 } 1257 if ((ret = __memp_fget(mpf, &argp->meta, ip, NULL, 1258 0, &meta)) != 0) 1259 goto out; 1260 if (LOG_COMPARE(&LSN(meta), &argp->meta_lsn) == 0) { 1261 REC_DIRTY(mpf, ip, dbc->priority, &meta); 1262 if (argp->last_free == PGNO_INVALID) { 1263 if (nelem == 0) 1264 meta->free = PGNO_INVALID; 1265 else 1266 meta->free = pglist->pgno; 1267 } 1268 meta->last_pgno = pgno; 1269 LSN(meta) = *lsnp; 1270 } 1271 } else { 1272 /* Put the free list back in its original order. */ 1273 for (lp = pglist; lp < &pglist[nelem]; lp++) { 1274 if ((ret = __memp_fget(mpf, &lp->pgno, ip, 1275 NULL, DB_MPOOL_CREATE, &pagep)) != 0) 1276 goto out; 1277 if (IS_ZERO_LSN(LSN(pagep)) || 1278 LOG_COMPARE(&LSN(pagep), lsnp) == 0) { 1279 REC_DIRTY(mpf, ip, dbc->priority, &pagep); 1280 if (lp == &pglist[nelem - 1]) 1281 pgno = PGNO_INVALID; 1282 else 1283 pgno = lp[1].pgno; 1284 1285 P_INIT(pagep, file_dbp->pgsize, 1286 lp->pgno, PGNO_INVALID, pgno, 0, P_INVALID); 1287 LSN(pagep) = lp->lsn; 1288 } 1289 if ((ret = __memp_fput(mpf, 1290 ip, pagep, file_dbp->priority)) != 0) 1291 goto out; 1292 } 1293 if (argp->last_free != PGNO_INVALID) { 1294 if ((ret = __memp_fget(mpf, &argp->last_free, 1295 ip, NULL, DB_MPOOL_EDIT, &meta)) == 0) { 1296 if (LOG_COMPARE(&LSN(meta), lsnp) == 0) { 1297 NEXT_PGNO(meta) = pglist->pgno; 1298 LSN(meta) = argp->last_lsn; 1299 } 1300 if ((ret = __memp_fput(mpf, ip, 1301 meta, file_dbp->priority)) != 0) 1302 goto out; 1303 } else if (ret != DB_PAGE_NOTFOUND) 1304 goto out; 1305 meta = NULL; 1306 } 1307 if ((ret = __memp_fget(mpf, &argp->meta, 1308 ip, NULL, DB_MPOOL_EDIT, &meta)) != 0) 1309 goto out; 1310 if (LOG_COMPARE(&LSN(meta), lsnp) == 0) { 1311 REC_DIRTY(mpf, ip, dbc->priority, &meta); 1312 meta->last_pgno = argp->last_pgno; 1313 if (argp->last_free == PGNO_INVALID) 1314 meta->free = pglist->pgno; 1315 LSN(meta) = argp->meta_lsn; 1316 } 1317 } 1318 if (op == DB_TXN_ABORT) { 1319 if ((ret = __memp_get_freelist(mpf, &felem, &list)) != 0) 1320 goto out; 1321 if (list != NULL) { 1322 DB_ASSERT(env, felem == 0 || 1323 argp->last_free == list[felem - 1]); 1324 if ((ret = __memp_extend_freelist( 1325 mpf, felem + nelem, &list)) != 0) 1326 goto out; 1327 for (lp = pglist; lp < &pglist[nelem]; lp++) 1328 list[felem++] = lp->pgno; 1329 } 1330 } 1331 1332 if ((ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0) 1333 goto out; 1334 1335done: *lsnp = argp->prev_lsn; 1336 ret = 0; 1337 1338out: REC_CLOSE; 1339#else 1340 /* 1341 * If HAVE_FTRUNCATE is not defined, we'll never see pg_sort records 1342 * to recover. 1343 */ 1344 COMPQUIET(env, NULL); 1345 COMPQUIET(dbtp, NULL); 1346 COMPQUIET(lsnp, NULL); 1347 COMPQUIET(op, DB_TXN_ABORT); 1348 COMPQUIET(info, NULL); 1349 return (EINVAL); 1350#endif 1351} 1352 1353/* 1354 * __db_pg_alloc_42_recover -- 1355 * Recovery function for pg_alloc. 1356 * 1357 * PUBLIC: int __db_pg_alloc_42_recover 1358 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 1359 */ 1360int 1361__db_pg_alloc_42_recover(env, dbtp, lsnp, op, info) 1362 ENV *env; 1363 DBT *dbtp; 1364 DB_LSN *lsnp; 1365 db_recops op; 1366 void *info; 1367{ 1368 __db_pg_alloc_42_args *argp; 1369 DB_THREAD_INFO *ip; 1370 DB *file_dbp; 1371 DBC *dbc; 1372 DBMETA *meta; 1373 DB_MPOOLFILE *mpf; 1374 PAGE *pagep; 1375 db_pgno_t pgno; 1376 int cmp_n, cmp_p, created, level, ret; 1377 1378 ip = ((DB_TXNHEAD *)info)->thread_info; 1379 meta = NULL; 1380 pagep = NULL; 1381 created = 0; 1382 REC_PRINT(__db_pg_alloc_42_print); 1383 REC_INTRO(__db_pg_alloc_42_read, ip, 0); 1384 1385 /* 1386 * Fix up the metadata page. If we're redoing the operation, we have 1387 * to get the metadata page and update its LSN and its free pointer. 1388 * If we're undoing the operation and the page was ever created, we put 1389 * it on the freelist. 1390 */ 1391 pgno = PGNO_BASE_MD; 1392 if ((ret = __memp_fget(mpf, &pgno, ip, NULL, 0, &meta)) != 0) { 1393 /* The metadata page must always exist on redo. */ 1394 if (DB_REDO(op)) { 1395 ret = __db_pgerr(file_dbp, pgno, ret); 1396 goto out; 1397 } else 1398 goto done; 1399 } 1400 cmp_n = LOG_COMPARE(lsnp, &LSN(meta)); 1401 cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn); 1402 CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn); 1403 if (cmp_p == 0 && DB_REDO(op)) { 1404 /* Need to redo update described. */ 1405 REC_DIRTY(mpf, ip, file_dbp->priority, &meta); 1406 LSN(meta) = *lsnp; 1407 meta->free = argp->next; 1408 if (argp->pgno > meta->last_pgno) 1409 meta->last_pgno = argp->pgno; 1410 } else if (cmp_n == 0 && DB_UNDO(op)) { 1411 goto no_rollback; 1412 } 1413 1414 /* 1415 * Fix up the allocated page. If the page does not exist 1416 * and we can truncate it then don't create it. 1417 * Otherwise if we're redoing the operation, we have 1418 * to get the page (creating it if it doesn't exist), and update its 1419 * LSN. If we're undoing the operation, we have to reset the page's 1420 * LSN and put it on the free list, or truncate it. 1421 */ 1422 if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) { 1423 /* 1424 * We have to be able to identify if a page was newly 1425 * created so we can recover it properly. We cannot simply 1426 * look for an empty header, because hash uses a pgin 1427 * function that will set the header. Instead, we explicitly 1428 * try for the page without CREATE and if that fails, then 1429 * create it. 1430 */ 1431 if ((ret = __memp_fget(mpf, &argp->pgno, 1432 ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0) { 1433 if (DB_UNDO(op) && ret == ENOSPC) 1434 goto do_truncate; 1435 ret = __db_pgerr(file_dbp, argp->pgno, ret); 1436 goto out; 1437 } 1438 created = 1; 1439 } 1440 1441 /* Fix up the allocated page. */ 1442 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 1443 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->page_lsn); 1444 1445 /* 1446 * If an initial allocation is aborted and then reallocated during 1447 * an archival restore the log record will have an LSN for the page 1448 * but the page will be empty. 1449 */ 1450 if (IS_ZERO_LSN(LSN(pagep)) || 1451 (IS_ZERO_LSN(argp->page_lsn) && IS_INIT_LSN(LSN(pagep)))) 1452 cmp_p = 0; 1453 1454 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->page_lsn); 1455 /* 1456 * Another special case we have to handle is if we ended up with a 1457 * page of all 0's which can happen if we abort between allocating a 1458 * page in mpool and initializing it. In that case, even if we're 1459 * undoing, we need to re-initialize the page. 1460 */ 1461 if (DB_REDO(op) && cmp_p == 0) { 1462 /* Need to redo update described. */ 1463 switch (argp->ptype) { 1464 case P_LBTREE: 1465 case P_LRECNO: 1466 case P_LDUP: 1467 level = LEAFLEVEL; 1468 break; 1469 default: 1470 level = 0; 1471 break; 1472 } 1473 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1474 P_INIT(pagep, file_dbp->pgsize, 1475 argp->pgno, PGNO_INVALID, PGNO_INVALID, level, argp->ptype); 1476 1477 pagep->lsn = *lsnp; 1478 } else if (DB_UNDO(op) && (cmp_n == 0 || created)) { 1479 /* 1480 * This is where we handle the case of a 0'd page (pagep->pgno 1481 * is equal to PGNO_INVALID). 1482 * Undo the allocation, reinitialize the page and 1483 * link its next pointer to the free list. 1484 */ 1485 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1486 P_INIT(pagep, file_dbp->pgsize, 1487 argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID); 1488 1489 pagep->lsn = argp->page_lsn; 1490 } 1491 1492do_truncate: 1493 /* 1494 * We cannot undo things from 4.2 land, because we nolonger 1495 * have limbo processing. 1496 */ 1497 if ((pagep == NULL || IS_ZERO_LSN(LSN(pagep))) && 1498 IS_ZERO_LSN(argp->page_lsn) && DB_UNDO(op)) { 1499no_rollback: __db_errx(env, 1500"Cannot replicate prepared transactions from master running release 4.2 "); 1501 ret = __env_panic(env, EINVAL); 1502 } 1503 1504 if (pagep != NULL && 1505 (ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0) 1506 goto out; 1507 pagep = NULL; 1508 1509 if ((ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0) 1510 goto out; 1511 meta = NULL; 1512 1513done: *lsnp = argp->prev_lsn; 1514 ret = 0; 1515 1516out: if (pagep != NULL) 1517 (void)__memp_fput(mpf, ip, pagep, file_dbp->priority); 1518 if (meta != NULL) 1519 (void)__memp_fput(mpf, ip, meta, file_dbp->priority); 1520 REC_CLOSE; 1521} 1522 1523/* 1524 * __db_pg_free_recover_42_int -- 1525 */ 1526static int 1527__db_pg_free_recover_42_int(env, ip, argp, file_dbp, lsnp, mpf, op, data) 1528 ENV *env; 1529 DB_THREAD_INFO *ip; 1530 __db_pg_freedata_42_args *argp; 1531 DB *file_dbp; 1532 DB_LSN *lsnp; 1533 DB_MPOOLFILE *mpf; 1534 db_recops op; 1535 int data; 1536{ 1537 DBMETA *meta; 1538 DB_LSN copy_lsn; 1539 PAGE *pagep, *prevp; 1540 int cmp_n, cmp_p, is_meta, ret; 1541 1542 meta = NULL; 1543 pagep = NULL; 1544 prevp = NULL; 1545 1546 /* 1547 * Get the "metapage". This will either be the metapage 1548 * or the previous page in the free list if we are doing 1549 * sorted allocations. If its a previous page then 1550 * we will not be truncating. 1551 */ 1552 is_meta = argp->meta_pgno == PGNO_BASE_MD; 1553 1554 REC_FGET(mpf, ip, argp->meta_pgno, &meta, check_meta); 1555 1556 if (argp->meta_pgno != PGNO_BASE_MD) 1557 prevp = (PAGE *)meta; 1558 1559 cmp_n = LOG_COMPARE(lsnp, &LSN(meta)); 1560 cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn); 1561 CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn); 1562 1563 /* 1564 * Fix up the metadata page. If we're redoing or undoing the operation 1565 * we get the page and update its LSN, last and free pointer. 1566 */ 1567 if (cmp_p == 0 && DB_REDO(op)) { 1568 /* Need to redo the deallocation. */ 1569 REC_DIRTY(mpf, ip, file_dbp->priority, &meta); 1570 if (prevp == NULL) 1571 meta->free = argp->pgno; 1572 else 1573 NEXT_PGNO(prevp) = argp->pgno; 1574 /* 1575 * If this was a compensating transaction and 1576 * we are a replica, then we never executed the 1577 * original allocation which incremented meta->free. 1578 */ 1579 if (prevp == NULL && meta->last_pgno < meta->free) 1580 meta->last_pgno = meta->free; 1581 LSN(meta) = *lsnp; 1582 } else if (cmp_n == 0 && DB_UNDO(op)) { 1583 /* Need to undo the deallocation. */ 1584 REC_DIRTY(mpf, ip, file_dbp->priority, &meta); 1585 if (prevp == NULL) 1586 meta->free = argp->next; 1587 else 1588 NEXT_PGNO(prevp) = argp->next; 1589 LSN(meta) = argp->meta_lsn; 1590 if (prevp == NULL && meta->last_pgno < argp->pgno) 1591 meta->last_pgno = argp->pgno; 1592 } 1593 1594check_meta: 1595 if (ret != 0 && is_meta) { 1596 /* The metadata page must always exist. */ 1597 ret = __db_pgerr(file_dbp, argp->meta_pgno, ret); 1598 goto out; 1599 } 1600 1601 /* 1602 * Get the freed page. If we support truncate then don't 1603 * create the page if we are going to free it. If we're 1604 * redoing the operation we get the page and explicitly discard 1605 * its contents, then update its LSN. If we're undoing the 1606 * operation, we get the page and restore its header. 1607 * If we don't support truncate, then we must create the page 1608 * and roll it back. 1609 */ 1610 if ((ret = __memp_fget(mpf, &argp->pgno, 1611 ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0) 1612 goto out; 1613 1614 (void)__ua_memcpy(©_lsn, &LSN(argp->header.data), sizeof(DB_LSN)); 1615 cmp_n = IS_ZERO_LSN(LSN(pagep)) ? 0 : LOG_COMPARE(lsnp, &LSN(pagep)); 1616 cmp_p = LOG_COMPARE(&LSN(pagep), ©_lsn); 1617 1618 CHECK_LSN(env, op, cmp_p, &LSN(pagep), ©_lsn); 1619 if (DB_REDO(op) && 1620 (cmp_p == 0 || 1621 (IS_ZERO_LSN(copy_lsn) && 1622 LOG_COMPARE(&LSN(pagep), &argp->meta_lsn) <= 0))) { 1623 /* Need to redo the deallocation. */ 1624 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1625 P_INIT(pagep, file_dbp->pgsize, 1626 argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID); 1627 pagep->lsn = *lsnp; 1628 } else if (cmp_n == 0 && DB_UNDO(op)) { 1629 /* Need to reallocate the page. */ 1630 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1631 memcpy(pagep, argp->header.data, argp->header.size); 1632 if (data) 1633 memcpy((u_int8_t*)pagep + HOFFSET(pagep), 1634 argp->data.data, argp->data.size); 1635 } 1636 if (pagep != NULL && 1637 (ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0) 1638 goto out; 1639 1640 pagep = NULL; 1641 if (meta != NULL && 1642 (ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0) 1643 goto out; 1644 meta = NULL; 1645 1646 ret = 0; 1647 1648out: if (pagep != NULL) 1649 (void)__memp_fput(mpf, ip, pagep, file_dbp->priority); 1650 if (meta != NULL) 1651 (void)__memp_fput(mpf, ip, meta, file_dbp->priority); 1652 1653 return (ret); 1654} 1655 1656/* 1657 * __db_pg_free_42_recover -- 1658 * Recovery function for pg_free. 1659 * 1660 * PUBLIC: int __db_pg_free_42_recover 1661 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 1662 */ 1663int 1664__db_pg_free_42_recover(env, dbtp, lsnp, op, info) 1665 ENV *env; 1666 DBT *dbtp; 1667 DB_LSN *lsnp; 1668 db_recops op; 1669 void *info; 1670{ 1671 __db_pg_free_42_args *argp; 1672 DB *file_dbp; 1673 DBC *dbc; 1674 DB_MPOOLFILE *mpf; 1675 DB_THREAD_INFO *ip; 1676 int ret; 1677 1678 ip = ((DB_TXNHEAD *)info)->thread_info; 1679 REC_PRINT(__db_pg_free_42_print); 1680 REC_INTRO(__db_pg_free_42_read, ip, 0); 1681 1682 ret = __db_pg_free_recover_42_int(env, ip, 1683 (__db_pg_freedata_42_args *)argp, file_dbp, lsnp, mpf, op, 0); 1684 1685done: *lsnp = argp->prev_lsn; 1686out: 1687 REC_CLOSE; 1688} 1689 1690/* 1691 * __db_pg_freedata_42_recover -- 1692 * Recovery function for pg_freedata. 1693 * 1694 * PUBLIC: int __db_pg_freedata_42_recover 1695 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 1696 */ 1697int 1698__db_pg_freedata_42_recover(env, dbtp, lsnp, op, info) 1699 ENV *env; 1700 DBT *dbtp; 1701 DB_LSN *lsnp; 1702 db_recops op; 1703 void *info; 1704{ 1705 __db_pg_freedata_42_args *argp; 1706 DB *file_dbp; 1707 DBC *dbc; 1708 DB_MPOOLFILE *mpf; 1709 DB_THREAD_INFO *ip; 1710 int ret; 1711 1712 ip = ((DB_TXNHEAD *)info)->thread_info; 1713 REC_PRINT(__db_pg_freedata_42_print); 1714 REC_INTRO(__db_pg_freedata_42_read, ip, 0); 1715 1716 ret = __db_pg_free_recover_42_int( 1717 env, ip, argp, file_dbp, lsnp, mpf, op, 1); 1718 1719done: *lsnp = argp->prev_lsn; 1720out: 1721 REC_CLOSE; 1722} 1723 1724/* 1725 * __db_relink_42_recover -- 1726 * Recovery function for relink. 1727 * 1728 * PUBLIC: int __db_relink_42_recover 1729 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 1730 */ 1731int 1732__db_relink_42_recover(env, dbtp, lsnp, op, info) 1733 ENV *env; 1734 DBT *dbtp; 1735 DB_LSN *lsnp; 1736 db_recops op; 1737 void *info; 1738{ 1739 __db_relink_42_args *argp; 1740 DB_THREAD_INFO *ip; 1741 DB *file_dbp; 1742 DBC *dbc; 1743 DB_MPOOLFILE *mpf; 1744 PAGE *pagep; 1745 int cmp_n, cmp_p, modified, ret; 1746 1747 ip = ((DB_TXNHEAD *)info)->thread_info; 1748 pagep = NULL; 1749 REC_PRINT(__db_relink_42_print); 1750 REC_INTRO(__db_relink_42_read, ip, 0); 1751 1752 /* 1753 * There are up to three pages we need to check -- the page, and the 1754 * previous and next pages, if they existed. For a page add operation, 1755 * the current page is the result of a split and is being recovered 1756 * elsewhere, so all we need do is recover the next page. 1757 */ 1758 if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) { 1759 if (DB_REDO(op)) { 1760 ret = __db_pgerr(file_dbp, argp->pgno, ret); 1761 goto out; 1762 } 1763 goto next2; 1764 } 1765 if (argp->opcode == DB_ADD_PAGE_COMPAT) 1766 goto next1; 1767 1768 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->lsn); 1769 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->lsn); 1770 if (cmp_p == 0 && DB_REDO(op)) { 1771 /* Redo the relink. */ 1772 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1773 pagep->lsn = *lsnp; 1774 } else if (LOG_COMPARE(lsnp, &LSN(pagep)) == 0 && DB_UNDO(op)) { 1775 /* Undo the relink. */ 1776 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1777 pagep->next_pgno = argp->next; 1778 pagep->prev_pgno = argp->prev; 1779 pagep->lsn = argp->lsn; 1780 } 1781next1: if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0) 1782 goto out; 1783 pagep = NULL; 1784 1785next2: if ((ret = __memp_fget(mpf, &argp->next, ip, NULL, 0, &pagep)) != 0) { 1786 if (DB_REDO(op)) { 1787 ret = __db_pgerr(file_dbp, argp->next, ret); 1788 goto out; 1789 } 1790 goto prev; 1791 } 1792 modified = 0; 1793 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 1794 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->lsn_next); 1795 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->lsn_next); 1796 if ((argp->opcode == DB_REM_PAGE_COMPAT && cmp_p == 0 && DB_REDO(op)) || 1797 (argp->opcode == DB_ADD_PAGE_COMPAT && cmp_n == 0 && DB_UNDO(op))) { 1798 /* Redo the remove or undo the add. */ 1799 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1800 pagep->prev_pgno = argp->prev; 1801 modified = 1; 1802 } else if ((argp->opcode == DB_REM_PAGE_COMPAT && 1803 cmp_n == 0 && DB_UNDO(op)) || 1804 (argp->opcode == DB_ADD_PAGE_COMPAT && cmp_p == 0 && DB_REDO(op))) { 1805 /* Undo the remove or redo the add. */ 1806 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1807 pagep->prev_pgno = argp->pgno; 1808 modified = 1; 1809 } 1810 if (modified) { 1811 if (DB_UNDO(op)) 1812 pagep->lsn = argp->lsn_next; 1813 else 1814 pagep->lsn = *lsnp; 1815 } 1816 if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0) 1817 goto out; 1818 pagep = NULL; 1819 if (argp->opcode == DB_ADD_PAGE_COMPAT) 1820 goto done; 1821 1822prev: if ((ret = __memp_fget(mpf, &argp->prev, ip, NULL, 0, &pagep)) != 0) { 1823 if (DB_REDO(op)) { 1824 ret = __db_pgerr(file_dbp, argp->prev, ret); 1825 goto out; 1826 } 1827 goto done; 1828 } 1829 modified = 0; 1830 cmp_p = LOG_COMPARE(&LSN(pagep), &argp->lsn_prev); 1831 CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->lsn_prev); 1832 if (cmp_p == 0 && DB_REDO(op)) { 1833 /* Redo the relink. */ 1834 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1835 pagep->next_pgno = argp->next; 1836 modified = 1; 1837 } else if (LOG_COMPARE(lsnp, &LSN(pagep)) == 0 && DB_UNDO(op)) { 1838 /* Undo the relink. */ 1839 REC_DIRTY(mpf, ip, file_dbp->priority, &pagep); 1840 pagep->next_pgno = argp->pgno; 1841 modified = 1; 1842 } 1843 if (modified) { 1844 if (DB_UNDO(op)) 1845 pagep->lsn = argp->lsn_prev; 1846 else 1847 pagep->lsn = *lsnp; 1848 } 1849 if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0) 1850 goto out; 1851 pagep = NULL; 1852 1853done: *lsnp = argp->prev_lsn; 1854 ret = 0; 1855 1856out: if (pagep != NULL) 1857 (void)__memp_fput(mpf, ip, pagep, file_dbp->priority); 1858 REC_CLOSE; 1859} 1860