@@ -99,6 +99,55 @@ do_decompress(void* dst, size_t dst_size, void const* src, size_t src_size,
9999 return -1 ;
100100}
101101
102+
103+ #define ZLIB_MAGIC 0x78
104+
105+ /*
106+ * Before version 2.0.23 there was a bug in pro_backup that pages which compressed
107+ * size is exactly the same as original size are not treated as compressed.
108+ * This check tries to detect and decompress such pages.
109+ * There is no 100% criteria to determine whether page is compressed or not.
110+ * But at least we will do this check only for pages which will no pass validation step.
111+ */
112+ static bool
113+ page_may_be_compressed (Page page , CompressAlg alg )
114+ {
115+ PageHeader phdr ;
116+
117+ phdr = (PageHeader ) page ;
118+
119+ /* First check if page header is valid (it seems to be fast enough check) */
120+ if (!(PageGetPageSize (phdr ) == BLCKSZ &&
121+ PageGetPageLayoutVersion (phdr ) == PG_PAGE_LAYOUT_VERSION &&
122+ (phdr -> pd_flags & ~PD_VALID_FLAG_BITS ) == 0 &&
123+ phdr -> pd_lower >= SizeOfPageHeaderData &&
124+ phdr -> pd_lower <= phdr -> pd_upper &&
125+ phdr -> pd_upper <= phdr -> pd_special &&
126+ phdr -> pd_special <= BLCKSZ &&
127+ phdr -> pd_special == MAXALIGN (phdr -> pd_special )))
128+ {
129+ /* ... end only if it is invalid, then do more checks */
130+ int major , middle , minor ;
131+ if ( parse_program_version (current .program_version ) >= 20023 )
132+ {
133+ /* Versions 2.0.23 and higher don't have such bug */
134+ return false;
135+ }
136+ #ifdef HAVE_LIBZ
137+ /* For zlib we can check page magic:
138+ * https://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like
139+ */
140+ if (alg == ZLIB_COMPRESS && * (char * )page != ZLIB_MAGIC )
141+ {
142+ return false;
143+ }
144+ #endif
145+ /* otherwize let's try to decompress the page */
146+ return true;
147+ }
148+ return false;
149+ }
150+
102151/*
103152 * When copying datafiles to backup we validate and compress them block
104153 * by block. Thus special header is required for each data block.
@@ -717,7 +766,8 @@ restore_data_file(const char *to_path, pgFile *file, bool allow_truncate,
717766 elog (ERROR , "cannot read block %u of \"%s\" read %lu of %d" ,
718767 blknum , file -> path , read_len , header .compressed_size );
719768
720- if (header .compressed_size != BLCKSZ )
769+ if (header .compressed_size != BLCKSZ
770+ || page_may_be_compressed (compressed_page .data , file -> compress_alg ))
721771 {
722772 int32 uncompressed_size = 0 ;
723773
@@ -1595,7 +1645,8 @@ check_file_pages(pgFile *file, XLogRecPtr stop_lsn, uint32 checksum_version)
15951645 elog (ERROR , "cannot read block %u of \"%s\" read %lu of %d" ,
15961646 blknum , file -> path , read_len , header .compressed_size );
15971647
1598- if (header .compressed_size != BLCKSZ )
1648+ if (header .compressed_size != BLCKSZ
1649+ || page_may_be_compressed (compressed_page .data , file -> compress_alg ))
15991650 {
16001651 int32 uncompressed_size = 0 ;
16011652
@@ -1605,9 +1656,15 @@ check_file_pages(pgFile *file, XLogRecPtr stop_lsn, uint32 checksum_version)
16051656 file -> compress_alg );
16061657
16071658 if (uncompressed_size != BLCKSZ )
1659+ {
1660+ if (header .compressed_size == BLCKSZ )
1661+ {
1662+ is_valid = false;
1663+ continue ;
1664+ }
16081665 elog (ERROR , "page of file \"%s\" uncompressed to %d bytes. != BLCKSZ" ,
16091666 file -> path , uncompressed_size );
1610-
1667+ }
16111668 if (validate_one_page (page .data , file , blknum ,
16121669 stop_lsn , checksum_version ) == PAGE_IS_FOUND_AND_NOT_VALID )
16131670 is_valid = false;
0 commit comments