Do not assume tar's default archive is stdout
[debian/tar] / src / diffarch.c
1 /* Diff files from a tar archive.
2    Copyright (C) 1988, 1992, 1993 Free Software Foundation
3
4 This file is part of GNU Tar.
5
6 GNU Tar is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Tar is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Tar; see the file COPYING.  If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 /*
21  * Diff files from a tar archive.
22  *
23  * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
24  */
25
26 #include <stdio.h>
27 #include <errno.h>
28 #ifndef STDC_HEADERS
29 extern int errno;
30 #endif
31 #include <sys/types.h>
32
33 #ifdef BSD42
34 #include <sys/file.h>
35 #else
36 #ifndef V7
37 #include <fcntl.h>
38 #endif
39 #endif
40
41 #ifdef HAVE_SYS_MTIO_H
42 #include <sys/ioctl.h>
43 #include <sys/mtio.h>
44 #endif
45
46 #include "tar.h"
47 #include "port.h"
48 #include "rmt.h"
49
50 #ifndef S_ISLNK
51 #define lstat stat
52 #endif
53
54 extern void *valloc ();
55
56 extern union record *head;      /* Points to current tape header */
57 extern struct stat hstat;       /* Stat struct corresponding */
58 extern int head_standard;       /* Tape header is in ANSI format */
59
60 void decode_header ();
61 void diff_sparse_files ();
62 void fill_in_sparse_array ();
63 void fl_read ();
64 long from_oct ();
65 int do_stat ();
66 extern void print_header ();
67 int read_header ();
68 void saverec ();
69 void sigh ();
70 extern void skip_file ();
71 extern void skip_extended_headers ();
72 int wantbytes ();
73
74 extern FILE *msg_file;
75
76 int now_verifying = 0;          /* Are we verifying at the moment? */
77
78 int diff_fd;                    /* Descriptor of file we're diffing */
79
80 char *diff_buf = 0;             /* Pointer to area for reading
81                                            file contents into */
82
83 char *diff_dir;                 /* Directory contents for LF_DUMPDIR */
84
85 int different = 0;
86
87 /*struct sp_array *sparsearray;
88 int             sp_ar_size = 10;*/
89 /*
90  * Initialize for a diff operation
91  */
92 void
93 diff_init ()
94 {
95   /*NOSTRICT*/
96   diff_buf = (char *) valloc ((unsigned) blocksize);
97   if (!diff_buf)
98     {
99       msg ("could not allocate memory for diff buffer of %d bytes",
100            blocksize);
101       exit (EX_ARGSBAD);
102     }
103 }
104
105 /*
106  * Diff a file against the archive.
107  */
108 void
109 diff_archive ()
110 {
111   register char *data;
112   int check, namelen;
113   int err;
114   long offset;
115   struct stat filestat;
116   int compare_chunk ();
117   int compare_dir ();
118   int no_op ();
119 #ifndef __MSDOS__
120   dev_t dev;
121   ino_t ino;
122 #endif
123   char *get_dir_contents ();
124   long from_oct ();
125
126   errno = EPIPE;                /* FIXME, remove perrors */
127
128   saverec (&head);              /* Make sure it sticks around */
129   userec (head);                /* And go past it in the archive */
130   decode_header (head, &hstat, &head_standard, 1);      /* Snarf fields */
131
132   /* Print the record from 'head' and 'hstat' */
133   if (f_verbose)
134     {
135       if (now_verifying)
136         fprintf (msg_file, "Verify ");
137       print_header ();
138     }
139
140   switch (head->header.linkflag)
141     {
142
143     default:
144       msg ("Unknown file type '%c' for %s, diffed as normal file",
145            head->header.linkflag, current_file_name);
146       /* FALL THRU */
147
148     case LF_OLDNORMAL:
149     case LF_NORMAL:
150     case LF_SPARSE:
151     case LF_CONTIG:
152       /*
153                  * Appears to be a file.
154                  * See if it's really a directory.
155                  */
156       namelen = strlen (current_file_name) - 1;
157       if (current_file_name[namelen] == '/')
158         goto really_dir;
159
160
161       if (do_stat (&filestat))
162         {
163           if (head->header.isextended)
164             skip_extended_headers ();
165           skip_file ((long) hstat.st_size);
166           different++;
167           goto quit;
168         }
169
170       if (!S_ISREG (filestat.st_mode))
171         {
172           fprintf (msg_file, "%s: not a regular file\n",
173                    current_file_name);
174           skip_file ((long) hstat.st_size);
175           different++;
176           goto quit;
177         }
178
179       filestat.st_mode &= 07777;
180       if (filestat.st_mode != hstat.st_mode)
181         sigh ("mode");
182       if (filestat.st_uid != hstat.st_uid)
183         sigh ("uid");
184       if (filestat.st_gid != hstat.st_gid)
185         sigh ("gid");
186       if (filestat.st_mtime != hstat.st_mtime)
187         sigh ("mod time");
188       if (head->header.linkflag != LF_SPARSE &&
189           filestat.st_size != hstat.st_size)
190         {
191           sigh ("size");
192           skip_file ((long) hstat.st_size);
193           goto quit;
194         }
195
196       diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
197
198       if (diff_fd < 0 && !f_absolute_paths)
199         {
200           char tmpbuf[NAMSIZ + 2];
201
202           tmpbuf[0] = '/';
203           strcpy (&tmpbuf[1], current_file_name);
204           diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
205         }
206       if (diff_fd < 0)
207         {
208           msg_perror ("cannot open %s", current_file_name);
209           if (head->header.isextended)
210             skip_extended_headers ();
211           skip_file ((long) hstat.st_size);
212           different++;
213           goto quit;
214         }
215       /*
216                  * Need to treat sparse files completely differently here.
217                  */
218       if (head->header.linkflag == LF_SPARSE)
219         diff_sparse_files (hstat.st_size);
220       else
221         wantbytes ((long) (hstat.st_size), compare_chunk);
222
223       check = close (diff_fd);
224       if (check < 0)
225         msg_perror ("Error while closing %s", current_file_name);
226
227     quit:
228       break;
229
230 #ifndef __MSDOS__
231     case LF_LINK:
232       if (do_stat (&filestat))
233         break;
234       dev = filestat.st_dev;
235       ino = filestat.st_ino;
236       err = stat (current_link_name, &filestat);
237       if (err < 0)
238         {
239           if (errno == ENOENT)
240             {
241               fprintf (msg_file, "%s: does not exist\n", current_file_name);
242             }
243           else
244             {
245               msg_perror ("cannot stat file %s", current_file_name);
246             }
247           different++;
248           break;
249         }
250       if (filestat.st_dev != dev || filestat.st_ino != ino)
251         {
252           fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
253           break;
254         }
255       break;
256 #endif
257
258 #ifdef S_ISLNK
259     case LF_SYMLINK:
260       {
261         char linkbuf[NAMSIZ + 3];
262         check = readlink (current_file_name, linkbuf,
263                           (sizeof linkbuf) - 1);
264
265         if (check < 0)
266           {
267             if (errno == ENOENT)
268               {
269                 fprintf (msg_file,
270                          "%s: no such file or directory\n",
271                          current_file_name);
272               }
273             else
274               {
275                 msg_perror ("cannot read link %s", current_file_name);
276               }
277             different++;
278             break;
279           }
280
281         linkbuf[check] = '\0';  /* Null-terminate it */
282         if (strncmp (current_link_name, linkbuf, check) != 0)
283           {
284             fprintf (msg_file, "%s: symlink differs\n",
285                      current_link_name);
286             different++;
287           }
288       }
289       break;
290 #endif
291
292 #ifdef S_IFCHR
293     case LF_CHR:
294       hstat.st_mode |= S_IFCHR;
295       goto check_node;
296 #endif
297
298 #ifdef S_IFBLK
299       /* If local system doesn't support block devices, use default case */
300     case LF_BLK:
301       hstat.st_mode |= S_IFBLK;
302       goto check_node;
303 #endif
304
305 #ifdef S_ISFIFO
306       /* If local system doesn't support FIFOs, use default case */
307     case LF_FIFO:
308 #ifdef S_IFIFO
309       hstat.st_mode |= S_IFIFO;
310 #endif
311       hstat.st_rdev = 0;        /* FIXME, do we need this? */
312       goto check_node;
313 #endif
314
315     check_node:
316       /* FIXME, deal with umask */
317       if (do_stat (&filestat))
318         break;
319       if (hstat.st_rdev != filestat.st_rdev)
320         {
321           fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
322           different++;
323           break;
324         }
325 #ifdef S_IFMT
326       if (hstat.st_mode != filestat.st_mode)
327 #else /* POSIX lossage */
328       if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
329 #endif
330         {
331           fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
332           different++;
333           break;
334         }
335       break;
336
337     case LF_DUMPDIR:
338       data = diff_dir = get_dir_contents (current_file_name, 0);
339       if (data)
340         {
341           wantbytes ((long) (hstat.st_size), compare_dir);
342           free (data);
343         }
344       else
345         wantbytes ((long) (hstat.st_size), no_op);
346       /* FALL THROUGH */
347
348     case LF_DIR:
349       /* Check for trailing / */
350       namelen = strlen (current_file_name) - 1;
351     really_dir:
352       while (namelen && current_file_name[namelen] == '/')
353         current_file_name[namelen--] = '\0';    /* Zap / */
354
355       if (do_stat (&filestat))
356         break;
357       if (!S_ISDIR (filestat.st_mode))
358         {
359           fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
360           different++;
361           break;
362         }
363       if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
364         sigh ("mode");
365       break;
366
367     case LF_VOLHDR:
368       break;
369
370     case LF_MULTIVOL:
371       namelen = strlen (current_file_name) - 1;
372       if (current_file_name[namelen] == '/')
373         goto really_dir;
374
375       if (do_stat (&filestat))
376         break;
377
378       if (!S_ISREG (filestat.st_mode))
379         {
380           fprintf (msg_file, "%s: not a regular file\n",
381                    current_file_name);
382           skip_file ((long) hstat.st_size);
383           different++;
384           break;
385         }
386
387       filestat.st_mode &= 07777;
388       offset = from_oct (1 + 12, head->header.offset);
389       if (filestat.st_size != hstat.st_size + offset)
390         {
391           sigh ("size");
392           skip_file ((long) hstat.st_size);
393           different++;
394           break;
395         }
396
397       diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
398
399       if (diff_fd < 0)
400         {
401           msg_perror ("cannot open file %s", current_file_name);
402           skip_file ((long) hstat.st_size);
403           different++;
404           break;
405         }
406       err = lseek (diff_fd, offset, 0);
407       if (err != offset)
408         {
409           msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
410           different++;
411           break;
412         }
413
414       wantbytes ((long) (hstat.st_size), compare_chunk);
415
416       check = close (diff_fd);
417       if (check < 0)
418         {
419           msg_perror ("Error while closing %s", current_file_name);
420         }
421       break;
422
423     }
424
425   /* We don't need to save it any longer. */
426   saverec ((union record **) 0);/* Unsave it */
427 }
428
429 int
430 compare_chunk (bytes, buffer)
431      long bytes;
432      char *buffer;
433 {
434   int err;
435
436   err = read (diff_fd, diff_buf, bytes);
437   if (err != bytes)
438     {
439       if (err < 0)
440         {
441           msg_perror ("can't read %s", current_file_name);
442         }
443       else
444         {
445           fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
446         }
447       different++;
448       return -1;
449     }
450   if (bcmp (buffer, diff_buf, bytes))
451     {
452       fprintf (msg_file, "%s: data differs\n", current_file_name);
453       different++;
454       return -1;
455     }
456   return 0;
457 }
458
459 int
460 compare_dir (bytes, buffer)
461      long bytes;
462      char *buffer;
463 {
464   if (bcmp (buffer, diff_dir, bytes))
465     {
466       fprintf (msg_file, "%s: data differs\n", current_file_name);
467       different++;
468       return -1;
469     }
470   diff_dir += bytes;
471   return 0;
472 }
473
474 /*
475  * Sigh about something that differs.
476  */
477 void
478 sigh (what)
479      char *what;
480 {
481
482   fprintf (msg_file, "%s: %s differs\n",
483            current_file_name, what);
484 }
485
486 void
487 verify_volume ()
488 {
489   int status;
490 #ifdef MTIOCTOP
491   struct mtop t;
492   int er;
493 #endif
494
495   if (!diff_buf)
496     diff_init ();
497 #ifdef MTIOCTOP
498   t.mt_op = MTBSF;
499   t.mt_count = 1;
500   if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
501     {
502       if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
503         {
504 #endif
505           if (rmtlseek (archive, 0L, 0) != 0)
506             {
507               /* Lseek failed.  Try a different method */
508               msg_perror ("Couldn't rewind archive file for verify");
509               return;
510             }
511 #ifdef MTIOCTOP
512         }
513     }
514 #endif
515   ar_reading = 1;
516   now_verifying = 1;
517   fl_read ();
518   for (;;)
519     {
520       status = read_header ();
521       if (status == 0)
522         {
523           unsigned n;
524
525           n = 0;
526           do
527             {
528               n++;
529               status = read_header ();
530             }
531           while (status == 0);
532           msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
533         }
534       if (status == 2 || status == EOF)
535         break;
536       diff_archive ();
537     }
538   ar_reading = 0;
539   now_verifying = 0;
540
541 }
542
543 int
544 do_stat (statp)
545      struct stat *statp;
546 {
547   int err;
548
549   err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
550   if (err < 0)
551     {
552       if (errno == ENOENT)
553         {
554           fprintf (msg_file, "%s: does not exist\n", current_file_name);
555         }
556       else
557         msg_perror ("can't stat file %s", current_file_name);
558       /*                skip_file((long)hstat.st_size);
559                 different++;*/
560       return 1;
561     }
562   else
563     return 0;
564 }
565
566 /*
567  * JK
568  * Diff'ing a sparse file with its counterpart on the tar file is a
569  * bit of a different story than a normal file.  First, we must know
570  * what areas of the file to skip through, i.e., we need to contruct
571  * a sparsearray, which will hold all the information we need.  We must
572  * compare small amounts of data at a time as we find it.
573  */
574
575 void
576 diff_sparse_files (filesize)
577      int filesize;
578
579 {
580   int sparse_ind = 0;
581   char *buf;
582   int buf_size = RECORDSIZE;
583   union record *datarec;
584   int err;
585   long numbytes;
586   /*    int             amt_read = 0;*/
587   int size = filesize;
588
589   buf = (char *) ck_malloc (buf_size * sizeof (char));
590
591   fill_in_sparse_array ();
592
593
594   while (size > 0)
595     {
596       datarec = findrec ();
597       if (!sparsearray[sparse_ind].numbytes)
598         break;
599
600       /*
601                  * 'numbytes' is nicer to write than
602                  * 'sparsearray[sparse_ind].numbytes' all the time ...
603                  */
604       numbytes = sparsearray[sparse_ind].numbytes;
605
606       lseek (diff_fd, sparsearray[sparse_ind].offset, 0);
607       /*
608                  * take care to not run out of room in our buffer
609                  */
610       while (buf_size < numbytes)
611         {
612           buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char));
613           buf_size *= 2;
614         }
615       while (numbytes > RECORDSIZE)
616         {
617           if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
618             {
619               if (err < 0)
620                 msg_perror ("can't read %s", current_file_name);
621               else
622                 fprintf (msg_file, "%s: could only read %d of %d bytes\n",
623                          current_file_name, err, numbytes);
624               break;
625             }
626           if (bcmp (buf, datarec->charptr, RECORDSIZE))
627             {
628               different++;
629               break;
630             }
631           numbytes -= err;
632           size -= err;
633           userec (datarec);
634           datarec = findrec ();
635         }
636       if ((err = read (diff_fd, buf, numbytes)) != numbytes)
637         {
638           if (err < 0)
639             msg_perror ("can't read %s", current_file_name);
640           else
641             fprintf (msg_file, "%s: could only read %d of %d bytes\n",
642                      current_file_name, err, numbytes);
643           break;
644         }
645
646       if (bcmp (buf, datarec->charptr, numbytes))
647         {
648           different++;
649           break;
650         }
651       /*                amt_read += numbytes;
652                 if (amt_read >= RECORDSIZE) {
653                         amt_read = 0;
654                         userec(datarec);
655                         datarec = findrec();
656                 }*/
657       userec (datarec);
658       sparse_ind++;
659       size -= numbytes;
660     }
661   /*
662          * if the number of bytes read isn't the
663          * number of bytes supposedly in the file,
664          * they're different
665          */
666   /*    if (amt_read != filesize)
667                 different++;*/
668   userec (datarec);
669   free (sparsearray);
670   if (different)
671     fprintf (msg_file, "%s: data differs\n", current_file_name);
672
673 }
674
675 /*
676  * JK
677  * This routine should be used more often than it is ... look into
678  * that.  Anyhow, what it does is translate the sparse information
679  * on the header, and in any subsequent extended headers, into an
680  * array of structures with true numbers, as opposed to character
681  * strings.  It simply makes our life much easier, doing so many
682  * comparisong and such.
683  */
684 void
685 fill_in_sparse_array ()
686 {
687   int ind;
688
689   /*
690          * allocate space for our scratch space; it's initially
691          * 10 elements long, but can change in this routine if
692          * necessary
693          */
694   sp_array_size = 10;
695   sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
696
697   /*
698          * there are at most five of these structures in the header
699          * itself; read these in first
700          */
701   for (ind = 0; ind < SPARSE_IN_HDR; ind++)
702     {
703       if (!head->header.sp[ind].numbytes)
704         break;
705       sparsearray[ind].offset =
706         from_oct (1 + 12, head->header.sp[ind].offset);
707       sparsearray[ind].numbytes =
708         from_oct (1 + 12, head->header.sp[ind].numbytes);
709     }
710   /*
711          * if the header's extended, we gotta read in exhdr's till
712          * we're done
713          */
714   if (head->header.isextended)
715     {
716       /* how far into the sparsearray we are 'so far' */
717       static int so_far_ind = SPARSE_IN_HDR;
718       union record *exhdr;
719
720       for (;;)
721         {
722           exhdr = findrec ();
723           for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
724             {
725               if (ind + so_far_ind > sp_array_size - 1)
726                 {
727                   /*
728                                  * we just ran out of room in our
729                                  *  scratch area - realloc it
730                                  */
731                   sparsearray = (struct sp_array *)
732                     ck_realloc (sparsearray,
733                              sp_array_size * 2 * sizeof (struct sp_array));
734                   sp_array_size *= 2;
735                 }
736               /*
737                          * convert the character strings into longs
738                          */
739               sparsearray[ind + so_far_ind].offset =
740                 from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset);
741               sparsearray[ind + so_far_ind].numbytes =
742                 from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes);
743             }
744           /*
745                  * if this is the last extended header for this
746                  * file, we can stop
747                  */
748           if (!exhdr->ext_hdr.isextended)
749             break;
750           else
751             {
752               so_far_ind += SPARSE_EXT_HDR;
753               userec (exhdr);
754             }
755         }
756       /* be sure to skip past the last one  */
757       userec (exhdr);
758     }
759 }