]> git.ipfire.org Git - thirdparty/tar.git/commitdiff
Fix restoring permissions of intermediate directories with --skip-old-files
authorSergey Poznyakoff <gray@gnu.org>
Fri, 14 Mar 2025 12:40:36 +0000 (14:40 +0200)
committerSergey Poznyakoff <gray@gnu.org>
Fri, 14 Mar 2025 13:07:27 +0000 (15:07 +0200)
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.

src/extract.c
tests/Makefile.am
tests/extrac28.at [new file with mode: 0644]
tests/multiv04.at
tests/testsuite.at

index 5fc2d9cf7c9ab41c063d994a6930afed3b9edd80..a6bc9d30998b6e221c255dfc91a2a91e419e9355 100644 (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;
index a28043477e8423e26a6b2cac62cb80b1d33e64e5..03ae97675d4170641e7944580bacf0a05c05beb4 100644 (file)
@@ -137,6 +137,7 @@ TESTSUITE_AT = \
  extrac25.at\
  extrac26.at\
  extrac27.at\
+ extrac28.at\
  filerem01.at\
  filerem02.at\
  grow.at\
diff --git a/tests/extrac28.at b/tests/extrac28.at
new file mode 100644 (file)
index 0000000..cecbd35
--- /dev/null
@@ -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
+
+
index 25b840d4d5c8dc821bf1c696d695c8ab87a42779..a21e522257b4fc407f244cc22173cb4a866736c3 100644 (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
index 80706b6c8fbacc7d19d481614cbcb2d5b8c33a8a..ff38dda64dbf159985c0fcf0da6baa66f2a4850f 100644 (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])