Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

qam_verify.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1999-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: qam_verify.c,v 12.3 2005/06/16 20:23:34 bostic Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #endif
00017 
00018 #include "db_int.h"
00019 #include "dbinc/db_page.h"
00020 #include "dbinc/db_verify.h"
00021 #include "dbinc/db_am.h"
00022 #include "dbinc/db_shash.h"
00023 #include "dbinc/mp.h"
00024 #include "dbinc/qam.h"
00025 /*
00026  * __qam_vrfy_meta --
00027  *      Verify the queue-specific part of a metadata page.
00028  *
00029  * PUBLIC: int __qam_vrfy_meta __P((DB *, VRFY_DBINFO *, QMETA *,
00030  * PUBLIC:     db_pgno_t, u_int32_t));
00031  */
00032 int
00033 __qam_vrfy_meta(dbp, vdp, meta, pgno, flags)
00034         DB *dbp;
00035         VRFY_DBINFO *vdp;
00036         QMETA *meta;
00037         db_pgno_t pgno;
00038         u_int32_t flags;
00039 {
00040         DB_ENV *dbenv;
00041         QUEUE *qp;
00042         VRFY_PAGEINFO *pip;
00043         db_pgno_t *extents, extid, first, last;
00044         size_t len;
00045         int count, i, isbad, nextents, ret, t_ret;
00046         char *buf, **names;
00047 
00048         COMPQUIET(count, 0);
00049 
00050         dbenv = dbp->dbenv;
00051         qp = (QUEUE *)dbp->q_internal;
00052         extents = NULL;
00053         first = last = 0;
00054         buf = NULL;
00055         names = NULL;
00056 
00057         if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
00058                 return (ret);
00059         isbad = 0;
00060 
00061         /*
00062          * Queue can't be used in subdatabases, so if this isn't set
00063          * something very odd is going on.
00064          */
00065         if (!F_ISSET(pip, VRFY_INCOMPLETE))
00066                 EPRINT((dbenv, "Page %lu: queue databases must be one-per-file",
00067                     (u_long)pgno));
00068 
00069         /*
00070          * Because the metapage pointers are rolled forward by
00071          * aborting transactions, the extent of the queue may
00072          * extend beyond the allocated pages, so we do
00073          * not check that meta_current is within the allocated
00074          * pages.
00075          */
00076 
00077         /*
00078          * re_len:  If this is bad, we can't safely verify queue data pages, so
00079          * return DB_VERIFY_FATAL
00080          */
00081         if (DB_ALIGN(meta->re_len + sizeof(QAMDATA) - 1, sizeof(u_int32_t)) *
00082             meta->rec_page + QPAGE_SZ(dbp) > dbp->pgsize) {
00083                 EPRINT((dbenv,
00084    "Page %lu: queue record length %lu too high for page size and recs/page",
00085                     (u_long)pgno, (u_long)meta->re_len));
00086                 ret = DB_VERIFY_FATAL;
00087                 goto err;
00088         } else {
00089                 /*
00090                  * We initialize the Queue internal pointer;  we may need
00091                  * it when handling extents.  It would get set up in open,
00092                  * if we called open normally, but we don't.
00093                  */
00094                 vdp->re_pad = meta->re_pad;
00095                 qp->re_pad = (int)meta->re_pad;
00096                 qp->re_len = vdp->re_len = meta->re_len;
00097                 qp->rec_page = vdp->rec_page = meta->rec_page;
00098                 qp->page_ext = vdp->page_ext = meta->page_ext;
00099         }
00100 
00101         /*
00102          * There's no formal maximum extentsize, and a 0 value represents
00103          * no extents, so there's nothing to verify.
00104          *
00105          * Note that since QUEUE databases can't have subdatabases, it's an
00106          * error to see more than one QUEUE metadata page in a single
00107          * verifier run.  Theoretically, this should really be a structure
00108          * rather than a per-page check, but since we're setting qp fields
00109          * here (and have only one qp to set) we raise the alarm now if
00110          * this assumption fails.  (We need the qp info to be reasonable
00111          * before we do per-page verification of queue extents.)
00112          */
00113         if (F_ISSET(vdp, VRFY_QMETA_SET)) {
00114                 isbad = 1;
00115                 EPRINT((dbenv,
00116                     "Page %lu: database contains multiple Queue metadata pages",
00117                     (u_long)pgno));
00118                 goto err;
00119         }
00120         F_SET(vdp, VRFY_QMETA_SET);
00121         qp->page_ext = meta->page_ext;
00122         dbp->pgsize = meta->dbmeta.pagesize;
00123         qp->q_meta = pgno;
00124         qp->q_root = pgno + 1;
00125         vdp->first_recno = meta->first_recno;
00126         vdp->last_recno = meta->cur_recno;
00127         if (qp->page_ext != 0) {
00128                 first = QAM_RECNO_EXTENT(dbp, vdp->first_recno);
00129                 last = QAM_RECNO_EXTENT(dbp, vdp->last_recno);
00130         }
00131 
00132         /*
00133          * Look in the data directory to see if there are any extents
00134          * around that are not in the range of the queue.  If so,
00135          * then report that and look there if we are salvaging.
00136          */
00137 
00138         if ((ret = __db_appname(dbenv,
00139             DB_APP_DATA, qp->dir, 0, NULL, &buf)) != 0)
00140                 goto err;
00141         if ((ret = __os_dirlist(dbenv, buf, &names, &count)) != 0)
00142                 goto err;
00143         __os_free(dbenv, buf);
00144         buf = NULL;
00145 
00146         len = strlen(QUEUE_EXTENT_HEAD) + strlen(qp->name) + 1;
00147         if ((ret = __os_malloc(dbenv, len, &buf)) != 0)
00148                 goto err;
00149         len = (size_t)snprintf(buf, len, QUEUE_EXTENT_HEAD, qp->name);
00150         for (i = nextents = 0; i < count; i++) {
00151                 if (strncmp(names[i], buf, len) == 0) {
00152                         /* Only save extents out of bounds. */
00153                         extid = (db_pgno_t)strtoul(&names[i][len], NULL, 10);
00154                         if (qp->page_ext != 0 &&
00155                             (last > first ?
00156                             (extid >= first && extid <= last) :
00157                             (extid >= first || extid <= last)))
00158                                 continue;
00159                         if (extents == NULL && (ret = __os_malloc(
00160                              dbenv, (size_t)(count - i) * sizeof(extid),
00161                              &extents)) != 0)
00162                                 goto err;
00163                         extents[nextents] = extid;
00164                         nextents++;
00165                 }
00166         }
00167         if (nextents > 0)
00168                 __db_err(dbenv,
00169                      "Warning: %d extra extent files found", nextents);
00170         vdp->nextents = nextents;
00171         vdp->extents = extents;
00172 
00173 err:    if ((t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0 && ret == 0)
00174                 ret = t_ret;
00175         if (names != NULL)
00176                 __os_dirfree(dbenv, names, count);
00177         if (buf != NULL)
00178                 __os_free(dbenv, buf);
00179         if (ret != 0 && extents != NULL)
00180                 __os_free(dbenv, extents);
00181         if (LF_ISSET(DB_SALVAGE) &&
00182            (t_ret = __db_salvage_markdone(vdp, pgno)) != 0 && ret == 0)
00183                 ret = t_ret;
00184         return (ret == 0 && isbad == 1 ? DB_VERIFY_BAD : ret);
00185 }
00186 
00187 /*
00188  * __qam_vrfy_data --
00189  *      Verify a queue data page.
00190  *
00191  * PUBLIC: int __qam_vrfy_data __P((DB *, VRFY_DBINFO *, QPAGE *,
00192  * PUBLIC:     db_pgno_t, u_int32_t));
00193  */
00194 int
00195 __qam_vrfy_data(dbp, vdp, h, pgno, flags)
00196         DB *dbp;
00197         VRFY_DBINFO *vdp;
00198         QPAGE *h;
00199         db_pgno_t pgno;
00200         u_int32_t flags;
00201 {
00202         DB fakedb;
00203         struct __queue fakeq;
00204         QAMDATA *qp;
00205         db_recno_t i;
00206 
00207         /*
00208          * Not much to do here, except make sure that flags are reasonable.
00209          *
00210          * QAM_GET_RECORD assumes a properly initialized q_internal
00211          * structure, however, and we don't have one, so we play
00212          * some gross games to fake it out.
00213          */
00214         fakedb.q_internal = &fakeq;
00215         fakedb.flags = dbp->flags;
00216         fakeq.re_len = vdp->re_len;
00217 
00218         for (i = 0; i < vdp->rec_page; i++) {
00219                 qp = QAM_GET_RECORD(&fakedb, h, i);
00220                 if ((u_int8_t *)qp >= (u_int8_t *)h + dbp->pgsize) {
00221                         EPRINT((dbp->dbenv,
00222                     "Page %lu: queue record %lu extends past end of page",
00223                             (u_long)pgno, (u_long)i));
00224                         return (DB_VERIFY_BAD);
00225                 }
00226 
00227                 if (qp->flags & ~(QAM_VALID | QAM_SET)) {
00228                         EPRINT((dbp->dbenv,
00229                             "Page %lu: queue record %lu has bad flags (%#lx)",
00230                             (u_long)pgno, (u_long)i, (u_long)qp->flags));
00231                         return (DB_VERIFY_BAD);
00232                 }
00233         }
00234 
00235         return (0);
00236 }
00237 
00238 /*
00239  * __qam_vrfy_structure --
00240  *      Verify a queue database structure, such as it is.
00241  *
00242  * PUBLIC: int __qam_vrfy_structure __P((DB *, VRFY_DBINFO *, u_int32_t));
00243  */
00244 int
00245 __qam_vrfy_structure(dbp, vdp, flags)
00246         DB *dbp;
00247         VRFY_DBINFO *vdp;
00248         u_int32_t flags;
00249 {
00250         VRFY_PAGEINFO *pip;
00251         db_pgno_t i;
00252         int ret, isbad;
00253 
00254         isbad = 0;
00255 
00256         if ((ret = __db_vrfy_getpageinfo(vdp, PGNO_BASE_MD, &pip)) != 0)
00257                 return (ret);
00258 
00259         if (pip->type != P_QAMMETA) {
00260                 EPRINT((dbp->dbenv,
00261                     "Page %lu: queue database has no meta page",
00262                     (u_long)PGNO_BASE_MD));
00263                 isbad = 1;
00264                 goto err;
00265         }
00266 
00267         if ((ret = __db_vrfy_pgset_inc(vdp->pgset, 0)) != 0)
00268                 goto err;
00269 
00270         for (i = 1; i <= vdp->last_pgno; i++) {
00271                 /* Send feedback to the application about our progress. */
00272                 if (!LF_ISSET(DB_SALVAGE))
00273                         __db_vrfy_struct_feedback(dbp, vdp);
00274 
00275                 if ((ret = __db_vrfy_putpageinfo(dbp->dbenv, vdp, pip)) != 0 ||
00276                     (ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0)
00277                         return (ret);
00278                 if (!F_ISSET(pip, VRFY_IS_ALLZEROES) &&
00279                     pip->type != P_QAMDATA) {
00280                         EPRINT((dbp->dbenv,
00281                     "Page %lu: queue database page of incorrect type %lu",
00282                             (u_long)i, (u_long)pip->type));
00283                         isbad = 1;
00284                         goto err;
00285                 } else if ((ret = __db_vrfy_pgset_inc(vdp->pgset, i)) != 0)
00286                         goto err;
00287         }
00288 
00289 err:    if ((ret = __db_vrfy_putpageinfo(dbp->dbenv, vdp, pip)) != 0)
00290                 return (ret);
00291         return (isbad == 1 ? DB_VERIFY_BAD : 0);
00292 }
00293 
00294 /*
00295  * __qam_vrfy_walkqueue --
00296  *    Do a "walkpages" per-page verification pass over the set of Queue
00297  * extent pages.
00298  *
00299  * PUBLIC: int __qam_vrfy_walkqueue __P((DB *, VRFY_DBINFO *, void *,
00300  * PUBLIC:    int (*)(void *, const void *), u_int32_t));
00301  */
00302 int
00303 __qam_vrfy_walkqueue(dbp, vdp, handle, callback, flags)
00304         DB *dbp;
00305         VRFY_DBINFO *vdp;
00306         void *handle;
00307         int (*callback) __P((void *, const void *));
00308         u_int32_t flags;
00309 {
00310         DB_ENV *dbenv;
00311         PAGE *h;
00312         QUEUE *qp;
00313         VRFY_PAGEINFO *pip;
00314         db_pgno_t first, i, last, pg_ext, stop;
00315         int isbad, nextents, ret, t_ret;
00316 
00317         COMPQUIET(h, NULL);
00318 
00319         dbenv = dbp->dbenv;
00320         qp = dbp->q_internal;
00321         pip = NULL;
00322         pg_ext = qp->page_ext;
00323         isbad = ret = t_ret = 0;
00324 
00325         /* If this database has no extents, we've seen all the pages already. */
00326         if (pg_ext == 0)
00327                 return (0);
00328 
00329         first = QAM_RECNO_PAGE(dbp, vdp->first_recno);
00330         last = QAM_RECNO_PAGE(dbp, vdp->last_recno);
00331 
00332         i = first;
00333         if (first > last)
00334                 stop = QAM_RECNO_PAGE(dbp, UINT32_MAX);
00335         else
00336                 stop = last;
00337         nextents = vdp->nextents;
00338 
00339         /* Verify/salvage each page. */
00340 begin:  for (; i <= stop; i++) {
00341                 /*
00342                  * If DB_SALVAGE is set, we inspect our database of completed
00343                  * pages, and skip any we've already printed in the subdb pass.
00344                  */
00345                 if (LF_ISSET(DB_SALVAGE) && (__db_salvage_isdone(vdp, i) != 0))
00346                         continue;
00347                 if ((t_ret = __qam_fget(dbp, &i, 0, &h)) != 0) {
00348                         if (t_ret == ENOENT || t_ret == DB_PAGE_NOTFOUND) {
00349                                 i += (pg_ext - ((i - 1) % pg_ext)) - 1;
00350                                 continue;
00351                         }
00352 
00353                         /*
00354                          * If an individual page get fails, keep going iff
00355                          * we're salvaging.
00356                          */
00357                         if (LF_ISSET(DB_SALVAGE)) {
00358                                 if (ret == 0)
00359                                         ret = t_ret;
00360                                 continue;
00361                         } else
00362                                 return (t_ret);
00363                 }
00364 
00365                 if (LF_ISSET(DB_SALVAGE)) {
00366                         /*
00367                          * We pretty much don't want to quit unless a
00368                          * bomb hits.  May as well return that something
00369                          * was screwy, however.
00370                          */
00371                         if ((t_ret = __db_salvage(dbp,
00372                             vdp, i, h, handle, callback, flags)) != 0) {
00373                                 if (ret == 0)
00374                                         ret = t_ret;
00375                                 isbad = 1;
00376                         }
00377                 } else {
00378                         /*
00379                          * If we are not salvaging, and we get any error
00380                          * other than DB_VERIFY_BAD, return immediately;
00381                          * it may not be safe to proceed.  If we get
00382                          * DB_VERIFY_BAD, keep going;  listing more errors
00383                          * may make it easier to diagnose problems and
00384                          * determine the magnitude of the corruption.
00385                          */
00386                         if ((ret = __db_vrfy_common(dbp,
00387                             vdp, h, i, flags)) == DB_VERIFY_BAD)
00388                                 isbad = 1;
00389                         else if (ret != 0)
00390                                 goto err;
00391 
00392                         __db_vrfy_struct_feedback(dbp, vdp);
00393 
00394                         if ((ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0)
00395                                 return (ret);
00396                         if (F_ISSET(pip, VRFY_IS_ALLZEROES))
00397                                 goto put;
00398                         if (pip->type != P_QAMDATA) {
00399                                 EPRINT((dbenv,
00400                     "Page %lu: queue database page of incorrect type %lu",
00401                                     (u_long)i, (u_long)pip->type));
00402                                 isbad = 1;
00403                                 goto err;
00404                         }
00405                         if ((ret = __db_vrfy_pgset_inc(vdp->pgset, i)) != 0)
00406                                 goto err;
00407                         if ((ret = __qam_vrfy_data(dbp, vdp,
00408                             (QPAGE *)h, i, flags)) == DB_VERIFY_BAD)
00409                                 isbad = 1;
00410                         else if (ret != 0)
00411                                 goto err;
00412 
00413 put:                    if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
00414                                 goto err;
00415                         pip = NULL;
00416                 }
00417 
00418                 /* Again, keep going iff we're salvaging. */
00419                 if ((t_ret = __qam_fput(dbp, i, h, 0)) != 0) {
00420                         if (LF_ISSET(DB_SALVAGE)) {
00421                                 if (ret == 0)
00422                                         ret = t_ret;
00423                                 continue;
00424                         } else
00425                                 return (t_ret);
00426                 }
00427         }
00428 
00429         if (first > last) {
00430                 i = 1;
00431                 stop = last;
00432                 first = last;
00433                 goto begin;
00434         }
00435 
00436         /*
00437          * Now check to see if there were any lingering
00438          * extents and dump their data.
00439          */
00440         if (LF_ISSET(DB_SALVAGE) && nextents != 0) {
00441                 nextents--;
00442                 i = 1 +
00443                     vdp->extents[nextents] * vdp->page_ext;
00444                 stop = i + vdp->page_ext;
00445                 goto begin;
00446         }
00447 
00448         if (0) {
00449 err:            if ((t_ret = __qam_fput(dbp, i, h, 0)) != 0)
00450                         return (ret == 0 ? t_ret : ret);
00451                 if (pip != NULL &&
00452                      (t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
00453                         return (ret == 0 ? t_ret : ret);
00454         }
00455         return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD : ret);
00456 }
00457 
00458 /*
00459  * __qam_salvage --
00460  *      Safely dump out all recnos and data on a queue page.
00461  *
00462  * PUBLIC: int __qam_salvage __P((DB *, VRFY_DBINFO *, db_pgno_t, PAGE *,
00463  * PUBLIC:     void *, int (*)(void *, const void *), u_int32_t));
00464  */
00465 int
00466 __qam_salvage(dbp, vdp, pgno, h, handle, callback, flags)
00467         DB *dbp;
00468         VRFY_DBINFO *vdp;
00469         db_pgno_t pgno;
00470         PAGE *h;
00471         void *handle;
00472         int (*callback) __P((void *, const void *));
00473         u_int32_t flags;
00474 {
00475         DBT dbt, key;
00476         QAMDATA *qp, *qep;
00477         db_recno_t recno;
00478         int ret, err_ret, t_ret;
00479         u_int32_t pagesize, qlen;
00480         u_int32_t i;
00481 
00482         memset(&dbt, 0, sizeof(DBT));
00483         memset(&key, 0, sizeof(DBT));
00484 
00485         err_ret = ret = 0;
00486 
00487         pagesize = (u_int32_t)dbp->mpf->mfp->stat.st_pagesize;
00488         qlen = ((QUEUE *)dbp->q_internal)->re_len;
00489         dbt.size = qlen;
00490         key.data = &recno;
00491         key.size = sizeof(recno);
00492         recno = (pgno - 1) * QAM_RECNO_PER_PAGE(dbp) + 1;
00493         i = 0;
00494         qep = (QAMDATA *)((u_int8_t *)h + pagesize - qlen);
00495         for (qp = QAM_GET_RECORD(dbp, h, i); qp < qep;
00496             recno++, i++, qp = QAM_GET_RECORD(dbp, h, i)) {
00497                 if (F_ISSET(qp, ~(QAM_VALID|QAM_SET)))
00498                         continue;
00499                 if (!F_ISSET(qp, QAM_SET))
00500                         continue;
00501 
00502                 if (!LF_ISSET(DB_AGGRESSIVE) && !F_ISSET(qp, QAM_VALID))
00503                         continue;
00504 
00505                 dbt.data = qp->data;
00506                 if ((ret = __db_vrfy_prdbt(&key,
00507                     0, " ", handle, callback, 1, vdp)) != 0)
00508                         err_ret = ret;
00509 
00510                 if ((ret = __db_vrfy_prdbt(&dbt,
00511                     0, " ", handle, callback, 0, vdp)) != 0)
00512                         err_ret = ret;
00513         }
00514 
00515         if ((t_ret = __db_salvage_markdone(vdp, pgno)) != 0)
00516                 return (t_ret);
00517         return ((ret == 0 && err_ret != 0) ? err_ret : ret);
00518 }

Generated on Sun Dec 25 12:14:44 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2