check-all revision 1.4
11.1Sperseant#!/usr/pkg/bin/perl 21.3Sperseant# 31.4Sperseant# $NetBSD: check-all,v 1.4 2006/07/21 00:29:23 perseant Exp $ 41.3Sperseant# 51.3Sperseant# Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc. 61.3Sperseant# All rights reserved. 71.3Sperseant# 81.3Sperseant# This code is derived from software contributed to The NetBSD Foundation 91.3Sperseant# by Konrad E. Schroder <perseant@hhhh.org>. 101.3Sperseant# 111.3Sperseant# Redistribution and use in source and binary forms, with or without 121.3Sperseant# modification, are permitted provided that the following conditions 131.3Sperseant# are met: 141.3Sperseant# 1. Redistributions of source code must retain the above copyright 151.3Sperseant# notice, this list of conditions and the following disclaimer. 161.3Sperseant# 2. Redistributions in binary form must reproduce the above copyright 171.3Sperseant# notice, this list of conditions and the following disclaimer in the 181.3Sperseant# documentation and/or other materials provided with the distribution. 191.3Sperseant# 3. All advertising materials mentioning features or use of this software 201.3Sperseant# must display the following acknowledgement: 211.3Sperseant# This product includes software developed by the NetBSD 221.3Sperseant# Foundation, Inc. and its contributors. 231.3Sperseant# 4. Neither the name of The NetBSD Foundation nor the names of its 241.3Sperseant# contributors may be used to endorse or promote products derived 251.3Sperseant# from this software without specific prior written permission. 261.3Sperseant# 271.3Sperseant# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 281.3Sperseant# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 291.3Sperseant# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 301.3Sperseant# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 311.3Sperseant# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 321.3Sperseant# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 331.3Sperseant# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 341.3Sperseant# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 351.3Sperseant# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 361.3Sperseant# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 371.3Sperseant# POSSIBILITY OF SUCH DAMAGE. 381.3Sperseant# 391.1Sperseant 401.1Sperseant# 411.1Sperseant# Use dumplfs to find all locations of the Ifile inode on a given disk. 421.1Sperseant# Order these by serial number and call fsck_lfs on the raw disk for each. 431.3Sperseant# If any fsck gives errors (any line of all capital letters, with a few 441.3Sperseant# exceptions) print an error code with the daddr of the failing Ifile inode 451.3Sperseant# location. 461.1Sperseant# 471.1Sperseant 481.2Sperseant$| = 1; 491.1Sperseant$rdev = $ARGV[0]; 501.2Sperseant$gfile = $ARGV[1]; 511.2Sperseant$wfile = $ARGV[2]; 521.2Sperseant$sstart = $ARGV[3]; 531.4Sperseant$test_rfw = 1; # $ARGV[4]; 541.1Sperseant$rollid = 0; 551.1Sperseantopen(DUMPLFS, "dumplfs $rdev |"); 561.1Sperseant 571.1Sperseant# Look for "roll_id" so we don't use garbage 581.1Sperseantwhile (<DUMPLFS>) { 591.2Sperseant if ($ssize == 0 && m/ssize *([0-9]*)/) { 601.2Sperseant $ssize = $1; 611.2Sperseant } 621.2Sperseant if ($fsize == 0 && m/fsize *([0-9]*)/) { 631.2Sperseant $fsize = $1; 641.2Sperseant } 651.1Sperseant if (m/roll_id *([x0-9a-f]*)/) { 661.1Sperseant $rollid = $1; 671.1Sperseant last; 681.1Sperseant } 691.1Sperseant} 701.1Sperseant 711.1Sperseant# Now look for inodes and segment summaries. Build a hash table of these 721.1Sperseant# based on serial number. Ignore any with serial numbers lower than $sstart. 731.1Sperseant 741.1Sperseant%iloc = (); 751.2Sperseant%snloc = (); 761.2Sperseant%sumloc = (); 771.2Sperseantprint "Reading segments:"; 781.1Sperseantwhile (<DUMPLFS>) { 791.1Sperseant if (m/roll_id *([0-9a-f]*)/) { 801.1Sperseant # print "rollid $1\n"; 811.1Sperseant if ("0x$1" ne $rollid) { 821.1Sperseant # Skip the rest of this segment 831.3Sperseant print "{skip bad rollid 0x$1}"; 841.1Sperseant while(<DUMPLFS>) { 851.1Sperseant last if m/SEGMENT/; 861.1Sperseant } 871.2Sperseant # Fall through 881.1Sperseant } 891.1Sperseant } 901.2Sperseant if (m/roll_id.*serial *([0-9]*)/) { 911.1Sperseant $serno = $1; 921.2Sperseant $snloc{$serno} = $segnum; 931.2Sperseant $sumloc{$serno} = $sumloc; 941.3Sperseant print "($serno)"; 951.1Sperseant if ($serno < $sstart) { 961.1Sperseant # Skip the rest of this partial segment 971.3Sperseant #print "{skip bad serno $serno}"; 981.1Sperseant while(<DUMPLFS>) { 991.2Sperseant last if m/Segment Summary/ || 1001.2Sperseant m/SEGMENT/; 1011.1Sperseant } 1021.2Sperseant # Fall through 1031.1Sperseant } 1041.1Sperseant } 1051.2Sperseant if (m/Segment Summary Info at 0x([0-9a-f]*)/) { 1061.2Sperseant $sumloc = $1; 1071.3Sperseant next; 1081.2Sperseant } 1091.1Sperseant if (m/0x([0-9a-f]*)/) { 1101.1Sperseant foreach $ss (split "0x", $_) { 1111.1Sperseant if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) { 1121.1Sperseant # print "iblk 0x$1\n"; 1131.1Sperseant $daddr = $1; 1141.1Sperseant if (m/[^0-9]1v1/) { 1151.1Sperseant # print "** ifblk 0x$daddr\n"; 1161.1Sperseant $iloc{$serno} = $daddr; 1171.2Sperseant $lastaddr = $daddr; 1181.1Sperseant } 1191.1Sperseant } 1201.1Sperseant } 1211.1Sperseant } 1221.2Sperseant if (m/SEGMENT *([0-9]*)/) { 1231.2Sperseant $segnum = $1; 1241.3Sperseant print "[$segnum]"; 1251.1Sperseant } 1261.1Sperseant} 1271.2Sperseantprint "\n"; 1281.1Sperseantclose(DUMPLFS); 1291.1Sperseant 1301.3Sperseant# Complain about missing partial-segments 1311.3Sperseantfor ($i = $sstart; $i < $serno; ++$i) { 1321.3Sperseant if (hex $sumloc{$i} == 0 && $i > 0) { 1331.3Sperseant print "Oops, couldn't find pseg $i\n"; 1341.3Sperseant } 1351.3Sperseant} 1361.3Sperseant 1371.2Sperseant# If there were no checkpoints, print *something* 1381.2Sperseantif ($#iloc == 0) { 1391.2Sperseant print "0 $sstart 0\n"; 1401.2Sperseant exit 0; 1411.2Sperseant} 1421.2Sperseant 1431.1Sperseant# 1441.1Sperseant# Now fsck each checkpoint in turn, beginning with $sstart. 1451.2Sperseant# Because the log wraps we will have to reconstruct the filesystem image 1461.2Sperseant# as it existed at each checkpoint before running fsck. 1471.2Sperseant# 1481.1Sperseant# Look for lines containing only caps or "!", but ignore known 1491.1Sperseant# false positives. 1501.1Sperseant# 1511.1Sperseant$error = 0; 1521.2Sperseant$lastgood = $sstart - 1; 1531.1Sperseantopen(LOG, ">>check-all.log"); 1541.2Sperseantprint "Available checkpoints:"; 1551.2Sperseantprint LOG "Available checkpoints:"; 1561.1Sperseantforeach $k (sort { $a <=> $b } keys %iloc) { 1571.1Sperseant $a = $iloc{$k}; 1581.2Sperseant print " $a"; 1591.2Sperseant print LOG " $a"; 1601.2Sperseant} 1611.2Sperseantprint "\n"; 1621.2Sperseantprint LOG "\n"; 1631.2Sperseant 1641.3Sperseant# 1651.3Sperseant# Copy the partial segments $_[0]--$_[1] from the raw device onto 1661.3Sperseant# the working file. Return the next partial-segment serial number 1671.3Sperseant# after the last one we copied (usually $_[1] + 1, except in case of 1681.3Sperseant# an error). 1691.3Sperseant# 1701.3Sperseantsub copypseg 1711.3Sperseant{ 1721.3Sperseant my ($blstart, $blstop, $segstop, $cmd); 1731.3Sperseant my ($totalstart, $totalstop); 1741.3Sperseant 1751.3Sperseant $totalstart = 0; 1761.3Sperseant $totalstop = 0; 1771.3Sperseant for ($i = $_[0]; $i <= $_[1]; ++$i) { 1781.3Sperseant $blstart = hex $sumloc{$i}; 1791.3Sperseant last if $blstart <= 0; 1801.3Sperseant $totalstart = $blstart if $totalstart == 0; 1811.3Sperseant $blstop = hex $sumloc{$i + 1}; 1821.3Sperseant $segstop = ((int ($blstart / $fps)) + 1) * $fps; 1831.3Sperseant if ($segstop < $blstop || $blstop < $blstart) { 1841.3Sperseant #print "Adjusting $blstop -> $segstop\n"; 1851.3Sperseant $blstop = $segstop; 1861.3Sperseant } 1871.3Sperseant $totalstop = $blstop; 1881.3Sperseant 1891.4Sperseant print "pseg $i: write blocks ", hex $blstart, "-", hex ($blstop - 1), "\n"; 1901.3Sperseant $blstart = $blstop; 1911.3Sperseant } 1921.3Sperseant $cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$totalstart " . 1931.3Sperseant "skip=$totalstart conv=notrunc count=" . 1941.3Sperseant ($totalstop - $totalstart); 1951.3Sperseant# print "$cmd\n"; 1961.3Sperseant system("$cmd >/dev/null 2>&1"); 1971.3Sperseant 1981.3Sperseant return $i; 1991.3Sperseant} 2001.3Sperseant 2011.2Sperseantprint "Recreating filesystem image as of $sstart:\n"; 2021.2Sperseantif ($sstart == 0) { 2031.2Sperseant $cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage 2041.2Sperseant} else { 2051.2Sperseant $cmd = "dd if=$gfile of=$wfile bs=1m"; 2061.2Sperseant} 2071.2Sperseantprint "$cmd\n"; 2081.2Sperseantsystem("$cmd >/dev/null 2>&1"); 2091.2Sperseant 2101.3Sperseantprint "Copying over first superblock\n"; 2111.3Sperseantsystem("dd if=$rdev of=$wfile bs=8k count=2 conv=notrunc >/dev/null 2>&1"); 2121.3Sperseant 2131.4Sperseantsub test_fsck 2141.4Sperseant{ 2151.4Sperseant my $a = $_[0]; 2161.4Sperseant my $flags = $_[1]; 2171.4Sperseant my $printit = $_[2]; 2181.4Sperseant my $output = ""; 2191.2Sperseant 2201.4Sperseant $flags = "-n -f -i 0x$a $wfile" unless $flags; 2211.2Sperseant 2221.4Sperseant $cmd = "fsck_lfs $flags"; 2231.2Sperseant print "$cmd\n"; 2241.2Sperseant print LOG "$cmd\n"; 2251.2Sperseant open(FSCK, "$cmd 2>&1 |"); 2261.1Sperseant while(<FSCK>) { 2271.1Sperseant print LOG; 2281.4Sperseant $rline = "$_"; 2291.1Sperseant chomp; 2301.1Sperseant 2311.1Sperseant # Known false positives (mismatch between sb and ifile, 2321.1Sperseant # which should be expected given we're using an arbitrarily 2331.3Sperseant # old version of the ifile) 2341.1Sperseant if (m/AVAIL GIVEN/ || 2351.1Sperseant m/BFREE GIVEN/ || 2361.1Sperseant m/DMETA GIVEN/ || 2371.2Sperseant m/NCLEAN GIVEN/ || 2381.2Sperseant m/FREE BUT NOT ON FREE LIST/ || # UNWRITTEN inodes OK 2391.4Sperseant m/FILE SYSTEM WAS MODIFIED/ || 2401.2Sperseant m/FREE LIST HEAD IN SUPERBLOCK/ ) { 2411.1Sperseant next; 2421.1Sperseant } 2431.1Sperseant 2441.1Sperseant # Fsck reports errors in ALL CAPS 2451.1Sperseant # But don't count hex numbers as "lowercase". 2461.4Sperseant $oline = "$_"; 2471.1Sperseant s/0x[0-9a-f]*//g; 2481.1Sperseant if (m/[A-Z]/ && ! m/[a-z]/) { 2491.1Sperseant $error = 1; 2501.2Sperseant $errsn = $k; 2511.4Sperseant $errstr = "1 $k 0x$a $oline"; 2521.4Sperseant # last; 2531.1Sperseant } 2541.4Sperseant 2551.4Sperseant # Log everything we get, except for some things we 2561.4Sperseant # will see every single time. 2571.4Sperseant if (m/checkpoint invalid/ || 2581.4Sperseant m/skipping free list check/ || 2591.4Sperseant m/expect discrepancies/) { 2601.4Sperseant next; 2611.4Sperseant } 2621.4Sperseant $output .= $rline; 2631.1Sperseant } 2641.1Sperseant close(FSCK); 2651.4Sperseant 2661.4Sperseant if ($? != 0) { 2671.4Sperseant $error = 1; 2681.4Sperseant $errsn = $k; 2691.4Sperseant $errstr = "1 $k 0x$a <" . (hex $?) . ">"; 2701.4Sperseant } 2711.4Sperseant 2721.4Sperseant if ($error || $printit) { 2731.4Sperseant print $output; 2741.4Sperseant } 2751.4Sperseant} 2761.4Sperseant 2771.4Sperseant$blstart = 0; 2781.4Sperseant$fps = $ssize / $fsize; 2791.4Sperseant$oind = ($sstart ? $sstart : 1); 2801.4SperseantBIGLOOP: foreach $k (sort { $a <=> $b } keys %iloc) { 2811.4Sperseant $a = $iloc{$k}; 2821.4Sperseant 2831.4Sperseant if (hex($a) > hex($lastaddr)) { 2841.4Sperseant print "Skipping out-of-place checkpoint $k at $a\n"; 2851.4Sperseant next; 2861.4Sperseant } 2871.4Sperseant 2881.4Sperseant if ($test_rfw && $iloc{$oind - 1}) { 2891.4Sperseant for ($tk = $oind; $tk < $k; $tk++) { 2901.4Sperseant print "Test roll-forward agent at non-checkpoint pseg $tk\n"; 2911.4Sperseant print LOG "Test roll-forward agent at non-checkpoint pseg $tk\n"; 2921.4Sperseant ©pseg($oind, $tk); 2931.4Sperseant # Add -d flag here for verbose debugging info 2941.4Sperseant $flags = "-p -f -i 0x" . $iloc{$oind - 1} . " $wfile"; 2951.4Sperseant &test_fsck($iloc{$oind - 1}, $flags, 1); 2961.4Sperseant last BIGLOOP if $error; 2971.4Sperseant 2981.4Sperseant # note lack of -i flag, since the roll-forward 2991.4Sperseant # will have rewritten the superblocks. 3001.4Sperseant &test_fsck($iloc{$oind - 1}, "-n -f $wfile", 0); 3011.4Sperseant last BIGLOOP if $error; 3021.4Sperseant } 3031.4Sperseant } 3041.4Sperseant 3051.4Sperseant print "Recreate fs state at checkpoint pseg $k (from " . ($oind - 1) . 3061.4Sperseant ")\n"; 3071.4Sperseant $oind = ©pseg($oind, $k); 3081.4Sperseant 3091.4Sperseant &test_fsck($a, "", 0); 3101.4Sperseant 3111.1Sperseant last if $error; 3121.2Sperseant $lastgood = $k; # record last good serial number 3131.2Sperseant} 3141.4Sperseant 3151.4Sperseantif ($errstr) { 3161.4Sperseant print "$errstr\n"; 3171.4Sperseant exit 0; 3181.4Sperseant} 3191.4Sperseant 3201.3Sperseantif (!$errstr) { 3211.3Sperseant print "Bring filesystem state up to log wrap\n"; 3221.3Sperseant $lastgood = ©pseg($oind, 100000000000) - 1; 3231.2Sperseant 3241.2Sperseant print "Copying this good image to $gfile\n"; 3251.2Sperseant system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1"); 3261.3Sperseant print "0 $lastgood 0x$a\n"; 3271.2Sperseant exit 0; 3281.2Sperseant} 3291.2Sperseant 3301.2Sperseant# 3311.3Sperseant# Ifile write-checking paranoia. 3321.3Sperseant# 3331.2Sperseant# If we found an error, try to find which blocks of the Ifile inode changed 3341.2Sperseant# between the last good checkpoint and this checkpoint; and which blocks 3351.2Sperseant# *should* have changed. This means (1) which segments were written; and 3361.2Sperseant# (2) which inodes were written. The 0 block of the Ifile should always 3371.2Sperseant# have changed since lfs_avail is always in flux. 3381.2Sperseant# 3391.2Sperseant 3401.2Sperseant$cmd = "dumplfs"; 3411.2Sperseant$oseg = -1; 3421.2Sperseant%iblk = (); 3431.2Sperseant%iblk_done = (); 3441.2Sperseant%why = (); 3451.2Sperseant$iblk{0} = 1; 3461.2Sperseantfor ($i = $lastgood + 1; $i <= $errsn; $i++) { 3471.2Sperseant if ($oseg != $snloc{$i}) { 3481.2Sperseant $oseg = 0 + $snloc{$i}; 3491.2Sperseant $cmd .= " -s$oseg"; 3501.2Sperseant } 3511.1Sperseant} 3521.2Sperseant$cmd .= " $rdev"; 3531.1Sperseant 3541.2Sperseantopen(DUMPLFS, "$cmd |"); 3551.2Sperseantwhile(<DUMPLFS>) { 3561.2Sperseant if (m/ifpb *([0-9]*)/) { 3571.2Sperseant $ifpb = $1; 3581.2Sperseant } 3591.2Sperseant if (m/sepb *([0-9]*)/) { 3601.2Sperseant $sepb = $1; 3611.2Sperseant } 3621.2Sperseant if (m/cleansz *([0-9]*)/) { 3631.2Sperseant $cleansz = $1; 3641.2Sperseant } 3651.2Sperseant if (m/segtabsz *([0-9]*)/) { 3661.2Sperseant $segtabsz = $1; 3671.2Sperseant } 3681.2Sperseant last if m/SEGMENT/; 3691.2Sperseant} 3701.2Sperseantwhile(<DUMPLFS>) { 3711.2Sperseant chomp; 3721.2Sperseant 3731.2Sperseant # Skip over partial segments outside our range of interest 3741.2Sperseant if (m/roll_id.*serial *([0-9]*)/) { 3751.2Sperseant $serno = $1; 3761.2Sperseant if ($serno <= $lastgood || $serno > $errsn) { 3771.2Sperseant # Skip the rest of this partial segment 3781.2Sperseant while(<DUMPLFS>) { 3791.2Sperseant last if m/Segment Summary/ || m/SEGMENT/; 3801.2Sperseant } 3811.2Sperseant next; 3821.2Sperseant } 3831.2Sperseant } 3841.2Sperseant 3851.2Sperseant # Look for inodes 3861.2Sperseant if (m/Inode addresses/) { 3871.2Sperseant s/^[^{]*{/ /o; 3881.2Sperseant s/}[^{]*$/ /o; 3891.2Sperseant s/}[^{]*{/,/og; 3901.2Sperseant s/v[0-9]*//og; 3911.2Sperseant @ilist = split(','); 3921.2Sperseant foreach $i (@ilist) { 3931.2Sperseant $i =~ s/ *//og; 3941.2Sperseant next if $i == 1; 3951.2Sperseant $iaddr = $cleansz + $segtabsz + int ($i / $ifpb); 3961.2Sperseant $iblk{$iaddr} = 1; 3971.2Sperseant $why{$iaddr} .= " $i"; 3981.2Sperseant } 3991.2Sperseant } 4001.2Sperseant 4011.2Sperseant # Look for Ifile blocks actually written 4021.2Sperseant if (m/FINFO for inode: ([0-9]*) version/) { 4031.2Sperseant $i = $1; 4041.2Sperseant $inoblkmode = ($i == 1); 4051.2Sperseant } 4061.2Sperseant if ($inoblkmode && m/^[-\t 0-9]*$/) { 4071.2Sperseant s/\t/ /og; 4081.2Sperseant s/^ *//o; 4091.2Sperseant s/ *$//o; 4101.2Sperseant @bn = split(' '); 4111.2Sperseant foreach $b (@bn) { 4121.2Sperseant $iblk_done{$b} = 1; 4131.2Sperseant } 4141.2Sperseant } 4151.2Sperseant} 4161.2Sperseantclose(DUMPLFS); 4171.2Sperseant 4181.2Sperseant# Report found and missing Ifile blocks 4191.2Sperseantprint "Ifile blocks found:"; 4201.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) { 4211.2Sperseant if ($iblk_done{$b} == 1) { 4221.2Sperseant print " $b"; 4231.2Sperseant } 4241.2Sperseant} 4251.2Sperseantprint "\n"; 4261.4Sperseant 4271.2Sperseantprint "Ifile blocks missing:"; 4281.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) { 4291.2Sperseant if ($iblk_done{$b} == 0) { 4301.2Sperseant $why{$b} =~ s/^ *//o; 4311.2Sperseant print " $b ($why{$b})"; 4321.2Sperseant } 4331.1Sperseant} 4341.2Sperseantprint "\n"; 4351.2Sperseant 4361.2Sperseantprint "$errstr\n"; 4371.2Sperseantexit 0; 438