@@ -1405,3 +1405,215 @@ calc_file_checksum(pgFile *file)
14051405
14061406 return true;
14071407}
1408+
1409+ /* Validate given page
1410+ * return value:
1411+ * 0 - if the page is not found
1412+ * 1 - if the page is found and valid
1413+ * -1 - if the page is found but invalid
1414+ */
1415+ #define PAGE_IS_NOT_FOUND 0
1416+ #define PAGE_IS_FOUND_AND_VALID 1
1417+ #define PAGE_IS_FOUND_AND__NOT_VALID -1
1418+ static int
1419+ validate_one_page (Page page , pgFile * file ,
1420+ BlockNumber blknum , XLogRecPtr stop_lsn ,
1421+ uint32 checksum_version )
1422+ {
1423+ PageHeader phdr ;
1424+ XLogRecPtr lsn ;
1425+ bool page_header_is_sane = false;
1426+ bool checksum_is_ok = false;
1427+
1428+ /* new level of paranoia */
1429+ if (page == NULL )
1430+ {
1431+ elog (LOG , "File %s, block %u, page is NULL" ,
1432+ file -> path , blknum );
1433+ return PAGE_IS_NOT_FOUND ;
1434+ }
1435+
1436+ if (PageIsNew (page ))
1437+ {
1438+ int i ;
1439+ /* Check if the page is zeroed. */
1440+ for (i = 0 ; i < BLCKSZ && page [i ] == 0 ; i ++ );
1441+
1442+ if (i == BLCKSZ )
1443+ {
1444+ elog (LOG , "File: %s blknum %u, page is New. empty zeroed page" ,
1445+ file -> path , blknum );
1446+ return PAGE_IS_FOUND_AND_VALID ;
1447+ }
1448+ else
1449+ {
1450+ elog (WARNING , "File: %s, block %u, page is New, but not zeroed" ,
1451+ file -> path , blknum );
1452+ }
1453+
1454+ /* Page is zeroed. No sense to check header and checksum. */
1455+ page_header_is_sane = false;
1456+ }
1457+ else
1458+ {
1459+ phdr = (PageHeader ) page ;
1460+
1461+ if (PageGetPageSize (phdr ) == BLCKSZ &&
1462+ PageGetPageLayoutVersion (phdr ) == PG_PAGE_LAYOUT_VERSION &&
1463+ (phdr -> pd_flags & ~PD_VALID_FLAG_BITS ) == 0 &&
1464+ phdr -> pd_lower >= SizeOfPageHeaderData &&
1465+ phdr -> pd_lower <= phdr -> pd_upper &&
1466+ phdr -> pd_upper <= phdr -> pd_special &&
1467+ phdr -> pd_special <= BLCKSZ &&
1468+ phdr -> pd_special == MAXALIGN (phdr -> pd_special ))
1469+ page_header_is_sane = true;
1470+ }
1471+
1472+ if (page_header_is_sane )
1473+ {
1474+ /* Verify checksum */
1475+ if (checksum_version )
1476+ {
1477+ /*
1478+ * If checksum is wrong, sleep a bit and then try again
1479+ * several times. If it didn't help, throw error
1480+ */
1481+ if (pg_checksum_page (page , file -> segno * RELSEG_SIZE + blknum )
1482+ == ((PageHeader ) page )-> pd_checksum )
1483+ {
1484+ checksum_is_ok = true;
1485+ }
1486+ else
1487+ {
1488+ elog (WARNING , "File: %s blknum %u have wrong checksum" ,
1489+ file -> path , blknum );
1490+ }
1491+ }
1492+
1493+ if (!checksum_version )
1494+ {
1495+ /* Get lsn from page header. Ensure that page is from our time */
1496+ lsn = PageXLogRecPtrGet (phdr -> pd_lsn );
1497+
1498+ if (lsn > stop_lsn )
1499+ elog (WARNING , "File: %s, block %u, checksum is not enabled."
1500+ "page is from future: pageLSN %X/%X stopLSN %X/%X" ,
1501+ file -> path , blknum , (uint32 ) (lsn >> 32 ), (uint32 ) lsn ,
1502+ (uint32 ) (stop_lsn >> 32 ), (uint32 ) stop_lsn );
1503+ else
1504+ return PAGE_IS_FOUND_AND_VALID ;
1505+ }
1506+
1507+ if (checksum_is_ok )
1508+ {
1509+ /* Get lsn from page header. Ensure that page is from our time */
1510+ lsn = PageXLogRecPtrGet (phdr -> pd_lsn );
1511+
1512+ if (lsn > stop_lsn )
1513+ elog (WARNING , "File: %s, block %u, checksum is correct."
1514+ "page is from future: pageLSN %X/%X stopLSN %X/%X" ,
1515+ file -> path , blknum , (uint32 ) (lsn >> 32 ), (uint32 ) lsn ,
1516+ (uint32 ) (stop_lsn >> 32 ), (uint32 ) stop_lsn );
1517+ else
1518+ return PAGE_IS_FOUND_AND_VALID ;
1519+ }
1520+ }
1521+
1522+ return PAGE_IS_FOUND_AND__NOT_VALID ;
1523+ }
1524+
1525+ /* Valiate pages of datafile in backup one by one */
1526+ bool
1527+ check_file_pages (pgFile * file , XLogRecPtr stop_lsn , uint32 checksum_version )
1528+ {
1529+ size_t read_len = 0 ;
1530+ bool is_valid = true;
1531+ FILE * in ;
1532+
1533+ elog (VERBOSE , "validate relation blocks for file %s" , file -> name );
1534+
1535+ in = fopen (file -> path , PG_BINARY_R );
1536+ if (in == NULL )
1537+ {
1538+ if (errno == ENOENT )
1539+ {
1540+ elog (WARNING , "File \"%s\" is not found" , file -> path );
1541+ return false;
1542+ }
1543+
1544+ elog (ERROR , "cannot open file \"%s\": %s" ,
1545+ file -> path , strerror (errno ));
1546+ }
1547+
1548+ /* read and validate pages one by one */
1549+ while (true)
1550+ {
1551+ Page compressed_page = NULL ; /* used as read buffer */
1552+ Page page = NULL ;
1553+ BackupPageHeader header ;
1554+ BlockNumber blknum ;
1555+
1556+ /* read BackupPageHeader */
1557+ read_len = fread (& header , 1 , sizeof (header ), in );
1558+ if (read_len != sizeof (header ))
1559+ {
1560+ int errno_tmp = errno ;
1561+ if (read_len == 0 && feof (in ))
1562+ break ; /* EOF found */
1563+ else if (read_len != 0 && feof (in ))
1564+ elog (ERROR ,
1565+ "odd size page found at block %u of \"%s\"" ,
1566+ blknum , file -> path );
1567+ else
1568+ elog (ERROR , "cannot read header of block %u of \"%s\": %s" ,
1569+ blknum , file -> path , strerror (errno_tmp ));
1570+ }
1571+
1572+ if (header .block < blknum )
1573+ elog (ERROR , "backup is broken at file->path %s block %u" ,
1574+ file -> path , blknum );
1575+
1576+ blknum = header .block ;
1577+
1578+ if (header .compressed_size == PageIsTruncated )
1579+ {
1580+ elog (LOG , "File %s, block %u is truncated" ,
1581+ file -> path , blknum );
1582+ continue ;
1583+ }
1584+
1585+ Assert (header .compressed_size <= BLCKSZ );
1586+
1587+ read_len = fread (compressed_page , 1 ,
1588+ MAXALIGN (header .compressed_size ), in );
1589+ if (read_len != MAXALIGN (header .compressed_size ))
1590+ elog (ERROR , "cannot read block %u of \"%s\" read %lu of %d" ,
1591+ blknum , file -> path , read_len , header .compressed_size );
1592+
1593+ if (header .compressed_size != BLCKSZ )
1594+ {
1595+ int32 uncompressed_size = 0 ;
1596+
1597+ uncompressed_size = do_decompress (page , BLCKSZ ,
1598+ compressed_page ,
1599+ MAXALIGN (header .compressed_size ),
1600+ file -> compress_alg );
1601+
1602+ if (uncompressed_size != BLCKSZ )
1603+ elog (ERROR , "page of file \"%s\" uncompressed to %d bytes. != BLCKSZ" ,
1604+ file -> path , uncompressed_size );
1605+
1606+ if (validate_one_page (page , file , blknum ,
1607+ stop_lsn , checksum_version ) == PAGE_IS_FOUND_AND__NOT_VALID )
1608+ is_valid = false;
1609+ }
1610+ else
1611+ {
1612+ if (validate_one_page (compressed_page , file , blknum ,
1613+ stop_lsn , checksum_version ) == PAGE_IS_FOUND_AND__NOT_VALID )
1614+ is_valid = false;
1615+ }
1616+ }
1617+
1618+ return is_valid ;
1619+ }
0 commit comments