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