Fix spurious "Not found in archive" errors.

* src/names.c (namelist_match_from): New function.
(namelist_match): Rewrite as a wrapper over it.
(register_match): New function.
(name_match)" Update all possible matches in the name list.

* tests/extrac29.at: New test.
* tests/Makefile.am: Add new test.
* tests/testsuite.at: Likewise.
This commit is contained in:
Sergey Poznyakoff
2025-05-14 15:17:09 +03:00
parent 9324b472b0
commit 4303066730
4 changed files with 91 additions and 11 deletions

View File

@@ -1280,11 +1280,9 @@ add_starting_file (char const *file_name)
/* Find a match for FILE_NAME in the name list. If EXACT is true,
look for exact match (no wildcards). */
static struct name *
namelist_match (char const *file_name, bool exact)
namelist_match_from (struct name *p, char const *file_name, bool exact)
{
struct name *p;
for (p = namelist; p; p = p->next)
for (; p; p = p->next)
{
if (p->name[0]
&& (exact ? !p->is_wildcard : true)
@@ -1295,6 +1293,12 @@ namelist_match (char const *file_name, bool exact)
return NULL;
}
static COMMON_INLINE struct name *
namelist_match (char const *file_name, bool exact)
{
return namelist_match_from (namelist, file_name, exact);
}
void
remname (struct name *name)
{
@@ -1311,6 +1315,15 @@ remname (struct name *name)
nametail = name->prev;
}
/* Update CURSOR to remember that it matched FILE_NAME. */
static COMMON_INLINE void
register_match (struct name *cursor, const char *file_name)
{
if (!(ISSLASH (file_name[cursor->length]) && recursion_option)
|| cursor->found_count == 0)
cursor->found_count++;
}
/* Return true if and only if name FILE_NAME (from an archive) matches any
name from the namelist. */
bool
@@ -1344,12 +1357,33 @@ name_match (const char *file_name)
}
if (cursor)
{
if (!(ISSLASH (file_name[cursor->length]) && recursion_option)
|| cursor->found_count == 0)
cursor->found_count++; /* remember it matched */
chdir_do (cursor->change_dir);
/* We got a match. */
return isfound (cursor);
/*
* Found the first match. It is still possible that the namelist
* contains other entries that also match that filename. Find all
* such entries and update their found_count to avoid spurious
* "Not found in archive" errors at the end of the run.
*
* FIXME: name matching logic should be rewritten to avoid O(N^2)
* time complexity.
*
* On the first entry to the loop below, the first match itself is
* updated.
*/
struct name *found = NULL;
while (cursor)
{
register_match (cursor, file_name);
if (!found && isfound (cursor))
found = cursor;
cursor = namelist_match_from (cursor->next, file_name, false);
}
if (!found)
return false;
/* We got a match. */
chdir_do (found->change_dir);
return true;
}
/* Filename from archive not found in namelist. If we have the whole

View File

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

44
tests/extrac29.at Normal file
View File

@@ -0,0 +1,44 @@
# 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 listing or extracting from an archive a given subset
# of members, only the first matching argument was counted. This led
# to spurious "Not found in archive" errors. For example, tar 1.35
# when used in the test below, outputs
#
# tar: dir/a1: Not found in archive
# tar: dir/b1: Not found in archive
#
# References: https://savannah.gnu.org/bugs/?67114
AT_SETUP([Keeping track of matched names])
AT_KEYWORDS([extract extrac29])
AT_TAR_CHECK([
AT_SORT_PREREQ
mkdir dir
touch dir/a1 dir/b1
tar cf a.tar dir
rm -rf dir
tar -xvf a.tar dir dir/a1 dir/b1 | sort
],
[0],
[dir/
dir/a1
dir/b1
])
AT_CLEANUP

View File

@@ -94,7 +94,7 @@ m4_define([AT_SORT_PREREQ],[
test -z "`sort < /dev/null 2>&1`" || AT_SKIP_TEST
])
dnl AT_UNPRIVILEGED_PREREQ - Skip test if running at root privileges
dnl AT_UNPRIVILEGED_PREREQ - Skip test if running with root privileges
m4_define([AT_UNPRIVILEGED_PREREQ],[
echo "test" > $[]$
chmod 0 $[]$
@@ -355,6 +355,7 @@ m4_include([extrac25.at])
m4_include([extrac26.at])
m4_include([extrac27.at])
m4_include([extrac28.at])
m4_include([extrac29.at])
m4_include([backup01.at])