]> granicus.if.org Git - postgresql/commitdiff
Error when creating names too long for tar format
authorPeter Eisentraut <peter_e@gmx.net>
Tue, 24 Feb 2015 18:41:07 +0000 (13:41 -0500)
committerPeter Eisentraut <peter_e@gmx.net>
Tue, 24 Feb 2015 18:41:07 +0000 (13:41 -0500)
The tar format (at least the version we are using), does not support
file names or symlink targets longer than 99 bytes.  Until now, the tar
creation code would silently truncate any names that are too long.  (Its
original application was pg_dump, where this never happens.)  This
creates problems when running base backups over the replication
protocol.

The most important problem is when a tablespace path is longer than 99
bytes, which will result in a truncated tablespace path being backed up.
Less importantly, the basebackup protocol also promises to back up any
other files it happens to find in the data directory, which would also
lead to file name truncation if someone put a file with a long name in
there.

Now both of these cases result in an error during the backup.

Add tests that fail when a too-long file name or symlink is attempted to
be backed up.

Reviewed-by: Robert Hass <robertmhaas@gmail.com>
src/backend/replication/basebackup.c
src/bin/pg_basebackup/t/010_pg_basebackup.pl
src/include/pgtar.h
src/port/tar.c

index 3058ce921b03b3b6c79f3bf745d9a17550f9ee2f..3563fd997fd89e39ea10be04a5ee8a22df6580b3 100644 (file)
@@ -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);
 }
 
index c966de0b741aab906266829bc54a0c8606dca226..7e9a776840ca1d5cd392d5c5bab59b129bbca1ff 100644 (file)
@@ -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;";
index 20e461091a229766d27c74ad56594cd19d569ee9..906db7cebcb099a12860db674f2f4fc518cf5ef9 100644 (file)
  *
  *-------------------------------------------------------------------------
  */
-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);
index 8ef4f9c3883099f0ad4f8349cfdedaa42e919a69..4721df3ddc33a942c956d787549048f31d25e6ee 100644 (file)
@@ -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;
 }