tar: don't crash if getcwd fails

* src/extract.c: Don't include xgetcwd.h.
(extract_dir): stat "." rather than statting getcwd's output.
* src/misc.c (normalize_filename_x): Rewrite so as not to resolve
/../, which can't be done reliably in the presence of symlinks.
Don't reject valid names such as ".".
(normalize_filename): Don't make it absolute; that way, we don't
have to invoke xgetcwd which might fail.  Don't bother to realloc
at the end, since that uses time and now saves little space.
(chdir_do): Don't crash if xgetcwd fails.
* tests/Makefile.am (TESTSUITE_AT): Add listed03.at.
* tests/listed03.at: New file.
* tests/testsuite.at: Include listed03.at.
This commit is contained in:
Paul R. Eggert
2010-07-15 11:24:39 -07:00
committed by Paul Eggert
parent ab6dd4948d
commit cc40c57a37
5 changed files with 92 additions and 91 deletions

View File

@@ -23,7 +23,6 @@
#include <quotearg.h> #include <quotearg.h>
#include <utimens.h> #include <utimens.h>
#include <errno.h> #include <errno.h>
#include <xgetcwd.h>
#include <priv-set.h> #include <priv-set.h>
#include "common.h" #include "common.h"
@@ -648,13 +647,11 @@ extract_dir (char *file_name, int typeflag)
if (one_file_system_option && root_device == 0) if (one_file_system_option && root_device == 0)
{ {
struct stat st; struct stat st;
char *dir = xgetcwd ();
if (deref_stat (true, dir, &st)) if (stat (".", &st) != 0)
stat_diag (dir); stat_diag (".");
else else
root_device = st.st_dev; root_device = st.st_dev;
free (dir);
} }
if (incremental_option) if (incremental_option)

View File

@@ -1,7 +1,7 @@
/* Miscellaneous functions, not really specific to GNU tar. /* Miscellaneous functions, not really specific to GNU tar.
Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001, Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc. 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the under the terms of the GNU General Public License as published by the
@@ -233,99 +233,54 @@ zap_slashes (char *name)
return name; return name;
} }
/* Normalize NAME by resolving any relative references and /* Normalize FILE_NAME by removing redundant slashes and "."
removing trailing slashes. Destructive version: modifies its argument. */ components, including redundant trailing slashes. Leave ".."
static int alone, as it may be significant in the presence of symlinks and on
normalize_filename_x (char *name) platforms where "/.." != "/". Destructive version: modifies its
argument. */
static void
normalize_filename_x (char *file_name)
{ {
char *p, *q; char *name = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
char *p;
char const *q;
char c;
p = name; /* Don't squeeze leading "//" to "/", on hosts where they're distinct. */
if (DOUBLE_SLASH_IS_DISTINCT_ROOT && ISSLASH (*p)) name += (DOUBLE_SLASH_IS_DISTINCT_ROOT
p++; && ISSLASH (*name) && ISSLASH (name[1]) && ! ISSLASH (name[2]));
/* Remove /./, resolve /../ and compress sequences of slashes */ /* Omit redundant leading "." components. */
for (q = p; *q; ) for (q = p = name; (*p = *q) == '.' && ISSLASH (q[1]); p += !*q)
for (q += 2; ISSLASH (*q); q++)
continue;
/* Copy components from Q to P, omitting redundant slashes and
internal "." components. */
while ((*p++ = c = *q++) != '\0')
if (ISSLASH (c))
while (ISSLASH (q[*q == '.']))
q += (*q == '.') + 1;
/* Omit redundant trailing "." component and slash. */
if (2 < p - name)
{ {
if (ISSLASH (*q)) p -= p[-2] == '.' && ISSLASH (p[-3]);
{ p -= 2 < p - name && ISSLASH (p[-2]);
*p++ = *q++; p[-1] = '\0';
while (ISSLASH (*q))
q++;
continue;
}
else if (p == name)
{
if (*q == '.')
{
if (ISSLASH (q[1]))
{
q += 2;
continue;
}
if (q[1] == '.' && ISSLASH (q[2]))
return 1;
}
}
else
{
if (*q == '.' && ISSLASH (p[-1]))
{
if (ISSLASH (q[1]))
{
q += 2;
while (ISSLASH (*q))
q++;
continue;
}
else if (q[1] == '.' && ISSLASH (q[2]))
{
do
{
--p;
}
while (p > name && !ISSLASH (p[-1]));
q += 3;
continue;
}
}
}
*p++ = *q++;
} }
/* Remove trailing slashes */
while (p - 1 > name && ISSLASH (p[-1]))
p--;
*p = 0;
return 0;
} }
/* Normalize NAME by resolving any relative references, removing trailing /* Normalize NAME by removing redundant slashes and "." components,
slashes, and converting it to absolute file name. Return the normalized including redundant trailing slashes. Return a normalized
name, or NULL in case of error. */ newly-allocated copy. */
char * char *
normalize_filename (const char *name) normalize_filename (const char *name)
{ {
char *copy; char *copy = xstrdup (name);
normalize_filename_x (copy);
if (name[0] != '/') return copy;
{
copy = xgetcwd ();
copy = xrealloc (copy, strlen (copy) + strlen (name) + 2);
strcat (copy, "/");
strcat (copy, name);
}
else
copy = xstrdup (name);
if (normalize_filename_x (copy))
{
free (copy);
return NULL;
}
return xrealloc (copy, strlen (copy) + 1);
} }
@@ -752,6 +707,8 @@ chdir_do (int i)
close (fd1); close (fd1);
prev->saved_cwd.desc = -1; prev->saved_cwd.desc = -1;
prev->saved_cwd.name = xgetcwd (); prev->saved_cwd.name = xgetcwd ();
if (! prev->saved_cwd.name)
err = errno;
} }
else else
err = errno; err = errno;

