]> Dogcows Code - chaz/tar/blobdiff - src/compare.c
Update copyright years.
[chaz/tar] / src / compare.c
index d4871c44350d9bb4d88c28390d75196f27775481..d29cfdd4822f683dec5c7dfb8dc01544c62330ce 100644 (file)
 /* Diff files from a tar archive.
-   Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
-   Written by John Gilmore, on 1987-04-30.
 
-   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 2, or (at your option) any later
-   version.
+   Copyright 1988, 1992-1994, 1996-1997, 1999-2001, 2003-2007,
+   2009-2010, 2012-2014 Free Software Foundation, Inc.
 
-   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.
+   This file is part of GNU tar.
 
-   You should have received a copy of the GNU General Public License along
-   with this program; if not, write to the Free Software Foundation, Inc.,
-   59 Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   GNU tar 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 of the License, or
+   (at your option) any later version.
 
-#include "system.h"
+   GNU tar 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/>.
+
+   Written by John Gilmore, on 1987-04-30.  */
+
+#include <system.h>
+#include <system-ioctl.h>
 
 #if HAVE_LINUX_FD_H
 # include <linux/fd.h>
 #endif
 
 #include "common.h"
-#include "rmt.h"
-
-/* Spare space for messages, hopefully safe even after gettext.  */
-#define MESSAGE_BUFFER_SIZE 100
+#include <quotearg.h>
+#include <rmt.h>
+#include <stdarg.h>
 
 /* Nonzero if we are verifying at the moment.  */
-int now_verifying = 0;
+bool now_verifying;
 
 /* File descriptor for the file we are diffing.  */
 static int diff_handle;
 
 /* Area for reading file contents into.  */
-static char *diff_buffer = NULL;
-
-/*--------------------------------.
-| Initialize for a diff operation |
-`--------------------------------*/
+static char *diff_buffer;
 
+/* Initialize for a diff operation.  */
 void
 diff_init (void)
 {
-  diff_buffer = (char *) valloc ((unsigned) record_size);
-  if (!diff_buffer)
-    FATAL_ERROR ((0, 0,
-                 _("Could not allocate memory for diff buffer of %d bytes"),
-                 record_size));
+  void *ptr;
+  diff_buffer = page_aligned_alloc (&ptr, record_size);
+  if (listed_incremental_option)
+    read_directory_file ();
 }
 
-/*------------------------------------------------------------------------.
-| Sigh about something that differs by writing a MESSAGE to stdlis, given |
-| MESSAGE is not NULL.  Also set the exit status if not already.          |
-`------------------------------------------------------------------------*/
-
-static void
-report_difference (const char *message)
+/* Sigh about something that differs by writing a MESSAGE to stdlis,
+   given MESSAGE is nonzero.  Also set the exit status if not already.  */
+void
+report_difference (struct tar_stat_info *st, const char *fmt, ...)
 {
-  if (message)
-    fprintf (stdlis, "%s: %s\n", current_file_name, message);
-
-  if (exit_status == TAREXIT_SUCCESS)
-    exit_status = TAREXIT_DIFFERS;
-}
+  if (fmt)
+    {
+      va_list ap;
 
-/*-----------------------------------------------------------------------.
-| Takes a buffer returned by read_and_process and does nothing with it.         |
-`-----------------------------------------------------------------------*/
+      fprintf (stdlis, "%s: ", quotearg_colon (st->file_name));
+      va_start (ap, fmt);
+      vfprintf (stdlis, fmt, ap);
+      va_end (ap);
+      fprintf (stdlis, "\n");
+    }
 
-/* Yes, I know.  SIZE and DATA are unused in this function.  Some compilers
-   may even report it.  That's OK, just relax!  */
+  set_exit_status (TAREXIT_DIFFERS);
+}
 
+/* Take a buffer returned by read_and_process and do nothing with it.  */
 static int
-process_noop (long size, char *data)
+process_noop (size_t size __attribute__ ((unused)),
+             char *data __attribute__ ((unused)))
 {
   return 1;
 }
 
-/*---.
-| ?  |
-`---*/
-
 static int
