Home | History | Annotate | Line # | Download | only in ckckp
check-all revision 1.4.18.1
      1       1.1  perseant #!/usr/pkg/bin/perl
      2       1.3  perseant #
      3  1.4.18.1      yamt #	$NetBSD: check-all,v 1.4.18.1 2008/05/18 12:30:47 yamt Exp $
      4       1.3  perseant #
      5       1.3  perseant # Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
      6       1.3  perseant # All rights reserved.
      7       1.3  perseant #
      8       1.3  perseant # This code is derived from software contributed to The NetBSD Foundation
      9       1.3  perseant # by Konrad E. Schroder <perseant (at] hhhh.org>.
     10       1.3  perseant #
     11       1.3  perseant # Redistribution and use in source and binary forms, with or without
     12       1.3  perseant # modification, are permitted provided that the following conditions
     13       1.3  perseant # are met:
     14       1.3  perseant # 1. Redistributions of source code must retain the above copyright
     15       1.3  perseant #    notice, this list of conditions and the following disclaimer.
     16       1.3  perseant # 2. Redistributions in binary form must reproduce the above copyright
     17       1.3  perseant #    notice, this list of conditions and the following disclaimer in the
     18       1.3  perseant #    documentation and/or other materials provided with the distribution.
     19       1.3  perseant #
     20       1.3  perseant # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21       1.3  perseant # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22       1.3  perseant # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23       1.3  perseant # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24       1.3  perseant # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25       1.3  perseant # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26       1.3  perseant # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27       1.3  perseant # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28       1.3  perseant # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29       1.3  perseant # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30       1.3  perseant # POSSIBILITY OF SUCH DAMAGE.
     31       1.3  perseant #
     32       1.1  perseant 
     33       1.1  perseant #
     34       1.1  perseant # Use dumplfs to find all locations of the Ifile inode on a given disk.
     35       1.1  perseant # Order these by serial number and call fsck_lfs on the raw disk for each.
     36       1.3  perseant # If any fsck gives errors (any line of all capital letters, with a few
     37       1.3  perseant # exceptions) print an error code with the daddr of the failing Ifile inode
     38       1.3  perseant # location.
     39       1.1  perseant #
     40       1.1  perseant 
     41       1.2  perseant $| = 1;
     42       1.1  perseant $rdev = $ARGV[0];
     43       1.2  perseant $gfile = $ARGV[1];
     44       1.2  perseant $wfile = $ARGV[2];
     45       1.2  perseant $sstart = $ARGV[3];
     46       1.4  perseant $test_rfw = 1; # $ARGV[4];
     47       1.1  perseant $rollid = 0;
     48       1.1  perseant open(DUMPLFS, "dumplfs $rdev |");
     49       1.1  perseant 
     50       1.1  perseant # Look for "roll_id" so we don't use garbage
     51       1.1  perseant while (<DUMPLFS>) {
     52       1.2  perseant 	if ($ssize == 0 && m/ssize *([0-9]*)/) {
     53       1.2  perseant 		$ssize = $1;
     54       1.2  perseant 	}
     55       1.2  perseant 	if ($fsize == 0 && m/fsize *([0-9]*)/) {
     56       1.2  perseant 		$fsize = $1;
     57       1.2  perseant 	}
     58       1.1  perseant 	if (m/roll_id *([x0-9a-f]*)/) {
     59       1.1  perseant 		$rollid = $1;
     60       1.1  perseant 		last;
     61       1.1  perseant 	}
     62       1.1  perseant }
     63       1.1  perseant 
     64       1.1  perseant # Now look for inodes and segment summaries.  Build a hash table of these
     65       1.1  perseant # based on serial number.  Ignore any with serial numbers lower than $sstart.
     66       1.1  perseant 
     67       1.1  perseant %iloc = ();
     68       1.2  perseant %snloc = ();
     69       1.2  perseant %sumloc = ();
     70       1.2  perseant print "Reading segments:";
     71       1.1  perseant while (<DUMPLFS>) {
     72       1.1  perseant 	if (m/roll_id *([0-9a-f]*)/) {
     73       1.1  perseant 		# print "rollid $1\n";
     74       1.1  perseant 		if ("0x$1" ne $rollid) {
     75       1.1  perseant 			# Skip the rest of this segment
     76       1.3  perseant 			print "{skip bad rollid 0x$1}";
     77       1.1  perseant 			while(<DUMPLFS>) {
     78       1.1  perseant 				last if m/SEGMENT/;
     79       1.1  perseant 			}
     80       1.2  perseant 			# Fall through
     81       1.1  perseant 		}
     82       1.1  perseant 	}
     83       1.2  perseant 	if (m/roll_id.*serial *([0-9]*)/) {
     84       1.1  perseant 		$serno = $1;
     85       1.2  perseant 		$snloc{$serno} = $segnum;
     86       1.2  perseant 		$sumloc{$serno} = $sumloc;
     87       1.3  perseant 		print "($serno)";
     88       1.1  perseant 		if ($serno < $sstart) {
     89       1.1  perseant 			# Skip the rest of this partial segment
     90       1.3  perseant 			#print "{skip bad serno $serno}";
     91       1.1  perseant 			while(<DUMPLFS>) {
     92       1.2  perseant 				last if m/Segment Summary/ ||
     93       1.2  perseant 					m/SEGMENT/;
     94       1.1  perseant 			}
     95       1.2  perseant 			# Fall through
     96       1.1  perseant 		}
     97       1.1  perseant 	}
     98       1.2  perseant 	if (m/Segment Summary Info at 0x([0-9a-f]*)/) {
     99       1.2  perseant 		$sumloc = $1;
    100       1.3  perseant 		next;
    101       1.2  perseant 	}
    102       1.1  perseant 	if (m/0x([0-9a-f]*)/) {
    103       1.1  perseant 		foreach $ss (split "0x", $_) {
    104       1.1  perseant 			if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) {
    105       1.1  perseant 				# print "iblk 0x$1\n";
    106       1.1  perseant 				$daddr = $1;
    107       1.1  perseant 				if (m/[^0-9]1v1/) {
    108       1.1  perseant 					# print "** ifblk 0x$daddr\n";
    109       1.1  perseant 					$iloc{$serno} = $daddr;
    110       1.2  perseant 					$lastaddr = $daddr;
    111       1.1  perseant 				}
    112       1.1  perseant 			}
    113       1.1  perseant 		}
    114       1.1  perseant 	}
    115       1.2  perseant 	if (m/SEGMENT *([0-9]*)/) {
    116       1.2  perseant 		$segnum = $1;
    117       1.3  perseant 		print "[$segnum]";
    118       1.1  perseant 	}
    119       1.1  perseant }
    120       1.2  perseant print "\n";
    121       1.1  perseant close(DUMPLFS);
    122       1.1  perseant 
    123       1.3  perseant # Complain about missing partial-segments
    124       1.3  perseant for ($i = $sstart; $i < $serno; ++$i) {
    125       1.3  perseant 	if (hex $sumloc{$i} == 0 && $i > 0) {
    126       1.3  perseant 		print "Oops, couldn't find pseg $i\n";
    127       1.3  perseant 	}
    128       1.3  perseant }
    129       1.3  perseant 
    130       1.2  perseant # If there were no checkpoints, print *something*
    131       1.2  perseant if ($#iloc == 0) {
    132       1.2  perseant 	print "0 $sstart 0\n";
    133       1.2  perseant 	exit 0;
    134       1.2  perseant }
    135       1.2  perseant 
    136       1.1  perseant #
    137       1.1  perseant # Now fsck each checkpoint in turn, beginning with $sstart.
    138       1.2  perseant # Because the log wraps we will have to reconstruct the filesystem image
    139       1.2  perseant # as it existed at each checkpoint before running fsck.
    140       1.2  perseant #
    141       1.1  perseant # Look for lines containing only caps or "!", but ignore known
    142       1.1  perseant # false positives.
    143       1.1  perseant #
    144       1.1  perseant $error = 0;
    145       1.2  perseant $lastgood = $sstart - 1;
    146       1.1  perseant open(LOG, ">>check-all.log");
    147       1.2  perseant print "Available checkpoints:";
    148       1.2  perseant print LOG "Available checkpoints:";
    149       1.1  perseant foreach $k (sort { $a <=> $b } keys %iloc) {
    150       1.1  perseant 	$a = $iloc{$k};
    151       1.2  perseant 	print " $a";
    152       1.2  perseant 	print LOG " $a";
    153       1.2  perseant }
    154       1.2  perseant print "\n";
    155       1.2  perseant print LOG "\n";
    156       1.2  perseant 
    157       1.3  perseant #
    158       1.3  perseant # Copy the partial segments $_[0]--$_[1] from the raw device onto
    159       1.3  perseant # the working file.  Return the next partial-segment serial number
    160       1.3  perseant # after the last one we copied (usually $_[1] + 1, except in case of
    161       1.3  perseant # an error).
    162       1.3  perseant #
    163       1.3  perseant sub copypseg
    164       1.3  perseant {
    165       1.3  perseant 	my ($blstart, $blstop, $segstop, $cmd);
    166       1.3  perseant 	my ($totalstart, $totalstop);
    167       1.3  perseant 
    168       1.3  perseant 	$totalstart = 0;
    169       1.3  perseant 	$totalstop = 0;
    170       1.3  perseant 	for ($i = $_[0]; $i <= $_[1]; ++$i) {
    171       1.3  perseant 		$blstart = hex $sumloc{$i};
    172       1.3  perseant 		last if $blstart <= 0;
    173       1.3  perseant 		$totalstart = $blstart if $totalstart == 0;
    174       1.3  perseant 		$blstop = hex $sumloc{$i + 1};
    175       1.3  perseant 		$segstop = ((int ($blstart / $fps)) + 1) * $fps;
    176       1.3  perseant 		if ($segstop < $blstop || $blstop < $blstart) {
    177       1.3  perseant 			#print "Adjusting $blstop -> $segstop\n";
    178       1.3  perseant 			$blstop = $segstop;
    179       1.3  perseant 		}
    180       1.3  perseant 		$totalstop = $blstop;
    181       1.3  perseant 
    182       1.4  perseant 		print "pseg $i: write blocks ", hex $blstart, "-", hex ($blstop - 1), "\n";
    183       1.3  perseant 		$blstart = $blstop;
    184       1.3  perseant 	}
    185       1.3  perseant 	$cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$totalstart " .
    186       1.3  perseant 		"skip=$totalstart conv=notrunc count=" .
    187       1.3  perseant 		($totalstop - $totalstart);
    188       1.3  perseant #	print "$cmd\n";
    189       1.3  perseant 	system("$cmd >/dev/null 2>&1");
    190       1.3  perseant 
    191       1.3  perseant 	return $i;
    192       1.3  perseant }
    193       1.3  perseant 
    194       1.2  perseant print "Recreating filesystem image as of $sstart:\n";
    195       1.2  perseant if ($sstart == 0) {
    196       1.2  perseant 	$cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage
    197       1.2  perseant } else {
    198       1.2  perseant 	$cmd = "dd if=$gfile of=$wfile bs=1m";
    199       1.2  perseant }
    200       1.2  perseant print "$cmd\n";
    201       1.2  perseant system("$cmd >/dev/null 2>&1");
    202       1.2  perseant 
    203       1.3  perseant print "Copying over first superblock\n";
    204       1.3  perseant system("dd if=$rdev of=$wfile bs=8k count=2 conv=notrunc >/dev/null 2>&1");
    205       1.3  perseant 
    206       1.4  perseant sub test_fsck
    207       1.4  perseant {
    208       1.4  perseant 	my $a = $_[0];
    209       1.4  perseant 	my $flags = $_[1];
    210       1.4  perseant 	my $printit = $_[2];
    211       1.4  perseant 	my $output = "";
    212       1.2  perseant 
    213       1.4  perseant 	$flags = "-n -f -i 0x$a $wfile" unless $flags;
    214       1.2  perseant 
    215       1.4  perseant 	$cmd = "fsck_lfs $flags";
    216       1.2  perseant 	print "$cmd\n";
    217       1.2  perseant 	print LOG "$cmd\n";
    218       1.2  perseant 	open(FSCK, "$cmd 2>&1 |");
    219       1.1  perseant 	while(<FSCK>) {
    220       1.1  perseant 		print LOG;
    221       1.4  perseant 		$rline = "$_";
    222       1.1  perseant 		chomp;
    223       1.1  perseant 
    224       1.1  perseant 		# Known false positives (mismatch between sb and ifile,
    225       1.1  perseant 		# which should be expected given we're using an arbitrarily
    226       1.3  perseant 		# old version of the ifile)
    227       1.1  perseant 		if (m/AVAIL GIVEN/ ||
    228       1.1  perseant 		    m/BFREE GIVEN/ ||
    229       1.1  perseant 		    m/DMETA GIVEN/ ||
    230       1.2  perseant 		    m/NCLEAN GIVEN/ ||
    231       1.2  perseant 		    m/FREE BUT NOT ON FREE LIST/ ||	# UNWRITTEN inodes OK
    232       1.4  perseant 		    m/FILE SYSTEM WAS MODIFIED/ ||
    233       1.2  perseant 		    m/FREE LIST HEAD IN SUPERBLOCK/ ) {
    234       1.1  perseant 			next;
    235       1.1  perseant 		}
    236       1.1  perseant 
    237       1.1  perseant 		# Fsck reports errors in ALL CAPS
    238       1.1  perseant 		# But don't count hex numbers as "lowercase".
    239       1.4  perseant 		$oline = "$_";
    240       1.1  perseant 		s/0x[0-9a-f]*//g;
    241       1.1  perseant 		if (m/[A-Z]/ && ! m/[a-z]/) {
    242       1.1  perseant 			$error = 1;
    243       1.2  perseant 			$errsn = $k;
    244       1.4  perseant 			$errstr = "1 $k 0x$a $oline";
    245       1.4  perseant 			# last;
    246       1.1  perseant 		}
    247       1.4  perseant 
    248       1.4  perseant 		# Log everything we get, except for some things we
    249       1.4  perseant 		# will see every single time.
    250       1.4  perseant 		if (m/checkpoint invalid/ ||
    251       1.4  perseant 		    m/skipping free list check/ ||
    252       1.4  perseant 		    m/expect discrepancies/) {
    253       1.4  perseant 			next;
    254       1.4  perseant 		}
    255       1.4  perseant 		$output .= $rline;
    256       1.1  perseant 	}
    257       1.1  perseant 	close(FSCK);
    258       1.4  perseant 
    259       1.4  perseant 	if ($? != 0) {
    260       1.4  perseant 		$error = 1;
    261       1.4  perseant 		$errsn = $k;
    262       1.4  perseant 		$errstr = "1 $k 0x$a <" . (hex $?) . ">";
    263       1.4  perseant 	}
    264       1.4  perseant 
    265       1.4  perseant 	if ($error || $printit) {
    266       1.4  perseant 		print $output;
    267       1.4  perseant 	}
    268       1.4  perseant }
    269       1.4  perseant 
    270       1.4  perseant $blstart = 0;
    271       1.4  perseant $fps = $ssize / $fsize;
    272       1.4  perseant $oind = ($sstart ? $sstart : 1);
    273       1.4  perseant BIGLOOP: foreach $k (sort { $a <=> $b } keys %iloc) {
    274       1.4  perseant 	$a = $iloc{$k};
    275       1.4  perseant 
    276       1.4  perseant 	if (hex($a) > hex($lastaddr)) {
    277       1.4  perseant 		print "Skipping out-of-place checkpoint $k at $a\n";
    278       1.4  perseant 		next;
    279       1.4  perseant 	}
    280       1.4  perseant 
    281       1.4  perseant 	if ($test_rfw && $iloc{$oind - 1}) {
    282       1.4  perseant 		for ($tk = $oind; $tk < $k; $tk++) {
    283       1.4  perseant 			print "Test roll-forward agent at non-checkpoint pseg $tk\n";
    284       1.4  perseant 			print LOG "Test roll-forward agent at non-checkpoint pseg $tk\n";
    285       1.4  perseant 			&copypseg($oind, $tk);
    286       1.4  perseant 			# Add -d flag here for verbose debugging info
    287       1.4  perseant 			$flags = "-p -f -i 0x" . $iloc{$oind - 1} . " $wfile";
    288       1.4  perseant 			&test_fsck($iloc{$oind - 1}, $flags, 1);
    289       1.4  perseant 			last BIGLOOP if $error;
    290       1.4  perseant 
    291       1.4  perseant 			# note lack of -i flag, since the roll-forward
    292       1.4  perseant 			# will have rewritten the superblocks.
    293       1.4  perseant 			&test_fsck($iloc{$oind - 1}, "-n -f $wfile", 0);
    294       1.4  perseant 			last BIGLOOP if $error;
    295       1.4  perseant 		}
    296       1.4  perseant 	}
    297       1.4  perseant 
    298       1.4  perseant 	print "Recreate fs state at checkpoint pseg $k (from " . ($oind - 1) .
    299       1.4  perseant 	      ")\n";
    300       1.4  perseant 	$oind = &copypseg($oind, $k);
    301       1.4  perseant 
    302       1.4  perseant 	&test_fsck($a, "", 0);
    303       1.4  perseant 
    304       1.1  perseant 	last if $error;
    305       1.2  perseant 	$lastgood = $k;	# record last good serial number
    306       1.2  perseant }
    307       1.4  perseant 
    308       1.4  perseant if ($errstr) {
    309       1.4  perseant 	print "$errstr\n";
    310       1.4  perseant 	exit 0;
    311       1.4  perseant }
    312       1.4  perseant 
    313       1.3  perseant if (!$errstr) {
    314       1.3  perseant 	print "Bring filesystem state up to log wrap\n";
    315       1.3  perseant 	$lastgood = &copypseg($oind, 100000000000) - 1;
    316       1.2  perseant 
    317       1.2  perseant 	print "Copying this good image to $gfile\n";
    318       1.2  perseant 	system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1");
    319       1.3  perseant 	print "0 $lastgood 0x$a\n";
    320       1.2  perseant 	exit 0;
    321       1.2  perseant }
    322       1.2  perseant 
    323       1.2  perseant #
    324       1.3  perseant # Ifile write-checking paranoia.
    325       1.3  perseant #
    326       1.2  perseant # If we found an error, try to find which blocks of the Ifile inode changed
    327       1.2  perseant # between the last good checkpoint and this checkpoint; and which blocks
    328       1.2  perseant # *should* have changed.  This means (1) which segments were written; and
    329       1.2  perseant # (2) which inodes were written.  The 0 block of the Ifile should always
    330       1.2  perseant # have changed since lfs_avail is always in flux.
    331       1.2  perseant #
    332       1.2  perseant 
    333       1.2  perseant $cmd = "dumplfs";
    334       1.2  perseant $oseg = -1;
    335       1.2  perseant %iblk = ();
    336       1.2  perseant %iblk_done = ();
    337       1.2  perseant %why = ();
    338       1.2  perseant $iblk{0} = 1;
    339       1.2  perseant for ($i = $lastgood + 1; $i <= $errsn; $i++) {
    340       1.2  perseant 	if ($oseg != $snloc{$i}) {
    341       1.2  perseant 		$oseg = 0 + $snloc{$i};
    342       1.2  perseant 		$cmd .= " -s$oseg";
    343       1.2  perseant 	}
    344       1.1  perseant }
    345       1.2  perseant $cmd .= " $rdev";
    346       1.1  perseant 
    347       1.2  perseant open(DUMPLFS, "$cmd |");
    348       1.2  perseant while(<DUMPLFS>) {
    349       1.2  perseant 	if (m/ifpb *([0-9]*)/) {
    350       1.2  perseant 		$ifpb = $1;
    351       1.2  perseant 	}
    352       1.2  perseant 	if (m/sepb *([0-9]*)/) {
    353       1.2  perseant 		$sepb = $1;
    354       1.2  perseant 	}
    355       1.2  perseant 	if (m/cleansz *([0-9]*)/) {
    356       1.2  perseant 		$cleansz = $1;
    357       1.2  perseant 	}
    358       1.2  perseant 	if (m/segtabsz *([0-9]*)/) {
    359       1.2  perseant 		$segtabsz = $1;
    360       1.2  perseant 	}
    361       1.2  perseant 	last if m/SEGMENT/;
    362       1.2  perseant }
    363       1.2  perseant while(<DUMPLFS>) {
    364       1.2  perseant 	chomp;
    365       1.2  perseant 
    366       1.2  perseant 	# Skip over partial segments outside our range of interest
    367       1.2  perseant 	if (m/roll_id.*serial *([0-9]*)/) {
    368       1.2  perseant 		$serno = $1;
    369       1.2  perseant 		if ($serno <= $lastgood || $serno > $errsn) {
    370       1.2  perseant 			# Skip the rest of this partial segment
    371       1.2  perseant 			while(<DUMPLFS>) {
    372       1.2  perseant 				last if m/Segment Summary/ || m/SEGMENT/;
    373       1.2  perseant 			}
    374       1.2  perseant 			next;
    375       1.2  perseant 		}
    376       1.2  perseant 	}
    377       1.2  perseant 
    378       1.2  perseant 	# Look for inodes
    379       1.2  perseant 	if (m/Inode addresses/) {
    380       1.2  perseant 		s/^[^{]*{/ /o;
    381       1.2  perseant 		s/}[^{]*$/ /o;
    382       1.2  perseant 		s/}[^{]*{/,/og;
    383       1.2  perseant 		s/v[0-9]*//og;
    384       1.2  perseant 		@ilist = split(',');
    385       1.2  perseant 		foreach $i (@ilist) {
    386       1.2  perseant 			$i =~ s/ *//og;
    387       1.2  perseant 			next if $i == 1;
    388       1.2  perseant 			$iaddr = $cleansz + $segtabsz + int ($i / $ifpb);
    389       1.2  perseant 			$iblk{$iaddr} = 1;
    390       1.2  perseant 			$why{$iaddr} .= " $i";
    391       1.2  perseant 		}
    392       1.2  perseant 	}
    393       1.2  perseant 
    394       1.2  perseant 	# Look for Ifile blocks actually written
    395       1.2  perseant 	if (m/FINFO for inode: ([0-9]*) version/) {
    396       1.2  perseant 		$i = $1;
    397       1.2  perseant 		$inoblkmode = ($i == 1);
    398       1.2  perseant 	}
    399       1.2  perseant 	if ($inoblkmode && m/^[-\t 0-9]*$/) {
    400       1.2  perseant 		s/\t/ /og;
    401       1.2  perseant 		s/^ *//o;
    402       1.2  perseant 		s/ *$//o;
    403       1.2  perseant 		@bn = split(' ');
    404       1.2  perseant 		foreach $b (@bn) {
    405       1.2  perseant 			$iblk_done{$b} = 1;
    406       1.2  perseant 		}
    407       1.2  perseant 	}
    408       1.2  perseant }
    409       1.2  perseant close(DUMPLFS);
    410       1.2  perseant 
    411       1.2  perseant # Report found and missing Ifile blocks
    412       1.2  perseant print "Ifile blocks found:";
    413       1.2  perseant foreach $b (sort { $a <=> $b } keys %iblk) {
    414       1.2  perseant 	if ($iblk_done{$b} == 1) {
    415       1.2  perseant 		print " $b";
    416       1.2  perseant 	}
    417       1.2  perseant }
    418       1.2  perseant print "\n";
    419       1.4  perseant 	
    420       1.2  perseant print "Ifile blocks missing:";
    421       1.2  perseant foreach $b (sort { $a <=> $b } keys %iblk) {
    422       1.2  perseant 	if ($iblk_done{$b} == 0) {
    423       1.2  perseant 		$why{$b} =~ s/^ *//o;
    424       1.2  perseant 		print " $b ($why{$b})";
    425       1.2  perseant 	}
    426       1.1  perseant }
    427       1.2  perseant print "\n";
    428       1.2  perseant 
    429       1.2  perseant print "$errstr\n";
    430       1.2  perseant exit 0;
    431