postgres.git / summary / log / commit / refs

commit    072b962d99c6317e5cd5a5ac11b21fae2cab6f8a
Author:   Robert Haas <rhaas@postgresql.org>
Date:     Mon Aug 03 16:25:01 2026 +0000

    Undo inadvertent loosening of archive filename checking.
    
    Commit c8a350a439826267186c187dbfbf1f839f7521aa attempted to consolidate
    code for identify possibly-compressed tar archives by suffix into a new
    function parse_tar_compress_algorithm(). Unfortunately, the refactoring
    wasn't perfect, and slightly changed the behavior at both existing call
    sites.
    
    In CreateBackupStreamer(), the previous code required the filename to
    consist of more than just a suffix, so the aforementioned commit had the
    effect of allowing pg_basebackup to accept a file from the server whose
    entire name was something like .tar.gz -- which should never happen, but
    let's reject it as previous releases did.
    
    In precheck_tar_backup_file(), the previous code required the suffix to
    be immediately adjacent to the prefix already checked, so the commit
    in question allowed pg_verifybackup to accept not only filenames like
    base.tar.gz but also filenames like baseFOOBARBAZ.tar.gz. While such
    filenames are perhaps unlikely, rejecting them is correct, so let's go
    back to that behavior.
    
    Discussion: http://postgr.es/m/CA+TgmoYJY8FkoeYKGF_YF1S6uOK7fd0Bd3zrw0XY_oZXbmVFpQ@mail.gmail.com
    Reported-by: Sarath Kumar <Sarath@iitmpravartak.net>
    Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
    Backpatch-through: 19


src/bin/pg_basebackup/pg_basebackup.c | 4 ++-- src/bin/pg_verifybackup/pg_verifybackup.c | 8 ++++++-- src/bin/pg_waldump/pg_waldump.c | 5 +++-- src/common/compression.c | 28 +++++++++++++++++++++------- src/include/common/compression.h | 2 +- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 80dc3bbc8da..00408f1de49 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -1082,8 +1082,8 @@ CreateBackupStreamer(char *archive_name, char *spclocation, inject_manifest = (format == 't' && strcmp(basedir, "-") == 0 && manifest); /* Check whether it is a tar archive and its compression type */ - is_tar = parse_tar_compress_algorithm(archive_name, - &compressed_tar_algorithm); + is_tar = (parse_tar_compress_algorithm(archive_name, + &compressed_tar_algorithm) > 0); /* Is this any kind of compressed tar? */ is_compressed_tar = (is_tar && diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c index bd4fe635c6f..a972dae3070 100644 --- a/src/bin/pg_verifybackup/pg_verifybackup.c +++ b/src/bin/pg_verifybackup/pg_verifybackup.c @@ -967,8 +967,12 @@ precheck_tar_backup_file(verifier_context *context, char *relpath, tblspc_oid = (Oid) num; } - /* Now, check the compression type of the tar */ - if (!parse_tar_compress_algorithm(suffix, &compress_algorithm)) + /* + * If parse_tar_compress_algorithm returns exactly 0, there are no + * characters between the prefix we already checked and the detected + * suffix. Any other case is unexpected. + */ + if (parse_tar_compress_algorithm(suffix, &compress_algorithm) != 0) { report_backup_error(context, "file \"%s\" is not expected in a tar format backup", diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c index c777e6763e5..d6b2a7adcbf 100644 --- a/src/bin/pg_waldump/pg_waldump.c +++ b/src/bin/pg_waldump/pg_waldump.c @@ -1242,7 +1242,7 @@ main(int argc, char **argv) if (waldir != NULL) { /* Check whether the path looks like a tar archive by its extension */ - if (parse_tar_compress_algorithm(waldir, &compression)) + if (parse_tar_compress_algorithm(waldir, &compression) >= 0) { split_path(waldir, &private.archive_dir, &private.archive_name); } @@ -1286,7 +1286,8 @@ main(int argc, char **argv) pg_fatal("could not open directory \"%s\": %m", waldir); } - if (fname != NULL && parse_tar_compress_algorithm(fname, &compression)) + if (fname != NULL && + parse_tar_compress_algorithm(fname, &compression) >= 0) { private.archive_dir = waldir; private.archive_name = fname; diff --git a/src/common/compression.c b/src/common/compression.c index ae2089d9406..e78913ad9dc 100644 --- a/src/common/compression.c +++ b/src/common/compression.c @@ -42,33 +42,47 @@ static bool expect_boolean_value(char *keyword, char *value, pg_compress_specification *result); /* - * Look up a compression algorithm by archive file extension. Returns true and - * sets *algorithm if the extension is recognized. Otherwise returns false. + * Look up a compression algorithm by archive file extension. Sets *algorithm + * and returns the length of the non-extension portion of the filename, or -1 + * if the filename does not end with a recognized tar extension. */ -bool +int parse_tar_compress_algorithm(const char *fname, pg_compress_algorithm *algorithm) { - size_t fname_len = strlen(fname); + int fname_len = strlen(fname); if (fname_len >= 4 && strcmp(fname + fname_len - 4, ".tar") == 0) + { *algorithm = PG_COMPRESSION_NONE; + return fname_len - 4; + } else if (fname_len >= 4 && strcmp(fname + fname_len - 4, ".tgz") == 0) + { *algorithm = PG_COMPRESSION_GZIP; + return fname_len - 4; + } else if (fname_len >= 7 && strcmp(fname + fname_len - 7, ".tar.gz") == 0) + { *algorithm = PG_COMPRESSION_GZIP; + return fname_len - 7; + } else if (fname_len >= 8 && strcmp(fname + fname_len - 8, ".tar.lz4") == 0) + { *algorithm = PG_COMPRESSION_LZ4; + return fname_len - 8; + } else if (fname_len >= 8 && strcmp(fname + fname_len - 8, ".tar.zst") == 0) + { *algorithm = PG_COMPRESSION_ZSTD; - else - return false; + return fname_len - 8; + } - return true; + return -1; } /* diff --git a/src/include/common/compression.h b/src/include/common/compression.h index f99c747cdd3..adbe668a105 100644 --- a/src/include/common/compression.h +++ b/src/include/common/compression.h @@ -41,7 +41,7 @@ typedef struct pg_compress_specification extern void parse_compress_options(const char *option, char **algorithm, char **detail); -extern bool parse_tar_compress_algorithm(const char *fname, +extern int parse_tar_compress_algorithm(const char *fname, pg_compress_algorithm *algorithm); extern bool parse_compress_algorithm(char *name, pg_compress_algorithm *algorithm); extern const char *get_compress_algorithm_name(pg_compress_algorithm algorithm); [parent: 1d24c588975a]