]> Dogcows Code - chaz/tar/blob - src/compare.c
(diff_sparse_files): Temporary placeholder.
[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 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 diff_buffer = valloc (record_size);
58 if (!diff_buffer)
59 xalloc_die ();
60 }
61
62 /* Sigh about something that differs by writing a MESSAGE to stdlis,
63 given MESSAGE is nonzero. Also set the exit status if not already. */
64 void
65 report_difference (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, char *data)
85 {
86 /* Yes, I know. SIZE and DATA are unused in this function. Some
87 compilers may even report it. That's OK, just relax! */
88 return 1;
89 }
90
91 static int
92 process_rawdata (size_t bytes, char *buffer)
93 {
94 ssize_t status = safe_read (diff_handle, diff_buffer, bytes);
95
96 if (status != bytes)
97 {
98 if (status < 0)
99 {
100 read_error (current_stat_info.file_name);
101 report_difference (NULL);
102 }
103 else
104 {
105 report_difference (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 (_("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 (_("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 static void
175 diff_sparse_files (void)
176 {
177 /*FIXME!!*/abort();
178 }
179
180 /* Call either stat or lstat over STAT_DATA, depending on
181 --dereference (-h), for a file which should exist. Diagnose any
182 problem. Return nonzero for success, zero otherwise. */
183 static int
184 get_stat_data (char const *file_name, struct stat *stat_data)
185 {
186 int status = deref_stat (dereference_option, file_name, stat_data);
187
188 if (status != 0)
189 {
190 if (errno == ENOENT)
191 stat_warn (file_name);
192 else
193 stat_error (file_name);
194 report_difference (NULL);
195 return 0;
196 }
197
198 return 1;
199 }
200
201 /* Diff a file against the archive. */
202 void
203 diff_archive (void)
204 {
205 struct stat stat_data;
206 int status;
207 struct utimbuf restore_times;
208
209 set_next_block_after (current_header);
210 decode_header (current_header, &current_stat_info, &current_format, 1);
211
212 /* Print the block from current_header and current_stat_info. */
213
214 if (verbose_option)
215 {
216 if (now_verifying)
217 fprintf (stdlis, _("Verify "));
218 print_header (&current_stat_info, -1);
219 }
220
221 switch (current_header->header.typeflag)
222 {
223 default:
224 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
225 quotearg_colon (current_stat_info.file_name),
226 current_header->header.typeflag));
227 /* Fall through. */
228
229 case AREGTYPE:
230 case REGTYPE:
231 case GNUTYPE_SPARSE:
232 case CONTTYPE:
233
234 /* Appears to be a file. See if it's really a directory. */
235
236 if (current_stat_info.had_trailing_slash)
237 goto really_dir;
238
239 if (!get_stat_data (current_stat_info.file_name, &stat_data))
240 {
241 skip_member ();
242 goto quit;
243 }
244
245 if (!S_ISREG (stat_data.st_mode))
246 {
247 report_difference (_("File type differs"));
248 skip_member ();
249 goto quit;
250 }
251
252 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
253 report_difference (_("Mode differs"));
254
255 sys_compare_uid_gid (&stat_data, &current_stat_info.stat);
256
257 if (stat_data.st_mtime != current_stat_info.stat.st_mtime)
258 report_difference (_("Mod time differs"));
259 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
260 stat_data.st_size != current_stat_info.stat.st_size)
261 {
262 report_difference (_("Size differs"));
263 skip_member ();
264 goto quit;
265 }
266
267 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
268
269 if (diff_handle < 0)
270 {
271 open_error (current_stat_info.file_name);
272 skip_member ();
273 report_difference (NULL);
274 goto quit;
275 }
276
277 restore_times.actime = stat_data.st_atime;
278 restore_times.modtime = stat_data.st_mtime;
279
280 /* Need to treat sparse files completely differently here. */
281
282 if (current_header->header.typeflag == GNUTYPE_SPARSE)
283 diff_sparse_files ();
284 else
285 {
286 if (multi_volume_option)
287 {
288 assign_string (&save_name, current_stat_info.file_name);
289 save_totsize = current_stat_info.stat.st_size;
290 /* save_sizeleft is set in read_and_process. */
291 }
292
293 read_and_process (current_stat_info.stat.st_size, process_rawdata);
294
295 if (multi_volume_option)
296 assign_string (&save_name, 0);
297 }
298
299 status = close (diff_handle);
300 if (status != 0)
301 close_error (current_stat_info.file_name);
302
303 if (atime_preserve_option)
304 utime (current_stat_info.file_name, &restore_times);
305
306 quit:
307 break;
308
309 case LNKTYPE:
310 {
311 struct stat link_data, stat_data;
312
313 if (!get_stat_data (current_stat_info.file_name, &stat_data))
314 break;
315 if (!get_stat_data (current_stat_info.link_name, &link_data))
316 break;
317 sys_compare_links (&stat_data, &link_data);
318 }
319 break;
320
321 #ifdef HAVE_READLINK
322 case SYMTYPE:
323 {
324 size_t len = strlen (current_stat_info.link_name);
325 char *linkbuf = alloca (len + 1);
326
327 status = readlink (current_stat_info.file_name, linkbuf, len + 1);
328
329 if (status < 0)
330 {
331 if (errno == ENOENT)
332 readlink_warn (current_stat_info.file_name);
333 else
334 readlink_error (current_stat_info.file_name);
335 report_difference (NULL);
336 }
337 else if (status != len
338 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
339 report_difference (_("Symlink differs"));
340
341 break;
342 }
343 #endif
344
345 case CHRTYPE:
346 case BLKTYPE:
347 case FIFOTYPE:
348
349 /* FIXME: deal with umask. */
350
351 if (!get_stat_data (current_stat_info.file_name, &stat_data))
352 break;
353
354 if (current_header->header.typeflag == CHRTYPE
355 ? !S_ISCHR (stat_data.st_mode)
356 : current_header->header.typeflag == BLKTYPE
357 ? !S_ISBLK (stat_data.st_mode)
358 : /* current_header->header.typeflag == FIFOTYPE */
359 !S_ISFIFO (stat_data.st_mode))
360 {
361 report_difference (_("File type differs"));
362 break;
363 }
364
365 if ((current_header->header.typeflag == CHRTYPE
366 || current_header->header.typeflag == BLKTYPE)
367 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
368 {
369 report_difference (_("Device number differs"));
370 break;
371 }
372
373 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
374 {
375 report_difference (_("Mode differs"));
376 break;
377 }
378
379 break;
380
381 case GNUTYPE_DUMPDIR:
382 {
383 char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name, 0);
384
385 if (multi_volume_option)
386 {
387 assign_string (&save_name, current_stat_info.file_name);
388 save_totsize = current_stat_info.stat.st_size;
389 /* save_sizeleft is set in read_and_process. */
390 }
391
392 if (dumpdir_buffer)
393 {
394 dumpdir_cursor = dumpdir_buffer;
395 read_and_process (current_stat_info.stat.st_size, process_dumpdir);
396 free (dumpdir_buffer);
397 }
398 else
399 read_and_process (current_stat_info.stat.st_size, process_noop);
400
401 if (multi_volume_option)
402 assign_string (&save_name, 0);
403 /* Fall through. */
404 }
405
406 case DIRTYPE:
407 really_dir:
408 if (!get_stat_data (current_stat_info.file_name, &stat_data))
409 break;
410
411 if (!S_ISDIR (stat_data.st_mode))
412 {
413 report_difference (_("File type differs"));
414 break;
415 }
416
417 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
418 {
419 report_difference (_("Mode differs"));
420 break;
421 }
422
423 break;
424
425 case GNUTYPE_VOLHDR:
426 break;
427
428 case GNUTYPE_MULTIVOL:
429 {
430 off_t offset;
431
432 if (current_stat_info.had_trailing_slash)
433 goto really_dir;
434
435 if (!get_stat_data (current_stat_info.file_name, &stat_data))
436 break;
437
438 if (!S_ISREG (stat_data.st_mode))
439 {
440 report_difference (_("File type differs"));
441 skip_member ();
442 break;
443 }
444
445 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
446 if (stat_data.st_size != current_stat_info.stat.st_size + offset)
447 {
448 report_difference (_("Size differs"));
449 skip_member ();
450 break;
451 }
452
453 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
454
455 if (diff_handle < 0)
456 {
457 open_error (current_stat_info.file_name);
458 report_difference (NULL);
459 skip_member ();
460 break;
461 }
462
463 if (lseek (diff_handle, offset, SEEK_SET) < 0)
464 {
465 seek_error_details (current_stat_info.file_name, offset);
466 report_difference (NULL);
467 break;
468 }
469
470 if (multi_volume_option)
471 {
472 assign_string (&save_name, current_stat_info.file_name);
473 save_totsize = stat_data.st_size;
474 /* save_sizeleft is set in read_and_process. */
475 }
476
477 read_and_process (current_stat_info.stat.st_size, process_rawdata);
478
479 if (multi_volume_option)
480 assign_string (&save_name, 0);
481
482 status = close (diff_handle);
483 if (status != 0)
484 close_error (current_stat_info.file_name);
485
486 break;
487 }
488 }
489 }
490
491 void
492 verify_volume (void)
493 {
494 if (!diff_buffer)
495 diff_init ();
496
497 /* Verifying an archive is meant to check if the physical media got it
498 correctly, so try to defeat clever in-memory buffering pertaining to
499 this particular media. On Linux, for example, the floppy drive would
500 not even be accessed for the whole verification.
501
502 The code was using fsync only when the ioctl is unavailable, but
503 Marty Leisner says that the ioctl does not work when not preceded by
504 fsync. So, until we know better, or maybe to please Marty, let's do it
505 the unbelievable way :-). */
506
507 #if HAVE_FSYNC
508 fsync (archive);
509 #endif
510 #ifdef FDFLUSH
511 ioctl (archive, FDFLUSH);
512 #endif
513
514 #ifdef MTIOCTOP
515 {
516 struct mtop operation;
517 int status;
518
519 operation.mt_op = MTBSF;
520 operation.mt_count = 1;
521 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
522 {
523 if (errno != EIO
524 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
525 status < 0))
526 {
527 #endif
528 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
529 {
530 /* Lseek failed. Try a different method. */
531 seek_warn (archive_name_array[0]);
532 return;
533 }
534 #ifdef MTIOCTOP
535 }
536 }
537 }
538 #endif
539
540 access_mode = ACCESS_READ;
541 now_verifying = 1;
542
543 flush_read ();
544 while (1)
545 {
546 enum read_header status = read_header (false);
547
548 if (status == HEADER_FAILURE)
549 {
550 int counter = 0;
551
552 do
553 {
554 counter++;
555 status = read_header (false);
556 }
557 while (status == HEADER_FAILURE);
558
559 ERROR ((0, 0,
560 ngettext ("VERIFY FAILURE: %d invalid header detected",
561 "VERIFY FAILURE: %d invalid headers detected",
562 counter), counter));
563 }
564 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
565 break;
566
567 diff_archive ();
568 }
569
570 access_mode = ACCESS_WRITE;
571 now_verifying = 0;
572 }
This page took 0.057485 seconds and 5 git commands to generate.