Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / tar / src / compare.c
1 /* Diff files from a tar archive.
2
3    Copyright 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001 Free
4    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 /* $FreeBSD: src/contrib/tar/src/compare.c,v 1.3.2.1 2002/07/14 13:19:44 sobomax Exp $ */
23
24 #include "system.h"
25
26 #if HAVE_UTIME_H
27 # include <utime.h>
28 #else
29 struct utimbuf
30   {
31     long actime;
32     long modtime;
33   };
34 #endif
35
36 #if HAVE_LINUX_FD_H
37 # include <linux/fd.h>
38 #endif
39
40 #include <quotearg.h>
41
42 #include "common.h"
43 #include "rmt.h"
44
45 /* Spare space for messages, hopefully safe even after gettext.  */
46 #define MESSAGE_BUFFER_SIZE 100
47
48 /* Nonzero if we are verifying at the moment.  */
49 int now_verifying;
50
51 /* File descriptor for the file we are diffing.  */
52 static int diff_handle;
53
54 /* Area for reading file contents into.  */
55 static char *diff_buffer;
56
57 /* Initialize for a diff operation.  */
58 void
59 diff_init (void)
60 {
61   diff_buffer = valloc (record_size);
62   if (!diff_buffer)
63     xalloc_die ();
64 }
65
66 /* Sigh about something that differs by writing a MESSAGE to stdlis,
67    given MESSAGE is nonzero.  Also set the exit status if not already.  */
68 static void
69 report_difference (const char *message)
70 {
71   if (message)
72     fprintf (stdlis, "%s: %s\n", quotearg_colon (current_file_name), message);
73
74   if (exit_status == TAREXIT_SUCCESS)
75     exit_status = TAREXIT_DIFFERS;
76 }
77
78 /* Take a buffer returned by read_and_process and do nothing with it.  */
79 static int
80 process_noop (size_t size, char *data)
81 {
82   /* Yes, I know.  SIZE and DATA are unused in this function.  Some
83      compilers may even report it.  That's OK, just relax!  */
84   return 1;
85 }
86
87 static int
88 process_rawdata (size_t bytes, char *buffer)
89 {
90   ssize_t status = safe_read (diff_handle, diff_buffer, bytes);
91   char message[MESSAGE_BUFFER_SIZE];
92
93   if (status != bytes)
94     {
95       if (status < 0)
96         {
97           read_error (current_file_name);
98           report_difference (0);
99         }
100       else
101         {
102           sprintf (message, _("Could only read %lu of %lu bytes"),
103                    (unsigned long) status, (unsigned long) bytes);
104           report_difference (message);
105         }
106       return 0;
107     }
108
109   if (memcmp (buffer, diff_buffer, bytes))
110     {
111       report_difference (_("Contents differ"));
112       return 0;
113     }
114
115   return 1;
116 }
117
118 /* Directory contents, only for GNUTYPE_DUMPDIR.  */
119
120 static char *dumpdir_cursor;
121
122 static int
123 process_dumpdir (size_t bytes, char *buffer)
124 {
125   if (memcmp (buffer, dumpdir_cursor, bytes))
126     {
127       report_difference (_("Contents differ"));
128       return 0;
129     }
130
131   dumpdir_cursor += bytes;
132   return 1;
133 }
134
135 /* Some other routine wants SIZE bytes in the archive.  For each chunk
136    of the archive, call PROCESSOR with the size of the chunk, and the
137    address of the chunk it can work with.  The PROCESSOR should return
138    nonzero for success.  It it return error once, continue skipping
139    without calling PROCESSOR anymore.  */
140 static void
141 read_and_process (off_t size, int (*processor) (size_t, char *))
142 {
143   union block *data_block;
144   size_t data_size;
145
146   if (multi_volume_option)
147     save_sizeleft = size;
148   while (size)
149     {
150       data_block = find_next_block ();
151       if (! data_block)
152         {
153           ERROR ((0, 0, _("Unexpected EOF in archive")));
154           return;
155         }
156
157       data_size = available_space_after (data_block);
158       if (data_size > size)
159         data_size = size;
160       if (!(*processor) (data_size, data_block->buffer))
161         processor = process_noop;
162       set_next_block_after ((union block *)
163                             (data_block->buffer + data_size - 1));
164       size -= data_size;
165       if (multi_volume_option)
166         save_sizeleft -= data_size;
167     }
168 }
169
170 /* JK This routine should be used more often than it is ... look into
171    that.  Anyhow, what it does is translate the sparse information on the
172    header, and in any subsequent extended headers, into an array of
173    structures with true numbers, as opposed to character strings.  It
174    simply makes our life much easier, doing so many comparisons and such.
175    */
176 static void
177 fill_in_sparse_array (void)
178 {
179   int counter;
180
181   /* Allocate space for our scratch space; it's initially 10 elements
182      long, but can change in this routine if necessary.  */
183
184   sp_array_size = 10;
185   sparsearray = xmalloc (sp_array_size * sizeof (struct sp_array));
186
187   /* There are at most five of these structures in the header itself;
188      read these in first.  */
189
190   for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
191     {
192       /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler.  */
193       if (current_header->oldgnu_header.sp[counter].numbytes == 0)
194         break;
195
196       sparsearray[counter].offset =
197         OFF_FROM_HEADER (current_header->oldgnu_header.sp[counter].offset);
198       sparsearray[counter].numbytes =
199         SIZE_FROM_HEADER (current_header->oldgnu_header.sp[counter].numbytes);
200     }
201
202   /* If the header's extended, we gotta read in exhdr's till we're done.  */
203
204   if (current_header->oldgnu_header.isextended)
205     {
206       /* How far into the sparsearray we are `so far'.  */
207       static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
208       union block *exhdr;
209
210       while (1)
211         {
212           exhdr = find_next_block ();
213           if (!exhdr)
214             FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
215
216           for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
217             {
218               if (counter + so_far_ind > sp_array_size - 1)
219                 {
220                   /* We just ran out of room in our scratch area -
221                      realloc it.  */
222
223                   sp_array_size *= 2;
224                   sparsearray =
225                     xrealloc (sparsearray,
226                               sp_array_size * sizeof (struct sp_array));
227                 }
228
229               /* Convert the character strings into offsets and sizes.  */
230
231               sparsearray[counter + so_far_ind].offset =
232                 OFF_FROM_HEADER (exhdr->sparse_header.sp[counter].offset);
233               sparsearray[counter + so_far_ind].numbytes =
234                 SIZE_FROM_HEADER (exhdr->sparse_header.sp[counter].numbytes);
235             }
236
237           /* If this is the last extended header for this file, we can
238              stop.  */
239
240           if (!exhdr->sparse_header.isextended)
241             break;
242
243           so_far_ind += SPARSES_IN_SPARSE_HEADER;
244           set_next_block_after (exhdr);
245         }
246
247       /* Be sure to skip past the last one.  */
248
249       set_next_block_after (exhdr);
250     }
251 }
252
253 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
254    bit of a different story than a normal file.  First, we must know what
255    areas of the file to skip through, i.e., we need to construct a
256    sparsearray, which will hold all the information we need.  We must
257    compare small amounts of data at a time as we find it.  */
258
259 /* FIXME: This does not look very solid to me, at first glance.  Zero areas
260    are not checked, spurious sparse entries seemingly goes undetected, and
261    I'm not sure overall identical sparsity is verified.  */
262
263 static void
264 diff_sparse_files (off_t size_of_file)
265 {
266   off_t remaining_size = size_of_file;
267   char *buffer = xmalloc (BLOCKSIZE * sizeof (char));
268   size_t buffer_size = BLOCKSIZE;
269   union block *data_block = 0;
270   int counter = 0;
271   int different = 0;
272
273   fill_in_sparse_array ();
274
275   while (remaining_size > 0)
276     {
277       ssize_t status;
278       size_t chunk_size;
279       off_t offset;
280
281 #if 0
282       off_t amount_read = 0;
283 #endif
284
285       data_block = find_next_block ();
286       if (!data_block)
287         FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
288       chunk_size = sparsearray[counter].numbytes;
289       if (!chunk_size)
290         break;
291
292       offset = sparsearray[counter].offset;
293       if (lseek (diff_handle, offset, SEEK_SET) < 0)
294         {
295           seek_error_details (current_file_name, offset);
296           report_difference (0);
297         }
298
299       /* Take care to not run out of room in our buffer.  */
300
301       while (buffer_size < chunk_size)
302         {
303           if (buffer_size * 2 < buffer_size)
304             xalloc_die ();
305           buffer_size *= 2;
306           buffer = xrealloc (buffer, buffer_size * sizeof (char));
307         }
308
309       while (chunk_size > BLOCKSIZE)
310         {
311           if (status = safe_read (diff_handle, buffer, BLOCKSIZE),
312               status != BLOCKSIZE)
313             {
314               if (status < 0)
315                 {
316                   read_error (current_file_name);
317                   report_difference (0);
318                 }
319               else
320                 {
321                   char message[MESSAGE_BUFFER_SIZE];
322
323                   sprintf (message, _("Could only read %lu of %lu bytes"),
324                            (unsigned long) status, (unsigned long) chunk_size);
325                   report_difference (message);
326                 }
327               break;
328             }
329
330           if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
331             {
332               different = 1;
333               break;
334             }
335
336           chunk_size -= status;
337           remaining_size -= status;
338           set_next_block_after (data_block);
339           data_block = find_next_block ();
340           if (!data_block)
341             FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
342         }
343       if (status = safe_read (diff_handle, buffer, chunk_size),
344           status != chunk_size)
345         {
346           if (status < 0)
347             {
348               read_error (current_file_name);
349               report_difference (0);
350             }
351           else
352             {
353               char message[MESSAGE_BUFFER_SIZE];
354
355               sprintf (message, _("Could only read %lu of %lu bytes"),
356                        (unsigned long) status, (unsigned long) chunk_size);
357               report_difference (message);
358             }
359           break;
360         }
361
362       if (memcmp (buffer, data_block->buffer, chunk_size))
363         {
364           different = 1;
365           break;
366         }
367 #if 0
368       amount_read += chunk_size;
369       if (amount_read >= BLOCKSIZE)
370         {
371           amount_read = 0;
372           set_next_block_after (data_block);
373           data_block = find_next_block ();
374           if (!data_block)
375             FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
376         }
377 #endif
378       set_next_block_after (data_block);
379       counter++;
380       remaining_size -= chunk_size;
381     }
382
383 #if 0
384   /* If the number of bytes read isn't the number of bytes supposedly in
385      the file, they're different.  */
386
387   if (amount_read != size_of_file)
388     different = 1;
389 #endif
390
391   set_next_block_after (data_block);
392   free (sparsearray);
393
394   if (different)
395     report_difference (_("Contents differ"));
396 }
397
398 /* Call either stat or lstat over STAT_DATA, depending on
399    --dereference (-h), for a file which should exist.  Diagnose any
400    problem.  Return nonzero for success, zero otherwise.  */
401 static int
402 get_stat_data (char const *file_name, struct stat *stat_data)
403 {
404   int status = deref_stat (dereference_option, file_name, stat_data);
405
406   if (status != 0)
407     {
408       if (errno == ENOENT)
409         {
410           report_difference (_("does not exist"));
411         }
412       else
413         {
414           stat_error (file_name);
415           report_difference (0);
416         }
417       return 0;
418     }
419
420   return 1;
421 }
422
423 /* Diff a file against the archive.  */
424 void
425 diff_archive (void)
426 {
427   struct stat stat_data;
428   size_t name_length;
429   int status;
430   struct utimbuf restore_times;
431
432   set_next_block_after (current_header);
433   decode_header (current_header, &current_stat, &current_format, 1);
434
435   /* Print the block from current_header and current_stat.  */
436
437   if (verbose_option)
438     {
439       if (now_verifying)
440         fprintf (stdlis, _("Verify "));
441       print_header ();
442     }
443
444   switch (current_header->header.typeflag)
445     {
446     default:
447       ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
448               quotearg_colon (current_file_name),
449               current_header->header.typeflag));
450       /* Fall through.  */
451
452     case AREGTYPE:
453     case REGTYPE:
454     case GNUTYPE_SPARSE:
455     case CONTTYPE:
456
457       /* Appears to be a file.  See if it's really a directory.  */
458
459       name_length = strlen (current_file_name) - 1;
460       if (ISSLASH (current_file_name[name_length]))
461         goto really_dir;
462
463       if (!get_stat_data (current_file_name, &stat_data))
464         {
465           skip_member ();
466           goto quit;
467         }
468
469       if (!S_ISREG (stat_data.st_mode))
470         {
471           report_difference (_("File type differs"));
472           skip_member ();
473           goto quit;
474         }
475
476       if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
477         report_difference (_("Mode differs"));
478
479 #if !MSDOS
480       /* stat() in djgpp's C library gives a constant number of 42 as the
481          uid and gid of a file.  So, comparing an FTP'ed archive just after
482          unpack would fail on MSDOS.  */
483       if (stat_data.st_uid != current_stat.st_uid)
484         report_difference (_("Uid differs"));
485       if (stat_data.st_gid != current_stat.st_gid)
486         report_difference (_("Gid differs"));
487 #endif
488
489       if (stat_data.st_mtime != current_stat.st_mtime)
490         report_difference (_("Mod time differs"));
491       if (current_header->header.typeflag != GNUTYPE_SPARSE &&
492           stat_data.st_size != current_stat.st_size)
493         {
494           report_difference (_("Size differs"));
495           skip_member ();
496           goto quit;
497         }
498
499       diff_handle = open (current_file_name, O_RDONLY | O_BINARY);
500
501       if (diff_handle < 0)
502         {
503           open_error (current_file_name);
504           skip_member ();
505           report_difference (0);
506           goto quit;
507         }
508
509       restore_times.actime = stat_data.st_atime;
510       restore_times.modtime = stat_data.st_mtime;
511
512       /* Need to treat sparse files completely differently here.  */
513
514       if (current_header->header.typeflag == GNUTYPE_SPARSE)
515         diff_sparse_files (current_stat.st_size);
516       else
517         {
518           if (multi_volume_option)
519             {
520               assign_string (&save_name, current_file_name);
521               save_totsize = current_stat.st_size;
522               /* save_sizeleft is set in read_and_process.  */
523             }
524
525           read_and_process (current_stat.st_size, process_rawdata);
526
527           if (multi_volume_option)
528             assign_string (&save_name, 0);
529         }
530
531       status = close (diff_handle);
532       if (status != 0)
533         close_error (current_file_name);
534
535       if (atime_preserve_option)
536         utime (current_file_name, &restore_times);
537
538     quit:
539       break;
540
541 #if !MSDOS
542     case LNKTYPE:
543       {
544         struct stat link_data;
545
546         if (!get_stat_data (current_file_name, &stat_data))
547           break;
548         if (!get_stat_data (current_link_name, &link_data))
549           break;
550
551         if (stat_data.st_dev != link_data.st_dev
552             || stat_data.st_ino != link_data.st_ino)
553           {
554             char *message =
555               xmalloc (MESSAGE_BUFFER_SIZE + 4 * strlen (current_link_name));
556
557             sprintf (message, _("Not linked to %s"),
558                      quote (current_link_name));
559             report_difference (message);
560             free (message);
561             break;
562           }
563
564         break;
565       }
566 #endif /* not MSDOS */
567
568 #ifdef HAVE_READLINK
569     case SYMTYPE:
570       {
571         size_t len = strlen (current_link_name);
572         char *linkbuf = alloca (len + 1);
573
574         status = readlink (current_file_name, linkbuf, len);
575
576         if (status < 0)
577           {
578             if (errno == ENOENT)
579               readlink_warn (current_file_name);
580             else
581               readlink_error (current_file_name);
582             report_difference (0);
583           }
584         else if (status != len
585                  || strncmp (current_link_name, linkbuf, len) != 0)
586           report_difference (_("Symlink differs"));
587
588         break;
589       }
590 #endif
591
592     case CHRTYPE:
593     case BLKTYPE:
594     case FIFOTYPE:
595
596       /* FIXME: deal with umask.  */
597
598       if (!get_stat_data (current_file_name, &stat_data))
599         break;
600
601       if (current_header->header.typeflag == CHRTYPE
602           ? !S_ISCHR (stat_data.st_mode)
603           : current_header->header.typeflag == BLKTYPE
604           ? !S_ISBLK (stat_data.st_mode)
605           : /* current_header->header.typeflag == FIFOTYPE */
606           !S_ISFIFO (stat_data.st_mode))
607         {
608           report_difference (_("File type differs"));
609           break;
610         }
611
612       if ((current_header->header.typeflag == CHRTYPE
613            || current_header->header.typeflag == BLKTYPE)
614           && current_stat.st_rdev != stat_data.st_rdev)
615         {
616           report_difference (_("Device number differs"));
617           break;
618         }
619
620       if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
621         {
622           report_difference (_("Mode differs"));
623           break;
624         }
625
626       break;
627
628     case GNUTYPE_DUMPDIR:
629       {
630         char *dumpdir_buffer = get_directory_contents (current_file_name, 0);
631
632         if (multi_volume_option)
633           {
634             assign_string (&save_name, current_file_name);
635             save_totsize = current_stat.st_size;
636             /* save_sizeleft is set in read_and_process.  */
637           }
638
639         if (dumpdir_buffer)
640           {
641             dumpdir_cursor = dumpdir_buffer;
642             read_and_process (current_stat.st_size, process_dumpdir);
643             free (dumpdir_buffer);
644           }
645         else
646           read_and_process (current_stat.st_size, process_noop);
647
648         if (multi_volume_option)
649           assign_string (&save_name, 0);
650         /* Fall through.  */
651       }
652
653     case DIRTYPE:
654       /* Check for trailing /.  */
655
656       name_length = strlen (current_file_name) - 1;
657
658     really_dir:
659       while (name_length && ISSLASH (current_file_name[name_length]))
660         current_file_name[name_length--] = '\0';        /* zap / */
661
662       if (!get_stat_data (current_file_name, &stat_data))
663         break;
664
665       if (!S_ISDIR (stat_data.st_mode))
666         {
667           report_difference (_("File type differs"));
668           break;
669         }
670
671       if ((current_stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
672         {
673           report_difference (_("Mode differs"));
674           break;
675         }
676
677       break;
678
679     case GNUTYPE_VOLHDR:
680       break;
681
682     case GNUTYPE_MULTIVOL:
683       {
684         off_t offset;
685
686         name_length = strlen (current_file_name) - 1;
687         if (ISSLASH (current_file_name[name_length]))
688           goto really_dir;
689
690         if (!get_stat_data (current_file_name, &stat_data))
691           break;
692
693         if (!S_ISREG (stat_data.st_mode))
694           {
695             report_difference (_("File type differs"));
696             skip_member ();
697             break;
698           }
699
700         offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
701         if (stat_data.st_size != current_stat.st_size + offset)
702           {
703             report_difference (_("Size differs"));
704             skip_member ();
705             break;
706           }
707
708         diff_handle = open (current_file_name, O_RDONLY | O_BINARY);
709
710         if (diff_handle < 0)
711           {
712             open_error (current_file_name);
713             report_difference (0);
714             skip_member ();
715             break;
716           }
717
718         if (lseek (diff_handle, offset, SEEK_SET) < 0)
719           {
720             seek_error_details (current_file_name, offset);
721             report_difference (0);
722             break;
723           }
724
725         if (multi_volume_option)
726           {
727             assign_string (&save_name, current_file_name);
728             save_totsize = stat_data.st_size;
729             /* save_sizeleft is set in read_and_process.  */
730           }
731
732         read_and_process (current_stat.st_size, process_rawdata);
733
734         if (multi_volume_option)
735           assign_string (&save_name, 0);
736
737         status = close (diff_handle);
738         if (status != 0)
739           close_error (current_file_name);
740
741         break;
742       }
743     }
744 }
745
746 void
747 verify_volume (void)
748 {
749   if (!diff_buffer)
750     diff_init ();
751
752   /* Verifying an archive is meant to check if the physical media got it
753      correctly, so try to defeat clever in-memory buffering pertaining to
754      this particular media.  On Linux, for example, the floppy drive would
755      not even be accessed for the whole verification.
756
757      The code was using fsync only when the ioctl is unavailable, but
758      Marty Leisner says that the ioctl does not work when not preceded by
759      fsync.  So, until we know better, or maybe to please Marty, let's do it
760      the unbelievable way :-).  */
761
762 #if HAVE_FSYNC
763   fsync (archive);
764 #endif
765 #ifdef FDFLUSH
766   ioctl (archive, FDFLUSH);
767 #endif
768
769 #ifdef MTIOCTOP
770   {
771     struct mtop operation;
772     int status;
773
774     operation.mt_op = MTBSF;
775     operation.mt_count = 1;
776     if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
777       {
778         if (errno != EIO
779             || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
780                 status < 0))
781           {
782 #endif
783             if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
784               {
785                 /* Lseek failed.  Try a different method.  */
786                 seek_warn (archive_name_array[0]);
787                 return;
788               }
789 #ifdef MTIOCTOP
790           }
791       }
792   }
793 #endif
794
795   access_mode = ACCESS_READ;
796   now_verifying = 1;
797
798   flush_read ();
799   while (1)
800     {
801       enum read_header status = read_header (0);
802
803       if (status == HEADER_FAILURE)
804         {
805           int counter = 0;
806
807           while (status == HEADER_FAILURE);
808             {
809               counter++;
810               status = read_header (0);
811             }
812           ERROR ((0, 0,
813                   _("VERIFY FAILURE: %d invalid header(s) detected"), counter));
814         }
815       if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
816         break;
817
818       diff_archive ();
819     }
820
821   access_mode = ACCESS_WRITE;
822   now_verifying = 0;
823 }