Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 35421a4

Browse files
committed
Fix pg_restore's misdesigned code for detecting archive file format.
Despite the clear comments pointing out that the duplicative code segments in ReadHead() and _discoverArchiveFormat() needed to be in sync, they were not: the latter did not bother to apply any of the sanity checks in the former. We'd missed noticing this partly because none of those checks would fail in scenarios we customarily test, and partly because the oversight would be masked if both segments execute, which they would in cases other than needing to autodetect the format of a non-seekable stdin source. However, in a case meeting all these requirements --- for example, trying to read a newer-than-supported archive format from non-seekable stdin --- pg_restore missed applying the version check and would likely dump core or otherwise misbehave. The whole thing is silly anyway, because there seems little reason to duplicate the logic beyond the one-line verification that the file starts with "PGDMP". There seems to have been an undocumented assumption that multiple major formats (major enough to require separate reader modules) would nonetheless share the first half-dozen fields of the custom-format header. This seems unlikely, so let's fix it by just nuking the duplicate logic in _discoverArchiveFormat(). Also get rid of the pointless attempt to seek back to the start of the file after successful autodetection. That wastes cycles and it means we have four behaviors to verify not two. Per bug #16951 from Sergey Koposov. This has been broken for decades, so back-patch to all supported versions. Discussion: https://postgr.es/m/16951-a4dd68cf0de23048@postgresql.org
1 parent 876ecfb commit 35421a4

File tree

3 files changed

+51
-110
lines changed

3 files changed

+51
-110
lines changed

src/bin/pg_dump/pg_backup_archiver.c

Lines changed: 39 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,6 +2080,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
20802080
if (AH->lookahead)
20812081
free(AH->lookahead);
20822082

2083+
AH->readHeader = 0;
20832084
AH->lookaheadSize = 512;
20842085
AH->lookahead = pg_malloc0(512);
20852086
AH->lookaheadLen = 0;
@@ -2151,62 +2152,9 @@ _discoverArchiveFormat(ArchiveHandle *AH)
21512152

21522153
if (strncmp(sig, "PGDMP", 5) == 0)
21532154
{
2154-
int byteread;
2155-
char vmaj,
2156-
vmin,
2157-
vrev;
2158-
2159-
/*
2160-
* Finish reading (most of) a custom-format header.
2161-
*
2162-
* NB: this code must agree with ReadHead().
2163-
*/
2164-
if ((byteread = fgetc(fh)) == EOF)
2165-
READ_ERROR_EXIT(fh);
2166-
2167-
vmaj = byteread;
2168-
2169-
if ((byteread = fgetc(fh)) == EOF)
2170-
READ_ERROR_EXIT(fh);
2171-
2172-
vmin = byteread;
2173-
2174-
/* Save these too... */
2175-
AH->lookahead[AH->lookaheadLen++] = vmaj;
2176-
AH->lookahead[AH->lookaheadLen++] = vmin;
2177-
2178-
/* Check header version; varies from V1.0 */
2179-
if (vmaj > 1 || (vmaj == 1 && vmin > 0)) /* Version > 1.0 */
2180-
{
2181-
if ((byteread = fgetc(fh)) == EOF)
2182-
READ_ERROR_EXIT(fh);
2183-
2184-
vrev = byteread;
2185-
AH->lookahead[AH->lookaheadLen++] = vrev;
2186-
}
2187-
else
2188-
vrev = 0;
2189-
2190-
AH->version = MAKE_ARCHIVE_VERSION(vmaj, vmin, vrev);
2191-
2192-
if ((AH->intSize = fgetc(fh)) == EOF)
2193-
READ_ERROR_EXIT(fh);
2194-
AH->lookahead[AH->lookaheadLen++] = AH->intSize;
2195-
2196-
if (AH->version >= K_VERS_1_7)
2197-
{
2198-
if ((AH->offSize = fgetc(fh)) == EOF)
2199-
READ_ERROR_EXIT(fh);
2200-
AH->lookahead[AH->lookaheadLen++] = AH->offSize;
2201-
}
2202-
else
2203-
AH->offSize = AH->intSize;
2204-
2205-
if ((byteread = fgetc(fh)) == EOF)
2206-
READ_ERROR_EXIT(fh);
2207-
2208-
AH->format = byteread;
2209-
AH->lookahead[AH->lookaheadLen++] = AH->format;
2155+
/* It's custom format, stop here */
2156+
AH->format = archCustom;
2157+
AH->readHeader = 1;
22102158
}
22112159
else
22122160
{
@@ -2243,22 +2191,15 @@ _discoverArchiveFormat(ArchiveHandle *AH)
22432191
AH->format = archTar;
22442192
}
22452193

2246-
/* If we can't seek, then mark the header as read */
2247-
if (fseeko(fh, 0, SEEK_SET) != 0)
2248-
{
2249-
/*
2250-
* NOTE: Formats that use the lookahead buffer can unset this in their
2251-
* Init routine.
2252-
*/
2253-
AH->readHeader = 1;
2254-
}
2255-
else
2256-
AH->lookaheadLen = 0; /* Don't bother since we've reset the file */
2257-
2258-
/* Close the file */
2194+
/* Close the file if we opened it */
22592195
if (wantClose)
2196+
{
22602197
if (fclose(fh) != 0)
22612198
fatal("could not close input file: %m");
2199+
/* Forget lookahead, since we'll re-read header after re-opening */
2200+
AH->readHeader = 0;
2201+
AH->lookaheadLen = 0;
2202+
}
22622203

