From cc40c57a37b1e443a5088fdda0fbb3687b1f4773 Mon Sep 17 00:00:00 2001 From: "Paul R. Eggert" Date: Thu, 15 Jul 2010 11:24:39 -0700 Subject: [PATCH] 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. --- src/extract.c | 7 +-- src/misc.c | 129 +++++++++++++++------------------------------ tests/Makefile.am | 3 +- tests/listed03.at | 45 ++++++++++++++++ tests/testsuite.at | 5 +- 5 files changed, 95 insertions(+), 94 deletions(-) create mode 100644 tests/listed03.at diff --git a/src/extract.c b/src/extract.c index 7ce9ce8..9897e82 100644 --- a/src/extract.c +++ b/src/extract.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include "common.h" @@ -648,13 +647,11 @@ extract_dir (char *file_name, int typeflag) if (one_file_system_option && root_device == 0) { struct stat st; - char *dir = xgetcwd (); - if (deref_stat (true, dir, &st)) - stat_diag (dir); + if (stat (".", &st) != 0) + stat_diag ("."); else root_device = st.st_dev; - free (dir); } if (incremental_option) diff --git a/src/misc.c b/src/misc.c index 3e7941d..12b40ac 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1,7 +1,7 @@ /* Miscellaneous functions, not really specific to GNU tar. 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 under the terms of the GNU General Public License as published by the @@ -233,99 +233,54 @@ zap_slashes (char *name) return name; } -/* Normalize NAME by resolving any relative references and - removing trailing slashes. Destructive version: modifies its argument. */ -static int -normalize_filename_x (char *name) -{ - char *p, *q; - - p = name; - if (DOUBLE_SLASH_IS_DISTINCT_ROOT && ISSLASH (*p)) - p++; - - /* Remove /./, resolve /../ and compress sequences of slashes */ - for (q = p; *q; ) +/* Normalize FILE_NAME by removing redundant slashes and "." + components, including redundant trailing slashes. Leave ".." + alone, as it may be significant in the presence of symlinks and on + platforms where "/.." != "/". Destructive version: modifies its + argument. */ +static void +normalize_filename_x (char *file_name) +{ + char *name = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); + char *p; + char const *q; + char c; + + /* Don't squeeze leading "//" to "/", on hosts where they're distinct. */ + name += (DOUBLE_SLASH_IS_DISTINCT_ROOT + && ISSLASH (*name) && ISSLASH (name[1]) && ! ISSLASH (name[2])); + + /* Omit redundant leading "." components. */ + 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++ = *q++; - 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++; + p -= p[-2] == '.' && ISSLASH (p[-3]); + p -= 2 < p - name && ISSLASH (p[-2]); + p[-1] = '\0'; } - - /* Remove trailing slashes */ - while (p - 1 > name && ISSLASH (p[-1])) - p--; - - *p = 0; - return 0; } -/* Normalize NAME by resolving any relative references, removing trailing - slashes, and converting it to absolute file name. Return the normalized - name, or NULL in case of error. */ +/* Normalize NAME by removing redundant slashes and "." components, + including redundant trailing slashes. Return a normalized + newly-allocated copy. */ char * normalize_filename (const char *name) { - char *copy; - - if (name[0] != '/') - { - 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); + char *copy = xstrdup (name); + normalize_filename_x (copy); + return copy; } @@ -752,6 +707,8 @@ chdir_do (int i) close (fd1); prev->saved_cwd.desc = -1; prev->saved_cwd.name = xgetcwd (); + if (! prev->saved_cwd.name) + err = errno; } else err = errno; diff --git a/tests/Makefile.am b/tests/Makefile.am index f5bc4ef..5232adb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,6 @@ # 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. # François Pinard , 1988. @@ -98,6 +98,7 @@ TESTSUITE_AT = \ link03.at\ listed01.at\ listed02.at\ + listed03.at\ long01.at\ longv7.at\ lustar01.at\ diff --git a/tests/listed03.at b/tests/listed03.at new file mode 100644 index 0000000..5bf8e58 --- /dev/null +++ b/tests/listed03.at @@ -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 . + +# 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 diff --git a/tests/testsuite.at b/tests/testsuite.at index 37c1907..6edcf4e 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -41,13 +41,13 @@ $1)],$2,$3,$4,$5,$6) m4_define([AT_TAR_WITH_HOOK],[ m4_pushdef([AT_TAR_CHECK_HOOK],[$1]) $2 - + m4_popdef([AT_TAR_CHECK_HOOK])]) m4_define([TAR_IGNREC_HOOK],[ AT_CHECK([grep -v '^.*tar: Record size = ' stderr; exit 0]) ]) - + m4_define([RE_CHECK],[ AT_DATA([$1.re],[$2]) awk '{print NR " " $[]0}' $1 > $[]$.1 @@ -162,6 +162,7 @@ m4_include([incr01.at]) m4_include([incr02.at]) m4_include([listed01.at]) m4_include([listed02.at]) +m4_include([listed03.at]) m4_include([incr03.at]) m4_include([incr04.at]) m4_include([incr05.at]) -- 2.44.0