Fix creation of incremental archives when a subdirectory becomes a mountpoint.
[debian/tar] / src / compare.c
1 /* Diff files from a tar archive.
2
3    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4    2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
5
6    Written by John Gilmore, on 1987-04-30.
7
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
11    version.
12
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.
17
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.  */
21
22 #include <system.h>
23 #include <system-ioctl.h>
24
25 #if HAVE_LINUX_FD_H
26 # include <linux/fd.h>
27 #endif
28
29 #include "common.h"
30 #include <quotearg.h>
31 #include <rmt.h>
32 #include <stdarg.h>
33
34 /* Nonzero if we are verifying at the moment.  */
35 bool now_verifying;
36
37 /* File descriptor for the file we are diffing.  */
38 static int diff_handle;
39
40 /* Area for reading file contents into.  */
41 static char *diff_buffer;
42
43 /* Initialize for a diff operation.  */
44 void
45 diff_init (void)
46 {
47   void *ptr;
48   diff_buffer = page_aligned_alloc (&ptr, record_size);
49   if (listed_incremental_option)
50     read_directory_file ();
51 }
52
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.  */
55 void
56 report_difference (struct tar_stat_info *st, const char *fmt, ...)
57 {
58   if (fmt)
59     {
60       va_list ap;
61
62       fprintf (stdlis, "%s: ", quotearg_colon (st->file_name));
63       va_start (ap, fmt);
64       vfprintf (stdlis, fmt, ap);
65       va_end (ap);
66       fprintf (stdlis, "\n");
67     }
68
69   set_exit_status (TAREXIT_DIFFERS);
70 }
71
72 /* Take a buffer returned by read_and_process and do nothing with it.  */
73 static int
74 process_noop (size_t size __attribute__ ((unused)),
75               char *data __attribute__ ((unused)))
76 {
77   return 1;
78 }
79
80 static int
81 process_rawdata (size_t bytes, char *buffer)
82 {
83   size_t status = blocking_read (diff_handle, diff_buffer, bytes);
84
85   if (status != bytes)
86     {
87       if (status == SAFE_READ_ERROR)
88         {
89           read_error (current_stat_info.file_name);
90           report_difference (&current_stat_info, NULL);
91         }
92       else
93         {
94           report_difference (&current_stat_info,
95                              ngettext ("Could only read %lu of %lu byte",
96                                        "Could only read %lu of %lu bytes",
97                                        bytes),
98                              (unsigned long) status, (unsigned long) bytes);
99         }
100       return 0;
101     }
102
103   if (memcmp (buffer, diff_buffer, bytes))
104     {
105       report_difference (&current_stat_info, _("Contents differ"));
106       return 0;
107     }
108
109   return 1;
110 }
111
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.  */
117
118 static void
119 read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
120 {
121   union block *data_block;
122   size_t data_size;
123   off_t size = st->stat.st_size;
124
125   mv_begin_read (st);
126   while (size)
127     {
128       data_block = find_next_block ();
129       if (! data_block)
130         {
131           ERROR ((0, 0, _("Unexpected EOF in archive")));
132           return;
133         }
134
135       data_size = available_space_after (data_block);
136       if (data_size > size)
137         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));
142       size -= data_size;
143       mv_size_left (size);
144     }
145   mv_end ();
146 }
147
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.  */
151 static int
152 get_stat_data (char const *file_name, struct stat *stat_data)
153 {
154   int status = deref_stat (file_name, stat_data);
155
156   if (status != 0)
157     {
158       if (errno == ENOENT)
159         stat_warn (file_name);
160       else
161         stat_error (file_name);
162       report_difference (&current_stat_info, NULL);
163       return 0;
164     }
165
166   return 1;
167 }
168
169 \f
170 static void
171 diff_dir (void)
172 {
173   struct stat stat_data;
174
175   if (!get_stat_data (current_stat_info.file_name, &stat_data))
176     return;
177
178   if (!S_ISDIR (stat_data.st_mode))
179     report_difference (&current_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 (&current_stat_info, _("Mode differs"));
183 }
184
185 static void
186 diff_file (void)
187 {
188   char const *file_name = current_stat_info.file_name;
189   struct stat stat_data;
190
191   if (!get_stat_data (file_name, &stat_data))
192     skip_member ();
193   else if (!S_ISREG (stat_data.st_mode))
194     {
195       report_difference (&current_stat_info, _("File type differs"));
196       skip_member ();
197     }
198   else
199     {
200       if ((current_stat_info.stat.st_mode & MODE_ALL) !=
201           (stat_data.st_mode & MODE_ALL))
202         report_difference (&current_stat_info, _("Mode differs"));
203
204       if (!sys_compare_uid (&stat_data, &current_stat_info.stat))
205         report_difference (&current_stat_info, _("Uid differs"));
206       if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
207         report_difference (&current_stat_info, _("Gid differs"));
208
209       if (tar_timespec_cmp (get_stat_mtime (&stat_data),
210                             current_stat_info.mtime))
211         report_difference (&current_stat_info, _("Mod time differs"));
212       if (current_header->header.typeflag != GNUTYPE_SPARSE
213           && stat_data.st_size != current_stat_info.stat.st_size)
214         {
215           report_difference (&current_stat_info, _("Size differs"));
216           skip_member ();
217         }
218       else
219         {
220           diff_handle = openat (chdir_fd, file_name, open_read_flags);
221
222           if (diff_handle < 0)
223             {
224               open_error (file_name);
225               skip_member ();
226               report_difference (&current_stat_info, NULL);
227             }
228           else
229             {
230               int status;
231
232               if (current_stat_info.is_sparse)
233                 sparse_diff_file (diff_handle, &current_stat_info);
234               else
235                 read_and_process (&current_stat_info, process_rawdata);
236
237               if (atime_preserve_option == replace_atime_preserve
238                   && stat_data.st_size != 0)
239                 {
240                   struct timespec atime = get_stat_atime (&stat_data);
241                   if (set_file_atime (diff_handle, chdir_fd, file_name, atime)
242                       != 0)
243                     utime_error (file_name);
244                 }
245
246               status = close (diff_handle);
247               if (status != 0)
248                 close_error (file_name);
249             }
250         }
251     }
252 }
253
254 static void
255 diff_link (void)
256 {
257   struct stat file_data;
258   struct stat link_data;
259
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 (&current_stat_info,
264                        _("Not linked to %s"),
265                        quote (current_stat_info.link_name));
266 }
267
268 #ifdef HAVE_READLINK
269 static void
270 diff_symlink (void)
271 {
272   size_t len = strlen (current_stat_info.link_name);
273   char *linkbuf = alloca (len + 1);
274
275   int status = readlinkat (chdir_fd, current_stat_info.file_name,
276                            linkbuf, len + 1);
277
278   if (status < 0)
279     {
280       if (errno == ENOENT)
281         readlink_warn (current_stat_info.file_name);
282       else
283         readlink_error (current_stat_info.file_name);
284       report_difference (&current_stat_info, NULL);
285     }
286   else if (status != len
287            || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
288     report_difference (&current_stat_info, _("Symlink differs"));
289 }
290 #endif
291
292 static void
293 diff_special (void)
294 {
295   struct stat stat_data;
296
297   /* FIXME: deal with umask.  */
298
299   if (!get_stat_data (current_stat_info.file_name, &stat_data))
300     return;
301
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))
308     {
309       report_difference (&current_stat_info, _("File type differs"));
310       return;
311     }
312
313   if ((current_header->header.typeflag == CHRTYPE
314        || current_header->header.typeflag == BLKTYPE)
315       && current_stat_info.stat.st_rdev != stat_data.st_rdev)
316     {
317       report_difference (&current_stat_info, _("Device number differs"));
318       return;
319     }
320
321   if ((current_stat_info.stat.st_mode & MODE_ALL) !=
322       (stat_data.st_mode & MODE_ALL))
323     report_difference (&current_stat_info, _("Mode differs"));
324 }
325
326 static int
327 dumpdir_cmp (const char *a, const char *b)
328 {
329   size_t len;
330
331   while (*a)
332     switch (*a)
333       {
334       case 'Y':
335       case 'N':
336         if (!strchr ("YN", *b))
337           return 1;
338         if (strcmp(a + 1, b + 1))
339           return 1;
340         len = strlen (a) + 1;
341         a += len;
342         b += len;
343         break;
344
345       case 'D':
346         if (strcmp(a, b))
347           return 1;
348         len = strlen (a) + 1;
349         a += len;
350         b += len;
351         break;
352
353       case 'R':
354       case 'T':
355       case 'X':
356         return *b;
357       }
358   return *b;
359 }
360
361 static void
362 diff_dumpdir (struct tar_stat_info *dir)
363 {
364   const char *dumpdir_buffer;
365
366   if (dir->fd == 0)
367     {
368       void (*diag) (char const *) = NULL;
369       int fd = subfile_open (dir->parent, dir->orig_file_name, open_read_flags);
370       if (fd < 0)
371         diag = open_diag;
372       else if (fstat (fd, &dir->stat))
373         diag = stat_diag;
374       else
375         dir->fd = fd;
376       if (diag)
377         {
378           file_removed_diag (dir->orig_file_name, false, diag);
379           return;
380         }
381     }
382   dumpdir_buffer = directory_contents (scan_directory (dir));
383
384   if (dumpdir_buffer)
385     {
386       if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer))
387         report_difference (dir, _("Contents differ"));
388     }
389   else
390     read_and_process (dir, process_noop);
391 }
392
393 static void
394 diff_multivol (void)
395 {
396   struct stat stat_data;
397   int fd, status;
398   off_t offset;
399
400   if (current_stat_info.had_trailing_slash)
401     {
402       diff_dir ();
403       return;
404     }
405
406   if (!get_stat_data (current_stat_info.file_name, &stat_data))
407     return;
408
409   if (!S_ISREG (stat_data.st_mode))
410     {
411       report_difference (&current_stat_info, _("File type differs"));
412       skip_member ();
413       return;
414     }
415
416   offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
417   if (stat_data.st_size != current_stat_info.stat.st_size + offset)
418     {
419       report_difference (&current_stat_info, _("Size differs"));
420       skip_member ();
421       return;
422     }
423
424
425   fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
426
427   if (fd < 0)
428     {
429       open_error (current_stat_info.file_name);
430       report_difference (&current_stat_info, NULL);
431       skip_member ();
432       return;
433     }
434
435   if (lseek (fd, offset, SEEK_SET) < 0)
436     {
437       seek_error_details (current_stat_info.file_name, offset);
438       report_difference (&current_stat_info, NULL);
439       return;
440     }
441
442   read_and_process (&current_stat_info, process_rawdata);
443
444   status = close (fd);
445   if (status != 0)
446     close_error (current_stat_info.file_name);
447 }
448
449 /* Diff a file against the archive.  */
450 void
451 diff_archive (void)
452 {
453
454   set_next_block_after (current_header);
455
456   /* Print the block from current_header and current_stat_info.  */
457
458   if (verbose_option)
459     {
460       if (now_verifying)
461         fprintf (stdlis, _("Verify "));
462       print_header (&current_stat_info, current_header, -1);
463     }
464
465   switch (current_header->header.typeflag)
466     {
467     default:
468       ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
469               quotearg_colon (current_stat_info.file_name),
470               current_header->header.typeflag));
471       /* Fall through.  */
472
473     case AREGTYPE:
474     case REGTYPE:
475     case GNUTYPE_SPARSE:
476     case CONTTYPE:
477
478       /* Appears to be a file.  See if it's really a directory.  */
479
480       if (current_stat_info.had_trailing_slash)
481         diff_dir ();
482       else
483         diff_file ();
484       break;
485
486     case LNKTYPE:
487       diff_link ();
488       break;
489
490 #ifdef HAVE_READLINK
491     case SYMTYPE:
492       diff_symlink ();
493       break;
494 #endif
495
496     case CHRTYPE:
497     case BLKTYPE:
498     case FIFOTYPE:
499       diff_special ();
500       break;
501
502     case GNUTYPE_DUMPDIR:
503     case DIRTYPE:
504       if (is_dumpdir (&current_stat_info))
505         diff_dumpdir (&current_stat_info);
506       diff_dir ();
507       break;
508
509     case GNUTYPE_VOLHDR:
510       break;
511
512     case GNUTYPE_MULTIVOL:
513       diff_multivol ();
514     }
515 }
516
517 void
518 verify_volume (void)
519 {
520   int may_fail = 0;
521   if (removed_prefixes_p ())
522     {
523       WARN((0, 0,
524             _("Archive contains file names with leading prefixes removed.")));
525       may_fail = 1;
526     }
527   if (transform_program_p ())
528     {
529       WARN((0, 0,
530             _("Archive contains transformed file names.")));
531       may_fail = 1;
532     }
533   if (may_fail)
534     WARN((0, 0,
535           _("Verification may fail to locate original files.")));
536
537   clear_directory_table ();
538
539   if (!diff_buffer)
540     diff_init ();
541
542   /* Verifying an archive is meant to check if the physical media got it
543      correctly, so try to defeat clever in-memory buffering pertaining to
544      this particular media.  On Linux, for example, the floppy drive would
545      not even be accessed for the whole verification.
546
547      The code was using fsync only when the ioctl is unavailable, but
548      Marty Leisner says that the ioctl does not work when not preceded by
549      fsync.  So, until we know better, or maybe to please Marty, let's do it
550      the unbelievable way :-).  */
551
552 #if HAVE_FSYNC
553   fsync (archive);
554 #endif
555 #ifdef FDFLUSH
556   ioctl (archive, FDFLUSH);
557 #endif
558
559 #ifdef MTIOCTOP
560   {
561     struct mtop operation;
562     int status;
563
564     operation.mt_op = MTBSF;
565     operation.mt_count = 1;
566     if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
567       {
568         if (errno != EIO
569             || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
570                 status < 0))
571           {
572 #endif
573             if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
574               {
575                 /* Lseek failed.  Try a different method.  */
576                 seek_warn (archive_name_array[0]);
577                 return;
578               }
579 #ifdef MTIOCTOP
580           }
581       }
582   }
583 #endif
584
585   access_mode = ACCESS_READ;
586   now_verifying = 1;
587
588   flush_read ();
589   while (1)
590     {
591       enum read_header status = read_header (&current_header,
592                                              &current_stat_info,
593                                              read_header_auto);
594
595       if (status == HEADER_FAILURE)
596         {
597           int counter = 0;
598
599           do
600             {
601               counter++;
602               set_next_block_after (current_header);
603               status = read_header (&current_header, &current_stat_info,
604                                     read_header_auto);
605             }
606           while (status == HEADER_FAILURE);
607
608           ERROR ((0, 0,
609                   ngettext ("VERIFY FAILURE: %d invalid header detected",
610                             "VERIFY FAILURE: %d invalid headers detected",
611                             counter), counter));
612         }
613       if (status == HEADER_END_OF_FILE)
614         break;
615       if (status == HEADER_ZERO_BLOCK)
616         {
617           set_next_block_after (current_header);
618           if (!ignore_zeros_option)
619             {
620               char buf[UINTMAX_STRSIZE_BOUND];
621
622               status = read_header (&current_header, &current_stat_info,
623                                     read_header_auto);
624               if (status == HEADER_ZERO_BLOCK)
625                 break;
626               WARNOPT (WARN_ALONE_ZERO_BLOCK,
627                        (0, 0, _("A lone zero block at %s"),
628                         STRINGIFY_BIGINT (current_block_ordinal (), buf)));
629             }
630           continue;
631         }
632
633       decode_header (current_header, &current_stat_info, &current_format, 1);
634       diff_archive ();
635       tar_stat_destroy (&current_stat_info);
636     }
637
638   access_mode = ACCESS_WRITE;
639   now_verifying = 0;
640 }