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 (tar_timespec_cmp (get_stat_mtime (&stat_data
),
228 current_stat_info
.mtime
))
229 report_difference (¤t_stat_info
, _("Mod time differs"));
230 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
231 stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
233 report_difference (¤t_stat_info
, _("Size differs"));
238 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
242 open_error (current_stat_info
.file_name
);
244 report_difference (¤t_stat_info
, NULL
);
250 if (current_stat_info
.is_sparse
)
251 sparse_diff_file (diff_handle
, ¤t_stat_info
);
254 if (multi_volume_option
)
256 assign_string (&save_name
,
257 current_stat_info
.orig_file_name
);
258 save_totsize
= current_stat_info
.stat
.st_size
;
259 /* save_sizeleft is set in read_and_process. */
262 read_and_process (current_stat_info
.stat
.st_size
,
265 if (multi_volume_option
)
266 assign_string (&save_name
, 0);
269 status
= close (diff_handle
);
271 close_error (current_stat_info
.file_name
);
273 if (atime_preserve_option
)
275 struct timespec ts
[2];
276 ts
[0] = get_stat_atime (&stat_data
);
277 ts
[1] = get_stat_mtime (&stat_data
);
278 if (utimens (current_stat_info
.file_name
, ts
) != 0)
279 utime_error (current_stat_info
.file_name
);
289 struct stat file_data
;
290 struct stat link_data
;
292 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
293 && get_stat_data (current_stat_info
.link_name
, &link_data
)
294 && !sys_compare_links (&file_data
, &link_data
))
295 report_difference (¤t_stat_info
,
296 _("Not linked to %s"),
297 quote (current_stat_info
.link_name
));
304 size_t len
= strlen (current_stat_info
.link_name
);
305 char *linkbuf
= alloca (len
+ 1);
307 int status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
312 readlink_warn (current_stat_info
.file_name
);
314 readlink_error (current_stat_info
.file_name
);
315 report_difference (¤t_stat_info
, NULL
);
317 else if (status
!= len
318 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
319 report_difference (¤t_stat_info
, _("Symlink differs"));
326 struct stat stat_data
;
328 /* FIXME: deal with umask. */
330 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
333 if (current_header
->header
.typeflag
== CHRTYPE
334 ? !S_ISCHR (stat_data
.st_mode
)
335 : current_header
->header
.typeflag
== BLKTYPE
336 ? !S_ISBLK (stat_data
.st_mode
)
337 : /* current_header->header.typeflag == FIFOTYPE */
338 !S_ISFIFO (stat_data
.st_mode
))
340 report_difference (¤t_stat_info
, _("File type differs"));
344 if ((current_header
->header
.typeflag
== CHRTYPE
345 || current_header
->header
.typeflag
== BLKTYPE
)
346 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
348 report_difference (¤t_stat_info
, _("Device number differs"));
352 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
353 (stat_data
.st_mode
& MODE_ALL
))
354 report_difference (¤t_stat_info
, _("Mode differs"));
360 char *dumpdir_buffer
;
364 if (deref_stat (true, current_stat_info
.file_name
, &stat
))
367 stat_warn (current_stat_info
.file_name
);
369 stat_error (current_stat_info
.file_name
);
374 dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
, dev
);
376 if (multi_volume_option
)
378 assign_string (&save_name
, current_stat_info
.orig_file_name
);
379 save_totsize
= current_stat_info
.stat
.st_size
;
380 /* save_sizeleft is set in read_and_process. */
385 dumpdir_cursor
= dumpdir_buffer
;
386 read_and_process (current_stat_info
.stat
.st_size
, process_dumpdir
);
387 free (dumpdir_buffer
);
390 read_and_process (current_stat_info
.stat
.st_size
, process_noop
);
392 if (multi_volume_option
)
393 assign_string (&save_name
, 0);
399 struct stat stat_data
;
403 if (current_stat_info
.had_trailing_slash
)
409 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
412 if (!S_ISREG (stat_data
.st_mode
))
414 report_difference (¤t_stat_info
, _("File type differs"));
419 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
420 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
422 report_difference (¤t_stat_info
, _("Size differs"));
427 fd
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
431 open_error (current_stat_info
.file_name
);
432 report_difference (¤t_stat_info
, NULL
);
437 if (lseek (fd
, offset
, SEEK_SET
) < 0)
439 seek_error_details (current_stat_info
.file_name
, offset
);
440 report_difference (¤t_stat_info
, NULL
);
444 if (multi_volume_option
)
446 assign_string (&save_name
, current_stat_info
.orig_file_name
);
447 save_totsize
= stat_data
.st_size
;
448 /* save_sizeleft is set in read_and_process. */
451 read_and_process (current_stat_info
.stat
.st_size
, process_rawdata
);
453 if (multi_volume_option
)
454 assign_string (&save_name
, 0);
458 close_error (current_stat_info
.file_name
);
461 /* Diff a file against the archive. */
466 set_next_block_after (current_header
);
467 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
469 /* Print the block from current_header and current_stat_info. */
474 fprintf (stdlis
, _("Verify "));
475 print_header (¤t_stat_info
, -1);
478 switch (current_header
->header
.typeflag
)
481 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
482 quotearg_colon (current_stat_info
.file_name
),
483 current_header
->header
.typeflag
));
491 /* Appears to be a file. See if it's really a directory. */
493 if (current_stat_info
.had_trailing_slash
)
515 case GNUTYPE_DUMPDIR
:
526 case GNUTYPE_MULTIVOL
:
534 if (removed_prefixes_p ())
537 _("Archive contains file names with leading prefixes removed.")));
539 _("Verification may fail to locate original files.")));
545 /* Verifying an archive is meant to check if the physical media got it
546 correctly, so try to defeat clever in-memory buffering pertaining to
547 this particular media. On Linux, for example, the floppy drive would
548 not even be accessed for the whole verification.
550 The code was using fsync only when the ioctl is unavailable, but
551 Marty Leisner says that the ioctl does not work when not preceded by
552 fsync. So, until we know better, or maybe to please Marty, let's do it
553 the unbelievable way :-). */
559 ioctl (archive
, FDFLUSH
);
564 struct mtop operation
;
567 operation
.mt_op
= MTBSF
;
568 operation
.mt_count
= 1;
569 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
572 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
576 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
578 /* Lseek failed. Try a different method. */
579 seek_warn (archive_name_array
[0]);
588 access_mode
= ACCESS_READ
;
594 enum read_header status
= read_header (false);
596 if (status
== HEADER_FAILURE
)
603 set_next_block_after (current_header
);
604 status
= read_header (false);
606 while (status
== HEADER_FAILURE
);
609 ngettext ("VERIFY FAILURE: %d invalid header detected",
610 "VERIFY FAILURE: %d invalid headers detected",
613 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
617 tar_stat_destroy (¤t_stat_info
);
618 xheader_destroy (&extended_header
);
621 access_mode
= ACCESS_WRITE
;