1 /* Diff files from a tar archive.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003, 2004, 2005, 2006, 2007, 2009 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 3, 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. */
23 #include <system-ioctl.h>
26 # include <linux/fd.h>
34 /* Nonzero if we are verifying at the moment. */
37 /* File descriptor for the file we are diffing. */
38 static int diff_handle
;
40 /* Area for reading file contents into. */
41 static char *diff_buffer
;
43 /* Initialize for a diff operation. */
48 diff_buffer
= page_aligned_alloc (&ptr
, record_size
);
49 if (listed_incremental_option
)
50 read_directory_file ();
53 /* Sigh about something that differs by writing a MESSAGE to stdlis,
54 given MESSAGE is nonzero. Also set the exit status if not already. */
56 report_difference (struct tar_stat_info
*st
, const char *fmt
, ...)
62 fprintf (stdlis
, "%s: ", quotearg_colon (st
->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 /* Some other routine wants SIZE bytes in the archive. For each chunk
114 of the archive, call PROCESSOR with the size of the chunk, and the
115 address of the chunk it can work with. The PROCESSOR should return
116 nonzero for success. Once it returns error, continue skipping
117 without calling PROCESSOR anymore. */
120 read_and_process (struct tar_stat_info
*st
, int (*processor
) (size_t, char *))
122 union block
*data_block
;
124 off_t size
= st
->stat
.st_size
;
129 data_block
= find_next_block ();
132 ERROR ((0, 0, _("Unexpected EOF in archive")));
136 data_size
= available_space_after (data_block
);
137 if (data_size
> size
)
139 if (!(*processor
) (data_size
, data_block
->buffer
))
140 processor
= process_noop
;
141 set_next_block_after ((union block
*)
142 (data_block
->buffer
+ data_size
- 1));
149 /* Call either stat or lstat over STAT_DATA, depending on
150 --dereference (-h), for a file which should exist. Diagnose any
151 problem. Return nonzero for success, zero otherwise. */
153 get_stat_data (char const *file_name
, struct stat
*stat_data
)
155 int status
= deref_stat (dereference_option
, file_name
, stat_data
);
160 stat_warn (file_name
);
162 stat_error (file_name
);
163 report_difference (¤t_stat_info
, NULL
);
174 struct stat stat_data
;
176 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
179 if (!S_ISDIR (stat_data
.st_mode
))
180 report_difference (¤t_stat_info
, _("File type differs"));
181 else if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
182 (stat_data
.st_mode
& MODE_ALL
))
183 report_difference (¤t_stat_info
, _("Mode differs"));
189 char const *file_name
= current_stat_info
.file_name
;
190 struct stat stat_data
;
192 if (!get_stat_data (file_name
, &stat_data
))
194 else if (!S_ISREG (stat_data
.st_mode
))
196 report_difference (¤t_stat_info
, _("File type differs"));
201 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
202 (stat_data
.st_mode
& MODE_ALL
))
203 report_difference (¤t_stat_info
, _("Mode differs"));
205 if (!sys_compare_uid (&stat_data
, ¤t_stat_info
.stat
))
206 report_difference (¤t_stat_info
, _("Uid differs"));
207 if (!sys_compare_gid (&stat_data
, ¤t_stat_info
.stat
))
208 report_difference (¤t_stat_info
, _("Gid differs"));
210 if (tar_timespec_cmp (get_stat_mtime (&stat_data
),
211 current_stat_info
.mtime
))
212 report_difference (¤t_stat_info
, _("Mod time differs"));
213 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
214 && stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
216 report_difference (¤t_stat_info
, _("Size differs"));
222 (atime_preserve_option
== system_atime_preserve
226 diff_handle
= open (file_name
, O_RDONLY
| O_BINARY
| atime_flag
);
230 open_error (file_name
);
232 report_difference (¤t_stat_info
, NULL
);
238 if (current_stat_info
.is_sparse
)
239 sparse_diff_file (diff_handle
, ¤t_stat_info
);
241 read_and_process (¤t_stat_info
, process_rawdata
);
243 if (atime_preserve_option
== replace_atime_preserve
)
245 struct timespec ts
[2];
246 ts
[0] = get_stat_atime (&stat_data
);
247 ts
[1] = get_stat_mtime (&stat_data
);
248 if (set_file_atime (diff_handle
, file_name
, ts
) != 0)
249 utime_error (file_name
);
252 status
= close (diff_handle
);
254 close_error (file_name
);
263 struct stat file_data
;
264 struct stat link_data
;
266 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
267 && get_stat_data (current_stat_info
.link_name
, &link_data
)
268 && !sys_compare_links (&file_data
, &link_data
))
269 report_difference (¤t_stat_info
,
270 _("Not linked to %s"),
271 quote (current_stat_info
.link_name
));
278 size_t len
= strlen (current_stat_info
.link_name
);
279 char *linkbuf
= alloca (len
+ 1);
281 int status
= readlink (current_stat_info
.file_name
, linkbuf
, len
+ 1);
286 readlink_warn (current_stat_info
.file_name
);
288 readlink_error (current_stat_info
.file_name
);
289 report_difference (¤t_stat_info
, NULL
);
291 else if (status
!= len
292 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
293 report_difference (¤t_stat_info
, _("Symlink differs"));
300 struct stat stat_data
;
302 /* FIXME: deal with umask. */
304 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
307 if (current_header
->header
.typeflag
== CHRTYPE
308 ? !S_ISCHR (stat_data
.st_mode
)
309 : current_header
->header
.typeflag
== BLKTYPE
310 ? !S_ISBLK (stat_data
.st_mode
)
311 : /* current_header->header.typeflag == FIFOTYPE */
312 !S_ISFIFO (stat_data
.st_mode
))
314 report_difference (¤t_stat_info
, _("File type differs"));
318 if ((current_header
->header
.typeflag
== CHRTYPE
319 || current_header
->header
.typeflag
== BLKTYPE
)
320 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
322 report_difference (¤t_stat_info
, _("Device number differs"));
326 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
327 (stat_data
.st_mode
& MODE_ALL
))
328 report_difference (¤t_stat_info
, _("Mode differs"));
332 dumpdir_cmp (const char *a
, const char *b
)
341 if (!strchr ("YN", *b
))
343 if (strcmp(a
+ 1, b
+ 1))
345 len
= strlen (a
) + 1;
353 len
= strlen (a
) + 1;
369 const char *dumpdir_buffer
;
371 struct stat stat_data
;
373 if (deref_stat (true, current_stat_info
.file_name
, &stat_data
))
376 stat_warn (current_stat_info
.file_name
);
378 stat_error (current_stat_info
.file_name
);
381 dev
= stat_data
.st_dev
;
383 dumpdir_buffer
= directory_contents
384 (scan_directory (current_stat_info
.file_name
, dev
, false));
388 if (dumpdir_cmp (current_stat_info
.dumpdir
, dumpdir_buffer
))
389 report_difference (¤t_stat_info
, _("Contents differ"));
392 read_and_process (¤t_stat_info
, process_noop
);
398 struct stat stat_data
;
402 if (current_stat_info
.had_trailing_slash
)
408 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
411 if (!S_ISREG (stat_data
.st_mode
))
413 report_difference (¤t_stat_info
, _("File type differs"));
418 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
419 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
421 report_difference (¤t_stat_info
, _("Size differs"));
426 fd
= open (current_stat_info
.file_name
, O_RDONLY
| O_BINARY
);
430 open_error (current_stat_info
.file_name
);
431 report_difference (¤t_stat_info
, NULL
);
436 if (lseek (fd
, offset
, SEEK_SET
) < 0)
438 seek_error_details (current_stat_info
.file_name
, offset
);
439 report_difference (¤t_stat_info
, NULL
);
443 read_and_process (¤t_stat_info
, process_rawdata
);
447 close_error (current_stat_info
.file_name
);
450 /* Diff a file against the archive. */
455 set_next_block_after (current_header
);
456 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
458 /* Print the block from current_header and current_stat_info. */
463 fprintf (stdlis
, _("Verify "));
464 print_header (¤t_stat_info
, -1);
467 switch (current_header
->header
.typeflag
)
470 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
471 quotearg_colon (current_stat_info
.file_name
),
472 current_header
->header
.typeflag
));
480 /* Appears to be a file. See if it's really a directory. */
482 if (current_stat_info
.had_trailing_slash
)
504 case GNUTYPE_DUMPDIR
:
506 if (is_dumpdir (¤t_stat_info
))
514 case GNUTYPE_MULTIVOL
:
522 if (removed_prefixes_p ())
525 _("Archive contains file names with leading prefixes removed.")));
527 _("Verification may fail to locate original files.")));
533 /* Verifying an archive is meant to check if the physical media got it
534 correctly, so try to defeat clever in-memory buffering pertaining to
535 this particular media. On Linux, for example, the floppy drive would
536 not even be accessed for the whole verification.
538 The code was using fsync only when the ioctl is unavailable, but
539 Marty Leisner says that the ioctl does not work when not preceded by
540 fsync. So, until we know better, or maybe to please Marty, let's do it
541 the unbelievable way :-). */
547 ioctl (archive
, FDFLUSH
);
552 struct mtop operation
;
555 operation
.mt_op
= MTBSF
;
556 operation
.mt_count
= 1;
557 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
560 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
564 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
566 /* Lseek failed. Try a different method. */
567 seek_warn (archive_name_array
[0]);
576 access_mode
= ACCESS_READ
;
582 enum read_header status
= read_header (false);
584 if (status
== HEADER_FAILURE
)
591 set_next_block_after (current_header
);
592 status
= read_header (false);
594 while (status
== HEADER_FAILURE
);
597 ngettext ("VERIFY FAILURE: %d invalid header detected",
598 "VERIFY FAILURE: %d invalid headers detected",
601 if (status
== HEADER_END_OF_FILE
)
603 if (status
== HEADER_ZERO_BLOCK
)
605 set_next_block_after (current_header
);
606 if (!ignore_zeros_option
)
608 char buf
[UINTMAX_STRSIZE_BOUND
];
610 status
= read_header (false);
611 if (status
== HEADER_ZERO_BLOCK
)
613 WARNOPT (WARN_ALONE_ZERO_BLOCK
,
614 (0, 0, _("A lone zero block at %s"),
615 STRINGIFY_BIGINT (current_block_ordinal (), buf
)));
620 tar_stat_destroy (¤t_stat_info
);
623 access_mode
= ACCESS_WRITE
;