* 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.
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);
{
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;
}
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;
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);
while (1)
{
enum read_header status = read_header (¤t_header,
- ¤t_stat_info, false);
+ ¤t_stat_info,
+ read_header_auto);
if (status == HEADER_FAILURE)
{
counter++;
set_next_block_after (current_header);
status = read_header (¤t_header, ¤t_stat_info,
- false);
+ read_header_auto);
}
while (status == HEADER_FAILURE);
{
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,
{
enum read_header status = read_header (¤t_header,
¤t_stat_info,
- true);
+ read_header_x_raw);
switch (status)
{
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);
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:
{
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,
}
/* 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;
|| 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)
OFF_FROM_HEADER (header->header.size));
xheader_decode_global (&xhdr);
xheader_destroy (&xhdr);
+ if (mode == read_header_x_global)
+ return HEADER_SUCCESS_EXTENDED;
}
/* Loop! */
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;
while (!found_end)
{
enum read_header status = read_header (¤t_header,
- ¤t_stat_info, false);
+ ¤t_stat_info,
+ read_header_auto);
switch (status)
{
multiv04.at\
multiv05.at\
multiv06.at\
+ multiv07.at\
old.at\
options.at\
options02.at\
--- /dev/null
+# 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
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
m4_include([multiv04.at])
m4_include([multiv05.at])
m4_include([multiv06.at])
+m4_include([multiv07.at])
m4_include([old.at])