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, 2010 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 set_exit_status (TAREXIT_DIFFERS
);
72 /* Take a buffer returned by read_and_process and do nothing with it. */
74 process_noop (size_t size
__attribute__ ((unused
)),
75 char *data
__attribute__ ((unused
)))
81 process_rawdata (size_t bytes
, char *buffer
)
83 size_t status
= safe_read (diff_handle
, diff_buffer
, bytes
);
87 if (status
== SAFE_READ_ERROR
)
89 read_error (current_stat_info
.file_name
);
90 report_difference (¤t_stat_info
, NULL
);
94 report_difference (¤t_stat_info
,
95 ngettext ("Could only read %lu of %lu byte",
96 "Could only read %lu of %lu bytes",
98 (unsigned long) status
, (unsigned long) bytes
);
103 if (memcmp (buffer
, diff_buffer
, bytes
))
105 report_difference (¤t_stat_info
, _("Contents differ"));
112 /* Some other routine wants SIZE bytes in the archive. For each chunk
113 of the archive, call PROCESSOR with the size of the chunk, and the
114 address of the chunk it can work with. The PROCESSOR should return
115 nonzero for success. Once it returns error, continue skipping
116 without calling PROCESSOR anymore. */
119 read_and_process (struct tar_stat_info
*st
, int (*processor
) (size_t, char *))
121 union block
*data_block
;
123 off_t size
= st
->stat
.st_size
;
128 data_block
= find_next_block ();
131 ERROR ((0, 0, _("Unexpected EOF in archive")));
135 data_size
= available_space_after (data_block
);
136 if (data_size
> size
)
138 if (!(*processor
) (data_size
, data_block
->buffer
))
139 processor
= process_noop
;
140 set_next_block_after ((union block
*)
141 (data_block
->buffer
+ data_size
- 1));
148 /* Call either stat or lstat over STAT_DATA, depending on
149 --dereference (-h), for a file which should exist. Diagnose any
150 problem. Return nonzero for success, zero otherwise. */
152 get_stat_data (char const *file_name
, struct stat
*stat_data
)
154 int status
= deref_stat (file_name
, stat_data
);
159 stat_warn (file_name
);
161 stat_error (file_name
);
162 report_difference (¤t_stat_info
, NULL
);
173 struct stat stat_data
;
175 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
178 if (!S_ISDIR (stat_data
.st_mode
))
179 report_difference (¤t_stat_info
, _("File type differs"));
180 else if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
181 (stat_data
.st_mode
& MODE_ALL
))
182 report_difference (¤t_stat_info
, _("Mode differs"));
188 char const *file_name
= current_stat_info
.file_name
;
189 struct stat stat_data
;
191 if (!get_stat_data (file_name
, &stat_data
))
193 else if (!S_ISREG (stat_data
.st_mode
))
195 report_difference (¤t_stat_info
, _("File type differs"));
200 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
201 (stat_data
.st_mode
& MODE_ALL
))
202 report_difference (¤t_stat_info
, _("Mode differs"));
204 if (!sys_compare_uid (&stat_data
, ¤t_stat_info
.stat
))
205 report_difference (¤t_stat_info
, _("Uid differs"));
206 if (!sys_compare_gid (&stat_data
, ¤t_stat_info
.stat
))
207 report_difference (¤t_stat_info
, _("Gid differs"));
209 if (tar_timespec_cmp (get_stat_mtime (&stat_data
),
210 current_stat_info
.mtime
))
211 report_difference (¤t_stat_info
, _("Mod time differs"));
212 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
213 && stat_data
.st_size
!= current_stat_info
.stat
.st_size
)
215 report_difference (¤t_stat_info
, _("Size differs"));
220 diff_handle
= openat (chdir_fd
, file_name
, open_read_flags
);
224 open_error (file_name
);
226 report_difference (¤t_stat_info
, NULL
);
232 if (current_stat_info
.is_sparse
)
233 sparse_diff_file (diff_handle
, ¤t_stat_info
);
235 read_and_process (¤t_stat_info
, process_rawdata
);
237 if (atime_preserve_option
== replace_atime_preserve
238 && stat_data
.st_size
!= 0)
240 struct timespec atime
= get_stat_atime (&stat_data
);
241 if (set_file_atime (diff_handle
, chdir_fd
, file_name
, atime
)
243 utime_error (file_name
);
246 status
= close (diff_handle
);
248 close_error (file_name
);
257 struct stat file_data
;
258 struct stat link_data
;
260 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
261 && get_stat_data (current_stat_info
.link_name
, &link_data
)
262 && !sys_compare_links (&file_data
, &link_data
))
263 report_difference (¤t_stat_info
,
264 _("Not linked to %s"),
265 quote (current_stat_info
.link_name
));
272 size_t len
= strlen (current_stat_info
.link_name
);
273 char *linkbuf
= alloca (len
+ 1);
275 int status
= readlinkat (chdir_fd
, current_stat_info
.file_name
,
281 readlink_warn (current_stat_info
.file_name
);
283 readlink_error (current_stat_info
.file_name
);
284 report_difference (¤t_stat_info
, NULL
);
286 else if (status
!= len
287 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
288 report_difference (¤t_stat_info
, _("Symlink differs"));
295 struct stat stat_data
;
297 /* FIXME: deal with umask. */
299 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
302 if (current_header
->header
.typeflag
== CHRTYPE
303 ? !S_ISCHR (stat_data
.st_mode
)
304 : current_header
->header
.typeflag
== BLKTYPE
305 ? !S_ISBLK (stat_data
.st_mode
)
306 : /* current_header->header.typeflag == FIFOTYPE */
307 !S_ISFIFO (stat_data
.st_mode
))
309 report_difference (¤t_stat_info
, _("File type differs"));
313 if ((current_header
->header
.typeflag
== CHRTYPE
314 || current_header
->header
.typeflag
== BLKTYPE
)
315 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
317 report_difference (¤t_stat_info
, _("Device number differs"));
321 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
322 (stat_data
.st_mode
& MODE_ALL
))
323 report_difference (¤t_stat_info
, _("Mode differs"));
327 dumpdir_cmp (const char *a
, const char *b
)
336 if (!strchr ("YN", *b
))
338 if (strcmp(a
+ 1, b
+ 1))
340 len
= strlen (a
) + 1;
348 len
= strlen (a
) + 1;
362 diff_dumpdir (struct tar_stat_info
*dir
)
364 const char *dumpdir_buffer
;
366 struct stat stat_data
;
368 if (deref_stat (dir
->file_name
, &stat_data
) != 0)
371 stat_warn (dir
->file_name
);
373 stat_error (dir
->file_name
);
376 dev
= stat_data
.st_dev
;
380 void (*diag
) (char const *) = NULL
;
381 int fd
= subfile_open (dir
->parent
, dir
->orig_file_name
, open_read_flags
);
384 else if (fstat (fd
, &dir
->stat
))
390 file_removed_diag (dir
->orig_file_name
, false, diag
);
394 dumpdir_buffer
= directory_contents (scan_directory (dir
));
398 if (dumpdir_cmp (dir
->dumpdir
, dumpdir_buffer
))
399 report_difference (dir
, _("Contents differ"));
402 read_and_process (dir
, process_noop
);
408 struct stat stat_data
;
412 if (current_stat_info
.had_trailing_slash
)
418 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
421 if (!S_ISREG (stat_data
.st_mode
))
423 report_difference (¤t_stat_info
, _("File type differs"));
428 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
429 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
431 report_difference (¤t_stat_info
, _("Size differs"));
437 fd
= openat (chdir_fd
, current_stat_info
.file_name
, open_read_flags
);
441 open_error (current_stat_info
.file_name
);
442 report_difference (¤t_stat_info
, NULL
);
447 if (lseek (fd
, offset
, SEEK_SET
) < 0)
449 seek_error_details (current_stat_info
.file_name
, offset
);
450 report_difference (¤t_stat_info
, NULL
);
454 read_and_process (¤t_stat_info
, process_rawdata
);
458 close_error (current_stat_info
.file_name
);
461 /* Diff a file against the archive. */
466 set_next_block_after (current_header
);
468 /* Print the block from current_header and current_stat_info. */
473 fprintf (stdlis
, _("Verify "));
474 print_header (¤t_stat_info
, current_header
, -1);
477 switch (current_header
->header
.typeflag
)
480 ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
481 quotearg_colon (current_stat_info
.file_name
),
482 current_header
->header
.typeflag
));
490 /* Appears to be a file. See if it's really a directory. */
492 if (current_stat_info
.had_trailing_slash
)
514 case GNUTYPE_DUMPDIR
:
516 if (is_dumpdir (¤t_stat_info
))
517 diff_dumpdir (¤t_stat_info
);
524 case GNUTYPE_MULTIVOL
:
533 if (removed_prefixes_p ())
536 _("Archive contains file names with leading prefixes removed.")));
539 if (transform_program_p ())
542 _("Archive contains transformed file names.")));
547 _("Verification may fail to locate original files.")));
549 clear_directory_table ();
554 /* Verifying an archive is meant to check if the physical media got it
555 correctly, so try to defeat clever in-memory buffering pertaining to
556 this particular media. On Linux, for example, the floppy drive would
557 not even be accessed for the whole verification.
559 The code was using fsync only when the ioctl is unavailable, but
560 Marty Leisner says that the ioctl does not work when not preceded by
561 fsync. So, until we know better, or maybe to please Marty, let's do it
562 the unbelievable way :-). */
568 ioctl (archive
, FDFLUSH
);
573 struct mtop operation
;
576 operation
.mt_op
= MTBSF
;
577 operation
.mt_count
= 1;
578 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
581 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
585 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
587 /* Lseek failed. Try a different method. */
588 seek_warn (archive_name_array
[0]);
597 access_mode
= ACCESS_READ
;
603 enum read_header status
= read_header (¤t_header
,
607 if (status
== HEADER_FAILURE
)
614 set_next_block_after (current_header
);
615 status
= read_header (¤t_header
, ¤t_stat_info
,
618 while (status
== HEADER_FAILURE
);
621 ngettext ("VERIFY FAILURE: %d invalid header detected",
622 "VERIFY FAILURE: %d invalid headers detected",
625 if (status
== HEADER_END_OF_FILE
)
627 if (status
== HEADER_ZERO_BLOCK
)
629 set_next_block_after (current_header
);
630 if (!ignore_zeros_option
)
632 char buf
[UINTMAX_STRSIZE_BOUND
];
634 status
= read_header (¤t_header
, ¤t_stat_info
,
636 if (status
== HEADER_ZERO_BLOCK
)
638 WARNOPT (WARN_ALONE_ZERO_BLOCK
,
639 (0, 0, _("A lone zero block at %s"),
640 STRINGIFY_BIGINT (current_block_ordinal (), buf
)));
645 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
647 tar_stat_destroy (¤t_stat_info
);
650 access_mode
= ACCESS_WRITE
;