check-all revision 1.2
11.1Sperseant#!/usr/pkg/bin/perl 21.1Sperseant 31.1Sperseant# 41.1Sperseant# Use dumplfs to find all locations of the Ifile inode on a given disk. 51.1Sperseant# Order these by serial number and call fsck_lfs on the raw disk for each. 61.1Sperseant# If any fsck gives errors (lines containing "!", with a few exceptions) 71.1Sperseant# print an error code with the daddr of the failing Ifile inode location. 81.1Sperseant# 91.1Sperseant 101.2Sperseant$| = 1; 111.1Sperseant$rdev = $ARGV[0]; 121.2Sperseant$gfile = $ARGV[1]; 131.2Sperseant$wfile = $ARGV[2]; 141.2Sperseant$sstart = $ARGV[3]; 151.1Sperseant$rollid = 0; 161.1Sperseantopen(DUMPLFS, "dumplfs $rdev |"); 171.1Sperseant 181.1Sperseant# Look for "roll_id" so we don't use garbage 191.1Sperseantwhile (<DUMPLFS>) { 201.2Sperseant if ($ssize == 0 && m/ssize *([0-9]*)/) { 211.2Sperseant $ssize = $1; 221.2Sperseant } 231.2Sperseant if ($fsize == 0 && m/fsize *([0-9]*)/) { 241.2Sperseant $fsize = $1; 251.2Sperseant } 261.1Sperseant if (m/roll_id *([x0-9a-f]*)/) { 271.1Sperseant $rollid = $1; 281.1Sperseant last; 291.1Sperseant } 301.1Sperseant} 311.1Sperseant 321.1Sperseant# Now look for inodes and segment summaries. Build a hash table of these 331.1Sperseant# based on serial number. Ignore any with serial numbers lower than $sstart. 341.1Sperseant 351.1Sperseant%iloc = (); 361.2Sperseant%snloc = (); 371.2Sperseant%sumloc = (); 381.2Sperseantprint "Reading segments:"; 391.1Sperseantwhile (<DUMPLFS>) { 401.1Sperseant if (m/roll_id *([0-9a-f]*)/) { 411.1Sperseant # print "rollid $1\n"; 421.1Sperseant if ("0x$1" ne $rollid) { 431.1Sperseant # Skip the rest of this segment 441.1Sperseant while(<DUMPLFS>) { 451.1Sperseant last if m/SEGMENT/; 461.1Sperseant } 471.2Sperseant # Fall through 481.1Sperseant } 491.1Sperseant } 501.2Sperseant if (m/roll_id.*serial *([0-9]*)/) { 511.1Sperseant $serno = $1; 521.2Sperseant $snloc{$serno} = $segnum; 531.2Sperseant $sumloc{$serno} = $sumloc; 541.1Sperseant # print "serno $serno\n"; 551.1Sperseant if ($serno < $sstart) { 561.1Sperseant # Skip the rest of this partial segment 571.1Sperseant while(<DUMPLFS>) { 581.2Sperseant last if m/Segment Summary/ || 591.2Sperseant m/SEGMENT/; 601.1Sperseant } 611.2Sperseant # Fall through 621.1Sperseant } 631.1Sperseant } 641.2Sperseant if (m/Segment Summary Info at 0x([0-9a-f]*)/) { 651.2Sperseant $sumloc = $1; 661.2Sperseant } 671.1Sperseant if (m/0x([0-9a-f]*)/) { 681.1Sperseant foreach $ss (split "0x", $_) { 691.1Sperseant if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) { 701.1Sperseant # print "iblk 0x$1\n"; 711.1Sperseant $daddr = $1; 721.1Sperseant if (m/[^0-9]1v1/) { 731.1Sperseant # print "** ifblk 0x$daddr\n"; 741.1Sperseant $iloc{$serno} = $daddr; 751.2Sperseant $lastaddr = $daddr; 761.1Sperseant } 771.1Sperseant } 781.1Sperseant } 791.1Sperseant } 801.2Sperseant if (m/SEGMENT *([0-9]*)/) { 811.2Sperseant $segnum = $1; 821.2Sperseant print " $segnum"; 831.1Sperseant } 841.1Sperseant} 851.2Sperseantprint "\n"; 861.1Sperseantclose(DUMPLFS); 871.1Sperseant 881.2Sperseant# If there were no checkpoints, print *something* 891.2Sperseantif ($#iloc == 0) { 901.2Sperseant print "0 $sstart 0\n"; 911.2Sperseant exit 0; 921.2Sperseant} 931.2Sperseant 941.1Sperseant# 951.1Sperseant# Now fsck each checkpoint in turn, beginning with $sstart. 961.2Sperseant# Because the log wraps we will have to reconstruct the filesystem image 971.2Sperseant# as it existed at each checkpoint before running fsck. 981.2Sperseant# 991.1Sperseant# Look for lines containing only caps or "!", but ignore known 1001.1Sperseant# false positives. 1011.1Sperseant# 1021.1Sperseant$error = 0; 1031.2Sperseant$lastgood = $sstart - 1; 1041.1Sperseantopen(LOG, ">>check-all.log"); 1051.2Sperseantprint "Available checkpoints:"; 1061.2Sperseantprint LOG "Available checkpoints:"; 1071.1Sperseantforeach $k (sort { $a <=> $b } keys %iloc) { 1081.1Sperseant $a = $iloc{$k}; 1091.2Sperseant print " $a"; 1101.2Sperseant print LOG " $a"; 1111.2Sperseant} 1121.2Sperseantprint "\n"; 1131.2Sperseantprint LOG "\n"; 1141.2Sperseant 1151.2Sperseantprint "Recreating filesystem image as of $sstart:\n"; 1161.2Sperseantif ($sstart == 0) { 1171.2Sperseant $cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage 1181.2Sperseant} else { 1191.2Sperseant $cmd = "dd if=$gfile of=$wfile bs=1m"; 1201.2Sperseant} 1211.2Sperseantprint "$cmd\n"; 1221.2Sperseantsystem("$cmd >/dev/null 2>&1"); 1231.2Sperseant 1241.2Sperseant$blstart = 0; 1251.2Sperseant$fps = $ssize / $fsize; 1261.2Sperseantforeach $k (sort { $a <=> $b } keys %iloc) { 1271.2Sperseant $a = $iloc{$k}; 1281.2Sperseant 1291.2Sperseant if (hex($a) > hex($lastaddr)) { 1301.2Sperseant print "Skipping out-of-place checkpoint $k at $a\n"; 1311.2Sperseant next; 1321.2Sperseant } 1331.2Sperseant 1341.2Sperseant # Copy the balance of the "new" fs image over the old one. 1351.2Sperseant $blstop = int ((hex $a) / $fps) + 1; 1361.2Sperseant $blstop *= $fps; 1371.2Sperseant if ($blstop != $blstart) { 1381.2Sperseant $cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$blstart " . 1391.2Sperseant "skip=$blstart conv=notrunc"; 1401.2Sperseant if ($blstop) { 1411.2Sperseant $cmd .= " count=" . ($blstop - $blstart); 1421.2Sperseant } 1431.2Sperseant $blstart = $blstop; 1441.2Sperseant print "$cmd\n"; 1451.2Sperseant system("$cmd >/dev/null 2>&1"); 1461.2Sperseant } 1471.2Sperseant 1481.2Sperseant $cmd = "fsck_lfs -n -f -i 0x$a $wfile"; 1491.2Sperseant print "$cmd\n"; 1501.2Sperseant print LOG "$cmd\n"; 1511.2Sperseant open(FSCK, "$cmd 2>&1 |"); 1521.1Sperseant while(<FSCK>) { 1531.1Sperseant print LOG; 1541.1Sperseant chomp; 1551.1Sperseant 1561.1Sperseant # Known false positives (mismatch between sb and ifile, 1571.1Sperseant # which should be expected given we're using an arbitrarily 1581.1Sperseant # old version fo the ifile) 1591.1Sperseant if (m/AVAIL GIVEN/ || 1601.1Sperseant m/BFREE GIVEN/ || 1611.1Sperseant m/DMETA GIVEN/ || 1621.2Sperseant m/NCLEAN GIVEN/ || 1631.2Sperseant m/FREE BUT NOT ON FREE LIST/ || # UNWRITTEN inodes OK 1641.2Sperseant m/FREE LIST HEAD IN SUPERBLOCK/ ) { 1651.1Sperseant next; 1661.1Sperseant } 1671.1Sperseant 1681.1Sperseant # Fsck reports errors in ALL CAPS 1691.1Sperseant # But don't count hex numbers as "lowercase". 1701.1Sperseant s/0x[0-9a-f]*//g; 1711.1Sperseant if (m/[A-Z]/ && ! m/[a-z]/) { 1721.1Sperseant $error = 1; 1731.2Sperseant $errsn = $k; 1741.2Sperseant $errstr = "1 $k 0x$a"; 1751.1Sperseant last; 1761.1Sperseant } 1771.1Sperseant } 1781.1Sperseant close(FSCK); 1791.1Sperseant last if $error; 1801.2Sperseant $lastgood = $k; # record last good serial number 1811.2Sperseant} 1821.2Sperseant$errstr = "0 $lastgood 0x$a" unless $errstr; 1831.2Sperseant 1841.2Sperseantif ($error == 0) { 1851.2Sperseant print "Copying this good image to $gfile\n"; 1861.2Sperseant system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1"); 1871.2Sperseant print "$errstr\n"; 1881.2Sperseant exit 0; 1891.2Sperseant} 1901.2Sperseant 1911.2Sperseant# 1921.2Sperseant# If we found an error, try to find which blocks of the Ifile inode changed 1931.2Sperseant# between the last good checkpoint and this checkpoint; and which blocks 1941.2Sperseant# *should* have changed. This means (1) which segments were written; and 1951.2Sperseant# (2) which inodes were written. The 0 block of the Ifile should always 1961.2Sperseant# have changed since lfs_avail is always in flux. 1971.2Sperseant# 1981.2Sperseant 1991.2Sperseant$cmd = "dumplfs"; 2001.2Sperseant$oseg = -1; 2011.2Sperseant%iblk = (); 2021.2Sperseant%iblk_done = (); 2031.2Sperseant%why = (); 2041.2Sperseant$iblk{0} = 1; 2051.2Sperseantfor ($i = $lastgood + 1; $i <= $errsn; $i++) { 2061.2Sperseant if ($oseg != $snloc{$i}) { 2071.2Sperseant $oseg = 0 + $snloc{$i}; 2081.2Sperseant $cmd .= " -s$oseg"; 2091.2Sperseant } 2101.1Sperseant} 2111.2Sperseant$cmd .= " $rdev"; 2121.1Sperseant 2131.2Sperseantopen(DUMPLFS, "$cmd |"); 2141.2Sperseantwhile(<DUMPLFS>) { 2151.2Sperseant if (m/ifpb *([0-9]*)/) { 2161.2Sperseant $ifpb = $1; 2171.2Sperseant } 2181.2Sperseant if (m/sepb *([0-9]*)/) { 2191.2Sperseant $sepb = $1; 2201.2Sperseant } 2211.2Sperseant if (m/cleansz *([0-9]*)/) { 2221.2Sperseant $cleansz = $1; 2231.2Sperseant } 2241.2Sperseant if (m/segtabsz *([0-9]*)/) { 2251.2Sperseant $segtabsz = $1; 2261.2Sperseant } 2271.2Sperseant last if m/SEGMENT/; 2281.2Sperseant} 2291.2Sperseantwhile(<DUMPLFS>) { 2301.2Sperseant chomp; 2311.2Sperseant 2321.2Sperseant # Skip over partial segments outside our range of interest 2331.2Sperseant if (m/roll_id.*serial *([0-9]*)/) { 2341.2Sperseant $serno = $1; 2351.2Sperseant if ($serno <= $lastgood || $serno > $errsn) { 2361.2Sperseant # Skip the rest of this partial segment 2371.2Sperseant while(<DUMPLFS>) { 2381.2Sperseant last if m/Segment Summary/ || m/SEGMENT/; 2391.2Sperseant } 2401.2Sperseant next; 2411.2Sperseant } 2421.2Sperseant } 2431.2Sperseant 2441.2Sperseant # Look for inodes 2451.2Sperseant if (m/Inode addresses/) { 2461.2Sperseant s/^[^{]*{/ /o; 2471.2Sperseant s/}[^{]*$/ /o; 2481.2Sperseant s/}[^{]*{/,/og; 2491.2Sperseant s/v[0-9]*//og; 2501.2Sperseant @ilist = split(','); 2511.2Sperseant foreach $i (@ilist) { 2521.2Sperseant $i =~ s/ *//og; 2531.2Sperseant next if $i == 1; 2541.2Sperseant $iaddr = $cleansz + $segtabsz + int ($i / $ifpb); 2551.2Sperseant $iblk{$iaddr} = 1; 2561.2Sperseant $why{$iaddr} .= " $i"; 2571.2Sperseant } 2581.2Sperseant } 2591.2Sperseant 2601.2Sperseant # Look for Ifile blocks actually written 2611.2Sperseant if (m/FINFO for inode: ([0-9]*) version/) { 2621.2Sperseant $i = $1; 2631.2Sperseant $inoblkmode = ($i == 1); 2641.2Sperseant } 2651.2Sperseant if ($inoblkmode && m/^[-\t 0-9]*$/) { 2661.2Sperseant s/\t/ /og; 2671.2Sperseant s/^ *//o; 2681.2Sperseant s/ *$//o; 2691.2Sperseant @bn = split(' '); 2701.2Sperseant foreach $b (@bn) { 2711.2Sperseant $iblk_done{$b} = 1; 2721.2Sperseant } 2731.2Sperseant } 2741.2Sperseant} 2751.2Sperseantclose(DUMPLFS); 2761.2Sperseant 2771.2Sperseant# Report found and missing Ifile blocks 2781.2Sperseantprint "Ifile blocks found:"; 2791.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) { 2801.2Sperseant if ($iblk_done{$b} == 1) { 2811.2Sperseant print " $b"; 2821.2Sperseant } 2831.2Sperseant} 2841.2Sperseantprint "\n"; 2851.2Sperseant 2861.2Sperseantprint "Ifile blocks missing:"; 2871.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) { 2881.2Sperseant if ($iblk_done{$b} == 0) { 2891.2Sperseant $why{$b} =~ s/^ *//o; 2901.2Sperseant print " $b ($why{$b})"; 2911.2Sperseant } 2921.1Sperseant} 2931.2Sperseantprint "\n"; 2941.2Sperseant 2951.2Sperseantprint "$errstr\n"; 2961.2Sperseantexit 0; 297