Fix a typo
[tar/ericb.git] / src / diffarch.c
blobce47d9d6cab5b152d79123049dca6b198c93e018
1 /* Diff files from a tar archive.
2 Copyright (C) 1988, 1992, 1993 Free Software Foundation
4 This file is part of GNU Tar.
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.
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.
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. */
21 * Diff files from a tar archive.
23 * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
26 #include <stdio.h>
27 #include <errno.h>
28 #ifndef STDC_HEADERS
29 extern int errno;
30 #endif
31 #include <sys/types.h>
33 #ifdef BSD42
34 #include <sys/file.h>
35 #else
36 #ifndef V7
37 #include <fcntl.h>
38 #endif
39 #endif
41 #ifdef HAVE_SYS_MTIO_H
42 #include <sys/ioctl.h>
43 #include <sys/mtio.h>
44 #endif
46 #include "tar.h"
47 #include "port.h"
48 #include "rmt.h"
50 #ifndef S_ISLNK
51 #define lstat stat
52 #endif
54 extern void *valloc ();
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 */
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 ();
74 extern FILE *msg_file;
76 int now_verifying = 0; /* Are we verifying at the moment? */
78 int diff_fd; /* Descriptor of file we're diffing */
80 char *diff_buf = 0; /* Pointer to area for reading
81 file contents into */
83 char *diff_dir; /* Directory contents for LF_DUMPDIR */
85 int different = 0;
87 /*struct sp_array *sparsearray;
88 int sp_ar_size = 10;*/
90 * Initialize for a diff operation
92 void
93 diff_init ()
95 /*NOSTRICT*/
96 diff_buf = (char *) valloc ((unsigned) blocksize);
97 if (!diff_buf)
99 msg ("could not allocate memory for diff buffer of %d bytes",
100 blocksize);
101 exit (EX_ARGSBAD);
106 * Diff a file against the archive.
108 void
109 diff_archive ()
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 ();
126 errno = EPIPE; /* FIXME, remove perrors */
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 */
132 /* Print the record from 'head' and 'hstat' */
133 if (f_verbose)
135 if (now_verifying)
136 fprintf (msg_file, "Verify ");
137 print_header ();
140 switch (head->header.linkflag)
143 default:
144 msg ("Unknown file type '%c' for %s, diffed as normal file",
145 head->header.linkflag, current_file_name);
146 /* FALL THRU */
148 case LF_OLDNORMAL:
149 case LF_NORMAL:
150 case LF_SPARSE:
151 case LF_CONTIG:
153 * Appears to be a file.
154 * See if it's really a directory.
156 namelen = strlen (current_file_name) - 1;
157 if (current_file_name[namelen] == '/')
158 goto really_dir;
161 if (do_stat (&filestat))
163 if (head->header.isextended)
164 skip_extended_headers ();
165 skip_file ((long) hstat.st_size);
166 different++;
167 goto quit;
170 if (!S_ISREG (filestat.st_mode))
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;
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)
191 sigh ("size");
192 skip_file ((long) hstat.st_size);
193 goto quit;
196 diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
198 if (diff_fd < 0 && !f_absolute_paths)
200 char tmpbuf[NAMSIZ + 2];
202 tmpbuf[0] = '/';
203 strcpy (&tmpbuf[1], current_file_name);
204 diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
206 if (diff_fd < 0)
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;
216 * Need to treat sparse files completely differently here.
218 if (head->header.linkflag == LF_SPARSE)
219 diff_sparse_files (hstat.st_size);
220 else
221 wantbytes ((long) (hstat.st_size), compare_chunk);
223 check = close (diff_fd);
224 if (check < 0)
225 msg_perror ("Error while closing %s", current_file_name);
227 quit:
228 break;
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)
239 if (errno == ENOENT)
241 fprintf (msg_file, "%s: does not exist\n", current_file_name);
243 else
245 msg_perror ("cannot stat file %s", current_file_name);
247 different++;
248 break;
250 if (filestat.st_dev != dev || filestat.st_ino != ino)
252 fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
253 break;
255 break;
256 #endif
258 #ifdef S_ISLNK
259 case LF_SYMLINK:
261 char linkbuf[NAMSIZ + 3];
262 check = readlink (current_file_name, linkbuf,
263 (sizeof linkbuf) - 1);
265 if (check < 0)
267 if (errno == ENOENT)
269 fprintf (msg_file,
270 "%s: no such file or directory\n",
271 current_file_name);
273 else
275 msg_perror ("cannot read link %s", current_file_name);
277 different++;
278 break;
281 linkbuf[check] = '\0'; /* Null-terminate it */
282 if (strncmp (current_link_name, linkbuf, check) != 0)
284 fprintf (msg_file, "%s: symlink differs\n",
285 current_link_name);
286 different++;
289 break;
290 #endif
292 #ifdef S_IFCHR
293 case LF_CHR:
294 hstat.st_mode |= S_IFCHR;
295 goto check_node;
296 #endif
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
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
315 check_node:
316 /* FIXME, deal with umask */
317 if (do_stat (&filestat))
318 break;
319 if (hstat.st_rdev != filestat.st_rdev)
321 fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
322 different++;
323 break;
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
331 fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
332 different++;
333 break;
335 break;
337 case LF_DUMPDIR:
338 data = diff_dir = get_dir_contents (current_file_name, 0);
339 if (data)
341 wantbytes ((long) (hstat.st_size), compare_dir);
342 free (data);
344 else
345 wantbytes ((long) (hstat.st_size), no_op);
346 /* FALL THROUGH */
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 / */
355 if (do_stat (&filestat))
356 break;
357 if (!S_ISDIR (filestat.st_mode))
359 fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
360 different++;
361 break;
363 if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
364 sigh ("mode");
365 break;
367 case LF_VOLHDR:
368 break;
370 case LF_MULTIVOL:
371 namelen = strlen (current_file_name) - 1;
372 if (current_file_name[namelen] == '/')
373 goto really_dir;
375 if (do_stat (&filestat))
376 break;
378 if (!S_ISREG (filestat.st_mode))
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;
387 filestat.st_mode &= 07777;
388 offset = from_oct (1 + 12, head->header.offset);
389 if (filestat.st_size != hstat.st_size + offset)
391 sigh ("size");
392 skip_file ((long) hstat.st_size);
393 different++;
394 break;
397 diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
399 if (diff_fd < 0)
401 msg_perror ("cannot open file %s", current_file_name);
402 skip_file ((long) hstat.st_size);
403 different++;
404 break;
406 err = lseek (diff_fd, offset, 0);
407 if (err != offset)
409 msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
410 different++;
411 break;
414 wantbytes ((long) (hstat.st_size), compare_chunk);
416 check = close (diff_fd);
417 if (check < 0)
419 msg_perror ("Error while closing %s", current_file_name);
421 break;
425 /* We don't need to save it any longer. */
426 saverec ((union record **) 0);/* Unsave it */
430 compare_chunk (bytes, buffer)
431 long bytes;
432 char *buffer;
434 int err;
436 err = read (diff_fd, diff_buf, bytes);
437 if (err != bytes)
439 if (err < 0)
441 msg_perror ("can't read %s", current_file_name);
443 else
445 fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
447 different++;
448 return -1;
450 if (bcmp (buffer, diff_buf, bytes))
452 fprintf (msg_file, "%s: data differs\n", current_file_name);
453 different++;
454 return -1;
456 return 0;
460 compare_dir (bytes, buffer)
461 long bytes;
462 char *buffer;
464 if (bcmp (buffer, diff_dir, bytes))
466 fprintf (msg_file, "%s: data differs\n", current_file_name);
467 different++;
468 return -1;
470 diff_dir += bytes;
471 return 0;
475 * Sigh about something that differs.
477 void
478 sigh (what)
479 char *what;
482 fprintf (msg_file, "%s: %s differs\n",
483 current_file_name, what);
486 void
487 verify_volume ()
489 int status;
490 #ifdef MTIOCTOP
491 struct mtop t;
492 int er;
493 #endif
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)
502 if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
504 #endif
505 if (rmtlseek (archive, 0L, 0) != 0)
507 /* Lseek failed. Try a different method */
508 msg_perror ("Couldn't rewind archive file for verify");
509 return;
511 #ifdef MTIOCTOP
514 #endif
515 ar_reading = 1;
516 now_verifying = 1;
517 fl_read ();
518 for (;;)
520 status = read_header ();
521 if (status == 0)
523 unsigned n;
525 n = 0;
528 n++;
529 status = read_header ();
531 while (status == 0);
532 msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
534 if (status == 2 || status == EOF)
535 break;
536 diff_archive ();
538 ar_reading = 0;
539 now_verifying = 0;
544 do_stat (statp)
545 struct stat *statp;
547 int err;
549 err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
550 if (err < 0)
552 if (errno == ENOENT)
554 fprintf (msg_file, "%s: does not exist\n", current_file_name);
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;
562 else
563 return 0;
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.
575 void
576 diff_sparse_files (filesize)
577 int filesize;
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;
589 buf = (char *) ck_malloc (buf_size * sizeof (char));
591 fill_in_sparse_array ();
594 while (size > 0)
596 datarec = findrec ();
597 if (!sparsearray[sparse_ind].numbytes)
598 break;
601 * 'numbytes' is nicer to write than
602 * 'sparsearray[sparse_ind].numbytes' all the time ...
604 numbytes = sparsearray[sparse_ind].numbytes;
606 lseek (diff_fd, sparsearray[sparse_ind].offset, 0);
608 * take care to not run out of room in our buffer
610 while (buf_size < numbytes)
612 buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char));
613 buf_size *= 2;
615 while (numbytes > RECORDSIZE)
617 if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
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;
626 if (bcmp (buf, datarec->charptr, RECORDSIZE))
628 different++;
629 break;
631 numbytes -= err;
632 size -= err;
633 userec (datarec);
634 datarec = findrec ();
636 if ((err = read (diff_fd, buf, numbytes)) != numbytes)
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;
646 if (bcmp (buf, datarec->charptr, numbytes))
648 different++;
649 break;
651 /* amt_read += numbytes;
652 if (amt_read >= RECORDSIZE) {
653 amt_read = 0;
654 userec(datarec);
655 datarec = findrec();
657 userec (datarec);
658 sparse_ind++;
659 size -= numbytes;
662 * if the number of bytes read isn't the
663 * number of bytes supposedly in the file,
664 * they're different
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);
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.
684 void
685 fill_in_sparse_array ()
687 int ind;
690 * allocate space for our scratch space; it's initially
691 * 10 elements long, but can change in this routine if
692 * necessary
694 sp_array_size = 10;
695 sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
698 * there are at most five of these structures in the header
699 * itself; read these in first
701 for (ind = 0; ind < SPARSE_IN_HDR; ind++)
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);
711 * if the header's extended, we gotta read in exhdr's till
712 * we're done
714 if (head->header.isextended)
716 /* how far into the sparsearray we are 'so far' */
717 static int so_far_ind = SPARSE_IN_HDR;
718 union record *exhdr;
720 for (;;)
722 exhdr = findrec ();
723 for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
725 if (ind + so_far_ind > sp_array_size - 1)
728 * we just ran out of room in our
729 * scratch area - realloc it
731 sparsearray = (struct sp_array *)
732 ck_realloc (sparsearray,
733 sp_array_size * 2 * sizeof (struct sp_array));
734 sp_array_size *= 2;
737 * convert the character strings into longs
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);
745 * if this is the last extended header for this
746 * file, we can stop
748 if (!exhdr->ext_hdr.isextended)
749 break;
750 else
752 so_far_ind += SPARSE_EXT_HDR;
753 userec (exhdr);
756 /* be sure to skip past the last one */
757 userec (exhdr);