diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index cc6efab0cfc44bb23199e37d10763569e30bd00d..18dd9fe4a439f8e6ab81ab211985b3dda97d4c93 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.174 2003/03/25 16:15:38 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.175 2003/03/28 20:17:13 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -1599,7 +1599,7 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
 
        <para>
         It should be noted that the performance penalty of having
-        <varname>fsync</> on considerably less in
+        <varname>fsync</> on is considerably less in
         <productname>PostgreSQL</> version 7.1 and later. If you
         previously suppressed <function>fsync</> for performance
         reasons, you may wish to reconsider your choice.
@@ -2174,6 +2174,24 @@ 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 lose 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.  The
+	default setting is off, and it can only be changed by a superuser.
+       </para>
+      </listitem>
+     </varlistentry>
+
     </variablelist>
    </sect2>
 
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index a1abaf22c138cf0ab8c6218d7479bd867e3e65b5..119cfe92b0dafa1de23109fbda6cf671a56eec8e 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.134 2003/02/13 05:35:11 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.135 2003/03/28 20:17:13 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"
@@ -59,6 +60,10 @@
 	(*((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)
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index fd10c2aa918d72c844e7eb2bf587a46991254d38..be024eb6a81011f980c2ca2433419a7a7fbc7c14 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -8,14 +8,12 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.51 2003/01/11 05:01:03 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.52 2003/03/28 20:17:13 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
  *
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 7c5d6d1884964f50bec140d773f58468b60937ea..89b625277971b3c660d327864d20ccb92f807aa1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -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.117 2003/03/20 04:51:44 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.118 2003/03/28 20:17:13 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -357,6 +357,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
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d14e9c78db821602fe1de414b496bcc555c1122f..ebf99187c670e4f2d79965e763f94e8ef76f6b89 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -213,6 +213,6 @@
 #sql_inheritance = true
 #transform_null_equals = false
 #statement_timeout = 0		# 0 is disabled, in milliseconds
+#zero_damaged_pages = false     # set this true only for disaster recovery
 #db_user_namespace = false
 #preload_libraries = ''
- 
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 5fcc354849df33ba6023e663f027b3c950648ff7..767530fc1891ecc4da3d271638fd3858bca5ade1 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -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.67 2003/03/28 20:17:13 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;
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 159a82a0228a61e0998434d5bd84a8bcd4b782e3..0222d1dc0eda2cb3e69ec58e774d2c8421f04d45 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -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.54 2003/03/28 20:17:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,7 +19,6 @@
 #include "storage/item.h"
 #include "storage/itemid.h"
 #include "storage/off.h"
-#include "storage/page.h"
 #include "access/xlog.h"
 
 /*
@@ -74,11 +73,7 @@
  * fields.
  */
 
-/*
- * PageIsValid
- *		True iff page is valid.
- */
-#define PageIsValid(page) PointerIsValid(page)
+typedef Pointer Page;
 
 
 /*
@@ -141,22 +136,12 @@ typedef PageHeaderData *PageHeader;
  *						page support macros
  * ----------------------------------------------------------------
  */
-/*
- * PageIsValid -- This is defined in page.h.
- */
 
 /*
- * PageIsUsed
- *		True iff the page size is used.
- *
- * Note:
- *		Assumes page is valid.
+ * PageIsValid
+ *		True iff page is valid.
  */
-#define PageIsUsed(page) \
-( \
-	AssertMacro(PageIsValid(page)), \
-	((bool) (((PageHeader) (page))->pd_lower != 0)) \
-)
+#define PageIsValid(page) PointerIsValid(page)
 
 /*
  * line pointer does not count as part of header
@@ -172,9 +157,8 @@ typedef PageHeaderData *PageHeader;
 
 /*
  * PageIsNew
- *		returns true iff page is not initialized (by PageInit)
+ *		returns true iff page has not been initialized (by PageInit)
  */
-
 #define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
 
 /*
@@ -199,12 +183,6 @@ typedef PageHeaderData *PageHeader;
 /*
  * PageSizeIsValid
  *		True iff the page size is valid.
- *
- * XXX currently all page sizes are "valid" but we only actually
- *	   use BLCKSZ.
- *
- * 01/06/98 Now does something useful.	darrenk
- *
  */
 #define PageSizeIsValid(pageSize) ((pageSize) == BLCKSZ)
 
@@ -214,7 +192,7 @@ typedef PageHeaderData *PageHeader;
  *
  * this can only be called on a formatted page (unlike
  * BufferGetPageSize, which can be called on an unformatted page).
- * however, it can be called on a page for which there is no buffer.
+ * however, it can be called on a page that is not stored in a buffer.
  */
 #define PageGetPageSize(page) \
 	((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
@@ -222,10 +200,6 @@ typedef PageHeaderData *PageHeader;
 /*
  * PageGetPageLayoutVersion
  *		Returns the page layout version of a page.
- *
- * this can only be called on a formatted page (unlike
- * BufferGetPageSize, which can be called on an unformatted page).
- * however, it can be called on a page for which there is no buffer.
  */
 #define PageGetPageLayoutVersion(page) \
 	(((PageHeader) (page))->pd_pagesize_version & 0x00FF)
@@ -251,9 +225,6 @@ typedef PageHeaderData *PageHeader;
 /*
  * PageGetSpecialSize
  *		Returns size of special space on a page.
- *
- * Note:
- *		Assumes page is locked.
  */
 #define PageGetSpecialSize(page) \
 	((uint16) (PageGetPageSize(page) - ((PageHeader)(page))->pd_special))
@@ -261,9 +232,6 @@ typedef PageHeaderData *PageHeader;
 /*
  * PageGetSpecialPointer
  *		Returns pointer to special space on a page.
- *
- * Note:
- *		Assumes page is locked.
  */
 #define PageGetSpecialPointer(page) \
 ( \
@@ -339,6 +307,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);
diff --git a/src/include/storage/page.h b/src/include/storage/page.h
deleted file mode 100644
index 6f2ad05e924119564af292056262f1b95bd00e4c..0000000000000000000000000000000000000000
--- a/src/include/storage/page.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * page.h
- *	  POSTGRES buffer page abstraction definitions.
- *
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: page.h,v 1.12 2002/06/20 20:29:52 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef PAGE_H
-#define PAGE_H
-
-typedef Pointer Page;
-
-/*
- * PageIsValid
- *		True iff page is valid.
- */
-#define PageIsValid(page) PointerIsValid(page)
-
-#endif   /* PAGE_H */