1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1999,2008 Oracle. All rights reserved. 5 * 6 * $Id: qam_verify.c,v 12.18 2008/03/13 15:44:50 mbrey Exp $ 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, 0, 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_vrfy_data -- 181 * Verify a queue data page. 182 * 183 * PUBLIC: int __qam_vrfy_data __P((DB *, VRFY_DBINFO *, QPAGE *, 184 * PUBLIC: db_pgno_t, u_int32_t)); 185 */ 186int 187__qam_vrfy_data(dbp, vdp, h, pgno, flags) 188 DB *dbp; 189 VRFY_DBINFO *vdp; 190 QPAGE *h; 191 db_pgno_t pgno; 192 u_int32_t flags; 193{ 194 DB fakedb; 195 struct __queue fakeq; 196 QAMDATA *qp; 197 db_recno_t i; 198 199 /* 200 * Not much to do here, except make sure that flags are reasonable. 201 * 202 * QAM_GET_RECORD assumes a properly initialized q_internal 203 * structure, however, and we don't have one, so we play 204 * some gross games to fake it out. 205 */ 206 fakedb.q_internal = &fakeq; 207 fakedb.flags = dbp->flags; 208 fakeq.re_len = vdp->re_len; 209 210 for (i = 0; i < vdp->rec_page; i++) { 211 qp = QAM_GET_RECORD(&fakedb, h, i); 212 if ((u_int8_t *)qp >= (u_int8_t *)h + dbp->pgsize) { 213 EPRINT((dbp->env, 214 "Page %lu: queue record %lu extends past end of page", 215 (u_long)pgno, (u_long)i)); 216 return (DB_VERIFY_BAD); 217 } 218 219 if (qp->flags & ~(QAM_VALID | QAM_SET)) { 220 EPRINT((dbp->env, 221 "Page %lu: queue record %lu has bad flags (%#lx)", 222 (u_long)pgno, (u_long)i, (u_long)qp->flags)); 223 return (DB_VERIFY_BAD); 224 } 225 } 226 227 return (0); 228} 229 230/* 231 * __qam_vrfy_structure -- 232 * Verify a queue database structure, such as it is. 233 * 234 * PUBLIC: int __qam_vrfy_structure __P((DB *, VRFY_DBINFO *, u_int32_t)); 235 */ 236int 237__qam_vrfy_structure(dbp, vdp, flags) 238 DB *dbp; 239 VRFY_DBINFO *vdp; 240 u_int32_t flags; 241{ 242 VRFY_PAGEINFO *pip; 243 db_pgno_t i; 244 int ret, isbad; 245 246 isbad = 0; 247 248 if ((ret = __db_vrfy_getpageinfo(vdp, PGNO_BASE_MD, &pip)) != 0) 249 return (ret); 250 251 if (pip->type != P_QAMMETA) { 252 EPRINT((dbp->env, 253 "Page %lu: queue database has no meta page", 254 (u_long)PGNO_BASE_MD)); 255 isbad = 1; 256 goto err; 257 } 258 259 if ((ret = __db_vrfy_pgset_inc(vdp->pgset, vdp->thread_info, 0)) != 0) 260 goto err; 261 262 for (i = 1; i <= vdp->last_pgno; i++) { 263 /* Send feedback to the application about our progress. */ 264 if (!LF_ISSET(DB_SALVAGE)) 265 __db_vrfy_struct_feedback(dbp, vdp); 266 267 if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0 || 268 (ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0) 269 return (ret); 270 if (!F_ISSET(pip, VRFY_IS_ALLZEROES) && 271 pip->type != P_QAMDATA) { 272 EPRINT((dbp->env, 273 "Page %lu: queue database page of incorrect type %lu", 274 (u_long)i, (u_long)pip->type)); 275 isbad = 1; 276 goto err; 277 } else if ((ret = __db_vrfy_pgset_inc(vdp->pgset, 278 vdp->thread_info, i)) != 0) 279 goto err; 280 } 281 282err: if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0) 283 return (ret); 284 return (isbad == 1 ? DB_VERIFY_BAD : 0); 285} 286 287/* 288 * __qam_vrfy_walkqueue -- 289 * Do a "walkpages" per-page verification pass over the set of Queue 290 * extent pages. 291 * 292 * PUBLIC: int __qam_vrfy_walkqueue __P((DB *, VRFY_DBINFO *, void *, 293 * PUBLIC: int (*)(void *, const void *), u_int32_t)); 294 */ 295int 296__qam_vrfy_walkqueue(dbp, vdp, handle, callback, flags) 297 DB *dbp; 298 VRFY_DBINFO *vdp; 299 void *handle; 300 int (*callback) __P((void *, const void *)); 301 u_int32_t flags; 302{ 303 DBC *dbc; 304 ENV *env; 305 PAGE *h; 306 QUEUE *qp; 307 VRFY_PAGEINFO *pip; 308 db_pgno_t first, i, last, pg_ext, stop; 309 int isbad, nextents, ret, t_ret; 310 311 COMPQUIET(h, NULL); 312 313 env = dbp->env; 314 qp = dbp->q_internal; 315 pip = NULL; 316 pg_ext = qp->page_ext; 317 isbad = ret = t_ret = 0; 318 h = NULL; 319 320 /* If this database has no extents, we've seen all the pages already. */ 321 if (pg_ext == 0) 322 return (0); 323 324 first = QAM_RECNO_PAGE(dbp, vdp->first_recno); 325 last = QAM_RECNO_PAGE(dbp, vdp->last_recno); 326 327 i = first; 328 if (first > last) 329 stop = QAM_RECNO_PAGE(dbp, UINT32_MAX); 330 else 331 stop = last; 332 nextents = vdp->nextents; 333 334 /* Verify/salvage each page. */ 335 if ((ret = __db_cursor(dbp, vdp->thread_info, NULL, &dbc, 0)) != 0) 336 return (ret); 337begin: for (; i <= stop; i++) { 338 /* 339 * If DB_SALVAGE is set, we inspect our database of completed 340 * pages, and skip any we've already printed in the subdb pass. 341 */ 342 if (LF_ISSET(DB_SALVAGE) && (__db_salvage_isdone(vdp, i) != 0)) 343 continue; 344 if ((t_ret = __qam_fget(dbc, &i, 0, &h)) != 0) { 345 if (t_ret == ENOENT || t_ret == DB_PAGE_NOTFOUND) { 346 i += (pg_ext - ((i - 1) % pg_ext)) - 1; 347 continue; 348 } 349 350 /* 351 * If an individual page get fails, keep going iff 352 * we're salvaging. 353 */ 354 if (LF_ISSET(DB_SALVAGE)) { 355 if (ret == 0) 356 ret = t_ret; 357 continue; 358 } 359 h = NULL; 360 ret = t_ret; 361 goto err; 362 } 363 364 if (LF_ISSET(DB_SALVAGE)) { 365 /* 366 * We pretty much don't want to quit unless a 367 * bomb hits. May as well return that something 368 * was screwy, however. 369 */ 370 if ((t_ret = __db_salvage(dbp, 371 vdp, i, h, handle, callback, flags)) != 0) { 372 if (ret == 0) 373 ret = t_ret; 374 isbad = 1; 375 } 376 } else { 377 /* 378 * If we are not salvaging, and we get any error 379 * other than DB_VERIFY_BAD, return immediately; 380 * it may not be safe to proceed. If we get 381 * DB_VERIFY_BAD, keep going; listing more errors 382 * may make it easier to diagnose problems and 383 * determine the magnitude of the corruption. 384 */ 385 if ((ret = __db_vrfy_common(dbp, 386 vdp, h, i, flags)) == DB_VERIFY_BAD) 387 isbad = 1; 388 else if (ret != 0) 389 goto err; 390 391 __db_vrfy_struct_feedback(dbp, vdp); 392 393 if ((ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0) 394 goto err; 395 if (F_ISSET(pip, VRFY_IS_ALLZEROES)) 396 goto put; 397 if (pip->type != P_QAMDATA) { 398 EPRINT((env, 399 "Page %lu: queue database page of incorrect type %lu", 400 (u_long)i, (u_long)pip->type)); 401 isbad = 1; 402 goto err; 403 } 404 if ((ret = __db_vrfy_pgset_inc(vdp->pgset, 405 vdp->thread_info, i)) != 0) 406 goto err; 407 if ((ret = __qam_vrfy_data(dbp, vdp, 408 (QPAGE *)h, i, flags)) == DB_VERIFY_BAD) 409 isbad = 1; 410 else if (ret != 0) 411 goto err; 412 413put: if ((ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0) 414 goto err1; 415 pip = NULL; 416 } 417 418 /* Again, keep going iff we're salvaging. */ 419 if ((t_ret = __qam_fput(dbc, i, h, dbp->priority)) != 0) { 420 if (LF_ISSET(DB_SALVAGE)) { 421 if (ret == 0) 422 ret = t_ret; 423 continue; 424 } 425 ret = t_ret; 426 goto err1; 427 } 428 } 429 430 if (first > last) { 431 i = 1; 432 stop = last; 433 first = last; 434 goto begin; 435 } 436 437 /* 438 * Now check to see if there were any lingering 439 * extents and dump their data. 440 */ 441 if (LF_ISSET(DB_SALVAGE) && nextents != 0) { 442 nextents--; 443 i = 1 + 444 vdp->extents[nextents] * vdp->page_ext; 445 stop = i + vdp->page_ext; 446 goto begin; 447 } 448 449 if (0) { 450err: if (h != NULL && 451 (t_ret = __qam_fput(dbc, i, h, dbp->priority)) != 0 452 && ret == 0) 453 ret = t_ret; 454 if (pip != NULL && 455 (t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 456 && ret == 0) 457 ret = t_ret; 458 } 459err1: if (dbc != NULL && (t_ret = __dbc_close(dbc)) != 0 && ret == 0) 460 ret = t_ret; 461 return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD : ret); 462} 463 464/* 465 * __qam_salvage -- 466 * Safely dump out all recnos and data on a queue page. 467 * 468 * PUBLIC: int __qam_salvage __P((DB *, VRFY_DBINFO *, db_pgno_t, PAGE *, 469 * PUBLIC: void *, int (*)(void *, const void *), u_int32_t)); 470 */ 471int 472__qam_salvage(dbp, vdp, pgno, h, handle, callback, flags) 473 DB *dbp; 474 VRFY_DBINFO *vdp; 475 db_pgno_t pgno; 476 PAGE *h; 477 void *handle; 478 int (*callback) __P((void *, const void *)); 479 u_int32_t flags; 480{ 481 DBT dbt, key; 482 QAMDATA *qp, *qep; 483 db_recno_t recno; 484 int ret, err_ret, t_ret; 485 u_int32_t pagesize, qlen; 486 u_int32_t i; 487 488 memset(&dbt, 0, sizeof(DBT)); 489 memset(&key, 0, sizeof(DBT)); 490 491 err_ret = ret = 0; 492 493 pagesize = (u_int32_t)dbp->mpf->mfp->stat.st_pagesize; 494 qlen = ((QUEUE *)dbp->q_internal)->re_len; 495 dbt.size = qlen; 496 key.data = &recno; 497 key.size = sizeof(recno); 498 recno = (pgno - 1) * QAM_RECNO_PER_PAGE(dbp) + 1; 499 i = 0; 500 qep = (QAMDATA *)((u_int8_t *)h + pagesize - qlen); 501 for (qp = QAM_GET_RECORD(dbp, h, i); qp < qep; 502 recno++, i++, qp = QAM_GET_RECORD(dbp, h, i)) { 503 if (F_ISSET(qp, ~(QAM_VALID|QAM_SET))) 504 continue; 505 if (!F_ISSET(qp, QAM_SET)) 506 continue; 507 508 if (!LF_ISSET(DB_AGGRESSIVE) && !F_ISSET(qp, QAM_VALID)) 509 continue; 510 511 dbt.data = qp->data; 512 if ((ret = __db_vrfy_prdbt(&key, 513 0, " ", handle, callback, 1, vdp)) != 0) 514 err_ret = ret; 515 516 if ((ret = __db_vrfy_prdbt(&dbt, 517 0, " ", handle, callback, 0, vdp)) != 0) 518 err_ret = ret; 519 } 520 521 if ((t_ret = __db_salvage_markdone(vdp, pgno)) != 0) 522 return (t_ret); 523 return ((ret == 0 && err_ret != 0) ? err_ret : ret); 524} 525