22632204
return AH->format;
22642205
}
@@ -3767,7 +3708,9 @@ WriteHead(ArchiveHandle *AH)
37673708
void
37683709
ReadHead(ArchiveHandle *AH)
37693710
{
3770-
char tmpMag[7];
3711+
char vmaj,
3712+
vmin,
3713+
vrev;
37713714
int fmt;
37723715
struct tm crtm;
37733716

@@ -3779,48 +3722,46 @@ ReadHead(ArchiveHandle *AH)
37793722
*/
37803723
if (!AH->readHeader)
37813724
{
3782-
char vmaj,
3783-
vmin,
3784-
vrev;
3725+
char tmpMag[7];
37853726

37863727
AH->ReadBufPtr(AH, tmpMag, 5);
37873728

37883729
if (strncmp(tmpMag, "PGDMP", 5) != 0)
37893730
fatal("did not find magic string in file header");
3731+
}
37903732

3791-
vmaj = AH->ReadBytePtr(AH);
3792-
vmin = AH->ReadBytePtr(AH);
3733+
vmaj = AH->ReadBytePtr(AH);
3734+
vmin = AH->ReadBytePtr(AH);
37933735

3794-
if (vmaj > 1 || (vmaj == 1 && vmin > 0)) /* Version > 1.0 */
3795-
vrev = AH->ReadBytePtr(AH);
3796-
else
3797-
vrev = 0;
3736+
if (vmaj > 1 || (vmaj == 1 && vmin > 0)) /* Version > 1.0 */
3737+
vrev = AH->ReadBytePtr(AH);
3738+
else
3739+
vrev = 0;
37983740

3799-
AH->version = MAKE_ARCHIVE_VERSION(vmaj, vmin, vrev);
3741+
AH->version = MAKE_ARCHIVE_VERSION(vmaj, vmin, vrev);
38003742

3801-
if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
3802-
fatal("unsupported version (%d.%d) in file header",
3803-
vmaj, vmin);
3743+
if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
3744+
fatal("unsupported version (%d.%d) in file header",
3745+
vmaj, vmin);
38043746

3805-
AH->intSize = AH->ReadBytePtr(AH);
3806-
if (AH->intSize > 32)
3807-
fatal("sanity check on integer size (%lu) failed",
3808-
(unsigned long) AH->intSize);
3747+
AH->intSize = AH->ReadBytePtr(AH);
3748+
if (AH->intSize > 32)
3749+
fatal("sanity check on integer size (%lu) failed",
3750+
(unsigned long) AH->intSize);
38093751

3810-
if (AH->intSize > sizeof(int))
3811-
pg_log_warning("archive was made on a machine with larger integers, some operations might fail");
3752+
if (AH->intSize > sizeof(int))
3753+
pg_log_warning("archive was made on a machine with larger integers, some operations might fail");
38123754

3813-
if (AH->version >= K_VERS_1_7)
3814-
AH->offSize = AH->ReadBytePtr(AH);
3815-
else
3816-
AH->offSize = AH->intSize;
3755+
if (AH->version >= K_VERS_1_7)
3756+
AH->offSize = AH->ReadBytePtr(AH);
3757+
else
3758+
AH->offSize = AH->intSize;
38173759

3818-
fmt = AH->ReadBytePtr(AH);
3760+
fmt = AH->ReadBytePtr(AH);
38193761

3820-
if (AH->format != fmt)
3821-
fatal("expected format (%d) differs from format found in file (%d)",
3822-
AH->format, fmt);
3823-
}
3762+
if (AH->format != fmt)
3763+
fatal("expected format (%d) differs from format found in file (%d)",
3764+
AH->format, fmt);
38243765

38253766
if (AH->version >= K_VERS_1_2)
38263767
{

src/bin/pg_dump/pg_backup_archiver.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,15 +256,21 @@ struct _archiveHandle
256256
time_t createDate; /* Date archive created */
257257

258258
/*
259-
* Fields used when discovering header. A format can always get the
260-
* previous read bytes from here...
259+
* Fields used when discovering archive format. For tar format, we load
260+
* the first block into the lookahead buffer, and verify that it looks
261+
* like a tar header. The tar module must then consume bytes from the
262+
* lookahead buffer before reading any more from the file. For custom
263+
* format, we load only the "PGDMP" marker into the buffer, and then set
264+
* readHeader after confirming it matches. The buffer is vestigial in
265+
* this case, as the subsequent code just checks readHeader and doesn't
266+
* examine the buffer.
261267
*/
262-
int readHeader; /* Used if file header has been read already */
268+
int readHeader; /* Set if we already read "PGDMP" marker */
263269
char *lookahead; /* Buffer used when reading header to discover
264270
* format */
265-
size_t lookaheadSize; /* Size of allocated buffer */
266-
size_t lookaheadLen; /* Length of data in lookahead */
267-
pgoff_t lookaheadPos; /* Current read position in lookahead buffer */
271+
size_t lookaheadSize; /* Allocated size of buffer */
272+
size_t lookaheadLen; /* Length of valid data in lookahead */
273+
size_t lookaheadPos; /* Current read position in lookahead buffer */
268274

269275
ArchiveEntryPtrType ArchiveEntryPtr; /* Called for each metadata object */
270276
StartDataPtrType StartDataPtr; /* Called when table data is about to be

src/bin/pg_dump/pg_backup_tar.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,6 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
228228

229229
ctx->hasSeek = checkSeek(ctx->tarFH);
230230

231-
/*
232-
* Forcibly unmark the header as read since we use the lookahead
233-
* buffer
234-
*/
235-
AH->readHeader = 0;
236-
237231
ctx->FH = (void *) tarOpen(AH, "toc.dat", 'r');
238232
ReadHead(AH);
239233
ReadToc(AH);

0 commit comments

Comments
 (0)