1 /* Diff files from a tar archive.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003 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 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
35 # include <linux/fd.h>
44 /* Nonzero if we are verifying at the moment. */
47 /* File descriptor for the file we are diffing. */
48 static int diff_handle;
50 /* Area for reading file contents into. */
51 static char *diff_buffer;
53 /* Initialize for a diff operation. */
57 diff_buffer = valloc (record_size);
62 /* Sigh about something that differs by writing a MESSAGE to stdlis,
63 given MESSAGE is nonzero. Also set the exit status if not already. */
65 report_difference (const char *fmt, ...)
71 fprintf (stdlis, "%s: ", quotearg_colon (current_stat_info.file_name));
73 vfprintf (stdlis, fmt, ap);
75 fprintf (stdlis, "\n");
78 if (exit_status == TAREXIT_SUCCESS)
79 exit_status = TAREXIT_DIFFERS;
82 /* Take a buffer returned by read_and_process and do nothing with it. */
84 process_noop (size_t size, char *data)
86 /* Yes, I know. SIZE and DATA are unused in this function. Some
87 compilers may even report it. That's OK, just relax! */
92 process_rawdata (size_t bytes, char *buffer)
94 ssize_t status = safe_read (diff_handle, diff_buffer, bytes);
100 read_error (current_stat_info.file_name);
101 report_difference (NULL);
105 report_difference (ngettext ("Could only read %lu of %lu byte",
106 "Could only read %lu of %lu bytes",
108 (unsigned long) status, (unsigned long) bytes);
113 if (memcmp (buffer, diff_buffer, bytes))
115 report_difference (_("Contents differ"));
122 /* Directory contents, only for GNUTYPE_DUMPDIR. */
124 static char *dumpdir_cursor;
127 process_dumpdir (size_t bytes, char *buffer)
129 if (memcmp (buffer, dumpdir_cursor, bytes))
131 report_difference (_("Contents differ"));
135 dumpdir_cursor += bytes;
139 /* Some other routine wants SIZE bytes in the archive. For each chunk
140 of the archive, call PROCESSOR with the size of the chunk, and the
141 address of the chunk it can work with. The PROCESSOR should return
142 nonzero for success. It it return error once, continue skipping
143 without calling PROCESSOR anymore. */
145 read_and_process (off_t size, int (*processor) (size_t, char *))
147 union block *data_block;
150 if (multi_volume_option)
151 save_sizeleft = size;
154 data_block = find_next_block ();
157 ERROR ((0, 0, _("Unexpected EOF in archive")));
161 data_size = available_space_after (data_block);
162 if (data_size > size)
164 if (!(*processor) (data_size, data_block->buffer))
165 processor = process_noop;
166 set_next_block_after ((union block *)
167 (data_block->buffer + data_size - 1));
169 if (multi_volume_option)
170 save_sizeleft -= data_size;
175 diff_sparse_files (void)
180 /* Call either stat or lstat over STAT_DATA, depending on
181 --dereference (-h), for a file which should exist. Diagnose any
182 problem. Return nonzero for success, zero otherwise. */
184 get_stat_data (char const *file_name, struct stat *stat_data)
186 int status = deref_stat (dereference_option, file_name, stat_data);
191 stat_warn (file_name);
193 stat_error (file_name);
194 report_difference (NULL);
201 /* Diff a file against the archive. */
205 struct stat stat_data;
207 struct utimbuf restore_times;
209 set_next_block_after (current_header);
210 decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
212 /* Print the block from current_header and current_stat_info. */
217 fprintf (stdlis, _("Verify "));
218 print_header (¤t_stat_info, -1);
221 switch (current_header->header.typeflag)
224 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
225 quotearg_colon (current_stat_info.file_name),
226 current_header->header.typeflag));
234 /* Appears to be a file. See if it's really a directory. */
236 if (current_stat_info.had_trailing_slash)
239 if (!get_stat_data (current_stat_info.file_name, &stat_data))
245 if (!S_ISREG (stat_data.st_mode))
247 report_difference (_("File type differs"));
252 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
253 report_difference (_("Mode differs"));
255 sys_compare_uid_gid (&stat_data, ¤t_stat_info.stat);
257 if (stat_data.st_mtime != current_stat_info.stat.st_mtime)
258 report_difference (_("Mod time differs"));
259 if (current_header->header.typeflag != GNUTYPE_SPARSE &&
260 stat_data.st_size != current_stat_info.stat.st_size)
262 report_difference (_("Size differs"));
267 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
271 open_error (current_stat_info.file_name);
273 report_difference (NULL);
277 restore_times.actime = stat_data.st_atime;
278 restore_times.modtime = stat_data.st_mtime;
280 /* Need to treat sparse files completely differently here. */
282 if (current_header->header.typeflag == GNUTYPE_SPARSE)
283 diff_sparse_files ();
286 if (multi_volume_option)
288 assign_string (&save_name, current_stat_info.file_name);
289 save_totsize = current_stat_info.stat.st_size;
290 /* save_sizeleft is set in read_and_process. */
293 read_and_process (current_stat_info.stat.st_size, process_rawdata);
295 if (multi_volume_option)
296 assign_string (&save_name, 0);
299 status = close (diff_handle);
301 close_error (current_stat_info.file_name);
303 if (atime_preserve_option)
304 utime (current_stat_info.file_name, &restore_times);
311 struct stat link_data, stat_data;
313 if (!get_stat_data (current_stat_info.file_name, &stat_data))
315 if (!get_stat_data (current_stat_info.link_name, &link_data))
317 sys_compare_links (&stat_data, &link_data);
324 size_t len = strlen (current_stat_info.link_name);
325 char *linkbuf = alloca (len + 1);
327 status = readlink (current_stat_info.file_name, linkbuf, len + 1);
332 readlink_warn (current_stat_info.file_name);
334 readlink_error (current_stat_info.file_name);
335 report_difference (NULL);
337 else if (status != len
338 || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
339 report_difference (_("Symlink differs"));
349 /* FIXME: deal with umask. */
351 if (!get_stat_data (current_stat_info.file_name, &stat_data))
354 if (current_header->header.typeflag == CHRTYPE
355 ? !S_ISCHR (stat_data.st_mode)
356 : current_header->header.typeflag == BLKTYPE
357 ? !S_ISBLK (stat_data.st_mode)
358 : /* current_header->header.typeflag == FIFOTYPE */
359 !S_ISFIFO (stat_data.st_mode))
361 report_difference (_("File type differs"));
365 if ((current_header->header.typeflag == CHRTYPE
366 || current_header->header.typeflag == BLKTYPE)
367 && current_stat_info.stat.st_rdev != stat_data.st_rdev)
369 report_difference (_("Device number differs"));
373 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
375 report_difference (_("Mode differs"));
381 case GNUTYPE_DUMPDIR:
383 char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name, 0);
385 if (multi_volume_option)
387 assign_string (&save_name, current_stat_info.file_name);
388 save_totsize = current_stat_info.stat.st_size;
389 /* save_sizeleft is set in read_and_process. */
394 dumpdir_cursor = dumpdir_buffer;
395 read_and_process (current_stat_info.stat.st_size, process_dumpdir);
396 free (dumpdir_buffer);
399 read_and_process (current_stat_info.stat.st_size, process_noop);
401 if (multi_volume_option)
402 assign_string (&save_name, 0);
408 if (!get_stat_data (current_stat_info.file_name, &stat_data))
411 if (!S_ISDIR (stat_data.st_mode))
413 report_difference (_("File type differs"));
417 if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
419 report_difference (_("Mode differs"));
428 case GNUTYPE_MULTIVOL:
432 if (current_stat_info.had_trailing_slash)
435 if (!get_stat_data (current_stat_info.file_name, &stat_data))
438 if (!S_ISREG (stat_data.st_mode))
440 report_difference (_("File type differs"));
445 offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
446 if (stat_data.st_size != current_stat_info.stat.st_size + offset)
448 report_difference (_("Size differs"));
453 diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
457 open_error (current_stat_info.file_name);
458 report_difference (NULL);
463 if (lseek (diff_handle, offset, SEEK_SET) < 0)
465 seek_error_details (current_stat_info.file_name, offset);
466 report_difference (NULL);
470 if (multi_volume_option)
472 assign_string (&save_name, current_stat_info.file_name);
473 save_totsize = stat_data.st_size;
474 /* save_sizeleft is set in read_and_process. */
477 read_and_process (current_stat_info.stat.st_size, process_rawdata);
479 if (multi_volume_option)
480 assign_string (&save_name, 0);
482 status = close (diff_handle);
484 close_error (current_stat_info.file_name);
497 /* Verifying an archive is meant to check if the physical media got it
498 correctly, so try to defeat clever in-memory buffering pertaining to
499 this particular media. On Linux, for example, the floppy drive would
500 not even be accessed for the whole verification.
502 The code was using fsync only when the ioctl is unavailable, but
503 Marty Leisner says that the ioctl does not work when not preceded by
504 fsync. So, until we know better, or maybe to please Marty, let's do it
505 the unbelievable way :-). */
511 ioctl (archive, FDFLUSH);
516 struct mtop operation;
519 operation.mt_op = MTBSF;
520 operation.mt_count = 1;
521 if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
524 || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
528 if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
530 /* Lseek failed. Try a different method. */
531 seek_warn (archive_name_array[0]);
540 access_mode = ACCESS_READ;
546 enum read_header status = read_header (false);
548 if (status == HEADER_FAILURE)
555 status = read_header (false);
557 while (status == HEADER_FAILURE);
560 ngettext ("VERIFY FAILURE: %d invalid header detected",
561 "VERIFY FAILURE: %d invalid headers detected",
564 if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
570 access_mode = ACCESS_WRITE;