-process_rawdata (long bytes, char *buffer)
+process_rawdata (size_t bytes, char *buffer)
 {
-  int status = read (diff_handle, diff_buffer, (size_t) bytes);
-  char message[MESSAGE_BUFFER_SIZE];
+  size_t status = blocking_read (diff_handle, diff_buffer, bytes);
 
   if (status != bytes)
     {
-      if (status < 0)
+      if (status == SAFE_READ_ERROR)
        {
-         WARN ((0, errno, _("Cannot read %s"), current_file_name));
-         report_difference (NULL);
+         read_error (current_stat_info.file_name);
+         report_difference (&current_stat_info, NULL);
        }
       else
        {
-         sprintf (message, _("Could only read %d of %ld bytes"),
-                  status, bytes);
-         report_difference (message);
+         report_difference (&current_stat_info,
+                            ngettext ("Could only read %lu of %lu byte",
+                                      "Could only read %lu of %lu bytes",
+                                      bytes),
+                            (unsigned long) status, (unsigned long) bytes);
        }
       return 0;
     }
 
-  if (memcmp (buffer, diff_buffer, (size_t) bytes))
+  if (memcmp (buffer, diff_buffer, bytes))
     {
-      report_difference (_("Data differs"));
+      report_difference (&current_stat_info, _("Contents differ"));
       return 0;
     }
 
   return 1;
 }
 
-/*---.
-| ?  |
-`---*/
-
-/* Directory contents, only for GNUTYPE_DUMPDIR.  */
-
-static char *dumpdir_cursor;
-
-static int
-process_dumpdir (long bytes, char *buffer)
-{
-  if (memcmp (buffer, dumpdir_cursor, (size_t) bytes))
-    {
-      report_difference (_("Data differs"));
-      return 0;
-    }
-
-  dumpdir_cursor += bytes;
-  return 1;
-}
-
-/*------------------------------------------------------------------------.
-| Some other routine wants SIZE bytes in the archive.  For each chunk of  |
-| the archive, call PROCESSOR with the size of the chunk, and the address |
-| of the chunk it can work with.  The PROCESSOR should return nonzero for |
-| success.  It it return error once, continue skipping without calling    |
-| PROCESSOR anymore.                                                      |
-`------------------------------------------------------------------------*/
+/* Some other routine wants SIZE bytes in the archive.  For each chunk
+   of the archive, call PROCESSOR with the size of the chunk, and the
+   address of the chunk it can work with.  The PROCESSOR should return
+   nonzero for success.  Once it returns error, continue skipping
+   without calling PROCESSOR anymore.  */
 
 static void
-read_and_process (long size, int (*processor) (long, char *))
+read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
 {
   union block *data_block;
-  long data_size;
+  size_t data_size;
+  off_t size = st->stat.st_size;
 
-  if (multi_volume_option)
-    save_sizeleft = size;
+  mv_begin_read (st);
   while (size)
     {
       data_block = find_next_block ();
-      if (data_block == NULL)
+      if (! data_block)
        {
-         ERROR ((0, 0, _("Unexpected EOF on archive file")));
+         ERROR ((0, 0, _("Unexpected EOF in archive")));
          return;
        }
 
@@ -168,292 +141,338 @@ read_and_process (long size, int (*processor) (long, char *))
       set_next_block_after ((union block *)
                            (data_block->buffer + data_size - 1));
       size -= data_size;
-      if (multi_volume_option)
-       save_sizeleft -= data_size;
+      mv_size_left (size);
     }
+  mv_end ();
 }
 
-/*---.
-| ?  |
-`---*/
+/* Call either stat or lstat over STAT_DATA, depending on
+   --dereference (-h), for a file which should exist.  Diagnose any
+   problem.  Return nonzero for success, zero otherwise.  */
+static int
+get_stat_data (char const *file_name, struct stat *stat_data)
+{
+  int status = deref_stat (file_name, stat_data);
+
+  if (status != 0)
+    {
+      if (errno == ENOENT)
+       stat_warn (file_name);
+      else
+       stat_error (file_name);
+      report_difference (&current_stat_info, NULL);
+      return 0;
+    }
 
-/* JK This routine should be used more often than it is ... look into
-   that.  Anyhow, what it does is translate the sparse information on the
-   header, and in any subsequent extended headers, into an array of
-   structures with true numbers, as opposed to character strings.  It
-   simply makes our life much easier, doing so many comparisong and such.
-   */
+  return 1;
+}
 
+\f
 static void
-fill_in_sparse_array (void)
+diff_dir (void)
 {
-  int counter;
+  struct stat stat_data;
 
-  /* Allocate space for our scratch space; it's initially 10 elements
-     long, but can change in this routine if necessary.  */
+  if (!get_stat_data (current_stat_info.file_name, &stat_data))
+    return;
 
-  sp_array_size = 10;
-  sparsearray = (struct sp_array *) xmalloc (sp_array_size * sizeof (struct sp_array));
+  if (!S_ISDIR (stat_data.st_mode))
+    report_difference (&current_stat_info, _("File type differs"));
+  else if ((current_stat_info.stat.st_mode & MODE_ALL) !=
+          (stat_data.st_mode & MODE_ALL))
+    report_difference (&current_stat_info, _("Mode differs"));
+}
 
