diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 3058ce921b0..3563fd997fd 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -1258,11 +1258,30 @@ _tarWriteHeader(const char *filename, const char *linktarget, struct stat * statbuf) { char h[512]; + enum tarError rc; - tarCreateHeader(h, filename, linktarget, statbuf->st_size, + rc = tarCreateHeader(h, filename, linktarget, statbuf->st_size, statbuf->st_mode, statbuf->st_uid, statbuf->st_gid, statbuf->st_mtime); + switch (rc) + { + case TAR_OK: + break; + case TAR_NAME_TOO_LONG: + ereport(ERROR, + (errmsg("file name too long for tar format: \"%s\"", + filename))); + break; + case TAR_SYMLINK_TOO_LONG: + ereport(ERROR, + (errmsg("symbolic link target too long for tar format: file name \"%s\", target \"%s\"", + filename, linktarget))); + break; + default: + elog(ERROR, "unrecognized tar error: %d", rc); + } + pq_putmessage('d', h, 512); } diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl index c966de0b741..7e9a776840c 100644 --- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl +++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl @@ -2,7 +2,7 @@ use strict; use warnings; use Cwd; use TestLib; -use Test::More tests => 33; +use Test::More tests => 35; program_help_ok('pg_basebackup'); program_version_ok('pg_basebackup'); @@ -49,6 +49,13 @@ command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup", '-Ft' ], 'tar format'); ok(-f "$tempdir/tarbackup/base.tar", 'backup tar was created'); +my $superlongname = "superlongname_" . ("x"x100); + +system_or_bail 'touch', "$tempdir/pgdata/$superlongname"; +command_fails([ 'pg_basebackup', '-D', "$tempdir/tarbackup_l1", '-Ft' ], + 'pg_basebackup tar with long name fails'); +unlink "$tempdir/pgdata/$superlongname"; + # Create a temporary directory in the system location and symlink it # to our physical temp location. That way we can use shorter names # for the tablespace directories, which hopefully won't run afoul of @@ -117,3 +124,9 @@ command_fails( command_fails( [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo" ], '-T with invalid format fails'); + +mkdir "$tempdir/$superlongname"; +psql 'postgres', "CREATE TABLESPACE tblspc3 LOCATION '$tempdir/$superlongname';"; +command_fails([ 'pg_basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ], + 'pg_basebackup tar with long symlink target fails'); +psql 'postgres', "DROP TABLESPACE tblspc3;"; diff --git a/src/include/pgtar.h b/src/include/pgtar.h index 20e461091a2..906db7cebcb 100644 --- a/src/include/pgtar.h +++ b/src/include/pgtar.h @@ -11,5 +11,13 @@ * *------------------------------------------------------------------------- */ -extern void tarCreateHeader(char *h, const char *filename, const char *linktarget, size_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime); + +enum tarError +{ + TAR_OK = 0, + TAR_NAME_TOO_LONG, + TAR_SYMLINK_TOO_LONG +}; + +extern enum tarError tarCreateHeader(char *h, const char *filename, const char *linktarget, size_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime); extern int tarChecksum(char *header); diff --git a/src/port/tar.c b/src/port/tar.c index 8ef4f9c3883..4721df3ddc3 100644 --- a/src/port/tar.c +++ b/src/port/tar.c @@ -49,10 +49,16 @@ tarChecksum(char *header) * must always have space for 512 characters, which is a requirement by * the tar format. */ -void +enum tarError tarCreateHeader(char *h, const char *filename, const char *linktarget, size_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime) { + if (strlen(filename) > 99) + return TAR_NAME_TOO_LONG; + + if (linktarget && strlen(linktarget) > 99) + return TAR_SYMLINK_TOO_LONG; + /* * Note: most of the fields in a tar header are not supposed to be * null-terminated. We use sprintf, which will write a null after the @@ -141,4 +147,6 @@ tarCreateHeader(char *h, const char *filename, const char *linktarget, * 6 digits, a space, and a null, which is legal per POSIX. */ sprintf(&h[148], "%06o ", tarChecksum(h)); + + return TAR_OK; }