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