Browse Source

GNU tar 1.12

Paul Eggert 28 years ago
parent
commit
799e915cd3
2 changed files with 1052 additions and 0 deletions
  1. 874 0
      src/compare.c
  2. 178 0
      src/open3.c

+ 874 - 0
src/compare.c

@@ -0,0 +1,874 @@
+/* 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.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+   Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation, Inc.,
+   59 Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "system.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
+
+/* Nonzero if we are verifying at the moment.  */
+int now_verifying = 0;
+
+/* 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 |
+`--------------------------------*/
+
+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));
+}
+
+/*------------------------------------------------------------------------.
+| 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)
+{
+  if (message)
+    fprintf (stdlis, "%s: %s\n", current_file_name, message);
+
+  if (exit_status == TAREXIT_SUCCESS)
+    exit_status = TAREXIT_DIFFERS;
+}
+
+/*-----------------------------------------------------------------------.
+| Takes a buffer returned by read_and_process and does nothing with it.	 |
+`-----------------------------------------------------------------------*/
+
+/* Yes, I know.  SIZE and DATA are unused in this function.  Some compilers
+   may even report it.  That's OK, just relax!  */
+
+static int
+process_noop (long size, char *data)
+{
+  return 1;
+}
+
+/*---.
+| ?  |
+`---*/
+
+static int
+process_rawdata (long bytes, char *buffer)
+{
+  int status = read (diff_handle, diff_buffer, (size_t) bytes);
+  char message[MESSAGE_BUFFER_SIZE];
+
+  if (status != bytes)
+    {
+      if (status < 0)
+	{
+	  WARN ((0, errno, _("Cannot read %s"), current_file_name));
+	  report_difference (NULL);
+	}
+      else
+	{
+	  sprintf (message, _("Could only read %d of %ld bytes"),
+		   status, bytes);
+	  report_difference (message);
+	}
+      return 0;
+    }
+
+  if (memcmp (buffer, diff_buffer, (size_t) bytes))
+    {
+      report_difference (_("Data differs"));
+      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.                                                      |
+`------------------------------------------------------------------------*/
+
+static void
+read_and_process (long size, int (*processor) (long, char *))
+{
+  union block *data_block;
+  long data_size;
+
+  if (multi_volume_option)
+    save_sizeleft = size;
+  while (size)
+    {
+      data_block = find_next_block ();
+      if (data_block == NULL)
+	{
+	  ERROR ((0, 0, _("Unexpected EOF on archive file")));
+	  return;
+	}
+
+      data_size = available_space_after (data_block);
+      if (data_size > size)
+	data_size = size;
+      if (!(*processor) (data_size, data_block->buffer))
+	processor = process_noop;
+      set_next_block_after ((union block *)
+			    (data_block->buffer + data_size - 1));
+      size -= data_size;
+      if (multi_volume_option)
+	save_sizeleft -= data_size;
+    }
+}
+
+/*---.
+| ?  |
+`---*/
+
+/* 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.
+   */
+
+static void
+fill_in_sparse_array (void)
+{
+  int counter;
+
+  /* Allocate space for our scratch space; it's initially 10 elements
+     long, but can change in this routine if necessary.  */
+
+  sp_array_size = 10;
+  sparsearray = (struct sp_array *) xmalloc (sp_array_size * sizeof (struct sp_array));
+
+  /* There are at most five of these structures in the header itself;
+     read these in first.  */
+
+  for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
+    {
+      /* 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);
+    }
+
+  /* If the header's extended, we gotta read in exhdr's till we're done.  */
+
+  if (current_header->oldgnu_header.isextended)
+    {
+      /* How far into the sparsearray we are `so far'.  */
+      static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
+      union block *exhdr;
+
+      while (1)
+	{
+	  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.  */
+
+	      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 this is the last extended header for this file, we can
+	     stop.  */
+
+	  if (!exhdr->sparse_header.isextended)
+	    break;
+
+	  so_far_ind += SPARSES_IN_SPARSE_HEADER;
+	  set_next_block_after (exhdr);
+	}
+
+      /* 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_sparse_files (int size_of_file)
+{
+  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;
+
+  fill_in_sparse_array ();
+
+  while (remaining_size > 0)
+    {
+      int status;
+      long chunk_size;
+#if 0
+      int amount_read = 0;
+#endif
+
+      data_block = find_next_block ();
+      chunk_size = sparsearray[counter].numbytes;
+      if (!chunk_size)
+	break;
+
+      lseek (diff_handle, sparsearray[counter].offset, 0);
+
+      /* Take care to not run out of room in our buffer.  */
+
+      while (buffer_size < chunk_size)
+	{
+	  buffer_size *= 2;
+	  buffer = (char *) xrealloc (buffer, buffer_size * sizeof (char));
+	}
+
+      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];
+
+		  sprintf (message, _("Could only read %d of %ld bytes"),
+			   status, chunk_size);
+		  report_difference (message);
+		}
+	      break;
+	    }
+
+	  if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
+	    {
+	      different = 1;
+	      break;
+	    }
+
+	  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];
+
+	      sprintf (message, _("Could only read %d of %ld bytes"),
+		       status, chunk_size);
+	      report_difference (message);
+	    }
+	  break;
+	}
+
+      if (memcmp (buffer, data_block->buffer, (size_t) chunk_size))
+	{
+	  different = 1;
+	  break;
+	}
+#if 0
+      amount_read += chunk_size;
+      if (amount_read >= BLOCKSIZE)
+	{
+	  amount_read = 0;
+	  set_next_block_after (data_block);
+	  data_block = find_next_block ();
+	}
+#endif
+      set_next_block_after (data_block);
+      counter++;
+      remaining_size -= chunk_size;
+    }
+
+#if 0
+  /* If the number of bytes read isn't the number of bytes supposedly in
+     the file, they're different.  */
+
+  if (amount_read != size_of_file)
+    different = 1;
+#endif
+
+  set_next_block_after (data_block);
+  free (sparsearray);
+
+  if (different)
+    report_difference (_("Data differs"));
+}
+
+/*---------------------------------------------------------------------.
+| 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 (struct stat *stat_data)
+{
+  int status = (dereference_option
+		? stat (current_file_name, stat_data)
+		: lstat (current_file_name, stat_data));
+
+  if (status < 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;
+    }
+
+  return 1;
+}
+
+/*----------------------------------.
+| 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'.  */
+
+  if (verbose_option)
+    {
+      if (now_verifying)
+	fprintf (stdlis, _("Verify "));
+      print_header ();
+    }
+
+  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));
+      /* Fall through.  */
+
+    case AREGTYPE:
+    case REGTYPE:
+    case GNUTYPE_SPARSE:
+    case CONTTYPE:
+
+      /* 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);
+      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:
+      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 */
+
+#ifdef S_ISLNK
+    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 */
+
+#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;
+	}
+
+      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"));
+      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;
+      }
+    }
+}
+
+/*---.
+| ?  |
+`---*/
+
+void
+verify_volume (void)
+{
+  if (!diff_buffer)
+    diff_init ();
+
+  /* Verifying an archive is meant to check if the physical media got it
+     correctly, so try to defeat clever in-memory buffering pertaining to
+     this particular media.  On Linux, for example, the floppy drive would
+     not even be accessed for the whole verification.
+
+     The code was using fsync only when the ioctl is unavailable, but
+     Marty Leisner says that the ioctl does not work when not preceded by
+     fsync.  So, until we know better, or maybe to please Marty, let's do it
+     the unbelievable way :-).  */
+
+#if HAVE_FSYNC
+  fsync (archive);
+#endif
+#ifdef FDFLUSH
+  ioctl (archive, FDFLUSH);
+#endif
+
+#ifdef MTIOCTOP
+  {
+    struct mtop operation;
+    int status;
+
+    operation.mt_op = MTBSF;
+    operation.mt_count = 1;
+    if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
+      {
+	if (errno != EIO
+	    || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
+		status < 0))
+	  {
+#endif
+	    if (rmtlseek (archive, 0L, 0) != 0)
+	      {
+		/* Lseek failed.  Try a different method.  */
+
+		WARN ((0, errno,
+		       _("Could not rewind archive file for verify")));
+		return;
+	      }
+#ifdef MTIOCTOP
+	  }
+      }
+  }
+#endif
+
+  access_mode = ACCESS_READ;
+  now_verifying = 1;
+
+  flush_read ();
+  while (1)
+    {
+      enum read_header status = read_header ();
+
+      if (status == HEADER_FAILURE)
+	{
+	  int counter = 0;
+
+	  while (status == HEADER_FAILURE);
+	    {
+	      counter++;
+	      status = read_header ();
+	    }
+	  ERROR ((0, 0,
+		  _("VERIFY FAILURE: %d invalid header(s) detected"), counter));
+	}
+      if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
+	break;
+
+      diff_archive ();
+    }
+
+  access_mode = ACCESS_WRITE;
+  now_verifying = 0;
+}

