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. */
139 read_and_process (struct tar_stat_info
*st
, int (*processor
) (size_t, char *))
141 union block
*data_block
;
143 size_t size
= st
->stat
.st_size
;
148 data_block
= find_next_block ();
151 ERROR ((0, 0, _("Unexpected EOF in archive")));
155 data_size
= available_space_after (data_block
);
156 if (data_size
> size
)
158 if (!(*processor
) (data_size
, data_block
->buffer
))
159 processor
= process_noop
;
160 set_next_block_after ((union block
*)
161 (data_block
->buffer
+ data_size
- 1));
168 /* Call either stat or lstat over STAT_DATA, depending on
169 --dereference (-h), for a file which should exist. Diagnose any
170 problem. Return nonzero for success, zero otherwise. */
172 get_stat_data (char const *file_name
, struct stat
*stat_data
)
174 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
179 stat_warn (file_name
);
181 stat_error (file_name
);
182 report_difference (¤t_stat_info
, NULL
);
193 struct stat stat_data
;
195 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
198 if (!S_ISDIR (stat_data
.st_mode
))
199 report_difference (¤t_stat_info
, _("File type differs"));
200 else if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
201 (stat_data
.st_mode
& MODE_ALL
))
202 report_difference (¤t_stat_info
, _("Mode differs"));
208 struct stat stat_data
;
210 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
212 else if (!S_ISREG (stat_data
.st_mode
))
214 report_difference (¤t_stat_info
, _("File type differs"));
219 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
220 (stat_data
.st_mode
& MODE_ALL
))
221 report_difference (¤t_stat_info
, _("Mode differs"));
223 if (!sys_compare_uid (&stat_data
, ¤t_stat_info
.stat
))
224 report_difference (¤t_stat_info
, _("Uid differs"));
225 if (!sys_compare_gid (&stat_data
, ¤t_stat_info
.stat
))
226 report_difference (¤t_stat_info
, _("Gid differs"));
228 if (tar_timespec_cmp (get_stat_mtime (&stat_data
),
229 current_stat_info
.mtime
))
230 report_difference (¤t_stat_info
, _("Mod time differs"));
231 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
232 stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
234 report_difference (¤t_stat_info
, _("Size differs"));
239 diff_handle
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
243 open_error (current_stat_info
.file_name
);
245 report_difference (¤t_stat_info
, NULL
);
251 if (current_stat_info
.is_sparse
)
252 sparse_diff_file (diff_handle
, ¤t_stat_info
);
254 read_and_process (¤t_stat_info
, process_rawdata
);
256 status
= close (diff_handle
);
258 close_error (current_stat_info
.file_name
);
260 if (atime_preserve_option
)
262 struct timespec ts
[2];
263 ts
[0] = get_stat_atime (&stat_data
);
264 ts
[1] = get_stat_mtime (&stat_data
);
265 if (utimens (current_stat_info
.file_name
, ts
) != 0)
266 utime_error (current_stat_info
.file_name
);
276 struct stat file_data
;
277 struct stat link_data
;
279 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
280 && get_stat_data (current_stat_info
.link_name
, &link_data
)
281 && !sys_compare_links (&file_data
, &link_data
))
282 report_difference (¤t_stat_info
,
283 _("Not linked to %s"),
284 quote (current_stat_info
.link_name
));
291 size_t len
= strlen (current_stat_info
.link_name
);
292 char *linkbuf
= alloca (len
+ 1);
294 int status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
299 readlink_warn (current_stat_info
.file_name
);
301 readlink_error (current_stat_info
.file_name
);
302 report_difference (¤t_stat_info
, NULL
);
304 else if (status
!= len
305 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
306 report_difference (¤t_stat_info
, _("Symlink differs"));
313 struct stat stat_data
;
315 /* FIXME: deal with umask. */
317 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
320 if (current_header
->header
.typeflag
== CHRTYPE
321 ? !S_ISCHR (stat_data
.st_mode
)
322 : current_header
->header
.typeflag
== BLKTYPE
323 ? !S_ISBLK (stat_data
.st_mode
)
324 : /* current_header->header.typeflag == FIFOTYPE */
325 !S_ISFIFO (stat_data
.st_mode
))
327 report_difference (¤t_stat_info
, _("File type differs"));
331 if ((current_header
->header
.typeflag
== CHRTYPE
332 || current_header
->header
.typeflag
== BLKTYPE
)
333 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
335 report_difference (¤t_stat_info
, _("Device number differs"));
339 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
340 (stat_data
.st_mode
& MODE_ALL
))
341 report_difference (¤t_stat_info
, _("Mode differs"));
347 char *dumpdir_buffer
;
351 if (deref_stat (true, current_stat_info
.file_name
, &stat
))
354 stat_warn (current_stat_info
.file_name
);
356 stat_error (current_stat_info
.file_name
);
361 dumpdir_buffer
= get_directory_contents (current_stat_info
.file_name
, dev
);
365 dumpdir_cursor
= dumpdir_buffer
;
366 read_and_process (¤t_stat_info
, process_dumpdir
);
367 free (dumpdir_buffer
);
370 read_and_process (¤t_stat_info
, process_noop
);
376 struct stat stat_data
;
380 if (current_stat_info
.had_trailing_slash
)
386 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
389 if (!S_ISREG (stat_data
.st_mode
))
391 report_difference (¤t_stat_info
, _("File type differs"));
396 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
397 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
399 report_difference (¤t_stat_info
, _("Size differs"));
404 fd
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
408 open_error (current_stat_info
.file_name
);
409 report_difference (¤t_stat_info
, NULL
);
414 if (lseek (fd
, offset
, SEEK_SET
) < 0)
416 seek_error_details (current_stat_info
.file_name
, offset
);
417 report_difference (¤t_stat_info
, NULL
);
421 read_and_process (¤t_stat_info
, process_rawdata
);
425 close_error (current_stat_info
.file_name
);
428 /* Diff a file against the archive. */
433 set_next_block_after (current_header
);
434 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
436 /* Print the block from current_header and current_stat_info. */
441 fprintf (stdlis
, _("Verify "));
442 print_header (¤t_stat_info
, -1);
445 switch (current_header
->header
.typeflag
)
448 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
449 quotearg_colon (current_stat_info
.file_name
),
450 current_header
->header
.typeflag
));
458 /* Appears to be a file. See if it's really a directory. */
460 if (current_stat_info
.had_trailing_slash
)
482 case GNUTYPE_DUMPDIR
:
493 case GNUTYPE_MULTIVOL
:
501 if (removed_prefixes_p ())
504 _("Archive contains file names with leading prefixes removed.")));
506 _("Verification may fail to locate original files.")));
512 /* Verifying an archive is meant to check if the physical media got it
513 correctly, so try to defeat clever in-memory buffering pertaining to
514 this particular media. On Linux, for example, the floppy drive would
515 not even be accessed for the whole verification.
517 The code was using fsync only when the ioctl is unavailable, but
518 Marty Leisner says that the ioctl does not work when not preceded by
519 fsync. So, until we know better, or maybe to please Marty, let's do it
520 the unbelievable way :-). */
526 ioctl (archive
, FDFLUSH
);
531 struct mtop operation
;
534 operation
.mt_op
= MTBSF
;
535 operation
.mt_count
= 1;
536 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
539 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
543 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
545 /* Lseek failed. Try a different method. */
546 seek_warn (archive_name_array
[0]);
555 access_mode
= ACCESS_READ
;
561 enum read_header status
= read_header (false);
563 if (status
== HEADER_FAILURE
)
570 set_next_block_after (current_header
);
571 status
= read_header (false);
573 while (status
== HEADER_FAILURE
);
576 ngettext ("VERIFY FAILURE: %d invalid header detected",
577 "VERIFY FAILURE: %d invalid headers detected",
580 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
584 tar_stat_destroy (¤t_stat_info
);
585 xheader_destroy (&extended_header
);
588 access_mode
= ACCESS_WRITE
;