1 /* Diff files from a tar archive.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003, 2004, 2005 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. */
25 # include <linux/fd.h>
35 /* Nonzero if we are verifying at the moment. */
38 /* File descriptor for the file we are diffing. */
39 static int diff_handle
;
41 /* Area for reading file contents into. */
42 static char *diff_buffer
;
44 /* Initialize for a diff operation. */
49 diff_buffer
= page_aligned_alloc (&ptr
, record_size
);
50 if (listed_incremental_option
)
51 read_directory_file ();
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. */
57 report_difference (struct tar_stat_info
*st
__attribute__ ((unused
)),
64 fprintf (stdlis
, "%s: ", quotearg_colon (current_stat_info
.file_name
));
66 vfprintf (stdlis
, fmt
, ap
);
68 fprintf (stdlis
, "\n");
71 if (exit_status
== TAREXIT_SUCCESS
)
72 exit_status
= TAREXIT_DIFFERS
;
75 /* Take a buffer returned by read_and_process and do nothing with it. */
77 process_noop (size_t size
__attribute__ ((unused
)),
78 char *data
__attribute__ ((unused
)))
84 process_rawdata (size_t bytes
, char *buffer
)
86 size_t status
= safe_read (diff_handle
, diff_buffer
, bytes
);
90 if (status
== SAFE_READ_ERROR
)
92 read_error (current_stat_info
.file_name
);
93 report_difference (¤t_stat_info
, NULL
);
97 report_difference (¤t_stat_info
,
98 ngettext ("Could only read %lu of %lu byte",
99 "Could only read %lu of %lu bytes",
101 (unsigned long) status
, (unsigned long) bytes
);
106 if (memcmp (buffer
, diff_buffer
, bytes
))
108 report_difference (¤t_stat_info
, _("Contents differ"));
115 /* Directory contents, only for GNUTYPE_DUMPDIR. */
117 static char *dumpdir_cursor
;
120 process_dumpdir (size_t bytes
, char *buffer
)
122 if (memcmp (buffer
, dumpdir_cursor
, bytes
))
124 report_difference (¤t_stat_info
, _("Contents differ"));
128 dumpdir_cursor
+= bytes
;
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. */
138 read_and_process (off_t size
, int (*processor
) (size_t, char *))
140 union block
*data_block
;
143 if (multi_volume_option
)
144 save_sizeleft
= size
;
147 data_block
= find_next_block ();
150 ERROR ((0, 0, _("Unexpected EOF in archive")));
154 data_size
= available_space_after (data_block
);
155 if (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));
162 if (multi_volume_option
)
163 save_sizeleft
-= data_size
;
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. */
171 get_stat_data (char const *file_name
, struct stat
*stat_data
)
173 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
178 stat_warn (file_name
);
180 stat_error (file_name
);
181 report_difference (¤t_stat_info
, NULL
);
192 struct stat stat_data
;
194 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
197 if (!S_ISDIR (stat_data
.st_mode
))
198 report_difference (¤t_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 (¤t_stat_info
, _("Mode differs"));
207 struct stat stat_data
;
209 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
211 else if (!S_ISREG (stat_data
.st_mode
))
213 report_difference (¤t_stat_info
, _("File type differs"));
218 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
219 (stat_data
.st_mode
& MODE_ALL
))
220 report_difference (¤t_stat_info
, _("Mode differs"));
222 if (!sys_compare_uid (&stat_data
, ¤t_stat_info
.stat
))
223 report_difference (¤t_stat_info
, _("Uid differs"));
224 if (!sys_compare_gid (&stat_data
, ¤t_stat_info
.stat
))
225 report_difference (¤t_stat_info
, _("Gid differs"));
227 if (timespec_cmp (get_stat_mtime (&stat_data
), current_stat_info
.mtime
))
228 report_difference (¤t_stat_info
, _("Mod time differs"));
229 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
230 stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
232 report_difference (¤t_stat_info
, _("Size differs"));
237 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
241 open_error (current_stat_info
.file_name
);
243 report_difference (¤t_stat_info
, NULL
);
249 if (current_stat_info
.is_sparse
)
250 sparse_diff_file (diff_handle
, ¤t_stat_info
);
253 if (multi_volume_option
)
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. */
261 read_and_process (current_stat_info
.stat
.st_size
,
264 if (multi_volume_option
)
265 assign_string (&save_name
, 0);
268 status
= close (diff_handle
);
270 close_error (current_stat_info
.file_name
);
272 if (atime_preserve_option
)
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
);
288 struct stat file_data
;
289 struct stat link_data
;
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 (¤t_stat_info
,
295 _("Not linked to %s"),
296 quote (current_stat_info
.link_name
));
303 size_t len
= strlen (current_stat_info
.link_name
);
304 char *linkbuf
= alloca (len
+ 1);
306 int status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
311 readlink_warn (current_stat_info
.file_name
);
313 readlink_error (current_stat_info
.file_name
);
314 report_difference (¤t_stat_info
, NULL
);
316 else if (status
!= len
317 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
318 report_difference (¤t_stat_info
, _("Symlink differs"));
325 struct stat stat_data
;
327 /* FIXME: deal with umask. */
329 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
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
))
339 report_difference (¤t_stat_info
, _("File type differs"));
343 if ((current_header
->header
.typeflag
== CHRTYPE
344 || current_header
->header
.typeflag
== BLKTYPE
)
345 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
347 report_difference (¤t_stat_info
, _("Device number differs"));
351 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
352 (stat_data
.st_mode
& MODE_ALL
))
353 report_difference (¤t_stat_info
, _("Mode differs"));
359 char *dumpdir_buffer
;
363 if (deref_stat (true, current_stat_info
.file_name
, &stat
))
366 stat_warn (current_stat_info
.file_name
);
368 stat_error (current_stat_info
.file_name
);
373 dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
, dev
);
375 if (multi_volume_option
)
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. */
384 dumpdir_cursor
= dumpdir_buffer
;
385 read_and_process (current_stat_info
.stat
.st_size
, process_dumpdir
);
386 free (dumpdir_buffer
);
389 read_and_process (current_stat_info
.stat
.st_size
, process_noop
);
391 if (multi_volume_option
)
392 assign_string (&save_name
, 0);
398 struct stat stat_data
;
402 if (current_stat_info
.had_trailing_slash
)
408 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
411 if (!S_ISREG (stat_data
.st_mode
))
413 report_difference (¤t_stat_info
, _("File type differs"));
418 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
419 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
421 report_difference (¤t_stat_info
, _("Size differs"));
426 fd
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
430 open_error (current_stat_info
.file_name
);
431 report_difference (¤t_stat_info
, NULL
);
436 if (lseek (fd
, offset
, SEEK_SET
) < 0)
438 seek_error_details (current_stat_info
.file_name
, offset
);
439 report_difference (¤t_stat_info
, NULL
);
443 if (multi_volume_option
)
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. */
450 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
452 if (multi_volume_option
)
453 assign_string (&save_name
, 0);
457 close_error (current_stat_info
.file_name
);
460 /* Diff a file against the archive. */
465 set_next_block_after (current_header
);
466 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
468 /* Print the block from current_header and current_stat_info. */
473 fprintf (stdlis
, _("Verify "));
474 print_header (¤t_stat_info
, -1);
477 switch (current_header
->header
.typeflag
)
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
));
490 /* Appears to be a file. See if it's really a directory. */
492 if (current_stat_info
.had_trailing_slash
)
514 case GNUTYPE_DUMPDIR
:
525 case GNUTYPE_MULTIVOL
:
533 if (removed_prefixes_p ())
536 _("Archive contains file names with leading prefixes removed.")));
538 _("Verification may fail to locate original files.")));
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.
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 :-). */
558 ioctl (archive
, FDFLUSH
);
563 struct mtop operation
;
566 operation
.mt_op
= MTBSF
;
567 operation
.mt_count
= 1;
568 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
571 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
575 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
577 /* Lseek failed. Try a different method. */
578 seek_warn (archive_name_array
[0]);
587 access_mode
= ACCESS_READ
;
593 enum read_header status
= read_header (false);
595 if (status
== HEADER_FAILURE
)
602 set_next_block_after (current_header
);
603 status
= read_header (false);
605 while (status
== HEADER_FAILURE
);
608 ngettext ("VERIFY FAILURE: %d invalid header detected",
609 "VERIFY FAILURE: %d invalid headers detected",
612 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
616 tar_stat_destroy (¤t_stat_info
);
617 xheader_destroy (&extended_header
);
620 access_mode
= ACCESS_WRITE
;