Przeglądaj źródła

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.
Sergey Poznyakoff 15 lat temu
rodzic
commit
c3fa22fc80
9 zmienionych plików z 127 dodań i 30 usunięć
  1. 35 10
      src/buffer.c
  2. 12 1
      src/common.h
  3. 5 3
      src/compare.c
  4. 3 2
      src/delete.c
  5. 24 12
      src/list.c
  6. 2 1
      src/update.c
  7. 1 0
      tests/Makefile.am
  8. 43 0
      tests/multiv07.at
  9. 2 1
      tests/testsuite.at

+ 35 - 10
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 (&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;
       }
 

+ 12 - 1
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);

+ 5 - 3
src/compare.c

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

+ 3 - 2
src/delete.c

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

+ 24 - 12
src/list.c

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

+ 2 - 1
src/update.c

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

+ 1 - 0
tests/Makefile.am

@@ -105,6 +105,7 @@ TESTSUITE_AT = \
  multiv04.at\
  multiv05.at\
  multiv06.at\
+ multiv07.at\
  old.at\
  options.at\
  options02.at\

+ 43 - 0
tests/multiv07.at

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

+ 2 - 1
tests/testsuite.at

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