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 (stat_data
.st_mtime
!= current_stat_info
.stat
.st_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
= get_directory_contents (current_stat_info
.file_name
,
362 if (multi_volume_option
)
364 assign_string (&save_name
, current_stat_info
.orig_file_name
);
365 save_totsize
= current_stat_info
.stat
.st_size
;
366 /* save_sizeleft is set in read_and_process. */
371 dumpdir_cursor
= dumpdir_buffer
;
372 read_and_process (current_stat_info
.stat
.st_size
, process_dumpdir
);
373 free (dumpdir_buffer
);
376 read_and_process (current_stat_info
.stat
.st_size
, process_noop
);
378 if (multi_volume_option
)
379 assign_string (&save_name
, 0);
385 struct stat stat_data
;
389 if (current_stat_info
.had_trailing_slash
)
395 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
398 if (!S_ISREG (stat_data
.st_mode
))
400 report_difference (¤t_stat_info
, _("File type differs"));
405 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
406 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
408 report_difference (¤t_stat_info
, _("Size differs"));
413 fd
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
417 open_error (current_stat_info
.file_name
);
418 report_difference (¤t_stat_info
, NULL
);
423 if (lseek (fd
, offset
, SEEK_SET
) < 0)
425 seek_error_details (current_stat_info
.file_name
, offset
);
426 report_difference (¤t_stat_info
, NULL
);
430 if (multi_volume_option
)
432 assign_string (&save_name
, current_stat_info
.orig_file_name
);
433 save_totsize
= stat_data
.st_size
;
434 /* save_sizeleft is set in read_and_process. */
437 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
439 if (multi_volume_option
)
440 assign_string (&save_name
, 0);
444 close_error (current_stat_info
.file_name
);
447 /* Diff a file against the archive. */
452 set_next_block_after (current_header
);
453 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
455 /* Print the block from current_header and current_stat_info. */
460 fprintf (stdlis
, _("Verify "));
461 print_header (¤t_stat_info
, -1);
464 switch (current_header
->header
.typeflag
)
467 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
468 quotearg_colon (current_stat_info
.file_name
),
469 current_header
->header
.typeflag
));
477 /* Appears to be a file. See if it's really a directory. */
479 if (current_stat_info
.had_trailing_slash
)
501 case GNUTYPE_DUMPDIR
:
512 case GNUTYPE_MULTIVOL
:
520 if (removed_prefixes_p ())
523 _("Archive contains file names with leading prefixes removed.")));
525 _("Verification may fail to locate original files.")));
531 /* Verifying an archive is meant to check if the physical media got it
532 correctly, so try to defeat clever in-memory buffering pertaining to
533 this particular media. On Linux, for example, the floppy drive would
534 not even be accessed for the whole verification.
536 The code was using fsync only when the ioctl is unavailable, but
537 Marty Leisner says that the ioctl does not work when not preceded by
538 fsync. So, until we know better, or maybe to please Marty, let's do it
539 the unbelievable way :-). */
545 ioctl (archive
, FDFLUSH
);
550 struct mtop operation
;
553 operation
.mt_op
= MTBSF
;
554 operation
.mt_count
= 1;
555 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
558 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
562 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
564 /* Lseek failed. Try a different method. */
565 seek_warn (archive_name_array
[0]);
574 access_mode
= ACCESS_READ
;
580 enum read_header status
= read_header (false);
582 if (status
== HEADER_FAILURE
)
589 set_next_block_after (current_header
);
590 status
= read_header (false);
592 while (status
== HEADER_FAILURE
);
595 ngettext ("VERIFY FAILURE: %d invalid header detected",
596 "VERIFY FAILURE: %d invalid headers detected",
599 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
603 tar_stat_destroy (¤t_stat_info
);
604 xheader_destroy (&extended_header
);
607 access_mode
= ACCESS_WRITE
;