Read POSIX multivolume archives split at the header boundary.
* src/common.h (read_header_mode): New enum. (read_header): Change type of the 3rd argument. * src/list.c (read_header): Change type of the 3rd argument. All callers updated. * src/buffer.c (try_new_volume): Allow for volumes split at the extended/ustar header boundary. This is against POSIX specs, but we must be able to read such archives anyway. * tests/multiv07.at: New test case. * tests/Makefile.am: Add multiv07.at * tests/testsuite.at: Likewise. * src/compare.c: Update calls to read_header. * src/delete.c: Likewise. * src/update.c: Likewise.
This commit is contained in:
45
src/buffer.c
45
src/buffer.c
@@ -1165,7 +1165,7 @@ read_header0 (struct tar_stat_info *info)
|
||||
enum read_header rc;
|
||||
|
||||
tar_stat_init (info);
|
||||
rc = read_header (¤t_header, info, false);
|
||||
rc = read_header (¤t_header, info, read_header_auto);
|
||||
if (rc == HEADER_SUCCESS)
|
||||
{
|
||||
set_next_block_after (current_header);
|
||||
@@ -1213,17 +1213,42 @@ try_new_volume ()
|
||||
{
|
||||
case XGLTYPE:
|
||||
{
|
||||
if (!read_header0 (&dummy))
|
||||
return false;
|
||||
tar_stat_init (&dummy);
|
||||
if (read_header (&header, &dummy, read_header_x_global)
|
||||
!= HEADER_SUCCESS_EXTENDED)
|
||||
{
|
||||
ERROR ((0, 0, _("This does not look like a tar archive")));
|
||||
return false;
|
||||
}
|
||||
|
||||
xheader_decode (&dummy); /* decodes values from the global header */
|
||||
tar_stat_destroy (&dummy);
|
||||
if (!real_s_name)
|
||||
{
|
||||
/* We have read the extended header of the first member in
|
||||
this volume. Put it back, so next read_header works as
|
||||
expected. */
|
||||
current_block = record_start;
|
||||
}
|
||||
|
||||
/* The initial global header must be immediately followed by
|
||||
an extended PAX header for the first member in this volume.
|
||||
However, in some cases tar may split volumes in the middle
|
||||
of a PAX header. This is incorrect, and should be fixed
|
||||
in the future versions. In the meantime we must be
|
||||
prepared to correctly list and extract such archives.
|
||||
|
||||
If this happens, the following call to read_header returns
|
||||
HEADER_FAILURE, which is ignored.
|
||||
|
||||
See also tests/multiv07.at */
|
||||
|
||||
switch (read_header (&header, &dummy, read_header_auto))
|
||||
{
|
||||
case HEADER_SUCCESS:
|
||||
set_next_block_after (header);
|
||||
break;
|
||||
|
||||
case HEADER_FAILURE:
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR ((0, 0, _("This does not look like a tar archive")));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
13
src/common.h
13
src/common.h
@@ -548,6 +548,17 @@ enum read_header
|
||||
HEADER_FAILURE /* ill-formed header, or bad checksum */
|
||||
};
|
||||
|
||||
/* Operation mode for read_header: */
|
||||
|
||||
enum read_header_mode
|
||||
{
|
||||
read_header_auto, /* process extended headers automatically */
|
||||
read_header_x_raw, /* return raw extended headers (return
|
||||
HEADER_SUCCESS_EXTENDED) */
|
||||
read_header_x_global /* when POSIX global extended header is read,
|
||||
decode it and return
|
||||
HEADER_SUCCESS_EXTENDED */
|
||||
};
|
||||
extern union block *current_header;
|
||||
extern enum archive_format current_format;
|
||||
extern size_t recent_long_name_blocks;
|
||||
@@ -586,7 +597,7 @@ void print_header (struct tar_stat_info *st, union block *blk,
|
||||
void read_and (void (*do_something) (void));
|
||||
enum read_header read_header (union block **return_block,
|
||||
struct tar_stat_info *info,
|
||||
bool raw_extended_headers);
|
||||
enum read_header_mode m);
|
||||
enum read_header tar_checksum (union block *header, bool silent);
|
||||
void skip_file (off_t size);
|
||||
void skip_member (void);
|
||||
|
||||
@@ -579,7 +579,8 @@ verify_volume (void)
|
||||
while (1)
|
||||
{
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info, false);
|
||||
¤t_stat_info,
|
||||
read_header_auto);
|
||||
|
||||
if (status == HEADER_FAILURE)
|
||||
{
|
||||
@@ -590,7 +591,7 @@ verify_volume (void)
|
||||
counter++;
|
||||
set_next_block_after (current_header);
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
false);
|
||||
read_header_auto);
|
||||
}
|
||||
while (status == HEADER_FAILURE);
|
||||
|
||||
@@ -608,7 +609,8 @@ verify_volume (void)
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info, false);
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
if (status == HEADER_ZERO_BLOCK)
|
||||
break;
|
||||
WARNOPT (WARN_ALONE_ZERO_BLOCK,
|
||||
|
||||
@@ -167,7 +167,7 @@ delete_archive_members (void)
|
||||
{
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info,
|
||||
true);
|
||||
read_header_x_raw);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
@@ -262,7 +262,8 @@ delete_archive_members (void)
|
||||
|
||||
if (current_block == record_end)
|
||||
flush_archive ();
|
||||
status = read_header (¤t_header, ¤t_stat_info, false);
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
|
||||
xheader_decode (¤t_stat_info);
|
||||
|
||||
|
||||
36
src/list.c
36
src/list.c
@@ -78,7 +78,8 @@ read_and (void (*do_something) (void))
|
||||
prev_status = status;
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info, false);
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
switch (status)
|
||||
{
|
||||
case HEADER_STILL_UNREAD:
|
||||
@@ -139,7 +140,8 @@ read_and (void (*do_something) (void))
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info, false);
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
if (status == HEADER_ZERO_BLOCK)
|
||||
break;
|
||||
WARNOPT (WARN_ALONE_ZERO_BLOCK,
|
||||
@@ -282,21 +284,29 @@ tar_checksum (union block *header, bool silent)
|
||||
}
|
||||
|
||||
/* Read a block that's supposed to be a header block. Return its
|
||||
address in "current_header", and if it is good, the file's size
|
||||
and names (file name, link name) in *info.
|
||||
address in *RETURN_BLOCK, and if it is good, the file's size
|
||||
and names (file name, link name) in *INFO.
|
||||
|
||||
Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a
|
||||
block full of zeros (EOF marker).
|
||||
Return one of enum read_header describing the status of the
|
||||
operation.
|
||||
|
||||
If RAW_EXTENDED_HEADERS is nonzero, do not automagically fold the
|
||||
GNU long name and link headers into later headers.
|
||||
The MODE parameter instructs read_header what to do with special
|
||||
header blocks, i.e.: extended POSIX, GNU long name or long link,
|
||||
etc.:
|
||||
|
||||
You must always set_next_block_after(current_header) to skip past
|
||||
read_header_auto process them automatically,
|
||||
read_header_x_raw when a special header is read, return
|
||||
HEADER_SUCCESS_EXTENDED without actually
|
||||
processing the header,
|
||||
read_header_x_global when a POSIX global header is read,
|
||||
decode it and return HEADER_SUCCESS_EXTENDED.
|
||||
|
||||
You must always set_next_block_after(*return_block) to skip past
|
||||
the header which this routine reads. */
|
||||
|
||||
enum read_header
|
||||
read_header (union block **return_block, struct tar_stat_info *info,
|
||||
bool raw_extended_headers)
|
||||
enum read_header_mode mode)
|
||||
{
|
||||
union block *header;
|
||||
union block *header_copy;
|
||||
@@ -333,7 +343,7 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
|| header->header.typeflag == XGLTYPE
|
||||
|| header->header.typeflag == SOLARIS_XHDTYPE)
|
||||
{
|
||||
if (raw_extended_headers)
|
||||
if (mode == read_header_x_raw)
|
||||
return HEADER_SUCCESS_EXTENDED;
|
||||
else if (header->header.typeflag == GNUTYPE_LONGNAME
|
||||
|| header->header.typeflag == GNUTYPE_LONGLINK)
|
||||
@@ -405,6 +415,8 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
OFF_FROM_HEADER (header->header.size));
|
||||
xheader_decode_global (&xhdr);
|
||||
xheader_destroy (&xhdr);
|
||||
if (mode == read_header_x_global)
|
||||
return HEADER_SUCCESS_EXTENDED;
|
||||
}
|
||||
|
||||
/* Loop! */
|
||||
@@ -1397,7 +1409,7 @@ test_archive_label ()
|
||||
name_gather ();
|
||||
|
||||
open_archive (ACCESS_READ);
|
||||
if (read_header (¤t_header, ¤t_stat_info, false)
|
||||
if (read_header (¤t_header, ¤t_stat_info, read_header_auto)
|
||||
== HEADER_SUCCESS)
|
||||
{
|
||||
char *s = NULL;
|
||||
|
||||
@@ -115,7 +115,8 @@ update_archive (void)
|
||||
while (!found_end)
|
||||
{
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info, false);
|
||||
¤t_stat_info,
|
||||
read_header_auto);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
|
||||
@@ -105,6 +105,7 @@ TESTSUITE_AT = \
|
||||
multiv04.at\
|
||||
multiv05.at\
|
||||
multiv06.at\
|
||||
multiv07.at\
|
||||
old.at\
|
||||
options.at\
|
||||
options02.at\
|
||||
|
||||
43
tests/multiv07.at
Normal file
43
tests/multiv07.at
Normal file
@@ -0,0 +1,43 @@
|
||||
# Test suite for GNU tar. -*- Autotest -*-
|
||||
# Copyright (C) 2010 Free Software Foundation, Inc.
|
||||
|
||||
# This program 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, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program 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 creating POSIX multivolume archives, tar may in
|
||||
# some cases write an extended header at the end of one volume, and
|
||||
# the corresponding ustar header at the beginning of the next volume.
|
||||
# Such archives do not fully comply with the POSIX specs, but tar must
|
||||
# be able to read them anyway. This is what this script tests.
|
||||
#
|
||||
# See function try_new_volume, in file src/buffer.c near line 1227
|
||||
# for additional details.
|
||||
|
||||
AT_SETUP([volumes split at an extended header])
|
||||
AT_KEYWORDS([multivolume multiv multiv07 xsplit])
|
||||
|
||||
AT_CHECK([
|
||||
AT_XFAIL_IF(test -f $[]XFAILFILE)
|
||||
AT_TARBALL_PREREQ([xsplit-1.tar],[0e008c84c517e48fbf23ca6a7033cde6])
|
||||
AT_TARBALL_PREREQ([xsplit-2.tar],[03150b9852d285458f43734e9e0b9a45])
|
||||
|
||||
cd $TEST_DATA_DIR
|
||||
tar -t -M -fxsplit-1.tar -fxsplit-2.tar
|
||||
],
|
||||
[0],
|
||||
[Archive volumes split at an extended header Volume 1
|
||||
foo
|
||||
bar
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
@@ -69,7 +69,7 @@ m4_define([AT_TARBALL_PREREQ],[
|
||||
test -z "$[]TEST_DATA_DIR" && AT_SKIP_TEST
|
||||
tarball_prereq $1 $2 $[]TEST_DATA_DIR $[]TEST_DATA_URL || AT_SKIP_TEST])
|
||||
|
||||
dnl AT_TARBALL_PREREQ(tarball, md5sum) - Same for star testfiles
|
||||
dnl AT_STAR_PREREQ(tarball, md5sum) - Same for star testfiles
|
||||
m4_define([AT_STAR_PREREQ],[
|
||||
test -z "$STAR_TESTSCRIPTS" && AT_SKIP_TEST
|
||||
tarball_prereq $1 $2 $[]STAR_TESTSCRIPTS $[]STAR_DATA_URL || AT_SKIP_TEST
|
||||
@@ -191,6 +191,7 @@ m4_include([multiv03.at])
|
||||
m4_include([multiv04.at])
|
||||
m4_include([multiv05.at])
|
||||
m4_include([multiv06.at])
|
||||
m4_include([multiv07.at])
|
||||
|
||||
m4_include([old.at])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user