]> Dogcows Code - chaz/tar/blob - src/compare.c
Update
[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 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 #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 if (exit_status == TAREXIT_SUCCESS)
70 exit_status = TAREXIT_DIFFERS;
71 }
72
73 /* Take a buffer returned by read_and_process and do nothing with it. */
74 static int
75 process_noop (size_t size __attribute__ ((unused)),
76 char *data __attribute__ ((unused)))
77 {
78 return 1;
79 }
80
81 static int
82 process_rawdata (size_t bytes, char *buffer)
83 {
84 size_t status = safe_read (diff_handle, diff_buffer, bytes);
85
86 if (status != bytes)
87 {
88 if (status == SAFE_READ_ERROR)
89 {
90 read_error (current_stat_info.file_name);
91 report_difference (&current_stat_info, NULL);
92 }
93 else
94 {
95 report_difference (&current_stat_info,
96 ngettext ("Could only read %lu of %lu byte",
97 "Could only read %lu of %lu bytes",
98 bytes),
99 (unsigned long) status, (unsigned long) bytes);
100 }
101 return 0;
102 }
103
104 if (memcmp (buffer, diff_buffer, bytes))
105 {
106 report_difference (&current_stat_info, _("Contents differ"));
107 return 0;
108 }
109
110 return 1;
111 }
112
113 /* Some other routine wants SIZE bytes in the archive. For each chunk
114 of the archive, call PROCESSOR with the size of the chunk, and the
115 address of the chunk it can work with. The PROCESSOR should return
116 nonzero for success. Once it returns error, continue skipping
117 without calling PROCESSOR anymore. */
118
119 static void
120 read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
121 {
122 union block *data_block;
123 size_t data_size;
124 off_t size = st->stat.st_size;
125
126 mv_begin (st);
127 while (size)
128 {
129 data_block = find_next_block ();
130 if (! data_block)
131 {
132 ERROR ((0, 0, _("Unexpected EOF in archive")));
133 return;
134 }
135
136 data_size = available_space_after (data_block);
137 if (data_size > size)
138 data_size = size;
139 if (!(*processor) (data_size, data_block->buffer))
140 processor = process_noop;
141 set_next_block_after ((union block *)
142 (data_block->buffer + data_size - 1));
143 size -= data_size;
144 mv_size_left (size);
145 }
146 mv_end ();
147 }
148
149 /* Call either stat or lstat over STAT_DATA, depending on
150 --dereference (-h), for a file which should exist. Diagnose any
151 problem. Return nonzero for success, zero otherwise. */
152 static int
153 get_stat_data (char const *file_name, struct stat *stat_data)
154 {
155 int status = deref_stat (dereference_option, file_name, stat_data);
156
157 if (status != 0)
158 {
159 if (errno == ENOENT)
160 stat_warn (file_name);
161 else
162 stat_error (file_name);
163 report_difference (&current_stat_info, NULL);
164 return 0;
165 }
166
167 return 1;
168 }
169
170 \f
171 static void
172 diff_dir (void)
173 {
174 struct stat stat_data;
175
176 if (!get_stat_data (current_stat_info.file_name, &stat_data))
177 return;
178
179 if (!S_ISDIR (stat_data.st_mode))
180 report_difference (&current_stat_info, _("File type differs"));
181 else if ((current_stat_info.stat.st_mode & MODE_ALL) !=
182 (stat_data.st_mode & MODE_ALL))
183 report_difference (&current_stat_info, _("Mode differs"));
184 }
185
186 static void
187 diff_file (void)
188 {
189 char const *file_name = current_stat_info.file_name;
190 struct stat stat_data;
191
192 if (!get_stat_data (file_name, &stat_data))
193 skip_member ();
194 else if (!S_ISREG (stat_data.st_mode))
195 {
196 report_difference (&current_stat_info, _("File type differs"));
197 skip_member ();
198 }
199 else
200 {
201 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
202 (stat_data.st_mode & MODE_ALL))
203 report_difference (&current_stat_info, _("Mode differs"));
204
205 if (!sys_compare_uid (&stat_data, &current_stat_info.stat))
206 report_difference (&current_stat_info, _("Uid differs"));
207 if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
208 report_difference (&current_stat_info, _("Gid differs"));
209
210 if (tar_timespec_cmp (get_stat_mtime (&stat_data),
211 current_stat_info.mtime))
212 report_difference (&current_stat_info, _("Mod time differs"));
213 if (current_header->header.typeflag != GNUTYPE_SPARSE
214 && stat_data.st_size != current_stat_info.stat.st_size)
215 {
216 report_difference (&current_stat_info, _("Size differs"));
217 skip_member ();
218 }
219 else
220 {
221 int atime_flag =
222 (atime_preserve_option == system_atime_preserve
223 ? O_NOATIME
224 : 0);
225
226 diff_handle = open (file_name, O_RDONLY | O_BINARY | atime_flag);
227
228 if (diff_handle < 0)
229 {
230 open_error (file_name);
231 skip_member ();
232 report_difference (&current_stat_info, NULL);
233 }
234 else
235 {
236 int status;
237
238 if (current_stat_info.is_sparse)
239 sparse_diff_file (diff_handle, &current_stat_info);
240 else
241 read_and_process (&current_stat_info, process_rawdata);
242
243 if (atime_preserve_option == replace_atime_preserve)
244 {
245 struct timespec ts[2];
246 ts[0] = get_stat_atime (&stat_data);
247 ts[1] = get_stat_mtime (&stat_data);
248 if (set_file_atime (diff_handle, file_name, ts) != 0)
249 utime_error (file_name);
250 }
251
252 status = close (diff_handle);
253 if (status != 0)
254 close_error (file_name);
255 }
256 }
257 }
258 }
259
260 static void
261 diff_link (void)
262 {
263 struct stat file_data;
264 struct stat link_data;
265
266 if (get_stat_data (current_stat_info.file_name, &file_data)
267 && get_stat_data (current_stat_info.link_name, &link_data)
268 && !sys_compare_links (&file_data, &link_data))
269 report_difference (&current_stat_info,
270 _("Not linked to %s"),
271 quote (current_stat_info.link_name));
272 }
273
274 #ifdef HAVE_READLINK
275 static void
276 diff_symlink (void)
277 {
278 size_t len = strlen (current_stat_info.link_name);
279 char *linkbuf = alloca (len + 1);
280
281 int status = readlink (current_stat_info.file_name, linkbuf, len + 1);
282
283 if (status < 0)
284 {
285 if (errno == ENOENT)
286 readlink_warn (current_stat_info.file_name);
287 else
288 readlink_error (current_stat_info.file_name);
289 report_difference (&current_stat_info, NULL);
290 }
291 else if (status != len
292 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
293 report_difference (&current_stat_info, _("Symlink differs"));
294 }
295 #endif
296
297 static void
298 diff_special (void)
299 {
300 struct stat stat_data;
301
302 /* FIXME: deal with umask. */
303
304 if (!get_stat_data (current_stat_info.file_name, &stat_data))
305 return;
306
307 if (current_header->header.typeflag == CHRTYPE
308 ? !S_ISCHR (stat_data.st_mode)
309 : current_header->header.typeflag == BLKTYPE
310 ? !S_ISBLK (stat_data.st_mode)
311 : /* current_header->header.typeflag == FIFOTYPE */
312 !S_ISFIFO (stat_data.st_mode))
313 {
314 report_difference (&current_stat_info, _("File type differs"));
315 return;
316 }
317
318 if ((current_header->header.typeflag == CHRTYPE
319 || current_header->header.typeflag == BLKTYPE)
320 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
321 {
322 report_difference (&current_stat_info, _("Device number differs"));
323 return;
324 }
325
326 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
327 (stat_data.st_mode & MODE_ALL))
328 report_difference (&current_stat_info, _("Mode differs"));
329 }
330
331 static int
332 dumpdir_cmp (const char *a, const char *b)
333 {
334 size_t len;
335
336 while (*a)
337 switch (*a)
338 {
339 case 'Y':
340 case 'N':
341 if (!strchr ("YN", *b))
342 return 1;
343 if (strcmp(a + 1, b + 1))
344 return 1;
345 len = strlen (a) + 1;
346 a += len;
347 b += len;
348 break;
349
350 case 'D':
351 if (strcmp(a, b))
352 return 1;
353 len = strlen (a) + 1;
354 a += len;
355 b += len;
356 break;
357
358 case 'R':
359 case 'T':
360 case 'X':
361 return *b;
362 }
363 return *b;
364 }
365
366 static void
367 diff_dumpdir (void)
368 {
369 char *dumpdir_buffer;
370 dev_t dev = 0;
371 struct stat stat;
372
373 if (deref_stat (true, current_stat_info.file_name, &stat))
374 {
375 if (errno == ENOENT)
376 stat_warn (current_stat_info.file_name);
377 else
378 stat_error (current_stat_info.file_name);
379 }
380 else
381 dev = stat.st_dev;
382
383 dumpdir_buffer = get_directory_contents (current_stat_info.file_name, dev);
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, -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 (false);
582
583 if (status == HEADER_FAILURE)
584 {
585 int counter = 0;
586
587 do
588 {
589 counter++;
590 set_next_block_after (current_header);
591 status = read_header (false);
592 }
593 while (status == HEADER_FAILURE);
594
595 ERROR ((0, 0,
596 ngettext ("VERIFY FAILURE: %d invalid header detected",
597 "VERIFY FAILURE: %d invalid headers detected",
598 counter), counter));
599 }
600 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
601 break;
602
603 diff_archive ();
604 tar_stat_destroy (&current_stat_info);
605 }
606
607 access_mode = ACCESS_WRITE;
608 now_verifying = 0;
609 }
This page took 0.058364 seconds and 4 git commands to generate.