]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Back-patch changes to validate page header fields immediately after
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Apr 2003 00:32:57 +0000 (00:32 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Apr 2003 00:32:57 +0000 (00:32 +0000)
reading in any page.  Also back-port the zero_damaged_pages boolean
that determines what to do about it.

doc/src/sgml/runtime.sgml
src/backend/storage/buffer/bufmgr.c
src/backend/storage/page/bufpage.c
src/backend/utils/misc/guc.c
src/include/storage/bufmgr.h
src/include/storage/bufpage.h

index 10507b81ff0f2ca97e892e3b0c1a584913b34475..f77ee8e035a7d539e94984dc727cb5b020917cfb 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.147.2.6 2003/01/11 05:04:26 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.147.2.7 2003/04/04 00:32:57 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -1525,10 +1525,10 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
        </para>
 
        <para>
-        It should be noted that the performance penalty of doing
-        <function>fsync</>s is considerably less in
+        It should be noted that the performance penalty of having
+        <function>fsync</> on is considerably less in
         <productname>PostgreSQL</> version 7.1 and later. If you
-        previously suppressed <function>fsync</>s for performance
+        previously suppressed <function>fsync</> for performance
         reasons, you may wish to reconsider your choice.
        </para>
 
@@ -2057,6 +2057,26 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><varname>ZERO_DAMAGED_PAGES</varname> (<type>boolean</type>)</term>
+      <listitem>
+       <para>
+        Detection of a damaged page header normally causes
+        <productname>PostgreSQL</> to report an error, aborting the current
+       transaction.  Setting <varname>zero_damaged_pages</> to true causes
+       the system to instead report a warning, zero out the damaged page,
+       and continue processing.  This behavior <emphasis>will destroy data</>,
+       namely all the rows on the damaged page.  But it allows you to get
+       past the error and retrieve rows from any undamaged pages that may
+       be present in the table.  So it is useful for recovering data if
+       corruption has occurred due to hardware or software error.  You should
+       generally not set this true until you have given up hope of recovering
+       data from the damaged page(s) of a table.  The
+       default setting is off, and it can only be changed by a superuser.
+       </para>
+      </listitem>
+     </varlistentry>
+
     </variablelist>
    </para>
    </sect2>
index 6d1e52d5220043d27376d17bb3a6d9174f1cbc84..35b1db79e8404369abc9169db00220f24519d1a5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.133 2002/09/14 19:59:20 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.133.2.1 2003/04/04 00:32:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,6 +49,7 @@
 #include "miscadmin.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
+#include "storage/bufpage.h"
 #include "storage/proc.h"
 #include "storage/smgr.h"
 #include "utils/relcache.h"
        (*((XLogRecPtr*) MAKE_PTR((bufHdr)->data)))
 
 
+/* GUC variable */
+bool   zero_damaged_pages = false;
+
+
 static void WaitIO(BufferDesc *buf);
 static void StartBufferIO(BufferDesc *buf, bool forInput);
 static void TerminateBufferIO(BufferDesc *buf);
@@ -217,6 +222,20 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
        {
                status = smgrread(DEFAULT_SMGR, reln, blockNum,
                                                  (char *) MAKE_PTR(bufHdr->data));
+               /* check for garbage data */
+               if (status == SM_SUCCESS &&
+                       !PageHeaderIsValid((PageHeader) MAKE_PTR(bufHdr->data)))
+               {
+                       if (zero_damaged_pages)
+                       {
+                               elog(WARNING, "Invalid page header in block %u of %s; zeroing out page",
+                                        blockNum, RelationGetRelationName(reln));
+                               MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
+                       }
+                       else
+                               elog(ERROR, "Invalid page header in block %u of %s",
+                                        blockNum, RelationGetRelationName(reln));
+               }
        }
 
        if (isLocalBuf)
index 83e0c3372289fb11ab9d78450dc01f20663e6413..c57ebf2fbdc73bdca033a044dde5c60b3eea2563 100644 (file)
@@ -8,14 +8,12 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.50 2002/09/04 20:31:26 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.50.2.1 2003/04/04 00:32:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include <sys/file.h>
-
 #include "storage/bufpage.h"
 
 
@@ -48,6 +46,51 @@ PageInit(Page page, Size pageSize, Size specialSize)
 }
 
 
