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