]> git.cworth.org Git - tar/blobdiff - src/list.c
Merge branch 'dfsg-orig' into dfsg-debian
[tar] / src / list.c
index 126a3c1fb8b2732a5fefe724bceb21e41fb3376b..fabe8b382725f76c368c581ce5a74b1ad17f4d98 100644 (file)
@@ -1,7 +1,7 @@
 /* List a tar archive, with support routines for reading a tar archive.
 
    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.
 
@@ -33,7 +33,22 @@ union block *recent_long_name;       /* recent long name header and contents */
 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_link_blocks;        /* likewise, for long link */
-
+union block *recent_global_header; /* Recent global header block */
+
+#define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where))
+#define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where))
+#define MINOR_FROM_HEADER(where) minor_from_header (where, sizeof (where))
+#define MODE_FROM_HEADER(where, hbits) \
+  mode_from_header (where, sizeof (where), hbits)
+#define TIME_FROM_HEADER(where) time_from_header (where, sizeof (where))
+#define UID_FROM_HEADER(where) uid_from_header (where, sizeof (where))
+
+static gid_t gid_from_header (const char *buf, size_t size);
+static major_t major_from_header (const char *buf, size_t size);
+static minor_t minor_from_header (const char *buf, size_t size);
+static mode_t mode_from_header (const char *buf, size_t size, unsigned *hbits);
+static time_t time_from_header (const char *buf, size_t size);
+static uid_t uid_from_header (const char *buf, size_t size);
 static uintmax_t from_header (const char *, size_t, const char *,
                              uintmax_t, uintmax_t, bool, bool);
 
@@ -77,7 +92,8 @@ read_and (void (*do_something) (void))
       prev_status = status;
       tar_stat_destroy (&current_stat_info);
 
