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.
This commit is contained in:
Paul Eggert
2015-07-13 09:53:00 -07:00
parent 3828942550
commit fe3b106cb3
2 changed files with 10 additions and 13 deletions

View File

@@ -19,6 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
alloca
areadlinkat-with-size
argmatch
argp
argp-version-etc

View File

@@ -22,6 +22,7 @@
#include <system.h>
#include <areadlink.h>
#include <quotearg.h>
#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);