mirror of
https://git.savannah.gnu.org/git/tar.git
synced 2026-04-26 11:30:39 +00:00
Fix more -t/-x discrepancies
Problem reported by Guillermo de Angel in:
https://lists.gnu.org/r/bug-tar/2026-03/msg00007.html
* THANKS: Add him, and sort.
* src/extract.c (extract_dir, extract_file):
* src/incremen.c (purge_directory):
Do not call skip_member, as the caller now does that, and does it
more reliably.
* src/extract.c (extract_file):
Mark file as skipped when we’ve read it.
(extract_archive): Always call skip_member after extracting,
as it suppresses the skip as needed.
* src/incremen.c (try_purge_directory): Remove; no longer
needed. Move internals to purge_directory.
* src/list.c (read_header): Do not treat LNKTYPE header as having
size zero, as it can be nonzero (e.g., ‘pax -o linkdata’).
Set info->skipped field according to how the header was read.
(member_is_dir): Remove; no longer needed.
(skim_member): Skip directory data too, unless it’s already been
skipped (i.e., read).
* tests/extrac32.at: New file.
* tests/Makefile.am (TESTSUITE_AT):
* tests/testsuite.at:
Add it.
* tests/skipdir.at (skip directory members):
Fix test to match the correct behavior.
This fixes a bug introduced in commit
b009124ffd
dated 2025-05-12 17:17:21 +0300.
This commit is contained in:
@@ -1132,7 +1132,7 @@ safe_dir_mode (struct stat const *st)
|
||||
/* Extractor functions for various member types */
|
||||
|
||||
static bool
|
||||
extract_dir (char *file_name, char typeflag)
|
||||
extract_dir (char *file_name, char UNNAMED (typeflag))
|
||||
{
|
||||
int status;
|
||||
mode_t mode;
|
||||
@@ -1157,8 +1157,6 @@ extract_dir (char *file_name, char typeflag)
|
||||
if (incremental_option)
|
||||
/* Read the entry and delete files that aren't listed in the archive. */
|
||||
purge_directory (file_name);
|
||||
else if (typeflag == GNUTYPE_DUMPDIR)
|
||||
skip_member ();
|
||||
|
||||
mode = safe_dir_mode (¤t_stat_info.stat);
|
||||
|
||||
@@ -1338,10 +1336,7 @@ extract_file (char *file_name, char typeflag)
|
||||
{
|
||||
fd = sys_exec_command (file_name, 'f', ¤t_stat_info);
|
||||
if (fd < 0)
|
||||
{
|
||||
skip_member ();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1362,7 +1357,6 @@ extract_file (char *file_name, char typeflag)
|
||||
= maybe_recoverable (file_name, true, &interdir_made);
|
||||
if (recover != RECOVER_OK)
|
||||
{
|
||||
skip_member ();
|
||||
if (recover == RECOVER_SKIP)
|
||||
return true;
|
||||
open_error (file_name);
|
||||
@@ -1409,6 +1403,7 @@ extract_file (char *file_name, char typeflag)
|
||||
}
|
||||
|
||||
skim_file (size, false);
|
||||
current_stat_info.skipped = true;
|
||||
|
||||
mv_end ();
|
||||
|
||||
@@ -1921,15 +1916,9 @@ extract_archive (void)
|
||||
|
||||
tar_extractor_t fun = prepare_to_extract (current_stat_info.file_name,
|
||||
typeflag);
|
||||
if (fun)
|
||||
{
|
||||
if (fun (current_stat_info.file_name, typeflag))
|
||||
return;
|
||||
}
|
||||
else
|
||||
skip_member ();
|
||||
|
||||
if (backup_option)
|
||||
bool ok = fun && fun (current_stat_info.file_name, typeflag);
|
||||
skip_member ();
|
||||
if (!ok && backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
|
||||
|
||||
@@ -1621,8 +1621,8 @@ dumpdir_ok (char *dumpdir)
|
||||
|
||||
/* Examine the directories under directory_name and delete any
|
||||
files that were not there at the time of the back-up. */
|
||||
static bool
|
||||
try_purge_directory (char const *directory_name)
|
||||
void
|
||||
purge_directory (char const *directory_name)
|
||||
{
|
||||
char *current_dir;
|
||||
char *cur, *arc, *p;
|
||||
@@ -1630,18 +1630,18 @@ try_purge_directory (char const *directory_name)
|
||||
struct dumpdir *dump;
|
||||
|
||||
if (!is_dumpdir (¤t_stat_info))
|
||||
return false;
|
||||
return;
|
||||
|
||||
current_dir = tar_savedir (directory_name, false);
|
||||
|
||||
if (!current_dir)
|
||||
/* The directory doesn't exist now. It'll be created. In any
|
||||
case, we don't have to delete any files out of it. */
|
||||
return false;
|
||||
return;
|
||||
|
||||
/* Verify if dump directory is sane */
|
||||
if (!dumpdir_ok (current_stat_info.dumpdir))
|
||||
return false;
|
||||
return;
|
||||
|
||||
/* Process renames */
|
||||
for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
|
||||
@@ -1661,7 +1661,7 @@ try_purge_directory (char const *directory_name)
|
||||
quote (temp_stub));
|
||||
free (temp_stub);
|
||||
free (current_dir);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (*arc == 'R')
|
||||
@@ -1695,7 +1695,7 @@ try_purge_directory (char const *directory_name)
|
||||
free (current_dir);
|
||||
/* FIXME: Make sure purge_directory(dst) will return
|
||||
immediately */
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1749,14 +1749,6 @@ try_purge_directory (char const *directory_name)
|
||||
dumpdir_free (dump);
|
||||
|
||||
free (current_dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
purge_directory (char const *directory_name)
|
||||
{
|
||||
if (!try_purge_directory (directory_name))
|
||||
skip_member ();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
48
src/list.c
48
src/list.c
@@ -423,20 +423,15 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
if ((status = tar_checksum (header, false)) != HEADER_SUCCESS)
|
||||
break;
|
||||
|
||||
/* Good block. Decode file size and return. */
|
||||
|
||||
if (header->header.typeflag == LNKTYPE)
|
||||
info->stat.st_size = 0; /* links 0 size on tape */
|
||||
else
|
||||
info->stat.st_size = OFF_FROM_HEADER (header->header.size);
|
||||
if (info->stat.st_size < 0)
|
||||
{
|
||||
info->stat.st_size = OFF_FROM_HEADER (header->header.size);
|
||||
if (info->stat.st_size < 0)
|
||||
{
|
||||
status = HEADER_FAILURE;
|
||||
break;
|
||||
}
|
||||
status = HEADER_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
info->skipped = false;
|
||||
|
||||
if (header->header.typeflag == GNUTYPE_LONGNAME
|
||||
|| header->header.typeflag == GNUTYPE_LONGLINK
|
||||
|| header->header.typeflag == XHDTYPE
|
||||
@@ -493,11 +488,15 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
}
|
||||
|
||||
*bp = '\0';
|
||||
info->skipped = true;
|
||||
}
|
||||
else if (header->header.typeflag == XHDTYPE
|
||||
|| header->header.typeflag == SOLARIS_XHDTYPE)
|
||||
xheader_read (&info->xhdr, header,
|
||||
OFF_FROM_HEADER (header->header.size));
|
||||
{
|
||||
xheader_read (&info->xhdr, header,
|
||||
OFF_FROM_HEADER (header->header.size));
|
||||
info->skipped = true;
|
||||
}
|
||||
else if (header->header.typeflag == XGLTYPE)
|
||||
{
|
||||
struct xheader xhdr;
|
||||
@@ -511,6 +510,7 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
OFF_FROM_HEADER (header->header.size));
|
||||
xheader_decode_global (&xhdr);
|
||||
xheader_destroy (&xhdr);
|
||||
info->skipped = true;
|
||||
if (mode == read_header_x_global)
|
||||
{
|
||||
status = HEADER_SUCCESS_EXTENDED;
|
||||
@@ -1412,23 +1412,6 @@ skip_member (void)
|
||||
skim_member (false);
|
||||
}
|
||||
|
||||
static bool
|
||||
member_is_dir (struct tar_stat_info *info, char typeflag)
|
||||
{
|
||||
switch (typeflag) {
|
||||
case AREGTYPE:
|
||||
case REGTYPE:
|
||||
case CONTTYPE:
|
||||
return info->had_trailing_slash;
|
||||
|
||||
case DIRTYPE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip the current member in the archive.
|
||||
If MUST_COPY, always copy instead of skipping. */
|
||||
void
|
||||
@@ -1436,18 +1419,17 @@ skim_member (bool must_copy)
|
||||
{
|
||||
if (!current_stat_info.skipped)
|
||||
{
|
||||
bool is_dir = member_is_dir (¤t_stat_info,
|
||||
current_header->header.typeflag);
|
||||
set_next_block_after (current_header);
|
||||
|
||||
mv_begin_read (¤t_stat_info);
|
||||
|
||||
if (current_stat_info.is_sparse)
|
||||
sparse_skim_file (¤t_stat_info, must_copy);
|
||||
else if (!is_dir)
|
||||
else
|
||||
skim_file (current_stat_info.stat.st_size, must_copy);
|
||||
|
||||
mv_end ();
|
||||
current_stat_info.skipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user