From: Sergey Poznyakoff Date: Wed, 17 Mar 2010 09:52:40 +0000 (+0200) Subject: Fix --remove-files. X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Ftar;a=commitdiff_plain;h=c1d3d134939d48e921b981f400808663f4e74001 Fix --remove-files. Tar --remove-files relied on canonicalize_file_name, which replaces symlinks in file name components with the directories they point to. Due to this, tar effectively ignored existence of symbolic links and was unable to remove a directory that contained any (Alexander Kozlov , 2010-03-15). * gnulib.modules: Remove canonicalize. * src/misc.c (normalize_filename): Rewrite from scratch. The function operates only on its input string, it makes no attempt to test components for existence or to resolve symbolic links. * tests/Makefile.am (TESTSUITE_AT): Add remfiles03.at. * tests/testsuite.at: Likewise. * tests/remfiles03.at: New test case. * NEWS: Update. --- diff --git a/NEWS b/NEWS index f53f8a9..a9be03c 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,16 @@ -GNU tar NEWS - User visible changes. 2010-03-11 +GNU tar NEWS - User visible changes. 2010-03-17 Please send GNU tar bug reports to -* --test-label behavior +* Bugfixes. + +** --remove-files + +Tar --remove-files failed to remove a directory which contained +symlinks to another files within that directory. + +** --test-label behavior In case of a mismatch, `tar --test-label LABEL' exits with code 1, not 2 as it did in previous versions. @@ -18,7 +25,7 @@ Several volume labels may be specified in a command line, e.g.: In this case, tar exits with code 0 if any one of the arguments matches the actual volume label. -* --label used with --update +** --label used with --update The `--label' option can be used with `--update' to prevent accidental update of an archive: diff --git a/gnulib.modules b/gnulib.modules index 7615677..12b788e 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -6,7 +6,6 @@ argmatch argp argp-version-etc backupfile -canonicalize closeout configmake dirname diff --git a/src/misc.c b/src/misc.c index f81111f..ff7e4b2 100644 --- a/src/misc.c +++ b/src/misc.c @@ -25,7 +25,6 @@ #include #include #include -#include #if HAVE_STROPTS_H # include @@ -34,6 +33,10 @@ # include #endif +#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT +# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0 +#endif + /* Handling strings. */ @@ -230,10 +233,99 @@ zap_slashes (char *name) return name; } +/* Normalize NAME by resolving any relative references and + removing trailing slashes. Destructive version: modifies its argument. */ +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; ) + { + 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++; + } + + /* 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. */ + char * normalize_filename (const char *name) { - return zap_slashes (canonicalize_filename_mode (name, CAN_MISSING)); + 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); } @@ -870,5 +962,3 @@ namebuf_name (namebuf_t buf, const char *name) return buf->buffer; } - - diff --git a/tests/Makefile.am b/tests/Makefile.am index f546f40..8f16244 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -121,6 +121,7 @@ TESTSUITE_AT = \ rename05.at\ remfiles01.at\ remfiles02.at\ + remfiles03.at\ same-order01.at\ same-order02.at\ shortfile.at\ diff --git a/tests/remfiles03.at b/tests/remfiles03.at new file mode 100644 index 0000000..02104e9 --- /dev/null +++ b/tests/remfiles03.at @@ -0,0 +1,45 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- + +# Test suite for GNU tar. +# Copyright (C) 2009 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# Description: Called with --remove-files, tar 1.23 failed +# to remove a directory if it contained symlinks to another files +# within that directory. +# Reported-by: Alexander Kozlov +# References: http://lists.gnu.org/archive/html/bug-tar/2010-03/msg00028.html +# + +AT_SETUP([remove-files with symbolic links]) +AT_KEYWORDS([create remove-files remfiles03]) + +AT_CHECK([ +mkdir a +mkdir a/b +ln -s b a/c || AT_SKIP_TEST +tar --remove-files -cf a.tar a +genfile --stat a +], +[0], +[], +[genfile: stat(a) failed: No such file or directory +]) + +AT_CLEANUP + + diff --git a/tests/testsuite.at b/tests/testsuite.at index fc96479..3e75ed8 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -229,6 +229,7 @@ m4_include([grow.at]) m4_include([remfiles01.at]) m4_include([remfiles02.at]) +m4_include([remfiles03.at]) m4_include([star/gtarfail.at]) m4_include([star/gtarfail2.at])