]> Dogcows Code - chaz/tar/blob - src/compare.c
Moved system dependencies to system.c
[chaz/tar] / src / compare.c
1 /* Diff files from a tar archive.
2
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003 Free Software Foundation, Inc.
5
6 Written by John Gilmore, on 1987-04-30.
7
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
11 version.
12
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.
17
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. */
21
22 #include "system.h"
23
24 #if HAVE_UTIME_H
25 # include <utime.h>
26 #else
27 struct utimbuf
28 {
29 long actime;
30 long modtime;
31 };
32 #endif
33
34 #if HAVE_LINUX_FD_H
35 # include <linux/fd.h>
36 #endif
37
38 #include <quotearg.h>
39
40 #include "common.h"
41 #include "rmt.h"
42 #include <stdarg.h>
43
44 /* Nonzero if we are verifying at the moment. */
45 bool now_verifying;
46
47 /* File descriptor for the file we are diffing. */
48 static int diff_handle;
49
50 /* Area for reading file contents into. */
51 static char *diff_buffer;
52
53 /* Initialize for a diff operation. */
54 void
55 diff_init (void)
56 {
57 diff_buffer = valloc (record_size);
58 if (!diff_buffer)
59 xalloc_die ();
60 }
61
62 /* Sigh about something that differs by writing a MESSAGE to stdlis,
63 given MESSAGE is nonzero. Also set the exit status if not already. */
64 void
65 report_difference (const char *fmt, ...)
66 {
67 if (fmt)
68 {
69 va_list ap;
70
71 fprintf (stdlis, "%s: ", quotearg_colon (current_stat_info.file_name));
72 va_start (ap, fmt);
73 vfprintf (stdlis, fmt, ap);
74 va_end (ap);
75 fprintf (stdlis, "\n");
76 }
77
78 if (exit_status == TAREXIT_SUCCESS)
79 exit_status = TAREXIT_DIFFERS;
80 }
81
82 /* Take a buffer returned by read_and_process and do nothing with it. */
83 static int
84 process_noop (size_t size, char *data)
85 {
86 /* Yes, I know. SIZE and DATA are unused in this function. Some
87 compilers may even report it. That's OK, just relax! */
88 return 1;
89 }
90
91 static int
92 process_rawdata (size_t bytes, char *buffer)
93 {
94 ssize_t status = safe_read (diff_handle, diff_buffer, bytes);
95
96 if (status != bytes)
97 {
98 if (status < 0)
99 {
100 read_error (current_stat_info.file_name);
101 report_difference (NULL);
102 }
103 else
104 {
105 report_difference (ngettext ("Could only read %lu of %lu byte",
106 "Could only read %lu of %lu bytes",
107 bytes),
108 (unsigned long) status, (unsigned long) bytes);
109 }
110 return 0;
111 }
112
113 if (memcmp (buffer, diff_buffer, bytes))
114 {
115 report_difference (_("Contents differ"));
116 return 0;
117 }
118
119 return 1;
120 }
121
122 /* Directory contents, only for GNUTYPE_DUMPDIR. */
123
124 static char *dumpdir_cursor;
125
126 static int
127 process_dumpdir (size_t bytes, char *buffer)
128 {
129 if (memcmp (buffer, dumpdir_cursor, bytes))
130 {
131 report_difference (_("Contents differ"));
132 return 0;
133 }
134
135 dumpdir_cursor += bytes;
136 return 1;
137 }
138
139 /* Some other routine wants SIZE bytes in the archive. For each chunk
140 of the archive, call PROCESSOR with the size of the chunk, and the
141 address of the chunk it can work with. The PROCESSOR should return
142 nonzero for success. It it return error once, continue skipping
143 without calling PROCESSOR anymore. */
144 static void
145 read_and_process (off_t size, int (*processor) (size_t, char *))
146 {
147 union block *data_block;
148 size_t data_size;
149
150 if (multi_volume_option)
151 save_sizeleft = size;
152 while (size)
153 {
154 data_block = find_next_block ();
155 if (! data_block)
156 {
157 ERROR ((0, 0, _("Unexpected EOF in archive")));
158 return;
159 }
160
161 data_size = available_space_after (data_block);
162 if (data_size > size)
163 data_size = size;
164 if (!(*processor) (data_size, data_block->buffer))
165 processor = process_noop;
166 set_next_block_after ((union block *)
167 (data_block->buffer + data_size - 1));
168 size -= data_size;
169 if (multi_volume_option)
170 save_sizeleft -= data_size;
171 }
172 }
173
174 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
175 bit of a different story than a normal file. First, we must know what
176 areas of the file to skip through, i.e., we need to construct a
177 sparsearray, which will hold all the information we need. We must
178 compare small amounts of data at a time as we find it. */
179
180 /* FIXME: This does not look very solid to me, at first glance. Zero areas
181 are not checked, spurious sparse entries seemingly goes undetected, and
182 I'm not sure overall identical sparsity is verified. */
183
184 static void
185 diff_sparse_files (void)
186 {
187 off_t remaining_size = current_stat_info.stat.st_size;
188 char *buffer = xmalloc (BLOCKSIZE * sizeof (char));
189 size_t buffer_size = BLOCKSIZE;
190 union block *data_block = 0;
191 int counter = 0;
192 int different = 0;
193
194 if (! fill_in_sparse_array ())
195 fatal_exit ();
196
197 while (remaining_size > 0)
198 {
199 ssize_t status;
200 size_t chunk_size;
201 off_t offset;
202
203 #if 0
204 off_t amount_read = 0;
205 #endif
206
207 data_block = find_next_block ();
208 if (!data_block)
209 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
210 chunk_size = sparsearray[counter].numbytes;
211 if (!chunk_size)
212 break;
213
214 offset = sparsearray[counter].offset;
215 if (lseek (diff_handle, offset, SEEK_SET) < 0)
216 {
217 seek_error_details (current_stat_info.file_name, offset);
218 report_difference (NULL);
219 }
220
221 /* Take care to not run out of room in our buffer. */
222
223 while (buffer_size < chunk_size)
224 {
225 if (buffer_size * 2 < buffer_size)
226 xalloc_die ();
227 buffer_size *= 2;
228 buffer = xrealloc (buffer, buffer_size * sizeof (char));
229 }
230
231 while (chunk_size > BLOCKSIZE)
232 {
233 if (status = safe_read (diff_handle, buffer, BLOCKSIZE),
234 status != BLOCKSIZE)
235 {
236 if (status < 0)
237 {
238 read_error (current_stat_info.file_name);
239 report_difference (NULL);
240 }
241 else
242 {
243 report_difference (ngettext ("Could only read %lu of %lu byte",
244 "Could only read %lu of %lu bytes",
245 chunk_size),
246 (unsigned long) status,
247 (unsigned long) chunk_size);
248 }
249 break;
250 }
251
252 if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
253 {
254 different = 1;
255 break;
256 }
257
258 chunk_size -= status;
259 remaining_size -= status;
260 set_next_block_after (data_block);
261 data_block = find_next_block ();
262 if (!data_block)
263 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
264 }
265 if (status = safe_read (diff_handle, buffer, chunk_size),
266 status != chunk_size)
267 {
268 if (status < 0)
269 {
270 read_error (current_stat_info.file_name);
271 report_difference (NULL);
272 }
273 else
274 {
275 report_difference (ngettext ("Could only read %lu of %lu byte",
276 "Could only read %lu of %lu bytes",
277 chunk_size),
278 (unsigned long) status,
279 (unsigned long) chunk_size);
280 }
281 break;
282 }
283
284 if (memcmp (buffer, data_block->buffer, chunk_size))
285 {
286 different = 1;
287 break;
288 }
289 #if 0
290 amount_read += chunk_size;
291 if (amount_read >= BLOCKSIZE)
292 {
293 amount_read = 0;
294 set_next_block_after (data_block);
295 data_block = find_next_block ();
296 if (!data_block)
297 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
298 }
299 #endif
300 set_next_block_after (data_block);
301 counter++;
302 remaining_size -= chunk_size;
303 }
304
305 #if 0
306 /* If the number of bytes read isn't the number of bytes supposedly in
307 the file, they're different. */
308
309 if (amount_read != current_stat_info.stat.st_size)
310 different = 1;
311 #endif
312
313 set_next_block_after (data_block);
314 free (sparsearray);
315
316 if (different)
317 report_difference (_("Contents differ"));
318 }
319
320 /* Call either stat or lstat over STAT_DATA, depending on
321 --dereference (-h), for a file which should exist. Diagnose any
322 problem. Return nonzero for success, zero otherwise. */
323 static int
324 get_stat_data (char const *file_name, struct stat *stat_data)
325 {
326 int status = deref_stat (dereference_option, file_name, stat_data);
327
328 if (status != 0)
329 {
330 if (errno == ENOENT)
331 stat_warn (file_name);
332 else
333 stat_error (file_name);
334 report_difference (NULL);
335 return 0;
336 }
337
338 return 1;
339 }
340
341 /* Diff a file against the archive. */
342 void
343 diff_archive (void)
344 {
345 struct stat stat_data;
346 int status;
347 struct utimbuf restore_times;
348
349 set_next_block_after (current_header);
350 decode_header (current_header, &current_stat_info, &current_format, 1);
351
352 /* Print the block from current_header and current_stat_info. */
353
354 if (verbose_option)
355 {
356 if (now_verifying)
357 fprintf (stdlis, _("Verify "));
358 print_header (-1);
359 }
360
361 switch (current_header->header.typeflag)
362 {
363 default:
364 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
365 quotearg_colon (current_stat_info.file_name),
366 current_header->header.typeflag));
367 /* Fall through. */
368
369 case AREGTYPE:
370 case REGTYPE:
371 case GNUTYPE_SPARSE:
372 case CONTTYPE:
373
374 /* Appears to be a file. See if it's really a directory. */
375
376 if (current_stat_info.had_trailing_slash)
377 goto really_dir;
378
379 if (!get_stat_data (current_stat_info.file_name, &stat_data))
380 {
381 skip_member ();
382 goto quit;
383 }
384
385 if (!S_ISREG (stat_data.st_mode))
386 {
387 report_difference (_("File type differs"));
388 skip_member ();
389 goto quit;
390 }
391
392 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
393 report_difference (_("Mode differs"));
394
395 sys_compare_uid_gid (&stat_data, &current_stat_info.stat);
396
397 if (stat_data.st_mtime != current_stat_info.stat.st_mtime)
398 report_difference (_("Mod time differs"));
399 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
400 stat_data.st_size != current_stat_info.stat.st_size)
401 {
402 report_difference (_("Size differs"));
403 skip_member ();
404 goto quit;
405 }
406
407 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
408
409 if (diff_handle < 0)
410 {
411 open_error (current_stat_info.file_name);
412 skip_member ();
413 report_difference (NULL);
414 goto quit;
415 }
416
417 restore_times.actime = stat_data.st_atime;
418 restore_times.modtime = stat_data.st_mtime;
419
420 /* Need to treat sparse files completely differently here. */
421
422 if (current_header->header.typeflag == GNUTYPE_SPARSE)
423 diff_sparse_files ();
424 else
425 {
426 if (multi_volume_option)
427 {
428 assign_string (&save_name, current_stat_info.file_name);
429 save_totsize = current_stat_info.stat.st_size;
430 /* save_sizeleft is set in read_and_process. */
431 }
432
433 read_and_process (current_stat_info.stat.st_size, process_rawdata);
434
435 if (multi_volume_option)
436 assign_string (&save_name, 0);
437 }
438
439 status = close (diff_handle);
440 if (status != 0)
441 close_error (current_stat_info.file_name);
442
443 if (atime_preserve_option)
444 utime (current_stat_info.file_name, &restore_times);
445
446 quit:
447 break;
448
449 case LNKTYPE:
450 {
451 struct stat link_data, stat_data;
452
453 if (!get_stat_data (current_stat_info.file_name, &stat_data))
454 break;
455 if (!get_stat_data (current_stat_info.link_name, &link_data))
456 break;
457 sys_compare_links (&stat_data, &link_data);
458 }
459 break;
460
461 #ifdef HAVE_READLINK
462 case SYMTYPE:
463 {
464 size_t len = strlen (current_stat_info.link_name);
465 char *linkbuf = alloca (len + 1);
466
467 status = readlink (current_stat_info.file_name, linkbuf, len + 1);
468
469 if (status < 0)
470 {
471 if (errno == ENOENT)
472 readlink_warn (current_stat_info.file_name);
473 else
474 readlink_error (current_stat_info.file_name);
475 report_difference (NULL);
476 }
477 else if (status != len
478 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
479 report_difference (_("Symlink differs"));
480
481 break;
482 }
483 #endif
484
485 case CHRTYPE:
486 case BLKTYPE:
487 case FIFOTYPE:
488
489 /* FIXME: deal with umask. */
490
491 if (!get_stat_data (current_stat_info.file_name, &stat_data))
492 break;
493
494 if (current_header->header.typeflag == CHRTYPE
495 ? !S_ISCHR (stat_data.st_mode)
496 : current_header->header.typeflag == BLKTYPE
497 ? !S_ISBLK (stat_data.st_mode)
498 : /* current_header->header.typeflag == FIFOTYPE */
499 !S_ISFIFO (stat_data.st_mode))
500 {
501 report_difference (_("File type differs"));
502 break;
503 }
504
505 if ((current_header->header.typeflag == CHRTYPE
506 || current_header->header.typeflag == BLKTYPE)
507 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
508 {
509 report_difference (_("Device number differs"));
510 break;
511 }
512
513 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
514 {
515 report_difference (_("Mode differs"));
516 break;
517 }
518
519 break;
520
521 case GNUTYPE_DUMPDIR:
522 {
523 char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name, 0);
524
525 if (multi_volume_option)
526 {
527 assign_string (&save_name, current_stat_info.file_name);
528 save_totsize = current_stat_info.stat.st_size;
529 /* save_sizeleft is set in read_and_process. */
530 }
531
532 if (dumpdir_buffer)
533 {
534 dumpdir_cursor = dumpdir_buffer;
535 read_and_process (current_stat_info.stat.st_size, process_dumpdir);
536 free (dumpdir_buffer);
537 }
538 else
539 read_and_process (current_stat_info.stat.st_size, process_noop);
540
541 if (multi_volume_option)
542 assign_string (&save_name, 0);
543 /* Fall through. */
544 }
545
546 case DIRTYPE:
547 really_dir:
548 if (!get_stat_data (current_stat_info.file_name, &stat_data))
549 break;
550
551 if (!S_ISDIR (stat_data.st_mode))
552 {
553 report_difference (_("File type differs"));
554 break;
555 }
556
557 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
558 {
559 report_difference (_("Mode differs"));
560 break;
561 }
562
563 break;
564
565 case GNUTYPE_VOLHDR:
566 break;
567
568 case GNUTYPE_MULTIVOL:
569 {
570 off_t offset;
571
572 if (current_stat_info.had_trailing_slash)
573 goto really_dir;
574
575 if (!get_stat_data (current_stat_info.file_name, &stat_data))
576 break;
577
578 if (!S_ISREG (stat_data.st_mode))
579 {
580 report_difference (_("File type differs"));
581 skip_member ();
582 break;
583 }
584
585 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
586 if (stat_data.st_size != current_stat_info.stat.st_size + offset)
587 {
588 report_difference (_("Size differs"));
589 skip_member ();
590 break;
591 }
592
593 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
594
595 if (diff_handle < 0)
596 {
597 open_error (current_stat_info.file_name);
598 report_difference (NULL);
599 skip_member ();
600 break;
601 }
602
603 if (lseek (diff_handle, offset, SEEK_SET) < 0)
604 {
605 seek_error_details (current_stat_info.file_name, offset);
606 report_difference (NULL);
607 break;
608 }
609
610 if (multi_volume_option)
611 {
612 assign_string (&save_name, current_stat_info.file_name);
613 save_totsize = stat_data.st_size;
614 /* save_sizeleft is set in read_and_process. */
615 }
616
617 read_and_process (current_stat_info.stat.st_size, process_rawdata);
618
619 if (multi_volume_option)
620 assign_string (&save_name, 0);
621
622 status = close (diff_handle);
623 if (status != 0)
624 close_error (current_stat_info.file_name);
625
626 break;
627 }
628 }
629 }
630
631 void
632 verify_volume (void)
633 {
634 if (!diff_buffer)
635 diff_init ();
636
637 /* Verifying an archive is meant to check if the physical media got it
638 correctly, so try to defeat clever in-memory buffering pertaining to
639 this particular media. On Linux, for example, the floppy drive would
640 not even be accessed for the whole verification.
641
642 The code was using fsync only when the ioctl is unavailable, but
643 Marty Leisner says that the ioctl does not work when not preceded by
644 fsync. So, until we know better, or maybe to please Marty, let's do it
645 the unbelievable way :-). */
646
647 #if HAVE_FSYNC
648 fsync (archive);
649 #endif
650 #ifdef FDFLUSH
651 ioctl (archive, FDFLUSH);
652 #endif
653
654 #ifdef MTIOCTOP
655 {
656 struct mtop operation;
657 int status;
658
659 operation.mt_op = MTBSF;
660 operation.mt_count = 1;
661 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
662 {
663 if (errno != EIO
664 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
665 status < 0))
666 {
667 #endif
668 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
669 {
670 /* Lseek failed. Try a different method. */
671 seek_warn (archive_name_array[0]);
672 return;
673 }
674 #ifdef MTIOCTOP
675 }
676 }
677 }
678 #endif
679
680 access_mode = ACCESS_READ;
681 now_verifying = 1;
682
683 flush_read ();
684 while (1)
685 {
686 enum read_header status = read_header (false);
687
688 if (status == HEADER_FAILURE)
689 {
690 int counter = 0;
691
692 do
693 {
694 counter++;
695 status = read_header (false);
696 }
697 while (status == HEADER_FAILURE);
698
699 ERROR ((0, 0,
700 ngettext ("VERIFY FAILURE: %d invalid header detected",
701 "VERIFY FAILURE: %d invalid headers detected",
702 counter), counter));
703 }
704 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
705 break;
706
707 diff_archive ();
708 }
709
710 access_mode = ACCESS_WRITE;
711 now_verifying = 0;
712 }
This page took 0.062543 seconds and 5 git commands to generate.