From bde3fb4e3125eed740b5d949a990b4e06d01499a Mon Sep 17 00:00:00 2001 From: Amul Sul Date: Sat, 21 Mar 2026 11:22:50 +0530 Subject: [PATCH] pg_waldump: Handle archive exhaustion in init_archive_reader(). When read_archive_file() returns 0, the archive may have already buffered a complete WAL file into the hash table before exhausting the input. Instead of immediately reporting an error, search the hash table for an entry containing at least sizeof(XLogLongPageHeader) bytes. Report a specific error if a WAL entry exists but is too short (truncated/corrupt), or a generic error if no WAL was found at all. Also tighten the loop condition to check for sizeof(XLogLongPageHeader) rather than XLOG_BLCKSZ, since only the long page header is needed at this stage. --- src/bin/pg_waldump/archive_waldump.c | 51 +++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/bin/pg_waldump/archive_waldump.c b/src/bin/pg_waldump/archive_waldump.c index b078c2d6960..5bd1faf3d95 100644 --- a/src/bin/pg_waldump/archive_waldump.c +++ b/src/bin/pg_waldump/archive_waldump.c @@ -176,13 +176,56 @@ init_archive_reader(XLogDumpPrivate *privateInfo, * the first WAL segment in the archive so we can extract the WAL segment * size from the long page header. */ - while (entry == NULL || entry->buf->len < XLOG_BLCKSZ) + while (entry == NULL || entry->read_len < sizeof(XLogLongPageHeader)) { if (read_archive_file(privateInfo, XLOG_BLCKSZ) == 0) - pg_fatal("could not find WAL in archive \"%s\"", - privateInfo->archive_name); + { + ArchivedWAL_iterator iter; + ArchivedWALFile *e = NULL; - entry = privateInfo->cur_file; + entry = NULL; + + /* + * read_archive_file() returned 0, meaning the archive is + * exhausted. However, a sufficiently compressed archive may have + * already read a complete WAL file and inserted it into the hash + * table before returning. Search the hash table for any entry + * that already has enough buffered data to contain the long page + * header; if none is found, the archive contains no usable WAL. + */ + ArchivedWAL_start_iterate(privateInfo->archive_wal_htab, &iter); + while ((e = ArchivedWAL_iterate(privateInfo->archive_wal_htab, + &iter)) != NULL) + { + if (e->read_len >= sizeof(XLogLongPageHeader)) + { + entry = e; + break; + } + } + + if (entry == NULL) + { + /* + * A WAL file was found in the hash table but it does not + * contain enough data to read the long page header, + * indicating a truncated or corrupt WAL segment. + */ + if (e != NULL) + pg_fatal("could not read file \"%s\" from \"%s\" archive: read %d of %d", + e->fname, privateInfo->archive_name, e->read_len, + (int) sizeof(XLogLongPageHeader)); + + /* + * The hash table contains no WAL entries at all, meaning the + * archive holds no WAL data. + */ + pg_fatal("could not find WAL in archive \"%s\"", + privateInfo->archive_name); + } + } + else + entry = privateInfo->cur_file; } /* Extract the WAL segment size from the long page header */ -- 2.47.1