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