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)
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.
31 #include <sys/types.h>
41 #ifdef HAVE_SYS_MTIO_H
42 #include <sys/ioctl.h>
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 ();
66 extern void print_header ();
70 extern void skip_file ();
71 extern void skip_extended_headers ();
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
83 char *diff_dir; /* Directory contents for LF_DUMPDIR */
87 /*struct sp_array *sparsearray;
88 int sp_ar_size = 10;*/
90 * Initialize for a diff operation
96 diff_buf = (char *) valloc ((unsigned) blocksize);
99 msg ("could not allocate memory for diff buffer of %d bytes",
106 * Diff a file against the archive.
115 struct stat filestat;
116 int compare_chunk ();
123 char *get_dir_contents ();
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' */
136 fprintf (msg_file, "Verify ");
140 switch (head->header.linkflag)
144 msg ("Unknown file type '%c' for %s, diffed as normal file",
145 head->header.linkflag, current_file_name);
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] == '/')
161 if (do_stat (&filestat))
163 if (head->header.isextended)
164 skip_extended_headers ();
165 skip_file ((long) hstat.st_size);
170 if (!S_ISREG (filestat.st_mode))
172 fprintf (msg_file, "%s: not a regular file\n",
174 skip_file ((long) hstat.st_size);
179 filestat.st_mode &= 07777;
180 if (filestat.st_mode != hstat.st_mode)
182 if (filestat.st_uid != hstat.st_uid)
184 if (filestat.st_gid != hstat.st_gid)
186 if (filestat.st_mtime != hstat.st_mtime)
188 if (head->header.linkflag != LF_SPARSE &&
189 filestat.st_size != hstat.st_size)
192 skip_file ((long) hstat.st_size);
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];
203 strcpy (&tmpbuf[1], current_file_name);
204 diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
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);
216 * Need to treat sparse files completely differently here.
218 if (head->header.linkflag == LF_SPARSE)
219 diff_sparse_files (hstat.st_size);
221 wantbytes ((long) (hstat.st_size), compare_chunk);
223 check = close (diff_fd);
225 msg_perror ("Error while closing %s", current_file_name);
232 if (do_stat (&filestat))
234 dev = filestat.st_dev;
235 ino = filestat.st_ino;
236 err = stat (current_link_name, &filestat);
241 fprintf (msg_file, "%s: does not exist\n", current_file_name);
245 msg_perror ("cannot stat file %s", current_file_name);
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);
261 char linkbuf[NAMSIZ + 3];
262 check = readlink (current_file_name, linkbuf,
263 (sizeof linkbuf) - 1);
270 "%s: no such file or directory\n",
275 msg_perror ("cannot read link %s", current_file_name);
281 linkbuf[check] = '\0'; /* Null-terminate it */
282 if (strncmp (current_link_name, linkbuf, check) != 0)
284 fprintf (msg_file, "%s: symlink differs\n",
294 hstat.st_mode |= S_IFCHR;
299 /* If local system doesn't support block devices, use default case */
301 hstat.st_mode |= S_IFBLK;
306 /* If local system doesn't support FIFOs, use default case */
309 hstat.st_mode |= S_IFIFO;
311 hstat.st_rdev = 0; /* FIXME, do we need this? */
316 /* FIXME, deal with umask */
317 if (do_stat (&filestat))
319 if (hstat.st_rdev != filestat.st_rdev)
321 fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
326 if (hstat.st_mode != filestat.st_mode)
327 #else /* POSIX lossage */
328 if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
331 fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
338 data = diff_dir = get_dir_contents (current_file_name, 0);
341 wantbytes ((long) (hstat.st_size), compare_dir);
345 wantbytes ((long) (hstat.st_size), no_op);
349 /* Check for trailing / */
350 namelen = strlen (current_file_name) - 1;
352 while (namelen && current_file_name[namelen] == '/')
353 current_file_name[namelen--] = '\0'; /* Zap / */
355 if (do_stat (&filestat))
357 if (!S_ISDIR (filestat.st_mode))
359 fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
363 if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
371 namelen = strlen (current_file_name) - 1;
372 if (current_file_name[namelen] == '/')
375 if (do_stat (&filestat))
378 if (!S_ISREG (filestat.st_mode))
380 fprintf (msg_file, "%s: not a regular file\n",
382 skip_file ((long) hstat.st_size);
387 filestat.st_mode &= 07777;
388 offset = from_oct (1 + 12, head->header.offset);
389 if (filestat.st_size != hstat.st_size + offset)
392 skip_file ((long) hstat.st_size);
397 diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
401 msg_perror ("cannot open file %s", current_file_name);
402 skip_file ((long) hstat.st_size);
406 err = lseek (diff_fd, offset, 0);
409 msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
414 wantbytes ((long) (hstat.st_size), compare_chunk);
416 check = close (diff_fd);
419 msg_perror ("Error while closing %s", current_file_name);
425 /* We don't need to save it any longer. */
426 saverec ((union record **) 0);/* Unsave it */
430 compare_chunk (bytes, buffer)
436 err = read (diff_fd, diff_buf, bytes);
441 msg_perror ("can't read %s", current_file_name);
445 fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
450 if (bcmp (buffer, diff_buf, bytes))
452 fprintf (msg_file, "%s: data differs\n", current_file_name);
460 compare_dir (bytes, buffer)
464 if (bcmp (buffer, diff_dir, bytes))
466 fprintf (msg_file, "%s: data differs\n", current_file_name);
475 * Sigh about something that differs.
482 fprintf (msg_file, "%s: %s differs\n",
483 current_file_name, what);
500 if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
502 if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
505 if (rmtlseek (archive, 0L, 0) != 0)
507 /* Lseek failed. Try a different method */
508 msg_perror ("Couldn't rewind archive file for verify");
520 status = read_header ();
529 status = read_header ();
532 msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
534 if (status == 2 || status == EOF)
549 err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
554 fprintf (msg_file, "%s: does not exist\n", current_file_name);
557 msg_perror ("can't stat file %s", current_file_name);
558 /* skip_file((long)hstat.st_size);
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.
576 diff_sparse_files (filesize)
582 int buf_size = RECORDSIZE;
583 union record *datarec;
586 /* int amt_read = 0;*/
589 buf = (char *) ck_malloc (buf_size * sizeof (char));
591 fill_in_sparse_array ();
596 datarec = findrec ();
597 if (!sparsearray[sparse_ind].numbytes)
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));
615 while (numbytes > RECORDSIZE)
617 if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
620 msg_perror ("can't read %s", current_file_name);
622 fprintf (msg_file, "%s: could only read %d of %d bytes\n",
623 current_file_name, err, numbytes);
626 if (bcmp (buf, datarec->charptr, RECORDSIZE))
634 datarec = findrec ();
636 if ((err = read (diff_fd, buf, numbytes)) != numbytes)
639 msg_perror ("can't read %s", current_file_name);
641 fprintf (msg_file, "%s: could only read %d of %d bytes\n",
642 current_file_name, err, numbytes);
646 if (bcmp (buf, datarec->charptr, numbytes))
651 /* amt_read += numbytes;
652 if (amt_read >= RECORDSIZE) {
662 * if the number of bytes read isn't the
663 * number of bytes supposedly in the file,
666 /* if (amt_read != filesize)
671 fprintf (msg_file, "%s: data differs\n", current_file_name);
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.
685 fill_in_sparse_array ()
690 * allocate space for our scratch space; it's initially
691 * 10 elements long, but can change in this routine if
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)
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
714 if (head->header.isextended)
716 /* how far into the sparsearray we are 'so far' */
717 static int so_far_ind = SPARSE_IN_HDR;
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));
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
748 if (!exhdr->ext_hdr.isextended)
752 so_far_ind += SPARSE_EXT_HDR;
756 /* be sure to skip past the last one */