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