49a369
From a62313c0461cf9dbc81b86a01b8ca2d8f20755ff Mon Sep 17 00:00:00 2001
49a369
From: Pavel Raiskup <praiskup@redhat.com>
49a369
Date: Thu, 16 Nov 2017 13:59:59 +0100
49a369
Subject: [PATCH] From: Sergey Poznyakoff <gray@gnu.org.ua> Date: Thu, 16 Nov
49a369
 2017 11:26:44 +0200 Subject: [PATCH] Fix the --delay-directory-restore option
49a369
49a369
* src/extract.c (find_direct_ancestor): New function.
49a369
(create_placeholder_file): Set after_links member on delayed_set_stat
49a369
entries starting from the direct ancestor of the placeholder file.
49a369
49a369
* tests/extrac21.at: New testcase.
49a369
* tests/testsuite.at: Add extrac21
49a369
* tests/Makefile.am: Likewise.
49a369
49a369
* NEWS: Update.
49a369
---
49a369
 NEWS               |  5 +++++
49a369
 src/extract.c      | 24 +++++++++++++++++-----
49a369
 tests/Makefile.am  |  1 +
49a369
 tests/extrac21.at  | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
49a369
 tests/testsuite.at |  1 +
49a369
 5 files changed, 85 insertions(+), 5 deletions(-)
49a369
 create mode 100644 tests/extrac21.at
49a369
49a369
diff --git a/NEWS b/NEWS
49a369
index 36a27da..36e90ca 100644
49a369
--- a/NEWS
49a369
+++ b/NEWS
49a369
@@ -18,6 +18,11 @@ symlinks to directories when extracting from the archive.
49a369
 It is mainly intended to provide compatibility with the Slackware
49a369
 installation scripts.
49a369
 
49a369
+* Fixed the --delay-directory-restore option
49a369
+
49a369
+In some cases tar would restore the directory permissions too early,
49a369
+causing subsequent link extractions in that directory to fail.
49a369
+
49a369
 
49a369
 version 1.26 - Sergey Poznyakoff, 2011-03-12
49a369
 
49a369
diff --git a/src/extract.c b/src/extract.c
49a369
index 63e4d14..0bed3e3 100644
49a369
--- a/src/extract.c
49a369
+++ b/src/extract.c
49a369
@@ -392,6 +392,24 @@ set_stat (char const *file_name,
49a369
   xattrs_selinux_set (st, file_name, typeflag);
49a369
 }
49a369
 
49a369
+/* Find the direct ancestor of FILE_NAME in the delayed_set_stat list.
49a369
+ */   
49a369
+static struct delayed_set_stat *
49a369
+find_direct_ancestor (char const *file_name)
49a369
+{
49a369
+  struct delayed_set_stat *h = delayed_set_stat_head;
49a369
+  while (h)
49a369
+    {
49a369
+      if (h && ! h->after_links
49a369
+	  && strncmp (file_name, h->file_name, h->file_name_len) == 0
49a369
+	  && ISSLASH (file_name[h->file_name_len])
49a369
+	  && (last_component (file_name) == file_name + h->file_name_len + 1))
49a369
+	break;
49a369
+      h = h->next;
49a369
+    }
49a369
+  return h;
49a369
+}
49a369
+
49a369
 /* For each entry H in the leading prefix of entries in HEAD that do
49a369
    not have after_links marked, mark H and fill in its dev and ino
49a369
    members.  Assume HEAD && ! HEAD->after_links.  */
49a369
@@ -1257,11 +1275,7 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
49a369
       xheader_xattr_copy (&current_stat_info, &p->xattr_map, &p->xattr_map_size);
49a369
       strcpy (p->target, current_stat_info.link_name);
49a369
 
49a369
-      h = delayed_set_stat_head;
49a369
-      if (h && ! h->after_links
49a369
-	  && strncmp (file_name, h->file_name, h->file_name_len) == 0
49a369
-	  && ISSLASH (file_name[h->file_name_len])
49a369
-	  && (last_component (file_name) == file_name + h->file_name_len + 1))
49a369
+      if ((h = find_direct_ancestor (file_name)) != NULL)
49a369
 	mark_after_links (h);
