X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcompare.c;h=98a953bf403aa524ead280729c106f1379fd1e35;hb=d659cbaccdc1f3279c49107cf15f15a639738529;hp=d4871c44350d9bb4d88c28390d75196f27775481;hpb=799e915cd3a889058eeefeaa25391cfe1eae7c3a;p=chaz%2Ftar diff --git a/src/compare.c b/src/compare.c index d4871c4..98a953b 100644 --- a/src/compare.c +++ b/src/compare.c @@ -1,5 +1,8 @@ /* 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 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 @@ -14,120 +17,111 @@ 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 #if HAVE_LINUX_FD_H # include #endif -#include "common.h" -#include "rmt.h" +#include +#include -/* Spare space for messages, hopefully safe even after gettext. */ -#define MESSAGE_BUFFER_SIZE 100 +#include "common.h" +#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 __attribute__ ((unused)), + const char *fmt, ...) { - if (message) - fprintf (stdlis, "%s: %s\n", current_file_name, message); + if (fmt) + { + va_list ap; + + fprintf (stdlis, "%s: ", quotearg_colon (current_stat_info.file_name)); + va_start (ap, fmt); + vfprintf (stdlis, fmt, ap); + va_end (ap); + fprintf (stdlis, "\n"); + } 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! */ - +/* 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 = safe_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) +process_dumpdir (size_t bytes, char *buffer) { - if (memcmp (buffer, dumpdir_cursor, (size_t) bytes)) + if (memcmp (buffer, dumpdir_cursor, bytes)) { - report_difference (_("Data differs")); + report_difference (¤t_stat_info, _("Contents differ")); return 0; } @@ -135,28 +129,25 @@ process_dumpdir (long bytes, char *buffer) 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. It it return error once, continue skipping + without calling PROCESSOR anymore. */ static void -read_and_process (long size, int (*processor) (long, char *)) +read_and_process (off_t size, int (*processor) (size_t, char *)) { union block *data_block; - long data_size; + size_t data_size; if (multi_volume_option) save_sizeleft = size; 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; } @@ -173,287 +164,309 @@ read_and_process (long size, int (*processor) (long, char *)) } } -/*---. -| ? | -`---*/ +/* 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 (dereference_option, file_name, stat_data); -/* 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. - */ + if (status != 0) + { + if (errno == ENOENT) + stat_warn (file_name); + else + stat_error (file_name); + report_difference (¤t_stat_info, NULL); + return 0; + } + 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) +{ + struct stat stat_data; - for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++) + if (!get_stat_data (current_stat_info.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; + 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")); - while (1) + if (stat_data.st_mtime != current_stat_info.stat.st_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++) + report_difference (¤t_stat_info, _("Size differs")); + skip_member (); + } + else + { + diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY); + + if (diff_handle < 0) { - if (counter + so_far_ind > sp_array_size - 1) - { - /* We just ran out of room in our scratch area - - realloc it. */ + open_error (current_stat_info.file_name); + skip_member (); + report_difference (¤t_stat_info, NULL); + } + else + { + int status; - sp_array_size *= 2; - sparsearray = (struct sp_array *) - xrealloc (sparsearray, - sp_array_size * sizeof (struct sp_array)); + if (current_stat_info.is_sparse) + sparse_diff_file (diff_handle, ¤t_stat_info); + else + { + if (multi_volume_option) + { + assign_string (&save_name, + current_stat_info.orig_file_name); + save_totsize = current_stat_info.stat.st_size; + /* save_sizeleft is set in read_and_process. */ + } + + read_and_process (current_stat_info.stat.st_size, + process_rawdata); + + if (multi_volume_option) + assign_string (&save_name, 0); } - /* Convert the character strings into longs. */ + status = close (diff_handle); + if (status != 0) + close_error (current_stat_info.file_name); - 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 (atime_preserve_option) + { + struct timespec ts[2]; + ts[0] = get_stat_atime (&stat_data); + ts[1] = get_stat_mtime (&stat_data); + if (utimens (current_stat_info.file_name, ts) != 0) + utime_error (current_stat_info.file_name); + } } - - /* 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_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 = readlink (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 void +diff_dumpdir (void) +{ + char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name, + 0); - 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]; + if (multi_volume_option) + { + assign_string (&save_name, current_stat_info.orig_file_name); + save_totsize = current_stat_info.stat.st_size; + /* save_sizeleft is set in read_and_process. */ + } - sprintf (message, _("Could only read %d of %ld bytes"), - status, chunk_size); - report_difference (message); - } - break; - } + if (dumpdir_buffer) + { + dumpdir_cursor = dumpdir_buffer; + read_and_process (current_stat_info.stat.st_size, process_dumpdir); + free (dumpdir_buffer); + } + else + read_and_process (current_stat_info.stat.st_size, process_noop); - 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 (multi_volume_option) + assign_string (&save_name, 0); +} + +static void +diff_multivol (void) +{ + struct stat stat_data; + int fd, status; + off_t offset; + + if (current_stat_info.had_trailing_slash) + { + diff_dir (); + return; } -#if 0 - /* If the number of bytes read isn't the number of bytes supposedly in - the file, they're different. */ + if (!get_stat_data (current_stat_info.file_name, &stat_data)) + return; - if (amount_read != size_of_file) - different = 1; -#endif + if (!S_ISREG (stat_data.st_mode)) + { + report_difference (¤t_stat_info, _("File type differs")); + skip_member (); + return; + } - set_next_block_after (data_block); - free (sparsearray); + offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset); + if (stat_data.st_size != current_stat_info.stat.st_size + offset) + { + report_difference (¤t_stat_info, _("Size differs")); + skip_member (); + return; + } - if (different) - report_difference (_("Data differs")); -} + fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY); -/*---------------------------------------------------------------------. -| 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 (fd < 0) + { + open_error (current_stat_info.file_name); + report_difference (¤t_stat_info, NULL); + 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)); + if (lseek (fd, offset, SEEK_SET) < 0) + { + seek_error_details (current_stat_info.file_name, offset); + report_difference (¤t_stat_info, NULL); + return; + } - if (status < 0) + if (multi_volume_option) { - 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; + assign_string (&save_name, current_stat_info.orig_file_name); + save_totsize = stat_data.st_size; + /* save_sizeleft is set in read_and_process. */ } - return 1; -} + read_and_process (current_stat_info.stat.st_size, process_rawdata); -/*----------------------------------. -| Diff a file against the archive. | -`----------------------------------*/ + if (multi_volume_option) + assign_string (&save_name, 0); + 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); + decode_header (current_header, ¤t_stat_info, ¤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, -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 +476,55 @@ 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. */ - } + diff_dumpdir (); + /* 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")); + 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) { + if (removed_prefixes_p ()) + { + WARN((0, 0, + _("Archive contains file names with leading prefixes removed."))); + WARN((0, 0, + _("Verification may fail to locate original files."))); + } + if (!diff_buffer) diff_init (); @@ -829,12 +559,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 +577,31 @@ verify_volume (void) flush_read (); while (1) { - enum read_header status = read_header (); + enum read_header status = read_header (false); 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 (false); } + 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) break; diff_archive (); + tar_stat_destroy (¤t_stat_info); + xheader_destroy (&extended_header); } access_mode = ACCESS_WRITE;