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