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:
parent
453f3503f3
commit
087771ae40
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user