]> Dogcows Code - chaz/tar/commitdiff
Read POSIX multivolume archives split at the header boundary.
authorSergey Poznyakoff <gray@gnu.org.ua>
Mon, 25 Jan 2010 15:03:28 +0000 (17:03 +0200)
committerSergey Poznyakoff <gray@gnu.org.ua>
Mon, 25 Jan 2010 15:03:28 +0000 (17:03 +0200)
* 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.

src/buffer.c
src/common.h
src/compare.c
src/delete.c
src/list.c
src/update.c
tests/Makefile.am
tests/multiv07.at [new file with mode: 0644]
tests/testsuite.at

index f419dd7bcb357232ca76f254a78417c0a6794142..b47b773ee8ea65c151db1fc8293e52ca8e58a433 100644 (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;
       }
 
index 271211592eaa395b0d9a05ca9a493da596410a45..d2de528e0e94809eef7fa0efbb1e3a8975faa3ad 100644 (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);
index 9385d40940c2de3fc75cc55ba069e999282c0139..f4e92da58f0fa6cf81825ba9c09fa2f502700522 100644 (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,
index 8f729ba20a3fe4fe56984e0fe33e434b2c63f416..75dc01f8961f532bcccd87e18924236f58e01690 100644 (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);
 
index 5a341aad2c81d5deb1505ff7f48cf2d515443749..716c0b4230a3e50c07dc7ec98afbf6d6aa56381f 100644 (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;
index 468c6458d2eea904583db52bc914263d3ed2906f..b0151759d9b0430b34086d013a29efae722d99ce 100644 (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)
        {
index 1b3b2c1b991113d89979484382836f1dbf895922..e9b753c50a17a0507ff43bf06897f37a6823c357 100644 (file)
@@ -105,6 +105,7 @@ TESTSUITE_AT = \
  multiv04.at\
  multiv05.at\
  multiv06.at\
+ multiv07.at\
  old.at\
  options.at\
  options02.at\
diff --git a/tests/multiv07.at b/tests/multiv07.at
new file mode 100644 (file)
index 0000000..ff965d6
--- /dev/null
@@ -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
index 6dbd6b3ba3881f18f43f4acefa7ab3d4e5491fc3..f581071a39e23848c19ed901d4e3bc3596ff77ed 100644 (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])
 
This page took 0.03012 seconds and 4 git commands to generate.