X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Ftar;a=blobdiff_plain;f=src%2Fcompare.c;h=d29cfdd4822f683dec5c7dfb8dc01544c62330ce;hp=d4871c44350d9bb4d88c28390d75196f27775481;hb=45ccda119355a1087450039a250359c1d0de0d08;hpb=799e915cd3a889058eeefeaa25391cfe1eae7c3a 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;