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