View File

@@ -1,6 +1,6 @@
# Makefile for GNU tar regression tests. # Makefile for GNU tar regression tests.
# Copyright (C) 1996, 1997, 1999, 2000, 2001, 2003, 2004, 2005, # Copyright (C) 1996, 1997, 1999, 2000, 2001, 2003, 2004, 2005,
# 2006, 2007, 2009 Free Software Foundation, Inc. # 2006, 2007, 2009 Free Software Foundation, Inc.
# François Pinard <pinard@iro.umontreal.ca>, 1988. # François Pinard <pinard@iro.umontreal.ca>, 1988.
@@ -98,6 +98,7 @@ TESTSUITE_AT = \
link03.at\ link03.at\
listed01.at\ listed01.at\
listed02.at\ listed02.at\
listed03.at\
long01.at\ long01.at\
longv7.at\ longv7.at\
lustar01.at\ lustar01.at\

45
tests/listed03.at Normal file
View File

@@ -0,0 +1,45 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
# Test suite for GNU tar.
# Copyright (C) 2010 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This checks for the --listed-incremental bug reported by J Chapman Flack at
# http://lists.gnu.org/archive/html/bug-tar/2010-06/msg00000.html
AT_SETUP([incremental dump when the parent directory is unreadable])
AT_KEYWORDS([listed incremental listed03])
AT_TAR_CHECK([
mkdir dir
mkdir dir/sub
mkdir dir/sub/a
genfile --file dir/sub/a/file
cd dir/sub
chmod a-r ..
tar -c -f archive.tar --listed-incremental=db.1 -v a
status=$?
chmod a+r ..
exit $status
],
[0],
[a/
a/file
],
[tar: a: Directory is new
],[],[],[gnu])
AT_CLEANUP

View File

@@ -41,13 +41,13 @@ $1)],$2,$3,$4,$5,$6)
m4_define([AT_TAR_WITH_HOOK],[ m4_define([AT_TAR_WITH_HOOK],[
m4_pushdef([AT_TAR_CHECK_HOOK],[$1]) m4_pushdef([AT_TAR_CHECK_HOOK],[$1])
$2 $2
m4_popdef([AT_TAR_CHECK_HOOK])]) m4_popdef([AT_TAR_CHECK_HOOK])])
m4_define([TAR_IGNREC_HOOK],[ m4_define([TAR_IGNREC_HOOK],[
AT_CHECK([grep -v '^.*tar: Record size = ' stderr; exit 0]) AT_CHECK([grep -v '^.*tar: Record size = ' stderr; exit 0])
]) ])
m4_define([RE_CHECK],[ m4_define([RE_CHECK],[
AT_DATA([$1.re],[$2]) AT_DATA([$1.re],[$2])
awk '{print NR " " $[]0}' $1 > $[]$.1 awk '{print NR " " $[]0}' $1 > $[]$.1
@@ -162,6 +162,7 @@ m4_include([incr01.at])
m4_include([incr02.at]) m4_include([incr02.at])
m4_include([listed01.at]) m4_include([listed01.at])
m4_include([listed02.at]) m4_include([listed02.at])
m4_include([listed03.at])
m4_include([incr03.at]) m4_include([incr03.at])
m4_include([incr04.at]) m4_include([incr04.at])
m4_include([incr05.at]) m4_include([incr05.at])