]> Dogcows Code - chaz/tar/blob - src/compare.c
Read POSIX multivolume archives split at the header boundary.
[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 (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, O_RDONLY | O_BINARY | atime_flag);
226
227 if (diff_handle < 0)
228 {
229 open_error (file_name);
230 skip_member ();
231 report_difference (&current_stat_info, NULL);
232 }
233 else
234 {
235 int status;
236
237 if (current_stat_info.is_sparse)
238 sparse_diff_file (diff_handle, &current_stat_info);
239 else
240 read_and_process (&current_stat_info, process_rawdata);
241
242 if (atime_preserve_option == replace_atime_preserve)
243 {
244 struct timespec ts[2];
245 ts[0] = get_stat_atime (&stat_data);
246 ts[1] = get_stat_mtime (&stat_data);
247 if (set_file_atime (diff_handle, file_name, ts) != 0)
248 utime_error (file_name);
249 }
250
251 status = close (diff_handle);
252 if (status != 0)
253 close_error (file_name);
254 }
255 }
256 }
257 }
258
259 static void
260 diff_link (void)
261 {
262 struct stat file_data;
263 struct stat link_data;
264
265 if (get_stat_data (current_stat_info.file_name, &file_data)
266 && get_stat_data (current_stat_info.link_name, &link_data)
267 && !sys_compare_links (&file_data, &link_data))
268 report_difference (&current_stat_info,
269 _("Not linked to %s"),
270 quote (current_stat_info.link_name));
271 }
272
273 #ifdef HAVE_READLINK
274 static void
275 diff_symlink (void)
276 {
277 size_t len = strlen (current_stat_info.link_name);
278 char *linkbuf = alloca (len + 1);
279
280 int status = readlink (current_stat_info.file_name, linkbuf, len + 1);
281
282 if (status < 0)
283 {
284 if (errno == ENOENT)
285 readlink_warn (current_stat_info.file_name);
286 else
287 readlink_error (current_stat_info.file_name);
288 report_difference (&current_stat_info, NULL);
289 }
290 else if (status != len
291 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
292 report_difference (&current_stat_info, _("Symlink differs"));
293 }
294 #endif
295
296 static void
297 diff_special (void)
298 {
299 struct stat stat_data;
300
301 /* FIXME: deal with umask. */
302
303 if (!get_stat_data (current_stat_info.file_name, &stat_data))
304 return;
305
306 if (current_header->header.typeflag == CHRTYPE
307 ? !S_ISCHR (stat_data.st_mode)
308 : current_header->header.typeflag == BLKTYPE
309 ? !S_ISBLK (stat_data.st_mode)
310 : /* current_header->header.typeflag == FIFOTYPE */
311 !S_ISFIFO (stat_data.st_mode))
312 {
313 report_difference (&current_stat_info, _("File type differs"));
314 return;
315 }
316
317 if ((current_header->header.typeflag == CHRTYPE
318 || current_header->header.typeflag == BLKTYPE)
319 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
320 {
321 report_difference (&current_stat_info, _("Device number differs"));
322 return;
323 }
324
325 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
326 (stat_data.st_mode & MODE_ALL))
327 report_difference (&current_stat_info, _("Mode differs"));
328 }
329
330 static int
331 dumpdir_cmp (const char *a, const char *b)
332 {
333 size_t len;
334
335 while (*a)
336 switch (*a)
337 {
338 case 'Y':
339 case 'N':
340 if (!strchr ("YN", *b))
341 return 1;
342 if (strcmp(a + 1, b + 1))
343 return 1;
344 len = strlen (a) + 1;
345 a += len;
346 b += len;
347 break;
348
349 case 'D':
350 if (strcmp(a, b))
351 return 1;
352 len = strlen (a) + 1;
353 a += len;
354 b += len;
355 break;
356
357 case 'R':
358 case 'T':
359 case 'X':
360 return *b;
361 }
362 return *b;
363 }
364
365 static void
366 diff_dumpdir (void)
367 {
368 const char *dumpdir_buffer;
369 dev_t dev = 0;
370 struct stat stat_data;
371
372 if (deref_stat (true, current_stat_info.file_name, &stat_data))
373 {
374 if (errno == ENOENT)
375 stat_warn (current_stat_info.file_name);
376 else
377 stat_error (current_stat_info.file_name);
378 }
379 else
380 dev = stat_data.st_dev;
381
382 dumpdir_buffer = directory_contents
383 (scan_directory (current_stat_info.file_name, dev, false));
384
385 if (dumpdir_buffer)
386 {
387 if (dumpdir_cmp (current_stat_info.dumpdir, dumpdir_buffer))
388 report_difference (&current_stat_info, _("Contents differ"));
389 }
390 else
391 read_and_process (&current_stat_info, process_noop);
392 }
393
394 static void
395 diff_multivol (void)
396 {
397 struct stat stat_data;
398 int fd, status;
399 off_t offset;
400
401 if (current_stat_info.had_trailing_slash)
402 {
403 diff_dir ();
404 return;
405 }
406
407 if (!get_stat_data (current_stat_info.file_name, &stat_data))
408 return;
409
410 if (!S_ISREG (stat_data.st_mode))
411 {
412 report_difference (&current_stat_info, _("File type differs"));
413 skip_member ();
414 return;
415 }
416
417 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
418 if (stat_data.st_size != current_stat_info.stat.st_size + offset)
419 {
420 report_difference (&current_stat_info, _("Size differs"));
421 skip_member ();
422 return;
423 }
424
425 fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
426
427 if (fd < 0)
428 {
429 open_error (current_stat_info.file_name);
430 report_difference (&current_stat_info, NULL);
431 skip_member ();
432 return;
433 }
434
435 if (lseek (fd, offset, SEEK_SET) < 0)
436 {
437 seek_error_details (current_stat_info.file_name, offset);
438 report_difference (&current_stat_info, NULL);
439 return;
440 }
441
442 read_and_process (&current_stat_info, process_rawdata);
443
444 status = close (fd);
445 if (status != 0)
446 close_error (current_stat_info.file_name);
447 }
448
449 /* Diff a file against the archive. */
450 void
451 diff_archive (void)
452 {
453
454 set_next_block_after (current_header);
455 decode_header (current_header, &current_stat_info, &current_format, 1);
456
457 /* Print the block from current_header and current_stat_info. */
458
459 if (verbose_option)
460 {
461 if (now_verifying)
462 fprintf (stdlis, _("Verify "));
463 print_header (&current_stat_info, current_header, -1);
464 }
465
466 switch (current_header->header.typeflag)
467 {
468 default:
469 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
470 quotearg_colon (current_stat_info.file_name),
471 current_header->header.typeflag));
472 /* Fall through. */
473
474 case AREGTYPE:
475 case REGTYPE:
476 case GNUTYPE_SPARSE:
477 case CONTTYPE:
478
479 /* Appears to be a file. See if it's really a directory. */
480
481 if (current_stat_info.had_trailing_slash)
482 diff_dir ();
483 else
484 diff_file ();
485 break;
486
487 case LNKTYPE:
488 diff_link ();
489 break;
490
491 #ifdef HAVE_READLINK
492 case SYMTYPE:
493 diff_symlink ();
494 break;
495 #endif
496
497 case CHRTYPE:
498 case BLKTYPE:
499 case FIFOTYPE:
500 diff_special ();
501 break;
502
503 case GNUTYPE_DUMPDIR:
504 case DIRTYPE:
505 if (is_dumpdir (&current_stat_info))
506 diff_dumpdir ();
507 diff_dir ();
508 break;
509
510 case GNUTYPE_VOLHDR:
511 break;
512
513 case GNUTYPE_MULTIVOL:
514 diff_multivol ();
515 }
516 }
517
518 void
519 verify_volume (void)
520 {
521 if (removed_prefixes_p ())
522 {
523 WARN((0, 0,
524 _("Archive contains file names with leading prefixes removed.")));
525 WARN((0, 0,
526 _("Verification may fail to locate original files.")));
527 }
528
529 if (!diff_buffer)
530 diff_init ();
531
532 /* Verifying an archive is meant to check if the physical media got it
533 correctly, so try to defeat clever in-memory buffering pertaining to
534 this particular media. On Linux, for example, the floppy drive would
535 not even be accessed for the whole verification.
536
537 The code was using fsync only when the ioctl is unavailable, but
538 Marty Leisner says that the ioctl does not work when not preceded by
539 fsync. So, until we know better, or maybe to please Marty, let's do it
540 the unbelievable way :-). */
541
542 #if HAVE_FSYNC
543 fsync (archive);
544 #endif
545 #ifdef FDFLUSH
546 ioctl (archive, FDFLUSH);
547 #endif
548
549 #ifdef MTIOCTOP
550 {
551 struct mtop operation;
552 int status;
553
554 operation.mt_op = MTBSF;
555 operation.mt_count = 1;
556 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
557 {
558 if (errno != EIO
559 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
560 status < 0))
561 {
562 #endif
563 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
564 {
565 /* Lseek failed. Try a different method. */
566 seek_warn (archive_name_array[0]);
567 return;
568 }
569 #ifdef MTIOCTOP
570 }
571 }
572 }
573 #endif
574
575 access_mode = ACCESS_READ;
576 now_verifying = 1;
577
578 flush_read ();
579 while (1)
580 {
581 enum read_header status = read_header (&current_header,
582 &current_stat_info,
583 read_header_auto);
584
585 if (status == HEADER_FAILURE)
586 {
587 int counter = 0;
588
589 do
590 {
591 counter++;
592 set_next_block_after (current_header);
593 status = read_header (&current_header, &current_stat_info,
594 read_header_auto);
595 }
596 while (status == HEADER_FAILURE);
597
598 ERROR ((0, 0,
599 ngettext ("VERIFY FAILURE: %d invalid header detected",
600 "VERIFY FAILURE: %d invalid headers detected",
601 counter), counter));
602 }
603 if (status == HEADER_END_OF_FILE)
604 break;
605 if (status == HEADER_ZERO_BLOCK)
606 {
607 set_next_block_after (current_header);
608 if (!ignore_zeros_option)
609 {
610 char buf[UINTMAX_STRSIZE_BOUND];
611
612 status = read_header (&current_header, &current_stat_info,
613 read_header_auto);
614 if (status == HEADER_ZERO_BLOCK)
615 break;
616 WARNOPT (WARN_ALONE_ZERO_BLOCK,
617 (0, 0, _("A lone zero block at %s"),
618 STRINGIFY_BIGINT (current_block_ordinal (), buf)));
619 }
620 }
621
622 diff_archive ();
623 tar_stat_destroy (&current_stat_info);
624 }
625
626 access_mode = ACCESS_WRITE;
627 now_verifying = 0;
628 }
This page took 0.082362 seconds and 5 git commands to generate.