1 /* Diff files from a tar archive.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003, 2004 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 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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. */
58 diff_buffer
= page_aligned_alloc (&ptr
, record_size
);
59 if (listed_incremental_option
)
60 read_directory_file ();
63 /* Sigh about something that differs by writing a MESSAGE to stdlis,
64 given MESSAGE is nonzero. Also set the exit status if not already. */
66 report_difference (struct tar_stat_info
*st
__attribute__ ((unused
)),
73 fprintf (stdlis
, "%s: ", quotearg_colon (current_stat_info
.file_name
));
75 vfprintf (stdlis
, fmt
, ap
);
77 fprintf (stdlis
, "\n");
80 if (exit_status
== TAREXIT_SUCCESS
)
81 exit_status
= TAREXIT_DIFFERS
;
84 /* Take a buffer returned by read_and_process and do nothing with it. */
86 process_noop (size_t size
__attribute__ ((unused
)),
87 char *data
__attribute__ ((unused
)))
93 process_rawdata (size_t bytes
, char *buffer
)
95 size_t status
= safe_read (diff_handle
, diff_buffer
, bytes
);
99 if (status
== SAFE_READ_ERROR
)
101 read_error (current_stat_info
.file_name
);
102 report_difference (¤t_stat_info
, NULL
);
106 report_difference (¤t_stat_info
,
107 ngettext ("Could only read %lu of %lu byte",
108 "Could only read %lu of %lu bytes",
110 (unsigned long) status
, (unsigned long) bytes
);
115 if (memcmp (buffer
, diff_buffer
, bytes
))
117 report_difference (¤t_stat_info
, _("Contents differ"));
124 /* Directory contents, only for GNUTYPE_DUMPDIR. */
126 static char *dumpdir_cursor
;
129 process_dumpdir (size_t bytes
, char *buffer
)
131 if (memcmp (buffer
, dumpdir_cursor
, bytes
))
133 report_difference (¤t_stat_info
, _("Contents differ"));
137 dumpdir_cursor
+= bytes
;
141 /* Some other routine wants SIZE bytes in the archive. For each chunk
142 of the archive, call PROCESSOR with the size of the chunk, and the
143 address of the chunk it can work with. The PROCESSOR should return
144 nonzero for success. It it return error once, continue skipping
145 without calling PROCESSOR anymore. */
147 read_and_process (off_t size
, int (*processor
) (size_t, char *))
149 union block
*data_block
;
152 if (multi_volume_option
)
153 save_sizeleft
= size
;
156 data_block
= find_next_block ();
159 ERROR ((0, 0, _("Unexpected EOF in archive")));
163 data_size
= available_space_after (data_block
);
164 if (data_size
> size
)
166 if (!(*processor
) (data_size
, data_block
->buffer
))
167 processor
= process_noop
;
168 set_next_block_after ((union block
*)
169 (data_block
->buffer
+ data_size
- 1));
171 if (multi_volume_option
)
172 save_sizeleft
-= data_size
;
176 /* Call either stat or lstat over STAT_DATA, depending on
177 --dereference (-h), for a file which should exist. Diagnose any
178 problem. Return nonzero for success, zero otherwise. */
180 get_stat_data (char const *file_name
, struct stat
*stat_data
)
182 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
187 stat_warn (file_name
);
189 stat_error (file_name
);
190 report_difference (¤t_stat_info
, NULL
);
201 struct stat stat_data
;
203 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
206 if (!S_ISDIR (stat_data
.st_mode
))
207 report_difference (¤t_stat_info
, _("File type differs"));
208 else if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
209 (stat_data
.st_mode
& MODE_ALL
))
210 report_difference (¤t_stat_info
, _("Mode differs"));
216 struct stat stat_data
;
218 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
220 else if (!S_ISREG (stat_data
.st_mode
))
222 report_difference (¤t_stat_info
, _("File type differs"));
227 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
228 (stat_data
.st_mode
& MODE_ALL
))
229 report_difference (¤t_stat_info
, _("Mode differs"));
231 if (!sys_compare_uid (&stat_data
, ¤t_stat_info
.stat
))
232 report_difference (¤t_stat_info
, _("Uid differs"));
233 if (!sys_compare_gid (&stat_data
, ¤t_stat_info
.stat
))
234 report_difference (¤t_stat_info
, _("Gid differs"));
236 if (stat_data
.st_mtime
!= current_stat_info
.stat
.st_mtime
)
237 report_difference (¤t_stat_info
, _("Mod time differs"));
238 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
239 stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
241 report_difference (¤t_stat_info
, _("Size differs"));
246 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
250 open_error (current_stat_info
.file_name
);
252 report_difference (¤t_stat_info
, NULL
);
257 struct utimbuf restore_times
;
259 restore_times
.actime
= stat_data
.st_atime
;
260 restore_times
.modtime
= stat_data
.st_mtime
;
262 if (current_stat_info
.is_sparse
)
263 sparse_diff_file (diff_handle
, ¤t_stat_info
);
266 if (multi_volume_option
)
268 assign_string (&save_name
,
269 current_stat_info
.orig_file_name
);
270 save_totsize
= current_stat_info
.stat
.st_size
;
271 /* save_sizeleft is set in read_and_process. */
274 read_and_process (current_stat_info
.stat
.st_size
,
277 if (multi_volume_option
)
278 assign_string (&save_name
, 0);
281 status
= close (diff_handle
);
283 close_error (current_stat_info
.file_name
);
285 if (atime_preserve_option
)
286 utime (current_stat_info
.file_name
, &restore_times
);
295 struct stat file_data
;
296 struct stat link_data
;
298 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
299 && get_stat_data (current_stat_info
.link_name
, &link_data
)
300 && !sys_compare_links (&file_data
, &link_data
))
301 report_difference (¤t_stat_info
,
302 _("Not linked to %s"),
303 quote (current_stat_info
.link_name
));
310 size_t len
= strlen (current_stat_info
.link_name
);
311 char *linkbuf
= alloca (len
+ 1);
313 int status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
318 readlink_warn (current_stat_info
.file_name
);
320 readlink_error (current_stat_info
.file_name
);
321 report_difference (¤t_stat_info
, NULL
);
323 else if (status
!= len
324 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
325 report_difference (¤t_stat_info
, _("Symlink differs"));
332 struct stat stat_data
;
334 /* FIXME: deal with umask. */
336 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
339 if (current_header
->header
.typeflag
== CHRTYPE
340 ? !S_ISCHR (stat_data
.st_mode
)
341 : current_header
->header
.typeflag
== BLKTYPE
342 ? !S_ISBLK (stat_data
.st_mode
)
343 : /* current_header->header.typeflag == FIFOTYPE */
344 !S_ISFIFO (stat_data
.st_mode
))
346 report_difference (¤t_stat_info
, _("File type differs"));
350 if ((current_header
->header
.typeflag
== CHRTYPE
351 || current_header
->header
.typeflag
== BLKTYPE
)
352 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
354 report_difference (¤t_stat_info
, _("Device number differs"));
358 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
359 (stat_data
.st_mode
& MODE_ALL
))
360 report_difference (¤t_stat_info
, _("Mode differs"));
366 char *dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
,
369 if (multi_volume_option
)
371 assign_string (&save_name
, current_stat_info
.orig_file_name
);
372 save_totsize
= current_stat_info
.stat
.st_size
;
373 /* save_sizeleft is set in read_and_process. */
378 dumpdir_cursor
= dumpdir_buffer
;
379 read_and_process (current_stat_info
.stat
.st_size
, process_dumpdir
);
380 free (dumpdir_buffer
);
383 read_and_process (current_stat_info
.stat
.st_size
, process_noop
);
385 if (multi_volume_option
)
386 assign_string (&save_name
, 0);
392 struct stat stat_data
;
396 if (current_stat_info
.had_trailing_slash
)
402 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
405 if (!S_ISREG (stat_data
.st_mode
))
407 report_difference (¤t_stat_info
, _("File type differs"));
412 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
413 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
415 report_difference (¤t_stat_info
, _("Size differs"));
420 fd
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
424 open_error (current_stat_info
.file_name
);
425 report_difference (¤t_stat_info
, NULL
);
430 if (lseek (fd
, offset
, SEEK_SET
) < 0)
432 seek_error_details (current_stat_info
.file_name
, offset
);
433 report_difference (¤t_stat_info
, NULL
);
437 if (multi_volume_option
)
439 assign_string (&save_name
, current_stat_info
.orig_file_name
);
440 save_totsize
= stat_data
.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);
451 close_error (current_stat_info
.file_name
);
454 /* Diff a file against the archive. */
459 set_next_block_after (current_header
);
460 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
462 /* Print the block from current_header and current_stat_info. */
467 fprintf (stdlis
, _("Verify "));
468 print_header (¤t_stat_info
, -1);
471 switch (current_header
->header
.typeflag
)
474 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
475 quotearg_colon (current_stat_info
.file_name
),
476 current_header
->header
.typeflag
));
484 /* Appears to be a file. See if it's really a directory. */
486 if (current_stat_info
.had_trailing_slash
)
508 case GNUTYPE_DUMPDIR
:
519 case GNUTYPE_MULTIVOL
:
527 if (removed_prefixes_p ())
530 _("Archive contains file names with leading prefixes removed.")));
532 _("Verification may fail to locate original files.")));
538 /* Verifying an archive is meant to check if the physical media got it
539 correctly, so try to defeat clever in-memory buffering pertaining to
540 this particular media. On Linux, for example, the floppy drive would
541 not even be accessed for the whole verification.
543 The code was using fsync only when the ioctl is unavailable, but
544 Marty Leisner says that the ioctl does not work when not preceded by
545 fsync. So, until we know better, or maybe to please Marty, let's do it
546 the unbelievable way :-). */
552 ioctl (archive
, FDFLUSH
);
557 struct mtop operation
;
560 operation
.mt_op
= MTBSF
;
561 operation
.mt_count
= 1;
562 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
565 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
569 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
571 /* Lseek failed. Try a different method. */
572 seek_warn (archive_name_array
[0]);
581 access_mode
= ACCESS_READ
;
587 enum read_header status
= read_header (false);
589 if (status
== HEADER_FAILURE
)
596 set_next_block_after (current_header
);
597 status
= read_header (false);
599 while (status
== HEADER_FAILURE
);
602 ngettext ("VERIFY FAILURE: %d invalid header detected",
603 "VERIFY FAILURE: %d invalid headers detected",
606 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
610 tar_stat_destroy (¤t_stat_info
);
611 xheader_destroy (&extended_header
);
614 access_mode
= ACCESS_WRITE
;