Add error checking to PageRepairFragmentation to ensure that it can
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 23 Oct 2001 02:20:15 +0000 (02:20 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 23 Oct 2001 02:20:15 +0000 (02:20 +0000)
never overwrite adjacent pages with copied data, even if page header
and/or item pointers are already corrupt.  Change inspired by trouble
report from Alvaro Herrera.

src/backend/storage/page/bufpage.c

index d208a80d5cc97a7e52a974205c283310280b2cbf..038ec65ac7e27b3dd22307e4638e94840b225de4 100644 (file)
@@ -8,15 +8,15 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.37 2001/03/22 03:59:47 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.38 2001/10/23 02:20:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
+#include "postgres.h"
+
 #include <sys/types.h>
 #include <sys/file.h>
 
-#include "postgres.h"
-
 #include "storage/bufpage.h"
 
 
@@ -38,12 +38,12 @@ PageInit(Page page, Size pageSize, Size specialSize)
 {
    PageHeader  p = (PageHeader) page;
 
+   specialSize = MAXALIGN(specialSize);
+
    Assert(pageSize == BLCKSZ);
    Assert(pageSize >
           specialSize + sizeof(PageHeaderData) - sizeof(ItemIdData));
 
-   specialSize = MAXALIGN(specialSize);
-
    p->pd_lower = sizeof(PageHeaderData) - sizeof(ItemIdData);
    p->pd_upper = pageSize - specialSize;
    p->pd_special = pageSize - specialSize;
@@ -93,7 +93,7 @@ PageAddItem(Page page,
    ItemId      itemId;
    OffsetNumber limit;
    bool        needshuffle = false;
-   bool        overwritemode = flags & OverwritePageMode;
+   bool        overwritemode = (flags & OverwritePageMode) != 0;
 
    flags &= ~OverwritePageMode;
 
@@ -209,7 +209,7 @@ PageGetTempPage(Page page, Size specialSize)
    thdr = (PageHeader) temp;
 
    /* copy old page in */
-   memmove(temp, page, pageSize);
+   memcpy(temp, page, pageSize);
 
    /* clear out the middle */
    size = (pageSize - sizeof(PageHeaderData)) + sizeof(ItemIdData);
@@ -239,27 +239,22 @@ PageRestoreTempPage(Page tempPage, Page oldPage)
    pfree(tempPage);
 }
 
-/* ----------------
- *     itemid stuff for PageRepairFragmentation
- * ----------------
+/*
+ * sorting support for PageRepairFragmentation
  */
 struct itemIdSortData
 {
    int         offsetindex;    /* linp array index */
-   ItemIdData  itemiddata;
+   int         itemoff;        /* page offset of item data */
+   Size        alignedlen;     /* MAXALIGN(item data len) */
 };
 
 static int
-itemidcompare(const void *itemidp1, const void *itemidp2)
+itemoffcompare(const void *itemidp1, const void *itemidp2)
 {
-   if (((struct itemIdSortData *) itemidp1)->itemiddata.lp_off ==
-       ((struct itemIdSortData *) itemidp2)->itemiddata.lp_off)
-       return 0;
-   else if (((struct itemIdSortData *) itemidp1)->itemiddata.lp_off <
-            ((struct itemIdSortData *) itemidp2)->itemiddata.lp_off)
-       return 1;
-   else
-       return -1;
+   /* Sort in decreasing itemoff order */
+   return ((struct itemIdSortData *) itemidp2)->itemoff -
+       ((struct itemIdSortData *) itemidp1)->itemoff;
 }
 
 /*
@@ -269,20 +264,40 @@ itemidcompare(const void *itemidp1, const void *itemidp2)
  * It doesn't remove unused line pointers! Please don't change this.
  * This routine is usable for heap pages only.
  *
+ * Returns number of unused line pointers on page.  If "unused" is not NULL
+ * then the unused[] array is filled with indexes of unused line pointers.
  */
 int
 PageRepairFragmentation(Page page, OffsetNumber *unused)
 {
-   int         i;
+   Offset      pd_lower = ((PageHeader) page)->pd_lower;
+   Offset      pd_upper = ((PageHeader) page)->pd_upper;
+   Offset      pd_special = ((PageHeader) page)->pd_special;
    struct itemIdSortData *itemidbase,
               *itemidptr;
    ItemId      lp;
    int         nline,
                nused;
+   int         i;
+   Size        totallen;
    Offset      upper;
-   Size        alignedSize;
 
-   nline = (int16) PageGetMaxOffsetNumber(page);
+   /*
+    * It's worth the trouble to be more paranoid here than in most places,
+    * because we are about to reshuffle data in (what is usually) a shared
+    * disk buffer.  If we aren't careful then corrupted pointers, lengths,
+    * etc could cause us to clobber adjacent disk buffers, spreading the
+    * data loss further.  So, check everything.
+    */
+   if (pd_lower < (sizeof(PageHeaderData) - sizeof(ItemIdData)) ||
+       pd_lower > pd_upper ||
+       pd_upper > pd_special ||
+       pd_special > BLCKSZ ||
+       pd_special != MAXALIGN(pd_special))
+       elog(ERROR, "PageRepairFragmentation: corrupted page pointers: lower = %u, upper = %u, special = %u",
+            pd_lower, pd_upper, pd_special);
+
+   nline = PageGetMaxOffsetNumber(page);
    nused = 0;
    for (i = 0; i < nline; i++)
    {
@@ -297,56 +312,65 @@ PageRepairFragmentation(Page page, OffsetNumber *unused)
 
    if (nused == 0)
    {
+       /* Page is completely empty, so just reset it quickly */
        for (i = 0; i < nline; i++)
        {
            lp = ((PageHeader) page)->pd_linp + i;
-           if ((*lp).lp_len > 0)       /* unused, but allocated */
-               (*lp).lp_len = 0;       /* indicate unused & deallocated */
+           (*lp).lp_len = 0;   /* indicate unused & deallocated */
        }
-
-       ((PageHeader) page)->pd_upper = ((PageHeader) page)->pd_special;
+       ((PageHeader) page)->pd_upper = pd_special;
    }
    else
    {                           /* nused != 0 */
+       /* Need to compact the page the hard way */
        itemidbase = (struct itemIdSortData *)
            palloc(sizeof(struct itemIdSortData) * nused);
-       MemSet((char *) itemidbase, 0, sizeof(struct itemIdSortData) * nused);
        itemidptr = itemidbase;
+       totallen = 0;
        for (i = 0; i < nline; i++)
        {
            lp = ((PageHeader) page)->pd_linp + i;
            if ((*lp).lp_flags & LP_USED)
            {
                itemidptr->offsetindex = i;
-               itemidptr->itemiddata = *lp;
+               itemidptr->itemoff = (*lp).lp_off;
+               if (itemidptr->itemoff < (int) pd_upper ||
+                   itemidptr->itemoff >= (int) pd_special)
+                   elog(ERROR, "PageRepairFragmentation: corrupted item pointer %u",
+                        itemidptr->itemoff);
+               itemidptr->alignedlen = MAXALIGN((*lp).lp_len);
+               totallen += itemidptr->alignedlen;
                itemidptr++;
            }
            else
            {
-               if ((*lp).lp_len > 0)   /* unused, but allocated */
-                   (*lp).lp_len = 0;   /* indicate unused & deallocated */
+               (*lp).lp_len = 0; /* indicate unused & deallocated */
            }
        }
 
-       /* sort itemIdSortData array... */
+       if (totallen > (Size) (pd_special - pd_lower))
+           elog(ERROR, "PageRepairFragmentation: corrupted item lengths, total %u, avail %u",
+                totallen, pd_special - pd_lower);
+
+       /* sort itemIdSortData array into decreasing itemoff order */
        qsort((char *) itemidbase, nused, sizeof(struct itemIdSortData),
-             itemidcompare);
+             itemoffcompare);
 
        /* compactify page */
-       ((PageHeader) page)->pd_upper = ((PageHeader) page)->pd_special;
+       upper = pd_special;
 
        for (i = 0, itemidptr = itemidbase; i < nused; i++, itemidptr++)
        {
            lp = ((PageHeader) page)->pd_linp + itemidptr->offsetindex;
-           alignedSize = MAXALIGN((*lp).lp_len);
-           upper = ((PageHeader) page)->pd_upper - alignedSize;
+           upper -= itemidptr->alignedlen;
            memmove((char *) page + upper,
-                   (char *) page + (*lp).lp_off,
-                   (*lp).lp_len);
+                   (char *) page + itemidptr->itemoff,
+                   itemidptr->alignedlen);
            (*lp).lp_off = upper;
-           ((PageHeader) page)->pd_upper = upper;
        }
 
+       ((PageHeader) page)->pd_upper = upper;
+
        pfree(itemidbase);
    }
 
@@ -362,12 +386,11 @@ PageGetFreeSpace(Page page)
 {
    Size        space;
 
-
    space = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower;
 
    if (space < sizeof(ItemIdData))
        return 0;
-   space -= sizeof(ItemIdData);/* XXX not always true */
+   space -= sizeof(ItemIdData); /* XXX not always appropriate */
 
    return space;
 }