-  /* There are at most five of these structures in the header itself;
-     read these in first.  */
+static void
+diff_file (void)
+{
+  char const *file_name = current_stat_info.file_name;
+  struct stat stat_data;
 
-  for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
+  if (!get_stat_data (file_name, &stat_data))
+    skip_member ();
+  else if (!S_ISREG (stat_data.st_mode))
     {
-      /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler.  */
-      if (current_header->oldgnu_header.sp[counter].numbytes == 0)
-       break;
-
-      sparsearray[counter].offset =
-       from_oct (1 + 12, current_header->oldgnu_header.sp[counter].offset);
-      sparsearray[counter].numbytes =
-       from_oct (1 + 12, current_header->oldgnu_header.sp[counter].numbytes);
+      report_difference (&current_stat_info, _("File type differs"));
+      skip_member ();
     }
-
-  /* If the header's extended, we gotta read in exhdr's till we're done.  */
-
-  if (current_header->oldgnu_header.isextended)
+  else
     {
-      /* How far into the sparsearray we are `so far'.  */
-      static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
-      union block *exhdr;
-
-      while (1)
+      if ((current_stat_info.stat.st_mode & MODE_ALL) !=
+         (stat_data.st_mode & MODE_ALL))
+       report_difference (&current_stat_info, _("Mode differs"));
+
+      if (!sys_compare_uid (&stat_data, &current_stat_info.stat))
+       report_difference (&current_stat_info, _("Uid differs"));
+      if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
+       report_difference (&current_stat_info, _("Gid differs"));
+
+      if (tar_timespec_cmp (get_stat_mtime (&stat_data),
+                            current_stat_info.mtime))
+       report_difference (&current_stat_info, _("Mod time differs"));
+      if (current_header->header.typeflag != GNUTYPE_SPARSE
+         && stat_data.st_size != current_stat_info.stat.st_size)
        {
-         exhdr = find_next_block ();
-         for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
-           {
-             if (counter + so_far_ind > sp_array_size - 1)
-               {
-                 /* We just ran out of room in our scratch area -
-                    realloc it.  */
-
-                 sp_array_size *= 2;
-                 sparsearray = (struct sp_array *)
-                   xrealloc (sparsearray,
-                             sp_array_size * sizeof (struct sp_array));
-               }
-
-             /* Convert the character strings into longs.  */
+         report_difference (&current_stat_info, _("Size differs"));
+         skip_member ();
+       }
+      else
+       {
+         diff_handle = openat (chdir_fd, file_name, open_read_flags);
 
-             sparsearray[counter + so_far_ind].offset =
-               from_oct (1 + 12, exhdr->sparse_header.sp[counter].offset);
-             sparsearray[counter + so_far_ind].numbytes =
-               from_oct (1 + 12, exhdr->sparse_header.sp[counter].numbytes);
+         if (diff_handle < 0)
+           {
+             open_error (file_name);
+             skip_member ();
+             report_difference (&current_stat_info, NULL);
            }
+         else
+           {
+             int status;
 
-         /* If this is the last extended header for this file, we can
-            stop.  */
+             if (current_stat_info.is_sparse)
+               sparse_diff_file (diff_handle, &current_stat_info);
+             else
+               read_and_process (&current_stat_info, process_rawdata);
 
-         if (!exhdr->sparse_header.isextended)
-           break;
+             if (atime_preserve_option == replace_atime_preserve
+                 && stat_data.st_size != 0)
+               {
+                 struct timespec atime = get_stat_atime (&stat_data);
+                 if (set_file_atime (diff_handle, chdir_fd, file_name, atime)
+                     != 0)
+                   utime_error (file_name);
+               }
 
-         so_far_ind += SPARSES_IN_SPARSE_HEADER;
-         set_next_block_after (exhdr);
+             status = close (diff_handle);
+             if (status != 0)
+               close_error (file_name);
+           }
        }
-
-      /* Be sure to skip past the last one.  */
-
-      set_next_block_after (exhdr);
     }
 }
 
-/*---.
-| ?  |
-`---*/
-
-/* JK Diff'ing a sparse file with its counterpart on the tar file is a
-   bit of a different story than a normal file.  First, we must know what
-   areas of the file to skip through, i.e., we need to contruct a
-   sparsearray, which will hold all the information we need.  We must
-   compare small amounts of data at a time as we find it.  */
-
-/* FIXME: This does not look very solid to me, at first glance.  Zero areas
-   are not checked, spurious sparse entries seemingly goes undetected, and
-   I'm not sure overall identical sparsity is verified.  */
+static void
+diff_link (void)
+{
+  struct stat file_data;
+  struct stat link_data;
+
+  if (get_stat_data (current_stat_info.file_name, &file_data)
+      && get_stat_data (current_stat_info.link_name, &link_data)
+      && !sys_compare_links (&file_data, &link_data))
+    report_difference (&current_stat_info,
+                      _("Not linked to %s"),
+                      quote (current_stat_info.link_name));
+}
 
+#ifdef HAVE_READLINK
 static void
-diff_sparse_files (int size_of_file)
+diff_symlink (void)
 {
-  int remaining_size = size_of_file;
-  char *buffer = (char *) xmalloc (BLOCKSIZE * sizeof (char));
-  int buffer_size = BLOCKSIZE;
-  union block *data_block = NULL;
-  int counter = 0;
-  int different = 0;
+  size_t len = strlen (current_stat_info.link_name);
+  char *linkbuf = alloca (len + 1);
 
-  fill_in_sparse_array ();
+  int status = readlinkat (chdir_fd, current_stat_info.file_name,
+                          linkbuf, len + 1);
 
-  while (remaining_size > 0)
+  if (status < 0)
     {
-      int status;
-      long chunk_size;
-#if 0
-      int amount_read = 0;
+      if (errno == ENOENT)
+       readlink_warn (current_stat_info.file_name);
+      else
+       readlink_error (current_stat_info.file_name);
+      report_difference (&current_stat_info, NULL);
+    }
+  else if (status != len
+          || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
+    report_difference (&current_stat_info, _("Symlink differs"));
+}
 #endif
 
-      data_block = find_next_block ();
-      chunk_size = sparsearray[counter].numbytes;
-      if (!chunk_size)
-       break;
+static void
+diff_special (void)
+{
+  struct stat stat_data;
 
-      lseek (diff_handle, sparsearray[counter].offset, 0);
+  /* FIXME: deal with umask.  */
 
-      /* Take care to not run out of room in our buffer.  */
+  if (!get_stat_data (current_stat_info.file_name, &stat_data))
+    return;
 
-      while (buffer_size < chunk_size)
-       {
-         buffer_size *= 2;
-         buffer = (char *) xrealloc (buffer, buffer_size * sizeof (char));
-       }
+  if (current_header->header.typeflag == CHRTYPE
+      ? !S_ISCHR (stat_data.st_mode)
+      : current_header->header.typeflag == BLKTYPE
+      ? !S_ISBLK (stat_data.st_mode)
+      : /* current_header->header.typeflag == FIFOTYPE */
+      !S_ISFIFO (stat_data.st_mode))
+    {
+      report_difference (&current_stat_info, _("File type differs"));
+      return;
+    }
 
-      while (chunk_size > BLOCKSIZE)
-       {
-         if (status = read (diff_handle, buffer, BLOCKSIZE),
-             status != BLOCKSIZE)
-           {
-             if (status < 0)
-               {
-                 WARN ((0, errno, _("Cannot read %s"), current_file_name));
-                 report_difference (NULL);
-               }
-             else
-               {
-                 char message[MESSAGE_BUFFER_SIZE];
+  if ((current_header->header.typeflag == CHRTYPE
+       || current_header->header.typeflag == BLKTYPE)
+      && current_stat_info.stat.st_rdev != stat_data.st_rdev)
+    {
+      report_difference (&current_stat_info, _("Device number differs"));
+      return;
+    }
 
-                 sprintf (message, _("Could only read %d of %ld bytes"),
-                          status, chunk_size);
-                 report_difference (message);
-               }
-             break;
-           }
+  if ((current_stat_info.stat.st_mode & MODE_ALL) !=
+      (stat_data.st_mode & MODE_ALL))
+    report_difference (&current_stat_info, _("Mode differs"));
+}
 
-         if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
-           {
-             different = 1;
-             break;
-           }
+static int
+dumpdir_cmp (const char *a, const char *b)
+{
+  size_t len;
 
-         chunk_size -= status;
-         remaining_size -= status;
-         set_next_block_after (data_block);
-         data_block = find_next_block ();
-       }
-      if (status = read (diff_handle, buffer, (size_t) chunk_size),
-         status != chunk_size)
-       {
-         if (status < 0)
-           {
-             WARN ((0, errno, _("Cannot read %s"), current_file_name));
-             report_difference (NULL);
-           }
-         else
-           {
-             char message[MESSAGE_BUFFER_SIZE];
+  while (*a)
+    switch (*a)
+      {
+      case 'Y':
+      case 'N':
+       if (!strchr ("YN", *b))
+         return 1;
+       if (strcmp(a + 1, b + 1))
+         return 1;
+       len = strlen (a) + 1;
+       a += len;
+       b += len;
+       break;
 
-             sprintf (message, _("Could only read %d of %ld bytes"),
-                      status, chunk_size);
-             report_difference (message);
-           }
-         break;
-       }
+      case 'D':
+       if (strcmp(a, b))
+         return 1;
+       len = strlen (a) + 1;
+       a += len;
+       b += len;
+       break;
 
-      if (memcmp (buffer, data_block->buffer, (size_t) chunk_size))
-       {
-         different = 1;
-         break;
-       }
-#if 0
-      amount_read += chunk_size;
-      if (amount_read >= BLOCKSIZE)
+      case 'R':
+      case 'T':
+      case 'X':
+       return *b;
+      }
+  return *b;
+}
+
+static void
+diff_dumpdir (struct tar_stat_info *dir)
+{
+  const char *dumpdir_buffer;
+
+  if (dir->fd == 0)
+    {
+      void (*diag) (char const *) = NULL;
+      int fd = subfile_open (dir->parent, dir->orig_file_name, open_read_flags);
+      if (fd < 0)
+       diag = open_diag;
+      else if (fstat (fd, &dir->stat))
+        {
+         diag = stat_diag;
+          close (fd);
+        }
+      else
+       dir->fd = fd;
+      if (diag)
        {
-         amount_read = 0;
-         set_next_block_after (data_block);
-         data_block = find_next_block ();
+         file_removed_diag (dir->orig_file_name, false, diag);
+         return;
        }
-#endif
-      set_next_block_after (data_block);
-      counter++;
-      remaining_size -= chunk_size;
     }
+  dumpdir_buffer = directory_contents (scan_directory (dir));
 
-#if 0
-  /* If the number of bytes read isn't the number of bytes supposedly in
-     the file, they're different.  */
+  if (dumpdir_buffer)
+    {
+      if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer))
+       report_difference (dir, _("Contents differ"));
+    }
+  else
+    read_and_process (dir, process_noop);
+}
 
