Problem reported by Rob Schmidt in:
https://lists.gnu.org/r/bug-tar/2026-08/msg00010.html
fixup_delayed_set_stat changed the name of an entry in
delayed_set_stat_table, which is hashed on that name, without moving
the entry to the new name's bucket.  A later lookup of the new name
missed it and made a second entry, so the older entry's status,
including its time stamps, was applied to the renamed directory last.
When the entry was later freed, hash_remove looked for it in the new
name's bucket, so the table kept pointing to freed memory.
The bug was introduced by commit
a5afb367655ac6dcbc79ba7802a2479044184b38 (2023-08-21).
* src/extract.c (fixup_delayed_set_stat): Remove the entry from
delayed_set_stat_table before renaming it, and insert it again after.
* tests/incr11.at: Check the mtime of the renamed directory.
---
 src/extract.c   | 3 +++
 tests/incr11.at | 6 ++++++
 2 files changed, 9 insertions(+)

diff --git a/src/extract.c b/src/extract.c
index 2bb0e80f..244b91f2 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -762,9 +762,12 @@ fixup_delayed_set_stat (char const *src, char const *dst)
       if (chdir_current == data->change_dir
          && streq (data->file_name, src))
        {
+         hash_remove (delayed_set_stat_table, data);
          free (data->file_name);
          data->file_name = xstrdup (dst);
          data->file_name_len = strlen (dst);
+         if (! hash_insert (delayed_set_stat_table, data))
+           xalloc_die ();
          return;
        }
     }
diff --git a/tests/incr11.at b/tests/incr11.at
index c02c2b9e..4e1da5a0 100644
--- a/tests/incr11.at
+++ b/tests/incr11.at
@@ -21,6 +21,8 @@
 # Reported by: Alex Efros <[email protected]>
 # References: <[email protected]>
 #             http://lists.gnu.org/archive/html/bug-tar/2015-11/msg00033.html
+# The renamed directory gets its mtime from the second archive.
+# References: https://lists.gnu.org/r/bug-tar/2026-08/msg00010.html

 AT_SETUP([concatenated incremental archives (renames)])
 AT_KEYWORDS([incremental concat cat incr11 chdir])
@@ -29,10 +31,13 @@ AT_TAR_CHECK([
 AT_CHECK_TIMESTAMP
 AT_SORT_PREREQ
 AT_TAR_MKHIER([data/dir],[file])
+touch -d 2000-01-01T00:00:00 data/dir >/dev/null 2>&1 || AT_SKIP_TEST
 decho Level 0
 tar -cvf full.tar -g snap -C data .
 decho Level 1
 mv data/dir data/dir2
+touch data/dir2
+genfile --stat=mtime data/dir2 > ts
 tar -cvf incr.tar -g snap -C data .
 decho Concat
 cp full.tar full2.tar
@@ -42,6 +47,7 @@ mkdir out
 tar -xvf full2.tar -g /dev/null -C out
 decho List
 find out | sort
+genfile --stat=mtime out/dir2 | diff ts -
 ],
 [0],
 [Level 0
-- 
2.53.0

Attachment: 0001-tar-rehash-delayed_set_stat-entry-on-rename.patch
Description: Binary data

Reply via email to