1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1999-2009 Oracle. 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/db_verify.h" 14#include "dbinc/db_am.h" 15#include "dbinc/mp.h" 16#include "dbinc/qam.h" 17/* 18 * __qam_vrfy_meta -- 19 * Verify the queue-specific part of a metadata page. 20 * 21 * PUBLIC: int __qam_vrfy_meta __P((DB *, VRFY_DBINFO *, QMETA *, 22 * PUBLIC: db_pgno_t, u_int32_t)); 23 */ 24int 25__qam_vrfy_meta(dbp, vdp, meta, pgno, flags) 26 DB *dbp; 27 VRFY_DBINFO *vdp; 28 QMETA *meta; 29 db_pgno_t pgno; 30 u_int32_t flags; 31{ 32 ENV *env; 33 QUEUE *qp; 34 VRFY_PAGEINFO *pip; 35 db_pgno_t *extents, extid, first, last; 36 size_t len; 37 int count, i, isbad, nextents, ret, t_ret; 38 char *buf, **names; 39 40 COMPQUIET(count, 0); 41 42 env = dbp->env; 43 qp = (QUEUE *)dbp->q_internal; 44 extents = NULL; 45 first = last = 0; 46 isbad = 0; 47 buf = NULL; 48 names = NULL; 49 50 if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) 51 return (ret); 52 53 /* 54 * Queue can't be used in subdatabases, so if this isn't set 55 * something very odd is going on. 56 */ 57 if (!F_ISSET(pip, VRFY_INCOMPLETE)) 58 EPRINT((env, "Page %lu: queue databases must be one-per-file", 59 (u_long)pgno)); 60 61 /* 62 * Because the metapage pointers are rolled forward by 63 * aborting transactions, the extent of the queue may 64 * extend beyond the allocated pages, so we do 65 * not check that meta_current is within the allocated 66 * pages. 67 */ 68 69 /* 70 * re_len: If this is bad, we can't safely verify queue data pages, so 71 * return DB_VERIFY_FATAL 72 */ 73 if (DB_ALIGN(meta->re_len + sizeof(QAMDATA) - 1, sizeof(u_int32_t)) * 74 meta->rec_page + QPAGE_SZ(dbp) > dbp->pgsize) { 75 EPRINT((env, 76 "Page %lu: queue record length %lu too high for page size and recs/page", 77 (u_long)pgno, (u_long)meta->re_len)); 78 ret = DB_VERIFY_FATAL; 79 goto err; 80 } else { 81 /* 82 * We initialize the Queue internal pointer; we may need 83 * it when handling extents. It would get set up in open, 84 * if we called open normally, but we don't. 85 */ 86 vdp->re_pad = meta->re_pad; 87 qp->re_pad = (int)meta->re_pad; 88 qp->re_len = vdp->re_len = meta->re_len; 89 qp->rec_page = vdp->rec_page = meta->rec_page; 90 qp->page_ext = vdp->page_ext = meta->page_ext; 91 } 92 93 /* 94 * There's no formal maximum extentsize, and a 0 value represents 95 * no extents, so there's nothing to verify. 96 * 97 * Note that since QUEUE databases can't have subdatabases, it's an 98 * error to see more than one QUEUE metadata page in a single 99 * verifier run. Theoretically, this should really be a structure 100 * rather than a per-page check, but since we're setting qp fields 101 * here (and have only one qp to set) we raise the alarm now if 102 * this assumption fails. (We need the qp info to be reasonable 103 * before we do per-page verification of queue extents.) 104 */ 105 if (F_ISSET(vdp, VRFY_QMETA_SET)) { 106 isbad = 1; 107 EPRINT((env, 108 "Page %lu: database contains multiple Queue metadata pages", 109 (u_long)pgno)); 110 goto err; 111 } 112 F_SET(vdp, VRFY_QMETA_SET); 113 qp->page_ext = meta->page_ext; 114 dbp->pgsize = meta->dbmeta.pagesize; 115 qp->q_meta = pgno; 116 qp->q_root = pgno + 1; 117 vdp->first_recno = meta->first_recno; 118 vdp->last_recno = meta->cur_recno; 119 if (qp->page_ext != 0) { 120 first = QAM_RECNO_EXTENT(dbp, vdp->first_recno); 121 last = QAM_RECNO_EXTENT(dbp, vdp->last_recno); 122 } 123 124 /* 125 * Look in the data directory to see if there are any extents 126 * around that are not in the range of the queue. If so, 127 * then report that and look there if we are salvaging. 128 */ 129 130 if ((ret = __db_appname(env, 131 DB_APP_DATA, qp->dir, NULL, &buf)) != 0) 132 goto err; 133 if ((ret = __os_dirlist(env, buf, 0, &names, &count)) != 0) 134 goto err; 135 __os_free(env, buf); 136 buf = NULL; 137 138 len = strlen(QUEUE_EXTENT_HEAD) + strlen(qp->name) + 1; 139 if ((ret = __os_malloc(env, len, &buf)) != 0) 140 goto err; 141 len = (size_t)snprintf(buf, len, QUEUE_EXTENT_HEAD, qp->name); 142 for (i = nextents = 0; i < count; i++) { 143 if (strncmp(names[i], buf, len) == 0) { 144 /* Only save extents out of bounds. */ 145 extid = (db_pgno_t)strtoul(&names[i][len], NULL, 10); 146 if (qp->page_ext != 0 && 147 (last > first ? 148 (extid >= first && extid <= last) : 149 (extid >= first || extid <= last))) 150 continue; 151 if (extents == NULL && (ret = __os_malloc( 152 env, (size_t)(count - i) * sizeof(extid), 153 &extents)) != 0) 154 goto err; 155 extents[nextents] = extid; 156 nextents++; 157 } 158 } 159 if (nextents > 0) 160 __db_errx(env, 161 "Warning: %d extra extent files found", nextents); 162 vdp->nextents = nextents; 163 vdp->extents = extents; 164 165err: if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0) 166 ret = t_ret; 167 if (names != NULL) 168 __os_dirfree(env, names, count); 169 if (buf != NULL) 170 __os_free(env, buf); 171 if (ret != 0 && extents != NULL) 172 __os_free(env, extents); 173 if (LF_ISSET(DB_SALVAGE) && 174 (t_ret = __db_salvage_markdone(vdp, pgno)) != 0 && ret == 0) 175 ret = t_ret; 176 return (ret == 0 && isbad == 1 ? DB_VERIFY_BAD : ret); 177} 178 179/* 180 * __qam_meta2pgset -- 181 * For a given Queue meta page, add all of the db's pages to the pgset. Dealing 182 * with extents complicates things, as it is possible for there to be gaps in 183 * the page number sequence (the user could have re-inserted record numbers that 184 * had been on deleted extents) so we test the existence of each extent before 185 * adding its pages to the pgset. If there are no extents, just loop from 186 * first_recno to last_recno. 187 * 188 * PUBLIC: int __qam_meta2pgset __P((DB *, VRFY_DBINFO *, DB *)); 189 */ 190int 191__qam_meta2pgset(dbp, vdp, pgset) 192 DB *dbp; 193 VRFY_DBINFO *vdp; 194 DB *pgset; 195{ 196 DBC *dbc; 197 PAGE *h; 198 db_pgno_t first, last, pgno, pg_ext, stop; 199 int ret, t_ret; 200 u_int32_t i; 201 202 ret = 0; 203 h = NULL; 204 if (vdp->last_recno <= vdp->first_recno) 205 return 0; 206 207 pg_ext = vdp->page_ext; 208 209 first = QAM_RECNO_PAGE(dbp, vdp->first_recno); 210 211 /* 212 * last_recno gives the next recno to be allocated, we want the last 213 * allocated recno. 214 */ 215 last = QAM_RECNO_PAGE(dbp, vdp->last_recno - 1); 216 217 if (first == PGNO_INVALID || last == PGNO_INVALID) 218 return (DB_VERIFY_BAD); 219 220 pgno = first; 221 if (first > last) 222 stop = QAM_RECNO_PAGE(dbp, UINT32_MAX); 223 else 224 stop = last; 225 226 /* 227 * If this db doesn't have extents, just add all page numbers from first 228 * to last. 229 */ 230 if (pg_ext == 0) { 231 for (pgno = first; pgno <= stop; pgno++) 232 if ((ret = __db_vrfy_pgset_inc( 233 pgset, vdp->thread_info, pgno)) != 0) 234 break; 235 if (first > last) 236 for (pgno = 1; pgno <= last; pgno++) 237 if ((ret = __db_vrfy_pgset_inc( 238 pgset, vdp->thread_info, pgno)) != 0) 239 break; 240 241 return ret; 242 } 243 244 if ((ret = __db_cursor(dbp, vdp->thread_info, NULL, &dbc, 0)) != 0) 245 return (ret); 246 /* 247 * Check if we can get the first page of each extent. If we can, then 248 * add all of that extent's pages to the pgset. If we can't, assume the 249 * extent doesn't exist and don't add any pages, if we're wrong we'll 250 * find the pages in __db_vrfy_walkpages. 251 */ 252begin: for (; pgno <= stop; pgno += pg_ext) { 253 if ((ret = __qam_fget(dbc, &pgno, 0, &h)) != 0) { 254 if (ret == ENOENT || ret == DB_PAGE_NOTFOUND) { 255 ret = 0; 256 continue; 257 } 258 goto err; 259 } 260 if ((ret = __qam_fput(dbc, pgno, h, dbp->priority)) != 0) 261 goto err; 262 263 for (i = 0; i < pg_ext && pgno + i <= last; i++) 264 if ((ret = __db_vrfy_pgset_inc( 265 pgset, vdp->thread_info, pgno + i)) != 0) 266 goto err; 267 268 /* The first recno won't always occur on the first page of the 269 * extent. Back up to the beginning of the extent before the 270 * end of the loop so that the increment works correctly. 271 */ 272 if (pgno == first) 273 pgno = pgno % pg_ext + 1; 274 } 275 276 if (first > last) { 277 pgno = 1; 278 first = last; 279 stop = last; 280 goto begin; 281 } 282 283err: 284 if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0) 285 ret = t_ret; 286 287 return ret; 288} 289 290/* 291 * __qam_vrfy_data -- 292 * Verify a queue data page. 293 * 294 * PUBLIC: int __qam_vrfy_data __P((DB *, VRFY_DBINFO *, QPAGE *, 295 * PUBLIC: db_pgno_t, u_int32_t)); 296 */ 297int 298__qam_vrfy_data(dbp, vdp, h, pgno, flags) 299 DB *dbp; 300 VRFY_DBINFO *vdp; 301 QPAGE *h; 302 db_pgno_t pgno; 303 u_int32_t flags; 304{ 305 DB fakedb; 306 struct __queue fakeq; 307 QAMDATA *qp; 308 db_recno_t i; 309 310 /* 311 * Not much to do here, except make sure that flags are reasonable. 312 * 313 * QAM_GET_RECORD assumes a properly initialized q_internal 314 * structure, however, and we don't have one, so we play 315 * some gross games to fake it out. 316 */ 317 fakedb.q_internal = &fakeq; 318 fakedb.flags = dbp->flags; 319 fakeq.re_len = vdp->re_len; 320 321 for (i = 0; i < vdp->rec_page; i++) { 322 qp = QAM_GET_RECORD(&fakedb, h, i); 323 if ((u_int8_t *)qp >= (u_int8_t *)h + dbp->pgsize) { 324 EPRINT((dbp->env, 325 "Page %lu: queue record %lu extends past end of page", 326 (u_long)pgno, (u_long)i)); 327 return (DB_VERIFY_BAD); 328 } 329 330 if (qp->flags & ~(QAM_VALID | QAM_SET)) { 331 EPRINT((dbp->env, 332 "Page %lu: queue record %lu has bad flags (%#lx)", 333 (u_long)pgno, (u_long)i, (u_long)qp->flags)); 334 return (DB_VERIFY_BAD); 335 } 336 } 337 338 return (0); 339} 340 341/* 342 * __qam_vrfy_structure -- 343 * Verify a queue database structure, such as it is. 344 * 345 * PUBLIC: int __qam_vrfy_structure __P((DB *, VRFY_DBINFO *, u_int32_t)); 346 */ 347int 348__qam_vrfy_structure(dbp, vdp, flags) 349 DB *dbp; 350 VRFY_DBINFO *vdp; 351 u_int32_t flags; 352{ 353 VRFY_PAGEINFO *pip; 354 db_pgno_t i; 355 int ret, isbad; 356 357 isbad = 0; 358 359 if ((ret = __db_vrfy_getpageinfo(vdp, PGNO_BASE_MD, &pip)) != 0) 360 return (ret); 361 362 if (pip->type != P_QAMMETA) { 363 EPRINT((dbp->env, 364 "Page %lu: queue database has no meta page", 365 (u_long)PGNO_BASE_MD)); 366 isbad = 1; 367 goto err; 368 } 369 370 if ((ret = __db_vrfy_pgset_inc(vdp->pgset, vdp->thread_info, 0)) != 0) 371 goto err; 372 373 for (i = 1; i <= vdp->last_pgno; i++) { 374 /* Send feedback to the application about our progress. */ 375 if (!LF_ISSET(DB_SALVAGE)) 376 __db_vrfy_struct_feedback(dbp, vdp); 377 378 if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0 || 379 (ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0) 380 return (ret); 381 if (!F_ISSET(pip, VRFY_IS_ALLZEROES) && 382 pip->type != P_QAMDATA) { 383 EPRINT((dbp->env, 384 "Page %lu: queue database page of incorrect type %lu", 385 (u_long)i, (u_long)pip->type)); 386 isbad = 1; 387 goto err; 388 } else if ((ret = __db_vrfy_pgset_inc(vdp->pgset, 389 vdp->thread_info, i)) != 0) 390 goto err; 391 } 392 393err: if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0) 394 return (ret); 395 return (isbad == 1 ? DB_VERIFY_BAD : 0); 396} 397 398/* 399 * __qam_vrfy_walkqueue -- 400 * Do a "walkpages" per-page verification pass over the set of Queue 401 * extent pages. 402 * 403 * PUBLIC: int __qam_vrfy_walkqueue __P((DB *, VRFY_DBINFO *, void *, 404 * PUBLIC: int (*)(void *, const void *), u_int32_t)); 405 */ 406int 407__qam_vrfy_walkqueue(dbp, vdp, handle, callback, flags) 408 DB *dbp; 409 VRFY_DBINFO *vdp; 410 void *handle; 411 int (*callback) __P((void *, const void *)); 412 u_int32_t flags; 413{ 414 DBC *dbc; 415 ENV *env; 416 PAGE *h; 417 QUEUE *qp; 418 VRFY_PAGEINFO *pip; 419 db_pgno_t first, i, last, pg_ext, stop; 420 int isbad, nextents, ret, t_ret; 421 422 COMPQUIET(h, NULL); 423 424 env = dbp->env; 425 qp = dbp->q_internal; 426 pip = NULL; 427 pg_ext = qp->page_ext; 428 isbad = ret = t_ret = 0; 429 h = NULL; 430 431 /* If this database has no extents, we've seen all the pages already. */ 432 if (pg_ext == 0) 433 return (0); 434 435 first = QAM_RECNO_PAGE(dbp, vdp->first_recno); 436 last = QAM_RECNO_PAGE(dbp, vdp->last_recno); 437 438 i = first; 439 if (first > last) 440 stop = QAM_RECNO_PAGE(dbp, UINT32_MAX); 441 else 442 stop = last; 443 nextents = vdp->nextents; 444 445 /* Verify/salvage each page. */ 446 if ((ret = __db_cursor(dbp, vdp->thread_info, NULL, &dbc, 0)) != 0) 447 return (ret); 448begin: for (; i <= stop; i++) { 449 /* 450 * If DB_SALVAGE is set, we inspect our database of completed 451 * pages, and skip any we've already printed in the subdb pass. 452 */ 453 if (LF_ISSET(DB_SALVAGE) && (__db_salvage_isdone(vdp, i) != 0)) 454 continue; 455 if ((t_ret = __qam_fget(dbc, &i, 0, &h)) != 0) { 456 if (t_ret == ENOENT || t_ret == DB_PAGE_NOTFOUND) { 457 i += (pg_ext - ((i - 1) % pg_ext)) - 1; 458 continue; 459 } 460 461 /* 462 * If an individual page get fails, keep going iff 463 * we're salvaging. 464 */ 465 if (LF_ISSET(DB_SALVAGE)) { 466 if (ret == 0) 467 ret = t_ret; 468 continue; 469 } 470 h = NULL; 471 ret = t_ret; 472 goto err; 473 } 474 475 if (LF_ISSET(DB_SALVAGE)) { 476 /* 477 * We pretty much don't want to quit unless a 478 * bomb hits. May as well return that something 479 * was screwy, however. 480 */ 481 if ((t_ret = __db_salvage_pg(dbp, 482 vdp, i, h, handle, callback, flags)) != 0) { 483 if (ret == 0) 484 ret = t_ret; 485 isbad = 1; 486 } 487 } else { 488 /* 489 * If we are not salvaging, and we get any error 490 * other than DB_VERIFY_BAD, return immediately; 491 * it may not be safe to proceed. If we get 492 * DB_VERIFY_BAD, keep going; listing more errors 493 * may make it easier to diagnose problems and 494 * determine the magnitude of the corruption. 495 */ 496 if ((ret = __db_vrfy_common(dbp, 497 vdp, h, i, flags)) == DB_VERIFY_BAD) 498 isbad = 1; 499 else if (ret != 0) 500 goto err; 501 502 __db_vrfy_struct_feedback(dbp, vdp); 503 504 if ((ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0) 505 goto err; 506 if (F_ISSET(pip, VRFY_IS_ALLZEROES)) 507 goto put; 508 if (pip->type != P_QAMDATA) { 509 EPRINT((env, 510 "Page %lu: queue database page of incorrect type %lu", 511 (u_long)i, (u_long)pip->type)); 512 isbad = 1; 513 goto err; 514 } 515 if ((ret = __db_vrfy_pgset_inc(vdp->pgset, 516 vdp->thread_info, i)) != 0) 517 goto err; 518 if ((ret = __qam_vrfy_data(dbp, vdp, 519 (QPAGE *)h, i, flags)) == DB_VERIFY_BAD) 520 isbad = 1; 521 else if (ret != 0) 522 goto err; 523 524put: if ((ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0) 525 goto err1; 526 pip = NULL; 527 } 528 529 /* Again, keep going iff we're salvaging. */ 530 if ((t_ret = __qam_fput(dbc, i, h, dbp->priority)) != 0) { 531 if (LF_ISSET(DB_SALVAGE)) { 532 if (ret == 0) 533 ret = t_ret; 534 continue; 535 } 536 ret = t_ret; 537 goto err1; 538 } 539 } 540 541 if (first > last) { 542 i = 1; 543 stop = last; 544 first = last; 545 goto begin; 546 } 547 548 /* 549 * Now check to see if there were any lingering 550 * extents and dump their data. 551 */ 552 if (LF_ISSET(DB_SALVAGE) && nextents != 0) { 553 nextents--; 554 i = 1 + 555 vdp->extents[nextents] * vdp->page_ext; 556 stop = i + vdp->page_ext; 557 goto begin; 558 } 559 560 if (0) { 561err: if (h != NULL && (t_ret = 562 __qam_fput(dbc, i, h, dbp->priority)) != 0 && ret == 0) 563 ret = t_ret; 564 if (pip != NULL && (t_ret = 565 __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0) 566 ret = t_ret; 567 } 568err1: if (dbc != NULL && (t_ret = __dbc_close(dbc)) != 0 && ret == 0) 569 ret = t_ret; 570 return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD : ret); 571} 572 573/* 574 * __qam_salvage -- 575 * Safely dump out all recnos and data on a queue page. 576 * 577 * PUBLIC: int __qam_salvage __P((DB *, VRFY_DBINFO *, db_pgno_t, PAGE *, 578 * PUBLIC: void *, int (*)(void *, const void *), u_int32_t)); 579 */ 580int 581__qam_salvage(dbp, vdp, pgno, h, handle, callback, flags) 582 DB *dbp; 583 VRFY_DBINFO *vdp; 584 db_pgno_t pgno; 585 PAGE *h; 586 void *handle; 587 int (*callback) __P((void *, const void *)); 588 u_int32_t flags; 589{ 590 DBT dbt, key; 591 QAMDATA *qp, *qep; 592 db_recno_t recno; 593 int ret, err_ret, t_ret; 594 u_int32_t pagesize, qlen; 595 u_int32_t i; 596 597 memset(&dbt, 0, sizeof(DBT)); 598 memset(&key, 0, sizeof(DBT)); 599 600 err_ret = ret = 0; 601 602 pagesize = (u_int32_t)dbp->mpf->mfp->stat.st_pagesize; 603 qlen = ((QUEUE *)dbp->q_internal)->re_len; 604 dbt.size = qlen; 605 key.data = &recno; 606 key.size = sizeof(recno); 607 recno = (pgno - 1) * QAM_RECNO_PER_PAGE(dbp) + 1; 608 i = 0; 609 qep = (QAMDATA *)((u_int8_t *)h + pagesize - qlen); 610 for (qp = QAM_GET_RECORD(dbp, h, i); qp < qep; 611 recno++, i++, qp = QAM_GET_RECORD(dbp, h, i)) { 612 if (F_ISSET(qp, ~(QAM_VALID|QAM_SET))) 613 continue; 614 if (!F_ISSET(qp, QAM_SET)) 615 continue; 616 617 if (!LF_ISSET(DB_AGGRESSIVE) && !F_ISSET(qp, QAM_VALID)) 618 continue; 619 620 dbt.data = qp->data; 621 if ((ret = __db_vrfy_prdbt(&key, 622 0, " ", handle, callback, 1, vdp)) != 0) 623 err_ret = ret; 624 625 if ((ret = __db_vrfy_prdbt(&dbt, 626 0, " ", handle, callback, 0, vdp)) != 0) 627 err_ret = ret; 628 } 629 630 if ((t_ret = __db_salvage_markdone(vdp, pgno)) != 0) 631 return (t_ret); 632 return ((ret == 0 && err_ret != 0) ? err_ret : ret); 633} 634