]>
Dogcows Code - chaz/tar/blob - src/compare.c
1 /* Diff files from a tar archive.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003 Free 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. */
35 # include <linux/fd.h>
43 /* Spare space for messages, hopefully safe even after gettext. */
44 #define MESSAGE_BUFFER_SIZE 100
46 /* Nonzero if we are verifying at the moment. */
49 /* File descriptor for the file we are diffing. */
50 static int diff_handle
;
52 /* Area for reading file contents into. */
53 static char *diff_buffer
;
55 /* Initialize for a diff operation. */
59 diff_buffer
= valloc (record_size
);
64 /* Sigh about something that differs by writing a MESSAGE to stdlis,
65 given MESSAGE is nonzero. Also set the exit status if not already. */
67 report_difference (const char *message
)
70 fprintf (stdlis
, "%s: %s\n", quotearg_colon (current_stat_info
.file_name
), message
);
72 if (exit_status
== TAREXIT_SUCCESS
)
73 exit_status
= TAREXIT_DIFFERS
;
76 /* Take a buffer returned by read_and_process and do nothing with it. */
78 process_noop (size_t size
, char *data
)
80 /* Yes, I know. SIZE and DATA are unused in this function. Some
81 compilers may even report it. That's OK, just relax! */
86 process_rawdata (size_t bytes
, char *buffer
)
88 ssize_t status
= safe_read (diff_handle
, diff_buffer
, bytes
);
89 char message
[MESSAGE_BUFFER_SIZE
];
95 read_error (current_stat_info
.file_name
);
96 report_difference (0);
100 sprintf (message
, _("Could only read %lu of %lu bytes"),
101 (unsigned long) status
, (unsigned long) bytes
);
102 report_difference (message
);
107 if (memcmp (buffer
, diff_buffer
, bytes
))
109 report_difference (_("Contents differ"));
116 /* Directory contents, only for GNUTYPE_DUMPDIR. */
118 static char *dumpdir_cursor
;
121 process_dumpdir (size_t bytes
, char *buffer
)
123 if (memcmp (buffer
, dumpdir_cursor
, bytes
))
125 report_difference (_("Contents differ"));
129 dumpdir_cursor
+= bytes
;
133 /* Some other routine wants SIZE bytes in the archive. For each chunk
134 of the archive, call PROCESSOR with the size of the chunk, and the
135 address of the chunk it can work with. The PROCESSOR should return
136 nonzero for success. It it return error once, continue skipping
137 without calling PROCESSOR anymore. */
139 read_and_process (off_t size
, int (*processor
) (size_t, char *))
141 union block
*data_block
;
144 if (multi_volume_option
)
145 save_sizeleft
= size
;
148 data_block
= find_next_block ();
151 ERROR ((0, 0, _("Unexpected EOF in archive")));
155 data_size
= available_space_after (data_block
);
156 if (data_size
> size
)
158 if (!(*processor
) (data_size
, data_block
->buffer
))
159 processor
= process_noop
;
160 set_next_block_after ((union block
*)
161 (data_block
->buffer
+ data_size
- 1));
163 if (multi_volume_option
)
164 save_sizeleft
-= data_size
;
168 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
169 bit of a different story than a normal file. First, we must know what
170 areas of the file to skip through, i.e., we need to construct a
171 sparsearray, which will hold all the information we need. We must
172 compare small amounts of data at a time as we find it. */
174 /* FIXME: This does not look very solid to me, at first glance. Zero areas
175 are not checked, spurious sparse entries seemingly goes undetected, and
176 I'm not sure overall identical sparsity is verified. */
179 diff_sparse_files (void)
181 off_t remaining_size
= current_stat_info
.stat
.st_size
;
182 char *buffer
= xmalloc (BLOCKSIZE
* sizeof (char));
183 size_t buffer_size
= BLOCKSIZE
;
184 union block
*data_block
= 0;
188 if (! fill_in_sparse_array ())
191 while (remaining_size
> 0)
198 off_t amount_read
= 0;
201 data_block
= find_next_block ();
203 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
204 chunk_size
= sparsearray
[counter
].numbytes
;
208 offset
= sparsearray
[counter
].offset
;
209 if (lseek (diff_handle
, offset
, SEEK_SET
) < 0)
211 seek_error_details (current_stat_info
.file_name
, offset
);
212 report_difference (0);
215 /* Take care to not run out of room in our buffer. */
217 while (buffer_size
< chunk_size
)
219 if (buffer_size
* 2 < buffer_size
)
222 buffer
= xrealloc (buffer
, buffer_size
* sizeof (char));
225 while (chunk_size
> BLOCKSIZE
)
227 if (status
= safe_read (diff_handle
, buffer
, BLOCKSIZE
),
232 read_error (current_stat_info
.file_name
);
233 report_difference (0);
237 char message
[MESSAGE_BUFFER_SIZE
];
239 sprintf (message
, _("Could only read %lu of %lu bytes"),
240 (unsigned long) status
, (unsigned long) chunk_size
);
241 report_difference (message
);
246 if (memcmp (buffer
, data_block
->buffer
, BLOCKSIZE
))
252 chunk_size
-= status
;
253 remaining_size
-= status
;
254 set_next_block_after (data_block
);
255 data_block
= find_next_block ();
257 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
259 if (status
= safe_read (diff_handle
, buffer
, chunk_size
),
260 status
!= chunk_size
)
264 read_error (current_stat_info
.file_name
);
265 report_difference (0);
269 char message
[MESSAGE_BUFFER_SIZE
];
271 sprintf (message
, _("Could only read %lu of %lu bytes"),
272 (unsigned long) status
, (unsigned long) chunk_size
);
273 report_difference (message
);
278 if (memcmp (buffer
, data_block
->buffer
, chunk_size
))
284 amount_read
+= chunk_size
;
285 if (amount_read
>= BLOCKSIZE
)
288 set_next_block_after (data_block
);
289 data_block
= find_next_block ();
291 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
294 set_next_block_after (data_block
);
296 remaining_size
-= chunk_size
;
300 /* If the number of bytes read isn't the number of bytes supposedly in
301 the file, they're different. */
303 if (amount_read
!= current_stat_info
.stat
.st_size
)
307 set_next_block_after (data_block
);
311 report_difference (_("Contents differ"));
314 /* Call either stat or lstat over STAT_DATA, depending on
315 --dereference (-h), for a file which should exist. Diagnose any
316 problem. Return nonzero for success, zero otherwise. */
318 get_stat_data (char const *file_name
, struct stat
*stat_data
)
320 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
325 stat_warn (file_name
);
327 stat_error (file_name
);
328 report_difference (0);
335 /* Diff a file against the archive. */
339 struct stat stat_data
;
341 struct utimbuf restore_times
;
343 set_next_block_after (current_header
);
344 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
346 /* Print the block from current_header and current_stat_info. */
351 fprintf (stdlis
, _("Verify "));
355 switch (current_header
->header
.typeflag
)
358 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
359 quotearg_colon (current_stat_info
.file_name
),
360 current_header
->header
.typeflag
));
368 /* Appears to be a file. See if it's really a directory. */
370 if (current_stat_info
.had_trailing_slash
)
373 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
379 if (!S_ISREG (stat_data
.st_mode
))
381 report_difference (_("File type differs"));
386 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
387 report_difference (_("Mode differs"));
390 /* stat() in djgpp's C library gives a constant number of 42 as the
391 uid and gid of a file. So, comparing an FTP'ed archive just after
392 unpack would fail on MSDOS. */
393 if (stat_data
.st_uid
!= current_stat_info
.stat
.st_uid
)
394 report_difference (_("Uid differs"));
395 if (stat_data
.st_gid
!= current_stat_info
.stat
.st_gid
)
396 report_difference (_("Gid differs"));
399 if (stat_data
.st_mtime
!= current_stat_info
.stat
.st_mtime
)
400 report_difference (_("Mod time differs"));
401 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
402 stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
404 report_difference (_("Size differs"));
409 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
413 open_error (current_stat_info
.file_name
);
415 report_difference (0);
419 restore_times
.actime
= stat_data
.st_atime
;
420 restore_times
.modtime
= stat_data
.st_mtime
;
422 /* Need to treat sparse files completely differently here. */
424 if (current_header
->header
.typeflag
== GNUTYPE_SPARSE
)
425 diff_sparse_files ();
428 if (multi_volume_option
)
430 assign_string (&save_name
, current_stat_info
.file_name
);
431 save_totsize
= current_stat_info
.stat
.st_size
;
432 /* save_sizeleft is set in read_and_process. */
435 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
437 if (multi_volume_option
)
438 assign_string (&save_name
, 0);
441 status
= close (diff_handle
);
443 close_error (current_stat_info
.file_name
);
445 if (atime_preserve_option
)
446 utime (current_stat_info
.file_name
, &restore_times
);
454 struct stat link_data
;
456 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
458 if (!get_stat_data (current_stat_info
.link_name
, &link_data
))
461 if (stat_data
.st_dev
!= link_data
.st_dev
462 || stat_data
.st_ino
!= link_data
.st_ino
)
465 xmalloc (MESSAGE_BUFFER_SIZE
+ 4 * strlen (current_stat_info
.link_name
));
467 sprintf (message
, _("Not linked to %s"),
468 quote (current_stat_info
.link_name
));
469 report_difference (message
);
476 #endif /* not MSDOS */
481 size_t len
= strlen (current_stat_info
.link_name
);
482 char *linkbuf
= alloca (len
+ 1);
484 status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
489 readlink_warn (current_stat_info
.file_name
);
491 readlink_error (current_stat_info
.file_name
);
492 report_difference (0);
494 else if (status
!= len
495 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
496 report_difference (_("Symlink differs"));
506 /* FIXME: deal with umask. */
508 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
511 if (current_header
->header
.typeflag
== CHRTYPE
512 ? !S_ISCHR (stat_data
.st_mode
)
513 : current_header
->header
.typeflag
== BLKTYPE
514 ? !S_ISBLK (stat_data
.st_mode
)
515 : /* current_header->header.typeflag == FIFOTYPE */
516 !S_ISFIFO (stat_data
.st_mode
))
518 report_difference (_("File type differs"));
522 if ((current_header
->header
.typeflag
== CHRTYPE
523 || current_header
->header
.typeflag
== BLKTYPE
)
524 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
526 report_difference (_("Device number differs"));
530 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
532 report_difference (_("Mode differs"));
538 case GNUTYPE_DUMPDIR
:
540 char *dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
, 0);
542 if (multi_volume_option
)
544 assign_string (&save_name
, current_stat_info
.file_name
);
545 save_totsize
= current_stat_info
.stat
.st_size
;
546 /* save_sizeleft is set in read_and_process. */
551 dumpdir_cursor
= dumpdir_buffer
;
552 read_and_process (current_stat_info
.stat
.st_size
, process_dumpdir
);
553 free (dumpdir_buffer
);
556 read_and_process (current_stat_info
.stat
.st_size
, process_noop
);
558 if (multi_volume_option
)
559 assign_string (&save_name
, 0);
565 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
568 if (!S_ISDIR (stat_data
.st_mode
))
570 report_difference (_("File type differs"));
574 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
576 report_difference (_("Mode differs"));
585 case GNUTYPE_MULTIVOL
:
589 if (current_stat_info
.had_trailing_slash
)
592 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
595 if (!S_ISREG (stat_data
.st_mode
))
597 report_difference (_("File type differs"));
602 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
603 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
605 report_difference (_("Size differs"));
610 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
614 open_error (current_stat_info
.file_name
);
615 report_difference (0);
620 if (lseek (diff_handle
, offset
, SEEK_SET
) < 0)
622 seek_error_details (current_stat_info
.file_name
, offset
);
623 report_difference (0);
627 if (multi_volume_option
)
629 assign_string (&save_name
, current_stat_info
.file_name
);
630 save_totsize
= stat_data
.st_size
;
631 /* save_sizeleft is set in read_and_process. */
634 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
636 if (multi_volume_option
)
637 assign_string (&save_name
, 0);
639 status
= close (diff_handle
);
641 close_error (current_stat_info
.file_name
);
654 /* Verifying an archive is meant to check if the physical media got it
655 correctly, so try to defeat clever in-memory buffering pertaining to
656 this particular media. On Linux, for example, the floppy drive would
657 not even be accessed for the whole verification.
659 The code was using fsync only when the ioctl is unavailable, but
660 Marty Leisner says that the ioctl does not work when not preceded by
661 fsync. So, until we know better, or maybe to please Marty, let's do it
662 the unbelievable way :-). */
668 ioctl (archive
, FDFLUSH
);
673 struct mtop operation
;
676 operation
.mt_op
= MTBSF
;
677 operation
.mt_count
= 1;
678 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
681 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
685 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
687 /* Lseek failed. Try a different method. */
688 seek_warn (archive_name_array
[0]);
697 access_mode
= ACCESS_READ
;
703 enum read_header status
= read_header (false);
705 if (status
== HEADER_FAILURE
)
712 status
= read_header (false);
714 while (status
== HEADER_FAILURE
);
717 _("VERIFY FAILURE: %d invalid header(s) detected"), counter
));
719 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
725 access_mode
= ACCESS_WRITE
;
This page took 0.064009 seconds and 4 git commands to generate.