]> Dogcows Code - chaz/tar/blob - src/diffarch.c
ce47d9d6cab5b152d79123049dca6b198c93e018
[chaz/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 }
This page took 0.060913 seconds and 3 git commands to generate.