-  if (amount_read != size_of_file)
-    different = 1;
-#endif
+static void
+diff_multivol (void)
+{
+  struct stat stat_data;
+  int fd, status;
+  off_t offset;
 
-  set_next_block_after (data_block);
-  free (sparsearray);
+  if (current_stat_info.had_trailing_slash)
+    {
+      diff_dir ();
+      return;
+    }
 
-  if (different)
-    report_difference (_("Data differs"));
-}
+  if (!get_stat_data (current_stat_info.file_name, &stat_data))
+    return;
 
-/*---------------------------------------------------------------------.
-| Call either stat or lstat over STAT_DATA, depending on --dereference |
-| (-h), for a file which should exist.  Diagnose any problem.  Return  |
-| nonzero for success, zero otherwise.                                |
-`---------------------------------------------------------------------*/
+  if (!S_ISREG (stat_data.st_mode))
+    {
+      report_difference (&current_stat_info, _("File type differs"));
+      skip_member ();
+      return;
+    }
 
-static int
-get_stat_data (struct stat *stat_data)
-{
-  int status = (dereference_option
-               ? stat (current_file_name, stat_data)
-               : lstat (current_file_name, stat_data));
+  offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
+  if (offset < 0
+      || INT_ADD_OVERFLOW (current_stat_info.stat.st_size, offset)
+      || stat_data.st_size != current_stat_info.stat.st_size + offset)
+    {
+      report_difference (&current_stat_info, _("Size differs"));
+      skip_member ();
+      return;
+    }
 
-  if (status < 0)
+
+  fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
+
+  if (fd < 0)
     {
-      if (errno == ENOENT)
-       report_difference (_("File does not exist"));
-      else
-       {
-         ERROR ((0, errno, _("Cannot stat file %s"), current_file_name));
-         report_difference (NULL);
-       }
-#if 0
-      skip_file ((long) current_stat.st_size);
-#endif
-      return 0;
+      open_error (current_stat_info.file_name);
+      report_difference (&current_stat_info, NULL);
+      skip_member ();
+      return;
     }
 
-  return 1;
-}
+  if (lseek (fd, offset, SEEK_SET) < 0)
+    {
+      seek_error_details (current_stat_info.file_name, offset);
+      report_difference (&current_stat_info, NULL);
+    }
+  else
+    read_and_process (&current_stat_info, process_rawdata);
 
