Fix restoring permissions of intermediate directories with --skip-old-files

Detailed bug report: https://savannah.gnu.org/bugs/index.php?66774

* src/extract.c (update_interdir_set_stat): New function.
(extract_dir): If the directory already exists, check if it
has been created as intermediate directory earlier.  If so,
update its delayed_set_stat data from archive.

* tests/Makefile.am: Add new testcase.
* tests/testsuite.at: Add new testcase.
* tests/extrac28.at: New file.
This commit is contained in:
Sergey Poznyakoff
2025-03-14 14:40:36 +02:00
parent 55ecb28315
commit cd1f6624f7
5 changed files with 80 additions and 1 deletions

View File

@@ -616,6 +616,35 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
mark_after_links (data);
}
/* If DIR is an intermediate directory created earlier, update its
metadata from the current_stat_info and clear, its intermediate
status and return true. Return false otherwise.
*/
static bool
update_interdir_set_stat (char const *dir)
{
if (delayed_set_stat_table)
{
struct delayed_set_stat key, *data;
key.file_name = (char *) dir;
data = hash_lookup (delayed_set_stat_table, &key);
if (data && data->interdir)
{
data->dev = current_stat_info.stat.st_dev;
data->ino = current_stat_info.stat.st_ino;
data->mode = current_stat_info.stat.st_mode;
data->uid = current_stat_info.stat.st_uid;
data->gid = current_stat_info.stat.st_gid;
data->atime = current_stat_info.atime;
data->mtime = current_stat_info.mtime;
data->interdir = false;
return true;
}
}
return false;
}
/* Update the delayed_set_stat info for an intermediate directory
created within the file name of DIR. The intermediate directory turned
out to be the same as this directory, e.g. due to ".." or symbolic
@@ -1158,6 +1187,8 @@ extract_dir (char *file_name, char typeflag)
}
}
}
else if (update_interdir_set_stat (file_name))
return true;
else if (old_files_option == UNLINK_FIRST_OLD_FILES)
{
status = 0;

View File

@@ -137,6 +137,7 @@ TESTSUITE_AT = \
extrac25.at\
extrac26.at\
extrac27.at\
extrac28.at\
filerem01.at\
filerem02.at\
grow.at\

46
tests/extrac28.at Normal file
View File

@@ -0,0 +1,46 @@
# Test suite for GNU tar. -*- autotest -*-
# Copyright 2025 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 <http://www.gnu.org/licenses/>.
# Description: When invoked with --skip-old-files, tar 1.35 failed
# to restore mode of a directory member, if the directory was previously
# created as an intermediate (i.e. member ordering in the archive was
# inverted).
# References: https://savannah.gnu.org/bugs/index.php?66774
AT_SETUP([extract intermediates with --skip-old-files])
AT_KEYWORDS([extract extrac28])
AT_TAR_CHECK([
mkdir dir
chmod 700 dir
touch dir/one dir/two
tar cf archive.tar dir/one
tar rf archive.tar --exclude dir/one dir
tar tf archive.tar
rm -rf dir
tar xf archive.tar --skip-old-files --delay-directory-restore
genfile --stat=mode.777 dir
],
[0],
[dir/one
dir/
dir/two
700
])
AT_CLEANUP

View File

@@ -25,7 +25,7 @@
# Test idea:
# 1. Create a listed-incremental archive of a directory containing
# a cetrain number of zero-length files.
# a certain number of zero-length files.
# 2. Using the same snapshot file, create a *multivolume* listed-incremental
# archive. Number of files created in the directory and volume size should
# be selected so that the first volume ends in the midst of the directory

View File

@@ -354,6 +354,7 @@ m4_include([extrac24.at])
m4_include([extrac25.at])
m4_include([extrac26.at])
m4_include([extrac27.at])
m4_include([extrac28.at])
m4_include([backup01.at])