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 $ */
23 /* $DragonFly: src/contrib/tar/src/Attic/compare.c,v 1.2 2003/06/17 04:24:06 dillon Exp $ */
38 # include <linux/fd.h>
46 /* Spare space for messages, hopefully safe even after gettext. */
47 #define MESSAGE_BUFFER_SIZE 100
49 /* Nonzero if we are verifying at the moment. */
52 /* File descriptor for the file we are diffing. */
53 static int diff_handle;
55 /* Area for reading file contents into. */
56 static char *diff_buffer;
58 /* Initialize for a diff operation. */
62 diff_buffer = valloc (record_size);
67 /* Sigh about something that differs by writing a MESSAGE to stdlis,
68 given MESSAGE is nonzero. Also set the exit status if not already. */
70 report_difference (const char *message)
73 fprintf (stdlis, "%s: %s\n", quotearg_colon (current_file_name), message);
75 if (exit_status == TAREXIT_SUCCESS)
76 exit_status = TAREXIT_DIFFERS;
79 /* Take a buffer returned by read_and_process and do nothing with it. */
81 process_noop (size_t size, char *data)
83 /* Yes, I know. SIZE and DATA are unused in this function. Some
84 compilers may even report it. That's OK, just relax! */
89 process_rawdata (size_t bytes, char *buffer)
91 ssize_t status = safe_read (diff_handle, diff_buffer, bytes);
92 char message[MESSAGE_BUFFER_SIZE];
98 read_error (current_file_name);
99 report_difference (0);
103 sprintf (message, _("Could only read %lu of %lu bytes"),
104 (unsigned long) status, (unsigned long) bytes);
105 report_difference (message);
110 if (memcmp (buffer, diff_buffer, bytes))
112 report_difference (_("Contents differ"));
119 /* Directory contents, only for GNUTYPE_DUMPDIR. */
121 static char *dumpdir_cursor;
124 process_dumpdir (size_t bytes, char *buffer)
126 if (memcmp (buffer, dumpdir_cursor, bytes))
128 report_difference (_("Contents differ"));
132 dumpdir_cursor += bytes;
136 /* Some other routine wants SIZE bytes in the archive. For each chunk
137 of the archive, call PROCESSOR with the size of the chunk, and the
138 address of the chunk it can work with. The PROCESSOR should return
139 nonzero for success. It it return error once, continue skipping
140 without calling PROCESSOR anymore. */
142 read_and_process (off_t size, int (*processor) (size_t, char *))
144 union block *data_block;
147 if (multi_volume_option)
148 save_sizeleft = size;
151 data_block = find_next_block ();
154 ERROR ((0, 0, _("Unexpected EOF in archive")));
158 data_size = available_space_after (data_block);
159 if (data_size > size)
161 if (!(*processor) (data_size, data_block->buffer))
162 processor = process_noop;
163 set_next_block_after ((union block *)
164 (data_block->buffer + data_size - 1));
166 if (multi_volume_option)
167 save_sizeleft -= data_size;
171 /* JK This routine should be used more often than it is ... look into
172 that. Anyhow, what it does is translate the sparse information on the
173 header, and in any subsequent extended headers, into an array of
174 structures with true numbers, as opposed to character strings. It
175 simply makes our life much easier, doing so many comparisons and such.
178 fill_in_sparse_array (void)
182 /* Allocate space for our scratch space; it's initially 10 elements
183 long, but can change in this routine if necessary. */
186 sparsearray = xmalloc (sp_array_size * sizeof (struct sp_array));
188 /* There are at most five of these structures in the header itself;
189 read these in first. */
191 for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
193 /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler. */
194 if (current_header->oldgnu_header.sp[counter].numbytes == 0)
197 sparsearray[counter].offset =
198 OFF_FROM_HEADER (current_header->oldgnu_header.sp[counter].offset);
199 sparsearray[counter].numbytes =
200 SIZE_FROM_HEADER (current_header->oldgnu_header.sp[counter].numbytes);
203 /* If the header's extended, we gotta read in exhdr's till we're done. */
205 if (current_header->oldgnu_header.isextended)
207 /* How far into the sparsearray we are `so far'. */
208 static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
213 exhdr = find_next_block ();
215 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
217 for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
219 if (counter + so_far_ind > sp_array_size - 1)
221 /* We just ran out of room in our scratch area -
226 xrealloc (sparsearray,
227 sp_array_size * sizeof (struct sp_array));
230 /* Convert the character strings into offsets and sizes. */
232 sparsearray[counter + so_far_ind].offset =
233 OFF_FROM_HEADER (exhdr->sparse_header.sp[counter].offset);
234 sparsearray[counter + so_far_ind].numbytes =
235 SIZE_FROM_HEADER (exhdr->sparse_header.sp[counter].numbytes);
238 /* If this is the last extended header for this file, we can
241 if (!exhdr->sparse_header.isextended)
244 so_far_ind += SPARSES_IN_SPARSE_HEADER;
245 set_next_block_after (exhdr);
248 /* Be sure to skip past the last one. */
250 set_next_block_after (exhdr);
254 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
255 bit of a different story than a normal file. First, we must know what
256 areas of the file to skip through, i.e., we need to construct a
257 sparsearray, which will hold all the information we need. We must
258 compare small amounts of data at a time as we find it. */
260 /* FIXME: This does not look very solid to me, at first glance. Zero areas
261 are not checked, spurious sparse entries seemingly goes undetected, and
262 I'm not sure overall identical sparsity is verified. */
265 diff_sparse_files (off_t size_of_file)
267 off_t remaining_size = size_of_file;
268 char *buffer = xmalloc (BLOCKSIZE * sizeof (char));
269 size_t buffer_size = BLOCKSIZE;
270 union block *data_block = 0;
274 fill_in_sparse_array ();
276 while (remaining_size > 0)
283 off_t amount_read = 0;
286 data_block = find_next_block ();
288 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
289 chunk_size = sparsearray[counter].numbytes;
293 offset = sparsearray[counter].offset;
294 if (lseek (diff_handle, offset, SEEK_SET) < 0)
296 seek_error_details (current_file_name, offset);
297 report_difference (0);
300 /* Take care to not run out of room in our buffer. */
302 while (buffer_size < chunk_size)
304 if (buffer_size * 2 < buffer_size)
307 buffer = xrealloc (buffer, buffer_size * sizeof (char));
310 while (chunk_size > BLOCKSIZE)
312 if (status = safe_read (diff_handle, buffer, BLOCKSIZE),
317 read_error (current_file_name);
318 report_difference (0);
322 char message[MESSAGE_BUFFER_SIZE];
324 sprintf (message, _("Could only read %lu of %lu bytes"),
325 (unsigned long) status, (unsigned long) chunk_size);
326 report_difference (message);
331 if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
337 chunk_size -= status;
338 remaining_size -= status;
339 set_next_block_after (data_block);
340 data_block = find_next_block ();
342 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
344 if (status = safe_read (diff_handle, buffer, chunk_size),
345 status != chunk_size)
349 read_error (current_file_name);
350 report_difference (0);
354 char message[MESSAGE_BUFFER_SIZE];
356 sprintf (message, _("Could only read %lu of %lu bytes"),
357 (unsigned long) status, (unsigned long) chunk_size);
358 report_difference (message);
363 if (memcmp (buffer, data_block->buffer, chunk_size))
369 amount_read += chunk_size;
370 if (amount_read >= BLOCKSIZE)
373 set_next_block_after (data_block);
374 data_block = find_next_block ();
376 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
379 set_next_block_after (data_block);
381 remaining_size -= chunk_size;
385 /* If the number of bytes read isn't the number of bytes supposedly in
386 the file, they're different. */
388 if (amount_read != size_of_file)
392 set_next_block_after (data_block);
396 report_difference (_("Contents differ"));
399 /* Call either stat or lstat over STAT_DATA, depending on
400 --dereference (-h), for a file which should exist. Diagnose any
401 problem. Return nonzero for success, zero otherwise. */
403 get_stat_data (char const *file_name, struct stat *stat_data)
405 int status = deref_stat (dereference_option, file_name, stat_data);
411 report_difference (_("does not exist"));
415 stat_error (file_name);
416 report_difference (0);
424 /* Diff a file against the archive. */
428 struct stat stat_data;
431 struct utimbuf restore_times;
433 set_next_block_after (current_header);
434 decode_header (current_header, ¤t_stat, ¤t_format, 1);
436 /* Print the block from current_header and current_stat. */
441 fprintf (stdlis, _("Verify "));
445 switch (current_header->header.typeflag)
448 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
449 quotearg_colon (current_file_name),
450 current_header->header.typeflag));
458 /* Appears to be a file. See if it's really a directory. */
460 name_length = strlen (current_file_name) - 1;
461 if (ISSLASH (current_file_name[name_length]))
464 if (!get_stat_data (current_file_name, &stat_data))
470 if (!S_ISREG (stat_data.st_mode))
472 report_difference (_("File type differs"));
477 if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
478 report_difference (_("Mode differs"));
481 /* stat() in djgpp's C library gives a constant number of 42 as the
482 uid and gid of a file. So, comparing an FTP'ed archive just after
483 unpack would fail on MSDOS. */
484 if (stat_data.st_uid != current_stat.st_uid)
485 report_difference (_("Uid differs"));
486 if (stat_data.st_gid != current_stat.st_gid)
487 report_difference (_("Gid differs"));
490 if (stat_data.st_mtime != current_stat.st_mtime)
491 report_difference (_("Mod time differs"));
492 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
493 stat_data.st_size != current_stat.st_size)
495 report_difference (_("Size differs"));
500 diff_handle = open (current_file_name, O_RDONLY | O_BINARY);
504 open_error (current_file_name);
506 report_difference (0);
510 restore_times.actime = stat_data.st_atime;
511 restore_times.modtime = stat_data.st_mtime;
513 /* Need to treat sparse files completely differently here. */
515 if (current_header->header.typeflag == GNUTYPE_SPARSE)
516 diff_sparse_files (current_stat.st_size);
519 if (multi_volume_option)
521 assign_string (&save_name, current_file_name);
522 save_totsize = current_stat.st_size;
523 /* save_sizeleft is set in read_and_process. */
526 read_and_process (current_stat.st_size, process_rawdata);
528 if (multi_volume_option)
529 assign_string (&save_name, 0);
532 status = close (diff_handle);
534 close_error (current_file_name);
536 if (atime_preserve_option)
537 utime (current_file_name, &restore_times);
545 struct stat link_data;
547 if (!get_stat_data (current_file_name, &stat_data))
549 if (!get_stat_data (current_link_name, &link_data))
552 if (stat_data.st_dev != link_data.st_dev
553 || stat_data.st_ino != link_data.st_ino)
556 xmalloc (MESSAGE_BUFFER_SIZE + 4 * strlen (current_link_name));
558 sprintf (message, _("Not linked to %s"),
559 quote (current_link_name));
560 report_difference (message);
567 #endif /* not MSDOS */
572 size_t len = strlen (current_link_name);
573 char *linkbuf = alloca (len + 1);
575 status = readlink (current_file_name, linkbuf, len);
580 readlink_warn (current_file_name);
582 readlink_error (current_file_name);
583 report_difference (0);
585 else if (status != len
586 || strncmp (current_link_name, linkbuf, len) != 0)
587 report_difference (_("Symlink differs"));
597 /* FIXME: deal with umask. */
599 if (!get_stat_data (current_file_name, &stat_data))
602 if (current_header->header.typeflag == CHRTYPE
603 ? !S_ISCHR (stat_data.st_mode)
604 : current_header->header.typeflag == BLKTYPE
605 ? !S_ISBLK (stat_data.st_mode)
606 : /* current_header->header.typeflag == FIFOTYPE */
607 !S_ISFIFO (stat_data.st_mode))
609 report_difference (_("File type differs"));
613 if ((current_header->header.typeflag == CHRTYPE
614 || current_header->header.typeflag == BLKTYPE)
615 && current_stat.st_rdev != stat_data.st_rdev)
617 report_difference (_("Device number differs"));
621 if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
623 report_difference (_("Mode differs"));
629 case GNUTYPE_DUMPDIR:
631 char *dumpdir_buffer = get_directory_contents (current_file_name, 0);
633 if (multi_volume_option)
635 assign_string (&save_name, current_file_name);
636 save_totsize = current_stat.st_size;
637 /* save_sizeleft is set in read_and_process. */
642 dumpdir_cursor = dumpdir_buffer;
643 read_and_process (current_stat.st_size, process_dumpdir);
644 free (dumpdir_buffer);
647 read_and_process (current_stat.st_size, process_noop);
649 if (multi_volume_option)
650 assign_string (&save_name, 0);
655 /* Check for trailing /. */
657 name_length = strlen (current_file_name) - 1;
660 while (name_length && ISSLASH (current_file_name[name_length]))
661 current_file_name[name_length--] = '\0'; /* zap / */
663 if (!get_stat_data (current_file_name, &stat_data))
666 if (!S_ISDIR (stat_data.st_mode))
668 report_difference (_("File type differs"));
672 if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
674 report_difference (_("Mode differs"));
683 case GNUTYPE_MULTIVOL:
687 name_length = strlen (current_file_name) - 1;
688 if (ISSLASH (current_file_name[name_length]))
691 if (!get_stat_data (current_file_name, &stat_data))
694 if (!S_ISREG (stat_data.st_mode))
696 report_difference (_("File type differs"));
701 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
702 if (stat_data.st_size != current_stat.st_size + offset)
704 report_difference (_("Size differs"));
709 diff_handle = open (current_file_name, O_RDONLY | O_BINARY);
713 open_error (current_file_name);
714 report_difference (0);
719 if (lseek (diff_handle, offset, SEEK_SET) < 0)
721 seek_error_details (current_file_name, offset);
722 report_difference (0);
726 if (multi_volume_option)
728 assign_string (&save_name, current_file_name);
729 save_totsize = stat_data.st_size;
730 /* save_sizeleft is set in read_and_process. */
733 read_and_process (current_stat.st_size, process_rawdata);
735 if (multi_volume_option)
736 assign_string (&save_name, 0);
738 status = close (diff_handle);
740 close_error (current_file_name);
753 /* Verifying an archive is meant to check if the physical media got it
754 correctly, so try to defeat clever in-memory buffering pertaining to
755 this particular media. On Linux, for example, the floppy drive would
756 not even be accessed for the whole verification.
758 The code was using fsync only when the ioctl is unavailable, but
759 Marty Leisner says that the ioctl does not work when not preceded by
760 fsync. So, until we know better, or maybe to please Marty, let's do it
761 the unbelievable way :-). */
767 ioctl (archive, FDFLUSH);
772 struct mtop operation;
775 operation.mt_op = MTBSF;
776 operation.mt_count = 1;
777 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
780 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
784 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
786 /* Lseek failed. Try a different method. */
787 seek_warn (archive_name_array[0]);
796 access_mode = ACCESS_READ;
802 enum read_header status = read_header (0);
804 if (status == HEADER_FAILURE)
808 while (status == HEADER_FAILURE);
811 status = read_header (0);
814 _("VERIFY FAILURE: %d invalid header(s) detected"), counter));
816 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
822 access_mode = ACCESS_WRITE;