]> Dogcows Code - chaz/tar/blob - src/compare.c
Update FSF postal mail address.
[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, 2004 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 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 void *ptr;
58 diff_buffer = page_aligned_alloc (&ptr, record_size);
59 if (listed_incremental_option)
60 read_directory_file ();
61 }
62
63 /* Sigh about something that differs by writing a MESSAGE to stdlis,
64 given MESSAGE is nonzero. Also set the exit status if not already. */
65 void
66 report_difference (struct tar_stat_info *st __attribute__ ((unused)),
67 const char *fmt, ...)
68 {
69 if (fmt)
70 {
71 va_list ap;
72
73 fprintf (stdlis, "%s: ", quotearg_colon (current_stat_info.file_name));
74 va_start (ap, fmt);
75 vfprintf (stdlis, fmt, ap);
76 va_end (ap);
77 fprintf (stdlis, "\n");
78 }
79
80 if (exit_status == TAREXIT_SUCCESS)
81 exit_status = TAREXIT_DIFFERS;
82 }
83
84 /* Take a buffer returned by read_and_process and do nothing with it. */
85 static int
86 process_noop (size_t size __attribute__ ((unused)),
87 char *data __attribute__ ((unused)))
88 {
89 return 1;
90 }
91
92 static int
93 process_rawdata (size_t bytes, char *buffer)
94 {
95 size_t status = safe_read (diff_handle, diff_buffer, bytes);
96
97 if (status != bytes)
98 {
99 if (status == SAFE_READ_ERROR)
100 {
101 read_error (current_stat_info.file_name);
102 report_difference (&current_stat_info, NULL);
103 }
104 else
105 {
106 report_difference (&current_stat_info,
107 ngettext ("Could only read %lu of %lu byte",
108 "Could only read %lu of %lu bytes",
109 bytes),
110 (unsigned long) status, (unsigned long) bytes);
111 }
112 return 0;
113 }
114
115 if (memcmp (buffer, diff_buffer, bytes))
116 {
117 report_difference (&current_stat_info, _("Contents differ"));
118 return 0;
119 }
120
121 return 1;
122 }
123
124 /* Directory contents, only for GNUTYPE_DUMPDIR. */
125
126 static char *dumpdir_cursor;
127
128 static int
129 process_dumpdir (size_t bytes, char *buffer)
130 {
131 if (memcmp (buffer, dumpdir_cursor, bytes))
132 {
133 report_difference (&current_stat_info, _("Contents differ"));
134 return 0;
135 }
136
137 dumpdir_cursor += bytes;
138 return 1;
139 }
140
141 /* Some other routine wants SIZE bytes in the archive. For each chunk
142 of the archive, call PROCESSOR with the size of the chunk, and the
143 address of the chunk it can work with. The PROCESSOR should return
144 nonzero for success. It it return error once, continue skipping
145 without calling PROCESSOR anymore. */
146 static void
147 read_and_process (off_t size, int (*processor) (size_t, char *))
148 {
149 union block *data_block;
150 size_t data_size;
151
152 if (multi_volume_option)
153 save_sizeleft = size;
154 while (size)
155 {
156 data_block = find_next_block ();
157 if (! data_block)
158 {
159 ERROR ((0, 0, _("Unexpected EOF in archive")));
160 return;
161 }
162
163 data_size = available_space_after (data_block);
164 if (data_size > size)
165 data_size = size;
166 if (!(*processor) (data_size, data_block->buffer))
167 processor = process_noop;
168 set_next_block_after ((union block *)
169 (data_block->buffer + data_size - 1));
170 size -= data_size;
171 if (multi_volume_option)
172 save_sizeleft -= data_size;
173 }
174 }
175
176 /* Call either stat or lstat over STAT_DATA, depending on
177 --dereference (-h), for a file which should exist. Diagnose any
178 problem. Return nonzero for success, zero otherwise. */
179 static int
180 get_stat_data (char const *file_name, struct stat *stat_data)
181 {
182 int status = deref_stat (dereference_option, file_name, stat_data);
183
184 if (status != 0)
185 {
186 if (errno == ENOENT)
187 stat_warn (file_name);
188 else
189 stat_error (file_name);
190 report_difference (&current_stat_info, NULL);
191 return 0;
192 }
193
194 return 1;
195 }
196
197 \f
198 static void
199 diff_dir ()
200 {
201 struct stat stat_data;
202
203 if (!get_stat_data (current_stat_info.file_name, &stat_data))
204 return;
205
206 if (!S_ISDIR (stat_data.st_mode))
207 report_difference (&current_stat_info, _("File type differs"));
208 else if ((current_stat_info.stat.st_mode & MODE_ALL) !=
209 (stat_data.st_mode & MODE_ALL))
210 report_difference (&current_stat_info, _("Mode differs"));
211 }
212
213 static void
214 diff_file ()
215 {
216 struct stat stat_data;
217
218 if (!get_stat_data (current_stat_info.file_name, &stat_data))
219 skip_member ();
220 else if (!S_ISREG (stat_data.st_mode))
221 {
222 report_difference (&current_stat_info, _("File type differs"));
223 skip_member ();
224 }
225 else
226 {
227 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
228 (stat_data.st_mode & MODE_ALL))
229 report_difference (&current_stat_info, _("Mode differs"));
230
231 if (!sys_compare_uid (&stat_data, &current_stat_info.stat))
232 report_difference (&current_stat_info, _("Uid differs"));
233 if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
234 report_difference (&current_stat_info, _("Gid differs"));
235
236 if (stat_data.st_mtime != current_stat_info.stat.st_mtime)
237 report_difference (&current_stat_info, _("Mod time differs"));
238 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
239 stat_data.st_size != current_stat_info.stat.st_size)
240 {
241 report_difference (&current_stat_info, _("Size differs"));
242 skip_member ();
243 }
244 else
245 {
246 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
247
248 if (diff_handle < 0)
249 {
250 open_error (current_stat_info.file_name);
251 skip_member ();
252 report_difference (&current_stat_info, NULL);
253 }
254 else
255 {
256 int status;
257 struct utimbuf restore_times;
258
259 restore_times.actime = stat_data.st_atime;
260 restore_times.modtime = stat_data.st_mtime;
261
262 if (current_stat_info.is_sparse)
263 sparse_diff_file (diff_handle, &current_stat_info);
264 else
265 {
266 if (multi_volume_option)
267 {
268 assign_string (&save_name,
269 current_stat_info.orig_file_name);
270 save_totsize = current_stat_info.stat.st_size;
271 /* save_sizeleft is set in read_and_process. */
272 }
273
274 read_and_process (current_stat_info.stat.st_size,
275 process_rawdata);
276
277 if (multi_volume_option)
278 assign_string (&save_name, 0);
279 }
280
281 status = close (diff_handle);
282 if (status != 0)
283 close_error (current_stat_info.file_name);
284
285 if (atime_preserve_option)
286 utime (current_stat_info.file_name, &restore_times);
287 }
288 }
289 }
290 }
291
292 static void
293 diff_link ()
294 {
295 struct stat file_data;
296 struct stat link_data;
297
298 if (get_stat_data (current_stat_info.file_name, &file_data)
299 && get_stat_data (current_stat_info.link_name, &link_data)
300 && !sys_compare_links (&file_data, &link_data))
301 report_difference (&current_stat_info,
302 _("Not linked to %s"),
303 quote (current_stat_info.link_name));
304 }
305
306 #ifdef HAVE_READLINK
307 static void
308 diff_symlink ()
309 {
310 size_t len = strlen (current_stat_info.link_name);
311 char *linkbuf = alloca (len + 1);
312
313 int status = readlink (current_stat_info.file_name, linkbuf, len + 1);
314
315 if (status < 0)
316 {
317 if (errno == ENOENT)
318 readlink_warn (current_stat_info.file_name);
319 else
320 readlink_error (current_stat_info.file_name);
321 report_difference (&current_stat_info, NULL);
322 }
323 else if (status != len
324 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
325 report_difference (&current_stat_info, _("Symlink differs"));
326 }
327 #endif
328
329 static void
330 diff_special ()
331 {
332 struct stat stat_data;
333
334 /* FIXME: deal with umask. */
335
336 if (!get_stat_data (current_stat_info.file_name, &stat_data))
337 return;
338
339 if (current_header->header.typeflag == CHRTYPE
340 ? !S_ISCHR (stat_data.st_mode)
341 : current_header->header.typeflag == BLKTYPE
342 ? !S_ISBLK (stat_data.st_mode)
343 : /* current_header->header.typeflag == FIFOTYPE */
344 !S_ISFIFO (stat_data.st_mode))
345 {
346 report_difference (&current_stat_info, _("File type differs"));
347 return;
348 }
349
350 if ((current_header->header.typeflag == CHRTYPE
351 || current_header->header.typeflag == BLKTYPE)
352 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
353 {
354 report_difference (&current_stat_info, _("Device number differs"));
355 return;
356 }
357
358 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
359 (stat_data.st_mode & MODE_ALL))
360 report_difference (&current_stat_info, _("Mode differs"));
361 }
362
363 static void
364 diff_dumpdir ()
365 {
366 char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name,
367 0);
368
369 if (multi_volume_option)
370 {
371 assign_string (&save_name, current_stat_info.orig_file_name);
372 save_totsize = current_stat_info.stat.st_size;
373 /* save_sizeleft is set in read_and_process. */
374 }
375
376 if (dumpdir_buffer)
377 {
378 dumpdir_cursor = dumpdir_buffer;
379 read_and_process (current_stat_info.stat.st_size, process_dumpdir);
380 free (dumpdir_buffer);
381 }
382 else
383 read_and_process (current_stat_info.stat.st_size, process_noop);
384
385 if (multi_volume_option)
386 assign_string (&save_name, 0);
387 }
388
389 static void
390 diff_multivol ()
391 {
392 struct stat stat_data;
393 int fd, status;
394 off_t offset;
395
396 if (current_stat_info.had_trailing_slash)
397 {
398 diff_dir ();
399 return;
400 }
401
402 if (!get_stat_data (current_stat_info.file_name, &stat_data))
403 return;
404
405 if (!S_ISREG (stat_data.st_mode))
406 {
407 report_difference (&current_stat_info, _("File type differs"));
408 skip_member ();
409 return;
410 }
411
412 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
413 if (stat_data.st_size != current_stat_info.stat.st_size + offset)
414 {
415 report_difference (&current_stat_info, _("Size differs"));
416 skip_member ();
417 return;
418 }
419
420 fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
421
422 if (fd < 0)
423 {
424 open_error (current_stat_info.file_name);
425 report_difference (&current_stat_info, NULL);
426 skip_member ();
427 return;
428 }
429
430 if (lseek (fd, offset, SEEK_SET) < 0)
431 {
432 seek_error_details (current_stat_info.file_name, offset);
433 report_difference (&current_stat_info, NULL);
434 return;
435 }
436
437 if (multi_volume_option)
438 {
439 assign_string (&save_name, current_stat_info.orig_file_name);
440 save_totsize = stat_data.st_size;
441 /* save_sizeleft is set in read_and_process. */
442 }
443
444 read_and_process (current_stat_info.stat.st_size, process_rawdata);
445
446 if (multi_volume_option)
447 assign_string (&save_name, 0);
448
449 status = close (fd);
450 if (status != 0)
451 close_error (current_stat_info.file_name);
452 }
453
454 /* Diff a file against the archive. */
455 void
456 diff_archive (void)
457 {
458
459 set_next_block_after (current_header);
460 decode_header (current_header, &current_stat_info, &current_format, 1);
461
462 /* Print the block from current_header and current_stat_info. */
463
464 if (verbose_option)
465 {
466 if (now_verifying)
467 fprintf (stdlis, _("Verify "));
468 print_header (&current_stat_info, -1);
469 }
470
471 switch (current_header->header.typeflag)
472 {
473 default:
474 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
475 quotearg_colon (current_stat_info.file_name),
476 current_header->header.typeflag));
477 /* Fall through. */
478
479 case AREGTYPE:
480 case REGTYPE:
481 case GNUTYPE_SPARSE:
482 case CONTTYPE:
483
484 /* Appears to be a file. See if it's really a directory. */
485
486 if (current_stat_info.had_trailing_slash)
487 diff_dir ();
488 else
489 diff_file ();
490 break;
491
492 case LNKTYPE:
493 diff_link ();
494 break;
495
496 #ifdef HAVE_READLINK
497 case SYMTYPE:
498 diff_symlink ();
499 break;
500 #endif
501
502 case CHRTYPE:
503 case BLKTYPE:
504 case FIFOTYPE:
505 diff_special ();
506 break;
507
508 case GNUTYPE_DUMPDIR:
509 diff_dumpdir ();
510 /* Fall through. */
511
512 case DIRTYPE:
513 diff_dir ();
514 break;
515
516 case GNUTYPE_VOLHDR:
517 break;
518
519 case GNUTYPE_MULTIVOL:
520 diff_multivol ();
521 }
522 }
523
524 void
525 verify_volume (void)
526 {
527 if (removed_prefixes_p ())
528 {
529 WARN((0, 0,
530 _("Archive contains file names with leading prefixes removed.")));
531 WARN((0, 0,
532 _("Verification may fail to locate original files.")));
533 }
534
535 if (!diff_buffer)
536 diff_init ();
537
538 /* Verifying an archive is meant to check if the physical media got it
539 correctly, so try to defeat clever in-memory buffering pertaining to
540 this particular media. On Linux, for example, the floppy drive would
541 not even be accessed for the whole verification.
542
543 The code was using fsync only when the ioctl is unavailable, but
544 Marty Leisner says that the ioctl does not work when not preceded by
545 fsync. So, until we know better, or maybe to please Marty, let's do it
546 the unbelievable way :-). */
547
548 #if HAVE_FSYNC
549 fsync (archive);
550 #endif
551 #ifdef FDFLUSH
552 ioctl (archive, FDFLUSH);
553 #endif
554
555 #ifdef MTIOCTOP
556 {
557 struct mtop operation;
558 int status;
559
560 operation.mt_op = MTBSF;
561 operation.mt_count = 1;
562 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
563 {
564 if (errno != EIO
565 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
566 status < 0))
567 {
568 #endif
569 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
570 {
571 /* Lseek failed. Try a different method. */
572 seek_warn (archive_name_array[0]);
573 return;
574 }
575 #ifdef MTIOCTOP
576 }
577 }
578 }
579 #endif
580
581 access_mode = ACCESS_READ;
582 now_verifying = 1;
583
584 flush_read ();
585 while (1)
586 {
587 enum read_header status = read_header (false);
588
589 if (status == HEADER_FAILURE)
590 {
591 int counter = 0;
592
593 do
594 {
595 counter++;
596 set_next_block_after (current_header);
597 status = read_header (false);
598 }
599 while (status == HEADER_FAILURE);
600
601 ERROR ((0, 0,
602 ngettext ("VERIFY FAILURE: %d invalid header detected",
603 "VERIFY FAILURE: %d invalid headers detected",
604 counter), counter));
605 }
606 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
607 break;
608
609 diff_archive ();
610 tar_stat_destroy (&current_stat_info);
611 xheader_destroy (&extended_header);
612 }
613
614 access_mode = ACCESS_WRITE;
615 now_verifying = 0;
616 }
This page took 0.061006 seconds and 5 git commands to generate.