* src/buffer.c (reading_from_pipe): Remove. All uses removed.
[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 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 2, 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    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
21
22 #include <system.h>
23
24 #if HAVE_UTIME_H
25 # include <utime.h>
26 #else
27 struct utimbuf
28   {
29     long actime;
30     long modtime;
31   };
32 #endif
33
34 #if HAVE_LINUX_FD_H
35 # include <linux/fd.h>
36 #endif
37
38 #include <quotearg.h>
39
40 #include "common.h"
41 #include <rmt.h>
42 #include <stdarg.h>
43
44 /* Nonzero if we are verifying at the moment.  */
45 bool now_verifying;
46
47 /* File descriptor for the file we are diffing.  */
48 static int diff_handle;
49
50 /* Area for reading file contents into.  */
51 static char *diff_buffer;
52
53 /* Initialize for a diff operation.  */
54 void
55 diff_init (void)
56 {
57   void *ptr;
58   diff_buffer = page_aligned_alloc (&ptr, record_size);
59   if (listed_incremental_option)
60     read_directory_file ();
61 }
62
63 /* Sigh about something that differs by writing a MESSAGE to stdlis,
64    given MESSAGE is nonzero.  Also set the exit status if not already.  */
65 void
66 report_difference (struct tar_stat_info *st __attribute__ ((unused)),
67                    const char *fmt, ...)
68 {
69   if (fmt)
70     {
71       va_list ap;
72
73       fprintf (stdlis, "%s: ", quotearg_colon (current_stat_info.file_name));
74       va_start (ap, fmt);
75       vfprintf (stdlis, fmt, ap);
76       va_end (ap);
77       fprintf (stdlis, "\n");
78     }
79
80   if (exit_status == TAREXIT_SUCCESS)
81     exit_status = TAREXIT_DIFFERS;
82 }
83
84 /* Take a buffer returned by read_and_process and do nothing with it.  */
85 static int
86 process_noop (size_t size __attribute__ ((unused)),
87               char *data __attribute__ ((unused)))
88 {
89   return 1;
90 }
91
92 static int
93 process_rawdata (size_t bytes, char *buffer)
94 {
95   size_t status = safe_read (diff_handle, diff_buffer, bytes);
96
97   if (status != bytes)
98     {
99       if (status == SAFE_READ_ERROR)
100         {
101           read_error (current_stat_info.file_name);
102           report_difference (&current_stat_info, NULL);
103         }
104       else
105         {
106           report_difference (&current_stat_info,
107                              ngettext ("Could only read %lu of %lu byte",
108                                        "Could only read %lu of %lu bytes",
109                                        bytes),
110                              (unsigned long) status, (unsigned long) bytes);
111         }
112       return 0;
113     }
114
115   if (memcmp (buffer, diff_buffer, bytes))
116     {
117       report_difference (&current_stat_info, _("Contents differ"));
118       return 0;
119     }
120
121   return 1;
122 }
123
124 /* Directory contents, only for GNUTYPE_DUMPDIR.  */
125
126 static char *dumpdir_cursor;
127
128 static int
129 process_dumpdir (size_t bytes, char *buffer)
130 {
131   if (memcmp (buffer, dumpdir_cursor, bytes))
132     {
133       report_difference (&current_stat_info, _("Contents differ"));
134       return 0;
135     }
136
137   dumpdir_cursor += bytes;
138   return 1;
139 }
140
141 /* Some other routine wants SIZE bytes in the archive.  For each chunk
142    of the archive, call PROCESSOR with the size of the chunk, and the
143    address of the chunk it can work with.  The PROCESSOR should return
144    nonzero for success.  It it return error once, continue skipping
145    without calling PROCESSOR anymore.  */
146 static void
147 read_and_process (off_t size, int (*processor) (size_t, char *))
148 {
149   union block *data_block;
150   size_t data_size;
151
152   if (multi_volume_option)
153     save_sizeleft = size;
154   while (size)
155     {
156       data_block = find_next_block ();
157       if (! data_block)
158         {
159           ERROR ((0, 0, _("Unexpected EOF in archive")));
160           return;
161         }
162
163       data_size = available_space_after (data_block);
164       if (data_size > size)
165         data_size = size;
166       if (!(*processor) (data_size, data_block->buffer))
167         processor = process_noop;
168       set_next_block_after ((union block *)
169                             (data_block->buffer + data_size - 1));
170       size -= data_size;
171       if (multi_volume_option)
172         save_sizeleft -= data_size;
173     }
174 }
175
176 /* Call either stat or lstat over STAT_DATA, depending on
177    --dereference (-h), for a file which should exist.  Diagnose any
178    problem.  Return nonzero for success, zero otherwise.  */
179 static int
180 get_stat_data (char const *file_name, struct stat *stat_data)
181 {
182   int status = deref_stat (dereference_option, file_name, stat_data);
183
184   if (status != 0)
185     {
186       if (errno == ENOENT)
187         stat_warn (file_name);
188       else
189         stat_error (file_name);
190       report_difference (&current_stat_info, NULL);
191       return 0;
192     }
193
194   return 1;
195 }
196
197 \f
198 static void
199 diff_dir ()
200 {
201   struct stat stat_data;
202
203   if (!get_stat_data (current_stat_info.file_name, &stat_data))
204     return;
205
206   if (!S_ISDIR (stat_data.st_mode))
207     report_difference (&current_stat_info, _("File type differs"));
208   else if ((current_stat_info.stat.st_mode & MODE_ALL) !=
209            (stat_data.st_mode & MODE_ALL))
210     report_difference (&current_stat_info, _("Mode differs"));
211 }
212
213 static void
214 diff_file ()
215 {
216   struct stat stat_data;
217
218   if (!get_stat_data (current_stat_info.file_name, &stat_data))
219     skip_member ();
220   else if (!S_ISREG (stat_data.st_mode))
221     {
222       report_difference (&current_stat_info, _("File type differs"));
223       skip_member ();
224     }
225   else
226     {
227       if ((current_stat_info.stat.st_mode & MODE_ALL) !=
228           (stat_data.st_mode & MODE_ALL))
229         report_difference (&current_stat_info, _("Mode differs"));
230
231       if (!sys_compare_uid (&stat_data, &current_stat_info.stat))
232         report_difference (&current_stat_info, _("Uid differs"));
233       if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
234         report_difference (&current_stat_info, _("Gid differs"));
235
236       if (stat_data.st_mtime != current_stat_info.stat.st_mtime)
237         report_difference (&current_stat_info, _("Mod time differs"));
238       if (current_header->header.typeflag != GNUTYPE_SPARSE &&
239           stat_data.st_size != current_stat_info.stat.st_size)
240         {
241           report_difference (&current_stat_info, _("Size differs"));
242           skip_member ();
243         }
244       else
245         {
246           int fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
247
248           if (fd < 0)
249             {
250               open_error (current_stat_info.file_name);
251               skip_member ();
252               report_difference (&current_stat_info, NULL);
253             }
254           else
255             {
256               int status;
257               struct utimbuf restore_times;
258               
259               restore_times.actime = stat_data.st_atime;
260               restore_times.modtime = stat_data.st_mtime;
261
262               if (current_stat_info.is_sparse)
263                 sparse_diff_file (diff_handle, &current_stat_info);
264               else
265                 {
266                   if (multi_volume_option)
267                     {
268                       assign_string (&save_name, 
269                                      current_stat_info.orig_file_name);
270                       save_totsize = current_stat_info.stat.st_size;
271                       /* save_sizeleft is set in read_and_process.  */
272                     }
273
274                   read_and_process (current_stat_info.stat.st_size,
275                                     process_rawdata);
276
277                   if (multi_volume_option)
278                     assign_string (&save_name, 0);
279                 }
280
281               status = close (fd);
282               if (status != 0)
283                 close_error (current_stat_info.file_name);
284
285               if (atime_preserve_option)
286                 utime (current_stat_info.file_name, &restore_times);
287             }
288         }
289     }
290 }
291
292 static void
293 diff_link ()
294 {
295   struct stat file_data;
296   struct stat link_data;
297
298   if (get_stat_data (current_stat_info.file_name, &file_data)
299       && get_stat_data (current_stat_info.link_name, &link_data)
300       && !sys_compare_links (&file_data, &link_data))
301     report_difference (&current_stat_info,
302                        _("Not linked to %s"),
303                        quote (current_stat_info.link_name));
304 }
305
306 #ifdef HAVE_READLINK
307 static void
308 diff_symlink ()
309 {
310   size_t len = strlen (current_stat_info.link_name);
311   char *linkbuf = alloca (len + 1);
312
313   int status = readlink (current_stat_info.file_name, linkbuf, len + 1);
314
315   if (status < 0)
316     {
317       if (errno == ENOENT)
318         readlink_warn (current_stat_info.file_name);
319       else
320         readlink_error (current_stat_info.file_name);
321       report_difference (&current_stat_info, NULL);
322     }
323   else if (status != len
324            || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
325     report_difference (&current_stat_info, _("Symlink differs"));
326 }
327 #endif
328
329 static void
330 diff_special ()
331 {
332   struct stat stat_data;
333
334   /* FIXME: deal with umask.  */
335
336   if (!get_stat_data (current_stat_info.file_name, &stat_data))
337     return;
338
339   if (current_header->header.typeflag == CHRTYPE
340       ? !S_ISCHR (stat_data.st_mode)
341       : current_header->header.typeflag == BLKTYPE
342       ? !S_ISBLK (stat_data.st_mode)
343       : /* current_header->header.typeflag == FIFOTYPE */
344       !S_ISFIFO (stat_data.st_mode))
345     {
346       report_difference (&current_stat_info, _("File type differs"));
347       return;
348     }
349
350   if ((current_header->header.typeflag == CHRTYPE
351        || current_header->header.typeflag == BLKTYPE)
352       && current_stat_info.stat.st_rdev != stat_data.st_rdev)
353     {
354       report_difference (&current_stat_info, _("Device number differs"));
355       return;
356     }
357
358   if ((current_stat_info.stat.st_mode & MODE_ALL) !=
359       (stat_data.st_mode & MODE_ALL))
360     report_difference (&current_stat_info, _("Mode differs"));
361 }
362
363 static void
364 diff_dumpdir ()
365 {
366   char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name,
367                                                  0);
368
369   if (multi_volume_option)
370     {
371       assign_string (&save_name, current_stat_info.orig_file_name);
372       save_totsize = current_stat_info.stat.st_size;
373       /* save_sizeleft is set in read_and_process.  */
374     }
375
376   if (dumpdir_buffer)
377     {
378       dumpdir_cursor = dumpdir_buffer;
379       read_and_process (current_stat_info.stat.st_size, process_dumpdir);
380       free (dumpdir_buffer);
381     }
382   else
383     read_and_process (current_stat_info.stat.st_size, process_noop);
384
385   if (multi_volume_option)
386     assign_string (&save_name, 0);
387 }
388
389 static void
390 diff_multivol ()
391 {
392   struct stat stat_data;
393   int fd, status;
394   off_t offset;
395
396   if (current_stat_info.had_trailing_slash)
397     {
398       diff_dir ();
399       return;
400     }
401   
402   if (!get_stat_data (current_stat_info.file_name, &stat_data))
403     return;
404
405   if (!S_ISREG (stat_data.st_mode))
406     {
407       report_difference (&current_stat_info, _("File type differs"));
408       skip_member ();
409       return;
410     }
411
412   offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
413   if (stat_data.st_size != current_stat_info.stat.st_size + offset)
414     {
415       report_difference (&current_stat_info, _("Size differs"));
416       skip_member ();
417       return;
418     }
419
420   fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
421   
422   if (fd < 0)
423     {
424       open_error (current_stat_info.file_name);
425       report_difference (&current_stat_info, NULL);
426       skip_member ();
427       return;
428     }
429
430   if (lseek (fd, offset, SEEK_SET) < 0)
431     {
432       seek_error_details (current_stat_info.file_name, offset);
433       report_difference (&current_stat_info, NULL);
434       return;
435     }
436
437   if (multi_volume_option)
438     {
439       assign_string (&save_name, current_stat_info.orig_file_name);
440       save_totsize = stat_data.st_size;
441       /* save_sizeleft is set in read_and_process.  */
442     }
443
444   read_and_process (current_stat_info.stat.st_size, process_rawdata);
445
446   if (multi_volume_option)
447     assign_string (&save_name, 0);
448   
449   status = close (fd);
450   if (status != 0)
451     close_error (current_stat_info.file_name);
452 }
453
454 /* Diff a file against the archive.  */
455 void
456 diff_archive (void)
457 {
458
459   set_next_block_after (current_header);
460   decode_header (current_header, &current_stat_info, &current_format, 1);
461
462   /* Print the block from current_header and current_stat_info.  */
463
464   if (verbose_option)
465     {
466       if (now_verifying)
467         fprintf (stdlis, _("Verify "));
468       print_header (&current_stat_info, -1);
469     }
470
471   switch (current_header->header.typeflag)
472     {
473     default:
474       ERROR ((0, 0, _("%s: Unknown file type `%c', diffed as normal file"),
475               quotearg_colon (current_stat_info.file_name),
476               current_header->header.typeflag));
477       /* Fall through.  */
478
479     case AREGTYPE:
480     case REGTYPE:
481     case GNUTYPE_SPARSE:
482     case CONTTYPE:
483
484       /* Appears to be a file.  See if it's really a directory.  */
485
486       if (current_stat_info.had_trailing_slash)
487         diff_dir ();
488       else
489         diff_file ();
490       break;
491
492     case LNKTYPE:
493       diff_link ();
494       break;
495
496 #ifdef HAVE_READLINK
497     case SYMTYPE:
498       diff_symlink ();
499       break;
500 #endif
501       
502     case CHRTYPE:
503     case BLKTYPE:
504     case FIFOTYPE:
505       diff_special ();
506       break;
507
508     case GNUTYPE_DUMPDIR:
509       diff_dumpdir ();
510       /* Fall through.  */
511       
512     case DIRTYPE:
513       diff_dir ();
514       break;
515
516     case GNUTYPE_VOLHDR:
517       break;
518
519     case GNUTYPE_MULTIVOL:
520       diff_multivol ();
521     }
522 }
523
524 void
525 verify_volume (void)
526 {
527   if (removed_prefixes_p ())
528     {
529       WARN((0, 0,
530             _("Archive contains file names with leading prefixes removed.")));
531       WARN((0, 0,
532             _("Verification may fail to locate original files.")));
533     }
534
535   if (!diff_buffer)
536     diff_init ();
537
538   /* Verifying an archive is meant to check if the physical media got it
539      correctly, so try to defeat clever in-memory buffering pertaining to
540      this particular media.  On Linux, for example, the floppy drive would
541      not even be accessed for the whole verification.
542
543      The code was using fsync only when the ioctl is unavailable, but
544      Marty Leisner says that the ioctl does not work when not preceded by
545      fsync.  So, until we know better, or maybe to please Marty, let's do it
546      the unbelievable way :-).  */
547
548 #if HAVE_FSYNC
549   fsync (archive);
550 #endif
551 #ifdef FDFLUSH
552   ioctl (archive, FDFLUSH);
553 #endif
554
555 #ifdef MTIOCTOP
556   {
557     struct mtop operation;
558     int status;
559
560     operation.mt_op = MTBSF;
561     operation.mt_count = 1;
562     if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
563       {
564         if (errno != EIO
565             || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
566                 status < 0))
567           {
568 #endif
569             if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
570               {
571                 /* Lseek failed.  Try a different method.  */
572                 seek_warn (archive_name_array[0]);
573                 return;
574               }
575 #ifdef MTIOCTOP
576           }
577       }
578   }
579 #endif
580
581   access_mode = ACCESS_READ;
582   now_verifying = 1;
583
584   flush_read ();
585   while (1)
586     {
587       enum read_header status = read_header (false);
588
589       if (status == HEADER_FAILURE)
590         {
591           int counter = 0;
592
593           do
594             {
595               counter++;
596               set_next_block_after (current_header);
597               status = read_header (false);
598             }
599           while (status == HEADER_FAILURE);
600
601           ERROR ((0, 0,
602                   ngettext ("VERIFY FAILURE: %d invalid header detected",
603                             "VERIFY FAILURE: %d invalid headers detected",
604                             counter), counter));
605         }
606       if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
607         break;
608
609       diff_archive ();
610       tar_stat_destroy (&current_stat_info);
611       xheader_destroy (&extended_header);
612     }
613
614   access_mode = ACCESS_WRITE;
615   now_verifying = 0;
616 }