X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcompare.c;h=d29cfdd4822f683dec5c7dfb8dc01544c62330ce;hb=55fb2fc38fbdd9ea39da0ab84ed4748f16ab9b73;hp=d4871c44350d9bb4d88c28390d75196f27775481;hpb=799e915cd3a889058eeefeaa25391cfe1eae7c3a;p=chaz%2Ftar
diff --git a/src/compare.c b/src/compare.c
index d4871c4..d29cfdd 100644
--- a/src/compare.c
+++ b/src/compare.c
@@ -1,162 +1,135 @@
/* 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 .
+
+ Written by John Gilmore, on 1987-04-30. */
+
+#include
+#include
#if HAVE_LINUX_FD_H
# include
#endif
#include "common.h"
-#include "rmt.h"
-
-/* Spare space for messages, hopefully safe even after gettext. */
-#define MESSAGE_BUFFER_SIZE 100
+#include
+#include
+#include
/* 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 (¤t_stat_info, NULL);
}
else
{
- sprintf (message, _("Could only read %d of %ld bytes"),
- status, bytes);
- report_difference (message);
+ report_difference (¤t_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 (¤t_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 (¤t_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;
+}
+
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 (¤t_stat_info, _("File type differs"));
+ else if ((current_stat_info.stat.st_mode & MODE_ALL) !=
+ (stat_data.st_mode & MODE_ALL))
+ report_difference (¤t_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 (¤t_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 (¤t_stat_info, _("Mode differs"));
+
+ if (!sys_compare_uid (&stat_data, ¤t_stat_info.stat))
+ report_difference (¤t_stat_info, _("Uid differs"));
+ if (!sys_compare_gid (&stat_data, ¤t_stat_info.stat))
+ report_difference (¤t_stat_info, _("Gid differs"));
+
+ if (tar_timespec_cmp (get_stat_mtime (&stat_data),
+ current_stat_info.mtime))
+ report_difference (¤t_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 (¤t_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 (¤t_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, ¤t_stat_info);
+ else
+ read_and_process (¤t_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 (¤t_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 (¤t_stat_info, NULL);
+ }
+ else if (status != len
+ || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
+ report_difference (¤t_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 (¤t_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 (¤t_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 (¤t_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 (¤t_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 (¤t_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 (¤t_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 (¤t_stat_info, NULL);
+ }
+ else
+ read_and_process (¤t_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, ¤t_stat, ¤t_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 (¤t_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 (¤t_stat_info))
+ diff_dumpdir (¤t_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 (¤t_header,
+ ¤t_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 (¤t_header, ¤t_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 (¤t_header, ¤t_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, ¤t_stat_info, ¤t_format, 1);
diff_archive ();
+ tar_stat_destroy (¤t_stat_info);
}
access_mode = ACCESS_WRITE;