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