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>
33 /* Nonzero if we are verifying at the moment. */
36 /* File descriptor for the file we are diffing. */
37 static int diff_handle
;
39 /* Area for reading file contents into. */
40 static char *diff_buffer
;
42 /* Initialize for a diff operation. */
47 diff_buffer
= page_aligned_alloc (&ptr
, record_size
);
48 if (listed_incremental_option
)
49 read_directory_file ();
52 /* Sigh about something that differs by writing a MESSAGE to stdlis,
53 given MESSAGE is nonzero. Also set the exit status if not already. */
55 report_difference (struct tar_stat_info
*st
__attribute__ ((unused
)),
62 fprintf (stdlis
, "%s: ", quotearg_colon (current_stat_info
.file_name
));
64 vfprintf (stdlis
, fmt
, ap
);
66 fprintf (stdlis
, "\n");
69 if (exit_status
== TAREXIT_SUCCESS
)
70 exit_status
= TAREXIT_DIFFERS
;
73 /* Take a buffer returned by read_and_process and do nothing with it. */
75 process_noop (size_t size
__attribute__ ((unused
)),
76 char *data
__attribute__ ((unused
)))
82 process_rawdata (size_t bytes
, char *buffer
)
84 size_t status
= safe_read (diff_handle
, diff_buffer
, bytes
);
88 if (status
== SAFE_READ_ERROR
)
90 read_error (current_stat_info
.file_name
);
91 report_difference (¤t_stat_info
, NULL
);
95 report_difference (¤t_stat_info
,
96 ngettext ("Could only read %lu of %lu byte",
97 "Could only read %lu of %lu bytes",
99 (unsigned long) status
, (unsigned long) bytes
);
104 if (memcmp (buffer
, diff_buffer
, bytes
))
106 report_difference (¤t_stat_info
, _("Contents differ"));
113 /* Directory contents, only for GNUTYPE_DUMPDIR. */
115 static char *dumpdir_cursor
;
118 process_dumpdir (size_t bytes
, char *buffer
)
120 if (memcmp (buffer
, dumpdir_cursor
, bytes
))
122 report_difference (¤t_stat_info
, _("Contents differ"));
126 dumpdir_cursor
+= bytes
;
130 /* Some other routine wants SIZE bytes in the archive. For each chunk
131 of the archive, call PROCESSOR with the size of the chunk, and the
132 address of the chunk it can work with. The PROCESSOR should return
133 nonzero for success. It it return error once, continue skipping
134 without calling PROCESSOR anymore. */
137 read_and_process (struct tar_stat_info
*st
, int (*processor
) (size_t, char *))
139 union block
*data_block
;
141 size_t size
= st
->stat
.st_size
;
146 data_block
= find_next_block ();
149 ERROR ((0, 0, _("Unexpected EOF in archive")));
153 data_size
= available_space_after (data_block
);
154 if (data_size
> size
)
156 if (!(*processor
) (data_size
, data_block
->buffer
))
157 processor
= process_noop
;
158 set_next_block_after ((union block
*)
159 (data_block
->buffer
+ data_size
- 1));
166 /* Call either stat or lstat over STAT_DATA, depending on
167 --dereference (-h), for a file which should exist. Diagnose any
168 problem. Return nonzero for success, zero otherwise. */
170 get_stat_data (char const *file_name
, struct stat
*stat_data
)
172 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
177 stat_warn (file_name
);
179 stat_error (file_name
);
180 report_difference (¤t_stat_info
, NULL
);
191 struct stat stat_data
;
193 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
196 if (!S_ISDIR (stat_data
.st_mode
))
197 report_difference (¤t_stat_info
, _("File type differs"));
198 else if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
199 (stat_data
.st_mode
& MODE_ALL
))
200 report_difference (¤t_stat_info
, _("Mode differs"));
206 char const *file_name
= current_stat_info
.file_name
;
207 struct stat stat_data
;
209 if (!get_stat_data (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"));
239 (atime_preserve_option
== system_atime_preserve
243 diff_handle
= open (file_name
, O_RDONLY
| O_BINARY
| atime_flag
);
247 open_error (file_name
);
249 report_difference (¤t_stat_info
, NULL
);
255 if (current_stat_info
.is_sparse
)
256 sparse_diff_file (diff_handle
, ¤t_stat_info
);
258 read_and_process (¤t_stat_info
, process_rawdata
);
260 if (atime_preserve_option
== replace_atime_preserve
)
262 struct timespec ts
[2];
263 ts
[0] = get_stat_atime (&stat_data
);
264 ts
[1] = get_stat_mtime (&stat_data
);
265 if (set_file_atime (diff_handle
, file_name
, ts
) != 0)
266 utime_error (file_name
);
269 status
= close (diff_handle
);
271 close_error (file_name
);
280 struct stat file_data
;
281 struct stat link_data
;
283 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
284 && get_stat_data (current_stat_info
.link_name
, &link_data
)
285 && !sys_compare_links (&file_data
, &link_data
))
286 report_difference (¤t_stat_info
,
287 _("Not linked to %s"),
288 quote (current_stat_info
.link_name
));
295 size_t len
= strlen (current_stat_info
.link_name
);
296 char *linkbuf
= alloca (len
+ 1);
298 int status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
303 readlink_warn (current_stat_info
.file_name
);
305 readlink_error (current_stat_info
.file_name
);
306 report_difference (¤t_stat_info
, NULL
);
308 else if (status
!= len
309 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
310 report_difference (¤t_stat_info
, _("Symlink differs"));
317 struct stat stat_data
;
319 /* FIXME: deal with umask. */
321 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
324 if (current_header
->header
.typeflag
== CHRTYPE
325 ? !S_ISCHR (stat_data
.st_mode
)
326 : current_header
->header
.typeflag
== BLKTYPE
327 ? !S_ISBLK (stat_data
.st_mode
)
328 : /* current_header->header.typeflag == FIFOTYPE */
329 !S_ISFIFO (stat_data
.st_mode
))
331 report_difference (¤t_stat_info
, _("File type differs"));
335 if ((current_header
->header
.typeflag
== CHRTYPE
336 || current_header
->header
.typeflag
== BLKTYPE
)
337 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
339 report_difference (¤t_stat_info
, _("Device number differs"));
343 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
344 (stat_data
.st_mode
& MODE_ALL
))
345 report_difference (¤t_stat_info
, _("Mode differs"));
351 char *dumpdir_buffer
;
355 if (deref_stat (true, current_stat_info
.file_name
, &stat
))
358 stat_warn (current_stat_info
.file_name
);
360 stat_error (current_stat_info
.file_name
);
365 dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
, dev
);
369 dumpdir_cursor
= dumpdir_buffer
;
370 read_and_process (¤t_stat_info
, process_dumpdir
);
371 free (dumpdir_buffer
);
374 read_and_process (¤t_stat_info
, process_noop
);
380 struct stat stat_data
;
384 if (current_stat_info
.had_trailing_slash
)
390 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
393 if (!S_ISREG (stat_data
.st_mode
))
395 report_difference (¤t_stat_info
, _("File type differs"));
400 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
401 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
403 report_difference (¤t_stat_info
, _("Size differs"));
408 fd
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
412 open_error (current_stat_info
.file_name
);
413 report_difference (¤t_stat_info
, NULL
);
418 if (lseek (fd
, offset
, SEEK_SET
) < 0)
420 seek_error_details (current_stat_info
.file_name
, offset
);
421 report_difference (¤t_stat_info
, NULL
);
425 read_and_process (¤t_stat_info
, process_rawdata
);
429 close_error (current_stat_info
.file_name
);
432 /* Diff a file against the archive. */
437 set_next_block_after (current_header
);
438 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
440 /* Print the block from current_header and current_stat_info. */
445 fprintf (stdlis
, _("Verify "));
446 print_header (¤t_stat_info
, -1);
449 switch (current_header
->header
.typeflag
)
452 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
453 quotearg_colon (current_stat_info
.file_name
),
454 current_header
->header
.typeflag
));
462 /* Appears to be a file. See if it's really a directory. */
464 if (current_stat_info
.had_trailing_slash
)
486 case GNUTYPE_DUMPDIR
:
497 case GNUTYPE_MULTIVOL
:
505 if (removed_prefixes_p ())
508 _("Archive contains file names with leading prefixes removed.")));
510 _("Verification may fail to locate original files.")));
516 /* Verifying an archive is meant to check if the physical media got it
517 correctly, so try to defeat clever in-memory buffering pertaining to
518 this particular media. On Linux, for example, the floppy drive would
519 not even be accessed for the whole verification.
521 The code was using fsync only when the ioctl is unavailable, but
522 Marty Leisner says that the ioctl does not work when not preceded by
523 fsync. So, until we know better, or maybe to please Marty, let's do it
524 the unbelievable way :-). */
530 ioctl (archive
, FDFLUSH
);
535 struct mtop operation
;
538 operation
.mt_op
= MTBSF
;
539 operation
.mt_count
= 1;
540 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
543 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
547 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
549 /* Lseek failed. Try a different method. */
550 seek_warn (archive_name_array
[0]);
559 access_mode
= ACCESS_READ
;
565 enum read_header status
= read_header (false);
567 if (status
== HEADER_FAILURE
)
574 set_next_block_after (current_header
);
575 status
= read_header (false);
577 while (status
== HEADER_FAILURE
);
580 ngettext ("VERIFY FAILURE: %d invalid header detected",
581 "VERIFY FAILURE: %d invalid headers detected",
584 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
588 tar_stat_destroy (¤t_stat_info
);
589 xheader_destroy (&extended_header
);
592 access_mode
= ACCESS_WRITE
;