Home | History | Annotate | Line # | Download | only in ckckp
check-all revision 1.2
      1  1.1  perseant #!/usr/pkg/bin/perl
      2  1.1  perseant 
      3  1.1  perseant #
      4  1.1  perseant # Use dumplfs to find all locations of the Ifile inode on a given disk.
      5  1.1  perseant # Order these by serial number and call fsck_lfs on the raw disk for each.
      6  1.1  perseant # If any fsck gives errors (lines containing "!", with a few exceptions)
      7  1.1  perseant # print an error code with the daddr of the failing Ifile inode location.
      8  1.1  perseant #
      9  1.1  perseant 
     10  1.2  perseant $| = 1;
     11  1.1  perseant $rdev = $ARGV[0];
     12  1.2  perseant $gfile = $ARGV[1];
     13  1.2  perseant $wfile = $ARGV[2];
     14  1.2  perseant $sstart = $ARGV[3];
     15  1.1  perseant $rollid = 0;
     16  1.1  perseant open(DUMPLFS, "dumplfs $rdev |");
     17  1.1  perseant 
     18  1.1  perseant # Look for "roll_id" so we don't use garbage
     19  1.1  perseant while (<DUMPLFS>) {
     20  1.2  perseant 	if ($ssize == 0 && m/ssize *([0-9]*)/) {
     21  1.2  perseant 		$ssize = $1;
     22  1.2  perseant 	}
     23  1.2  perseant 	if ($fsize == 0 && m/fsize *([0-9]*)/) {
     24  1.2  perseant 		$fsize = $1;
     25  1.2  perseant 	}
     26  1.1  perseant 	if (m/roll_id *([x0-9a-f]*)/) {
     27  1.1  perseant 		$rollid = $1;
     28  1.1  perseant 		last;
     29  1.1  perseant 	}
     30  1.1  perseant }
     31  1.1  perseant 
     32  1.1  perseant # Now look for inodes and segment summaries.  Build a hash table of these
     33  1.1  perseant # based on serial number.  Ignore any with serial numbers lower than $sstart.
     34  1.1  perseant 
     35  1.1  perseant %iloc = ();
     36  1.2  perseant %snloc = ();
     37  1.2  perseant %sumloc = ();
     38  1.2  perseant print "Reading segments:";
     39  1.1  perseant while (<DUMPLFS>) {
     40  1.1  perseant 	if (m/roll_id *([0-9a-f]*)/) {
     41  1.1  perseant 		# print "rollid $1\n";
     42  1.1  perseant 		if ("0x$1" ne $rollid) {
     43  1.1  perseant 			# Skip the rest of this segment
     44  1.1  perseant 			while(<DUMPLFS>) {
     45  1.1  perseant 				last if m/SEGMENT/;
     46  1.1  perseant 			}
     47  1.2  perseant 			# Fall through
     48  1.1  perseant 		}
     49  1.1  perseant 	}
     50  1.2  perseant 	if (m/roll_id.*serial *([0-9]*)/) {
     51  1.1  perseant 		$serno = $1;
     52  1.2  perseant 		$snloc{$serno} = $segnum;
     53  1.2  perseant 		$sumloc{$serno} = $sumloc;
     54  1.1  perseant 		# print "serno $serno\n";
     55  1.1  perseant 		if ($serno < $sstart) {
     56  1.1  perseant 			# Skip the rest of this partial segment
     57  1.1  perseant 			while(<DUMPLFS>) {
     58  1.2  perseant 				last if m/Segment Summary/ ||
     59  1.2  perseant 					m/SEGMENT/;
     60  1.1  perseant 			}
     61  1.2  perseant 			# Fall through
     62  1.1  perseant 		}
     63  1.1  perseant 	}
     64  1.2  perseant 	if (m/Segment Summary Info at 0x([0-9a-f]*)/) {
     65  1.2  perseant 		$sumloc = $1;
     66  1.2  perseant 	}
     67  1.1  perseant 	if (m/0x([0-9a-f]*)/) {
     68  1.1  perseant 		foreach $ss (split "0x", $_) {
     69  1.1  perseant 			if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) {
     70  1.1  perseant 				# print "iblk 0x$1\n";
     71  1.1  perseant 				$daddr = $1;
     72  1.1  perseant 				if (m/[^0-9]1v1/) {
     73  1.1  perseant 					# print "** ifblk 0x$daddr\n";
     74  1.1  perseant 					$iloc{$serno} = $daddr;
     75  1.2  perseant 					$lastaddr = $daddr;
     76  1.1  perseant 				}
     77  1.1  perseant 			}
     78  1.1  perseant 		}
     79  1.1  perseant 	}
     80  1.2  perseant 	if (m/SEGMENT *([0-9]*)/) {
     81  1.2  perseant 		$segnum = $1;
     82  1.2  perseant 		print " $segnum";
     83  1.1  perseant 	}
     84  1.1  perseant }
     85  1.2  perseant print "\n";
     86  1.1  perseant close(DUMPLFS);
     87  1.1  perseant 
     88  1.2  perseant # If there were no checkpoints, print *something*
     89  1.2  perseant if ($#iloc == 0) {
     90  1.2  perseant 	print "0 $sstart 0\n";
     91  1.2  perseant 	exit 0;
     92  1.2  perseant }
     93  1.2  perseant 
     94  1.1  perseant #
     95  1.1  perseant # Now fsck each checkpoint in turn, beginning with $sstart.
     96  1.2  perseant # Because the log wraps we will have to reconstruct the filesystem image
     97  1.2  perseant # as it existed at each checkpoint before running fsck.
     98  1.2  perseant #
     99  1.1  perseant # Look for lines containing only caps or "!", but ignore known
    100  1.1  perseant # false positives.
    101  1.1  perseant #
    102  1.1  perseant $error = 0;
    103  1.2  perseant $lastgood = $sstart - 1;
    104  1.1  perseant open(LOG, ">>check-all.log");
    105  1.2  perseant print "Available checkpoints:";
    106  1.2  perseant print LOG "Available checkpoints:";
    107  1.1  perseant foreach $k (sort { $a <=> $b } keys %iloc) {
    108  1.1  perseant 	$a = $iloc{$k};
    109  1.2  perseant 	print " $a";
    110  1.2  perseant 	print LOG " $a";
    111  1.2  perseant }
    112  1.2  perseant print "\n";
    113  1.2  perseant print LOG "\n";
    114  1.2  perseant 
    115  1.2  perseant print "Recreating filesystem image as of $sstart:\n";
    116  1.2  perseant if ($sstart == 0) {
    117  1.2  perseant 	$cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage
    118  1.2  perseant } else {
    119  1.2  perseant 	$cmd = "dd if=$gfile of=$wfile bs=1m";
    120  1.2  perseant }
    121  1.2  perseant print "$cmd\n";
    122  1.2  perseant system("$cmd >/dev/null 2>&1");
    123  1.2  perseant 
    124  1.2  perseant $blstart = 0;
    125  1.2  perseant $fps = $ssize / $fsize;
    126  1.2  perseant foreach $k (sort { $a <=> $b } keys %iloc) {
    127  1.2  perseant 	$a = $iloc{$k};
    128  1.2  perseant 
    129  1.2  perseant 	if (hex($a) > hex($lastaddr)) {
    130  1.2  perseant 		print "Skipping out-of-place checkpoint $k at $a\n";
    131  1.2  perseant 		next;
    132  1.2  perseant 	}
    133  1.2  perseant 
    134  1.2  perseant 	# Copy the balance of the "new" fs image over the old one.
    135  1.2  perseant 	$blstop = int ((hex $a) / $fps) + 1;
    136  1.2  perseant 	$blstop *= $fps;
    137  1.2  perseant 	if ($blstop != $blstart) {
    138  1.2  perseant 		$cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$blstart " .
    139  1.2  perseant 			"skip=$blstart conv=notrunc";
    140  1.2  perseant 		if ($blstop) {
    141  1.2  perseant 			$cmd .= " count=" . ($blstop - $blstart);
    142  1.2  perseant 		}
    143  1.2  perseant 		$blstart = $blstop;
    144  1.2  perseant 		print "$cmd\n";
    145  1.2  perseant 		system("$cmd >/dev/null 2>&1");
    146  1.2  perseant 	}
    147  1.2  perseant 
    148  1.2  perseant 	$cmd = "fsck_lfs -n -f -i 0x$a $wfile";
    149  1.2  perseant 	print "$cmd\n";
    150  1.2  perseant 	print LOG "$cmd\n";
    151  1.2  perseant 	open(FSCK, "$cmd 2>&1 |");
    152  1.1  perseant 	while(<FSCK>) {
    153  1.1  perseant 		print LOG;
    154  1.1  perseant 		chomp;
    155  1.1  perseant 
    156  1.1  perseant 		# Known false positives (mismatch between sb and ifile,
    157  1.1  perseant 		# which should be expected given we're using an arbitrarily
    158  1.1  perseant 		# old version fo the ifile)
    159  1.1  perseant 		if (m/AVAIL GIVEN/ ||
    160  1.1  perseant 		    m/BFREE GIVEN/ ||
    161  1.1  perseant 		    m/DMETA GIVEN/ ||
    162  1.2  perseant 		    m/NCLEAN GIVEN/ ||
    163  1.2  perseant 		    m/FREE BUT NOT ON FREE LIST/ ||	# UNWRITTEN inodes OK
    164  1.2  perseant 		    m/FREE LIST HEAD IN SUPERBLOCK/ ) {
    165  1.1  perseant 			next;
    166  1.1  perseant 		}
    167  1.1  perseant 
    168  1.1  perseant 		# Fsck reports errors in ALL CAPS
    169  1.1  perseant 		# But don't count hex numbers as "lowercase".
    170  1.1  perseant 		s/0x[0-9a-f]*//g;
    171  1.1  perseant 		if (m/[A-Z]/ && ! m/[a-z]/) {
    172  1.1  perseant 			$error = 1;
    173  1.2  perseant 			$errsn = $k;
    174  1.2  perseant 			$errstr = "1 $k 0x$a";
    175  1.1  perseant 			last;
    176  1.1  perseant 		}
    177  1.1  perseant 	}
    178  1.1  perseant 	close(FSCK);
    179  1.1  perseant 	last if $error;
    180  1.2  perseant 	$lastgood = $k;	# record last good serial number
    181  1.2  perseant }
    182  1.2  perseant $errstr = "0 $lastgood 0x$a" unless $errstr;
    183  1.2  perseant 
    184  1.2  perseant if ($error == 0) {
    185  1.2  perseant 	print "Copying this good image to $gfile\n";
    186  1.2  perseant 	system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1");
    187  1.2  perseant 	print "$errstr\n";
    188  1.2  perseant 	exit 0;
    189  1.2  perseant }
    190  1.2  perseant 
    191  1.2  perseant #
    192  1.2  perseant # If we found an error, try to find which blocks of the Ifile inode changed
    193  1.2  perseant # between the last good checkpoint and this checkpoint; and which blocks
    194  1.2  perseant # *should* have changed.  This means (1) which segments were written; and
    195  1.2  perseant # (2) which inodes were written.  The 0 block of the Ifile should always
    196  1.2  perseant # have changed since lfs_avail is always in flux.
    197  1.2  perseant #
    198  1.2  perseant 
    199  1.2  perseant $cmd = "dumplfs";
    200  1.2  perseant $oseg = -1;
    201  1.2  perseant %iblk = ();
    202  1.2  perseant %iblk_done = ();
    203  1.2  perseant %why = ();
    204  1.2  perseant $iblk{0} = 1;
    205  1.2  perseant for ($i = $lastgood + 1; $i <= $errsn; $i++) {
    206  1.2  perseant 	if ($oseg != $snloc{$i}) {
    207  1.2  perseant 		$oseg = 0 + $snloc{$i};
    208  1.2  perseant 		$cmd .= " -s$oseg";
    209  1.2  perseant 	}
    210  1.1  perseant }
    211  1.2  perseant $cmd .= " $rdev";
    212  1.1  perseant 
    213  1.2  perseant open(DUMPLFS, "$cmd |");
    214  1.2  perseant while(<DUMPLFS>) {
    215  1.2  perseant 	if (m/ifpb *([0-9]*)/) {
    216  1.2  perseant 		$ifpb = $1;
    217  1.2  perseant 	}
    218  1.2  perseant 	if (m/sepb *([0-9]*)/) {
    219  1.2  perseant 		$sepb = $1;
    220  1.2  perseant 	}
    221  1.2  perseant 	if (m/cleansz *([0-9]*)/) {
    222  1.2  perseant 		$cleansz = $1;
    223  1.2  perseant 	}
    224  1.2  perseant 	if (m/segtabsz *([0-9]*)/) {
    225  1.2  perseant 		$segtabsz = $1;
    226  1.2  perseant 	}
    227  1.2  perseant 	last if m/SEGMENT/;
    228  1.2  perseant }
    229  1.2  perseant while(<DUMPLFS>) {
    230  1.2  perseant 	chomp;
    231  1.2  perseant 
    232  1.2  perseant 	# Skip over partial segments outside our range of interest
    233  1.2  perseant 	if (m/roll_id.*serial *([0-9]*)/) {
    234  1.2  perseant 		$serno = $1;
    235  1.2  perseant 		if ($serno <= $lastgood || $serno > $errsn) {
    236  1.2  perseant 			# Skip the rest of this partial segment
    237  1.2  perseant 			while(<DUMPLFS>) {
    238  1.2  perseant 				last if m/Segment Summary/ || m/SEGMENT/;
    239  1.2  perseant 			}
    240  1.2  perseant 			next;
    241  1.2  perseant 		}
    242  1.2  perseant 	}
    243  1.2  perseant 
    244  1.2  perseant 	# Look for inodes
    245  1.2  perseant 	if (m/Inode addresses/) {
    246  1.2  perseant 		s/^[^{]*{/ /o;
    247  1.2  perseant 		s/}[^{]*$/ /o;
    248  1.2  perseant 		s/}[^{]*{/,/og;
    249  1.2  perseant 		s/v[0-9]*//og;
    250  1.2  perseant 		@ilist = split(',');
    251  1.2  perseant 		foreach $i (@ilist) {
    252  1.2  perseant 			$i =~ s/ *//og;
    253  1.2  perseant 			next if $i == 1;
    254  1.2  perseant 			$iaddr = $cleansz + $segtabsz + int ($i / $ifpb);
    255  1.2  perseant 			$iblk{$iaddr} = 1;
    256  1.2  perseant 			$why{$iaddr} .= " $i";
    257  1.2  perseant 		}
    258  1.2  perseant 	}
    259  1.2  perseant 
    260  1.2  perseant 	# Look for Ifile blocks actually written
    261  1.2  perseant 	if (m/FINFO for inode: ([0-9]*) version/) {
    262  1.2  perseant 		$i = $1;
    263  1.2  perseant 		$inoblkmode = ($i == 1);
    264  1.2  perseant 	}
    265  1.2  perseant 	if ($inoblkmode && m/^[-\t 0-9]*$/) {
    266  1.2  perseant 		s/\t/ /og;
    267  1.2  perseant 		s/^ *//o;
    268  1.2  perseant 		s/ *$//o;
    269  1.2  perseant 		@bn = split(' ');
    270  1.2  perseant 		foreach $b (@bn) {
    271  1.2  perseant 			$iblk_done{$b} = 1;
    272  1.2  perseant 		}
    273  1.2  perseant 	}
    274  1.2  perseant }
    275  1.2  perseant close(DUMPLFS);
    276  1.2  perseant 
    277  1.2  perseant # Report found and missing Ifile blocks
    278  1.2  perseant print "Ifile blocks found:";
    279  1.2  perseant foreach $b (sort { $a <=> $b } keys %iblk) {
    280  1.2  perseant 	if ($iblk_done{$b} == 1) {
    281  1.2  perseant 		print " $b";
    282  1.2  perseant 	}
    283  1.2  perseant }
    284  1.2  perseant print "\n";
    285  1.2  perseant 
    286  1.2  perseant print "Ifile blocks missing:";
    287  1.2  perseant foreach $b (sort { $a <=> $b } keys %iblk) {
    288  1.2  perseant 	if ($iblk_done{$b} == 0) {
    289  1.2  perseant 		$why{$b} =~ s/^ *//o;
    290  1.2  perseant 		print " $b ($why{$b})";
    291  1.2  perseant 	}
    292  1.1  perseant }
    293  1.2  perseant print "\n";
    294  1.2  perseant 
    295  1.2  perseant print "$errstr\n";
    296  1.2  perseant exit 0;
    297