aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/replication/basebackup.c16
-rw-r--r--src/bin/pg_basebackup/bbstreamer_tar.c10
-rw-r--r--src/bin/pg_basebackup/pg_basebackup.c25
3 files changed, 45 insertions, 6 deletions
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 38c82c46196..92430439f53 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -374,7 +374,16 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
Assert(lnext(state.tablespaces, lc) == NULL);
}
else
+ {
+ /* Properly terminate the tarfile. */
+ StaticAssertStmt(TAR_BLOCK_SIZE <= 2 * BLCKSZ,
+ "BLCKSZ too small for 2 tar blocks");
+ memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
+ bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
+
+ /* OK, that's the end of the archive. */
bbsink_end_archive(sink);
+ }
}
basebackup_progress_wait_wal_archive(&state);
@@ -611,6 +620,13 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
sendFileWithContent(sink, pathbuf, "", &manifest);
}
+ /* Properly terminate the tar file. */
+ StaticAssertStmt(TAR_BLOCK_SIZE <= 2 * BLCKSZ,
+ "BLCKSZ too small for 2 tar blocks");
+ memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
+ bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
+
+ /* OK, that's the end of the archive. */
bbsink_end_archive(sink);
}
diff --git a/src/bin/pg_basebackup/bbstreamer_tar.c b/src/bin/pg_basebackup/bbstreamer_tar.c
index 5fded0f4e6f..e6bd3ef52ed 100644
--- a/src/bin/pg_basebackup/bbstreamer_tar.c
+++ b/src/bin/pg_basebackup/bbstreamer_tar.c
@@ -2,6 +2,16 @@
*
* bbstreamer_tar.c
*
+ * This module implements three types of tar processing. A tar parser
+ * expects unlabelled chunks of data (e.g. BBSTREAMER_UNKNOWN) and splits
+ * it into labelled chunks (any other value of bbstreamer_archive_context).
+ * A tar archiver does the reverse: it takes a bunch of labelled chunks
+ * and produces a tarfile, optionally replacing member headers and trailers
+ * so that upstream bbstreamer objects can perform surgery on the tarfile
+ * contents without knowing the details of the tar format. A tar terminator
+ * just adds two blocks of NUL bytes to the end of the file, since older
+ * server versions produce files with this terminator omitted.
+ *
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
*
* IDENTIFICATION
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 30efc03b830..1739ac63823 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -86,6 +86,12 @@ typedef void (*WriteDataCallback) (size_t nbytes, char *buf,
#define MINIMUM_VERSION_FOR_MANIFESTS 130000
/*
+ * Before v15, tar files received from the server will be improperly
+ * terminated.
+ */
+#define MINIMUM_VERSION_FOR_TERMINATED_TARFILE 150000
+
+/*
* Different ways to include WAL
*/
typedef enum
@@ -166,7 +172,8 @@ static void progress_report(int tablespacenum, bool force, bool finished);
static bbstreamer *CreateBackupStreamer(char *archive_name, char *spclocation,
bbstreamer **manifest_inject_streamer_p,
- bool is_recovery_guc_supported);
+ bool is_recovery_guc_supported,
+ bool expect_unterminated_tarfile);
static void ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
bool tablespacenum);
static void ReceiveTarCopyChunk(size_t r, char *copybuf, void *callback_data);
@@ -965,7 +972,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
static bbstreamer *
CreateBackupStreamer(char *archive_name, char *spclocation,
bbstreamer **manifest_inject_streamer_p,
- bool is_recovery_guc_supported)
+ bool is_recovery_guc_supported,
+ bool expect_unterminated_tarfile)
{
bbstreamer *streamer;
bbstreamer *manifest_inject_streamer = NULL;
@@ -1074,12 +1082,13 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
/*
* If we're doing anything that involves understanding the contents of
* the archive, we'll need to parse it. If not, we can skip parsing it,
- * but the tar files the server sends are not properly terminated, so
- * we'll need to add the terminator here.
+ * but old versions of the server send improperly terminated tarfiles,
+ * so if we're talking to such a server we'll need to add the terminator
+ * here.
*/
if (must_parse_archive)
streamer = bbstreamer_tar_parser_new(streamer);
- else
+ else if (expect_unterminated_tarfile)
streamer = bbstreamer_tar_terminator_new(streamer);
/* Return the results. */
@@ -1099,14 +1108,18 @@ ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
WriteTarState state;
bbstreamer *manifest_inject_streamer;
bool is_recovery_guc_supported;
+ bool expect_unterminated_tarfile;
/* Pass all COPY data through to the backup streamer. */
memset(&state, 0, sizeof(state));
is_recovery_guc_supported =
PQserverVersion(conn) >= MINIMUM_VERSION_FOR_RECOVERY_GUC;
+ expect_unterminated_tarfile =
+ PQserverVersion(conn) < MINIMUM_VERSION_FOR_TERMINATED_TARFILE;
state.streamer = CreateBackupStreamer(archive_name, spclocation,
&manifest_inject_streamer,
- is_recovery_guc_supported);
+ is_recovery_guc_supported,
+ expect_unterminated_tarfile);
state.tablespacenum = tablespacenum;
ReceiveCopyData(conn, ReceiveTarCopyChunk, &state);
progress_filename = NULL;