]> Dogcows Code - chaz/tar/blobdiff - src/compare.c
maint: remove unnecessary file
[chaz/tar] / src / compare.c
index d4871c44350d9bb4d88c28390d75196f27775481..0e99a7201cadf528bf57f1dc866c68c35091146c 100644 (file)
@@ -1,10 +1,14 @@
 /* Diff files from a tar archive.
 /* Diff files from a tar archive.
-   Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
+
+   Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
+   2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 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
    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
+   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
    version.
 
    This program is distributed in the hope that it will be useful, but
 
    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.,
 
    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.  */
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 
-#include "system.h"
+#include <system.h>
+#include <system-ioctl.h>
 
 #if HAVE_LINUX_FD_H
 # include <linux/fd.h>
 #endif
 
 #include "common.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.  */
 
 /* 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.  */
 
 /* 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)
 {
 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
 static int
-process_noop (long size, char *data)
+process_noop (size_t size __attribute__ ((unused)),
+             char *data __attribute__ ((unused)))
 {
   return 1;
 }
 
 {
   return 1;
 }
 
-/*---.
-| ?  |
-`---*/
-
 static int
 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 != 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
        {
        }
       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;
     }
 
        }
       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;
 }
 
       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
 
 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;
 {
   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 ();
   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;
        }
 
          return;
        }
 
@@ -168,292 +141,336 @@ read_and_process (long size, int (*processor) (long, char *))
       set_next_block_after ((union block *)
                            (data_block->buffer + data_size - 1));
       size -= data_size;
       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
 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
 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
 
 #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;
+      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);
+      return;
+    }
 
 
-/*----------------------------------.
-| Diff a file against the archive.  |
-`----------------------------------*/
+  read_and_process (&current_stat_info, process_rawdata);
 
 
+  status = close (fd);
+  if (status != 0)
+    close_error (current_stat_info.file_name);
+}
+
+/* Diff a file against the archive.  */
 void
 diff_archive (void)
 {
 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);
 
   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 "));
 
   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:
     }
 
   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:
       /* Fall through.  */
 
     case AREGTYPE:
@@ -463,338 +480,65 @@ diff_archive (void)
 
       /* Appears to be a file.  See if it's really a directory.  */
 
 
       /* 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
       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;
 
       break;
 
-#if !MSDOS
     case LNKTYPE:
     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:
     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:
     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:
     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:
     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:
       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:
     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:
       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)
 {
 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 ();
 
   if (!diff_buffer)
     diff_init ();
 
@@ -829,12 +573,10 @@ verify_volume (void)
                status < 0))
          {
 #endif
                status < 0))
          {
 #endif
-           if (rmtlseek (archive, 0L, 0) != 0)
+           if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
              {
                /* Lseek failed.  Try a different method.  */
              {
                /* 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
                return;
              }
 #ifdef MTIOCTOP
@@ -849,24 +591,51 @@ verify_volume (void)
   flush_read ();
   while (1)
     {
   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;
 
 
       if (status == HEADER_FAILURE)
        {
          int counter = 0;
 
-         while (status == HEADER_FAILURE);
+         do
            {
              counter++;
            {
              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,
          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;
        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 ();
       diff_archive ();
+      tar_stat_destroy (&current_stat_info);
     }
 
   access_mode = ACCESS_WRITE;
     }
 
   access_mode = ACCESS_WRITE;
This page took 0.052191 seconds and 4 git commands to generate.