>From 1fd3cbdebddcf7f31c92b6b967b955a41737c71e Mon Sep 17 00:00:00 2001
From: Paul Eggert
Date: Mon, 13 Jul 2015 09:53:00 -0700
Subject: [PATCH 4/4] tar: fix symlink race and symlink transform bug
Problem reported by Tobias Stoeckmann in:
http://lists.gnu.org/archive/html/bug-tar/2015-07/msg00004.html
* gnulib.modules: Add areadlinkat-with-size.
* src/create.c: Include areadlink.h.
(dump_file0): Use areadlinkat_with_size, rather than trying to do
it by hand, incorrectly. This also avoids assumption that
the symlink contents fit on the stack. Also, use the transformed
link name, not the original link name, when deciding whether the
name is long enough to require writing a long link.
---
gnulib.modules | 1 +
src/create.c | 22 +++++++++-------------
2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/gnulib.modules b/gnulib.modules
index ec3dc90..fe5ab73 100644
--- a/gnulib.modules
+++ b/gnulib.modules
@@ -19,6 +19,7 @@
# along with this program. If not, see .
alloca
+areadlinkat-with-size
argmatch
argp
argp-version-etc
diff --git a/src/create.c b/src/create.c
index 1b08e0b..7cdc978 100644
--- a/src/create.c
+++ b/src/create.c
@@ -22,6 +22,7 @@
#include
+#include
#include
#include "common.h"
@@ -1114,7 +1115,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
return;
info_attach_exclist (st);
-
+
if (incremental_option && archive_format != POSIX_FORMAT)
blk->header.typeflag = GNUTYPE_DUMPDIR;
else /* if (standard_option) */
@@ -1198,7 +1199,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
char const *entry;
size_t entry_len;
size_t name_len;
-
+
name_buf = xstrdup (st->orig_file_name);
name_size = name_len = strlen (name_buf);
@@ -1837,22 +1838,17 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
#ifdef HAVE_READLINK
else if (S_ISLNK (st->stat.st_mode))
{
- char *buffer;
- int size;
- size_t linklen = st->stat.st_size;
- if (linklen != st->stat.st_size || linklen + 1 == 0)
- xalloc_die ();
- buffer = (char *) alloca (linklen + 1);
- size = readlinkat (parentfd, name, buffer, linklen + 1);
- if (size < 0)
+ st->link_name = areadlinkat_with_size (parentfd, name, st->stat.st_size);
+ if (!st->link_name)
{
+ if (errno == ENOMEM)
+ xalloc_die ();
file_removed_diag (p, top_level, readlink_diag);
return;
}
- buffer[size] = '\0';
- assign_string (&st->link_name, buffer);
transform_name (&st->link_name, XFORM_SYMLINK);
- if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
+ if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT)
+ < strlen (st->link_name))
write_long_link (st);
xattrs_selinux_get (parentfd, name, st, 0);
--
2.1.0