]> Dogcows Code - chaz/tar/blob - src/compare.c
(verify_volume): Call set_next_block_after
[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 void *ptr;
58 diff_buffer = page_aligned_alloc (&ptr, record_size);
59 }
60
61 /* Sigh about something that differs by writing a MESSAGE to stdlis,
62 given MESSAGE is nonzero. Also set the exit status if not already. */
63 void
64 report_difference (struct tar_stat_info *st __attribute__ ((unused)),
65 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 __attribute__ ((unused)),
85 char *data __attribute__ ((unused)))
86 {
87 return 1;
88 }
89
90 static int
91 process_rawdata (size_t bytes, char *buffer)
92 {
93 size_t status = safe_read (diff_handle, diff_buffer, bytes);
94
95 if (status != bytes)
96 {
97 if (status == SAFE_READ_ERROR)
98 {
99 read_error (current_stat_info.file_name);
100 report_difference (&current_stat_info, NULL);
101 }
102 else
103 {
104 report_difference (&current_stat_info,
105 ngettext ("Could only read %lu of %lu byte",
106 "Could only read %lu of %lu bytes",
107 bytes),
108 (unsigned long) status, (unsigned long) bytes);
109 }
110 return 0;
111 }
112
113 if (memcmp (buffer, diff_buffer, bytes))
114 {
115 report_difference (&current_stat_info,
116 _("Contents differ"));
117 return 0;
118 }
119
120 return 1;
121 }
122
123 /* Directory contents, only for GNUTYPE_DUMPDIR. */
124
125 static char *dumpdir_cursor;
126
127 static int
128 process_dumpdir (size_t bytes, char *buffer)
129 {
130 if (memcmp (buffer, dumpdir_cursor, bytes))
131 {
132 report_difference (&current_stat_info, _("Contents differ"));
133 return 0;
134 }
135
136 dumpdir_cursor += bytes;
137 return 1;
138 }
139
140 /* Some other routine wants SIZE bytes in the archive. For each chunk
141 of the archive, call PROCESSOR with the size of the chunk, and the
142 address of the chunk it can work with. The PROCESSOR should return
143 nonzero for success. It it return error once, continue skipping
144 without calling PROCESSOR anymore. */
145 static void
146 read_and_process (off_t size, int (*processor) (size_t, char *))
147 {
148 union block *data_block;
149 size_t data_size;
150
151 if (multi_volume_option)
152 save_sizeleft = size;
153 while (size)
154 {
155 data_block = find_next_block ();
156 if (! data_block)
157 {
158 ERROR ((0, 0, _("Unexpected EOF in archive")));
159 return;
160 }
161
162 data_size = available_space_after (data_block);
163 if (data_size > size)
164 data_size = size;
165 if (!(*processor) (data_size, data_block->buffer))
166 processor = process_noop;
167 set_next_block_after ((union block *)
168 (data_block->buffer + data_size - 1));
169 size -= data_size;
170 if (multi_volume_option)
171 save_sizeleft -= data_size;
172 }
173 }
174
175 /* Call either stat or lstat over STAT_DATA, depending on
176 --dereference (-h), for a file which should exist. Diagnose any
177 problem. Return nonzero for success, zero otherwise. */
178 static int
179 get_stat_data (char const *file_name, struct stat *stat_data)
180 {
181 int status = deref_stat (dereference_option, file_name, stat_data);
182
183 if (status != 0)
184 {
185 if (errno == ENOENT)
186 stat_warn (file_name);
187 else
188 stat_error (file_name);
189 report_difference (&current_stat_info, NULL);
190 return 0;
191 }
192
193 return 1;
194 }
195
196 /* Diff a file against the archive. */
197 void
198 diff_archive (void)
199 {
200 struct stat stat_data;
201 int status;
202 struct utimbuf restore_times;
203
204 set_next_block_after (current_header);
205 decode_header (current_header, &current_stat_info, &current_format, 1);
206
207 /* Print the block from current_header and current_stat_info. */
208
209 if (verbose_option)
210 {
211 if (now_verifying)
212 fprintf (stdlis, _("Verify "));
213 print_header (&current_stat_info, -1);
214 }
215
216 switch (current_header->header.typeflag)
217 {
218 default:
219 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
220 quotearg_colon (current_stat_info.file_name),
221 current_header->header.typeflag));
222 /* Fall through. */
223
224 case AREGTYPE:
225 case REGTYPE:
226 case GNUTYPE_SPARSE:
227 case CONTTYPE:
228
229 /* Appears to be a file. See if it's really a directory. */
230
231 if (current_stat_info.had_trailing_slash)
232 goto really_dir;
233
234 if (!get_stat_data (current_stat_info.file_name, &stat_data))
235 {
236 skip_member ();
237 goto quit;
238 }
239
240 if (!S_ISREG (stat_data.st_mode))
241 {
242 report_difference (&current_stat_info, _("File type differs"));
243 skip_member ();
244 goto quit;
245 }
246
247 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
248 (stat_data.st_mode & MODE_ALL))
249 report_difference (&current_stat_info, _("Mode differs"));
250
251 if (!sys_compare_uid (&stat_data, &current_stat_info.stat))
252 report_difference (&current_stat_info, _("Uid differs"));
253 if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
254 report_difference (&current_stat_info, _("Gid differs"));
255
256 if (stat_data.st_mtime != current_stat_info.stat.st_mtime)
257 report_difference (&current_stat_info, _("Mod time differs"));
258 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
259 stat_data.st_size != current_stat_info.stat.st_size)
260 {
261 report_difference (&current_stat_info, _("Size differs"));
262 skip_member ();
263 goto quit;
264 }
265
266 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
267
268 if (diff_handle < 0)
269 {
270 open_error (current_stat_info.file_name);
271 skip_member ();
272 report_difference (&current_stat_info, NULL);
273 goto quit;
274 }
275
276 restore_times.actime = stat_data.st_atime;
277 restore_times.modtime = stat_data.st_mtime;
278
279 /* Need to treat sparse files completely differently here. */
280
281 if (current_stat_info.is_sparse)
282 sparse_diff_file (diff_handle, &current_stat_info);
283 else
284 {
285 if (multi_volume_option)
286 {
287 assign_string (&save_name, current_stat_info.file_name);
288 save_totsize = current_stat_info.stat.st_size;
289 /* save_sizeleft is set in read_and_process. */
290 }
291
292 read_and_process (current_stat_info.stat.st_size, process_rawdata);
293
294 if (multi_volume_option)
295 assign_string (&save_name, 0);
296 }
297
298 status = close (diff_handle);
299 if (status != 0)
300 close_error (current_stat_info.file_name);
301
302 if (atime_preserve_option)
303 utime (current_stat_info.file_name, &restore_times);
304
305 quit:
306 break;
307
308 case LNKTYPE:
309 {
310 struct stat file_data;
311 struct stat link_data;
312
313 if (!get_stat_data (current_stat_info.file_name, &file_data))
314 break;
315 if (!get_stat_data (current_stat_info.link_name, &link_data))
316 break;
317 if (!sys_compare_links (&file_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 (removed_prefixes_p ())
498 {
499 WARN((0, 0,
500 _("Archive contains file names with leading prefixes removed.")));
501 WARN((0, 0,
502 _("Verification may fail to locate original files.")));
503 }
504
505 if (!diff_buffer)
506 diff_init ();
507
508 /* Verifying an archive is meant to check if the physical media got it
509 correctly, so try to defeat clever in-memory buffering pertaining to
510 this particular media. On Linux, for example, the floppy drive would
511 not even be accessed for the whole verification.
512
513 The code was using fsync only when the ioctl is unavailable, but
514 Marty Leisner says that the ioctl does not work when not preceded by
515 fsync. So, until we know better, or maybe to please Marty, let's do it
516 the unbelievable way :-). */
517
518 #if HAVE_FSYNC
519 fsync (archive);
520 #endif
521 #ifdef FDFLUSH
522 ioctl (archive, FDFLUSH);
523 #endif
524
525 #ifdef MTIOCTOP
526 {
527 struct mtop operation;
528 int status;
529
530 operation.mt_op = MTBSF;
531 operation.mt_count = 1;
532 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
533 {
534 if (errno != EIO
535 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
536 status < 0))
537 {
538 #endif
539 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
540 {
541 /* Lseek failed. Try a different method. */
542 seek_warn (archive_name_array[0]);
543 return;
544 }
545 #ifdef MTIOCTOP
546 }
547 }
548 }
549 #endif
550
551 access_mode = ACCESS_READ;
552 now_verifying = 1;
553
554 flush_read ();
555 while (1)
556 {
557 enum read_header status = read_header (false);
558
559 if (status == HEADER_FAILURE)
560 {
561 int counter = 0;
562
563 do
564 {
565 counter++;
566 set_next_block_after (current_header);
567 status = read_header (false);
568 }
569 while (status == HEADER_FAILURE);
570
571 ERROR ((0, 0,
572 ngettext ("VERIFY FAILURE: %d invalid header detected",
573 "VERIFY FAILURE: %d invalid headers detected",
574 counter), counter));
575 }
576 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
577 break;
578
579 diff_archive ();
580 tar_stat_destroy (&current_stat_info);
581 xheader_destroy (&extended_header);
582 }
583
584 access_mode = ACCESS_WRITE;
585 now_verifying = 0;
586 }
This page took 0.058946 seconds and 5 git commands to generate.