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 (dereference_option
, 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"));
221 (atime_preserve_option
== system_atime_preserve
225 diff_handle
= openat (chdir_fd
, file_name
,
226 (O_RDONLY
| O_BINARY
| O_CLOEXEC
| O_NOCTTY
227 | O_NONBLOCK
| atime_flag
));
231 open_error (file_name
);
233 report_difference (¤t_stat_info
, NULL
);
239 if (current_stat_info
.is_sparse
)
240 sparse_diff_file (diff_handle
, ¤t_stat_info
);
242 read_and_process (¤t_stat_info
, process_rawdata
);
244 if (atime_preserve_option
== replace_atime_preserve
)
246 struct timespec atime
= get_stat_atime (&stat_data
);
247 if (set_file_atime (diff_handle
, chdir_fd
, file_name
,
250 utime_error (file_name
);
253 status
= close (diff_handle
);
255 close_error (file_name
);
264 struct stat file_data
;
265 struct stat link_data
;
267 if (get_stat_data (current_stat_info
.file_name
, &file_data
)
268 && get_stat_data (current_stat_info
.link_name
, &link_data
)
269 && !sys_compare_links (&file_data
, &link_data
))
270 report_difference (¤t_stat_info
,
271 _("Not linked to %s"),
272 quote (current_stat_info
.link_name
));
279 size_t len
= strlen (current_stat_info
.link_name
);
280 char *linkbuf
= alloca (len
+ 1);
282 int status
= readlinkat (chdir_fd
, current_stat_info
.file_name
,
288 readlink_warn (current_stat_info
.file_name
);
290 readlink_error (current_stat_info
.file_name
);
291 report_difference (¤t_stat_info
, NULL
);
293 else if (status
!= len
294 || strncmp (current_stat_info
.link_name
, linkbuf
, len
) != 0)
295 report_difference (¤t_stat_info
, _("Symlink differs"));
302 struct stat stat_data
;
304 /* FIXME: deal with umask. */
306 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
309 if (current_header
->header
.typeflag
== CHRTYPE
310 ? !S_ISCHR (stat_data
.st_mode
)
311 : current_header
->header
.typeflag
== BLKTYPE
312 ? !S_ISBLK (stat_data
.st_mode
)
313 : /* current_header->header.typeflag == FIFOTYPE */
314 !S_ISFIFO (stat_data
.st_mode
))
316 report_difference (¤t_stat_info
, _("File type differs"));
320 if ((current_header
->header
.typeflag
== CHRTYPE
321 || current_header
->header
.typeflag
== BLKTYPE
)
322 && current_stat_info
.stat
.st_rdev
!= stat_data
.st_rdev
)
324 report_difference (¤t_stat_info
, _("Device number differs"));
328 if ((current_stat_info
.stat
.st_mode
& MODE_ALL
) !=
329 (stat_data
.st_mode
& MODE_ALL
))
330 report_difference (¤t_stat_info
, _("Mode differs"));
334 dumpdir_cmp (const char *a
, const char *b
)
343 if (!strchr ("YN", *b
))
345 if (strcmp(a
+ 1, b
+ 1))
347 len
= strlen (a
) + 1;
355 len
= strlen (a
) + 1;
371 const char *dumpdir_buffer
;
373 struct stat stat_data
;
375 if (deref_stat (true, current_stat_info
.file_name
, &stat_data
))
378 stat_warn (current_stat_info
.file_name
);
380 stat_error (current_stat_info
.file_name
);
383 dev
= stat_data
.st_dev
;
385 dumpdir_buffer
= directory_contents (scan_directory (¤t_stat_info
));
389 if (dumpdir_cmp (current_stat_info
.dumpdir
, dumpdir_buffer
))
390 report_difference (¤t_stat_info
, _("Contents differ"));
393 read_and_process (¤t_stat_info
, process_noop
);
399 struct stat stat_data
;
403 (atime_preserve_option
== system_atime_preserve
407 if (current_stat_info
.had_trailing_slash
)
413 if (!get_stat_data (current_stat_info
.file_name
, &stat_data
))
416 if (!S_ISREG (stat_data
.st_mode
))
418 report_difference (¤t_stat_info
, _("File type differs"));
423 offset
= OFF_FROM_HEADER (current_header
->oldgnu_header
.offset
);
424 if (stat_data
.st_size
!= current_stat_info
.stat
.st_size
+ offset
)
426 report_difference (¤t_stat_info
, _("Size differs"));
432 fd
= openat (chdir_fd
, current_stat_info
.file_name
,
433 (O_RDONLY
| O_BINARY
| O_CLOEXEC
| O_NOCTTY
| O_NONBLOCK
438 open_error (current_stat_info
.file_name
);
439 report_difference (¤t_stat_info
, NULL
);
444 if (lseek (fd
, offset
, SEEK_SET
) < 0)
446 seek_error_details (current_stat_info
.file_name
, offset
);
447 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
))
521 case GNUTYPE_MULTIVOL
:
529 if (removed_prefixes_p ())
532 _("Archive contains file names with leading prefixes removed.")));
534 _("Verification may fail to locate original files.")));
540 /* Verifying an archive is meant to check if the physical media got it
541 correctly, so try to defeat clever in-memory buffering pertaining to
542 this particular media. On Linux, for example, the floppy drive would
543 not even be accessed for the whole verification.
545 The code was using fsync only when the ioctl is unavailable, but
546 Marty Leisner says that the ioctl does not work when not preceded by
547 fsync. So, until we know better, or maybe to please Marty, let's do it
548 the unbelievable way :-). */
554 ioctl (archive
, FDFLUSH
);
559 struct mtop operation
;
562 operation
.mt_op
= MTBSF
;
563 operation
.mt_count
= 1;
564 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
567 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
571 if (rmtlseek (archive
, (off_t
) 0, SEEK_SET
) != 0)
573 /* Lseek failed. Try a different method. */
574 seek_warn (archive_name_array
[0]);
583 access_mode
= ACCESS_READ
;
589 enum read_header status
= read_header (¤t_header
,
593 if (status
== HEADER_FAILURE
)
600 set_next_block_after (current_header
);
601 status
= read_header (¤t_header
, ¤t_stat_info
,
604 while (status
== HEADER_FAILURE
);
607 ngettext ("VERIFY FAILURE: %d invalid header detected",
608 "VERIFY FAILURE: %d invalid headers detected",
611 if (status
== HEADER_END_OF_FILE
)
613 if (status
== HEADER_ZERO_BLOCK
)
615 set_next_block_after (current_header
);
616 if (!ignore_zeros_option
)
618 char buf
[UINTMAX_STRSIZE_BOUND
];
620 status
= read_header (¤t_header
, ¤t_stat_info
,
622 if (status
== HEADER_ZERO_BLOCK
)
624 WARNOPT (WARN_ALONE_ZERO_BLOCK
,
625 (0, 0, _("A lone zero block at %s"),
626 STRINGIFY_BIGINT (current_block_ordinal (), buf
)));
631 tar_stat_destroy (¤t_stat_info
);
634 access_mode
= ACCESS_WRITE
;