]> Dogcows Code - chaz/tar/blob - src/diffarch.c
Initial revision
[chaz/tar] / src / diffarch.c
1 /* Diff files from a tar archive.
2 Copyright (C) 1988 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 #ifndef NO_MTIO
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 char *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 msg("could not allocate memory for diff buffer of %d bytes",
99 blocksize);
100 exit(EX_ARGSBAD);
101 }
102 }
103
104 /*
105 * Diff a file against the archive.
106 */
107 void
108 diff_archive()
109 {
110 register char *data;
111 int check, namelen;
112 int err;
113 long offset;
114 struct stat filestat;
115 int compare_chunk();
116 int compare_dir();
117 int no_op();
118 #ifndef __MSDOS__
119 dev_t dev;
120 ino_t ino;
121 #endif
122 char *get_dir_contents();
123 long from_oct();
124
125 errno = EPIPE; /* FIXME, remove perrors */
126
127 saverec(&head); /* Make sure it sticks around */
128 userec(head); /* And go past it in the archive */
129 decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */
130
131 /* Print the record from 'head' and 'hstat' */
132 if (f_verbose) {
133 if(now_verifying)
134 fprintf(msg_file,"Verify ");
135 print_header();
136 }
137
138 switch (head->header.linkflag) {
139
140 default:
141 msg("Unknown file type '%c' for %s, diffed as normal file",
142 head->header.linkflag, head->header.name);
143 /* FALL THRU */
144
145 case LF_OLDNORMAL:
146 case LF_NORMAL:
147 case LF_SPARSE:
148 case LF_CONTIG:
149 /*
150 * Appears to be a file.
151 * See if it's really a directory.
152 */
153 namelen = strlen(head->header.name)-1;
154 if (head->header.name[namelen] == '/')
155 goto really_dir;
156
157
158 if(do_stat(&filestat)) {
159 if (head->header.isextended)
160 skip_extended_headers();
161 skip_file((long)hstat.st_size);
162 different++;
163 goto quit;
164 }
165
166 if (!S_ISREG(filestat.st_mode)) {
167 fprintf(msg_file, "%s: not a regular file\n",
168 head->header.name);
169 skip_file((long)hstat.st_size);
170 different++;
171 goto quit;
172 }
173
174 filestat.st_mode &= 07777;
175 if (filestat.st_mode != hstat.st_mode)
176 sigh("mode");
177 if (filestat.st_uid != hstat.st_uid)
178 sigh("uid");
179 if (filestat.st_gid != hstat.st_gid)
180 sigh("gid");
181 if (filestat.st_mtime != hstat.st_mtime)
182 sigh("mod time");
183 if (head->header.linkflag != LF_SPARSE &&
184 filestat.st_size != hstat.st_size) {
185 sigh("size");
186 skip_file((long)hstat.st_size);
187 goto quit;
188 }
189
190 diff_fd = open(head->header.name, O_NDELAY|O_RDONLY|O_BINARY);
191
192 if (diff_fd < 0 && !f_absolute_paths) {
193 char tmpbuf[NAMSIZ+2];
194
195 tmpbuf[0]='/';
196 strcpy(&tmpbuf[1],head->header.name);
197 diff_fd=open(tmpbuf, O_NDELAY|O_RDONLY);
198 }
199 if (diff_fd < 0) {
200 msg_perror("cannot open %s",head->header.name);
201 if (head->header.isextended)
202 skip_extended_headers();
203 skip_file((long)hstat.st_size);
204 different++;
205 goto quit;
206 }
207 /*
208 * Need to treat sparse files completely differently here.
209 */
210 if (head->header.linkflag == LF_SPARSE)
211 diff_sparse_files(hstat.st_size);
212 else
213 wantbytes((long)(hstat.st_size),compare_chunk);
214
215 check = close(diff_fd);
216 if (check < 0)
217 msg_perror("Error while closing %s",head->header.name);
218
219 quit:
220 break;
221
222 #ifndef __MSDOS__
223 case LF_LINK:
224 if(do_stat(&filestat))
225 break;
226 dev = filestat.st_dev;
227 ino = filestat.st_ino;
228 err = stat(head->header.linkname, &filestat);
229 if (err < 0) {
230 if (errno==ENOENT) {
231 fprintf(msg_file, "%s: does not exist\n",head->header.name);
232 } else {
233 msg_perror("cannot stat file %s",head->header.name);
234 }
235 different++;
236 break;
237 }
238 if(filestat.st_dev!=dev || filestat.st_ino!=ino) {
239 fprintf(msg_file, "%s not linked to %s\n",head->header.name,head->header.linkname);
240 break;
241 }
242 break;
243 #endif
244
245 #ifdef S_ISLNK
246 case LF_SYMLINK:
247 {
248 char linkbuf[NAMSIZ+3];
249 check = readlink(head->header.name, linkbuf,
250 (sizeof linkbuf)-1);
251
252 if (check < 0) {
253 if (errno == ENOENT) {
254 fprintf(msg_file,
255 "%s: no such file or directory\n",
256 head->header.name);
257 } else {
258 msg_perror("cannot read link %s",head->header.name);
259 }
260 different++;
261 break;
262 }
263
264 linkbuf[check] = '\0'; /* Null-terminate it */
265 if (strncmp(head->header.linkname, linkbuf, check) != 0) {
266 fprintf(msg_file, "%s: symlink differs\n",
267 head->header.linkname);
268 different++;
269 }
270 }
271 break;
272 #endif
273
274 #ifdef S_IFCHR
275 case LF_CHR:
276 hstat.st_mode |= S_IFCHR;
277 goto check_node;
278 #endif
279
280 #ifdef S_IFBLK
281 /* If local system doesn't support block devices, use default case */
282 case LF_BLK:
283 hstat.st_mode |= S_IFBLK;
284 goto check_node;
285 #endif
286
287 #ifdef S_ISFIFO
288 /* If local system doesn't support FIFOs, use default case */
289 case LF_FIFO:
290 #ifdef S_IFIFO
291 hstat.st_mode |= S_IFIFO;
292 #endif
293 hstat.st_rdev = 0; /* FIXME, do we need this? */
294 goto check_node;
295 #endif
296
297 check_node:
298 /* FIXME, deal with umask */
299 if(do_stat(&filestat))
300 break;
301 if(hstat.st_rdev != filestat.st_rdev) {
302 fprintf(msg_file, "%s: device numbers changed\n", head->header.name);
303 different++;
304 break;
305 }
306 #ifdef S_IFMT
307 if(hstat.st_mode != filestat.st_mode)
308 #else /* POSIX lossage */
309 if((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
310 #endif
311 {
312 fprintf(msg_file, "%s: mode or device-type changed\n", head->header.name);
313 different++;
314 break;
315 }
316 break;
317
318 case LF_DUMPDIR:
319 data=diff_dir=get_dir_contents(head->header.name,0);
320 if (data) {
321 wantbytes((long)(hstat.st_size),compare_dir);
322 free(data);
323 } else
324 wantbytes((long)(hstat.st_size),no_op);
325 /* FALL THROUGH */
326
327 case LF_DIR:
328 /* Check for trailing / */
329 namelen = strlen(head->header.name)-1;
330 really_dir:
331 while (namelen && head->header.name[namelen] == '/')
332 head->header.name[namelen--] = '\0'; /* Zap / */
333
334 if(do_stat(&filestat))
335 break;
336 if(!S_ISDIR(filestat.st_mode)) {
337 fprintf(msg_file, "%s is no longer a directory\n",head->header.name);
338 different++;
339 break;
340 }
341 if((filestat.st_mode&07777) != (hstat.st_mode&07777))
342 sigh("mode");
343 break;
344
345 case LF_VOLHDR:
346 break;
347
348 case LF_MULTIVOL:
349 namelen = strlen(head->header.name)-1;
350 if (head->header.name[namelen] == '/')
351 goto really_dir;
352
353 if(do_stat(&filestat))
354 break;
355
356 if (!S_ISREG(filestat.st_mode)) {
357 fprintf(msg_file, "%s: not a regular file\n",
358 head->header.name);
359 skip_file((long)hstat.st_size);
360 different++;
361 break;
362 }
363
364 filestat.st_mode &= 07777;
365 offset = from_oct(1+12, head->header.offset);
366 if (filestat.st_size != hstat.st_size + offset) {
367 sigh("size");
368 skip_file((long)hstat.st_size);
369 different++;
370 break;
371 }
372
373 diff_fd = open(head->header.name, O_NDELAY|O_RDONLY|O_BINARY);
374
375 if (diff_fd < 0) {
376 msg_perror("cannot open file %s",head->header.name);
377 skip_file((long)hstat.st_size);
378 different++;
379 break;
380 }
381 err = lseek(diff_fd, offset, 0);
382 if(err!=offset) {
383 msg_perror("cannot seek to %ld in file %s",offset,head->header.name);
384 different++;
385 break;
386 }
387
388 wantbytes((long)(hstat.st_size),compare_chunk);
389
390 check = close(diff_fd);
391 if (check < 0) {
392 msg_perror("Error while closing %s",head->header.name);
393 }
394 break;
395
396 }
397
398 /* We don't need to save it any longer. */
399 saverec((union record **) 0); /* Unsave it */
400 }
401
402 int
403 compare_chunk(bytes,buffer)
404 long bytes;
405 char *buffer;
406 {
407 int err;
408
409 err=read(diff_fd,diff_buf,bytes);
410 if(err!=bytes) {
411 if(err<0) {
412 msg_perror("can't read %s",head->header.name);
413 } else {
414 fprintf(msg_file,"%s: could only read %d of %d bytes\n",head->header.name,err,bytes);
415 }
416 different++;
417 return -1;
418 }
419 if(bcmp(buffer,diff_buf,bytes)) {
420 fprintf(msg_file, "%s: data differs\n",head->header.name);
421 different++;
422 return -1;
423 }
424 return 0;
425 }
426
427 int
428 compare_dir(bytes,buffer)
429 long bytes;
430 char *buffer;
431 {
432 if(bcmp(buffer,diff_dir,bytes)) {
433 fprintf(msg_file, "%s: data differs\n",head->header.name);
434 different++;
435 return -1;
436 }
437 diff_dir+=bytes;
438 return 0;
439 }
440
441 /*
442 * Sigh about something that differs.
443 */
444 void
445 sigh(what)
446 char *what;
447 {
448
449 fprintf(msg_file, "%s: %s differs\n",
450 head->header.name, what);
451 }
452
453 void
454 verify_volume()
455 {
456 int status;
457 #ifdef MTIOCTOP
458 struct mtop t;
459 int er;
460 #endif
461
462 if(!diff_buf)
463 diff_init();
464 #ifdef MTIOCTOP
465 t.mt_op = MTBSF;
466 t.mt_count = 1;
467 if((er=rmtioctl(archive,MTIOCTOP,&t))<0) {
468 if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) {
469 #endif
470 if(rmtlseek(archive,0L,0)!=0) {
471 /* Lseek failed. Try a different method */
472 msg_perror("Couldn't rewind archive file for verify");
473 return;
474 }
475 #ifdef MTIOCTOP
476 }
477 }
478 #endif
479 ar_reading=1;
480 now_verifying = 1;
481 fl_read();
482 for(;;) {
483 status = read_header();
484 if(status==0) {
485 unsigned n;
486
487 n=0;
488 do {
489 n++;
490 status=read_header();
491 } while(status==0);
492 msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s");
493 }
494 if(status==2 || status==EOF)
495 break;
496 diff_archive();
497 }
498 ar_reading=0;
499 now_verifying = 0;
500
501 }
502
503 int
504 do_stat(statp)
505 struct stat *statp;
506 {
507 int err;
508
509 err = f_follow_links ? stat(head->header.name, statp) : lstat(head->header.name, statp);
510 if (err < 0) {
511 if (errno==ENOENT) {
512 fprintf(msg_file, "%s: does not exist\n",head->header.name);
513 } else
514 msg_perror("can't stat file %s",head->header.name);
515 /* skip_file((long)hstat.st_size);
516 different++;*/
517 return 1;
518 } else
519 return 0;
520 }
521
522 /*
523 * JK
524 * Diff'ing a sparse file with its counterpart on the tar file is a
525 * bit of a different story than a normal file. First, we must know
526 * what areas of the file to skip through, i.e., we need to contruct
527 * a sparsearray, which will hold all the information we need. We must
528 * compare small amounts of data at a time as we find it.
529 */
530
531 void
532 diff_sparse_files(filesize)
533 int filesize;
534
535 {
536 int sparse_ind = 0;
537 char *buf;
538 int buf_size = RECORDSIZE;
539 union record *datarec;
540 int err;
541 long numbytes;
542 /* int amt_read = 0;*/
543 int size = filesize;
544
545 buf = (char *) malloc(buf_size * sizeof (char));
546
547 fill_in_sparse_array();
548
549
550 while (size > 0) {
551 datarec = findrec();
552 if (!sparsearray[sparse_ind].numbytes)
553 break;
554
555 /*
556 * 'numbytes' is nicer to write than
557 * 'sparsearray[sparse_ind].numbytes' all the time ...
558 */
559 numbytes = sparsearray[sparse_ind].numbytes;
560
561 lseek(diff_fd, sparsearray[sparse_ind].offset, 0);
562 /*
563 * take care to not run out of room in our buffer
564 */
565 while (buf_size < numbytes) {
566 buf = (char *) realloc(buf, buf_size * 2 * sizeof(char));
567 buf_size *= 2;
568 }
569 while (numbytes > RECORDSIZE) {
570 if ((err = read(diff_fd, buf, RECORDSIZE)) != RECORDSIZE) {
571 if (err < 0)
572 msg_perror("can't read %s", head->header.name);
573 else
574 fprintf(msg_file, "%s: could only read %d of %d bytes\n",
575 err, numbytes);
576 break;
577 }
578 if (bcmp(buf, datarec->charptr, RECORDSIZE)) {
579 different++;
580 break;
581 }
582 numbytes -= err;
583 size -= err;
584 userec(datarec);
585 datarec = findrec();
586 }
587 if ((err = read(diff_fd, buf, numbytes)) != numbytes) {
588 if (err < 0)
589 msg_perror("can't read %s", head->header.name);
590 else
591 fprintf(msg_file, "%s: could only read %d of %d bytes\n",
592 err, numbytes);
593 break;
594 }
595
596 if (bcmp(buf, datarec->charptr, numbytes)) {
597 different++;
598 break;
599 }
600 /* amt_read += numbytes;
601 if (amt_read >= RECORDSIZE) {
602 amt_read = 0;
603 userec(datarec);
604 datarec = findrec();
605 }*/
606 userec(datarec);
607 sparse_ind++;
608 size -= numbytes;
609 }
610 /*
611 * if the number of bytes read isn't the
612 * number of bytes supposedly in the file,
613 * they're different
614 */
615 /* if (amt_read != filesize)
616 different++;*/
617 userec(datarec);
618 free(sparsearray);
619 if (different)
620 fprintf(msg_file, "%s: data differs\n", head->header.name);
621
622 }
623
624 /*
625 * JK
626 * This routine should be used more often than it is ... look into
627 * that. Anyhow, what it does is translate the sparse information
628 * on the header, and in any subsequent extended headers, into an
629 * array of structures with true numbers, as opposed to character
630 * strings. It simply makes our life much easier, doing so many
631 * comparisong and such.
632 */
633 void
634 fill_in_sparse_array()
635 {
636 int ind;
637
638 /*
639 * allocate space for our scratch space; it's initially
640 * 10 elements long, but can change in this routine if
641 * necessary
642 */
643 sp_array_size = 10;
644 sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
645
646 /*
647 * there are at most five of these structures in the header
648 * itself; read these in first
649 */
650 for (ind = 0; ind < SPARSE_IN_HDR; ind++) {
651 if (!head->header.sp[ind].numbytes)
652 break;
653 sparsearray[ind].offset =
654 from_oct(1+12, head->header.sp[ind].offset);
655 sparsearray[ind].numbytes =
656 from_oct(1+12, head->header.sp[ind].numbytes);
657 }
658 /*
659 * if the header's extended, we gotta read in exhdr's till
660 * we're done
661 */
662 if (head->header.isextended) {
663 /* how far into the sparsearray we are 'so far' */
664 static int so_far_ind = SPARSE_IN_HDR;
665 union record *exhdr;
666
667 for (;;) {
668 exhdr = findrec();
669 for (ind = 0; ind < SPARSE_EXT_HDR; ind++) {
670 if (ind+so_far_ind > sp_array_size-1) {
671 /*
672 * we just ran out of room in our
673 * scratch area - realloc it
674 */
675 sparsearray = (struct sp_array *)
676 realloc(sparsearray,
677 sp_array_size*2*sizeof(struct sp_array));
678 sp_array_size *= 2;
679 }
680 /*
681 * convert the character strings into longs
682 */
683 sparsearray[ind+so_far_ind].offset =
684 from_oct(1+12, exhdr->ext_hdr.sp[ind].offset);
685 sparsearray[ind+so_far_ind].numbytes =
686 from_oct(1+12, exhdr->ext_hdr.sp[ind].numbytes);
687 }
688 /*
689 * if this is the last extended header for this
690 * file, we can stop
691 */
692 if (!exhdr->ext_hdr.isextended)
693 break;
694 else {
695 so_far_ind += SPARSE_EXT_HDR;
696 userec(exhdr);
697 }
698 }
699 /* be sure to skip past the last one */
700 userec(exhdr);
701 }
702 }
This page took 0.06395 seconds and 5 git commands to generate.