1 /* Diff files from a tar archive.
3 Copyright 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001 Free
4 Software Foundation, Inc.
6 Written by John Gilmore, on 1987-04-30.
8 This program is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any later
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 Public License for more details.
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* $FreeBSD: src/contrib/tar/src/compare.c,v 1.3.2.1 2002/07/14 13:19:44 sobomax Exp $ */
37 # include <linux/fd.h>
45 /* Spare space for messages, hopefully safe even after gettext. */
46 #define MESSAGE_BUFFER_SIZE 100
48 /* Nonzero if we are verifying at the moment. */
51 /* File descriptor for the file we are diffing. */
52 static int diff_handle;
54 /* Area for reading file contents into. */
55 static char *diff_buffer;
57 /* Initialize for a diff operation. */
61 diff_buffer = valloc (record_size);
66 /* Sigh about something that differs by writing a MESSAGE to stdlis,
67 given MESSAGE is nonzero. Also set the exit status if not already. */
69 report_difference (const char *message)
72 fprintf (stdlis, "%s: %s\n", quotearg_colon (current_file_name), message);
74 if (exit_status == TAREXIT_SUCCESS)
75 exit_status = TAREXIT_DIFFERS;
78 /* Take a buffer returned by read_and_process and do nothing with it. */
80 process_noop (size_t size, char *data)
82 /* Yes, I know. SIZE and DATA are unused in this function. Some
83 compilers may even report it. That's OK, just relax! */
88 process_rawdata (size_t bytes, char *buffer)
90 ssize_t status = safe_read (diff_handle, diff_buffer, bytes);
91 char message[MESSAGE_BUFFER_SIZE];
97 read_error (current_file_name);
98 report_difference (0);
102 sprintf (message, _("Could only read %lu of %lu bytes"),
103 (unsigned long) status, (unsigned long) bytes);
104 report_difference (message);
109 if (memcmp (buffer, diff_buffer, bytes))
111 report_difference (_("Contents differ"));
118 /* Directory contents, only for GNUTYPE_DUMPDIR. */
120 static char *dumpdir_cursor;
123 process_dumpdir (size_t bytes, char *buffer)
125 if (memcmp (buffer, dumpdir_cursor, bytes))
127 report_difference (_("Contents differ"));
131 dumpdir_cursor += bytes;
135 /* Some other routine wants SIZE bytes in the archive. For each chunk
136 of the archive, call PROCESSOR with the size of the chunk, and the
137 address of the chunk it can work with. The PROCESSOR should return
138 nonzero for success. It it return error once, continue skipping
139 without calling PROCESSOR anymore. */
141 read_and_process (off_t size, int (*processor) (size_t, char *))
143 union block *data_block;
146 if (multi_volume_option)
147 save_sizeleft = size;
150 data_block = find_next_block ();
153 ERROR ((0, 0, _("Unexpected EOF in archive")));
157 data_size = available_space_after (data_block);
158 if (data_size > size)
160 if (!(*processor) (data_size, data_block->buffer))
161 processor = process_noop;
162 set_next_block_after ((union block *)
163 (data_block->buffer + data_size - 1));
165 if (multi_volume_option)
166 save_sizeleft -= data_size;
170 /* JK This routine should be used more often than it is ... look into
171 that. Anyhow, what it does is translate the sparse information on the
172 header, and in any subsequent extended headers, into an array of
173 structures with true numbers, as opposed to character strings. It
174 simply makes our life much easier, doing so many comparisons and such.
177 fill_in_sparse_array (void)
181 /* Allocate space for our scratch space; it's initially 10 elements
182 long, but can change in this routine if necessary. */
185 sparsearray = xmalloc (sp_array_size * sizeof (struct sp_array));
187 /* There are at most five of these structures in the header itself;
188 read these in first. */
190 for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
192 /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler. */
193 if (current_header->oldgnu_header.sp[counter].numbytes == 0)
196 sparsearray[counter].offset =
197 OFF_FROM_HEADER (current_header->oldgnu_header.sp[counter].offset);
198 sparsearray[counter].numbytes =
199 SIZE_FROM_HEADER (current_header->oldgnu_header.sp[counter].numbytes);
202 /* If the header's extended, we gotta read in exhdr's till we're done. */
204 if (current_header->oldgnu_header.isextended)
206 /* How far into the sparsearray we are `so far'. */
207 static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
212 exhdr = find_next_block ();
214 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
216 for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
218 if (counter + so_far_ind > sp_array_size - 1)
220 /* We just ran out of room in our scratch area -
225 xrealloc (sparsearray,
226 sp_array_size * sizeof (struct sp_array));
229 /* Convert the character strings into offsets and sizes. */
231 sparsearray[counter + so_far_ind].offset =
232 OFF_FROM_HEADER (exhdr->sparse_header.sp[counter].offset);
233 sparsearray[counter + so_far_ind].numbytes =
234 SIZE_FROM_HEADER (exhdr->sparse_header.sp[counter].numbytes);
237 /* If this is the last extended header for this file, we can
240 if (!exhdr->sparse_header.isextended)
243 so_far_ind += SPARSES_IN_SPARSE_HEADER;
244 set_next_block_after (exhdr);
247 /* Be sure to skip past the last one. */
249 set_next_block_after (exhdr);
253 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
254 bit of a different story than a normal file. First, we must know what
255 areas of the file to skip through, i.e., we need to construct a
256 sparsearray, which will hold all the information we need. We must
257 compare small amounts of data at a time as we find it. */
259 /* FIXME: This does not look very solid to me, at first glance. Zero areas
260 are not checked, spurious sparse entries seemingly goes undetected, and
261 I'm not sure overall identical sparsity is verified. */
264 diff_sparse_files (off_t size_of_file)
266 off_t remaining_size = size_of_file;
267 char *buffer = xmalloc (BLOCKSIZE * sizeof (char));
268 size_t buffer_size = BLOCKSIZE;
269 union block *data_block = 0;
273 fill_in_sparse_array ();
275 while (remaining_size > 0)
282 off_t amount_read = 0;
285 data_block = find_next_block ();
287 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
288 chunk_size = sparsearray[counter].numbytes;
292 offset = sparsearray[counter].offset;
293 if (lseek (diff_handle, offset, SEEK_SET) < 0)
295 seek_error_details (current_file_name, offset);
296 report_difference (0);
299 /* Take care to not run out of room in our buffer. */
301 while (buffer_size < chunk_size)
303 if (buffer_size * 2 < buffer_size)
306 buffer = xrealloc (buffer, buffer_size * sizeof (char));
309 while (chunk_size > BLOCKSIZE)
311 if (status = safe_read (diff_handle, buffer, BLOCKSIZE),
316 read_error (current_file_name);
317 report_difference (0);
321 char message[MESSAGE_BUFFER_SIZE];
323 sprintf (message, _("Could only read %lu of %lu bytes"),
324 (unsigned long) status, (unsigned long) chunk_size);
325 report_difference (message);
330 if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
336 chunk_size -= status;
337 remaining_size -= status;
338 set_next_block_after (data_block);
339 data_block = find_next_block ();
341 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
343 if (status = safe_read (diff_handle, buffer, chunk_size),
344 status != chunk_size)
348 read_error (current_file_name);
349 report_difference (0);
353 char message[MESSAGE_BUFFER_SIZE];
355 sprintf (message, _("Could only read %lu of %lu bytes"),
356 (unsigned long) status, (unsigned long) chunk_size);
357 report_difference (message);
362 if (memcmp (buffer, data_block->buffer, chunk_size))
368 amount_read += chunk_size;
369 if (amount_read >= BLOCKSIZE)
372 set_next_block_after (data_block);
373 data_block = find_next_block ();
375 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
378 set_next_block_after (data_block);
380 remaining_size -= chunk_size;
384 /* If the number of bytes read isn't the number of bytes supposedly in
385 the file, they're different. */
387 if (amount_read != size_of_file)
391 set_next_block_after (data_block);
395 report_difference (_("Contents differ"));
398 /* Call either stat or lstat over STAT_DATA, depending on
399 --dereference (-h), for a file which should exist. Diagnose any
400 problem. Return nonzero for success, zero otherwise. */
402 get_stat_data (char const *file_name, struct stat *stat_data)
404 int status = deref_stat (dereference_option, file_name, stat_data);
410 report_difference (_("does not exist"));
414 stat_error (file_name);
415 report_difference (0);
423 /* Diff a file against the archive. */
427 struct stat stat_data;
430 struct utimbuf restore_times;
432 set_next_block_after (current_header);
433 decode_header (current_header, ¤t_stat, ¤t_format, 1);
435 /* Print the block from current_header and current_stat. */
440 fprintf (stdlis, _("Verify "));
444 switch (current_header->header.typeflag)
447 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
448 quotearg_colon (current_file_name),
449 current_header->header.typeflag));
457 /* Appears to be a file. See if it's really a directory. */
459 name_length = strlen (current_file_name) - 1;
460 if (ISSLASH (current_file_name[name_length]))
463 if (!get_stat_data (current_file_name, &stat_data))
469 if (!S_ISREG (stat_data.st_mode))
471 report_difference (_("File type differs"));
476 if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
477 report_difference (_("Mode differs"));
480 /* stat() in djgpp's C library gives a constant number of 42 as the
481 uid and gid of a file. So, comparing an FTP'ed archive just after
482 unpack would fail on MSDOS. */
483 if (stat_data.st_uid != current_stat.st_uid)
484 report_difference (_("Uid differs"));
485 if (stat_data.st_gid != current_stat.st_gid)
486 report_difference (_("Gid differs"));
489 if (stat_data.st_mtime != current_stat.st_mtime)
490 report_difference (_("Mod time differs"));
491 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
492 stat_data.st_size != current_stat.st_size)
494 report_difference (_("Size differs"));
499 diff_handle = open (current_file_name, O_RDONLY | O_BINARY);
503 open_error (current_file_name);
505 report_difference (0);
509 restore_times.actime = stat_data.st_atime;
510 restore_times.modtime = stat_data.st_mtime;
512 /* Need to treat sparse files completely differently here. */
514 if (current_header->header.typeflag == GNUTYPE_SPARSE)
515 diff_sparse_files (current_stat.st_size);
518 if (multi_volume_option)
520 assign_string (&save_name, current_file_name);
521 save_totsize = current_stat.st_size;
522 /* save_sizeleft is set in read_and_process. */
525 read_and_process (current_stat.st_size, process_rawdata);
527 if (multi_volume_option)
528 assign_string (&save_name, 0);
531 status = close (diff_handle);
533 close_error (current_file_name);
535 if (atime_preserve_option)
536 utime (current_file_name, &restore_times);
544 struct stat link_data;
546 if (!get_stat_data (current_file_name, &stat_data))
548 if (!get_stat_data (current_link_name, &link_data))
551 if (stat_data.st_dev != link_data.st_dev
552 || stat_data.st_ino != link_data.st_ino)
555 xmalloc (MESSAGE_BUFFER_SIZE + 4 * strlen (current_link_name));
557 sprintf (message, _("Not linked to %s"),
558 quote (current_link_name));
559 report_difference (message);
566 #endif /* not MSDOS */
571 size_t len = strlen (current_link_name);
572 char *linkbuf = alloca (len + 1);
574 status = readlink (current_file_name, linkbuf, len);
579 readlink_warn (current_file_name);
581 readlink_error (current_file_name);
582 report_difference (0);
584 else if (status != len
585 || strncmp (current_link_name, linkbuf, len) != 0)
586 report_difference (_("Symlink differs"));
596 /* FIXME: deal with umask. */
598 if (!get_stat_data (current_file_name, &stat_data))
601 if (current_header->header.typeflag == CHRTYPE
602 ? !S_ISCHR (stat_data.st_mode)
603 : current_header->header.typeflag == BLKTYPE
604 ? !S_ISBLK (stat_data.st_mode)
605 : /* current_header->header.typeflag == FIFOTYPE */
606 !S_ISFIFO (stat_data.st_mode))
608 report_difference (_("File type differs"));
612 if ((current_header->header.typeflag == CHRTYPE
613 || current_header->header.typeflag == BLKTYPE)
614 && current_stat.st_rdev != stat_data.st_rdev)
616 report_difference (_("Device number differs"));
620 if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
622 report_difference (_("Mode differs"));
628 case GNUTYPE_DUMPDIR:
630 char *dumpdir_buffer = get_directory_contents (current_file_name, 0);
632 if (multi_volume_option)
634 assign_string (&save_name, current_file_name);
635 save_totsize = current_stat.st_size;
636 /* save_sizeleft is set in read_and_process. */
641 dumpdir_cursor = dumpdir_buffer;
642 read_and_process (current_stat.st_size, process_dumpdir);
643 free (dumpdir_buffer);
646 read_and_process (current_stat.st_size, process_noop);
648 if (multi_volume_option)
649 assign_string (&save_name, 0);
654 /* Check for trailing /. */
656 name_length = strlen (current_file_name) - 1;
659 while (name_length && ISSLASH (current_file_name[name_length]))
660 current_file_name[name_length--] = '\0'; /* zap / */
662 if (!get_stat_data (current_file_name, &stat_data))
665 if (!S_ISDIR (stat_data.st_mode))
667 report_difference (_("File type differs"));
671 if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
673 report_difference (_("Mode differs"));
682 case GNUTYPE_MULTIVOL:
686 name_length = strlen (current_file_name) - 1;
687 if (ISSLASH (current_file_name[name_length]))
690 if (!get_stat_data (current_file_name, &stat_data))
693 if (!S_ISREG (stat_data.st_mode))
695 report_difference (_("File type differs"));
700 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
701 if (stat_data.st_size != current_stat.st_size + offset)
703 report_difference (_("Size differs"));
708 diff_handle = open (current_file_name, O_RDONLY | O_BINARY);
712 open_error (current_file_name);
713 report_difference (0);
718 if (lseek (diff_handle, offset, SEEK_SET) < 0)
720 seek_error_details (current_file_name, offset);
721 report_difference (0);
725 if (multi_volume_option)
727 assign_string (&save_name, current_file_name);
728 save_totsize = stat_data.st_size;
729 /* save_sizeleft is set in read_and_process. */
732 read_and_process (current_stat.st_size, process_rawdata);
734 if (multi_volume_option)
735 assign_string (&save_name, 0);
737 status = close (diff_handle);
739 close_error (current_file_name);
752 /* Verifying an archive is meant to check if the physical media got it
753 correctly, so try to defeat clever in-memory buffering pertaining to
754 this particular media. On Linux, for example, the floppy drive would
755 not even be accessed for the whole verification.
757 The code was using fsync only when the ioctl is unavailable, but
758 Marty Leisner says that the ioctl does not work when not preceded by
759 fsync. So, until we know better, or maybe to please Marty, let's do it
760 the unbelievable way :-). */
766 ioctl (archive, FDFLUSH);
771 struct mtop operation;
774 operation.mt_op = MTBSF;
775 operation.mt_count = 1;
776 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
779 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
783 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
785 /* Lseek failed. Try a different method. */
786 seek_warn (archive_name_array[0]);
795 access_mode = ACCESS_READ;
801 enum read_header status = read_header (0);
803 if (status == HEADER_FAILURE)
807 while (status == HEADER_FAILURE);
810 status = read_header (0);
813 _("VERIFY FAILURE: %d invalid header(s) detected"), counter));
815 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
821 access_mode = ACCESS_WRITE;