]>
Dogcows Code - chaz/tar/blob - src/compare.c
4ae3392878decfaa9728c292f3a0549656dfdc17
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>
43 /* Spare space for messages, hopefully safe even after gettext. */
44 #define MESSAGE_BUFFER_SIZE 100
46 /* Nonzero if we are verifying at the moment. */
49 /* File descriptor for the file we are diffing. */
50 static int diff_handle
;
52 /* Area for reading file contents into. */
53 static char *diff_buffer
;
55 /* Initialize for a diff operation. */
59 diff_buffer
= valloc (record_size
);
64 /* Sigh about something that differs by writing a MESSAGE to stdlis,
65 given MESSAGE is nonzero. Also set the exit status if not already. */
67 report_difference (const char *message
)
70 fprintf (stdlis
, "%s: %s\n", quotearg_colon (current_stat_info
.file_name
), message
);
72 if (exit_status
== TAREXIT_SUCCESS
)
73 exit_status
= TAREXIT_DIFFERS
;
76 /* Take a buffer returned by read_and_process and do nothing with it. */
78 process_noop (size_t size
, char *data
)
80 /* Yes, I know. SIZE and DATA are unused in this function. Some
81 compilers may even report it. That's OK, just relax! */
86 process_rawdata (size_t bytes
, char *buffer
)
88 ssize_t status
= safe_read (diff_handle
, diff_buffer
, bytes
);
89 char message
[MESSAGE_BUFFER_SIZE
];
95 read_error (current_stat_info
.file_name
);
96 report_difference (0);
101 ngettext ("Could only read %lu of %lu byte",
102 "Could only read %lu of %lu bytes",
104 (unsigned long) status
, (unsigned long) bytes
);
105 report_difference (message
);
110 if (memcmp (buffer
, diff_buffer
, bytes
))
112 report_difference (_("Contents differ"));
119 /* Directory contents, only for GNUTYPE_DUMPDIR. */
121 static char *dumpdir_cursor
;
124 process_dumpdir (size_t bytes
, char *buffer
)
126 if (memcmp (buffer
, dumpdir_cursor
, bytes
))
128 report_difference (_("Contents differ"));
132 dumpdir_cursor
+= bytes
;
136 /* Some other routine wants SIZE bytes in the archive. For each chunk
137 of the archive, call PROCESSOR with the size of the chunk, and the
138 address of the chunk it can work with. The PROCESSOR should return
139 nonzero for success. It it return error once, continue skipping
140 without calling PROCESSOR anymore. */
142 read_and_process (off_t size
, int (*processor
) (size_t, char *))
144 union block
*data_block
;
147 if (multi_volume_option
)
148 save_sizeleft
= size
;
151 data_block
= find_next_block ();
154 ERROR ((0, 0, _("Unexpected EOF in archive")));
158 data_size
= available_space_after (data_block
);
159 if (data_size
> size
)
161 if (!(*processor
) (data_size
, data_block
->buffer
))
162 processor
= process_noop
;
163 set_next_block_after ((union block
*)
164 (data_block
->buffer
+ data_size
- 1));
166 if (multi_volume_option
)
167 save_sizeleft
-= data_size
;
171 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
172 bit of a different story than a normal file. First, we must know what
173 areas of the file to skip through, i.e., we need to construct a
174 sparsearray, which will hold all the information we need. We must
175 compare small amounts of data at a time as we find it. */
177 /* FIXME: This does not look very solid to me, at first glance. Zero areas
178 are not checked, spurious sparse entries seemingly goes undetected, and
179 I'm not sure overall identical sparsity is verified. */
182 diff_sparse_files (void)
184 off_t remaining_size
= current_stat_info
.stat
.st_size
;
185 char *buffer
= xmalloc (BLOCKSIZE
* sizeof (char));
186 size_t buffer_size
= BLOCKSIZE
;
187 union block
*data_block
= 0;
191 if (! fill_in_sparse_array ())
194 while (remaining_size
> 0)
201 off_t amount_read
= 0;
204 data_block
= find_next_block ();
206 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
207 chunk_size
= sparsearray
[counter
].numbytes
;
211 offset
= sparsearray
[counter
].offset
;
212 if (lseek (diff_handle
, offset
, SEEK_SET
) < 0)
214 seek_error_details (current_stat_info
.file_name
, offset
);
215 report_difference (0);
218 /* Take care to not run out of room in our buffer. */
220 while (buffer_size
< chunk_size
)
222 if (buffer_size
* 2 < buffer_size
)
225 buffer
= xrealloc (buffer
, buffer_size
* sizeof (char));
228 while (chunk_size
> BLOCKSIZE
)
230 if (status
= safe_read (diff_handle
, buffer
, BLOCKSIZE
),
235 read_error (current_stat_info
.file_name
);
236 report_difference (0);
240 char message
[MESSAGE_BUFFER_SIZE
];
243 ngettext ("Could only read %lu of %lu byte",
244 "Could only read %lu of %lu bytes",
246 (unsigned long) status
, (unsigned long) chunk_size
);
247 report_difference (message
);
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 (0);
275 char message
[MESSAGE_BUFFER_SIZE
];
278 ngettext ("Could only read %lu of %lu byte",
279 "Could only read %lu of %lu bytes",
281 (unsigned long) status
, (unsigned long) chunk_size
);
282 report_difference (message
);
287 if (memcmp (buffer
, data_block
->buffer
, chunk_size
))
293 amount_read
+= chunk_size
;
294 if (amount_read
>= BLOCKSIZE
)
297 set_next_block_after (data_block
);
298 data_block
= find_next_block ();
300 FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
303 set_next_block_after (data_block
);
305 remaining_size
-= chunk_size
;
309 /* If the number of bytes read isn't the number of bytes supposedly in
310 the file, they're different. */
312 if (amount_read
!= current_stat_info
.stat
.st_size
)
316 set_next_block_after (data_block
);
320 report_difference (_("Contents differ"));
323 /* Call either stat or lstat over STAT_DATA, depending on
324 --dereference (-h), for a file which should exist. Diagnose any
325 problem. Return nonzero for success, zero otherwise. */
327 get_stat_data (char const *file_name
, struct stat
*stat_data
)
329 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
334 stat_warn (file_name
);
336 stat_error (file_name
);
337 report_difference (0);
344 /* Diff a file against the archive. */
348 struct stat stat_data
;
350 struct utimbuf restore_times
;
352 set_next_block_after (current_header
);
353 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
355 /* Print the block from current_header and current_stat_info. */
360 fprintf (stdlis
, _("Verify "));
364 switch (current_header
->header
.typeflag
)
367 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
368 quotearg_colon (current_stat_info
.file_name
),
369 current_header
->header
.typeflag
));
377 /* Appears to be a file. See if it's really a directory. */
379 if (current_stat_info
.had_trailing_slash
)
382 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
388 if (!S_ISREG (stat_data
.st_mode
))
390 report_difference (_("File type differs"));
395 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
396 report_difference (_("Mode differs"));
399 /* stat() in djgpp's C library gives a constant number of 42 as the
400 uid and gid of a file. So, comparing an FTP'ed archive just after
401 unpack would fail on MSDOS. */
402 if (stat_data
.st_uid
!= current_stat_info
.stat
.st_uid
)
403 report_difference (_("Uid differs"));
404 if (stat_data
.st_gid
!= current_stat_info
.stat
.st_gid
)
405 report_difference (_("Gid differs"));
408 if (stat_data
.st_mtime
!= current_stat_info
.stat
.st_mtime
)
409 report_difference (_("Mod time differs"));
410 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
411 stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
413 report_difference (_("Size differs"));
418 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
422 open_error (current_stat_info
.file_name
);
424 report_difference (0);
428 restore_times
.actime
= stat_data
.st_atime
;
429 restore_times
.modtime
= stat_data
.st_mtime
;
431 /* Need to treat sparse files completely differently here. */
433 if (current_header
->header
.typeflag
== GNUTYPE_SPARSE
)
434 diff_sparse_files ();
437 if (multi_volume_option
)
439 assign_string (&save_name
, current_stat_info
.file_name
);
440 save_totsize
= current_stat_info
.stat
.st_size
;
441 /* save_sizeleft is set in read_and_process. */
444 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
446 if (multi_volume_option
)
447 assign_string (&save_name
, 0);
450 status
= close (diff_handle
);
452 close_error (current_stat_info
.file_name
);
454 if (atime_preserve_option
)
455 utime (current_stat_info
.file_name
, &restore_times
);
463 struct stat link_data
;
465 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
467 if (!get_stat_data (current_stat_info
.link_name
, &link_data
))
470 if (stat_data
.st_dev
!= link_data
.st_dev
471 || stat_data
.st_ino
!= link_data
.st_ino
)
474 xmalloc (MESSAGE_BUFFER_SIZE
+ 4 * strlen (current_stat_info
.link_name
));
476 sprintf (message
, _("Not linked to %s"),
477 quote (current_stat_info
.link_name
));
478 report_difference (message
);
485 #endif /* not MSDOS */
490 size_t len
= strlen (current_stat_info
.link_name
);
491 char *linkbuf
= alloca (len
+ 1);
493 status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
498 readlink_warn (current_stat_info
.file_name
);
500 readlink_error (current_stat_info
.file_name
);
501 report_difference (0);
503 else if (status
!= len
504 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
505 report_difference (_("Symlink differs"));
515 /* FIXME: deal with umask. */
517 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
520 if (current_header
->header
.typeflag
== CHRTYPE
521 ? !S_ISCHR (stat_data
.st_mode
)
522 : current_header
->header
.typeflag
== BLKTYPE
523 ? !S_ISBLK (stat_data
.st_mode
)
524 : /* current_header->header.typeflag == FIFOTYPE */
525 !S_ISFIFO (stat_data
.st_mode
))
527 report_difference (_("File type differs"));
531 if ((current_header
->header
.typeflag
== CHRTYPE
532 || current_header
->header
.typeflag
== BLKTYPE
)
533 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
535 report_difference (_("Device number differs"));
539 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
541 report_difference (_("Mode differs"));
547 case GNUTYPE_DUMPDIR
:
549 char *dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
, 0);
551 if (multi_volume_option
)
553 assign_string (&save_name
, current_stat_info
.file_name
);
554 save_totsize
= current_stat_info
.stat
.st_size
;
555 /* save_sizeleft is set in read_and_process. */
560 dumpdir_cursor
= dumpdir_buffer
;
561 read_and_process (current_stat_info
.stat
.st_size
, process_dumpdir
);
562 free (dumpdir_buffer
);
565 read_and_process (current_stat_info
.stat
.st_size
, process_noop
);
567 if (multi_volume_option
)
568 assign_string (&save_name
, 0);
574 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
577 if (!S_ISDIR (stat_data
.st_mode
))
579 report_difference (_("File type differs"));
583 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) != (stat_data
.st_mode
& MODE_ALL
))
585 report_difference (_("Mode differs"));
594 case GNUTYPE_MULTIVOL
:
598 if (current_stat_info
.had_trailing_slash
)
601 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
604 if (!S_ISREG (stat_data
.st_mode
))
606 report_difference (_("File type differs"));
611 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
612 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
614 report_difference (_("Size differs"));
619 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
623 open_error (current_stat_info
.file_name
);
624 report_difference (0);
629 if (lseek (diff_handle
, offset
, SEEK_SET
) < 0)
631 seek_error_details (current_stat_info
.file_name
, offset
);
632 report_difference (0);
636 if (multi_volume_option
)
638 assign_string (&save_name
, current_stat_info
.file_name
);
639 save_totsize
= stat_data
.st_size
;
640 /* save_sizeleft is set in read_and_process. */
643 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
645 if (multi_volume_option
)
646 assign_string (&save_name
, 0);
648 status
= close (diff_handle
);
650 close_error (current_stat_info
.file_name
);
663 /* Verifying an archive is meant to check if the physical media got it
664 correctly, so try to defeat clever in-memory buffering pertaining to
665 this particular media. On Linux, for example, the floppy drive would
666 not even be accessed for the whole verification.
668 The code was using fsync only when the ioctl is unavailable, but
669 Marty Leisner says that the ioctl does not work when not preceded by
670 fsync. So, until we know better, or maybe to please Marty, let's do it
671 the unbelievable way :-). */
677 ioctl (archive
, FDFLUSH
);
682 struct mtop operation
;
685 operation
.mt_op
= MTBSF
;
686 operation
.mt_count
= 1;
687 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
690 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
694 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
696 /* Lseek failed. Try a different method. */
697 seek_warn (archive_name_array
[0]);
706 access_mode
= ACCESS_READ
;
712 enum read_header status
= read_header (false);
714 if (status
== HEADER_FAILURE
)
721 status
= read_header (false);
723 while (status
== HEADER_FAILURE
);
726 ngettext ("VERIFY FAILURE: %d invalid header detected",
727 "VERIFY FAILURE: %d invalid headers detected",
730 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
736 access_mode
= ACCESS_WRITE
;
This page took 0.068255 seconds and 4 git commands to generate.