]> 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 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
138 static void
139 read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
140 {
141 union block *data_block;
142 size_t data_size;
143 size_t size = st->stat.st_size;
144
145 mv_begin (st);
146 while (size)
147 {
148 data_block = find_next_block ();
149 if (! data_block)
150 {
151 ERROR ((0, 0, _("Unexpected EOF in archive")));
152 return;
153 }
154
155 data_size = available_space_after (data_block);
156 if (data_size > size)
157 data_size = size;
158 if (!(*processor) (data_size, data_block->buffer))
159 processor = process_noop;
160 set_next_block_after ((union block *)
161 (data_block->buffer + data_size - 1));
162 size -= data_size;
163 mv_size_left (size);
164 }
165 mv_end ();
166 }
167
168 /* Call either stat or lstat over STAT_DATA, depending on
169 --dereference (-h), for a file which should exist. Diagnose any
170 problem. Return nonzero for success, zero otherwise. */
171 static int
172 get_stat_data (char const *file_name, struct stat *stat_data)
173 {
174 int status = deref_stat (dereference_option, file_name, stat_data);
175
176 if (status != 0)
177 {
178 if (errno == ENOENT)
179 stat_warn (file_name);
180 else
181 stat_error (file_name);
182 report_difference (&current_stat_info, NULL);
183 return 0;
184 }
185
186 return 1;
187 }
188
189 \f
190 static void
191 diff_dir (void)
192 {
193 struct stat stat_data;
194
195 if (!get_stat_data (current_stat_info.file_name, &stat_data))
196 return;
197
198 if (!S_ISDIR (stat_data.st_mode))
199 report_difference (&current_stat_info, _("File type differs"));
200 else if ((current_stat_info.stat.st_mode & MODE_ALL) !=
201 (stat_data.st_mode & MODE_ALL))
202 report_difference (&current_stat_info, _("Mode differs"));
203 }
204
205 static void
206 diff_file (void)
207 {
208 struct stat stat_data;
209
210 if (!get_stat_data (current_stat_info.file_name, &stat_data))
211 skip_member ();
212 else if (!S_ISREG (stat_data.st_mode))
213 {
214 report_difference (&current_stat_info, _("File type differs"));
215 skip_member ();
216 }
217 else
218 {
219 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
220 (stat_data.st_mode & MODE_ALL))
221 report_difference (&current_stat_info, _("Mode differs"));
222
223 if (!sys_compare_uid (&stat_data, &current_stat_info.stat))
224 report_difference (&current_stat_info, _("Uid differs"));
225 if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
226 report_difference (&current_stat_info, _("Gid differs"));
227
228 if (tar_timespec_cmp (get_stat_mtime (&stat_data),
229 current_stat_info.mtime))
230 report_difference (&current_stat_info, _("Mod time differs"));
231 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
232 stat_data.st_size != current_stat_info.stat.st_size)
233 {
234 report_difference (&current_stat_info, _("Size differs"));
235 skip_member ();
236 }
237 else
238 {
239 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
240
241 if (diff_handle < 0)
242 {
243 open_error (current_stat_info.file_name);
244 skip_member ();
245 report_difference (&current_stat_info, NULL);
246 }
247 else
248 {
249 int status;
250
251 if (current_stat_info.is_sparse)
252 sparse_diff_file (diff_handle, &current_stat_info);
253 else
254 read_and_process (&current_stat_info, process_rawdata);
255
256 status = close (diff_handle);
257 if (status != 0)
258 close_error (current_stat_info.file_name);
259
260 if (atime_preserve_option)
261 {
262 struct timespec ts[2];
263 ts[0] = get_stat_atime (&stat_data);
264 ts[1] = get_stat_mtime (&stat_data);
265 if (utimens (current_stat_info.file_name, ts) != 0)
266 utime_error (current_stat_info.file_name);
267 }
268 }
269 }
270 }
271 }
272
273 static void
274 diff_link (void)
275 {
276 struct stat file_data;
277 struct stat link_data;
278
279 if (get_stat_data (current_stat_info.file_name, &file_data)
280 && get_stat_data (current_stat_info.link_name, &link_data)
281 && !sys_compare_links (&file_data, &link_data))
282 report_difference (&current_stat_info,
283 _("Not linked to %s"),
284 quote (current_stat_info.link_name));
285 }
286
287 #ifdef HAVE_READLINK
288 static void
289 diff_symlink (void)
290 {
291 size_t len = strlen (current_stat_info.link_name);
292 char *linkbuf = alloca (len + 1);
293
294 int status = readlink (current_stat_info.file_name, linkbuf, len + 1);
295
296 if (status < 0)
297 {
298 if (errno == ENOENT)
299 readlink_warn (current_stat_info.file_name);
300 else
301 readlink_error (current_stat_info.file_name);
302 report_difference (&current_stat_info, NULL);
303 }
304 else if (status != len
305 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
306 report_difference (&current_stat_info, _("Symlink differs"));
307 }
308 #endif
309
310 static void
311 diff_special (void)
312 {
313 struct stat stat_data;
314
315 /* FIXME: deal with umask. */
316
317 if (!get_stat_data (current_stat_info.file_name, &stat_data))
318 return;
319
320 if (current_header->header.typeflag == CHRTYPE
321 ? !S_ISCHR (stat_data.st_mode)
322 : current_header->header.typeflag == BLKTYPE
323 ? !S_ISBLK (stat_data.st_mode)
324 : /* current_header->header.typeflag == FIFOTYPE */
325 !S_ISFIFO (stat_data.st_mode))
326 {
327 report_difference (&current_stat_info, _("File type differs"));
328 return;
329 }
330
331 if ((current_header->header.typeflag == CHRTYPE
332 || current_header->header.typeflag == BLKTYPE)
333 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
334 {
335 report_difference (&current_stat_info, _("Device number differs"));
336 return;
337 }
338
339 if ((current_stat_info.stat.st_mode & MODE_ALL) !=
340 (stat_data.st_mode & MODE_ALL))
341 report_difference (&current_stat_info, _("Mode differs"));
342 }
343
344 static void
345 diff_dumpdir (void)
346 {
347 char *dumpdir_buffer;
348 dev_t dev = 0;
349 struct stat stat;
350
351 if (deref_stat (true, current_stat_info.file_name, &stat))
352 {
353 if (errno == ENOENT)
354 stat_warn (current_stat_info.file_name);
355 else
356 stat_error (current_stat_info.file_name);
357 }
358 else
359 dev = stat.st_dev;
360
361 dumpdir_buffer = get_directory_contents (current_stat_info.file_name, dev);
362
363 if (dumpdir_buffer)
364 {
365 dumpdir_cursor = dumpdir_buffer;
366 read_and_process (&current_stat_info, process_dumpdir);
367 free (dumpdir_buffer);
368 }
369 else
370 read_and_process (&current_stat_info, process_noop);
371 }
372
373 static void
374 diff_multivol (void)
375 {
376 struct stat stat_data;
377 int fd, status;
378 off_t offset;
379
380 if (current_stat_info.had_trailing_slash)
381 {
382 diff_dir ();
383 return;
384 }
385
386 if (!get_stat_data (current_stat_info.file_name, &stat_data))
387 return;
388
389 if (!S_ISREG (stat_data.st_mode))
390 {
391 report_difference (&current_stat_info, _("File type differs"));
392 skip_member ();
393 return;
394 }
395
396 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
397 if (stat_data.st_size != current_stat_info.stat.st_size + offset)
398 {
399 report_difference (&current_stat_info, _("Size differs"));
400 skip_member ();
401 return;
402 }
403
404 fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
405
406 if (fd < 0)
407 {
408 open_error (current_stat_info.file_name);
409 report_difference (&current_stat_info, NULL);
410 skip_member ();
411 return;
412 }
413
414 if (lseek (fd, offset, SEEK_SET) < 0)
415 {
416 seek_error_details (current_stat_info.file_name, offset);
417 report_difference (&current_stat_info, NULL);
418 return;
419 }
420
421 read_and_process (&current_stat_info, process_rawdata);
422
423 status = close (fd);
424 if (status != 0)
425 close_error (current_stat_info.file_name);
426 }
427
428 /* Diff a file against the archive. */
429 void
430 diff_archive (void)
431 {
432
433 set_next_block_after (current_header);
434 decode_header (current_header, &current_stat_info, &current_format, 1);
435
436 /* Print the block from current_header and current_stat_info. */
437
438 if (verbose_option)
439 {
440 if (now_verifying)
441 fprintf (stdlis, _("Verify "));
442 print_header (&current_stat_info, -1);
443 }
444
445 switch (current_header->header.typeflag)
446 {
447 default:
448 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
449 quotearg_colon (current_stat_info.file_name),
450 current_header->header.typeflag));
451 /* Fall through. */
452
453 case AREGTYPE:
454 case REGTYPE:
455 case GNUTYPE_SPARSE:
456 case CONTTYPE:
457
458 /* Appears to be a file. See if it's really a directory. */
459
460 if (current_stat_info.had_trailing_slash)
461 diff_dir ();
462 else
463 diff_file ();
464 break;
465
466 case LNKTYPE:
467 diff_link ();
468 break;
469
470 #ifdef HAVE_READLINK
471 case SYMTYPE:
472 diff_symlink ();
473 break;
474 #endif
475
476 case CHRTYPE:
477 case BLKTYPE:
478 case FIFOTYPE:
479 diff_special ();
480 break;
481
482 case GNUTYPE_DUMPDIR:
483 diff_dumpdir ();
484 /* Fall through. */
485
486 case DIRTYPE:
487 diff_dir ();
488 break;
489
490 case GNUTYPE_VOLHDR:
491 break;
492
493 case GNUTYPE_MULTIVOL:
494 diff_multivol ();
495 }
496 }
497
498 void
499 verify_volume (void)
500 {
501 if (removed_prefixes_p ())
502 {
503 WARN((0, 0,
504 _("Archive contains file names with leading prefixes removed.")));
505 WARN((0, 0,
506 _("Verification may fail to locate original files.")));
507 }
508
509 if (!diff_buffer)
510 diff_init ();
511
512 /* Verifying an archive is meant to check if the physical media got it
513 correctly, so try to defeat clever in-memory buffering pertaining to
514 this particular media. On Linux, for example, the floppy drive would
515 not even be accessed for the whole verification.
516
517 The code was using fsync only when the ioctl is unavailable, but
518 Marty Leisner says that the ioctl does not work when not preceded by
519 fsync. So, until we know better, or maybe to please Marty, let's do it
520 the unbelievable way :-). */
521
522 #if HAVE_FSYNC
523 fsync (archive);
524 #endif
525 #ifdef FDFLUSH
526 ioctl (archive, FDFLUSH);
527 #endif
528
529 #ifdef MTIOCTOP
530 {
531 struct mtop operation;
532 int status;
533
534 operation.mt_op = MTBSF;
535 operation.mt_count = 1;
536 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
537 {
538 if (errno != EIO
539 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
540 status < 0))
541 {
542 #endif
543 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
544 {
545 /* Lseek failed. Try a different method. */
546 seek_warn (archive_name_array[0]);
547 return;
548 }
549 #ifdef MTIOCTOP
550 }
551 }
552 }
553 #endif
554
555 access_mode = ACCESS_READ;
556 now_verifying = 1;
557
558 flush_read ();
559 while (1)
560 {
561 enum read_header status = read_header (false);
562
563 if (status == HEADER_FAILURE)
564 {
565 int counter = 0;
566
567 do
568 {
569 counter++;
570 set_next_block_after (current_header);
571 status = read_header (false);
572 }
573 while (status == HEADER_FAILURE);
574
575 ERROR ((0, 0,
576 ngettext ("VERIFY FAILURE: %d invalid header detected",
577 "VERIFY FAILURE: %d invalid headers detected",
578 counter), counter));
579 }
580 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
581 break;
582
583 diff_archive ();
584 tar_stat_destroy (&current_stat_info);
585 xheader_destroy (&extended_header);
586 }
587
588 access_mode = ACCESS_WRITE;
589 now_verifying = 0;
590 }
This page took 0.059164 seconds and 4 git commands to generate.