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