49a369
 
49a369
       return 0;
49a369
diff --git a/tests/Makefile.am b/tests/Makefile.am
49a369
index bd8d3e8..8d0073f 100644
49a369
--- a/tests/Makefile.am
49a369
+++ b/tests/Makefile.am
49a369
@@ -80,6 +80,7 @@ TESTSUITE_AT = \
49a369
  extrac18.at\
49a369
  extrac19.at\
49a369
  extrac20.at\
49a369
+ extrac21.at\
49a369
  extrac10.at\
49a369
  extrac11.at\
49a369
  extrac12.at\
49a369
diff --git a/tests/extrac21.at b/tests/extrac21.at
49a369
new file mode 100644
49a369
index 0000000..671b101
49a369
--- /dev/null
49a369
+++ b/tests/extrac21.at
49a369
@@ -0,0 +1,59 @@
49a369
+# Test suite for GNU tar.                             -*- Autotest -*-
49a369
+# Copyright 2017 Free Software Foundation, Inc.
49a369
+#
49a369
+# This file is part of GNU tar.
49a369
+#
49a369
+# GNU tar is free software; you can redistribute it and/or modify
49a369
+# it under the terms of the GNU General Public License as published by
49a369
+# the Free Software Foundation; either version 3 of the License, or
49a369
+# (at your option) any later version.
49a369
+#
49a369
+# GNU tar is distributed in the hope that it will be useful,
49a369
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
49a369
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
49a369
+# GNU General Public License for more details.
49a369
+#
49a369
+# You should have received a copy of the GNU General Public License
49a369
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
49a369
+
49a369
+# When called with the --delay-directory-restore option, tar would
49a369
+# in some cases restore the directory permissions too early, before
49a369
+# attempting to replace softlink placeholders with the actual link.
49a369
+# This caused failure if the permissions forbade writing.
49a369
+#
49a369
+# The bug was caused by incorrect assumption about delayed_set_stat
49a369
+# ordering in create_placeholder_file.
49a369
+#
49a369
+# Reported by: Giuseppe Scrivano <gscrivano@gnu.org>
49a369
+# References: <878tfa17ti.fsf@redhat.com>,
49a369
+#    <http://lists.gnu.org/archive/html/bug-tar/2017-11/msg00009.html>
49a369
+
49a369
+AT_SETUP([delay-directory-restore])
49a369
+AT_KEYWORDS([extract extract21 read-only symlink delay-directory-restore])
49a369
+AT_TAR_CHECK([
49a369
+AT_UNPRIVILEGED_PREREQ
49a369
+
49a369
+mkdir a a/b a/c
49a369
+genfile --file a/b/D
49a369
+genfile --file a/c/A
49a369
+cd a/b
49a369
+ln -sf ../c/A
49a369
+cd ../..
49a369
+chmod a-w a/b
49a369
+tar --no-recurs -c -f A.tar a a/b a/b/D a/c a/b/A a/c/A
49a369
+mkdir out
49a369
+tar -C out -v -x -f A.tar --delay-directory-restore
49a369
+],
49a369
+[0],
49a369
+[a/
49a369
+a/b/
49a369
+a/b/D
49a369
+a/c/
49a369
+a/b/A
49a369
+a/c/A
49a369
+],
49a369
+[],[],[],[ustar]) # Testing one format is enough
49a369
+
49a369
+AT_CLEANUP
49a369
+
49a369
+
49a369
diff --git a/tests/testsuite.at b/tests/testsuite.at
49a369
index b540948..9b49a48 100644
49a369
--- a/tests/testsuite.at
49a369
+++ b/tests/testsuite.at
49a369
@@ -237,6 +237,7 @@ m4_include([extrac17.at])
49a369
 m4_include([extrac18.at])
49a369
 m4_include([extrac19.at])
49a369
 m4_include([extrac20.at])
49a369
+m4_include([extrac21.at])
49a369
 
49a369
 m4_include([label01.at])
49a369
 m4_include([label02.at])
49a369
-- 
49a369
2.14.3
49a369