-/*----------------------------------.
-| Diff a file against the archive.  |
-`----------------------------------*/
+  status = close (fd);
+  if (status != 0)
+    close_error (current_stat_info.file_name);
+}
 
+/* Diff a file against the archive.  */
 void
 diff_archive (void)
 {
-  struct stat stat_data;
-  int name_length;
-  int status;
-
-  errno = EPIPE;               /* FIXME: errno should be read-only */
-                               /* FIXME: remove perrors */
 
   set_next_block_after (current_header);
-  decode_header (current_header, &current_stat, &current_format, 1);
 
-  /* Print the block from `current_header' and `current_stat'.  */
+  /* Print the block from current_header and current_stat_info.  */
 
   if (verbose_option)
     {
       if (now_verifying)
        fprintf (stdlis, _("Verify "));
-      print_header ();
+      print_header (&current_stat_info, current_header, -1);
     }
 
   switch (current_header->header.typeflag)
     {
     default:
-      WARN ((0, 0, _("Unknown file type '%c' for %s, diffed as normal file"),
-                current_header->header.typeflag, current_file_name));
+      ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
+             quotearg_colon (current_stat_info.file_name),
+             current_header->header.typeflag));
       /* Fall through.  */
 
     case AREGTYPE:
@@ -463,338 +482,65 @@ diff_archive (void)
 
       /* Appears to be a file.  See if it's really a directory.  */
 