+/*
+ * PageHeaderIsValid
+ *             Check that the header fields of a page appear valid.
+ *
+ * This is called when a page has just been read in from disk.  The idea is
+ * to cheaply detect trashed pages before we go nuts following bogus item
+ * pointers, testing invalid transaction identifiers, etc.
+ *
+ * It turns out to be necessary to allow zeroed pages here too.  Even though
+ * this routine is *not* called when deliberately adding a page to a relation,
+ * there are scenarios in which a zeroed page might be found in a table.
+ * (Example: a backend extends a relation, then crashes before it can write
+ * any WAL entry about the new page.  The kernel will already have the
+ * zeroed page in the file, and it will stay that way after restart.)  So we
+ * allow zeroed pages here, and are careful that the page access macros
+ * treat such a page as empty and without free space.  Eventually, VACUUM
+ * will clean up such a page and make it usable.
+ */
+bool
+PageHeaderIsValid(PageHeader page)
+{
+       char       *pagebytes;
+       int                     i;
+
+       /* Check normal case */
+       if (PageGetPageSize(page) == BLCKSZ &&
+               PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
+               page->pd_lower >= SizeOfPageHeaderData &&
+               page->pd_lower <= page->pd_upper &&
+               page->pd_upper <= page->pd_special &&
+               page->pd_special <= BLCKSZ &&
+               page->pd_special == MAXALIGN(page->pd_special))
+               return true;
+
+       /* Check all-zeroes case */
+       pagebytes = (char *) page;
+       for (i = 0; i < BLCKSZ; i++)
+       {
+               if (pagebytes[i] != 0)
+                       return false;
+       }
+       return true;
+}
+
+
 /* ----------------
  *             PageAddItem
  *
index edb5e353644f894ba9431ffe93d0ab7a379e23d6..b49b36565e604f06714e8977f2e431e5843b61cc 100644 (file)
@@ -5,7 +5,7 @@
  * command, configuration file, and command line options.
  * See src/backend/utils/misc/README for more information.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.99.2.4 2003/01/28 18:04:13 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.99.2.5 2003/04/04 00:32:57 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -352,6 +352,10 @@ static struct config_bool
                {"fsync", PGC_SIGHUP}, &enableFsync,
                true, NULL, NULL
        },
+       {
+               {"zero_damaged_pages", PGC_SUSET}, &zero_damaged_pages,
+               false, NULL, NULL
+       },
        {
                {"silent_mode", PGC_POSTMASTER}, &SilentMode,
                false, NULL, NULL
index 5fcc354849df33ba6023e663f027b3c950648ff7..c2f970c0b6c39336645105f30c885cea2ad45057 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: bufmgr.h,v 1.66 2002/10/22 20:00:48 petere Exp $
+ * $Id: bufmgr.h,v 1.66.2.1 2003/04/04 00:32:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,9 @@ typedef void *Block;
 /* in globals.c ... this duplicates miscadmin.h */
 extern DLLIMPORT int   NBuffers;
 
+/* in bufmgr.c */
+extern bool zero_damaged_pages;
+
 /* in buf_init.c */
 extern DLLIMPORT Block *BufferBlockPointers;
 extern long *PrivateRefCount;
index 159a82a0228a61e0998434d5bd84a8bcd4b782e3..fc589c18f9d846b97f237170412eb9119be39f3a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: bufpage.h,v 1.53 2002/09/04 20:31:45 momjian Exp $
+ * $Id: bufpage.h,v 1.53.2.1 2003/04/04 00:32:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -339,6 +339,7 @@ typedef PageHeaderData *PageHeader;
  */
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern bool PageHeaderIsValid(PageHeader page);
 extern OffsetNumber PageAddItem(Page page, Item item, Size size,
                        OffsetNumber offsetNumber, ItemIdFlags flags);
 extern Page PageGetTempPage(Page page, Size specialSize);