From 4bf4efe97d25784eb5e56c8ee337af3c7866ec34 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Tue, 24 Sep 2013 14:01:13 +0300
Subject: [PATCH 01/11] Fix normalize_filename.

The function did not take into account eventual -C options, which
in particular led to various problems when using -C and --remove-files
together.

* src/common.h (namebuf_add_dir,namebuf_finish)
(tar_getcwd): New prototypes.
* src/misc.c (namebuf_add_dir,namebuf_finish)
(tar_getcwd): New functions.
(normalize_filename): Use tar_getcwd.
---
 src/common.h |  4 ++++
 src/misc.c   | 41 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/src/common.h b/src/common.h
index 16ba401..85a6977 100644
--- a/src/common.h
+++ b/src/common.h
@@ -603,6 +603,10 @@ typedef struct namebuf *namebuf_t;
 namebuf_t namebuf_create (const char *dir);
 void namebuf_free (namebuf_t buf);
 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);

 void code_ns_fraction (int ns, char *p);
 char const *code_timespec (struct timespec ts, char *sbuf);
diff --git a/src/misc.c b/src/misc.c
index b75f2ab..f45f79a 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -283,7 +283,7 @@ 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 = xgetcwd ();
+      copy = tar_getcwd ();
       if (copy)
         {
           size_t copylen = strlen (copy);
@@ -777,6 +777,21 @@ chdir_do (int i)
     }
 }
 
+char *
+tar_getcwd (void)
+{
+  static char *cwd;
+  namebuf_t nbuf;
+  int i;
+
+  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);
+}
+
 void
 close_diag (char const *name)
 {
@@ -945,3 +960,27 @@ namebuf_name (namebuf_t buf, const char *name)

   return ret;
 }
+
+void
+namebuf_add_dir (namebuf_t buf, const char *name)
+{
+  static char dirsep[] = { DIRECTORY_SEPARATOR, 0 };
+  if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
+    {
+      namebuf_name (buf, dirsep);
+      buf->dir_length++;
+    }
+  namebuf_name (buf, name);
+  buf->dir_length += strlen (name);
+}
+
+char *
+namebuf_finish (namebuf_t buf)
+{
+  char *res = buf->buffer;
+
+  if (ISSLASH (buf->buffer[buf->dir_length - 1]))
+    buf->buffer[buf->dir_length] = 0;
+  free (buf);
+  return res;
+}
--
2.9.3


From 272e1c879644b3684031acd62c9adb0adc5133b5 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Wed, 25 Sep 2013 15:58:43 +0300
Subject: [PATCH 02/11] Improve tar_getcwd

* src/common.h (tar_getcwd): Return pointer is const.
* src/misc.c (wd) <cwd>: 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         |  3 +-
 tests/Makefile.am   |  5 ++++
 tests/incr08.at     | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/remfiles04.at | 53 +++++++++++++++++++++++++++++++++
 tests/remfiles05.at | 60 +++++++++++++++++++++++++++++++++++++
 tests/remfiles06.at | 66 ++++++++++++++++++++++++++++++++++++++++
 tests/remfiles07.at | 63 +++++++++++++++++++++++++++++++++++++++
 tests/testsuite.at  |  5 ++++
 10 files changed, 378 insertions(+), 22 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 85a6977..99f8552 100644
--- a/src/common.h
+++ b/src/common.h
@@ -606,7 +606,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);

 void code_ns_fraction (int ns, char *p);
 char const *code_timespec (struct timespec ts, char *sbuf);
diff --git a/src/misc.c b/src/misc.c
index f45f79a..2fd5280 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;
@@ -632,7 +631,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.  */
@@ -687,6 +687,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++;
	}
@@ -704,6 +705,7 @@ chdir_arg (char const *dir)
     }

   wd[wd_count].name = dir;
+  wd[wd_count].cwd = NULL;
   wd[wd_count].fd = 0;
   return wd_count++;
 }
@@ -777,7 +779,7 @@ chdir_do (int i)
     }
 }
 
-char *
+const char *
 tar_getcwd (void)
 {
   static char *cwd;
@@ -786,10 +788,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 3911f8c..8c3052f 100644
--- a/src/names.c
+++ b/src/names.c
@@ -351,8 +351,7 @@ name_next_elt (int change_dirs)

       if (change_dirs && ep->type == NELT_CHDIR)
	{
-	  if (chdir (name_buffer) < 0)
-	    chdir_fatal (name_buffer);
+	  chdir_do (chdir_arg (xstrdup (ep->v.name)));
	}
       else
	{
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 228e936..1d10360 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -99,6 +99,7 @@ TESTSUITE_AT = \
  incr05.at\
  incr06.at\
  incr07.at\
+ incr08.at\
  indexfile.at\
  ignfail.at\
  label01.at\
@@ -139,6 +140,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 <http://www.gnu.org/licenses/>.
+
+# 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 <nathanst@ontko.com>
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: Tar 1.26 would remove wrong files when called with
+# --remove-files and -C
+# Reported by: Jörgen Strand <Jorgen.Strand@sonymobile.com>
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: Tar 1.26 would remove wrong files when invoked with
+# --listed-incremental and -C
+# Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
+# 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..75ddcfa
--- /dev/null
+++ b/tests/remfiles06.at
@@ -0,0 +1,66 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# 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 <nathanst@ontko.com>
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: See remfiles06.at
+# Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
+# 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 10cf26a..5c805e7 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -260,6 +260,7 @@ m4_include([incr03.at])
 m4_include([incr05.at])
 m4_include([incr06.at])
 m4_include([incr07.at])
+m4_include([incr08.at])

 m4_include([filerem01.at])
 m4_include([filerem02.at])
@@ -330,6 +331,10 @@ m4_include([grow.at])
 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])

 m4_include([sigpipe.at])

--
2.9.3


From 0c5f95ca80d507a00825c8e3fd05ed5ad993ce17 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Thu, 26 Sep 2013 15:41:47 +0300
Subject: [PATCH 03/11] Use relative addressing in deferred unlinks.

* src/common.h (tar_dirname): New function.
* src/misc.c (normalize_filename_x): Make extern.
(tar_dirname): New function.
(tar_getcwd): Take into account absoulte pathnames.
* src/unlink.c (deferred_unlink) <dir_idx>: New member; keeps the
value of chdir_current at the moment of structure allocation.
(flush_deferred_unlinks): Use chdir_do and relative addressing.
(queue_deferred_unlink): Initialize dir_idx.
* tests/Makefile.am: Add new tests.
* tests/testsuite.at: Add new tests.
* tests/remfiles06.at: Fix description.
* tests/remfiles07.at: Fix description.
* tests/remfiles08.at: New test case.
---
 src/common.h        |  2 ++
 src/misc.c          | 28 +++++++++++++++++++---------
 src/unlink.c        | 27 +++++++++++++++++++++++----
 tests/Makefile.am   |  1 +
 tests/remfiles06.at |  4 ++--
 tests/remfiles07.at |  4 ++--
 tests/remfiles08.at | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/testsuite.at  |  1 +
 8 files changed, 98 insertions(+), 17 deletions(-)
 create mode 100644 tests/remfiles08.at

diff --git a/src/common.h b/src/common.h
index 99f8552..7d64227 100644
--- a/src/common.h
+++ b/src/common.h
@@ -596,6 +596,7 @@ void assign_string (char **dest, const char *src);
 int unquote_string (char *str);
 char *zap_slashes (char *name);
 char *normalize_filename (const char *name);
+void normalize_filename_x (char *name);
 void replace_prefix (char **pname, const char *samp, size_t slen,
		     const char *repl, size_t rlen);
 char *tar_savedir (const char *name, int must_exist);
@@ -607,6 +608,7 @@ void namebuf_add_dir (namebuf_t buf, const char *name);
 char *namebuf_finish (namebuf_t buf);

 const char *tar_getcwd (void);
+const char *tar_dirname (void);

 void code_ns_fraction (int ns, char *p);
 char const *code_timespec (struct timespec ts, char *sbuf);
diff --git a/src/misc.c b/src/misc.c
index 2fd5280..c7d51b2 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -229,11 +229,12 @@ zap_slashes (char *name)
 }

 /* 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
+   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. */
+void
 normalize_filename_x (char *file_name)
 {
   char *name = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
@@ -267,8 +268,9 @@ normalize_filename_x (char *file_name)
 }

 /* Normalize NAME by removing redundant slashes and "." components,
-   including redundant trailing slashes.  Return a normalized
-   newly-allocated copy.  */
+   including redundant trailing slashes.
+
+   Return a normalized newly-allocated copy.  */

 char *
 normalize_filename (const char *name)
@@ -780,6 +782,12 @@ chdir_do (int i)
 }
 
 const char *
