]>
Dogcows Code - chaz/tar/blob - src/compare.c
1 /* Diff files from a tar archive.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003 Free Software Foundation, Inc.
6 Written by John Gilmore, on 1987-04-30.
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
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.
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. */
35 # include <linux/fd.h>
44 /* Nonzero if we are verifying at the moment. */
47 /* File descriptor for the file we are diffing. */
48 static int diff_handle
;
50 /* Area for reading file contents into. */
51 static char *diff_buffer
;
53 /* Initialize for a diff operation. */
57 diff_buffer
= valloc (record_size
);
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. */
65 report_difference (const char *fmt
, ...)
71 fprintf (stdlis
, "%s: ", quotearg_colon (current_stat_info
.file_name
));
73 vfprintf (stdlis
, fmt
, ap
);
75 fprintf (stdlis
, "\n");
78 if (exit_status
== TAREXIT_SUCCESS
)
79 exit_status
= TAREXIT_DIFFERS
;
82 /* Take a buffer returned by read_and_process and do nothing with it. */
84 process_noop (size_t size
, char *data
)
86 /* Yes, I know. SIZE and DATA are unused in this function. Some
87 compilers may even report it. That's OK, just relax! */
92 process_rawdata (size_t bytes
, char *buffer
)
94 ssize_t status
= safe_read (diff_handle
, diff_buffer
, bytes
);
100 read_error (current_stat_info
.file_name
);
101 report_difference (NULL
);
105 report_difference (ngettext ("Could only read %lu of %lu byte",
106 "Could only read %lu of %lu bytes",
108 (unsigned long) status
, (unsigned long) bytes
);
113 if (memcmp (buffer
, diff_buffer
, bytes
))
115 report_difference (_("Contents differ"));
122 /* Directory contents, only for GNUTYPE_DUMPDIR. */
124 static char *dumpdir_cursor
;
127 process_dumpdir (size_t bytes
, char *buffer
)
129 if (memcmp (buffer
, dumpdir_cursor
, bytes
))
131 report_difference (_("Contents differ"));
135 dumpdir_cursor
+= bytes
;
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. */
145 read_and_process (off_t size
, int (*processor
) (size_t, char *))
147 union block
*data_block
;
150 if (multi_volume_option
)
151 save_sizeleft
= size
;
154 data_block
= find_next_block ();
157 ERROR ((0, 0, _("Unexpected EOF in archive")));
161 data_size
= available_space_after (data_block
);
162 if (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));
169 if (multi_volume_option
)
170 save_sizeleft
-= data_size
;
174 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
175 bit of a different story than a normal file. First, we must know what
176 areas of the file to skip through, i.e., we need to construct a
177 sparsearray, which will hold all the information we need. We must
178 compare small amounts of data at a time as we find it. */
180 /* FIXME: This does not look very solid to me, at first glance. Zero areas
181 are not checked, spurious sparse entries seemingly goes undetected, and
182 I'm not sure overall identical sparsity is verified. */
185 diff_sparse_files (void)
187 off_t remaining_size
= current_stat_info
.stat
.st_size
;
188 char *buffer
= xmalloc (BLOCKSIZE
* sizeof (char));
189 size_t buffer_size
= BLOCKSIZE
;
190 union block
*data_block
= 0;
194 if (! fill_in_sparse_array ())
197 while (remaining_size
> 0)
204 off_t amount_read
= 0;
207 data_block
= find_next_block ();
209 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
210 chunk_size
= sparsearray
[counter
].numbytes
;
214 offset
= sparsearray
[counter
].offset
;
215 if (lseek (diff_handle
, offset
, SEEK_SET
) < 0)
217 seek_error_details (current_stat_info
.file_name
, offset
);
218 report_difference (NULL
);
221 /* Take care to not run out of room in our buffer. */
223 while (buffer_size
< chunk_size
)
225 if (buffer_size
* 2 < buffer_size
)
228 buffer
= xrealloc (buffer
, buffer_size
* sizeof (char));
231 while (chunk_size
> BLOCKSIZE
)
233 if (status
= safe_read (diff_handle
, buffer
, BLOCKSIZE
),
238 read_error (current_stat_info
.file_name
);
239 report_difference (NULL
);
243 report_difference (ngettext ("Could only read %lu of %lu byte",
244 "Could only read %lu of %lu bytes",
246 (unsigned long) status
,
247 (unsigned long) chunk_size
);
252 if (memcmp (buffer
, data_block
->buffer
, BLOCKSIZE
))
258 chunk_size
-= status
;
259 remaining_size
-= status
;
260 set_next_block_after (data_block
);
261 data_block
= find_next_block ();
263 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
265 if (status
= safe_read (diff_handle
, buffer
, chunk_size
),
266 status
!= chunk_size
)
270 read_error (current_stat_info
.file_name
);
271 report_difference (NULL
);
275 report_difference (ngettext ("Could only read %lu of %lu byte",
276 "Could only read %lu of %lu bytes",
278 (unsigned long) status
,
279 (unsigned long) chunk_size
);
284 if (memcmp (buffer
, data_block
->buffer
, chunk_size
))
290 amount_read
+= chunk_size
;
291 if (amount_read
>= BLOCKSIZE
)
294 set_next_block_after (data_block
);
295 data_block
= find_next_block ();
297 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
300 set_next_block_after (data_block
);
302 remaining_size
-= chunk_size
;
306 /* If the number of bytes read isn't the number of bytes supposedly in
307 the file, they're different. */
309 if (amount_read
!= current_stat_info
.stat
.st_size
)
313 set_next_block_after (data_block
);
317 report_difference (_("Contents differ"));
320 /* Call either stat or lstat over STAT_DATA, depending on
321 --dereference (-h), for a file which should exist. Diagnose any
322 problem. Return nonzero for success, zero otherwise. */
324 get_stat_data (char const *file_name
, struct stat
*stat_data
)
326 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
331 stat_warn (file_name
);
333 stat_error (file_name
);
334 report_difference (NULL
);
341 /* Diff a file against the archive. */
345 struct stat stat_data
;
347 struct utimbuf restore_times
;
349 set_next_block_after (current_header
);
350 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
352 /* Print the block from current_header and current_stat_info. */
357 fprintf (stdlis
, _("Verify "));
361 switch (current_header
->header
.typeflag
)
364 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
365 quotearg_colon (current_stat_info
.file_name
),
366 current_header
->header
.typeflag
));
374 /* Appears to be a file. See if it's really a directory. */
376 if (current_stat_info
.had_trailing_slash
)
379 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
385 if (!S_ISREG (stat_data
.st_mode
))
387 report_difference (_("File type differs"));
392 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
393 report_difference (_("Mode differs"));
395 sys_compare_uid_gid (&stat_data
, ¤t_stat_info
.stat
);
397 if (stat_data
.st_mtime
!= current_stat_info
.stat
.st_mtime
)
398 report_difference (_("Mod time differs"));
399 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
400 stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
402 report_difference (_("Size differs"));
407 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
411 open_error (current_stat_info
.file_name
);
413 report_difference (NULL
);
417 restore_times
.actime
= stat_data
.st_atime
;
418 restore_times
.modtime
= stat_data
.st_mtime
;
420 /* Need to treat sparse files completely differently here. */
422 if (current_header
->header
.typeflag
== GNUTYPE_SPARSE
)
423 diff_sparse_files ();
426 if (multi_volume_option
)
428 assign_string (&save_name
, current_stat_info
.file_name
);
429 save_totsize
= current_stat_info
.stat
.st_size
;
430 /* save_sizeleft is set in read_and_process. */
433 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
435 if (multi_volume_option
)
436 assign_string (&save_name
, 0);
439 status
= close (diff_handle
);
441 close_error (current_stat_info
.file_name
);
443 if (atime_preserve_option
)
444 utime (current_stat_info
.file_name
, &restore_times
);
451 struct stat link_data
, stat_data
;
453 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
455 if (!get_stat_data (current_stat_info
.link_name
, &link_data
))
457 sys_compare_links (&stat_data
, &link_data
);
464 size_t len
= strlen (current_stat_info
.link_name
);
465 char *linkbuf
= alloca (len
+ 1);
467 status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
472 readlink_warn (current_stat_info
.file_name
);
474 readlink_error (current_stat_info
.file_name
);
475 report_difference (NULL
);
477 else if (status
!= len
478 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
479 report_difference (_("Symlink differs"));
489 /* FIXME: deal with umask. */
491 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
494 if (current_header
->header
.typeflag
== CHRTYPE
495 ? !S_ISCHR (stat_data
.st_mode
)
496 : current_header
->header
.typeflag
== BLKTYPE
497 ? !S_ISBLK (stat_data
.st_mode
)
498 : /* current_header->header.typeflag == FIFOTYPE */
499 !S_ISFIFO (stat_data
.st_mode
))
501 report_difference (_("File type differs"));
505 if ((current_header
->header
.typeflag
== CHRTYPE
506 || current_header
->header
.typeflag
== BLKTYPE
)
507 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
509 report_difference (_("Device number differs"));
513 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
515 report_difference (_("Mode differs"));
521 case GNUTYPE_DUMPDIR
:
523 char *dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
, 0);
525 if (multi_volume_option
)
527 assign_string (&save_name
, current_stat_info
.file_name
);
528 save_totsize
= current_stat_info
.stat
.st_size
;
529 /* save_sizeleft is set in read_and_process. */
534 dumpdir_cursor
= dumpdir_buffer
;
535 read_and_process (current_stat_info
.stat
.st_size
, process_dumpdir
);
536 free (dumpdir_buffer
);
539 read_and_process (current_stat_info
.stat
.st_size
, process_noop
);
541 if (multi_volume_option
)
542 assign_string (&save_name
, 0);
548 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
551 if (!S_ISDIR (stat_data
.st_mode
))
553 report_difference (_("File type differs"));
557 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
559 report_difference (_("Mode differs"));
568 case GNUTYPE_MULTIVOL
:
572 if (current_stat_info
.had_trailing_slash
)
575 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
578 if (!S_ISREG (stat_data
.st_mode
))
580 report_difference (_("File type differs"));
585 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
586 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
588 report_difference (_("Size differs"));
593 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
597 open_error (current_stat_info
.file_name
);
598 report_difference (NULL
);
603 if (lseek (diff_handle
, offset
, SEEK_SET
) < 0)
605 seek_error_details (current_stat_info
.file_name
, offset
);
606 report_difference (NULL
);
610 if (multi_volume_option
)
612 assign_string (&save_name
, current_stat_info
.file_name
);
613 save_totsize
= stat_data
.st_size
;
614 /* save_sizeleft is set in read_and_process. */
617 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
619 if (multi_volume_option
)
620 assign_string (&save_name
, 0);
622 status
= close (diff_handle
);
624 close_error (current_stat_info
.file_name
);
637 /* Verifying an archive is meant to check if the physical media got it
638 correctly, so try to defeat clever in-memory buffering pertaining to
639 this particular media. On Linux, for example, the floppy drive would
640 not even be accessed for the whole verification.
642 The code was using fsync only when the ioctl is unavailable, but
643 Marty Leisner says that the ioctl does not work when not preceded by
644 fsync. So, until we know better, or maybe to please Marty, let's do it
645 the unbelievable way :-). */
651 ioctl (archive
, FDFLUSH
);
656 struct mtop operation
;
659 operation
.mt_op
= MTBSF
;
660 operation
.mt_count
= 1;
661 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
664 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
668 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
670 /* Lseek failed. Try a different method. */
671 seek_warn (archive_name_array
[0]);
680 access_mode
= ACCESS_READ
;
686 enum read_header status
= read_header (false);
688 if (status
== HEADER_FAILURE
)
695 status
= read_header (false);
697 while (status
== HEADER_FAILURE
);
700 ngettext ("VERIFY FAILURE: %d invalid header detected",
701 "VERIFY FAILURE: %d invalid headers detected",
704 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
710 access_mode
= ACCESS_WRITE
;
This page took 0.063481 seconds and 4 git commands to generate.