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