1 /* Diff files from a tar archive.
3 Copyright 1988, 1992-1994, 1996-1997, 1999-2001, 2003-2007,
4 2009-2010, 2012-2014 Free Software Foundation, Inc.
6 This file is part of GNU tar.
8 GNU tar is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 GNU tar is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 Written by John Gilmore, on 1987-04-30. */
24 #include <system-ioctl.h>
27 # 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
, const char *fmt
, ...)
63 fprintf (stdlis
, "%s: ", quotearg_colon (st
->file_name
));
65 vfprintf (stdlis
, fmt
, ap
);
67 fprintf (stdlis
, "\n");
70 set_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
= blocking_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 (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"));
221 diff_handle
= openat (chdir_fd
, file_name
, open_read_flags
);
225 open_error (file_name
);
227 report_difference (¤t_stat_info
, NULL
);
233 if (current_stat_info
.is_sparse
)
234 sparse_diff_file (diff_handle
, ¤t_stat_info
);
236 read_and_process (¤t_stat_info
, process_rawdata
);
238 if (atime_preserve_option
== replace_atime_preserve
239 && stat_data
.st_size
!= 0)
241 struct timespec atime
= get_stat_atime (&stat_data
);
242 if (set_file_atime (diff_handle
, chdir_fd
, file_name
, atime
)
244 utime_error (file_name
);
247 status
= close (diff_handle
);
249 close_error (file_name
);
258 struct stat file_data
;
259 struct stat link_data
;
261 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
262 && get_stat_data (current_stat_info
.link_name
, &link_data
)
263 && !sys_compare_links (&file_data
, &link_data
))
264 report_difference (¤t_stat_info
,
265 _("Not linked to %s"),
266 quote (current_stat_info
.link_name
));
274 size_t len
= strlen (current_stat_info
.link_name
);
275 char *linkbuf
= len
< sizeof buf
? buf
: xmalloc (len
+ 1);
277 ssize_t status
= readlinkat (chdir_fd
, current_stat_info
.file_name
,
283 readlink_warn (current_stat_info
.file_name
);
285 readlink_error (current_stat_info
.file_name
);
286 report_difference (¤t_stat_info
, NULL
);
288 else if (status
!= len
289 || memcmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
290 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;
367 diff_dumpdir (struct tar_stat_info
*dir
)
369 const char *dumpdir_buffer
;
373 void (*diag
) (char const *) = NULL
;
374 int fd
= subfile_open (dir
->parent
, dir
->orig_file_name
, open_read_flags
);
377 else if (fstat (fd
, &dir
->stat
))
386 file_removed_diag (dir
->orig_file_name
, false, diag
);
390 dumpdir_buffer
= directory_contents (scan_directory (dir
));
394 if (dumpdir_cmp (dir
->dumpdir
, dumpdir_buffer
))
395 report_difference (dir
, _("Contents differ"));
398 read_and_process (dir
, process_noop
);
404 struct stat stat_data
;
408 if (current_stat_info
.had_trailing_slash
)
414 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
417 if (!S_ISREG (stat_data
.st_mode
))
419 report_difference (¤t_stat_info
, _("File type differs"));
424 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
426 || INT_ADD_OVERFLOW (current_stat_info
.stat
.st_size
, offset
)
427 || stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
429 report_difference (¤t_stat_info
, _("Size differs"));
435 fd
= openat (chdir_fd
, current_stat_info
.file_name
, open_read_flags
);
439 open_error (current_stat_info
.file_name
);
440 report_difference (¤t_stat_info
, NULL
);
445 if (lseek (fd
, offset
, SEEK_SET
) < 0)
447 seek_error_details (current_stat_info
.file_name
, offset
);
448 report_difference (¤t_stat_info
, NULL
);
451 read_and_process (¤t_stat_info
, process_rawdata
);
455 close_error (current_stat_info
.file_name
);
458 /* Diff a file against the archive. */
463 set_next_block_after (current_header
);
465 /* Print the block from current_header and current_stat_info. */
470 fprintf (stdlis
, _("Verify "));
471 print_header (¤t_stat_info
, current_header
, -1);
474 switch (current_header
->header
.typeflag
)
477 ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
478 quotearg_colon (current_stat_info
.file_name
),
479 current_header
->header
.typeflag
));
487 /* Appears to be a file. See if it's really a directory. */
489 if (current_stat_info
.had_trailing_slash
)
511 case GNUTYPE_DUMPDIR
:
513 if (is_dumpdir (¤t_stat_info
))
514 diff_dumpdir (¤t_stat_info
);
521 case GNUTYPE_MULTIVOL
:
530 if (removed_prefixes_p ())
533 _("Archive contains file names with leading prefixes removed.")));
536 if (transform_program_p ())
539 _("Archive contains transformed file names.")));
544 _("Verification may fail to locate original files.")));
546 clear_directory_table ();
551 /* Verifying an archive is meant to check if the physical media got it
552 correctly, so try to defeat clever in-memory buffering pertaining to
553 this particular media. On Linux, for example, the floppy drive would
554 not even be accessed for the whole verification.
556 The code was using fsync only when the ioctl is unavailable, but
557 Marty Leisner says that the ioctl does not work when not preceded by
558 fsync. So, until we know better, or maybe to please Marty, let's do it
559 the unbelievable way :-). */
565 ioctl (archive
, FDFLUSH
);
570 struct mtop operation
;
573 operation
.mt_op
= MTBSF
;
574 operation
.mt_count
= 1;
575 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
578 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
582 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
584 /* Lseek failed. Try a different method. */
585 seek_warn (archive_name_array
[0]);
594 access_mode
= ACCESS_READ
;
600 enum read_header status
= read_header (¤t_header
,
604 if (status
== HEADER_FAILURE
)
611 set_next_block_after (current_header
);
612 status
= read_header (¤t_header
, ¤t_stat_info
,
615 while (status
== HEADER_FAILURE
);
618 ngettext ("VERIFY FAILURE: %d invalid header detected",
619 "VERIFY FAILURE: %d invalid headers detected",
622 if (status
== HEADER_END_OF_FILE
)
624 if (status
== HEADER_ZERO_BLOCK
)
626 set_next_block_after (current_header
);
627 if (!ignore_zeros_option
)
629 char buf
[UINTMAX_STRSIZE_BOUND
];
631 status
= read_header (¤t_header
, ¤t_stat_info
,
633 if (status
== HEADER_ZERO_BLOCK
)
635 WARNOPT (WARN_ALONE_ZERO_BLOCK
,
636 (0, 0, _("A lone zero block at %s"),
637 STRINGIFY_BIGINT (current_block_ordinal (), buf
)));
642 decode_header (current_header
, ¤t_stat_info
, ¤t_format
, 1);
644 tar_stat_destroy (¤t_stat_info
);
647 access_mode
= ACCESS_WRITE
;