Browse Source

Fix listing of volume labels (in particular in PAX archives).

* src/buffer.c (match_volume_label): Call set_volume_label.
(check_label_pattern): Get label string
as argument.
(match_volume_label): Handle volume labels stored in
global PAX headers.
* src/common.c (print_header,read_header): Change signature.
(read_header_primitive): Remove prototype.
* src/list.c (recent_global_header): New static.
(list_archive): Always print volume labels.
(read_header_primitive): Remove.
(read_header): Change the signature (all callers updated)
Save the recent global header.
(volume_label_printed): New static.
(simple_print_header): New function (ex-print_header).
(print_header): Change the signature (all callers updated).
For POSIX formats, print first volume header (if set).
* src/xheader.c (xheader_write_global): Write the data
accumulated in xhdr->stk even if keyword_global_override_list
is empty.
(xheader_read): On unexpected EOF, report error instead of
coredumping.
(XHDR_PROTECTED, XHDR_GLOBAL): New defines.
(struct xhdr_tab): Remove `protected' with `flags'. All uses updated.
(decg): If XHDR_GLOBAL bit is set, call the keyword's decode
method instead of adding it to `kwl'.

* src/compare.c: Update calls to read_header.
* src/create.c: Likewise.
* src/delete.c: Likewise.
* src/update.c: Likewise.
* src/extract.c: Likewise.
(extract_volhdr): Do not print "Reading <label>" statement, because
it is inconsistent: it is not printed if the volume begins with a
member continued from the previous volume.

* tests/label01.at: New testcase.
* tests/label02.at: New testcase.
* tests/Makefile.am, tests/testsuite.at: Add new testcases.
Sergey Poznyakoff 15 years ago
parent
commit
166b7c7d02
13 changed files with 271 additions and 110 deletions
  1. 41 14
      src/buffer.c
  2. 8 5
      src/common.h
  3. 7 5
      src/compare.c
  4. 3 3
      src/create.c
  5. 5 3
      src/delete.c
  6. 2 4
      src/extract.c
  7. 62 31
      src/list.c
  8. 3 2
      src/update.c
  9. 62 43
      src/xheader.c
  10. 2 0
      tests/Makefile.am
  11. 35 0
      tests/label01.at
  12. 38 0
      tests/label02.at
  13. 3 0
      tests/testsuite.at

+ 41 - 14
src/buffer.c

@@ -1,7 +1,8 @@
 /* Buffer management for tar.
 /* Buffer management for tar.
 
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+   Foundation, Inc.
 
 
    Written by John Gilmore, on 1985-08-25.
    Written by John Gilmore, on 1985-08-25.
 
 
@@ -1167,7 +1168,7 @@ read_header0 (struct tar_stat_info *info)
   enum read_header rc;
   enum read_header rc;
 
 
   tar_stat_init (info);
   tar_stat_init (info);
-  rc = read_header_primitive (false, info);
+  rc = read_header (&current_header, info, false);
   if (rc == HEADER_SUCCESS)
   if (rc == HEADER_SUCCESS)
     {
     {
       set_next_block_after (current_header);
       set_next_block_after (current_header);
@@ -1312,20 +1313,17 @@ try_new_volume ()
 }
 }
 
 
 
 
-/* Check the LABEL block against the volume label, seen as a globbing
+/* Check LABEL against the volume label, seen as a globbing
    pattern.  Return true if the pattern matches.  In case of failure,
    pattern.  Return true if the pattern matches.  In case of failure,
    retry matching a volume sequence number before giving up in
    retry matching a volume sequence number before giving up in
    multi-volume mode.  */
    multi-volume mode.  */
 static bool
 static bool
-check_label_pattern (union block *label)
+check_label_pattern (const char *label)
 {
 {
   char *string;
   char *string;
   bool result;
   bool result;
 
 
-  if (! memchr (label->header.name, '\0', sizeof label->header.name))
-    return false;
-
-  if (fnmatch (volume_label_option, label->header.name, 0) == 0)
+  if (fnmatch (volume_label_option, label, 0) == 0)
     return true;
     return true;
 
 
   if (!multi_volume_option)
   if (!multi_volume_option)
@@ -1335,7 +1333,7 @@ check_label_pattern (union block *label)
                     + sizeof VOLUME_LABEL_APPEND + 1);
                     + sizeof VOLUME_LABEL_APPEND + 1);
   strcpy (string, volume_label_option);
   strcpy (string, volume_label_option);
   strcat (string, VOLUME_LABEL_APPEND);
   strcat (string, VOLUME_LABEL_APPEND);
-  result = fnmatch (string, label->header.name, 0) == 0;
+  result = fnmatch (string, label, 0) == 0;
   free (string);
   free (string);
   return result;
   return result;
 }
 }
@@ -1345,14 +1343,43 @@ check_label_pattern (union block *label)
 static void
 static void
 match_volume_label (void)
 match_volume_label (void)
 {
 {
-  union block *label = find_next_block ();
-
-  if (!label)
+  if (!volume_label)
+    {
+      union block *label = find_next_block ();
+  
+      if (!label)
+	FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
+		      quote (volume_label_option)));
+      if (label->header.typeflag == GNUTYPE_VOLHDR)
+	{
+	  if (memchr (label->header.name, '\0', sizeof label->header.name))
+	    assign_string (&volume_label, label->header.name);
+	  else
+	    {
+	      volume_label = xmalloc (sizeof (label->header.name) + 1);
+	      memcpy (volume_label, label->header.name,
+		      sizeof (label->header.name));
+	      volume_label[sizeof (label->header.name)] = 0;
+	    }
+	}
+      else if (label->header.typeflag == XGLTYPE)
+	{
+	  struct tar_stat_info st;
+	  tar_stat_init (&st);
+	  xheader_read (&st.xhdr, label,
+			OFF_FROM_HEADER (label->header.size));
+	  xheader_decode (&st);
+	  tar_stat_destroy (&st);
+	}
+    }
+  
+  if (!volume_label)
     FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
     FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
                   quote (volume_label_option)));
                   quote (volume_label_option)));
-  if (!check_label_pattern (label))
+  
+  if (!check_label_pattern (volume_label))
     FATAL_ERROR ((0, 0, _("Volume %s does not match %s"),
     FATAL_ERROR ((0, 0, _("Volume %s does not match %s"),
-                  quote_n (0, label->header.name),
+                  quote_n (0, volume_label),
                   quote_n (1, volume_label_option)));
                   quote_n (1, volume_label_option)));
 }
 }
 
 

+ 8 - 5
src/common.h

@@ -1,7 +1,8 @@
 /* Common declarations for the tar program.
 /* Common declarations for the tar program.
 
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, 
+   Inc.
 
 
    This program is free software; you can redistribute it and/or modify it
    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
    under the terms of the GNU General Public License as published by the
@@ -396,6 +397,7 @@ extern enum access_mode access_mode;
 extern FILE *stdlis;
 extern FILE *stdlis;
 extern bool write_archive_to_stdout;
 extern bool write_archive_to_stdout;
 extern char *volume_label;
 extern char *volume_label;
+extern size_t volume_label_count;
 extern char *continued_file_name;
 extern char *continued_file_name;
 extern uintmax_t continued_file_size;
 extern uintmax_t continued_file_size;
 extern uintmax_t continued_file_offset;
 extern uintmax_t continued_file_offset;
@@ -577,11 +579,12 @@ uintmax_t uintmax_from_header (const char *buf, size_t size);
 
 
 void list_archive (void);
 void list_archive (void);
 void print_for_mkdir (char *dirname, int length, mode_t mode);
 void print_for_mkdir (char *dirname, int length, mode_t mode);
-void print_header (struct tar_stat_info *st, off_t block_ordinal);
+void print_header (struct tar_stat_info *st, union block *blk,
+	           off_t block_ordinal);
 void read_and (void (*do_something) (void));
 void read_and (void (*do_something) (void));
-enum read_header read_header_primitive (bool raw_extended_headers,
-					struct tar_stat_info *info);
-enum read_header read_header (bool raw_extended_headers);
+enum read_header read_header (union block **return_block,
+			      struct tar_stat_info *info,
+			      bool raw_extended_headers);
 enum read_header tar_checksum (union block *header, bool silent);
 enum read_header tar_checksum (union block *header, bool silent);
 void skip_file (off_t size);
 void skip_file (off_t size);
 void skip_member (void);
 void skip_member (void);

+ 7 - 5
src/compare.c

@@ -1,7 +1,7 @@
 /* Diff files from a tar archive.
 /* Diff files from a tar archive.
 
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
 
 
    Written by John Gilmore, on 1987-04-30.
    Written by John Gilmore, on 1987-04-30.
 
 
@@ -460,7 +460,7 @@ diff_archive (void)
     {
     {
       if (now_verifying)
       if (now_verifying)
 	fprintf (stdlis, _("Verify "));
 	fprintf (stdlis, _("Verify "));
-      print_header (&current_stat_info, -1);
+      print_header (&current_stat_info, current_header, -1);
     }
     }
 
 
   switch (current_header->header.typeflag)
   switch (current_header->header.typeflag)
@@ -578,7 +578,8 @@ verify_volume (void)
   flush_read ();
   flush_read ();
   while (1)
   while (1)
     {
     {
-      enum read_header status = read_header (false);
+      enum read_header status = read_header (&current_header, 
+                                             &current_stat_info, false);
 
 
       if (status == HEADER_FAILURE)
       if (status == HEADER_FAILURE)
 	{
 	{
@@ -588,7 +589,8 @@ verify_volume (void)
 	    {
 	    {
 	      counter++;
 	      counter++;
 	      set_next_block_after (current_header);
 	      set_next_block_after (current_header);
-	      status = read_header (false);
+	      status = read_header (&current_header, &current_stat_info,
+	                            false);
 	    }
 	    }
 	  while (status == HEADER_FAILURE);
 	  while (status == HEADER_FAILURE);
 
 
@@ -606,7 +608,7 @@ verify_volume (void)
             {
             {
 	      char buf[UINTMAX_STRSIZE_BOUND];
 	      char buf[UINTMAX_STRSIZE_BOUND];
 
 
-	      status = read_header (false);
+	      status = read_header (&current_header, &current_stat_info, false);
 	      if (status == HEADER_ZERO_BLOCK)
 	      if (status == HEADER_ZERO_BLOCK)
 	        break;
 	        break;
 	      WARNOPT (WARN_ALONE_ZERO_BLOCK,
 	      WARNOPT (WARN_ALONE_ZERO_BLOCK,

+ 3 - 3
src/create.c

@@ -1,7 +1,7 @@
 /* Create a tar archive.
 /* Create a tar archive.
 
 
    Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
    Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
 
 
    Written by John Gilmore, on 1985-08-25.
    Written by John Gilmore, on 1985-08-25.
 
 
@@ -996,11 +996,11 @@ finish_header (struct tar_stat_info *st,
       && header->header.typeflag != XHDTYPE
       && header->header.typeflag != XHDTYPE
       && header->header.typeflag != XGLTYPE)
       && header->header.typeflag != XGLTYPE)
     {
     {
-      /* These globals are parameters to print_header, sigh.  */
+      /* FIXME: These globals are parameters to print_header, sigh.  */
 
 
       current_header = header;
       current_header = header;
       current_format = archive_format;
       current_format = archive_format;
-      print_header (st, block_ordinal);
+      print_header (st, current_header, block_ordinal);
     }
     }
 
 
   header = write_extended (false, st, header);
   header = write_extended (false, st, header);

+ 5 - 3
src/delete.c

@@ -1,7 +1,7 @@
 /* Delete entries from a tar archive.
 /* Delete entries from a tar archive.
 
 
    Copyright (C) 1988, 1992, 1994, 1996, 1997, 2000, 2001, 2003, 2004,
    Copyright (C) 1988, 1992, 1994, 1996, 1997, 2000, 2001, 2003, 2004,
-   2005, 2006 Free Software Foundation, Inc.
+   2005, 2006, 2010 Free Software Foundation, Inc.
 
 
    This program is free software; you can redistribute it and/or modify it
    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
    under the terms of the GNU General Public License as published by the
@@ -165,7 +165,9 @@ delete_archive_members (void)
 
 
   do
   do
     {
     {
-      enum read_header status = read_header (true);
+      enum read_header status = read_header (&current_header,
+                                             &current_stat_info,
+                                             true);
 
 
       switch (status)
       switch (status)
 	{
 	{
@@ -260,7 +262,7 @@ delete_archive_members (void)
 
 
 	  if (current_block == record_end)
 	  if (current_block == record_end)
 	    flush_archive ();
 	    flush_archive ();
-	  status = read_header (false);
+	  status = read_header (&current_header, &current_stat_info, false);
 
 
 	  xheader_decode (&current_stat_info);
 	  xheader_decode (&current_stat_info);
 
 

+ 2 - 4
src/extract.c

@@ -1,7 +1,7 @@
 /* Extract files from a tar archive.
 /* Extract files from a tar archive.
 
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
-   2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
 
 
    Written by John Gilmore, on 1985-11-19.
    Written by John Gilmore, on 1985-11-19.
 
 
@@ -1092,8 +1092,6 @@ extract_fifo (char *file_name, int typeflag)
 static int
 static int
 extract_volhdr (char *file_name, int typeflag)
 extract_volhdr (char *file_name, int typeflag)
 {
 {
-  if (verbose_option)
-    fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
   skip_member ();
   skip_member ();
   return 0;
   return 0;
 }
 }
@@ -1259,7 +1257,7 @@ extract_archive (void)
 
 
   /* Print the block from current_header and current_stat.  */
   /* Print the block from current_header and current_stat.  */
   if (verbose_option)
   if (verbose_option)
-    print_header (&current_stat_info, -1);
+    print_header (&current_stat_info, current_header, -1);
 
 
   /* Restore stats for all non-ancestor directories, unless
   /* Restore stats for all non-ancestor directories, unless
      it is an incremental archive.
      it is an incremental archive.

+ 62 - 31
src/list.c

@@ -1,7 +1,7 @@
 /* List a tar archive, with support routines for reading a tar archive.
 /* List a tar archive, with support routines for reading a tar archive.
 
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
-   2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
 
 
    Written by John Gilmore, on 1985-08-26.
    Written by John Gilmore, on 1985-08-26.
 
 
@@ -33,6 +33,7 @@ union block *recent_long_name;	/* recent long name header and contents */
 union block *recent_long_link;	/* likewise, for long link */
 union block *recent_long_link;	/* likewise, for long link */
 size_t recent_long_name_blocks;	/* number of blocks in recent_long_name */
 size_t recent_long_name_blocks;	/* number of blocks in recent_long_name */
 size_t recent_long_link_blocks;	/* likewise, for long link */
 size_t recent_long_link_blocks;	/* likewise, for long link */
+union block *recent_global_header; /* Recent global header block */
 
 
 static uintmax_t from_header (const char *, size_t, const char *,
 static uintmax_t from_header (const char *, size_t, const char *,
 			      uintmax_t, uintmax_t, bool, bool);
 			      uintmax_t, uintmax_t, bool, bool);
@@ -77,7 +78,7 @@ read_and (void (*do_something) (void))
       prev_status = status;
       prev_status = status;
       tar_stat_destroy (&current_stat_info);
       tar_stat_destroy (&current_stat_info);
 
 
-      status = read_header (false);
+      status = read_header (&current_header, &current_stat_info, false);
       switch (status)
       switch (status)
 	{
 	{
 	case HEADER_STILL_UNREAD:
 	case HEADER_STILL_UNREAD:
@@ -138,7 +139,7 @@ read_and (void (*do_something) (void))
 	    {
 	    {
 	      char buf[UINTMAX_STRSIZE_BOUND];
 	      char buf[UINTMAX_STRSIZE_BOUND];
 
 
-	      status = read_header (false);
+	      status = read_header (&current_header, &current_stat_info, false);
 	      if (status == HEADER_ZERO_BLOCK)
 	      if (status == HEADER_ZERO_BLOCK)
 		break;
 		break;
 	      WARNOPT (WARN_ALONE_ZERO_BLOCK,
 	      WARNOPT (WARN_ALONE_ZERO_BLOCK,
@@ -205,11 +206,12 @@ void
 list_archive (void)
 list_archive (void)
 {
 {
   off_t block_ordinal = current_block_ordinal ();
   off_t block_ordinal = current_block_ordinal ();
-  /* Print the header block.  */
 
 
+  /* Print the header block.  */
+  
   decode_header (current_header, &current_stat_info, &current_format, 0);
   decode_header (current_header, &current_stat_info, &current_format, 0);
   if (verbose_option)
   if (verbose_option)
-    print_header (&current_stat_info, block_ordinal);
+    print_header (&current_stat_info, current_header, block_ordinal);
 
 
   if (incremental_option)
   if (incremental_option)
     {
     {
@@ -293,7 +295,8 @@ tar_checksum (union block *header, bool silent)
    the header which this routine reads.  */
    the header which this routine reads.  */
 
 
 enum read_header
 enum read_header
-read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
+read_header (union block **return_block, struct tar_stat_info *info,
+	     bool raw_extended_headers)
 {
 {
   union block *header;
   union block *header;
   union block *header_copy;
   union block *header_copy;
@@ -310,7 +313,7 @@ read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
       enum read_header status;
       enum read_header status;
 
 
       header = find_next_block ();
       header = find_next_block ();
-      current_header = header;
+      *return_block = header;
       if (!header)
       if (!header)
 	return HEADER_END_OF_FILE;
 	return HEADER_END_OF_FILE;
 
 
@@ -392,6 +395,11 @@ read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
 	  else if (header->header.typeflag == XGLTYPE)
 	  else if (header->header.typeflag == XGLTYPE)
 	    {
 	    {
 	      struct xheader xhdr;
 	      struct xheader xhdr;
+
+	      if (!recent_global_header)
+		recent_global_header = xmalloc (sizeof *recent_global_header);
+	      memcpy (recent_global_header, header,
+		      sizeof *recent_global_header);
 	      memset (&xhdr, 0, sizeof xhdr);
 	      memset (&xhdr, 0, sizeof xhdr);
 	      xheader_read (&xhdr, header,
 	      xheader_read (&xhdr, header,
 			    OFF_FROM_HEADER (header->header.size));
 			    OFF_FROM_HEADER (header->header.size));
@@ -405,7 +413,7 @@ read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
       else
       else
 	{
 	{
 	  char const *name;
 	  char const *name;
-	  struct posix_header const *h = &current_header->header;
+	  struct posix_header const *h = &header->header;
 	  char namebuf[sizeof h->prefix + 1 + NAME_FIELD_SIZE + 1];
 	  char namebuf[sizeof h->prefix + 1 + NAME_FIELD_SIZE + 1];
 
 
 	  if (recent_long_name)
 	  if (recent_long_name)
@@ -464,12 +472,6 @@ read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
     }
     }
 }
 }
 
 
-enum read_header
-read_header (bool raw_extended_headers)
-{
-  return read_header_primitive (raw_extended_headers, &current_stat_info);
-}
-
 static char *
 static char *
 decode_xform (char *file_name, void *data)
 decode_xform (char *file_name, void *data)
 {
 {
@@ -1019,9 +1021,6 @@ tartime (struct timespec t, bool full_time)
    they shouldn't.  Unix tar is pretty random here anyway.  */
    they shouldn't.  Unix tar is pretty random here anyway.  */
 
 
 
 
-/* FIXME: Note that print_header uses the globals HEAD, HSTAT, and
-   HEAD_STANDARD, which must be set up in advance.  Not very clean..  */
-
 /* Width of "user/group size", with initial value chosen
 /* Width of "user/group size", with initial value chosen
    heuristically.  This grows as needed, though this may cause some
    heuristically.  This grows as needed, though this may cause some
    stairstepping in the output.  Make it too small and the output will
    stairstepping in the output.  Make it too small and the output will
@@ -1034,8 +1033,11 @@ static int ugswidth = 19;
    USGWIDTH, some stairstepping may occur.  */
    USGWIDTH, some stairstepping may occur.  */
 static int datewidth = sizeof "YYYY-MM-DD HH:MM" - 1;
 static int datewidth = sizeof "YYYY-MM-DD HH:MM" - 1;
 
 
-void
-print_header (struct tar_stat_info *st, off_t block_ordinal)
+static bool volume_label_printed = false;
+
+static void
+simple_print_header (struct tar_stat_info *st, union block *blk,
+		     off_t block_ordinal)
 {
 {
   char modes[11];
   char modes[11];
   char const *time_stamp;
   char const *time_stamp;
@@ -1051,7 +1053,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
   int pad;
   int pad;
   int sizelen;
   int sizelen;
 
 
-  if (test_label_option && current_header->header.typeflag != GNUTYPE_VOLHDR)
+  if (test_label_option && blk->header.typeflag != GNUTYPE_VOLHDR)
     return;
     return;
 
 
   if (show_transformed_names_option)
   if (show_transformed_names_option)
@@ -1080,9 +1082,10 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
       /* File type and modes.  */
       /* File type and modes.  */
 
 
       modes[0] = '?';
       modes[0] = '?';
-      switch (current_header->header.typeflag)
+      switch (blk->header.typeflag)
 	{
 	{
 	case GNUTYPE_VOLHDR:
 	case GNUTYPE_VOLHDR:
+	  volume_label_printed = true;
 	  modes[0] = 'V';
 	  modes[0] = 'V';
 	  break;
 	  break;
 
 
@@ -1150,8 +1153,8 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 	  /* Try parsing it as an unsigned integer first, and as a
 	  /* Try parsing it as an unsigned integer first, and as a
 	     uid_t if that fails.  This method can list positive user
 	     uid_t if that fails.  This method can list positive user
 	     ids that are too large to fit in a uid_t.  */
 	     ids that are too large to fit in a uid_t.  */
-	  uintmax_t u = from_header (current_header->header.uid,
-				     sizeof current_header->header.uid, 0,
+	  uintmax_t u = from_header (blk->header.uid,
+				     sizeof blk->header.uid, 0,
 				     (uintmax_t) 0,
 				     (uintmax_t) 0,
 				     (uintmax_t) TYPE_MAXIMUM (uintmax_t),
 				     (uintmax_t) TYPE_MAXIMUM (uintmax_t),
 				     false, false);
 				     false, false);
@@ -1160,7 +1163,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 	  else
 	  else
 	    {
 	    {
 	      sprintf (uform, "%ld",
 	      sprintf (uform, "%ld",
-		       (long) UID_FROM_HEADER (current_header->header.uid));
+		       (long) UID_FROM_HEADER (blk->header.uid));
 	      user = uform;
 	      user = uform;
 	    }
 	    }
 	}
 	}
@@ -1175,8 +1178,8 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 	  /* Try parsing it as an unsigned integer first, and as a
 	  /* Try parsing it as an unsigned integer first, and as a
 	     gid_t if that fails.  This method can list positive group
 	     gid_t if that fails.  This method can list positive group
 	     ids that are too large to fit in a gid_t.  */
 	     ids that are too large to fit in a gid_t.  */
-	  uintmax_t g = from_header (current_header->header.gid,
-				     sizeof current_header->header.gid, 0,
+	  uintmax_t g = from_header (blk->header.gid,
+				     sizeof blk->header.gid, 0,
 				     (uintmax_t) 0,
 				     (uintmax_t) 0,
 				     (uintmax_t) TYPE_MAXIMUM (uintmax_t),
 				     (uintmax_t) TYPE_MAXIMUM (uintmax_t),
 				     false, false);
 				     false, false);
@@ -1185,14 +1188,14 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 	  else
 	  else
 	    {
 	    {
 	      sprintf (gform, "%ld",
 	      sprintf (gform, "%ld",
-		       (long) GID_FROM_HEADER (current_header->header.gid));
+		       (long) GID_FROM_HEADER (blk->header.gid));
 	      group = gform;
 	      group = gform;
 	    }
 	    }
 	}
 	}
 
 
       /* Format the file size or major/minor device numbers.  */
       /* Format the file size or major/minor device numbers.  */
 
 
-      switch (current_header->header.typeflag)
+      switch (blk->header.typeflag)
 	{
 	{
 	case CHRTYPE:
 	case CHRTYPE:
 	case BLKTYPE:
 	case BLKTYPE:
@@ -1222,7 +1225,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 
 
       fprintf (stdlis, " %s", quotearg (temp_name));
       fprintf (stdlis, " %s", quotearg (temp_name));
 
 
-      switch (current_header->header.typeflag)
+      switch (blk->header.typeflag)
 	{
 	{
 	case SYMTYPE:
 	case SYMTYPE:
 	  fprintf (stdlis, " -> %s\n", quotearg (st->link_name));
 	  fprintf (stdlis, " -> %s\n", quotearg (st->link_name));
@@ -1235,7 +1238,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 	default:
 	default:
 	  {
 	  {
 	    char type_string[2];
 	    char type_string[2];
-	    type_string[0] = current_header->header.typeflag;
+	    type_string[0] = blk->header.typeflag;
 	    type_string[1] = '\0';
 	    type_string[1] = '\0';
 	    fprintf (stdlis, _(" unknown file type %s\n"),
 	    fprintf (stdlis, _(" unknown file type %s\n"),
 		     quote (type_string));
 		     quote (type_string));
@@ -1269,7 +1272,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 	case GNUTYPE_MULTIVOL:
 	case GNUTYPE_MULTIVOL:
 	  strcpy (size,
 	  strcpy (size,
 		  STRINGIFY_BIGINT
 		  STRINGIFY_BIGINT
-		  (UINTMAX_FROM_HEADER (current_header->oldgnu_header.offset),
+		  (UINTMAX_FROM_HEADER (blk->oldgnu_header.offset),
 		   uintbuf));
 		   uintbuf));
 	  fprintf (stdlis, _("--Continued at byte %s--\n"), size);
 	  fprintf (stdlis, _("--Continued at byte %s--\n"), size);
 	  break;
 	  break;
@@ -1278,6 +1281,34 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
   fflush (stdlis);
   fflush (stdlis);
 }
 }
 
 
+
+void
+print_header (struct tar_stat_info *st, union block *blk,
+	      off_t block_ordinal)
+{
+  if (current_format == POSIX_FORMAT && !volume_label_printed && volume_label)
+    {
+      struct tar_stat_info vstat;
+      union block vblk;
+      enum archive_format dummy;
+
+      volume_label_printed = true;
+
+      memset (&vblk, 0, sizeof (vblk));
+      vblk.header.typeflag = GNUTYPE_VOLHDR;
+      if (recent_global_header)
+	memcpy (vblk.header.mtime, recent_global_header->header.mtime,
+		sizeof vblk.header.mtime);
+      tar_stat_init (&vstat);
+      assign_string (&vstat.file_name, ".");
+      decode_header (&vblk, &vstat, &dummy, 0);
+      assign_string (&vstat.file_name, volume_label);
+      simple_print_header (&vstat, &vblk, block_ordinal);
+      tar_stat_destroy (&vstat);
+    }
+  simple_print_header (st, blk, block_ordinal);
+}
+
 /* Print a similar line when we make a directory automatically.  */
 /* Print a similar line when we make a directory automatically.  */
 void
 void
 print_for_mkdir (char *dirname, int length, mode_t mode)
 print_for_mkdir (char *dirname, int length, mode_t mode)

+ 3 - 2
src/update.c

@@ -1,7 +1,7 @@
 /* Update a tar archive.
 /* Update a tar archive.
 
 
    Copyright (C) 1988, 1992, 1994, 1996, 1997, 1999, 2000, 2001, 2003,
    Copyright (C) 1988, 1992, 1994, 1996, 1997, 1999, 2000, 2001, 2003,
-   2004, 2005, 2007 Free Software Foundation, Inc.
+   2004, 2005, 2007, 2010 Free Software Foundation, Inc.
 
 
    This program is free software; you can redistribute it and/or modify it
    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
    under the terms of the GNU General Public License as published by the
@@ -114,7 +114,8 @@ update_archive (void)
 
 
   while (!found_end)
   while (!found_end)
     {
     {
-      enum read_header status = read_header (false);
+      enum read_header status = read_header (&current_header, 
+                                             &current_stat_info, false);
 
 
       switch (status)
       switch (status)
 	{
 	{

+ 62 - 43
src/xheader.c

@@ -1,6 +1,7 @@
 /* POSIX extended headers for tar.
 /* POSIX extended headers for tar.
 
 
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software
+   Foundation, Inc.
 
 
    This program is free software; you can redistribute it and/or modify it
    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
    under the terms of the GNU General Public License as published by the
@@ -440,30 +441,37 @@ xheader_write (char type, char *name, time_t t, struct xheader *xhdr)
 void
 void
 xheader_write_global (struct xheader *xhdr)
 xheader_write_global (struct xheader *xhdr)
 {
 {
-  char *name;
-  struct keyword_list *kp;
-
-  if (!keyword_global_override_list)
-    return;
+  if (keyword_global_override_list)
+    {
+      struct keyword_list *kp;
 
 
-  xheader_init (xhdr);
-  for (kp = keyword_global_override_list; kp; kp = kp->next)
-    code_string (kp->value, kp->pattern, xhdr);
-  xheader_finish (xhdr);
-  xheader_write (XGLTYPE, name = xheader_ghdr_name (), time (NULL), xhdr);
-  free (name);
+      xheader_init (xhdr);
+      for (kp = keyword_global_override_list; kp; kp = kp->next)
+	code_string (kp->value, kp->pattern, xhdr);
+    }
+  if (xhdr->stk)
+    {
+      char *name;
+      
+      xheader_finish (xhdr);
+      xheader_write (XGLTYPE, name = xheader_ghdr_name (), time (NULL), xhdr);
+      free (name);
+    }
 }
 }
 
 
 
 
 /* General Interface */
 /* General Interface */
 
 
+#define XHDR_PROTECTED 0x01
+#define XHDR_GLOBAL    0x02
+
 struct xhdr_tab
 struct xhdr_tab
 {
 {
   char const *keyword;
   char const *keyword;
   void (*coder) (struct tar_stat_info const *, char const *,
   void (*coder) (struct tar_stat_info const *, char const *,
 		 struct xheader *, void const *data);
 		 struct xheader *, void const *data);
   void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
   void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
-  bool protect;
+  int flags;
 };
 };
 
 
 /* This declaration must be extern, because ISO C99 section 6.9.2
 /* This declaration must be extern, because ISO C99 section 6.9.2
@@ -491,7 +499,7 @@ xheader_protected_pattern_p (const char *pattern)
   struct xhdr_tab const *p;
   struct xhdr_tab const *p;
 
 
   for (p = xhdr_tab; p->keyword; p++)
   for (p = xhdr_tab; p->keyword; p++)
-    if (p->protect && fnmatch (pattern, p->keyword, 0) == 0)
+    if ((p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0)
       return true;
       return true;
   return false;
   return false;
 }
 }
@@ -502,7 +510,7 @@ xheader_protected_keyword_p (const char *keyword)
   struct xhdr_tab const *p;
   struct xhdr_tab const *p;
 
 
   for (p = xhdr_tab; p->keyword; p++)
   for (p = xhdr_tab; p->keyword; p++)
-    if (p->protect && strcmp (p->keyword, keyword) == 0)
+    if ((p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0)
       return true;
       return true;
   return false;
   return false;
 }
 }
@@ -633,7 +641,11 @@ decg (void *data, char const *keyword, char const *value,
       size_t size __attribute__((unused)))
       size_t size __attribute__((unused)))
 {
 {
   struct keyword_list **kwl = data;
   struct keyword_list **kwl = data;
-  xheader_list_append (kwl, keyword, value);
+  struct xhdr_tab const *tab = locate_handler (keyword);
+  if (tab && (tab->flags & XHDR_GLOBAL))
+    tab->decoder (data, keyword, value, size);
+  else
+    xheader_list_append (kwl, keyword, value);
 }
 }
 
 
 void
 void
@@ -695,6 +707,9 @@ xheader_read (struct xheader *xhdr, union block *p, size_t size)
       if (len > BLOCKSIZE)
       if (len > BLOCKSIZE)
 	len = BLOCKSIZE;
 	len = BLOCKSIZE;
 
 
+      if (!p)
+	FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
+      
       memcpy (&xhdr->buffer[j], p->buffer, len);
       memcpy (&xhdr->buffer[j], p->buffer, len);
       set_next_block_after (p);
       set_next_block_after (p);
 
 
@@ -1491,51 +1506,53 @@ sparse_minor_decoder (struct tar_stat_info *st,
 }
 }
 
 
 struct xhdr_tab const xhdr_tab[] = {
 struct xhdr_tab const xhdr_tab[] = {
-  { "atime",	atime_coder,	atime_decoder,	  false },
-  { "comment",	dummy_coder,	dummy_decoder,	  false },
-  { "charset",	dummy_coder,	dummy_decoder,	  false },
-  { "ctime",	ctime_coder,	ctime_decoder,	  false },
-  { "gid",	gid_coder,	gid_decoder,	  false },
-  { "gname",	gname_coder,	gname_decoder,	  false },
-  { "linkpath", linkpath_coder, linkpath_decoder, false },
-  { "mtime",	mtime_coder,	mtime_decoder,	  false },
-  { "path",	path_coder,	path_decoder,	  false },
-  { "size",	size_coder,	size_decoder,	  false },
-  { "uid",	uid_coder,	uid_decoder,	  false },
-  { "uname",	uname_coder,	uname_decoder,	  false },
+  { "atime",	atime_coder,	atime_decoder,	  0 },
+  { "comment",	dummy_coder,	dummy_decoder,	  0 },
+  { "charset",	dummy_coder,	dummy_decoder,	  0 },
+  { "ctime",	ctime_coder,	ctime_decoder,	  0 },
+  { "gid",	gid_coder,	gid_decoder,	  0 },
+  { "gname",	gname_coder,	gname_decoder,	  0 },
+  { "linkpath", linkpath_coder, linkpath_decoder, 0 },
+  { "mtime",	mtime_coder,	mtime_decoder,	  0 },
+  { "path",	path_coder,	path_decoder,	  0 },
+  { "size",	size_coder,	size_decoder,	  0 },
+  { "uid",	uid_coder,	uid_decoder,	  0 },
+  { "uname",	uname_coder,	uname_decoder,	  0 },
 
 
   /* Sparse file handling */
   /* Sparse file handling */
   { "GNU.sparse.name",       path_coder, path_decoder,
   { "GNU.sparse.name",       path_coder, path_decoder,
-    true },
+    XHDR_PROTECTED },
   { "GNU.sparse.major",      sparse_major_coder, sparse_major_decoder,
   { "GNU.sparse.major",      sparse_major_coder, sparse_major_decoder,
-    true },
+    XHDR_PROTECTED },
   { "GNU.sparse.minor",      sparse_minor_coder, sparse_minor_decoder,
   { "GNU.sparse.minor",      sparse_minor_coder, sparse_minor_decoder,
-    true },
+    XHDR_PROTECTED },
   { "GNU.sparse.realsize",   sparse_size_coder, sparse_size_decoder,
   { "GNU.sparse.realsize",   sparse_size_coder, sparse_size_decoder,
-    true },
+    XHDR_PROTECTED },
   { "GNU.sparse.numblocks",  sparse_numblocks_coder, sparse_numblocks_decoder,
   { "GNU.sparse.numblocks",  sparse_numblocks_coder, sparse_numblocks_decoder,
-    true },
+    XHDR_PROTECTED },
 
 
   /* tar 1.14 - 1.15.90 keywords. */
   /* tar 1.14 - 1.15.90 keywords. */
-  { "GNU.sparse.size",       sparse_size_coder, sparse_size_decoder, true },
+  { "GNU.sparse.size",       sparse_size_coder, sparse_size_decoder,
+    XHDR_PROTECTED },
   /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x'
   /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x'
      headers, and each of them was meaningful. It confilcted with POSIX specs,
      headers, and each of them was meaningful. It confilcted with POSIX specs,
      which requires that "when extended header records conflict, the last one
      which requires that "when extended header records conflict, the last one
      given in the header shall take precedence." */
      given in the header shall take precedence." */
   { "GNU.sparse.offset",     sparse_offset_coder, sparse_offset_decoder,
   { "GNU.sparse.offset",     sparse_offset_coder, sparse_offset_decoder,
-    true },
+    XHDR_PROTECTED },
   { "GNU.sparse.numbytes",   sparse_numbytes_coder, sparse_numbytes_decoder,
   { "GNU.sparse.numbytes",   sparse_numbytes_coder, sparse_numbytes_decoder,
-    true },
+    XHDR_PROTECTED },
   /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */
   /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */
   { "GNU.sparse.map",        NULL /* Unused, see pax_dump_header() */,
   { "GNU.sparse.map",        NULL /* Unused, see pax_dump_header() */,
-    sparse_map_decoder, false },
+    sparse_map_decoder, 0 },
 
 
   { "GNU.dumpdir",           dumpdir_coder, dumpdir_decoder,
   { "GNU.dumpdir",           dumpdir_coder, dumpdir_decoder,
-    true },
+    XHDR_PROTECTED },
 
 
   /* Keeps the tape/volume label. May be present only in the global headers.
   /* Keeps the tape/volume label. May be present only in the global headers.
      Equivalent to GNUTYPE_VOLHDR.  */
      Equivalent to GNUTYPE_VOLHDR.  */
-  { "GNU.volume.label", volume_label_coder, volume_label_decoder, true },
+  { "GNU.volume.label", volume_label_coder, volume_label_decoder,
+    XHDR_PROTECTED | XHDR_GLOBAL },
 
 
   /* These may be present in a first global header of the archive.
   /* These may be present in a first global header of the archive.
      They provide the same functionality as GNUTYPE_MULTIVOL header.
      They provide the same functionality as GNUTYPE_MULTIVOL header.
@@ -1544,9 +1561,11 @@ struct xhdr_tab const xhdr_tab[] = {
      GNU.volume.offset keeps the offset of the start of this volume,
      GNU.volume.offset keeps the offset of the start of this volume,
      otherwise kept in oldgnu_header.offset.  */
      otherwise kept in oldgnu_header.offset.  */
   { "GNU.volume.filename", volume_label_coder, volume_filename_decoder,
   { "GNU.volume.filename", volume_label_coder, volume_filename_decoder,
-    true },
-  { "GNU.volume.size", volume_size_coder, volume_size_decoder, true },
-  { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder, true },
+    XHDR_PROTECTED | XHDR_GLOBAL },
+  { "GNU.volume.size", volume_size_coder, volume_size_decoder,
+    XHDR_PROTECTED | XHDR_GLOBAL },
+  { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder,
+    XHDR_PROTECTED | XHDR_GLOBAL },
 
 
-  { NULL, NULL, NULL, false }
+  { NULL, NULL, NULL, 0 }
 };
 };

+ 2 - 0
tests/Makefile.am

@@ -87,6 +87,8 @@ TESTSUITE_AT = \
  incr06.at\
  incr06.at\
  indexfile.at\
  indexfile.at\
  ignfail.at\
  ignfail.at\
+ label01.at\
+ label02.at\
  link01.at\
  link01.at\
  link02.at\
  link02.at\
  link03.at\
  link03.at\

+ 35 - 0
tests/label01.at

@@ -0,0 +1,35 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# 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/>.
+
+AT_SETUP([single-volume label])
+AT_KEYWORDS([label label01])
+
+AT_TAR_CHECK([
+genfile --file foo
+genfile --file bar
+tar -cf archive --label=Test foo bar
+tar tf archive
+],
+[0],
+[Test
+foo
+bar
+],
+[],[],[],[gnu,oldgnu,posix])
+
+AT_CLEANUP

+ 38 - 0
tests/label02.at

@@ -0,0 +1,38 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# 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/>.
+
+AT_SETUP([multi-volume label])
+AT_KEYWORDS([label label02 multi-label multivolume multiv])
+
+AT_TAR_CHECK([
+exec <&-
+genfile --length 0 --file foo
+genfile --length 12288 --file bar
+genfile --length 12288 --file baz
+tar --label=Test -cM -L10 -f 1.tar -f 2.tar -f 3.tar -f 4.tar foo bar baz
+tar -Mt -f 1.tar -f 2.tar -f 3.tar -f 4.tar 
+],
+[0],
+[Test Volume 1
+foo
+bar
+baz
+],
+[],[],[],[gnu,oldgnu,posix])
+
+AT_CLEANUP

+ 3 - 0
tests/testsuite.at

@@ -146,6 +146,9 @@ m4_include([extrac06.at])
 m4_include([extrac07.at])
 m4_include([extrac07.at])
 m4_include([extrac08.at])
 m4_include([extrac08.at])
 
 
+m4_include([label01.at])
+m4_include([label02.at])
+
 m4_include([backup01.at])
 m4_include([backup01.at])
 
 
 m4_include([gzip.at])
 m4_include([gzip.at])