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:
Sergey Poznyakoff
2010-01-25 17:03:28 +02:00
parent 706bd01915
commit c3fa22fc80
9 changed files with 127 additions and 30 deletions

View File

@@ -1165,7 +1165,7 @@ read_header0 (struct tar_stat_info *info)
enum read_header rc;
tar_stat_init (info);
rc = read_header (&current_header, info, false);
rc = read_header (&current_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;
}

View File

@@ -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);

View File

@@ -579,7 +579,8 @@ verify_volume (void)
while (1)
{
enum read_header status = read_header (&current_header,
&current_stat_info, false);
&current_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 (&current_header, &current_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 (&current_header, &current_stat_info, false);
status = read_header (&current_header, &current_stat_info,
read_header_auto);
if (status == HEADER_ZERO_BLOCK)
break;
WARNOPT (WARN_ALONE_ZERO_BLOCK,

View File

@@ -167,7 +167,7 @@ delete_archive_members (void)
{
enum read_header status = read_header (&current_header,
&current_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 (&current_header, &current_stat_info, false);
status = read_header (&current_header, &current_stat_info,
read_header_auto);
xheader_decode (&current_stat_info);

View File

@@ -78,7 +78,8 @@ read_and (void (*do_something) (void))
prev_status = status;
tar_stat_destroy (&current_stat_info);
status = read_header (&current_header, &current_stat_info, false);
status = read_header (&current_header, &current_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 (&current_header, &current_stat_info, false);
status = read_header (&current_header, &current_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 (&current_header, &current_stat_info, false)
if (read_header (&current_header, &current_stat_info, read_header_auto)
== HEADER_SUCCESS)
{
char *s = NULL;

View File

@@ -115,7 +115,8 @@ update_archive (void)
while (!found_end)
{
enum read_header status = read_header (&current_header,
&current_stat_info, false);
&current_stat_info,
read_header_auto);
switch (status)
{

View File

@@ -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
View 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

View File

@@ -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])