From b41b004638faa8b446fe37bb8b178a6be496c666 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 25 Sep 2013 15:58:43 +0300 Subject: [PATCH] Improve tar_getcwd * src/common.h (tar_getcwd): Return pointer is const. * src/misc.c (wd) : New member. (chdir_arg): Initialize cwd. (tar_getcwd): Use cwd member to cache the result. Take into account absolute pathnames, (normalize_filename): Don't free the value returned from tar_getcwd. * src/names.c (name_next_elt): Remove leftover call chdir(). * tests/Makefile.am: Add new tests. * tests/testsuite.at: Likewise. * tests/incr08.at: New testcase. * tests/remfiles04.at: New testcase. * tests/remfiles05.at: New testcase. * tests/remfiles06.at: New testcase. * tests/remfiles07.at: New testcase. --- src/common.h | 2 +- src/misc.c | 57 ++++++++++++++++++++---------- src/names.c | 6 ++-- tests/Makefile.am | 5 +++ tests/incr08.at | 86 +++++++++++++++++++++++++++++++++++++++++++++ tests/remfiles04.at | 53 ++++++++++++++++++++++++++++ tests/remfiles05.at | 60 +++++++++++++++++++++++++++++++ tests/remfiles06.at | 65 ++++++++++++++++++++++++++++++++++ tests/remfiles07.at | 63 +++++++++++++++++++++++++++++++++ tests/testsuite.at | 5 +++ 10 files changed, 378 insertions(+), 24 deletions(-) create mode 100644 tests/incr08.at create mode 100644 tests/remfiles04.at create mode 100644 tests/remfiles05.at create mode 100644 tests/remfiles06.at create mode 100644 tests/remfiles07.at diff --git a/src/common.h b/src/common.h index ac96f26..2a6d7d4 100644 --- a/src/common.h +++ b/src/common.h @@ -608,7 +608,7 @@ char *namebuf_name (namebuf_t buf, const char *name); void namebuf_add_dir (namebuf_t buf, const char *name); char *namebuf_finish (namebuf_t buf); -char *tar_getcwd (void); +const char *tar_getcwd (void); /* Represent N using a signed integer I such that (uintmax_t) I == N. With a good optimizing compiler, this is equivalent to (intmax_t) i diff --git a/src/misc.c b/src/misc.c index b8ef6ea..bd396a3 100644 --- a/src/misc.c +++ b/src/misc.c @@ -283,21 +283,20 @@ normalize_filename (const char *name) getcwd is slow, it might fail, and it does not necessarily return a canonical name even when it succeeds. Perhaps we can use dev+ino pairs instead of names? */ - copy = tar_getcwd (); - if (copy) - { - size_t copylen = strlen (copy); - bool need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT - && copylen == 2 && ISSLASH (copy[1])); - copy = xrealloc (copy, copylen + need_separator + strlen (name) + 1); - copy[copylen] = DIRECTORY_SEPARATOR; - strcpy (copy + copylen + need_separator, name); - } - else - WARN ((0, errno, _("Cannot get working directory"))); + const char *cwd = tar_getcwd (); + size_t copylen; + bool need_separator; + + copylen = strlen (cwd); + need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT + && copylen == 2 && ISSLASH (cwd[1])); + copy = xmalloc (copylen + need_separator + strlen (name) + 1); + strcpy (copy, cwd); + copy[copylen] = DIRECTORY_SEPARATOR; + strcpy (copy + copylen + need_separator, name); } - if (! copy) + if (!copy) copy = xstrdup (name); normalize_filename_x (copy); return copy; @@ -831,7 +830,8 @@ struct wd { /* The directory's name. */ char const *name; - + /* Current working directory; initialized by tar_getcwd */ + char *cwd; /* If nonzero, the file descriptor of the directory, or AT_FDCWD if the working directory. If zero, the directory needs to be opened to be used. */ @@ -886,6 +886,7 @@ chdir_arg (char const *dir) if (! wd_count) { wd[wd_count].name = "."; + wd[wd_count].cwd = NULL; wd[wd_count].fd = AT_FDCWD; wd_count++; } @@ -903,6 +904,7 @@ chdir_arg (char const *dir) } wd[wd_count].name = dir; + wd[wd_count].cwd = NULL; wd[wd_count].fd = 0; return wd_count++; } @@ -976,7 +978,7 @@ chdir_do (int i) } } -char * +const char * tar_getcwd (void) { static char *cwd; @@ -985,10 +987,27 @@ tar_getcwd (void) if (!cwd) cwd = xgetcwd (); - nbuf = namebuf_create (cwd); - for (i = 1; i <= chdir_current; i++) - namebuf_add_dir (nbuf, wd[i].name); - return namebuf_finish (nbuf); + if (!wd) + return cwd; + + if (0 == chdir_current || !wd[chdir_current].cwd) + { + if (IS_ABSOLUTE_FILE_NAME (wd[chdir_current].name)) + return wd[chdir_current].name; + + if (!wd[0].cwd) + wd[0].cwd = cwd; + + for (i = chdir_current - 1; i > 0; i--) + if (wd[i].cwd) + break; + + nbuf = namebuf_create (wd[i].cwd); + for (i++; i <= chdir_current; i++) + namebuf_add_dir (nbuf, wd[i].name); + wd[chdir_current].cwd = namebuf_finish (nbuf); + } + return wd[chdir_current].cwd; } void diff --git a/src/names.c b/src/names.c index 85049b1..8b32de1 100644 --- a/src/names.c +++ b/src/names.c @@ -578,13 +578,11 @@ name_next_elt (int change_dirs) case NELT_CHDIR: if (change_dirs) { - copy_name (ep); - if (chdir (name_buffer) < 0) - chdir_fatal (name_buffer); + chdir_do (chdir_arg (xstrdup (ep->v.name))); name_list_advance (); break; } - /* fall trhough */ + /* fall through */ case NELT_NAME: copy_name (ep); if (unquote_option) diff --git a/tests/Makefile.am b/tests/Makefile.am index cfab3e0..4823b60 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -112,6 +112,7 @@ TESTSUITE_AT = \ incr05.at\ incr06.at\ incr07.at\ + incr08.at\ indexfile.at\ ignfail.at\ label01.at\ @@ -161,6 +162,10 @@ TESTSUITE_AT = \ remfiles01.at\ remfiles02.at\ remfiles03.at\ + remfiles04.at\ + remfiles05.at\ + remfiles06.at\ + remfiles07.at\ same-order01.at\ same-order02.at\ shortfile.at\ diff --git a/tests/incr08.at b/tests/incr08.at new file mode 100644 index 0000000..5210d28 --- /dev/null +++ b/tests/incr08.at @@ -0,0 +1,86 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# Test suite for GNU tar. +# Copyright 2013 Free Software Foundation, Inc. +# +# GNU tar 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 of the License, or +# (at your option) any later version. +# +# GNU tar 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 . + +# Description: In tar 1.26 listed-incremental with -C and absolute path +# would malfunction under certain conditions due to buggy filename +# normalization. +# +# The value returned by normalize_filename() is used to populate the "caname" +# field in both the "directory" structure in incremen.c and the "name" +# structure in names.c, and in both cases that field is then used in the +# "hash" and "compare" functions for the related hash tables. Thus, the +# fact that the returned value doesn't reflect the operation of previous +# "-C" options means that it's possible for two different directories to +# be given the same "caname" value in the hashed structure and thus end up +# being confused with each other. +# +# The bug is triggered when dumping both relative paths after -C and +# absolute paths that match the process' current working directory. +# +# Reported by: Nathan Stratton Treadway +# References: <20130922192135.GJ32256@shire.ontko.com>, +# http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00034.html + +AT_SETUP([filename normalization]) +AT_KEYWORDS([incremental create incr08]) + +AT_TAR_CHECK([ +AT_SORT_PREREQ +mkdir tartest +cd tartest +mkdir foo +mkdir foo/subdir +mkdir foo/subdir/dir1 +mkdir subdir +mkdir subdir/dir2 +decho A +find|sort + +decho B +DIR=`pwd` +tar -cvf ../foo.tar --listed-incremental=../foo.snar -C foo . $DIR 2>../err |\ + sed "s|$DIR|ABSPATH|" +sed "s|$DIR|ABSPATH|" ../err >&2 +], +[0], +[A +. +./foo +./foo/subdir +./foo/subdir/dir1 +./subdir +./subdir/dir2 +B +./ +./subdir/ +./subdir/dir1/ +ABSPATH/ +ABSPATH/subdir/ +ABSPATH/subdir/dir2/ +], +[A +B +tar: .: Directory is new +tar: ./subdir: Directory is new +tar: ./subdir/dir1: Directory is new +tar: ABSPATH: Directory is new +tar: ABSPATH/subdir: Directory is new +tar: ABSPATH/subdir/dir2: Directory is new +tar: Removing leading `/' from member names +],[],[],[gnu]) + +AT_CLEANUP diff --git a/tests/remfiles04.at b/tests/remfiles04.at new file mode 100644 index 0000000..04df45b --- /dev/null +++ b/tests/remfiles04.at @@ -0,0 +1,53 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# Test suite for GNU tar. +# Copyright 2013 Free Software Foundation, Inc. +# +# GNU tar 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 of the License, or +# (at your option) any later version. +# +# GNU tar 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 . + +# Description: Tar 1.26 would remove wrong files when called with +# --remove-files and -C +# Reported by: Jörgen Strand +# References: <9FC79E5CB90CEC47B9647DCAB7BD327A01AD83B452EE@seldmbx02.corpusers.net> +# http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00024.html + +AT_SETUP([remove-files with -C]) +AT_KEYWORDS([create remove-files remfiles04]) + +AT_TAR_CHECK([ +AT_SORT_PREREQ +mkdir foo +echo bar > bar +echo foobar > foo/bar +tar -cf foo.tar --remove-files -C foo bar +echo A +find . | sort +echo foobar > foo/bar +tar -rf foo.tar --remove-files -C foo bar +echo B +find . | sort +], +[0], +[A +. +./bar +./foo +./foo.tar +B +. +./bar +./foo +./foo.tar +],[],[],[],[gnu]) + +AT_CLEANUP diff --git a/tests/remfiles05.at b/tests/remfiles05.at new file mode 100644 index 0000000..04425a7 --- /dev/null +++ b/tests/remfiles05.at @@ -0,0 +1,60 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# Test suite for GNU tar. +# Copyright 2013 Free Software Foundation, Inc. +# +# GNU tar 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 of the License, or +# (at your option) any later version. +# +# GNU tar 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 . + +# Description: Tar 1.26 would remove wrong files when invoked with +# --listed-incremental and -C +# Reported by: Nathan Stratton Treadway +# References: <20130921171234.GG32256@shire.ontko.com>, +# http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00028.html + +AT_SETUP([incremental and -C]) +AT_KEYWORDS([incremental create remove-files remfiles05]) + +AT_TAR_CHECK([ +AT_SORT_PREREQ +mkdir foo +echo bar > bar +echo foo/bar > foo/bar +decho A +find . | sort + +decho B +tar -cvf foo.tar --listed-incremental=foo.snar --remove-files -C foo bar +decho C +find . | sort +], +[0], +[A +. +./bar +./foo +./foo/bar +B +bar +C +. +./bar +./foo +./foo.snar +./foo.tar +], +[A +B +C +],[],[],[gnu]) + +AT_CLEANUP diff --git a/tests/remfiles06.at b/tests/remfiles06.at new file mode 100644 index 0000000..0fa8c15 --- /dev/null +++ b/tests/remfiles06.at @@ -0,0 +1,65 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# Test suite for GNU tar. +# Copyright 2013 Free Software Foundation, Inc. +# +# GNU tar 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 of the License, or +# (at your option) any later version. +# +# GNU tar 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 . + +# Description: There was a leftover call to chdir in name_next_elt() in +# tar 1.26. After commit e3d28d84 this call would confuse the tar_getcwd +# function. +# Reported by: Nathan Stratton Treadway +# References: <20130924145657.GM32256@shire.ontko.com>, +# http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00045.html + +AT_SETUP([incremental with two -C]) +AT_KEYWORDS([incremental create remove-files remfiles06]) + +AT_TAR_CHECK([ +AT_SORT_PREREQ +mkdir tartest +cd tartest +mkdir foo +echo foo/file > foo/file +mkdir bar +echo bar/file > bar/file +decho A +find|sort + +decho B +tar -cvf ../foo.tar --remove-files -C foo file -C ../bar file + +decho C +find|sort +], +[0], +[A +. +./bar +./bar/file +./foo +./foo/file +B +file +file +C +. +./bar +./foo +], +[A +B +C +],[],[],[gnu]) + +AT_CLEANUP diff --git a/tests/remfiles07.at b/tests/remfiles07.at new file mode 100644 index 0000000..84ab625 --- /dev/null +++ b/tests/remfiles07.at @@ -0,0 +1,63 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# Test suite for GNU tar. +# Copyright 2013 Free Software Foundation, Inc. +# +# GNU tar 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 of the License, or +# (at your option) any later version. +# +# GNU tar 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 . + +# Description: See remfiles06.at +# Reported by: Nathan Stratton Treadway +# References: <20130924185129.GO32256@shire.ontko.com> + +AT_SETUP([incremental with -C to absolute path]) +AT_KEYWORDS([incremental create remove-files remfiles07]) + +AT_TAR_CHECK([ +AT_SORT_PREREQ +mkdir tartest +cd tartest +mkdir foo +echo foo/file > foo/file +mkdir bar +echo bar/file > bar/file +decho A +find|sort + +DIR=`pwd` +decho B +tar -cvf ../foo.tar --remove-files -C foo file -C $DIR/bar file + +decho C +find|sort +], +[0], +[A +. +./bar +./bar/file +./foo +./foo/file +B +file +file +C +. +./bar +./foo +], +[A +B +C +],[],[],[gnu]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 8aeebfe..0192ac4 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -297,6 +297,7 @@ m4_include([incr04.at]) m4_include([incr05.at]) m4_include([incr06.at]) m4_include([incr07.at]) +m4_include([incr08.at]) AT_BANNER([Files removed while archiving]) m4_include([filerem01.at]) @@ -377,6 +378,10 @@ AT_BANNER([Removing files after archiving]) m4_include([remfiles01.at]) m4_include([remfiles02.at]) m4_include([remfiles03.at]) +m4_include([remfiles04.at]) +m4_include([remfiles05.at]) +m4_include([remfiles06.at]) +m4_include([remfiles07.at]) AT_BANNER([Extended attributes]) m4_include([xattr01.at]) -- 2.43.0