+tar_dirname (void)
+{
+  return wd[chdir_current].name;
+}
+
+const char *
 tar_getcwd (void)
 {
   static char *cwd;
@@ -794,8 +802,10 @@ tar_getcwd (void)
   if (0 == chdir_current || !wd[chdir_current].cwd)
     {
       if (IS_ABSOLUTE_FILE_NAME (wd[chdir_current].name))
-	return wd[chdir_current].name;
-
+	{
+	  wd[chdir_current].cwd = xstrdup (wd[chdir_current].name);
+	  return wd[chdir_current].cwd;
+	}
       if (!wd[0].cwd)
	wd[0].cwd = cwd;

diff --git a/src/unlink.c b/src/unlink.c
index b281636..10e0b41 100644
--- a/src/unlink.c
+++ b/src/unlink.c
@@ -22,7 +22,9 @@
 struct deferred_unlink
   {
     struct deferred_unlink *next;   /* Next unlink in the queue */
-    char *file_name;                /* Absolute name of the file to unlink */
+    int dir_idx;                    /* Directory index in wd */
+    char *file_name;                /* Name of the file to unlink, relative
+				       to dir_idx */
     bool is_dir;                    /* True if file_name is a directory */
     off_t records_written;          /* Number of records written when this
				       entry got added to the queue */
@@ -68,16 +70,30 @@ static void
 flush_deferred_unlinks (bool force)
 {
   struct deferred_unlink *p, *prev = NULL;
+  int saved_chdir = chdir_current;

   for (p = dunlink_head; p; )
     {
       struct deferred_unlink *next = p->next;
+
       if (force
	  || records_written > p->records_written + deferred_unlink_delay)
	{
+          chdir_do (p->dir_idx);
	  if (p->is_dir)
	    {
-	      if (unlinkat (chdir_fd, p->file_name, AT_REMOVEDIR) != 0)
+	      const char *fname;
+
+	      if (p->file_name[0] == 0 ||
+		  strcmp (p->file_name, ".") == 0)
+		{
+		  fname = tar_dirname ();
+		  chdir_do (p->dir_idx - 1);
+		}
+	      else
+		fname = p->file_name;
+
+	      if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
		{
		  switch (errno)
		    {
@@ -95,7 +111,7 @@ flush_deferred_unlinks (bool force)
			}
		      /* fall through */
		    default:
-		      rmdir_error (p->file_name);
+		      rmdir_error (fname);
		    }
		}
	    }
@@ -120,6 +136,7 @@ flush_deferred_unlinks (bool force)
     }
   if (!dunlink_head)
     dunlink_tail = NULL;
+  chdir_do (saved_chdir);
 }

 void
@@ -145,7 +162,9 @@ queue_deferred_unlink (const char *name, bool is_dir)

   p = dunlink_alloc ();
   p->next = NULL;
-  p->file_name = normalize_filename (name);
+  p->dir_idx = chdir_current;
+  p->file_name = xstrdup (name);
+  normalize_filename_x (p->file_name);
   p->is_dir = is_dir;
   p->records_written = records_written;

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1d10360..29ebab1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -144,6 +144,7 @@ TESTSUITE_AT = \
  remfiles05.at\
  remfiles06.at\
  remfiles07.at\
+ remfiles08.at\
  same-order01.at\
  same-order02.at\
  shortfile.at\
diff --git a/tests/remfiles06.at b/tests/remfiles06.at
index 75ddcfa..c2d9876 100644
--- a/tests/remfiles06.at
+++ b/tests/remfiles06.at
@@ -22,8 +22,8 @@
 # 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_SETUP([remove with two -C])
+AT_KEYWORDS([remove-files remfiles06])

 AT_TAR_CHECK([
 AT_SORT_PREREQ
diff --git a/tests/remfiles07.at b/tests/remfiles07.at
index 84ab625..742e0a1 100644
--- a/tests/remfiles07.at
+++ b/tests/remfiles07.at
@@ -19,8 +19,8 @@
 # Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
 # References: <20130924185129.GO32256@shire.ontko.com>

-AT_SETUP([incremental with -C to absolute path])
-AT_KEYWORDS([incremental create remove-files remfiles07])
+AT_SETUP([remove with -C to absolute path])
+AT_KEYWORDS([create remove-files remfiles07])

 AT_TAR_CHECK([
 AT_SORT_PREREQ
diff --git a/tests/remfiles08.at b/tests/remfiles08.at
new file mode 100644
index 0000000..54f5de1
--- /dev/null
+++ b/tests/remfiles08.at
@@ -0,0 +1,48 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: See remfiles06.at
+# Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
+# References: <20130926050634.GW32256@shire.ontko.com>
+
+AT_SETUP([remove with -C to absolute and relative paths])
+AT_KEYWORDS([incremental create remove-files remfiles08])
+
+AT_TAR_CHECK([
+mkdir foo
+mkdir bar
+echo foo/foo_file > foo/foo_file
+echo bar/bar_file > bar/bar_file
+decho A
+tar -cvf foo.tar --remove-files -C `pwd`/foo . -C ../bar .
+decho B
+],
+[0],
+[A
+./
+./foo_file
+./
+./bar_file
+B
+.
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 5c805e7..d468dcf 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -335,6 +335,7 @@ m4_include([remfiles04.at])
 m4_include([remfiles05.at])
 m4_include([remfiles06.at])
 m4_include([remfiles07.at])
+m4_include([remfiles08.at])

 m4_include([sigpipe.at])

--
2.9.3


From 195c6f2b71f49ecc374ae01e20d7287f24501178 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Fri, 27 Sep 2013 00:59:18 +0300
Subject: [PATCH 04/11] Bugfix

* tests/remfiles08.at: Restore missing find
---
 tests/remfiles08.at | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/remfiles08.at b/tests/remfiles08.at
index 54f5de1..0649e85 100644
--- a/tests/remfiles08.at
+++ b/tests/remfiles08.at
@@ -30,6 +30,7 @@ echo bar/bar_file > bar/bar_file
 decho A
 tar -cvf foo.tar --remove-files -C `pwd`/foo . -C ../bar .
 decho B
+find
 ],
 [0],
 [A
--
2.9.3


From 2cce74ec554ec7fca4c3b1d2963beb6a729881fe Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Tue, 1 Oct 2013 21:48:30 +0300
Subject: [PATCH 05/11] Revamp tar_getcwd/normalize_filename stuff.

The changes are based on the discussion with Nathan.

* src/common.h (normalize_filename): Take two arguments. All
callers updated.
(tar_getcwd): Replaced with ..
(tar_getcdpath): New proto.
* src/misc.c (normalize_filename): Take two arguments.
(chdir_arg): Populate cwd along with creating the
structure.
(tar_getcwd): Removed.
(tar_getcdpath): New function.

* tests/incr09.at: New test case.
* tests/Makefile.am: Add new tests.
* tests/testsuite.at: Likewise.
---
 src/common.h       |  4 ++--
 src/incremen.c     |  4 ++--
 src/misc.c         | 48 ++++++++++++++++----------------------------
 src/names.c        |  6 ++----
 tests/Makefile.am  |  1 +
 tests/incr09.at    | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/testsuite.at |  1 +
 7 files changed, 84 insertions(+), 39 deletions(-)
 create mode 100644 tests/incr09.at

diff --git a/src/common.h b/src/common.h
index 7d64227..16b501b 100644
--- a/src/common.h
+++ b/src/common.h
@@ -595,7 +595,7 @@ void skip_member (void);
 void assign_string (char **dest, const char *src);
 int unquote_string (char *str);
 char *zap_slashes (char *name);
-char *normalize_filename (const char *name);
+char *normalize_filename (int cdidx, const char *name);
 void normalize_filename_x (char *name);
 void replace_prefix (char **pname, const char *samp, size_t slen,
		     const char *repl, size_t rlen);
@@ -607,7 +607,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);

-const char *tar_getcwd (void);
+const char *tar_getcdpath (int);
 const char *tar_dirname (void);

 void code_ns_fraction (int ns, char *p);
diff --git a/src/incremen.c b/src/incremen.c
index b2ab5bf..cb12bbc 100644
--- a/src/incremen.c
+++ b/src/incremen.c
@@ -279,7 +279,7 @@ free_directory (struct directory *dir)
 static struct directory *
 attach_directory (const char *name)
 {
-  char *cname = normalize_filename (name);
+  char *cname = normalize_filename (chdir_current, name);
   struct directory *dir = make_directory (name, cname);
   if (dirtail)
     dirtail->next = dir;
@@ -350,7 +350,7 @@ find_directory (const char *name)
     return 0;
   else
     {
-      char *caname = normalize_filename (name);
+      char *caname = normalize_filename (chdir_current, name);
       struct directory *dir = make_directory (name, caname);
       struct directory *ret = hash_lookup (directory_table, dir);
       free_directory (dir);
diff --git a/src/misc.c b/src/misc.c
index c7d51b2..280f85c 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -273,7 +273,7 @@ normalize_filename_x (char *file_name)
    Return a normalized newly-allocated copy.  */

 char *
-normalize_filename (const char *name)
+normalize_filename (int cdidx, const char *name)
 {
   char *copy = NULL;

@@ -285,7 +285,7 @@ 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?  */
-      const char *cwd = tar_getcwd ();
+      const char *cwd = tar_getcdpath (cdidx);
       size_t copylen;
       bool need_separator;

@@ -689,7 +689,7 @@ chdir_arg (char const *dir)
       if (! wd_count)
	{
	  wd[wd_count].name = ".";
-	  wd[wd_count].cwd = NULL;
+	  wd[wd_count].cwd = xgetcwd ();
	  wd[wd_count].fd = AT_FDCWD;
	  wd_count++;
	}
@@ -707,7 +707,14 @@ chdir_arg (char const *dir)
     }

   wd[wd_count].name = dir;
-  wd[wd_count].cwd = NULL;
+  if (IS_ABSOLUTE_FILE_NAME (wd[wd_count].name))
+    wd[wd_count].cwd = xstrdup (wd[wd_count].name);
+  else
+    {
+      namebuf_t nbuf = namebuf_create (wd[wd_count - 1].cwd);
+      namebuf_add_dir (nbuf, wd[wd_count].name);
+      wd[wd_count].cwd = namebuf_finish (nbuf);
+    }
   wd[wd_count].fd = 0;
   return wd_count++;
 }
@@ -788,37 +795,16 @@ tar_dirname (void)
 }

 const char *
-tar_getcwd (void)
+tar_getcdpath (int idx)
 {
-  static char *cwd;
-  namebuf_t nbuf;
-  int i;
-
-  if (!cwd)
-    cwd = xgetcwd ();
   if (!wd)
-    return cwd;
-
-  if (0 == chdir_current || !wd[chdir_current].cwd)
     {
-      if (IS_ABSOLUTE_FILE_NAME (wd[chdir_current].name))
-	{
-	  wd[chdir_current].cwd = xstrdup (wd[chdir_current].name);
-	  return wd[chdir_current].cwd;
-	}
-      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);
+      static char *cwd;
+      if (!cwd)
+	cwd = xgetcwd ();
+      return cwd;
     }
-  return wd[chdir_current].cwd;
+  return wd[idx].cwd;
 }
 
 void
diff --git a/src/names.c b/src/names.c
index 8c3052f..125f0b5 100644
--- a/src/names.c
+++ b/src/names.c
@@ -1004,13 +1004,11 @@ collect_and_sort_names (void)
   namelist = merge_sort (namelist, num_names, compare_names);

   num_names = 0;
-  nametab = hash_initialize (0, 0,
-			     name_hash,
-			     name_compare, NULL);
+  nametab = hash_initialize (0, 0, name_hash, name_compare, NULL);
   for (name = namelist; name; name = next_name)
     {
       next_name = name->next;
-      name->caname = normalize_filename (name->name);
+      name->caname = normalize_filename (name->change_dir, name->name);
       if (prev_name)
	{
	  struct name *p = hash_lookup (nametab, name);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 29ebab1..b05a151 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -99,6 +99,7 @@ TESTSUITE_AT = \
  incr06.at\
  incr07.at\
  incr08.at\
+ incr09.at\
  indexfile.at\
  ignfail.at\
  label01.at\
diff --git a/tests/incr09.at b/tests/incr09.at
new file mode 100644
index 0000000..b6130a6
--- /dev/null
+++ b/tests/incr09.at
@@ -0,0 +1,59 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+AT_SETUP([incremental with alternating -C])
+AT_KEYWORDS([incremental create incr09])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo bar middle
+echo foo/foo_file > foo/foo_file
+echo bar/bar_file > bar/bar_file
+echo middle/file > middle/middle_file
+decho A
+tar -cvf foo.tar --incremental -C foo . -C `pwd` middle  -C bar .
+
+rm foo.tar
+>toplevel_file
+decho B
+tar -cvf foo.tar --incremental -C foo . -C `pwd` toplevel_file  -C bar .
+],
+[0],
+[A
+./
+./
+middle/
+./bar_file
+./foo_file
+middle/middle_file
+B
+./
+./
+toplevel_file
+./bar_file
+./foo_file
+],
+[A
+tar: .: Directory is new
+tar: middle: Directory is new
+tar: .: Directory is new
+B
+tar: .: Directory is new
+tar: .: Directory is new
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index d468dcf..a9f2ab6 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -260,6 +260,7 @@ m4_include([incr04.at])
 m4_include([incr06.at])
 m4_include([incr07.at])
 m4_include([incr08.at])
+m4_include([incr09.at])

 m4_include([filerem01.at])
 m4_include([filerem02.at])
--
2.9.3


From 779b02280a9561029d0e459275af3b3a59e521c3 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Thu, 3 Oct 2013 22:41:04 +0300
Subject: [PATCH 06/11] Tiny changes.

* src/misc.c: Fix comments, rename wd.cwd to wd.abspath (Nathan Stratton
Treadway);
* src/tar.c (options): Reword description of the --starting-file and
--preserve-order options.
(decode_options): Both --starting-file and --preserve-order have meaning
only when used together with an archive reading command. (Pavel Raiskup).
---
 src/misc.c | 44 +++++++++++++++++++++++++++++---------------
 src/tar.c  |  5 +++--
 2 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/src/misc.c b/src/misc.c
index 280f85c..201ed16 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -279,21 +279,23 @@ normalize_filename (int cdidx, const char *name)

   if (IS_RELATIVE_FILE_NAME (name))
     {
-      /* Set COPY to the absolute file name if possible.
+      /* Set COPY to the absolute path for this name.

          FIXME: There should be no need to get the absolute file 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?  */
-      const char *cwd = tar_getcdpath (cdidx);
+         tar_getcdpath does not return a true "canonical" path, so
+         this following approach may lead to situations where the same
+         file or directory is processed twice under different absolute
+         paths without that duplication being detected.  Perhaps we
+         should use dev+ino pairs instead of names?  */
+      const char *cdpath = tar_getcdpath (cdidx);
       size_t copylen;
       bool need_separator;

-      copylen = strlen (cwd);
+      copylen = strlen (cdpath);
       need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
-			  && copylen == 2 && ISSLASH (cwd[1]));
+			  && copylen == 2 && ISSLASH (cdpath[1]));
       copy = xmalloc (copylen + need_separator + strlen (name) + 1);
-      strcpy (copy, cwd);
+      strcpy (copy, cdpath);
       copy[copylen] = DIRECTORY_SEPARATOR;
       strcpy (copy + copylen + need_separator, name);
     }
@@ -633,8 +635,10 @@ struct wd
 {
   /* The directory's name.  */
   char const *name;
-  /* Current working directory; initialized by tar_getcwd */
-  char *cwd;
+  /* "absolute" path representing this directory; in the contrast to
+     the real absolute pathname, it can contain /../ components (see
+     normalize_filename_x for the reason of it). */
+  char *abspath;
   /* 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.  */
@@ -689,7 +693,7 @@ chdir_arg (char const *dir)
       if (! wd_count)
	{
	  wd[wd_count].name = ".";
-	  wd[wd_count].cwd = xgetcwd ();
+	  wd[wd_count].abspath = xgetcwd ();
	  wd[wd_count].fd = AT_FDCWD;
	  wd_count++;
	}
@@ -707,13 +711,16 @@ chdir_arg (char const *dir)
     }

   wd[wd_count].name = dir;
+  /* if the given name is an absolute path, then use that path
+     to represent this working directory; otherwise, construct
+     a path based on the previous -C option's absolute path */
   if (IS_ABSOLUTE_FILE_NAME (wd[wd_count].name))
-    wd[wd_count].cwd = xstrdup (wd[wd_count].name);
+    wd[wd_count].abspath = xstrdup (wd[wd_count].name);
   else
     {
-      namebuf_t nbuf = namebuf_create (wd[wd_count - 1].cwd);
+      namebuf_t nbuf = namebuf_create (wd[wd_count - 1].abspath);
       namebuf_add_dir (nbuf, wd[wd_count].name);
-      wd[wd_count].cwd = namebuf_finish (nbuf);
+      wd[wd_count].abspath = namebuf_finish (nbuf);
     }
   wd[wd_count].fd = 0;
   return wd_count++;
@@ -794,6 +801,13 @@ tar_dirname (void)
   return wd[chdir_current].name;
 }

+/* Return the absolute path that represents the working
+   directory referenced by IDX.
+
+   If wd is empty, then there were no -C options given, and
+   chdir_args() has never been called, so we simply return the
+   process's actual cwd.  (Note that in this case IDX is ignored,
+   since it should always be 0.) */
 const char *
 tar_getcdpath (int idx)
 {
@@ -804,7 +818,7 @@ tar_getcdpath (int idx)
	cwd = xgetcwd ();
       return cwd;
     }
-  return wd[idx].cwd;
+  return wd[idx].abspath;
 }
 
 void
diff --git a/src/tar.c b/src/tar.c
index 18277e4..d11daa1 100644
--- a/src/tar.c
+++ b/src/tar.c
@@ -536,7 +536,8 @@ static struct argp_option options[] = {
   {"no-same-permissions", NO_SAME_PERMISSIONS_OPTION, 0, 0,
    N_("apply the user's umask when extracting permissions from the archive (default for ordinary users)"), GRID+1 },
   {"preserve-order", 's', 0, 0,
-   N_("sort names to extract to match archive"), GRID+1 },
+   N_("member arguments are listed in the same order as the "
+      "files in the archive"), GRID+1 },
   {"same-order", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
   {"preserve", PRESERVE_OPTION, 0, 0,
    N_("same as both -p and -s"), GRID+1 },
@@ -730,7 +731,7 @@ static struct argp_option options[] = {
   {"hard-dereference", HARD_DEREFERENCE_OPTION, 0, 0,
    N_("follow hard links; archive and dump the files they refer to"), GRID+1 },
   {"starting-file", 'K', N_("MEMBER-NAME"), 0,
-   N_("begin at member MEMBER-NAME in the archive"), GRID+1 },
+   N_("begin at member MEMBER-NAME when reading the archive"), GRID+1 },
   {"newer", 'N', N_("DATE-OR-FILE"), 0,
    N_("only store files newer than DATE-OR-FILE"), GRID+1 },
   {"after-date", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
--
2.9.3


From 79f04038e17dec01031113f4ba68f291f22012c3 Mon Sep 17 00:00:00 2001
From: Nathan Stratton Treadway <nathanst@ontko.com>
Date: Sat, 5 Oct 2013 08:53:08 +0300
Subject: [PATCH 07/11] Provide comprehensive testcases for various file
 removal modes.

* tests/Makefile.am: Add new testcases.
* tests/testsuite.at: Likewise.
* tests/incr09.at: Add description.
* tests/remfiles04a.at: New file.
* tests/remfiles05.at: Rename to ...
* tests/remfiles04b.at: ... this.
* tests/remfiles04.at: Rename to ...
* tests/remfiles04c.at: ... this.
* tests/remfiles05a.at: New file.
* tests/remfiles05b.at: New file.
* tests/remfiles06.at: Rename to ...
* tests/remfiles05c.at: ... this.
* tests/remfiles06a.at: New file.
* tests/remfiles06b.at: New file.
* tests/remfiles06c.at: New file.
* tests/remfiles07a.at: New file.
* tests/remfiles07b.at: New file.
* tests/remfiles07c.at: New file.
* tests/remfiles08a.at: New file.
* tests/remfiles08b.at: New file.
* tests/remfiles08c.at: New file.
* tests/remfiles08.at: Rename to ...
* tests/remfiles09a.at: ... this.
* tests/remfiles09b.at: New file.
* tests/remfiles07.at: Rename to ...
* tests/remfiles09c.at: ... this.
---
 tests/Makefile.am                       | 23 ++++++++---
 tests/incr09.at                         |  8 ++++
 tests/remfiles04a.at                    | 45 ++++++++++++++++++++++
 tests/remfiles04b.at                    | 53 +++++++++++++++++++++++++
 tests/{remfiles04.at => remfiles04c.at} | 21 +++++++---
 tests/remfiles05a.at                    | 64 +++++++++++++++++++++++++++++++
 tests/remfiles05b.at                    | 55 ++++++++++++++++++++++++++
 tests/{remfiles05.at => remfiles05c.at} | 35 ++++++++++-------
 tests/remfiles06.at                     | 65 -------------------------------
 tests/remfiles06a.at                    | 56 +++++++++++++++++++++++++++
 tests/remfiles06b.at                    | 56 +++++++++++++++++++++++++++
 tests/remfiles06c.at                    | 68 +++++++++++++++++++++++++++++++++
 tests/remfiles07a.at                    | 56 +++++++++++++++++++++++++++
 tests/remfiles07b.at                    | 56 +++++++++++++++++++++++++++
 tests/remfiles07c.at                    | 68 +++++++++++++++++++++++++++++++++
 tests/remfiles08a.at                    | 56 +++++++++++++++++++++++++++
 tests/remfiles08b.at                    | 56 +++++++++++++++++++++++++++
 tests/remfiles08c.at                    | 68 +++++++++++++++++++++++++++++++++
 tests/{remfiles08.at => remfiles09a.at} | 27 +++++++------
 tests/remfiles09b.at                    | 57 +++++++++++++++++++++++++++
 tests/{remfiles07.at => remfiles09c.at} | 37 ++++++++----------
 tests/testsuite.at                      | 23 ++++++++---
 22 files changed, 923 insertions(+), 130 deletions(-)
 create mode 100644 tests/remfiles04a.at
 create mode 100644 tests/remfiles04b.at
 rename tests/{remfiles04.at => remfiles04c.at} (69%)
 create mode 100644 tests/remfiles05a.at
 create mode 100644 tests/remfiles05b.at
 rename tests/{remfiles05.at => remfiles05c.at} (63%)
 create mode 100644 tests/remfiles06a.at
 create mode 100644 tests/remfiles06b.at
 create mode 100644 tests/remfiles06c.at
 create mode 100644 tests/remfiles07a.at
 create mode 100644 tests/remfiles07b.at
 create mode 100644 tests/remfiles07c.at
 create mode 100644 tests/remfiles08a.at
 create mode 100644 tests/remfiles08b.at
 create mode 100644 tests/remfiles08c.at
 rename tests/{remfiles08.at => remfiles09a.at} (66%)
 create mode 100644 tests/remfiles09b.at
 rename tests/{remfiles07.at => remfiles09c.at} (68%)

diff --git a/tests/Makefile.am b/tests/Makefile.am
index b05a151..cf6f576 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -141,11 +141,24 @@ TESTSUITE_AT = \
  remfiles01.at\
  remfiles02.at\
  remfiles03.at\
- remfiles04.at\
- remfiles05.at\
- remfiles06.at\
- remfiles07.at\
- remfiles08.at\
+ remfiles04a.at\
+ remfiles04b.at\
+ remfiles04c.at\
+ remfiles05a.at\
+ remfiles05b.at\
+ remfiles05c.at\
+ remfiles06a.at\
+ remfiles06b.at\
+ remfiles06c.at\
+ remfiles07a.at\
+ remfiles07b.at\
+ remfiles07c.at\
+ remfiles08a.at\
+ remfiles08b.at\
+ remfiles08c.at\
+ remfiles09a.at\
+ remfiles09b.at\
+ remfiles09c.at\
  same-order01.at\
  same-order02.at\
  shortfile.at\
diff --git a/tests/incr09.at b/tests/incr09.at
index b6130a6..e91fb5a 100644
--- a/tests/incr09.at
+++ b/tests/incr09.at
@@ -15,6 +15,14 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

+# Description: For some intermediate versions of tar 1.26.90,
+# tar would fail to correctly cannonicalize archive member names
+# in incremental mode if there was a -C options with an absolute path
+# on the command line without any archive members specified within that
+# directory. (In that case, the canonical name generated for
+# members specified after later -C options wouldn't correctly reflect the
+# previous absolute path.)
+
 AT_SETUP([incremental with alternating -C])
 AT_KEYWORDS([incremental create incr09])

diff --git a/tests/remfiles04a.at b/tests/remfiles04a.at
new file mode 100644
index 0000000..d1e4614
--- /dev/null
+++ b/tests/remfiles04a.at
@@ -0,0 +1,45 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a single relative-path -C option,
+# in --create/non-incremental mode.
+#
+
+AT_SETUP([remove-files with -C:rel in -c/non-incr. mode])
+AT_KEYWORDS([create remove-files remfiles04 remfiles04a])
+
+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
+],
+[0],
+[A
+.
+./bar
+./foo
+./foo.tar
+],[],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles04b.at b/tests/remfiles04b.at
new file mode 100644
index 0000000..3208557
--- /dev/null
+++ b/tests/remfiles04b.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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a single relative-path -C option,
+# in --create/incremental mode.
+#
+# (Tar 1.26 would remove files in original working directory when called in
+# this manner.  [It would follow the -C for archiving the files, but ignore it
+# for removing them afterwards.]
+#
+# Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
+# References: <20130921171234.GG32256@shire.ontko.com>,
+#             http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00028.html
+# )
+
+AT_SETUP([remove-files with -C:rel in -c/incr. mode])
+AT_KEYWORDS([create incremental remove-files remfiles04 remfiles04b])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+echo bar > bar
+echo foobar > foo/bar
+tar -cf foo.tar --incremental --remove-files -C foo bar
+echo A
+find . | sort
+],
+[0],
+[A
+.
+./bar
+./foo
+./foo.tar
+],[],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles04.at b/tests/remfiles04c.at
similarity index 69%
rename from tests/remfiles04.at
rename to tests/remfiles04c.at
index 04df45b..a1b6d56 100644
--- a/tests/remfiles04.at
+++ b/tests/remfiles04c.at
@@ -15,24 +15,32 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-# Description: Tar 1.26 would remove wrong files when called with
-# --remove-files and -C
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a single relative-path -C option,
+# in --append mode.
+#
+# (Tar 1.26 would remove files in original working directory when called in
+# this manner.  [It would follow the -C for archiving the files, but ignore it
+# for removing them afterwards.]
+#
 # Reported by: Jörgen Strand <Jorgen.Strand@sonymobile.com>
 # 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_SETUP([remove-files with -C:rel in -r mode])
+AT_KEYWORDS([create append remove-files remfiles04 remfiles04c])

 AT_TAR_CHECK([
 AT_SORT_PREREQ
 mkdir foo
 echo bar > bar
 echo foobar > foo/bar
-tar -cf foo.tar --remove-files -C foo bar
+tar -cf foo.tar -C foo bar
 echo A
 find . | sort
-echo foobar > foo/bar
 tar -rf foo.tar --remove-files -C foo bar
 echo B
 find . | sort
@@ -43,6 +51,7 @@ find . | sort
 ./bar
 ./foo
 ./foo.tar
+./foo/bar
 B
 .
 ./bar
diff --git a/tests/remfiles05a.at b/tests/remfiles05a.at
new file mode 100644
index 0000000..4ceec37
--- /dev/null
+++ b/tests/remfiles05a.at
@@ -0,0 +1,64 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of two relative-path -C options,
+# in --create/non-incremental mode.
+#
+# (This specific case failed during development of tar 1.26.90:
+# 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 <nathanst@ontko.com>
+# References: <20130924145657.GM32256@shire.ontko.com>,
+#             http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00045.html
+# )
+
+AT_SETUP([remove-files with -C:rel,rel in -c/non-incr. mode])
+AT_KEYWORDS([create remove-files remfiles05 remfiles05a])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+decho A
+tar -cvf foo.tar --remove-files -C foo file -C ../bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles05b.at b/tests/remfiles05b.at
new file mode 100644
index 0000000..d120efd
--- /dev/null
+++ b/tests/remfiles05b.at
@@ -0,0 +1,55 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of two relative-path -C options,
+# in --create/incremental mode.
+#
+
+AT_SETUP([remove-files with -C:rel,rel in -c/incr. mode])
+AT_KEYWORDS([create incremental remove-files remfiles05 remfiles05b])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+decho A
+tar -cvf foo.tar --incremental --remove-files -C foo file -C ../bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles05.at b/tests/remfiles05c.at
similarity index 63%
rename from tests/remfiles05.at
rename to tests/remfiles05c.at
index 04425a7..a01b092 100644
--- a/tests/remfiles05.at
+++ b/tests/remfiles05c.at
@@ -15,25 +15,28 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-# Description: Tar 1.26 would remove wrong files when invoked with
-# --listed-incremental and -C
-# Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
-# References: <20130921171234.GG32256@shire.ontko.com>,
-#             http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00028.html
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of two relative-path -C options,
+# in --append mode.
+#

-AT_SETUP([incremental and -C])
-AT_KEYWORDS([incremental create remove-files remfiles05])
+AT_SETUP([remove-files with -C:rel,rel in -r mode])
+AT_KEYWORDS([create append remove-files remfiles05 remfiles05c])

 AT_TAR_CHECK([
 AT_SORT_PREREQ
 mkdir foo
-echo bar > bar
-echo foo/bar > foo/bar
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+tar -cf foo.tar -C foo file -C ../bar file
 decho A
 find . | sort
-
 decho B
-tar -cvf foo.tar --listed-incremental=foo.snar --remove-files -C foo bar
+tar -rvf foo.tar --remove-files -C foo file -C ../bar file
 decho C
 find . | sort
 ],
@@ -41,15 +44,19 @@ find . | sort
 [A
 .
 ./bar
+./bar/file
+./file
 ./foo
-./foo/bar
+./foo.tar
+./foo/file
 B
-bar
+file
+file
 C
 .
 ./bar
+./file
 ./foo
-./foo.snar
 ./foo.tar
 ],
 [A
diff --git a/tests/remfiles06.at b/tests/remfiles06.at
deleted file mode 100644
index c2d9876..8b13789
--- a/tests/remfiles06.at
+++ /dev/null
@@ -1,66 +0,0 @@
-# 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 <http://www.gnu.org/licenses/>.
-
-# 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 <nathanst@ontko.com>
-# References: <20130924145657.GM32256@shire.ontko.com>,
-#             http://lists.gnu.org/archive/html/bug-tar/2013-09/msg00045.html
-
-AT_SETUP([remove with two -C])
-AT_KEYWORDS([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/remfiles06a.at b/tests/remfiles06a.at
new file mode 100644
index 0000000..fe762c1
--- /dev/null
+++ b/tests/remfiles06a.at
@@ -0,0 +1,56 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a relative -C option followed by an absolute -C,
+# in --create/non-incremental mode.
+#
+
+AT_SETUP([remove-files with -C:rel,abs in -c/non-incr. mode])
+AT_KEYWORDS([create remove-files remfiles06 remfiles06a])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+decho A
+tar -cvf foo.tar --remove-files -C foo file -C $DIR/bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles06b.at b/tests/remfiles06b.at
new file mode 100644
index 0000000..3b867fb
--- /dev/null
+++ b/tests/remfiles06b.at
@@ -0,0 +1,56 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a relative -C option followed by an absolute -C,
+# in --create/incremental mode.
+#
+
+AT_SETUP([remove-files with -C:rel,abs in -c/incr. mode])
+AT_KEYWORDS([create incremental remove-files remfiles06 remfiles06b])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+decho A
+tar -cvf foo.tar --incremental --remove-files -C foo file -C $DIR/bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles06c.at b/tests/remfiles06c.at
new file mode 100644
index 0000000..ad9164d
--- /dev/null
+++ b/tests/remfiles06c.at
@@ -0,0 +1,68 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a relative -C option followed by an absolute -C,
+# in --append mode.
+#
+
+AT_SETUP([remove-files with -C:rel,abs in -r mode])
+AT_KEYWORDS([create append remove-files remfiles06 remfiles06c])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+tar -cf foo.tar -C foo file -C $DIR/bar file
+decho A
+find . | sort
+decho B
+tar -rvf foo.tar --remove-files -C foo file -C ../bar file
+decho C
+find . | sort
+],
+[0],
+[A
+.
+./bar
+./bar/file
+./file
+./foo
+./foo.tar
+./foo/file
+B
+file
+file
+C
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+C
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles07a.at b/tests/remfiles07a.at
new file mode 100644
index 0000000..95f645c
--- /dev/null
+++ b/tests/remfiles07a.at
@@ -0,0 +1,56 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a relative -C option followed by an absolute -C,
+# in --create/non-incremental mode.
+#
+
+AT_SETUP([remove-files with -C:rel,abs in -c/non-incr. mode])
+AT_KEYWORDS([create remove-files remfiles07 remfiles07a])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+decho A
+tar -cvf foo.tar --remove-files -C foo file -C $DIR/bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles07b.at b/tests/remfiles07b.at
new file mode 100644
index 0000000..ca67e5d
--- /dev/null
+++ b/tests/remfiles07b.at
@@ -0,0 +1,56 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a relative -C option followed by an absolute -C,
+# in --create/incremental mode.
+#
+
+AT_SETUP([remove-files with -C:rel,abs in -c/incr. mode])
+AT_KEYWORDS([create incremental remove-files remfiles07 remfiles07b])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+decho A
+tar -cvf foo.tar --incremental --remove-files -C foo file -C $DIR/bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles07c.at b/tests/remfiles07c.at
new file mode 100644
index 0000000..6a5c870
--- /dev/null
+++ b/tests/remfiles07c.at
@@ -0,0 +1,68 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of a relative -C option followed by an absolute -C,
+# in --append mode.
+#
+
+AT_SETUP([remove-files with -C:rel,abs in -r mode])
+AT_KEYWORDS([create append remove-files remfiles07 remfiles07c])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+tar -cf foo.tar -C foo file -C $DIR/bar file
+decho A
+find . | sort
+decho B
+tar -rvf foo.tar --remove-files -C foo file -C $DIR/bar file
+decho C
+find . | sort
+],
+[0],
+[A
+.
+./bar
+./bar/file
+./file
+./foo
+./foo.tar
+./foo/file
+B
+file
+file
+C
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+C
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles08a.at b/tests/remfiles08a.at
new file mode 100644
index 0000000..eadf149
--- /dev/null
+++ b/tests/remfiles08a.at
@@ -0,0 +1,56 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of an absolute -C option followed by a relative -C,
+# in --create/non-incremental mode.
+#
+
+AT_SETUP([remove-files with -C:abs,rel in -c/non-incr. mode])
+AT_KEYWORDS([create remove-files remfiles08 remfiles08a])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+decho A
+tar -cvf foo.tar --remove-files -C $DIR/foo file -C ../bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles08b.at b/tests/remfiles08b.at
new file mode 100644
index 0000000..9faf2bb
--- /dev/null
+++ b/tests/remfiles08b.at
@@ -0,0 +1,56 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of an absolute -C option followed by a relative -C,
+# in --create/incremental mode.
+#
+
+AT_SETUP([remove-files with -C:abs,rel in -c/incr. mode])
+AT_KEYWORDS([create incremental remove-files remfiles08 remfiles08b])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+decho A
+tar -cvf foo.tar --incremental --remove-files -C $DIR/foo file -C ../bar file
+decho B
+find . | sort
+],
+[0],
+[A
+file
+file
+B
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles08c.at b/tests/remfiles08c.at
new file mode 100644
index 0000000..a220f4c
--- /dev/null
+++ b/tests/remfiles08c.at
@@ -0,0 +1,68 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: ensure tar correctly respects -C option when deleting
+# files due to the --remove-files option.
+#
+# This case checks the use of an absolute -C option followed by a relative -C,
+# in --append mode.
+#
+
+AT_SETUP([remove-files with -C:abs,rel in -r mode])
+AT_KEYWORDS([create append remove-files remfiles08 remfiles08c])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+mkdir bar
+echo file > file
+echo foo/file > foo/file
+echo bar/file > bar/file
+DIR=`pwd`
+tar -cf foo.tar -C $DIR/foo file -C ../bar file
+decho A
+find . | sort
+decho B
+tar -rvf foo.tar --remove-files -C $DIR/foo file -C ../bar file
+decho C
+find . | sort
+],
+[0],
+[A
+.
+./bar
+./bar/file
+./file
+./foo
+./foo.tar
+./foo/file
+B
+file
+file
+C
+.
+./bar
+./file
+./foo
+./foo.tar
+],
+[A
+B
+C
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles08.at b/tests/remfiles09a.at
similarity index 66%
rename from tests/remfiles08.at
rename to tests/remfiles09a.at
index 0649e85..fd28b4f 100644
--- a/tests/remfiles08.at
+++ b/tests/remfiles09a.at
@@ -15,29 +15,28 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-# Description: See remfiles06.at
-# Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
-# References: <20130926050634.GW32256@shire.ontko.com>
+# Description: check --remove-files operation when archiving/deleting
+# directory trees.
+#
+# This case checks the operation
+# in --create/non-incremental mode.
+#

-AT_SETUP([remove with -C to absolute and relative paths])
-AT_KEYWORDS([incremental create remove-files remfiles08])
+AT_SETUP([remove-files on full directory in -c/non-incr. mode])
+AT_KEYWORDS([create remove-files remfiles09 remfiles09a])

 AT_TAR_CHECK([
 mkdir foo
-mkdir bar
-echo foo/foo_file > foo/foo_file
-echo bar/bar_file > bar/bar_file
+echo foo/file > foo/file
 decho A
-tar -cvf foo.tar --remove-files -C `pwd`/foo . -C ../bar .
+tar -cvf foo.tar --remove-files foo
 decho B
-find
+find .
 ],
 [0],
 [A
-./
-./foo_file
-./
-./bar_file
+foo/
+foo/file
 B
 .
 ./foo.tar
diff --git a/tests/remfiles09b.at b/tests/remfiles09b.at
new file mode 100644
index 0000000..30cc3ee
--- /dev/null
+++ b/tests/remfiles09b.at
@@ -0,0 +1,57 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Description: check --remove-files operation when archiving/deleting
+# directory trees.
+#
+# This case checks the operation
+# in --create/incremental mode.
+#
+# Note: in tar 1.27, when run in incremental mode tar will attempt to remove
+# the directory before removing the files within that directory, and thus
+# the --remove-files operation will cause tar to abort with an error status.
+# This issue will be fixed in a later version of tar.
+
+AT_SETUP([remove-files on full directory in -c/incr. mode])
+AT_KEYWORDS([create incremental remove-files remfiles09 remfiles09b])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir foo
+echo foo/file > foo/file
+decho A
+tar -cvf foo.tar --incremental --remove-files foo
+TARSTAT=$?
+decho B
+find .
+test $TARSTAT -ne 0 && AT_SKIP_TEST   # we expect to fail in tar 1.27
+],
+[0],
+[A
+foo/
+foo/file
+B
+.
+./foo
+./foo.tar
+],
+[A
+tar: foo: Directory is new
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/remfiles07.at b/tests/remfiles09c.at
similarity index 68%
rename from tests/remfiles07.at
rename to tests/remfiles09c.at
index 742e0a1..7241608 100644
--- a/tests/remfiles07.at
+++ b/tests/remfiles09c.at
@@ -15,45 +15,40 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-# Description: See remfiles06.at
-# Reported by: Nathan Stratton Treadway <nathanst@ontko.com>
-# References: <20130924185129.GO32256@shire.ontko.com>
+# Description: check --remove-files operation when archiving/deleting
+# directory trees.
+#
+# This case checks the operation
+# in --append mode.
+#

-AT_SETUP([remove with -C to absolute path])
-AT_KEYWORDS([create remove-files remfiles07])
+AT_SETUP([remove-files on full directory in -r mode])
+AT_KEYWORDS([create append remove-files remfiles09 remfiles09c])

 AT_TAR_CHECK([
 AT_SORT_PREREQ
-mkdir tartest
-cd tartest
 mkdir foo
 echo foo/file > foo/file
-mkdir bar
-echo bar/file > bar/file
+tar -cf foo.tar foo
 decho A
-find|sort
-
-DIR=`pwd`
+find . | sort
 decho B
-tar -cvf ../foo.tar --remove-files -C foo file -C $DIR/bar file
-
+tar -rvf foo.tar --remove-files foo
 decho C
-find|sort
+find . | sort
 ],
 [0],
 [A
 .
-./bar
-./bar/file
 ./foo
+./foo.tar
 ./foo/file
 B
-file
-file
+foo/
+foo/file
 C
 .
-./bar
-./foo
+./foo.tar
 ],
 [A
 B
diff --git a/tests/testsuite.at b/tests/testsuite.at
index a9f2ab6..1cc425f 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -332,11 +332,24 @@ m4_include([grow.at])
 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])
-m4_include([remfiles08.at])
+m4_include([remfiles04a.at])
+m4_include([remfiles04b.at])
+m4_include([remfiles04c.at])
+m4_include([remfiles05a.at])
+m4_include([remfiles05b.at])
+m4_include([remfiles05c.at])
+m4_include([remfiles06a.at])
+m4_include([remfiles06b.at])
+m4_include([remfiles06c.at])
+m4_include([remfiles07a.at])
+m4_include([remfiles07b.at])
+m4_include([remfiles07c.at])
+m4_include([remfiles08a.at])
+m4_include([remfiles08b.at])
+m4_include([remfiles08c.at])
+m4_include([remfiles09a.at])
+m4_include([remfiles09b.at])
+m4_include([remfiles09c.at])

 m4_include([sigpipe.at])

--
2.9.3


From c3d32c9c848f1f305cd9aefd9f485cdfbcee51b2 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Sat, 5 Oct 2013 09:29:55 +0300
Subject: [PATCH 08/11] Xfail the remfiles09b test.

* tests/remfiles09b.at: Turn into expected failure.
---
 tests/remfiles09b.at | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/remfiles09b.at b/tests/remfiles09b.at
index 30cc3ee..de9b172 100644
--- a/tests/remfiles09b.at
+++ b/tests/remfiles09b.at
@@ -29,6 +29,8 @@
 AT_SETUP([remove-files on full directory in -c/incr. mode])
 AT_KEYWORDS([create incremental remove-files remfiles09 remfiles09b])

+AT_XFAIL_IF(true) # we expect to fail in tar 1.27
+
 AT_TAR_CHECK([
 AT_SORT_PREREQ
 mkdir foo
@@ -38,7 +40,6 @@ tar -cvf foo.tar --incremental --remove-files foo
 TARSTAT=$?
 decho B
 find .
-test $TARSTAT -ne 0 && AT_SKIP_TEST   # we expect to fail in tar 1.27
 ],
 [0],
 [A
--
2.9.3


From 2c5449cc473b0a9affed02feaf3ad42e5e89bfb5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Tue, 29 Apr 2014 14:22:07 -0700
Subject: [PATCH 09/11] tar: do not dereference NULL pointer with
 '--remove-files .'

Problem reported by Thorsten Hirsch in:
http://lists.gnu.org/archive/html/bug-tar/2014-04/msg00011.html
* src/unlink.c (flush_deferred_unlinks):
Do not attempt to find the parent of "." when "." is
at the top level.
* tests/remfiles10.at: New file.
* tests/Makefile.am (TESTSUITE_AT):
* tests/testsuite.at: Add it.
---
 src/unlink.c        |  5 +++--
 tests/Makefile.am   |  1 +
 tests/remfiles10.at | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/testsuite.at  |  1 +
 4 files changed, 51 insertions(+), 2 deletions(-)
 create mode 100644 tests/remfiles10.at

diff --git a/src/unlink.c b/src/unlink.c
index 10e0b41..6e41acc 100644
--- a/src/unlink.c
+++ b/src/unlink.c
@@ -84,8 +84,9 @@ flush_deferred_unlinks (bool force)
	    {
	      const char *fname;

-	      if (p->file_name[0] == 0 ||
-		  strcmp (p->file_name, ".") == 0)
+	      if (p->dir_idx
+		  && (p->file_name[0] == 0
+		      || strcmp (p->file_name, ".") == 0))
		{
		  fname = tar_dirname ();
		  chdir_do (p->dir_idx - 1);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cf6f576..792c83c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -159,6 +159,7 @@ TESTSUITE_AT = \
  remfiles09a.at\
  remfiles09b.at\
  remfiles09c.at\
+ remfiles10.at\
  same-order01.at\
  same-order02.at\
  shortfile.at\
diff --git a/tests/remfiles10.at b/tests/remfiles10.at
new file mode 100644
index 0000000..b4fe139
--- /dev/null
+++ b/tests/remfiles10.at
@@ -0,0 +1,46 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+# Test suite for GNU tar.
+# Copyright 2014 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 <http://www.gnu.org/licenses/>.
+
+# Check --remove-files with .
+
+AT_SETUP([remove-files])
+AT_KEYWORDS([create remove-files remfiles10])
+
+AT_TAR_CHECK([
+mkdir foo
+echo foo/file > foo/file
+decho A
+(cd foo && tar -cvf ../foo.tar --remove-files .)
+tar_status=$?
+decho B
+find foo
+exit $tar_status
+],
+[2],
+[A
+./
+./file
+B
+foo
+],
+[A
+tar: .: Cannot rmdir: Invalid argument
+tar: Exiting with failure status due to previous errors
+B
+],[],[],[gnu])
+
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 1cc425f..1078724 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -350,6 +350,7 @@ m4_include([remfiles08c.at])
 m4_include([remfiles09a.at])
 m4_include([remfiles09b.at])
 m4_include([remfiles09c.at])
+m4_include([remfiles10.at])

 m4_include([sigpipe.at])

--
2.9.3


From 612e134f4905f479b78b6d7faf9798494697a742 Mon Sep 17 00:00:00 2001
From: Nathan Stratton Treadway <nathanst@ontko.com>
Date: Sun, 27 Jul 2014 23:27:28 +0300
Subject: [PATCH 10/11] Restructure the remfiles testsuite.

---
 tests/remfiles06c.at |  2 +-
 tests/remfiles07a.at |  6 +++---
 tests/remfiles07b.at |  6 +++---
 tests/remfiles07c.at |  8 ++++----
 tests/remfiles08a.at | 31 +++++++++++++++----------------
 tests/remfiles08b.at | 38 ++++++++++++++++++++++----------------
 tests/remfiles08c.at | 37 ++++++++++++++++++-------------------
 tests/remfiles09a.at |  2 +-
 tests/remfiles09b.at |  3 ---
 9 files changed, 67 insertions(+), 66 deletions(-)

diff --git a/tests/remfiles06c.at b/tests/remfiles06c.at
index ad9164d..abb8e68 100644
--- a/tests/remfiles06c.at
+++ b/tests/remfiles06c.at
@@ -37,7 +37,7 @@ tar -cf foo.tar -C foo file -C $DIR/bar file
 decho A
 find . | sort
 decho B
-tar -rvf foo.tar --remove-files -C foo file -C ../bar file
+tar -rvf foo.tar --remove-files -C foo file -C $DIR/bar file
 decho C
 find . | sort
 ],
diff --git a/tests/remfiles07a.at b/tests/remfiles07a.at
index 95f645c..5b7df3e 100644
--- a/tests/remfiles07a.at
+++ b/tests/remfiles07a.at
@@ -18,11 +18,11 @@
 # Description: ensure tar correctly respects -C option when deleting
 # files due to the --remove-files option.
 #
-# This case checks the use of a relative -C option followed by an absolute -C,
+# This case checks the use of an absolute -C option followed by a relative -C,
 # in --create/non-incremental mode.
 #

-AT_SETUP([remove-files with -C:rel,abs in -c/non-incr. mode])
+AT_SETUP([remove-files with -C:abs,rel in -c/non-incr. mode])
 AT_KEYWORDS([create remove-files remfiles07 remfiles07a])

 AT_TAR_CHECK([
@@ -34,7 +34,7 @@ echo foo/file > foo/file
 echo bar/file > bar/file
 DIR=`pwd`
 decho A
-tar -cvf foo.tar --remove-files -C foo file -C $DIR/bar file
+tar -cvf foo.tar --remove-files -C $DIR/foo file -C ../bar file
 decho B
 find . | sort
 ],
diff --git a/tests/remfiles07b.at b/tests/remfiles07b.at
index ca67e5d..0147c5d 100644
--- a/tests/remfiles07b.at
+++ b/tests/remfiles07b.at
@@ -18,11 +18,11 @@
 # Description: ensure tar correctly respects -C option when deleting
 # files due to the --remove-files option.
 #
-# This case checks the use of a relative -C option followed by an absolute -C,
+# This case checks the use of an absolute -C option followed by a relative -C,
 # in --create/incremental mode.
 #

-AT_SETUP([remove-files with -C:rel,abs in -c/incr. mode])
+AT_SETUP([remove-files with -C:abs,rel in -c/incr. mode])
 AT_KEYWORDS([create incremental remove-files remfiles07 remfiles07b])

 AT_TAR_CHECK([
@@ -34,7 +34,7 @@ echo foo/file > foo/file
 echo bar/file > bar/file
 DIR=`pwd`
 decho A
-tar -cvf foo.tar --incremental --remove-files -C foo file -C $DIR/bar file
+tar -cvf foo.tar --incremental --remove-files -C $DIR/foo file -C ../bar file
 decho B
 find . | sort
 ],
diff --git a/tests/remfiles07c.at b/tests/remfiles07c.at
index 6a5c870..f190539 100644
--- a/tests/remfiles07c.at
+++ b/tests/remfiles07c.at
@@ -18,11 +18,11 @@
 # Description: ensure tar correctly respects -C option when deleting
 # files due to the --remove-files option.
 #
-# This case checks the use of a relative -C option followed by an absolute -C,
+# This case checks the use of an absolute -C option followed by a relative -C,
 # in --append mode.
 #

-AT_SETUP([remove-files with -C:rel,abs in -r mode])
+AT_SETUP([remove-files with -C:abs,rel in -r mode])
 AT_KEYWORDS([create append remove-files remfiles07 remfiles07c])

 AT_TAR_CHECK([
@@ -33,11 +33,11 @@ echo file > file
 echo foo/file > foo/file
 echo bar/file > bar/file
 DIR=`pwd`
-tar -cf foo.tar -C foo file -C $DIR/bar file
+tar -cf foo.tar -C $DIR/foo file -C ../bar file
 decho A
 find . | sort
 decho B
-tar -rvf foo.tar --remove-files -C foo file -C $DIR/bar file
+tar -rvf foo.tar --remove-files -C $DIR/foo file -C ../bar file
 decho C
 find . | sort
 ],
diff --git a/tests/remfiles08a.at b/tests/remfiles08a.at
index eadf149..1ffffb2 100644
--- a/tests/remfiles08a.at
+++ b/tests/remfiles08a.at
@@ -15,38 +15,37 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-# Description: ensure tar correctly respects -C option when deleting
-# files due to the --remove-files option.
+# Description: If tar 1.26 was called with the --remove-files option and told
+# to archive (and thus delete) two subdirectories where the second was
+# specified relative to the first, it would be unable to delete the
+# second directory (and its contents), since the relative path would no
+# longer be valid once the first directory was deleted.
 #
-# This case checks the use of an absolute -C option followed by a relative -C,
+# This case checks for successful deletion of all archived items
 # in --create/non-incremental mode.
 #

-AT_SETUP([remove-files with -C:abs,rel in -c/non-incr. mode])
+AT_SETUP([remove-files deleting two subdirs in -c/non-incr. mode])
 AT_KEYWORDS([create remove-files remfiles08 remfiles08a])

 AT_TAR_CHECK([
-AT_SORT_PREREQ
 mkdir foo
 mkdir bar
-echo file > file
-echo foo/file > foo/file
-echo bar/file > bar/file
-DIR=`pwd`
+echo foo/foo_file > foo/foo_file
+echo bar/bar_file > bar/bar_file
 decho A
-tar -cvf foo.tar --remove-files -C $DIR/foo file -C ../bar file
+tar -cvf foo.tar --remove-files -C foo . -C ../bar .
 decho B
-find . | sort
+find .
 ],
 [0],
 [A
-file
-file
+./
+./foo_file
+./
+./bar_file
 B
 .
-./bar
-./file
-./foo
 ./foo.tar
 ],
 [A
diff --git a/tests/remfiles08b.at b/tests/remfiles08b.at
index 9faf2bb..d61c9ab 100644
--- a/tests/remfiles08b.at
+++ b/tests/remfiles08b.at
@@ -15,41 +15,47 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-# Description: ensure tar correctly respects -C option when deleting
-# files due to the --remove-files option.
+# Description: If tar 1.26 was called with the --remove-files option and told
+# to archive (and thus delete) two subdirectories where the second was
+# specified relative to the first, it would be unable to delete the
+# second directory (and its contents), since the relative path would no
+# longer be valid once the first directory was deleted.
 #
-# This case checks the use of an absolute -C option followed by a relative -C,
+# This case checks for successful deletion of all archived items
 # in --create/incremental mode.
 #
+# Note: tar 1.27 fails this test case due to a more general issue
+# archving-and-removing a full directory tree when run in incremental
+# mode; see remfiles09b.at for that specific test case.

-AT_SETUP([remove-files with -C:abs,rel in -c/incr. mode])
+AT_SETUP([remove-files deleting two subdirs in -c/incr. mode])
 AT_KEYWORDS([create incremental remove-files remfiles08 remfiles08b])

+AT_XFAIL_IF(true) # we expect to fail in tar 1.27
+
 AT_TAR_CHECK([
-AT_SORT_PREREQ
 mkdir foo
 mkdir bar
-echo file > file
-echo foo/file > foo/file
-echo bar/file > bar/file
-DIR=`pwd`
+echo foo/foo_file > foo/foo_file
+echo bar/bar_file > bar/bar_file
 decho A
-tar -cvf foo.tar --incremental --remove-files -C $DIR/foo file -C ../bar file
+tar -cvf foo.tar --incremental --remove-files -C foo . -C ../bar .
 decho B
-find . | sort
+find .
 ],
 [0],
 [A
-file
-file
+./
+./
+./foo_file
+./bar_file
 B
 .
-./bar
-./file
-./foo
 ./foo.tar
 ],
 [A
+tar: .: Directory is new
+tar: .: Directory is new
 B
 ],[],[],[gnu])

diff --git a/tests/remfiles08c.at b/tests/remfiles08c.at
index a220f4c..19b18e2 100644
--- a/tests/remfiles08c.at
+++ b/tests/remfiles08c.at
@@ -15,49 +15,48 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

-# Description: ensure tar correctly respects -C option when deleting
-# files due to the --remove-files option.
+# Description: If tar 1.26 was called with the --remove-files option and told
+# to archive (and thus delete) two subdirectories where the second was
+# specified relative to the first, it would be unable to delete the
+# second directory (and its contents), since the relative path would no
+# longer be valid once the first directory was deleted.
 #
-# This case checks the use of an absolute -C option followed by a relative -C,
+# This case checks for successful deletion of all archived items
 # in --append mode.
 #

-AT_SETUP([remove-files with -C:abs,rel in -r mode])
+AT_SETUP([remove-files deleting two subdirs in -r mode])
 AT_KEYWORDS([create append remove-files remfiles08 remfiles08c])

 AT_TAR_CHECK([
 AT_SORT_PREREQ
 mkdir foo
 mkdir bar
-echo file > file
-echo foo/file > foo/file
-echo bar/file > bar/file
-DIR=`pwd`
-tar -cf foo.tar -C $DIR/foo file -C ../bar file
+echo foo/foo_file > foo/foo_file
+echo bar/bar_file > bar/bar_file
+tar -cf foo.tar -C foo . -C ../bar .
 decho A
 find . | sort
 decho B
-tar -rvf foo.tar --remove-files -C $DIR/foo file -C ../bar file
+tar -rvf foo.tar --remove-files -C foo . -C ../bar .
 decho C
-find . | sort
+find .
 ],
 [0],
 [A
 .
 ./bar
-./bar/file
-./file
+./bar/bar_file
 ./foo
 ./foo.tar
-./foo/file
+./foo/foo_file
 B
-file
-file
+./
+./foo_file
+./
+./bar_file
 C
 .
-./bar
-./file
-./foo
 ./foo.tar
 ],
 [A
diff --git a/tests/remfiles09a.at b/tests/remfiles09a.at
index fd28b4f..f4a3bf5 100644
--- a/tests/remfiles09a.at
+++ b/tests/remfiles09a.at
@@ -31,7 +31,7 @@ echo foo/file > foo/file
 decho A
 tar -cvf foo.tar --remove-files foo
 decho B
-find .
+find .
 ],
 [0],
 [A
diff --git a/tests/remfiles09b.at b/tests/remfiles09b.at
index de9b172..e12fe32 100644
--- a/tests/remfiles09b.at
+++ b/tests/remfiles09b.at
@@ -32,12 +32,10 @@ AT_KEYWORDS([create incremental remove-files remfiles09 remfiles09b])
 AT_XFAIL_IF(true) # we expect to fail in tar 1.27

 AT_TAR_CHECK([
-AT_SORT_PREREQ
 mkdir foo
 echo foo/file > foo/file
 decho A
 tar -cvf foo.tar --incremental --remove-files foo
-TARSTAT=$?
 decho B
 find .
 ],
@@ -47,7 +45,6 @@ foo/
 foo/file
 B
 .
-./foo
 ./foo.tar
 ],
 [A
--
2.9.3


From 6030b8a2589ff69d9200578e0aecc1f10aedb073 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org>
Date: Wed, 11 Nov 2015 13:01:45 +0200
Subject: [PATCH 11/11] Work around unlinkat bug on FreeBSD and GNU/Hurd

* src/unlink.c (dunlink_insert): New function.
(flush_deferred_unlinks): Skip cwds and nonempty directories
at the first pass.  If force is requested, run a second pass
removing them.
(queue_deferred_unlink): Make sure current working directory
entries are sorted in descending order by the value of dir_idx.
This makes sure they will be removed in right order, which works
around unlinkat bug on FreeBSD and GNU/Hurd.
* tests/remfiles08b.at: Remove expected failure.
* tests/remfiles09b.at: Likewise.
---
 src/unlink.c         | 91 +++++++++++++++++++++++++++++++++++++++++-----------
 tests/remfiles08b.at |  2 --
 tests/remfiles09b.at |  2 --
 3 files changed, 72 insertions(+), 23 deletions(-)

diff --git a/src/unlink.c b/src/unlink.c
index 6e41acc..f5fb81d 100644
--- a/src/unlink.c
+++ b/src/unlink.c
@@ -30,6 +30,10 @@ struct deferred_unlink
				       entry got added to the queue */
   };

+#define IS_CWD(p) \
+  ((p)->is_dir \
+   && ((p)->file_name[0] == 0 || strcmp ((p)->file_name, ".") == 0))
+
 /* The unlink queue */
 static struct deferred_unlink *dunlink_head, *dunlink_tail;

@@ -59,6 +63,24 @@ dunlink_alloc (void)
 }

 static void
+dunlink_insert (struct deferred_unlink *anchor, struct deferred_unlink *p)
+{
+  if (anchor)
+    {
+      p->next = anchor->next;
+      anchor->next = p;
+    }
+  else
+    {
+      p->next = dunlink_head;
+      dunlink_head = p;
+    }
+  if (!p->next)
+    dunlink_tail = p;
+  dunlink_count++;
+}
+
+static void
 dunlink_reclaim (struct deferred_unlink *p)
 {
   free (p->file_name);
@@ -84,12 +106,11 @@ flush_deferred_unlinks (bool force)
	    {
	      const char *fname;

-	      if (p->dir_idx
-		  && (p->file_name[0] == 0
-		      || strcmp (p->file_name, ".") == 0))
+	      if (p->dir_idx && IS_CWD (p))
		{
-		  fname = tar_dirname ();
-		  chdir_do (p->dir_idx - 1);
+		  prev = p;
+		  p = next;
+		  continue;
		}
	      else
		fname = p->file_name;
@@ -102,15 +123,12 @@ flush_deferred_unlinks (bool force)
		      /* nothing to worry about */
		      break;
		    case ENOTEMPTY:
-		      if (!force)
-			{
-			  /* Keep the record in list, in the hope we'll
-			     be able to remove it later */
-			  prev = p;
-			  p = next;
-			  continue;
-			}
-		      /* fall through */
+		      /* Keep the record in list, in the hope we'll
+			 be able to remove it later */
+		      prev = p;
+		      p = next;
+		      continue;
+
		    default:
		      rmdir_error (fname);
		    }
@@ -137,6 +155,34 @@ flush_deferred_unlinks (bool force)
     }
   if (!dunlink_head)
     dunlink_tail = NULL;
+  else if (force)
+    {
+      for (p = dunlink_head; p; )
+	{
+	  struct deferred_unlink *next = p->next;
+	  const char *fname;
+
+	  chdir_do (p->dir_idx);
+	  if (p->dir_idx && IS_CWD (p))
+	    {
+	      fname = tar_dirname ();
+	      chdir_do (p->dir_idx - 1);
+	    }
+	  else
+	    fname = p->file_name;
+
+	  if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
+	    {
+	      if (errno != ENOENT)
+		rmdir_error (fname);
+	    }
+	  dunlink_reclaim (p);
+	  dunlink_count--;
+	  p = next;
+	}
+      dunlink_head = dunlink_tail = NULL;
+    }
+
   chdir_do (saved_chdir);
 }

@@ -169,10 +215,17 @@ queue_deferred_unlink (const char *name, bool is_dir)
   p->is_dir = is_dir;
   p->records_written = records_written;

-  if (dunlink_tail)
-    dunlink_tail->next = p;
+  if (IS_CWD (p))
+    {
+      struct deferred_unlink *q, *prev;
+      for (q = dunlink_head, prev = NULL; q; prev = q, q = q->next)
+	if (IS_CWD (q) && q->dir_idx < p->dir_idx)
+	  break;
+      if (q)
+	dunlink_insert (prev, p);
+      else
+	dunlink_insert (dunlink_tail, p);
+    }
   else
-    dunlink_head = p;
-  dunlink_tail = p;
-  dunlink_count++;
+    dunlink_insert (dunlink_tail, p);
 }
diff --git a/tests/remfiles08b.at b/tests/remfiles08b.at
index d61c9ab..4487f83 100644
--- a/tests/remfiles08b.at
+++ b/tests/remfiles08b.at
@@ -31,8 +31,6 @@
 AT_SETUP([remove-files deleting two subdirs in -c/incr. mode])
 AT_KEYWORDS([create incremental remove-files remfiles08 remfiles08b])

-AT_XFAIL_IF(true) # we expect to fail in tar 1.27
-
 AT_TAR_CHECK([
 mkdir foo
 mkdir bar
diff --git a/tests/remfiles09b.at b/tests/remfiles09b.at
index e12fe32..4d05aa5 100644
--- a/tests/remfiles09b.at
+++ b/tests/remfiles09b.at
@@ -29,8 +29,6 @@
 AT_SETUP([remove-files on full directory in -c/incr. mode])
 AT_KEYWORDS([create incremental remove-files remfiles09 remfiles09b])

-AT_XFAIL_IF(true) # we expect to fail in tar 1.27
-
 AT_TAR_CHECK([
 mkdir foo
 echo foo/file > foo/file
--
2.9.3