-      name_length = strlen (current_file_name) - 1;
-      if (current_file_name[name_length] == '/')
-       goto really_dir;
-
-      if (!get_stat_data (&stat_data))
-       {
-         if (current_header->oldgnu_header.isextended)
-           skip_extended_headers ();
-         skip_file ((long) current_stat.st_size);
-         goto quit;
-       }
-
-      if (!S_ISREG (stat_data.st_mode))
-       {
-         report_difference (_("Not a regular file"));
-         skip_file ((long) current_stat.st_size);
-         goto quit;
-       }
-
-      stat_data.st_mode &= 07777;
-      if (stat_data.st_mode != current_stat.st_mode)
-       report_difference (_("Mode differs"));
-
-#if !MSDOS
-      /* stat() in djgpp's C library gives a constant number of 42 as the
-        uid and gid of a file.  So, comparing an FTP'ed archive just after
-        unpack would fail on MSDOS.  */
-      if (stat_data.st_uid != current_stat.st_uid)
-       report_difference (_("Uid differs"));
-      if (stat_data.st_gid != current_stat.st_gid)
-       report_difference (_("Gid differs"));
-#endif
-
-      if (stat_data.st_mtime != current_stat.st_mtime)
-       report_difference (_("Mod time differs"));
-      if (current_header->header.typeflag != GNUTYPE_SPARSE &&
-         stat_data.st_size != current_stat.st_size)
-       {
-         report_difference (_("Size differs"));
-         skip_file ((long) current_stat.st_size);
-         goto quit;
-       }
-
-      diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
-
-      if (diff_handle < 0 && !absolute_names_option)
-       {
-         char *tmpbuf = xmalloc (strlen (current_file_name) + 2);
-
-         *tmpbuf = '/';
-         strcpy (tmpbuf + 1, current_file_name);
-         diff_handle = open (tmpbuf, O_NDELAY | O_RDONLY);
-         free (tmpbuf);
-       }
-      if (diff_handle < 0)
-       {
-         ERROR ((0, errno, _("Cannot open %s"), current_file_name));
-         if (current_header->oldgnu_header.isextended)
-           skip_extended_headers ();
-         skip_file ((long) current_stat.st_size);
-         report_difference (NULL);
-         goto quit;
-       }
-
-      /* Need to treat sparse files completely differently here.  */
-
-      if (current_header->header.typeflag == GNUTYPE_SPARSE)
-       diff_sparse_files (current_stat.st_size);
+      if (current_stat_info.had_trailing_slash)
+       diff_dir ();
       else
-       {
-         if (multi_volume_option)
-           {
-             assign_string (&save_name, current_file_name);
-             save_totsize = current_stat.st_size;
-             /* save_sizeleft is set in read_and_process.  */
-           }
-
-         read_and_process ((long) (current_stat.st_size), process_rawdata);
-
-         if (multi_volume_option)
-           assign_string (&save_name, NULL);
-       }
-
-      status = close (diff_handle);
-      if (status < 0)
-       ERROR ((0, errno, _("Error while closing %s"), current_file_name));
-
-    quit:
+       diff_file ();
       break;
 
-#if !MSDOS
     case LNKTYPE:
-      {
-       dev_t dev;
-       ino_t ino;
-
-       if (!get_stat_data (&stat_data))
-         break;
-
-       dev = stat_data.st_dev;
-       ino = stat_data.st_ino;
-       status = stat (current_link_name, &stat_data);
-       if (status < 0)
-         {
-           if (errno == ENOENT)
-             report_difference (_("Does not exist"));
-           else
-             {
-               WARN ((0, errno, _("Cannot stat file %s"), current_file_name));
-               report_difference (NULL);
-             }
-           break;
-         }
-
-       if (stat_data.st_dev != dev || stat_data.st_ino != ino)
-         {
-           char *message = (char *)
-             xmalloc (MESSAGE_BUFFER_SIZE + strlen (current_link_name));
-
-           sprintf (message, _("Not linked to %s"), current_link_name);
-           report_difference (message);
-           free (message);
-           break;
-         }
-
-       break;
-      }
-#endif /* not MSDOS */
+      diff_link ();
+      break;
 