+ 178 - 0
src/open3.c

@@ -0,0 +1,178 @@
+/* Defines for Sys V style 3-argument open call.
+   Copyright (C) 1988, 1994, 1995, 1996 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any later
+   version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+   Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation, Inc.,
+   59 Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "system.h"
+
+#if EMUL_OPEN3
+
+/* open3.h -- #defines for the various flags for the Sys V style 3-argument
+   open() call.  On BSD or System 5, the system already has this in an
+   include file.  This file is needed for V7 and MINIX systems for the
+   benefit of open3() in port.c, a routine that emulates the 3-argument call
+   using system calls available on V7/MINIX.
+
+   Written 1987-06-10 by Richard Todd.
+
+   The names have been changed by John Gilmore, 1987-07-31, since Richard
+   called it "bsdopen", and really this change was introduced in AT&T Unix
+   systems before BSD picked it up.  */
+
+/*-----------------------------------------------------------------------.
+| open3 -- routine to emulate the 3-argument open system.		 |
+| 									 |
+| open3 (path, flag, mode);						 |
+| 									 |
+| Attempts to open the file specified by the given pathname.  The	 |
+| following flag bits specify options to the routine.  Needless to say,	 |
+| you should only specify one of the first three.  Function returns file |
+| descriptor if successful, -1 and errno if not.			 |
+`-----------------------------------------------------------------------*/
+
+/* The routine obeys the following mode arguments:
+
+   O_RDONLY	file open for read only
+   O_WRONLY	file open for write only
+   O_RDWR	file open for both read & write
+
+   O_CREAT	file is created with specified mode if it needs to be
+   O_TRUNC	if file exists, it is truncated to 0 bytes
+   O_EXCL	used with O_CREAT--routine returns error if file exists  */
+
+/* Call that if present in most modern Unix systems.  This version attempts
+   to support all the flag bits except for O_NDELAY and O_APPEND, which are
+   silently ignored.  The emulation is not as efficient as the real thing
+   (at worst, 4 system calls instead of one), but there's not much I can do
+   about that.  */
+
+/* Array to give arguments to access for various modes FIXME, this table
+   depends on the specific integer values of O_*, and also contains
+   integers (args to 'access') that should be #define's.  */
+
+static int modes[] =
+  {
+    04,				/* O_RDONLY */
+    02,				/* O_WRONLY */
+    06,				/* O_RDWR */
+    06,				/* invalid, just cope: O_WRONLY+O_RDWR */
+  };
+
+/* Shut off the automatic emulation of open(), we'll need it. */
+#undef open
+
+int
+open3 (char *path, int flags, int mode)
+{
+  int exists = 1;
+  int call_creat = 0;
+
+  /* We actually do the work by calling the open() or creat() system
+     call, depending on the flags.  Call_creat is true if we will use
+     creat(), false if we will use open().  */
+
+  /* See if the file exists and is accessible in the requested mode.
+
+     Strictly speaking we shouldn't be using access, since access checks
+     against real uid, and the open call should check against euid.  Most
+     cases real uid == euid, so it won't matter.  FIXME.  FIXME, the
+     construction "flags & 3" and the modes table depends on the specific
+     integer values of the O_* #define's.  Foo!  */
+
+  if (access (path, modes[flags & 3]) < 0)
+    {
+      if (errno == ENOENT)
+	{
+	  /* The file does not exist.  */
+
+	  exists = 0;
+	}
+      else
+	{
+	  /* Probably permission violation.  */
+
+	  if (flags & O_EXCL)
+	    {
+	      /* Oops, the file exists, we didn't want it.  No matter
+		 what the error, claim EEXIST.  */
+
+	      errno = EEXIST;	/* FIXME: errno should be read-only */
+	    }
+	  return -1;
+	}
+    }
+
+  /* If we have the O_CREAT bit set, check for O_EXCL.  */
+
+  if (flags & O_CREAT)
+    {
+      if ((flags & O_EXCL) && exists)
+	{
+	  /* Oops, the file exists and we didn't want it to.  */
+
+	  errno = EEXIST;	/* FIXME: errno should be read-only */
+	  return -1;
+	}
+
+      /* If the file doesn't exist, be sure to call creat() so that it
+	 will be created with the proper mode.  */
+
+      if (!exists)
+	call_creat = 1;
+    }
+  else
+    {
+      /* If O_CREAT isn't set and the file doesn't exist, error.  */
+
+      if (!exists)
+	{
+	  errno = ENOENT;	/* FIXME: errno should be read-only */
+	  return -1;
+	}
+    }
+
+  /* If the O_TRUNC flag is set and the file exists, we want to call
+     creat() anyway, since creat() guarantees that the file will be
+     truncated and open()-for-writing doesn't.  (If the file doesn't
+     exist, we're calling creat() anyway and the file will be created
+     with zero length.)  */
+
+  if ((flags & O_TRUNC) && exists)
+    call_creat = 1;
+
+  /* Actually do the call.  */
+
+  if (call_creat)
+
+    /* Call creat.  May have to close and reopen the file if we want
+       O_RDONLY or O_RDWR access -- creat() only gives O_WRONLY.  */
+
+    {
+      int fd = creat (path, mode);
+
+      if (fd < 0 || (flags & O_WRONLY))
+	return fd;
+      if (close (fd) < 0)
+	return -1;
+
+      /* Fall out to reopen the file we've created.  */
+    }
+
+  /* Calling old open, we strip most of the new flags just in case.  */
+
+  return open (path, flags & (O_RDONLY | O_WRONLY | O_RDWR | O_BINARY));
+}
+
+#endif /* EMUL_OPEN3 */