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 */