]>
Dogcows Code - chaz/tar/blob - src/diffarch.c
1 /* Diff files from a tar archive.
2 Copyright (C) 1988 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>
42 #include <sys/ioctl.h>
54 extern char *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 */
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
);
98 msg("could not allocate memory for diff buffer of %d bytes",
105 * Diff a file against the archive.
114 struct stat filestat
;
122 char *get_dir_contents();
125 errno
= EPIPE
; /* FIXME, remove perrors */
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 */
131 /* Print the record from 'head' and 'hstat' */
134 fprintf(msg_file
,"Verify ");
138 switch (head
->header
.linkflag
) {
141 msg("Unknown file type '%c' for %s, diffed as normal file",
142 head
->header
.linkflag
, head
->header
.name
);
150 * Appears to be a file.
151 * See if it's really a directory.
153 namelen
= strlen(head
->header
.name
)-1;
154 if (head
->header
.name
[namelen
] == '/')
158 if(do_stat(&filestat
)) {
159 if (head
->header
.isextended
)
160 skip_extended_headers();
161 skip_file((long)hstat
.st_size
);
166 if (!S_ISREG(filestat
.st_mode
)) {
167 fprintf(msg_file
, "%s: not a regular file\n",
169 skip_file((long)hstat
.st_size
);
174 filestat
.st_mode
&= 07777;
175 if (filestat
.st_mode
!= hstat
.st_mode
)
177 if (filestat
.st_uid
!= hstat
.st_uid
)
179 if (filestat
.st_gid
!= hstat
.st_gid
)
181 if (filestat
.st_mtime
!= hstat
.st_mtime
)
183 if (head
->header
.linkflag
!= LF_SPARSE
&&
184 filestat
.st_size
!= hstat
.st_size
) {
186 skip_file((long)hstat
.st_size
);
190 diff_fd
= open(head
->header
.name
, O_NDELAY
|O_RDONLY
|O_BINARY
);
192 if (diff_fd
< 0 && !f_absolute_paths
) {
193 char tmpbuf
[NAMSIZ
+2];
196 strcpy(&tmpbuf
[1],head
->header
.name
);
197 diff_fd
=open(tmpbuf
, O_NDELAY
|O_RDONLY
);
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
);
208 * Need to treat sparse files completely differently here.
210 if (head
->header
.linkflag
== LF_SPARSE
)
211 diff_sparse_files(hstat
.st_size
);
213 wantbytes((long)(hstat
.st_size
),compare_chunk
);
215 check
= close(diff_fd
);
217 msg_perror("Error while closing %s",head
->header
.name
);
224 if(do_stat(&filestat
))
226 dev
= filestat
.st_dev
;
227 ino
= filestat
.st_ino
;
228 err
= stat(head
->header
.linkname
, &filestat
);
231 fprintf(msg_file
, "%s: does not exist\n",head
->header
.name
);
233 msg_perror("cannot stat file %s",head
->header
.name
);
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
);
248 char linkbuf
[NAMSIZ
+3];
249 check
= readlink(head
->header
.name
, linkbuf
,
253 if (errno
== ENOENT
) {
255 "%s: no such file or directory\n",
258 msg_perror("cannot read link %s",head
->header
.name
);
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
);
276 hstat
.st_mode
|= S_IFCHR
;
281 /* If local system doesn't support block devices, use default case */
283 hstat
.st_mode
|= S_IFBLK
;
288 /* If local system doesn't support FIFOs, use default case */
291 hstat
.st_mode
|= S_IFIFO
;
293 hstat
.st_rdev
= 0; /* FIXME, do we need this? */
298 /* FIXME, deal with umask */
299 if(do_stat(&filestat
))
301 if(hstat
.st_rdev
!= filestat
.st_rdev
) {
302 fprintf(msg_file
, "%s: device numbers changed\n", head
->header
.name
);
307 if(hstat
.st_mode
!= filestat
.st_mode
)
308 #else /* POSIX lossage */
309 if((hstat
.st_mode
& 07777) != (filestat
.st_mode
& 07777))
312 fprintf(msg_file
, "%s: mode or device-type changed\n", head
->header
.name
);
319 data
=diff_dir
=get_dir_contents(head
->header
.name
,0);
321 wantbytes((long)(hstat
.st_size
),compare_dir
);
324 wantbytes((long)(hstat
.st_size
),no_op
);
328 /* Check for trailing / */
329 namelen
= strlen(head
->header
.name
)-1;
331 while (namelen
&& head
->header
.name
[namelen
] == '/')
332 head
->header
.name
[namelen
--] = '\0'; /* Zap / */
334 if(do_stat(&filestat
))
336 if(!S_ISDIR(filestat
.st_mode
)) {
337 fprintf(msg_file
, "%s is no longer a directory\n",head
->header
.name
);
341 if((filestat
.st_mode
&07777) != (hstat
.st_mode
&07777))
349 namelen
= strlen(head
->header
.name
)-1;
350 if (head
->header
.name
[namelen
] == '/')
353 if(do_stat(&filestat
))
356 if (!S_ISREG(filestat
.st_mode
)) {
357 fprintf(msg_file
, "%s: not a regular file\n",
359 skip_file((long)hstat
.st_size
);
364 filestat
.st_mode
&= 07777;
365 offset
= from_oct(1+12, head
->header
.offset
);
366 if (filestat
.st_size
!= hstat
.st_size
+ offset
) {
368 skip_file((long)hstat
.st_size
);
373 diff_fd
= open(head
->header
.name
, O_NDELAY
|O_RDONLY
|O_BINARY
);
376 msg_perror("cannot open file %s",head
->header
.name
);
377 skip_file((long)hstat
.st_size
);
381 err
= lseek(diff_fd
, offset
, 0);
383 msg_perror("cannot seek to %ld in file %s",offset
,head
->header
.name
);
388 wantbytes((long)(hstat
.st_size
),compare_chunk
);
390 check
= close(diff_fd
);
392 msg_perror("Error while closing %s",head
->header
.name
);
398 /* We don't need to save it any longer. */
399 saverec((union record
**) 0); /* Unsave it */
403 compare_chunk(bytes
,buffer
)
409 err
=read(diff_fd
,diff_buf
,bytes
);
412 msg_perror("can't read %s",head
->header
.name
);
414 fprintf(msg_file
,"%s: could only read %d of %d bytes\n",head
->header
.name
,err
,bytes
);
419 if(bcmp(buffer
,diff_buf
,bytes
)) {
420 fprintf(msg_file
, "%s: data differs\n",head
->header
.name
);
428 compare_dir(bytes
,buffer
)
432 if(bcmp(buffer
,diff_dir
,bytes
)) {
433 fprintf(msg_file
, "%s: data differs\n",head
->header
.name
);
442 * Sigh about something that differs.
449 fprintf(msg_file
, "%s: %s differs\n",
450 head
->header
.name
, what
);
467 if((er
=rmtioctl(archive
,MTIOCTOP
,&t
))<0) {
468 if(errno
!=EIO
|| (er
=rmtioctl(archive
,MTIOCTOP
,&t
))<0) {
470 if(rmtlseek(archive
,0L,0)!=0) {
471 /* Lseek failed. Try a different method */
472 msg_perror("Couldn't rewind archive file for verify");
483 status
= read_header();
490 status
=read_header();
492 msg("VERIFY FAILURE: %d invalid header%s detected!",n
,n
==1?"":"s");
494 if(status
==2 || status
==EOF
)
509 err
= f_follow_links
? stat(head
->header
.name
, statp
) : lstat(head
->header
.name
, statp
);
512 fprintf(msg_file
, "%s: does not exist\n",head
->header
.name
);
514 msg_perror("can't stat file %s",head
->header
.name
);
515 /* skip_file((long)hstat.st_size);
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.
532 diff_sparse_files(filesize
)
538 int buf_size
= RECORDSIZE
;
539 union record
*datarec
;
542 /* int amt_read = 0;*/
545 buf
= (char *) malloc(buf_size
* sizeof (char));
547 fill_in_sparse_array();
552 if (!sparsearray
[sparse_ind
].numbytes
)
556 * 'numbytes' is nicer to write than
557 * 'sparsearray[sparse_ind].numbytes' all the time ...
559 numbytes
= sparsearray
[sparse_ind
].numbytes
;
561 lseek(diff_fd
, sparsearray
[sparse_ind
].offset
, 0);
563 * take care to not run out of room in our buffer
565 while (buf_size
< numbytes
) {
566 buf
= (char *) realloc(buf
, buf_size
* 2 * sizeof(char));
569 while (numbytes
> RECORDSIZE
) {
570 if ((err
= read(diff_fd
, buf
, RECORDSIZE
)) != RECORDSIZE
) {
572 msg_perror("can't read %s", head
->header
.name
);
574 fprintf(msg_file
, "%s: could only read %d of %d bytes\n",
578 if (bcmp(buf
, datarec
->charptr
, RECORDSIZE
)) {
587 if ((err
= read(diff_fd
, buf
, numbytes
)) != numbytes
) {
589 msg_perror("can't read %s", head
->header
.name
);
591 fprintf(msg_file
, "%s: could only read %d of %d bytes\n",
596 if (bcmp(buf
, datarec
->charptr
, numbytes
)) {
600 /* amt_read += numbytes;
601 if (amt_read >= RECORDSIZE) {
611 * if the number of bytes read isn't the
612 * number of bytes supposedly in the file,
615 /* if (amt_read != filesize)
620 fprintf(msg_file
, "%s: data differs\n", head
->header
.name
);
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.
634 fill_in_sparse_array()
639 * allocate space for our scratch space; it's initially
640 * 10 elements long, but can change in this routine if
644 sparsearray
= (struct sp_array
*) malloc(sp_array_size
* sizeof(struct sp_array
));
647 * there are at most five of these structures in the header
648 * itself; read these in first
650 for (ind
= 0; ind
< SPARSE_IN_HDR
; ind
++) {
651 if (!head
->header
.sp
[ind
].numbytes
)
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
);
659 * if the header's extended, we gotta read in exhdr's till
662 if (head
->header
.isextended
) {
663 /* how far into the sparsearray we are 'so far' */
664 static int so_far_ind
= SPARSE_IN_HDR
;
669 for (ind
= 0; ind
< SPARSE_EXT_HDR
; ind
++) {
670 if (ind
+so_far_ind
> sp_array_size
-1) {
672 * we just ran out of room in our
673 * scratch area - realloc it
675 sparsearray
= (struct sp_array
*)
677 sp_array_size
*2*sizeof(struct sp_array
));
681 * convert the character strings into longs
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
);
689 * if this is the last extended header for this
692 if (!exhdr
->ext_hdr
.isextended
)
695 so_far_ind
+= SPARSE_EXT_HDR
;
699 /* be sure to skip past the last one */
This page took 0.06395 seconds and 5 git commands to generate.