@@ -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