]>
Dogcows Code - chaz/tar/blob - src/compare.c
1 /* Diff files from a tar archive.
2 Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
3 Written by John Gilmore, on 1987-04-30.
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option) any later
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 59 Place - Suite 330, Boston, MA 02111-1307, USA. */
22 # include <linux/fd.h>
28 /* Spare space for messages, hopefully safe even after gettext. */
29 #define MESSAGE_BUFFER_SIZE 100
31 /* Nonzero if we are verifying at the moment. */
32 int now_verifying
= 0;
34 /* File descriptor for the file we are diffing. */
35 static int diff_handle
;
37 /* Area for reading file contents into. */
38 static char *diff_buffer
= NULL
;
40 /*--------------------------------.
41 | Initialize for a diff operation |
42 `--------------------------------*/
47 diff_buffer
= (char *) valloc ((unsigned) record_size
);
50 _("Could not allocate memory for diff buffer of %d bytes"),
54 /*------------------------------------------------------------------------.
55 | Sigh about something that differs by writing a MESSAGE to stdlis, given |
56 | MESSAGE is not NULL. Also set the exit status if not already. |
57 `------------------------------------------------------------------------*/
60 report_difference (const char *message
)
63 fprintf (stdlis
, "%s: %s\n", current_file_name
, message
);
65 if (exit_status
== TAREXIT_SUCCESS
)
66 exit_status
= TAREXIT_DIFFERS
;
69 /*-----------------------------------------------------------------------.
70 | Takes a buffer returned by read_and_process and does nothing with it. |
71 `-----------------------------------------------------------------------*/
73 /* Yes, I know. SIZE and DATA are unused in this function. Some compilers
74 may even report it. That's OK, just relax! */
77 process_noop (long size
, char *data
)
87 process_rawdata (long bytes
, char *buffer
)
89 int status
= read (diff_handle
, diff_buffer
, (size_t) bytes
);
90 char message
[MESSAGE_BUFFER_SIZE
];
96 WARN ((0, errno
, _("Cannot read %s"), current_file_name
));
97 report_difference (NULL
);
101 sprintf (message
, _("Could only read %d of %ld bytes"),
103 report_difference (message
);
108 if (memcmp (buffer
, diff_buffer
, (size_t) bytes
))
110 report_difference (_("Data differs"));
121 /* Directory contents, only for GNUTYPE_DUMPDIR. */
123 static char *dumpdir_cursor
;
126 process_dumpdir (long bytes
, char *buffer
)
128 if (memcmp (buffer
, dumpdir_cursor
, (size_t) bytes
))
130 report_difference (_("Data differs"));
134 dumpdir_cursor
+= bytes
;
138 /*------------------------------------------------------------------------.
139 | Some other routine wants SIZE bytes in the archive. For each chunk of |
140 | the archive, call PROCESSOR with the size of the chunk, and the address |
141 | of the chunk it can work with. The PROCESSOR should return nonzero for |
142 | success. It it return error once, continue skipping without calling |
143 | PROCESSOR anymore. |
144 `------------------------------------------------------------------------*/
147 read_and_process (long size
, int (*processor
) (long, char *))
149 union block
*data_block
;
152 if (multi_volume_option
)
153 save_sizeleft
= size
;
156 data_block
= find_next_block ();
157 if (data_block
== NULL
)
159 ERROR ((0, 0, _("Unexpected EOF on archive file")));
163 data_size
= available_space_after (data_block
);
164 if (data_size
> size
)
166 if (!(*processor
) (data_size
, data_block
->buffer
))
167 processor
= process_noop
;
168 set_next_block_after ((union block
*)
169 (data_block
->buffer
+ data_size
- 1));
171 if (multi_volume_option
)
172 save_sizeleft
-= data_size
;
180 /* JK This routine should be used more often than it is ... look into
181 that. Anyhow, what it does is translate the sparse information on the
182 header, and in any subsequent extended headers, into an array of
183 structures with true numbers, as opposed to character strings. It
184 simply makes our life much easier, doing so many comparisong and such.
188 fill_in_sparse_array (void)
192 /* Allocate space for our scratch space; it's initially 10 elements
193 long, but can change in this routine if necessary. */
196 sparsearray
= (struct sp_array
*) xmalloc (sp_array_size
* sizeof (struct sp_array
));
198 /* There are at most five of these structures in the header itself;
199 read these in first. */
201 for (counter
= 0; counter
< SPARSES_IN_OLDGNU_HEADER
; counter
++)
203 /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler. */
204 if (current_header
->oldgnu_header
.sp
[counter
].numbytes
== 0)
207 sparsearray
[counter
].offset
=
208 from_oct (1 + 12, current_header
->oldgnu_header
.sp
[counter
].offset
);
209 sparsearray
[counter
].numbytes
=
210 from_oct (1 + 12, current_header
->oldgnu_header
.sp
[counter
].numbytes
);
213 /* If the header's extended, we gotta read in exhdr's till we're done. */
215 if (current_header
->oldgnu_header
.isextended
)
217 /* How far into the sparsearray we are `so far'. */
218 static int so_far_ind
= SPARSES_IN_OLDGNU_HEADER
;
223 exhdr
= find_next_block ();
224 for (counter
= 0; counter
< SPARSES_IN_SPARSE_HEADER
; counter
++)
226 if (counter
+ so_far_ind
> sp_array_size
- 1)
228 /* We just ran out of room in our scratch area -
232 sparsearray
= (struct sp_array
*)
233 xrealloc (sparsearray
,
234 sp_array_size
* sizeof (struct sp_array
));
237 /* Convert the character strings into longs. */
239 sparsearray
[counter
+ so_far_ind
].offset
=
240 from_oct (1 + 12, exhdr
->sparse_header
.sp
[counter
].offset
);
241 sparsearray
[counter
+ so_far_ind
].numbytes
=
242 from_oct (1 + 12, exhdr
->sparse_header
.sp
[counter
].numbytes
);
245 /* If this is the last extended header for this file, we can
248 if (!exhdr
->sparse_header
.isextended
)
251 so_far_ind
+= SPARSES_IN_SPARSE_HEADER
;
252 set_next_block_after (exhdr
);
255 /* Be sure to skip past the last one. */
257 set_next_block_after (exhdr
);
265 /* JK Diff'ing a sparse file with its counterpart on the tar file is a
266 bit of a different story than a normal file. First, we must know what
267 areas of the file to skip through, i.e., we need to contruct a
268 sparsearray, which will hold all the information we need. We must
269 compare small amounts of data at a time as we find it. */
271 /* FIXME: This does not look very solid to me, at first glance. Zero areas
272 are not checked, spurious sparse entries seemingly goes undetected, and
273 I'm not sure overall identical sparsity is verified. */
276 diff_sparse_files (int size_of_file
)
278 int remaining_size
= size_of_file
;
279 char *buffer
= (char *) xmalloc (BLOCKSIZE
* sizeof (char));
280 int buffer_size
= BLOCKSIZE
;
281 union block
*data_block
= NULL
;
285 fill_in_sparse_array ();
287 while (remaining_size
> 0)
295 data_block
= find_next_block ();
296 chunk_size
= sparsearray
[counter
].numbytes
;
300 lseek (diff_handle
, sparsearray
[counter
].offset
, 0);
302 /* Take care to not run out of room in our buffer. */
304 while (buffer_size
< chunk_size
)
307 buffer
= (char *) xrealloc (buffer
, buffer_size
* sizeof (char));
310 while (chunk_size
> BLOCKSIZE
)
312 if (status
= read (diff_handle
, buffer
, BLOCKSIZE
),
317 WARN ((0, errno
, _("Cannot read %s"), current_file_name
));
318 report_difference (NULL
);
322 char message
[MESSAGE_BUFFER_SIZE
];
324 sprintf (message
, _("Could only read %d of %ld bytes"),
326 report_difference (message
);
331 if (memcmp (buffer
, data_block
->buffer
, BLOCKSIZE
))
337 chunk_size
-= status
;
338 remaining_size
-= status
;
339 set_next_block_after (data_block
);
340 data_block
= find_next_block ();
342 if (status
= read (diff_handle
, buffer
, (size_t) chunk_size
),
343 status
!= chunk_size
)
347 WARN ((0, errno
, _("Cannot read %s"), current_file_name
));
348 report_difference (NULL
);
352 char message
[MESSAGE_BUFFER_SIZE
];
354 sprintf (message
, _("Could only read %d of %ld bytes"),
356 report_difference (message
);
361 if (memcmp (buffer
, data_block
->buffer
, (size_t) chunk_size
))
367 amount_read
+= chunk_size
;
368 if (amount_read
>= BLOCKSIZE
)
371 set_next_block_after (data_block
);
372 data_block
= find_next_block ();
375 set_next_block_after (data_block
);
377 remaining_size
-= chunk_size
;
381 /* If the number of bytes read isn't the number of bytes supposedly in
382 the file, they're different. */
384 if (amount_read
!= size_of_file
)
388 set_next_block_after (data_block
);
392 report_difference (_("Data differs"));
395 /*---------------------------------------------------------------------.
396 | Call either stat or lstat over STAT_DATA, depending on --dereference |
397 | (-h), for a file which should exist. Diagnose any problem. Return |
398 | nonzero for success, zero otherwise. |
399 `---------------------------------------------------------------------*/
402 get_stat_data (struct stat
*stat_data
)
404 int status
= (dereference_option
405 ? stat (current_file_name
, stat_data
)
406 : lstat (current_file_name
, stat_data
));
411 report_difference (_("File does not exist"));
414 ERROR ((0, errno
, _("Cannot stat file %s"), current_file_name
));
415 report_difference (NULL
);
418 skip_file ((long) current_stat
.st_size
);
426 /*----------------------------------.
427 | Diff a file against the archive. |
428 `----------------------------------*/
433 struct stat stat_data
;
437 errno
= EPIPE
; /* FIXME: errno should be read-only */
438 /* FIXME: remove perrors */
440 set_next_block_after (current_header
);
441 decode_header (current_header
, ¤t_stat
, ¤t_format
, 1);
443 /* Print the block from `current_header' and `current_stat'. */
448 fprintf (stdlis
, _("Verify "));
452 switch (current_header
->header
.typeflag
)
455 WARN ((0, 0, _("Unknown file type '%c' for %s, diffed as normal file"),
456 current_header
->header
.typeflag
, current_file_name
));
464 /* Appears to be a file. See if it's really a directory. */
466 name_length
= strlen (current_file_name
) - 1;
467 if (current_file_name
[name_length
] == '/')
470 if (!get_stat_data (&stat_data
))
472 if (current_header
->oldgnu_header
.isextended
)
473 skip_extended_headers ();
474 skip_file ((long) current_stat
.st_size
);
478 if (!S_ISREG (stat_data
.st_mode
))
480 report_difference (_("Not a regular file"));
481 skip_file ((long) current_stat
.st_size
);
485 stat_data
.st_mode
&= 07777;
486 if (stat_data
.st_mode
!= current_stat
.st_mode
)
487 report_difference (_("Mode differs"));
490 /* stat() in djgpp's C library gives a constant number of 42 as the
491 uid and gid of a file. So, comparing an FTP'ed archive just after
492 unpack would fail on MSDOS. */
493 if (stat_data
.st_uid
!= current_stat
.st_uid
)
494 report_difference (_("Uid differs"));
495 if (stat_data
.st_gid
!= current_stat
.st_gid
)
496 report_difference (_("Gid differs"));
499 if (stat_data
.st_mtime
!= current_stat
.st_mtime
)
500 report_difference (_("Mod time differs"));
501 if (current_header
->header
.typeflag
!= GNUTYPE_SPARSE
&&
502 stat_data
.st_size
!= current_stat
.st_size
)
504 report_difference (_("Size differs"));
505 skip_file ((long) current_stat
.st_size
);
509 diff_handle
= open (current_file_name
, O_NDELAY
| O_RDONLY
| O_BINARY
);
511 if (diff_handle
< 0 && !absolute_names_option
)
513 char *tmpbuf
= xmalloc (strlen (current_file_name
) + 2);
516 strcpy (tmpbuf
+ 1, current_file_name
);
517 diff_handle
= open (tmpbuf
, O_NDELAY
| O_RDONLY
);
522 ERROR ((0, errno
, _("Cannot open %s"), current_file_name
));
523 if (current_header
->oldgnu_header
.isextended
)
524 skip_extended_headers ();
525 skip_file ((long) current_stat
.st_size
);
526 report_difference (NULL
);
530 /* Need to treat sparse files completely differently here. */
532 if (current_header
->header
.typeflag
== GNUTYPE_SPARSE
)
533 diff_sparse_files (current_stat
.st_size
);
536 if (multi_volume_option
)
538 assign_string (&save_name
, current_file_name
);
539 save_totsize
= current_stat
.st_size
;
540 /* save_sizeleft is set in read_and_process. */
543 read_and_process ((long) (current_stat
.st_size
), process_rawdata
);
545 if (multi_volume_option
)
546 assign_string (&save_name
, NULL
);
549 status
= close (diff_handle
);
551 ERROR ((0, errno
, _("Error while closing %s"), current_file_name
));
562 if (!get_stat_data (&stat_data
))
565 dev
= stat_data
.st_dev
;
566 ino
= stat_data
.st_ino
;
567 status
= stat (current_link_name
, &stat_data
);
571 report_difference (_("Does not exist"));
574 WARN ((0, errno
, _("Cannot stat file %s"), current_file_name
));
575 report_difference (NULL
);
580 if (stat_data
.st_dev
!= dev
|| stat_data
.st_ino
!= ino
)
582 char *message
= (char *)
583 xmalloc (MESSAGE_BUFFER_SIZE
+ strlen (current_link_name
));
585 sprintf (message
, _("Not linked to %s"), current_link_name
);
586 report_difference (message
);
593 #endif /* not MSDOS */
598 char linkbuf
[NAME_FIELD_SIZE
+ 3]; /* FIXME: may be too short. */
600 status
= readlink (current_file_name
, linkbuf
, (sizeof linkbuf
) - 1);
605 report_difference (_("No such file or directory"));
608 WARN ((0, errno
, _("Cannot read link %s"), current_file_name
));
609 report_difference (NULL
);
614 linkbuf
[status
] = '\0'; /* null-terminate it */
615 if (strncmp (current_link_name
, linkbuf
, (size_t) status
) != 0)
616 report_difference (_("Symlink differs"));
620 #endif /* not S_ISLNK */
624 current_stat
.st_mode
|= S_IFCHR
;
626 #endif /* not S_IFCHR */
629 /* If local system doesn't support block devices, use default case. */
632 current_stat
.st_mode
|= S_IFBLK
;
634 #endif /* not S_IFBLK */
637 /* If local system doesn't support FIFOs, use default case. */
641 current_stat
.st_mode
|= S_IFIFO
;
643 current_stat
.st_rdev
= 0; /* FIXME: do we need this? */
645 #endif /* S_ISFIFO */
648 /* FIXME: deal with umask. */
650 if (!get_stat_data (&stat_data
))
653 if (current_stat
.st_rdev
!= stat_data
.st_rdev
)
655 report_difference (_("Device numbers changed"));
661 current_stat
.st_mode
!= stat_data
.st_mode
664 (current_stat
.st_mode
& 07777) != (stat_data
.st_mode
& 07777)
668 report_difference (_("Mode or device-type changed"));
674 case GNUTYPE_DUMPDIR
:
676 char *dumpdir_buffer
= get_directory_contents (current_file_name
, 0);
678 if (multi_volume_option
)
680 assign_string (&save_name
, current_file_name
);
681 save_totsize
= current_stat
.st_size
;
682 /* save_sizeleft is set in read_and_process. */
687 dumpdir_cursor
= dumpdir_buffer
;
688 read_and_process ((long) (current_stat
.st_size
), process_dumpdir
);
689 free (dumpdir_buffer
);
692 read_and_process ((long) (current_stat
.st_size
), process_noop
);
694 if (multi_volume_option
)
695 assign_string (&save_name
, NULL
);
700 /* Check for trailing /. */
702 name_length
= strlen (current_file_name
) - 1;
705 while (name_length
&& current_file_name
[name_length
] == '/')
706 current_file_name
[name_length
--] = '\0'; /* zap / */
708 if (!get_stat_data (&stat_data
))
711 if (!S_ISDIR (stat_data
.st_mode
))
713 report_difference (_("No longer a directory"));
717 if ((stat_data
.st_mode
& 07777) != (current_stat
.st_mode
& 07777))
718 report_difference (_("Mode differs"));
724 case GNUTYPE_MULTIVOL
:
728 name_length
= strlen (current_file_name
) - 1;
729 if (current_file_name
[name_length
] == '/')
732 if (!get_stat_data (&stat_data
))
735 if (!S_ISREG (stat_data
.st_mode
))
737 report_difference (_("Not a regular file"));
738 skip_file ((long) current_stat
.st_size
);
742 stat_data
.st_mode
&= 07777;
743 offset
= from_oct (1 + 12, current_header
->oldgnu_header
.offset
);
744 if (stat_data
.st_size
!= current_stat
.st_size
+ offset
)
746 report_difference (_("Size differs"));
747 skip_file ((long) current_stat
.st_size
);
751 diff_handle
= open (current_file_name
, O_NDELAY
| O_RDONLY
| O_BINARY
);
755 WARN ((0, errno
, _("Cannot open file %s"), current_file_name
));
756 report_difference (NULL
);
757 skip_file ((long) current_stat
.st_size
);
761 status
= lseek (diff_handle
, offset
, 0);
762 if (status
!= offset
)
764 WARN ((0, errno
, _("Cannot seek to %ld in file %s"),
765 offset
, current_file_name
));
766 report_difference (NULL
);
770 if (multi_volume_option
)
772 assign_string (&save_name
, current_file_name
);
773 save_totsize
= stat_data
.st_size
;
774 /* save_sizeleft is set in read_and_process. */
777 read_and_process ((long) (current_stat
.st_size
), process_rawdata
);
779 if (multi_volume_option
)
780 assign_string (&save_name
, NULL
);
782 status
= close (diff_handle
);
784 ERROR ((0, errno
, _("Error while closing %s"), current_file_name
));
801 /* Verifying an archive is meant to check if the physical media got it
802 correctly, so try to defeat clever in-memory buffering pertaining to
803 this particular media. On Linux, for example, the floppy drive would
804 not even be accessed for the whole verification.
806 The code was using fsync only when the ioctl is unavailable, but
807 Marty Leisner says that the ioctl does not work when not preceded by
808 fsync. So, until we know better, or maybe to please Marty, let's do it
809 the unbelievable way :-). */
815 ioctl (archive
, FDFLUSH
);
820 struct mtop operation
;
823 operation
.mt_op
= MTBSF
;
824 operation
.mt_count
= 1;
825 if (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
), status
< 0)
828 || (status
= rmtioctl (archive
, MTIOCTOP
, (char *) &operation
),
832 if (rmtlseek (archive
, 0L, 0) != 0)
834 /* Lseek failed. Try a different method. */
837 _("Could not rewind archive file for verify")));
846 access_mode
= ACCESS_READ
;
852 enum read_header status
= read_header ();
854 if (status
== HEADER_FAILURE
)
858 while (status
== HEADER_FAILURE
);
861 status
= read_header ();
864 _("VERIFY FAILURE: %d invalid header(s) detected"), counter
));
866 if (status
== HEADER_ZERO_BLOCK
|| status
== HEADER_END_OF_FILE
)
872 access_mode
= ACCESS_WRITE
;
This page took 0.06931 seconds and 4 git commands to generate.