]> Dogcows Code - chaz/tar/blob - src/compare.c
Merge recent gnulib changes, and remove some lint.
[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 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 __attribute__ ((unused)),
66 const char *fmt, ...)
67 {
68 if (fmt)
69 {
70 va_list ap;
71
72 fprintf (stdlis, "%s: ", quotearg_colon (current_stat_info.file_name));
73 va_start (ap, fmt);
74 vfprintf (stdlis, fmt, ap);
75 va_end (ap);
76 fprintf (stdlis, "\n");
77 }
78
79 if (exit_status == TAREXIT_SUCCESS)
80 exit_status = TAREXIT_DIFFERS;
81 }
82
83 /* Take a buffer returned by read_and_process and do nothing with it. */
84 static int
85 process_noop (size_t size __attribute__ ((unused)),
86 char *data __attribute__ ((unused)))
87 {
88 return 1;
89 }
90
91 static int
92 process_rawdata (size_t bytes, char *buffer)
93 {
94 size_t status = safe_read (diff_handle, diff_buffer, bytes);
95
96 if (status != bytes)
97 {
98 if (status == SAFE_READ_ERROR)
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_stat_info.is_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 file_data;
312 struct stat link_data;
313
314 if (!get_stat_data (current_stat_info.file_name, &file_data))
315 break;
316 if (!get_stat_data (current_stat_info.link_name, &link_data))
317 break;
318 if (!sys_compare_links (&file_data, &link_data))
319 report_difference (&current_stat_info,
320 _("Not linked to %s"),
321 quote (current_stat_info.link_name));
322 }
323 break;
324
325 #ifdef HAVE_READLINK
326 case SYMTYPE:
327 {
328 size_t len = strlen (current_stat_info.link_name);
329 char *linkbuf = alloca (len + 1);
330
331 status = readlink (current_stat_info.file_name, linkbuf, len + 1);
332
333 if (status < 0)
334 {
335 if (errno == ENOENT)
336 readlink_warn (current_stat_info.file_name);
337 else
338 readlink_error (current_stat_info.file_name);
339 report_difference (&current_stat_info, NULL);
340 }
341 else if (status != len
342 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
343 report_difference (&current_stat_info, _("Symlink differs"));
344
345 break;
346 }
347 #endif
348
349 case CHRTYPE:
350 case BLKTYPE:
351 case FIFOTYPE:
352
353 /* FIXME: deal with umask. */
354
355 if (!get_stat_data (current_stat_info.file_name, &stat_data))
356 break;
357
358 if (current_header->header.typeflag == CHRTYPE
359 ? !S_ISCHR (stat_data.st_mode)
360 : current_header->header.typeflag == BLKTYPE
361 ? !S_ISBLK (stat_data.st_mode)
362 : /* current_header->header.typeflag == FIFOTYPE */
363 !S_ISFIFO (stat_data.st_mode))
364 {
365 report_difference (&current_stat_info, _("File type differs"));
366 break;
367 }
368
369 if ((current_header->header.typeflag == CHRTYPE
370 || current_header->header.typeflag == BLKTYPE)
371 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
372 {
373 report_difference (&current_stat_info, _("Device number differs"));
374 break;
375 }
376
377 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
378 {
379 report_difference (&current_stat_info, _("Mode differs"));
380 break;
381 }
382
383 break;
384
385 case GNUTYPE_DUMPDIR:
386 {
387 char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name, 0);
388
389 if (multi_volume_option)
390 {
391 assign_string (&save_name, current_stat_info.file_name);
392 save_totsize = current_stat_info.stat.st_size;
393 /* save_sizeleft is set in read_and_process. */
394 }
395
396 if (dumpdir_buffer)
397 {
398 dumpdir_cursor = dumpdir_buffer;
399 read_and_process (current_stat_info.stat.st_size, process_dumpdir);
400 free (dumpdir_buffer);
401 }
402 else
403 read_and_process (current_stat_info.stat.st_size, process_noop);
404
405 if (multi_volume_option)
406 assign_string (&save_name, 0);
407 /* Fall through. */
408 }
409
410 case DIRTYPE:
411 really_dir:
412 if (!get_stat_data (current_stat_info.file_name, &stat_data))
413 break;
414
415 if (!S_ISDIR (stat_data.st_mode))
416 {
417 report_difference (&current_stat_info, _("File type differs"));
418 break;
419 }
420
421 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
422 {
423 report_difference (&current_stat_info, _("Mode differs"));
424 break;
425 }
426
427 break;
428
429 case GNUTYPE_VOLHDR:
430 break;
431
432 case GNUTYPE_MULTIVOL:
433 {
434 off_t offset;
435
436 if (current_stat_info.had_trailing_slash)
437 goto really_dir;
438
439 if (!get_stat_data (current_stat_info.file_name, &stat_data))
440 break;
441
442 if (!S_ISREG (stat_data.st_mode))
443 {
444 report_difference (&current_stat_info, _("File type differs"));
445 skip_member ();
446 break;
447 }
448
449 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
450 if (stat_data.st_size != current_stat_info.stat.st_size + offset)
451 {
452 report_difference (&current_stat_info, _("Size differs"));
453 skip_member ();
454 break;
455 }
456
457 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
458
459 if (diff_handle < 0)
460 {
461 open_error (current_stat_info.file_name);
462 report_difference (&current_stat_info, NULL);
463 skip_member ();
464 break;
465 }
466
467 if (lseek (diff_handle, offset, SEEK_SET) < 0)
468 {
469 seek_error_details (current_stat_info.file_name, offset);
470 report_difference (&current_stat_info, NULL);
471 break;
472 }
473
474 if (multi_volume_option)
475 {
476 assign_string (&save_name, current_stat_info.file_name);
477 save_totsize = stat_data.st_size;
478 /* save_sizeleft is set in read_and_process. */
479 }
480
481 read_and_process (current_stat_info.stat.st_size, process_rawdata);
482
483 if (multi_volume_option)
484 assign_string (&save_name, 0);
485
486 status = close (diff_handle);
487 if (status != 0)
488 close_error (current_stat_info.file_name);
489
490 break;
491 }
492 }
493 }
494
495 void
496 verify_volume (void)
497 {
498 if (!diff_buffer)
499 diff_init ();
500
501 /* Verifying an archive is meant to check if the physical media got it
502 correctly, so try to defeat clever in-memory buffering pertaining to
503 this particular media. On Linux, for example, the floppy drive would
504 not even be accessed for the whole verification.
505
506 The code was using fsync only when the ioctl is unavailable, but
507 Marty Leisner says that the ioctl does not work when not preceded by
508 fsync. So, until we know better, or maybe to please Marty, let's do it
509 the unbelievable way :-). */
510
511 #if HAVE_FSYNC
512 fsync (archive);
513 #endif
514 #ifdef FDFLUSH
515 ioctl (archive, FDFLUSH);
516 #endif
517
518 #ifdef MTIOCTOP
519 {
520 struct mtop operation;
521 int status;
522
523 operation.mt_op = MTBSF;
524 operation.mt_count = 1;
525 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
526 {
527 if (errno != EIO
528 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
529 status < 0))
530 {
531 #endif
532 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
533 {
534 /* Lseek failed. Try a different method. */
535 seek_warn (archive_name_array[0]);
536 return;
537 }
538 #ifdef MTIOCTOP
539 }
540 }
541 }
542 #endif
543
544 access_mode = ACCESS_READ;
545 now_verifying = 1;
546
547 flush_read ();
548 while (1)
549 {
550 enum read_header status = read_header (false);
551
552 if (status == HEADER_FAILURE)
553 {
554 int counter = 0;
555
556 do
557 {
558 counter++;
559 status = read_header (false);
560 }
561 while (status == HEADER_FAILURE);
562
563 ERROR ((0, 0,
564 ngettext ("VERIFY FAILURE: %d invalid header detected",
565 "VERIFY FAILURE: %d invalid headers detected",
566 counter), counter));
567 }
568 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
569 break;
570
571 diff_archive ();
572 }
573
574 access_mode = ACCESS_WRITE;
575 now_verifying = 0;
576 }
This page took 0.057592 seconds and 5 git commands to generate.