-      status = read_header (false);
+      status = read_header (&current_header, &current_stat_info,
+                            read_header_auto);
       switch (status)
        {
        case HEADER_STILL_UNREAD:
@@ -88,7 +104,8 @@ read_and (void (*do_something) (void))
 
          /* Valid header.  We should decode next field (mode) first.
             Ensure incoming names are null terminated.  */
-
+         decode_header (current_header, &current_stat_info,
+                        &current_format, 1);
          if (! name_match (current_stat_info.file_name)
              || (NEWER_OPTION_INITIALIZED (newer_mtime_option)
                  /* FIXME: We get mtime now, and again later; this causes
@@ -114,8 +131,6 @@ read_and (void (*do_something) (void))
                           quotearg_colon (current_stat_info.file_name)));
                  /* Fall through.  */
                default:
-                 decode_header (current_header,
-                                &current_stat_info, &current_format, 0);
                  skip_member ();
                  continue;
                }
@@ -138,7 +153,8 @@ read_and (void (*do_something) (void))
            {
              char buf[UINTMAX_STRSIZE_BOUND];
 
-             status = read_header (false);
+             status = read_header (&current_header, &current_stat_info,
+                                   read_header_auto);
              if (status == HEADER_ZERO_BLOCK)
                break;
              /* 
@@ -148,8 +164,9 @@ read_and (void (*do_something) (void))
               * let's not be pedantic about issuing the warning.
               */
 #if 0         
-             WARN ((0, 0, _("A lone zero block at %s"),
-                    STRINGIFY_BIGINT (current_block_ordinal (), buf)));
+             WARNOPT (WARN_ALONE_ZERO_BLOCK,
+                      (0, 0, _("A lone zero block at %s"),
+                       STRINGIFY_BIGINT (current_block_ordinal (), buf)));
 #endif
              break;
            }
@@ -212,11 +229,10 @@ void
 list_archive (void)
 {
   off_t block_ordinal = current_block_ordinal ();
-  /* Print the header block.  */
 
-  decode_header (current_header, &current_stat_info, &current_format, 0);
+  /* Print the header block.  */
   if (verbose_option)
-    print_header (&current_stat_info, block_ordinal);
+    print_header (&current_stat_info, current_header, block_ordinal);
 
   if (incremental_option)
     {
@@ -287,20 +303,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_primitive (bool raw_extended_headers, struct tar_stat_info *info)
+read_header (union block **return_block, struct tar_stat_info *info,
+            enum read_header_mode mode)
 {
   union block *header;
   union block *header_copy;
@@ -317,7 +342,7 @@ read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
       enum read_header status;
 
       header = find_next_block ();
-      current_header = header;
+      *return_block = header;
       if (!header)
        return HEADER_END_OF_FILE;
 
@@ -337,7 +362,7 @@ read_header_primitive (bool raw_extended_headers, 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)
@@ -399,11 +424,18 @@ read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
          else if (header->header.typeflag == XGLTYPE)
            {
              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);
              xheader_read (&xhdr, header,
                            OFF_FROM_HEADER (header->header.size));
              xheader_decode_global (&xhdr);
              xheader_destroy (&xhdr);
+             if (mode == read_header_x_global)
+               return HEADER_SUCCESS_EXTENDED;
            }
 
          /* Loop!  */
@@ -412,7 +444,7 @@ read_header_primitive (bool raw_extended_headers, struct tar_stat_info *info)
       else
        {
          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];
 
          if (recent_long_name)
@@ -471,12 +503,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 *
 decode_xform (char *file_name, void *data)
 {
@@ -489,18 +515,18 @@ decode_xform (char *file_name, void *data)
         links subject to filename transformation.  In the absence of another
         solution, symbolic links are exempt from component stripping and
         name suffix normalization, but subject to filename transformation
-        proper. */ 
+        proper. */
       return file_name;
-      
+
     case XFORM_LINK:
       file_name = safer_name_suffix (file_name, true, absolute_names_option);
       break;
-      
+
     case XFORM_REGFILE:
       file_name = safer_name_suffix (file_name, false, absolute_names_option);
       break;
     }
-  
+
   if (strip_name_components)
     {
       size_t prefix_len = stripped_prefix_len (file_name,
@@ -512,7 +538,7 @@ decode_xform (char *file_name, void *data)
   return file_name;
 }
 
-bool
+static bool
 transform_member_name (char **pinput, int type)
 {
   return transform_name_fp (pinput, type, decode_xform, &type);
@@ -538,6 +564,8 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
               enum archive_format *format_pointer, int do_user_group)
 {
   enum archive_format format;
+  unsigned hbits; /* high bits of the file mode. */
+  mode_t mode = MODE_FROM_HEADER (header->header.mode, &hbits);
 
   if (strcmp (header->header.magic, TMAGIC) == 0)
     {
@@ -553,12 +581,12 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
        format = USTAR_FORMAT;
     }
   else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
-    format = OLDGNU_FORMAT;
+    format = hbits ? OLDGNU_FORMAT : GNU_FORMAT;
   else
     format = V7_FORMAT;
   *format_pointer = format;
 
-  stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode);
+  stat_info->stat.st_mode = mode;
   stat_info->mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime);
   stat_info->mtime.tv_nsec = 0;
   assign_string (&stat_info->uname,
@@ -636,13 +664,17 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
        stat_info->is_dumpdir = true;
     }
 
+  if (header->header.typeflag == GNUTYPE_VOLHDR)
+    /* Name transformations don't apply to volume headers. */
+    return;
+  
   transform_member_name (&stat_info->file_name, XFORM_REGFILE);
   switch (header->header.typeflag)
     {
     case SYMTYPE:
       transform_member_name (&stat_info->link_name, XFORM_SYMLINK);
       break;
-      
+
     case LNKTYPE:
       transform_member_name (&stat_info->link_name, XFORM_LINK);
     }
@@ -675,7 +707,8 @@ from_header (char const *where0, size_t digs, char const *type,
        {
          if (type && !silent)
            ERROR ((0, 0,
-                   /* TRANSLATORS: %s is type of the value (gid_t, uid_t, etc.) */
+                   /* TRANSLATORS: %s is type of the value (gid_t, uid_t,
+                      etc.) */
                    _("Blanks in header where numeric %s value expected"),
                    type));
          return -1;
@@ -867,7 +900,7 @@ from_header (char const *where0, size_t digs, char const *type,
   return -1;
 }
 
-gid_t
+static gid_t
 gid_from_header (const char *p, size_t s)
 {
   return from_header (p, s, "gid_t",
@@ -876,7 +909,7 @@ gid_from_header (const char *p, size_t s)
                      false, false);
 }
 
-major_t
+static major_t
 major_from_header (const char *p, size_t s)
 {
   return from_header (p, s, "major_t",
@@ -884,7 +917,7 @@ major_from_header (const char *p, size_t s)
                      (uintmax_t) TYPE_MAXIMUM (major_t), false, false);
 }
 
-minor_t
+static minor_t
 minor_from_header (const char *p, size_t s)
 {
   return from_header (p, s, "minor_t",
@@ -892,25 +925,28 @@ minor_from_header (const char *p, size_t s)
                      (uintmax_t) TYPE_MAXIMUM (minor_t), false, false);
 }
 
-mode_t
-mode_from_header (const char *p, size_t s)
+/* Convert P to the file mode, as understood by tar.
+   Store unrecognized mode bits (from 10th up) in HBITS. */
+static mode_t
+mode_from_header (const char *p, size_t s, unsigned *hbits)
 {
-  /* Do not complain about unrecognized mode bits.  */
   unsigned u = from_header (p, s, "mode_t",
                            - (uintmax_t) TYPE_MINIMUM (mode_t),
                            TYPE_MAXIMUM (uintmax_t), false, false);
-  return ((u & TSUID ? S_ISUID : 0)
-         | (u & TSGID ? S_ISGID : 0)
-         | (u & TSVTX ? S_ISVTX : 0)
-         | (u & TUREAD ? S_IRUSR : 0)
-         | (u & TUWRITE ? S_IWUSR : 0)
-         | (u & TUEXEC ? S_IXUSR : 0)
-         | (u & TGREAD ? S_IRGRP : 0)
-         | (u & TGWRITE ? S_IWGRP : 0)
-         | (u & TGEXEC ? S_IXGRP : 0)
-         | (u & TOREAD ? S_IROTH : 0)
-         | (u & TOWRITE ? S_IWOTH : 0)
-         | (u & TOEXEC ? S_IXOTH : 0));
+  mode_t mode = ((u & TSUID ? S_ISUID : 0)
+                | (u & TSGID ? S_ISGID : 0)
+                | (u & TSVTX ? S_ISVTX : 0)
+                | (u & TUREAD ? S_IRUSR : 0)
+                | (u & TUWRITE ? S_IWUSR : 0)
+                | (u & TUEXEC ? S_IXUSR : 0)
+                | (u & TGREAD ? S_IRGRP : 0)
+                | (u & TGWRITE ? S_IWGRP : 0)
+                | (u & TGEXEC ? S_IXGRP : 0)
+                | (u & TOREAD ? S_IROTH : 0)
+                | (u & TOWRITE ? S_IWOTH : 0)
+                | (u & TOEXEC ? S_IXOTH : 0));
+  *hbits = mode ^ u;
+  return mode;
 }
 
 off_t
@@ -922,14 +958,7 @@ off_from_header (const char *p, size_t s)
                      (uintmax_t) TYPE_MAXIMUM (off_t), false, false);
 }
 
-size_t
-size_from_header (const char *p, size_t s)
-{
-  return from_header (p, s, "size_t", (uintmax_t) 0,
-                     (uintmax_t) TYPE_MAXIMUM (size_t), false, false);
-}
-
-time_t
+static time_t
 time_from_header (const char *p, size_t s)
 {
   return from_header (p, s, "time_t",
@@ -937,7 +966,7 @@ time_from_header (const char *p, size_t s)
                      (uintmax_t) TYPE_MAXIMUM (time_t), false, false);
 }
 
-uid_t
+static uid_t
 uid_from_header (const char *p, size_t s)
 {
   return from_header (p, s, "uid_t",
@@ -1021,9 +1050,6 @@ tartime (struct timespec t, bool full_time)
    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
    heuristically.  This grows as needed, though this may cause some
    stairstepping in the output.  Make it too small and the output will
@@ -1036,8 +1062,11 @@ static int ugswidth = 19;
    USGWIDTH, some stairstepping may occur.  */
 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 const *time_stamp;
@@ -1053,9 +1082,6 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
   int pad;
   int sizelen;
 
-  if (test_label_option && current_header->header.typeflag != GNUTYPE_VOLHDR)
-    return;
-
   if (show_transformed_names_option)
     temp_name = st->file_name ? st->file_name : st->orig_file_name;
   else
@@ -1082,9 +1108,10 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
       /* File type and modes.  */
 
       modes[0] = '?';
-      switch (current_header->header.typeflag)
+      switch (blk->header.typeflag)
        {
        case GNUTYPE_VOLHDR:
+         volume_label_printed = true;
          modes[0] = 'V';
          break;
 
@@ -1135,7 +1162,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 
       /* Time stamp.  */
 
-      time_stamp = tartime (st->mtime, false);
+      time_stamp = tartime (st->mtime, full_time_option);
       time_stamp_len = strlen (time_stamp);
       if (datewidth < time_stamp_len)
        datewidth = time_stamp_len;
@@ -1152,8 +1179,8 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
          /* Try parsing it as an unsigned integer first, and as a
             uid_t if that fails.  This method can list positive user
             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) TYPE_MAXIMUM (uintmax_t),
                                     false, false);
@@ -1162,7 +1189,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
          else
            {
              sprintf (uform, "%ld",
-                      (long) UID_FROM_HEADER (current_header->header.uid));
+                      (long) UID_FROM_HEADER (blk->header.uid));
              user = uform;
            }
        }
@@ -1177,8 +1204,8 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
          /* Try parsing it as an unsigned integer first, and as a
             gid_t if that fails.  This method can list positive group
             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) TYPE_MAXIMUM (uintmax_t),
                                     false, false);
@@ -1187,14 +1214,14 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
          else
            {
              sprintf (gform, "%ld",
-                      (long) GID_FROM_HEADER (current_header->header.gid));
+                      (long) GID_FROM_HEADER (blk->header.gid));
              group = gform;
            }
        }
 
       /* Format the file size or major/minor device numbers.  */
 
-      switch (current_header->header.typeflag)
+      switch (blk->header.typeflag)
        {
        case CHRTYPE:
        case BLKTYPE:
@@ -1224,7 +1251,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 
       fprintf (stdlis, " %s", quotearg (temp_name));
 
-      switch (current_header->header.typeflag)
+      switch (blk->header.typeflag)
        {
        case SYMTYPE:
          fprintf (stdlis, " -> %s\n", quotearg (st->link_name));
@@ -1237,7 +1264,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
        default:
          {
            char type_string[2];
-           type_string[0] = current_header->header.typeflag;
+           type_string[0] = blk->header.typeflag;
            type_string[1] = '\0';
            fprintf (stdlis, _(" unknown file type %s\n"),
                     quote (type_string));
@@ -1271,7 +1298,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
        case GNUTYPE_MULTIVOL:
          strcpy (size,
                  STRINGIFY_BIGINT
-                 (UINTMAX_FROM_HEADER (current_header->oldgnu_header.offset),
+                 (UINTMAX_FROM_HEADER (blk->oldgnu_header.offset),
                   uintbuf));
          fprintf (stdlis, _("--Continued at byte %s--\n"), size);
          break;
@@ -1280,6 +1307,40 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
   fflush (stdlis);
 }
 
+
+static void
+print_volume_label (void)
+{
+  struct tar_stat_info vstat;
+  union block vblk;
+  enum archive_format dummy;
+
+  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, 0);
+  tar_stat_destroy (&vstat);
+}
+
+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)
+    {
+      print_volume_label ();
+      volume_label_printed = true;
+    }
+
+  simple_print_header (st, blk, block_ordinal);
+}
+
 /* Print a similar line when we make a directory automatically.  */
 void
 print_for_mkdir (char *dirname, int length, mode_t mode)
@@ -1311,7 +1372,7 @@ skip_file (off_t size)
 {
   union block *x;
 
-  /* FIXME: Make sure mv_begin is always called before it */
+  /* FIXME: Make sure mv_begin_read is always called before it */
 
   if (seekable_archive)
     {
@@ -1346,7 +1407,7 @@ skip_member (void)
       char save_typeflag = current_header->header.typeflag;
       set_next_block_after (current_header);
 
-      mv_begin (&current_stat_info);
+      mv_begin_read (&current_stat_info);
 
       if (current_stat_info.is_sparse)
        sparse_skip_file (&current_stat_info);
@@ -1356,3 +1417,34 @@ skip_member (void)
       mv_end ();
     }
 }
+
+void
+test_archive_label ()
+{
+  base64_init ();
+  name_gather ();
+
+  open_archive (ACCESS_READ);
+  if (read_header (&current_header, &current_stat_info, read_header_auto)
+      == HEADER_SUCCESS)
+    {
+      decode_header (current_header,
+                    &current_stat_info, &current_format, 0);
+      if (current_header->header.typeflag == GNUTYPE_VOLHDR)
+       assign_string (&volume_label, current_header->header.name);
+
+      if (volume_label)
+       {
+         if (verbose_option)
+           print_volume_label ();
+         if (!name_match (volume_label) && multi_volume_option)
+           {
+             char *s = drop_volume_label_suffix (volume_label);
+             name_match (s);
+             free (s);
+           }
+       }
+    }
+  close_archive ();
+  label_notfound ();
+}