-#ifdef S_ISLNK
+#ifdef HAVE_READLINK
     case SYMTYPE:
-      {
-       char linkbuf[NAME_FIELD_SIZE + 3]; /* FIXME: may be too short.  */
-
-       status = readlink (current_file_name, linkbuf, (sizeof linkbuf) - 1);
-
-       if (status < 0)
-         {
-           if (errno == ENOENT)
-             report_difference (_("No such file or directory"));
-           else
-             {
-               WARN ((0, errno, _("Cannot read link %s"), current_file_name));
-               report_difference (NULL);
-             }
-           break;
-         }
-
-       linkbuf[status] = '\0'; /* null-terminate it */
-       if (strncmp (current_link_name, linkbuf, (size_t) status) != 0)
-         report_difference (_("Symlink differs"));
-
-       break;
-      }
-#endif /* not S_ISLNK */
+      diff_symlink ();
+      break;
+#endif
 
-#ifdef S_IFCHR
     case CHRTYPE:
-      current_stat.st_mode |= S_IFCHR;
-      goto check_node;
-#endif /* not S_IFCHR */
-
-#ifdef S_IFBLK
-      /* If local system doesn't support block devices, use default case.  */
-
     case BLKTYPE:
-      current_stat.st_mode |= S_IFBLK;
-      goto check_node;
-#endif /* not S_IFBLK */
-
-#ifdef S_ISFIFO
-      /* If local system doesn't support FIFOs, use default case.  */
-
     case FIFOTYPE:
-# ifdef S_IFIFO
-      current_stat.st_mode |= S_IFIFO;
-# endif
-      current_stat.st_rdev = 0;        /* FIXME: do we need this? */
-      goto check_node;
-#endif /* S_ISFIFO */
-
-    check_node:
-      /* FIXME: deal with umask.  */
-
-      if (!get_stat_data (&stat_data))
-       break;
-
-      if (current_stat.st_rdev != stat_data.st_rdev)
-       {
-         report_difference (_("Device numbers changed"));
-         break;
-       }
-
-      if (
-#ifdef S_IFMT
-         current_stat.st_mode != stat_data.st_mode
-#else
-         /* POSIX lossage.  */
-         (current_stat.st_mode & 07777) != (stat_data.st_mode & 07777)
-#endif
-         )
-       {
-         report_difference (_("Mode or device-type changed"));
-         break;
-       }
-
+      diff_special ();
       break;
 
     case GNUTYPE_DUMPDIR:
-      {
-       char *dumpdir_buffer = get_directory_contents (current_file_name, 0);
-
-       if (multi_volume_option)
-         {
-           assign_string (&save_name, current_file_name);
-           save_totsize = current_stat.st_size;
-           /* save_sizeleft is set in read_and_process.  */
-         }
-
-       if (dumpdir_buffer)
-         {
-           dumpdir_cursor = dumpdir_buffer;
-           read_and_process ((long) (current_stat.st_size), process_dumpdir);
-           free (dumpdir_buffer);
-         }
-       else
-         read_and_process ((long) (current_stat.st_size), process_noop);
-
-       if (multi_volume_option)
-         assign_string (&save_name, NULL);
-       /* Fall through.  */
-      }
-
     case DIRTYPE:
-      /* Check for trailing /.  */
-
-      name_length = strlen (current_file_name) - 1;
-
-    really_dir:
-      while (name_length && current_file_name[name_length] == '/')
-       current_file_name[name_length--] = '\0';        /* zap / */
-
-      if (!get_stat_data (&stat_data))
-       break;
-
-      if (!S_ISDIR (stat_data.st_mode))
-       {
-         report_difference (_("No longer a directory"));
-         break;
-       }
-
-      if ((stat_data.st_mode & 07777) != (current_stat.st_mode & 07777))
-       report_difference (_("Mode differs"));
+      if (is_dumpdir (&current_stat_info))
+       diff_dumpdir (&current_stat_info);
+      diff_dir ();
       break;
 
     case GNUTYPE_VOLHDR:
       break;
 
     case GNUTYPE_MULTIVOL:
-      {
-       off_t offset;
-
-       name_length = strlen (current_file_name) - 1;
-       if (current_file_name[name_length] == '/')
-         goto really_dir;
-
-       if (!get_stat_data (&stat_data))
-         break;
-
-       if (!S_ISREG (stat_data.st_mode))
-         {
-           report_difference (_("Not a regular file"));
-           skip_file ((long) current_stat.st_size);
-           break;
-         }
-
-       stat_data.st_mode &= 07777;
-       offset = from_oct (1 + 12, current_header->oldgnu_header.offset);
-       if (stat_data.st_size != current_stat.st_size + offset)
-         {
-           report_difference (_("Size differs"));
-           skip_file ((long) current_stat.st_size);
-           break;
-         }
-
-       diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
-
-       if (diff_handle < 0)
-         {
-           WARN ((0, errno, _("Cannot open file %s"), current_file_name));
-           report_difference (NULL);
-           skip_file ((long) current_stat.st_size);
-           break;
-         }
-
-       status = lseek (diff_handle, offset, 0);
-       if (status != offset)
-         {
-           WARN ((0, errno, _("Cannot seek to %ld in file %s"),
-                  offset, current_file_name));
-           report_difference (NULL);
-           break;
-         }
-
-       if (multi_volume_option)
-         {
-           assign_string (&save_name, current_file_name);
-           save_totsize = stat_data.st_size;
-           /* save_sizeleft is set in read_and_process.  */
-         }
-
-       read_and_process ((long) (current_stat.st_size), process_rawdata);
-
-       if (multi_volume_option)
-         assign_string (&save_name, NULL);
-
-       status = close (diff_handle);
-       if (status < 0)
-         ERROR ((0, errno, _("Error while closing %s"), current_file_name));
-
-       break;
-      }
+      diff_multivol ();
     }
 }
 
-/*---.
-| ?  |
-`---*/
-
 void
 verify_volume (void)
 {
+  int may_fail = 0;
+  if (removed_prefixes_p ())
+    {
+      WARN((0, 0,
+           _("Archive contains file names with leading prefixes removed.")));
+      may_fail = 1;
+    }
+  if (transform_program_p ())
+    {
+      WARN((0, 0,
+           _("Archive contains transformed file names.")));
+      may_fail = 1;
+    }
+  if (may_fail)
+    WARN((0, 0,
+         _("Verification may fail to locate original files.")));
+
+  clear_directory_table ();
+
   if (!diff_buffer)
     diff_init ();
 
@@ -829,12 +575,10 @@ verify_volume (void)
                status < 0))
          {
 #endif
-           if (rmtlseek (archive, 0L, 0) != 0)
+           if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
              {
                /* Lseek failed.  Try a different method.  */
-
-               WARN ((0, errno,
-                      _("Could not rewind archive file for verify")));
+               seek_warn (archive_name_array[0]);
                return;
              }
 #ifdef MTIOCTOP
@@ -849,24 +593,51 @@ verify_volume (void)
   flush_read ();
   while (1)
     {
-      enum read_header status = read_header ();
+      enum read_header status = read_header (&current_header,
+                                             &current_stat_info,
+                                             read_header_auto);
 
       if (status == HEADER_FAILURE)
        {
          int counter = 0;
 
-         while (status == HEADER_FAILURE);
+         do
            {
              counter++;
-             status = read_header ();
+             set_next_block_after (current_header);
+             status = read_header (&current_header, &current_stat_info,
+                                   read_header_auto);
            }
+         while (status == HEADER_FAILURE);
+
          ERROR ((0, 0,
-                 _("VERIFY FAILURE: %d invalid header(s) detected"), counter));
+                 ngettext ("VERIFY FAILURE: %d invalid header detected",
+                           "VERIFY FAILURE: %d invalid headers detected",
+                           counter), counter));
        }
-      if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
+      if (status == HEADER_END_OF_FILE)
        break;
+      if (status == HEADER_ZERO_BLOCK)
+       {
+         set_next_block_after (current_header);
+          if (!ignore_zeros_option)
+            {
+             char buf[UINTMAX_STRSIZE_BOUND];
+
+             status = read_header (&current_header, &current_stat_info,
+                                   read_header_auto);
+             if (status == HEADER_ZERO_BLOCK)
+               break;
+             WARNOPT (WARN_ALONE_ZERO_BLOCK,
+                      (0, 0, _("A lone zero block at %s"),
+                       STRINGIFY_BIGINT (current_block_ordinal (), buf)));
+            }
+         continue;
+       }
 
+      decode_header (current_header, &current_stat_info, &current_format, 1);
       diff_archive ();
+      tar_stat_destroy (&current_stat_info);
     }
 
   access_mode = ACCESS_WRITE;
This page took 0.043695 seconds and 4 git commands to generate.