d4871c44350d9bb4d88c28390d75196f27775481
[debian/tar] / 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.
4
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
8    version.
9
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.
14
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.  */
18
19 #include "system.h"
20
21 #if HAVE_LINUX_FD_H
22 # include <linux/fd.h>
23 #endif
24
25 #include "common.h"
26 #include "rmt.h"
27
28 /* Spare space for messages, hopefully safe even after gettext.  */
29 #define MESSAGE_BUFFER_SIZE 100
30
31 /* Nonzero if we are verifying at the moment.  */
32 int now_verifying = 0;
33
34 /* File descriptor for the file we are diffing.  */
35 static int diff_handle;
36
37 /* Area for reading file contents into.  */
38 static char *diff_buffer = NULL;
39
40 /*--------------------------------.
41 | Initialize for a diff operation |
42 `--------------------------------*/
43
44 void
45 diff_init (void)
46 {
47   diff_buffer = (char *) valloc ((unsigned) record_size);
48   if (!diff_buffer)
49     FATAL_ERROR ((0, 0,
50                   _("Could not allocate memory for diff buffer of %d bytes"),
51                   record_size));
52 }
53
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 `------------------------------------------------------------------------*/
58
59 static void
60 report_difference (const char *message)
61 {
62   if (message)
63     fprintf (stdlis, "%s: %s\n", current_file_name, message);
64
65   if (exit_status == TAREXIT_SUCCESS)
66     exit_status = TAREXIT_DIFFERS;
67 }
68
69 /*-----------------------------------------------------------------------.
70 | Takes a buffer returned by read_and_process and does nothing with it.  |
71 `-----------------------------------------------------------------------*/
72
73 /* Yes, I know.  SIZE and DATA are unused in this function.  Some compilers
74    may even report it.  That's OK, just relax!  */
75
76 static int
77 process_noop (long size, char *data)
78 {
79   return 1;
80 }
81
82 /*---.
83 | ?  |
84 `---*/
85
86 static int
87 process_rawdata (long bytes, char *buffer)
88 {
89   int status = read (diff_handle, diff_buffer, (size_t) bytes);
90   char message[MESSAGE_BUFFER_SIZE];
91
92   if (status != bytes)
93     {
94       if (status < 0)
95         {
96           WARN ((0, errno, _("Cannot read %s"), current_file_name));
97           report_difference (NULL);
98         }
99       else
100         {
101           sprintf (message, _("Could only read %d of %ld bytes"),
102                    status, bytes);
103           report_difference (message);
104         }
105       return 0;
106     }
107
108   if (memcmp (buffer, diff_buffer, (size_t) bytes))
109     {
110       report_difference (_("Data differs"));
111       return 0;
112     }
113
114   return 1;
115 }
116
117 /*---.
118 | ?  |
119 `---*/
120
121 /* Directory contents, only for GNUTYPE_DUMPDIR.  */
122
123 static char *dumpdir_cursor;
124
125 static int
126 process_dumpdir (long bytes, char *buffer)
127 {
128   if (memcmp (buffer, dumpdir_cursor, (size_t) bytes))
129     {
130       report_difference (_("Data differs"));
131       return 0;
132     }
133
134   dumpdir_cursor += bytes;
135   return 1;
136 }
137
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 `------------------------------------------------------------------------*/
145
146 static void
147 read_and_process (long size, int (*processor) (long, char *))
148 {
149   union block *data_block;
150   long data_size;
151
152   if (multi_volume_option)
153     save_sizeleft = size;
154   while (size)
155     {
156       data_block = find_next_block ();
157       if (data_block == NULL)
158         {
159           ERROR ((0, 0, _("Unexpected EOF on archive file")));
160           return;
161         }
162
163       data_size = available_space_after (data_block);
164       if (data_size > size)
165         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));
170       size -= data_size;
171       if (multi_volume_option)
172         save_sizeleft -= data_size;
173     }
174 }
175
176 /*---.
177 | ?  |
178 `---*/
179
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.
185    */
186
187 static void
188 fill_in_sparse_array (void)
189 {
190   int counter;
191
192   /* Allocate space for our scratch space; it's initially 10 elements
193      long, but can change in this routine if necessary.  */
194
195   sp_array_size = 10;
196   sparsearray = (struct sp_array *) xmalloc (sp_array_size * sizeof (struct sp_array));
197
198   /* There are at most five of these structures in the header itself;
199      read these in first.  */
200
201   for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
202     {
203       /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler.  */
204       if (current_header->oldgnu_header.sp[counter].numbytes == 0)
205         break;
206
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);
211     }
212
213   /* If the header's extended, we gotta read in exhdr's till we're done.  */
214
215   if (current_header->oldgnu_header.isextended)
216     {
217       /* How far into the sparsearray we are `so far'.  */
218       static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
219       union block *exhdr;
220
221       while (1)
222         {
223           exhdr = find_next_block ();
224           for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
225             {
226               if (counter + so_far_ind > sp_array_size - 1)
227                 {
228                   /* We just ran out of room in our scratch area -
229                      realloc it.  */
230
231                   sp_array_size *= 2;
232                   sparsearray = (struct sp_array *)
233                     xrealloc (sparsearray,
234                               sp_array_size * sizeof (struct sp_array));
235                 }
236
237               /* Convert the character strings into longs.  */
238
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);
243             }
244
245           /* If this is the last extended header for this file, we can
246              stop.  */
247
248           if (!exhdr->sparse_header.isextended)
249             break;
250
251           so_far_ind += SPARSES_IN_SPARSE_HEADER;
252           set_next_block_after (exhdr);
253         }
254
255       /* Be sure to skip past the last one.  */
256
257       set_next_block_after (exhdr);
258     }
259 }
260
261 /*---.
262 | ?  |
263 `---*/
264
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.  */
270
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.  */
274
275 static void
276 diff_sparse_files (int size_of_file)
277 {
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;
282   int counter = 0;
283   int different = 0;
284
285   fill_in_sparse_array ();
286
287   while (remaining_size > 0)
288     {
289       int status;
290       long chunk_size;
291 #if 0
292       int amount_read = 0;
293 #endif
294
295       data_block = find_next_block ();
296       chunk_size = sparsearray[counter].numbytes;
297       if (!chunk_size)
298         break;
299
300       lseek (diff_handle, sparsearray[counter].offset, 0);
301
302       /* Take care to not run out of room in our buffer.  */
303
304       while (buffer_size < chunk_size)
305         {
306           buffer_size *= 2;
307           buffer = (char *) xrealloc (buffer, buffer_size * sizeof (char));
308         }
309
310       while (chunk_size > BLOCKSIZE)
311         {
312           if (status = read (diff_handle, buffer, BLOCKSIZE),
313               status != BLOCKSIZE)
314             {
315               if (status < 0)
316                 {
317                   WARN ((0, errno, _("Cannot read %s"), current_file_name));
318                   report_difference (NULL);
319                 }
320               else
321                 {
322                   char message[MESSAGE_BUFFER_SIZE];
323
324                   sprintf (message, _("Could only read %d of %ld bytes"),
325                            status, chunk_size);
326                   report_difference (message);
327                 }
328               break;
329             }
330
331           if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
332             {
333               different = 1;
334               break;
335             }
336
337           chunk_size -= status;
338           remaining_size -= status;
339           set_next_block_after (data_block);
340           data_block = find_next_block ();
341         }
342       if (status = read (diff_handle, buffer, (size_t) chunk_size),
343           status != chunk_size)
344         {
345           if (status < 0)
346             {
347               WARN ((0, errno, _("Cannot read %s"), current_file_name));
348               report_difference (NULL);
349             }
350           else
351             {
352               char message[MESSAGE_BUFFER_SIZE];
353
354               sprintf (message, _("Could only read %d of %ld bytes"),
355                        status, chunk_size);
356               report_difference (message);
357             }
358           break;
359         }
360
361       if (memcmp (buffer, data_block->buffer, (size_t) chunk_size))
362         {
363           different = 1;
364           break;
365         }
366 #if 0
367       amount_read += chunk_size;
368       if (amount_read >= BLOCKSIZE)
369         {
370           amount_read = 0;
371           set_next_block_after (data_block);
372           data_block = find_next_block ();
373         }
374 #endif
375       set_next_block_after (data_block);
376       counter++;
377       remaining_size -= chunk_size;
378     }
379
380 #if 0
381   /* If the number of bytes read isn't the number of bytes supposedly in
382      the file, they're different.  */
383
384   if (amount_read != size_of_file)
385     different = 1;
386 #endif
387
388   set_next_block_after (data_block);
389   free (sparsearray);
390
391   if (different)
392     report_difference (_("Data differs"));
393 }
394
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 `---------------------------------------------------------------------*/
400
401 static int
402 get_stat_data (struct stat *stat_data)
403 {
404   int status = (dereference_option
405                 ? stat (current_file_name, stat_data)
406                 : lstat (current_file_name, stat_data));
407
408   if (status < 0)
409     {
410       if (errno == ENOENT)
411         report_difference (_("File does not exist"));
412       else
413         {
414           ERROR ((0, errno, _("Cannot stat file %s"), current_file_name));
415           report_difference (NULL);
416         }
417 #if 0
418       skip_file ((long) current_stat.st_size);
419 #endif
420       return 0;
421     }
422
423   return 1;
424 }
425
426 /*----------------------------------.
427 | Diff a file against the archive.  |
428 `----------------------------------*/
429
430 void
431 diff_archive (void)
432 {
433   struct stat stat_data;
434   int name_length;
435   int status;
436
437   errno = EPIPE;                /* FIXME: errno should be read-only */
438                                 /* FIXME: remove perrors */
439
440   set_next_block_after (current_header);
441   decode_header (current_header, &current_stat, &current_format, 1);
442
443   /* Print the block from `current_header' and `current_stat'.  */
444
445   if (verbose_option)
446     {
447       if (now_verifying)
448         fprintf (stdlis, _("Verify "));
449       print_header ();
450     }
451
452   switch (current_header->header.typeflag)
453     {
454     default:
455       WARN ((0, 0, _("Unknown file type '%c' for %s, diffed as normal file"),
456                  current_header->header.typeflag, current_file_name));
457       /* Fall through.  */
458
459     case AREGTYPE:
460     case REGTYPE:
461     case GNUTYPE_SPARSE:
462     case CONTTYPE:
463
464       /* Appears to be a file.  See if it's really a directory.  */
465
466       name_length = strlen (current_file_name) - 1;
467       if (current_file_name[name_length] == '/')
468         goto really_dir;
469
470       if (!get_stat_data (&stat_data))
471         {
472           if (current_header->oldgnu_header.isextended)
473             skip_extended_headers ();
474           skip_file ((long) current_stat.st_size);
475           goto quit;
476         }
477
478       if (!S_ISREG (stat_data.st_mode))
479         {
480           report_difference (_("Not a regular file"));
481           skip_file ((long) current_stat.st_size);
482           goto quit;
483         }
484
485       stat_data.st_mode &= 07777;
486       if (stat_data.st_mode != current_stat.st_mode)
487         report_difference (_("Mode differs"));
488
489 #if !MSDOS
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"));
497 #endif
498
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)
503         {
504           report_difference (_("Size differs"));
505           skip_file ((long) current_stat.st_size);
506           goto quit;
507         }
508
509       diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
510
511       if (diff_handle < 0 && !absolute_names_option)
512         {
513           char *tmpbuf = xmalloc (strlen (current_file_name) + 2);
514
515           *tmpbuf = '/';
516           strcpy (tmpbuf + 1, current_file_name);
517           diff_handle = open (tmpbuf, O_NDELAY | O_RDONLY);
518           free (tmpbuf);
519         }
520       if (diff_handle < 0)
521         {
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);
527           goto quit;
528         }
529
530       /* Need to treat sparse files completely differently here.  */
531
532       if (current_header->header.typeflag == GNUTYPE_SPARSE)
533         diff_sparse_files (current_stat.st_size);
534       else
535         {
536           if (multi_volume_option)
537             {
538               assign_string (&save_name, current_file_name);
539               save_totsize = current_stat.st_size;
540               /* save_sizeleft is set in read_and_process.  */
541             }
542
543           read_and_process ((long) (current_stat.st_size), process_rawdata);
544
545           if (multi_volume_option)
546             assign_string (&save_name, NULL);
547         }
548
549       status = close (diff_handle);
550       if (status < 0)
551         ERROR ((0, errno, _("Error while closing %s"), current_file_name));
552
553     quit:
554       break;
555
556 #if !MSDOS
557     case LNKTYPE:
558       {
559         dev_t dev;
560         ino_t ino;
561
562         if (!get_stat_data (&stat_data))
563           break;
564
565         dev = stat_data.st_dev;
566         ino = stat_data.st_ino;
567         status = stat (current_link_name, &stat_data);
568         if (status < 0)
569           {
570             if (errno == ENOENT)
571               report_difference (_("Does not exist"));
572             else
573               {
574                 WARN ((0, errno, _("Cannot stat file %s"), current_file_name));
575                 report_difference (NULL);
576               }
577             break;
578           }
579
580         if (stat_data.st_dev != dev || stat_data.st_ino != ino)
581           {
582             char *message = (char *)
583               xmalloc (MESSAGE_BUFFER_SIZE + strlen (current_link_name));
584
585             sprintf (message, _("Not linked to %s"), current_link_name);
586             report_difference (message);
587             free (message);
588             break;
589           }
590
591         break;
592       }
593 #endif /* not MSDOS */
594
595 #ifdef S_ISLNK
596     case SYMTYPE:
597       {
598         char linkbuf[NAME_FIELD_SIZE + 3]; /* FIXME: may be too short.  */
599
600         status = readlink (current_file_name, linkbuf, (sizeof linkbuf) - 1);
601
602         if (status < 0)
603           {
604             if (errno == ENOENT)
605               report_difference (_("No such file or directory"));
606             else
607               {
608                 WARN ((0, errno, _("Cannot read link %s"), current_file_name));
609                 report_difference (NULL);
610               }
611             break;
612           }
613
614         linkbuf[status] = '\0'; /* null-terminate it */
615         if (strncmp (current_link_name, linkbuf, (size_t) status) != 0)
616           report_difference (_("Symlink differs"));
617
618         break;
619       }
620 #endif /* not S_ISLNK */
621
622 #ifdef S_IFCHR
623     case CHRTYPE:
624       current_stat.st_mode |= S_IFCHR;
625       goto check_node;
626 #endif /* not S_IFCHR */
627
628 #ifdef S_IFBLK
629       /* If local system doesn't support block devices, use default case.  */
630
631     case BLKTYPE:
632       current_stat.st_mode |= S_IFBLK;
633       goto check_node;
634 #endif /* not S_IFBLK */
635
636 #ifdef S_ISFIFO
637       /* If local system doesn't support FIFOs, use default case.  */
638
639     case FIFOTYPE:
640 # ifdef S_IFIFO
641       current_stat.st_mode |= S_IFIFO;
642 # endif
643       current_stat.st_rdev = 0; /* FIXME: do we need this? */
644       goto check_node;
645 #endif /* S_ISFIFO */
646
647     check_node:
648       /* FIXME: deal with umask.  */
649
650       if (!get_stat_data (&stat_data))
651         break;
652
653       if (current_stat.st_rdev != stat_data.st_rdev)
654         {
655           report_difference (_("Device numbers changed"));
656           break;
657         }
658
659       if (
660 #ifdef S_IFMT
661           current_stat.st_mode != stat_data.st_mode
662 #else
663           /* POSIX lossage.  */
664           (current_stat.st_mode & 07777) != (stat_data.st_mode & 07777)
665 #endif
666           )
667         {
668           report_difference (_("Mode or device-type changed"));
669           break;
670         }
671
672       break;
673
674     case GNUTYPE_DUMPDIR:
675       {
676         char *dumpdir_buffer = get_directory_contents (current_file_name, 0);
677
678         if (multi_volume_option)
679           {
680             assign_string (&save_name, current_file_name);
681             save_totsize = current_stat.st_size;
682             /* save_sizeleft is set in read_and_process.  */
683           }
684
685         if (dumpdir_buffer)
686           {
687             dumpdir_cursor = dumpdir_buffer;
688             read_and_process ((long) (current_stat.st_size), process_dumpdir);
689             free (dumpdir_buffer);
690           }
691         else
692           read_and_process ((long) (current_stat.st_size), process_noop);
693
694         if (multi_volume_option)
695           assign_string (&save_name, NULL);
696         /* Fall through.  */
697       }
698
699     case DIRTYPE:
700       /* Check for trailing /.  */
701
702       name_length = strlen (current_file_name) - 1;
703
704     really_dir:
705       while (name_length && current_file_name[name_length] == '/')
706         current_file_name[name_length--] = '\0';        /* zap / */
707
708       if (!get_stat_data (&stat_data))
709         break;
710
711       if (!S_ISDIR (stat_data.st_mode))
712         {
713           report_difference (_("No longer a directory"));
714           break;
715         }
716
717       if ((stat_data.st_mode & 07777) != (current_stat.st_mode & 07777))
718         report_difference (_("Mode differs"));
719       break;
720
721     case GNUTYPE_VOLHDR:
722       break;
723
724     case GNUTYPE_MULTIVOL:
725       {
726         off_t offset;
727
728         name_length = strlen (current_file_name) - 1;
729         if (current_file_name[name_length] == '/')
730           goto really_dir;
731
732         if (!get_stat_data (&stat_data))
733           break;
734
735         if (!S_ISREG (stat_data.st_mode))
736           {
737             report_difference (_("Not a regular file"));
738             skip_file ((long) current_stat.st_size);
739             break;
740           }
741
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)
745           {
746             report_difference (_("Size differs"));
747             skip_file ((long) current_stat.st_size);
748             break;
749           }
750
751         diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
752
753         if (diff_handle < 0)
754           {
755             WARN ((0, errno, _("Cannot open file %s"), current_file_name));
756             report_difference (NULL);
757             skip_file ((long) current_stat.st_size);
758             break;
759           }
760
761         status = lseek (diff_handle, offset, 0);
762         if (status != offset)
763           {
764             WARN ((0, errno, _("Cannot seek to %ld in file %s"),
765                    offset, current_file_name));
766             report_difference (NULL);
767             break;
768           }
769
770         if (multi_volume_option)
771           {
772             assign_string (&save_name, current_file_name);
773             save_totsize = stat_data.st_size;
774             /* save_sizeleft is set in read_and_process.  */
775           }
776
777         read_and_process ((long) (current_stat.st_size), process_rawdata);
778
779         if (multi_volume_option)
780           assign_string (&save_name, NULL);
781
782         status = close (diff_handle);
783         if (status < 0)
784           ERROR ((0, errno, _("Error while closing %s"), current_file_name));
785
786         break;
787       }
788     }
789 }
790
791 /*---.
792 | ?  |
793 `---*/
794
795 void
796 verify_volume (void)
797 {
798   if (!diff_buffer)
799     diff_init ();
800
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.
805
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 :-).  */
810
811 #if HAVE_FSYNC
812   fsync (archive);
813 #endif
814 #ifdef FDFLUSH
815   ioctl (archive, FDFLUSH);
816 #endif
817
818 #ifdef MTIOCTOP
819   {
820     struct mtop operation;
821     int status;
822
823     operation.mt_op = MTBSF;
824     operation.mt_count = 1;
825     if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
826       {
827         if (errno != EIO
828             || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
829                 status < 0))
830           {
831 #endif
832             if (rmtlseek (archive, 0L, 0) != 0)
833               {
834                 /* Lseek failed.  Try a different method.  */
835
836                 WARN ((0, errno,
837                        _("Could not rewind archive file for verify")));
838                 return;
839               }
840 #ifdef MTIOCTOP
841           }
842       }
843   }
844 #endif
845
846   access_mode = ACCESS_READ;
847   now_verifying = 1;
848
849   flush_read ();
850   while (1)
851     {
852       enum read_header status = read_header ();
853
854       if (status == HEADER_FAILURE)
855         {
856           int counter = 0;
857
858           while (status == HEADER_FAILURE);
859             {
860               counter++;
861               status = read_header ();
862             }
863           ERROR ((0, 0,
864                   _("VERIFY FAILURE: %d invalid header(s) detected"), counter));
865         }
866       if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
867         break;
868
869       diff_archive ();
870     }
871
872   access_mode = ACCESS_WRITE;
873   now_verifying = 0;
874 }