Add error checking to PageRepairFragmentation to ensure that it can

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.
This commit is contained in:
Tom Lane 2001-10-23 02:20:15 +00:00
parent 453f3503f3
commit 087771ae40

View File

@ -8,15 +8,15 @@
* *
* *
* IDENTIFICATION * 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/types.h>
#include <sys/file.h> #include <sys/file.h>
#include "postgres.h"
#include "storage/bufpage.h" #include "storage/bufpage.h"
@ -38,12 +38,12 @@ PageInit(Page page, Size pageSize, Size specialSize)
{ {
PageHeader p = (PageHeader) page; PageHeader p = (PageHeader) page;
specialSize = MAXALIGN(specialSize);
Assert(pageSize == BLCKSZ); Assert(pageSize == BLCKSZ);
Assert(pageSize > Assert(pageSize >
specialSize + sizeof(PageHeaderData) - sizeof(ItemIdData)); specialSize + sizeof(PageHeaderData) - sizeof(ItemIdData));
specialSize = MAXALIGN(specialSize);
p->pd_lower = sizeof(PageHeaderData) - sizeof(ItemIdData); p->pd_lower = sizeof(PageHeaderData) - sizeof(ItemIdData);
p->pd_upper = pageSize - specialSize; p->pd_upper = pageSize - specialSize;
p->pd_special = pageSize - specialSize; p->pd_special = pageSize - specialSize;
@ -93,7 +93,7 @@ PageAddItem(Page page,
ItemId itemId; ItemId itemId;
OffsetNumber limit; OffsetNumber limit;
bool needshuffle = false; bool needshuffle = false;
bool overwritemode = flags & OverwritePageMode; bool overwritemode = (flags & OverwritePageMode) != 0;
flags &= ~OverwritePageMode; flags &= ~OverwritePageMode;
@ -209,7 +209,7 @@ PageGetTempPage(Page page, Size specialSize)
thdr = (PageHeader) temp; thdr = (PageHeader) temp;
/* copy old page in */ /* copy old page in */
memmove(temp, page, pageSize); memcpy(temp, page, pageSize);
/* clear out the middle */ /* clear out the middle */
size = (pageSize - sizeof(PageHeaderData)) + sizeof(ItemIdData); size = (pageSize - sizeof(PageHeaderData)) + sizeof(ItemIdData);
@ -239,27 +239,22 @@ PageRestoreTempPage(Page tempPage, Page oldPage)
pfree(tempPage); pfree(tempPage);
} }
/* ---------------- /*
* itemid stuff for PageRepairFragmentation * sorting support for PageRepairFragmentation
* ----------------
*/ */
struct itemIdSortData struct itemIdSortData
{ {
int offsetindex; /* linp array index */ int offsetindex; /* linp array index */
ItemIdData itemiddata; int itemoff; /* page offset of item data */
Size alignedlen; /* MAXALIGN(item data len) */
}; };
static int static int
itemidcompare(const void *itemidp1, const void *itemidp2) itemoffcompare(const void *itemidp1, const void *itemidp2)
{ {
if (((struct itemIdSortData *) itemidp1)->itemiddata.lp_off == /* Sort in decreasing itemoff order */
((struct itemIdSortData *) itemidp2)->itemiddata.lp_off) return ((struct itemIdSortData *) itemidp2)->itemoff -
return 0; ((struct itemIdSortData *) itemidp1)->itemoff;
else if (((struct itemIdSortData *) itemidp1)->itemiddata.lp_off <
((struct itemIdSortData *) itemidp2)->itemiddata.lp_off)
return 1;
else
return -1;
} }
/* /*
@ -269,20 +264,40 @@ itemidcompare(const void *itemidp1, const void *itemidp2)
* It doesn't remove unused line pointers! Please don't change this. * It doesn't remove unused line pointers! Please don't change this.
* This routine is usable for heap pages only. * 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 int
PageRepairFragmentation(Page page, OffsetNumber *unused) 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, struct itemIdSortData *itemidbase,
*itemidptr; *itemidptr;
ItemId lp; ItemId lp;
int nline, int nline,
nused; nused;
int i;
Size totallen;
Offset upper; 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; nused = 0;
for (i = 0; i < nline; i++) for (i = 0; i < nline; i++)
{ {
@ -297,56 +312,65 @@ PageRepairFragmentation(Page page, OffsetNumber *unused)
if (nused == 0) if (nused == 0)
{ {
/* Page is completely empty, so just reset it quickly */
for (i = 0; i < nline; i++) for (i = 0; i < nline; i++)
{ {
lp = ((PageHeader) page)->pd_linp + 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 = pd_special;
((PageHeader) page)->pd_upper = ((PageHeader) page)->pd_special;
} }
else else
{ /* nused != 0 */ { /* nused != 0 */
/* Need to compact the page the hard way */
itemidbase = (struct itemIdSortData *) itemidbase = (struct itemIdSortData *)
palloc(sizeof(struct itemIdSortData) * nused); palloc(sizeof(struct itemIdSortData) * nused);
MemSet((char *) itemidbase, 0, sizeof(struct itemIdSortData) * nused);
itemidptr = itemidbase; itemidptr = itemidbase;
totallen = 0;
for (i = 0; i < nline; i++) for (i = 0; i < nline; i++)
{ {
lp = ((PageHeader) page)->pd_linp + i; lp = ((PageHeader) page)->pd_linp + i;
if ((*lp).lp_flags & LP_USED) if ((*lp).lp_flags & LP_USED)
{ {
itemidptr->offsetindex = i; 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++; itemidptr++;
} }
else 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), qsort((char *) itemidbase, nused, sizeof(struct itemIdSortData),
itemidcompare); itemoffcompare);
/* compactify page */ /* compactify page */
((PageHeader) page)->pd_upper = ((PageHeader) page)->pd_special; upper = pd_special;
for (i = 0, itemidptr = itemidbase; i < nused; i++, itemidptr++) for (i = 0, itemidptr = itemidbase; i < nused; i++, itemidptr++)
{ {
lp = ((PageHeader) page)->pd_linp + itemidptr->offsetindex; lp = ((PageHeader) page)->pd_linp + itemidptr->offsetindex;
alignedSize = MAXALIGN((*lp).lp_len); upper -= itemidptr->alignedlen;
upper = ((PageHeader) page)->pd_upper - alignedSize;
memmove((char *) page + upper, memmove((char *) page + upper,
(char *) page + (*lp).lp_off, (char *) page + itemidptr->itemoff,
(*lp).lp_len); itemidptr->alignedlen);
(*lp).lp_off = upper; (*lp).lp_off = upper;
((PageHeader) page)->pd_upper = upper;
} }
((PageHeader) page)->pd_upper = upper;
pfree(itemidbase); pfree(itemidbase);
} }
@ -362,12 +386,11 @@ PageGetFreeSpace(Page page)
{ {
Size space; Size space;
space = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower; space = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower;
if (space < sizeof(ItemIdData)) if (space < sizeof(ItemIdData))
return 0; return 0;
space -= sizeof(ItemIdData);/* XXX not always true */ space -= sizeof(ItemIdData); /* XXX not always appropriate */
return space; return space;
} }