check-all revision 1.3
11.1Sperseant#!/usr/pkg/bin/perl 21.3Sperseant# 31.3Sperseant# $NetBSD: check-all,v 1.3 2006/04/27 22:37:54 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.1Sperseant$rollid = 0; 541.1Sperseantopen(DUMPLFS, "dumplfs $rdev |"); 551.1Sperseant 561.1Sperseant# Look for "roll_id" so we don't use garbage 571.1Sperseantwhile (<DUMPLFS>) { 581.2Sperseant if ($ssize == 0 && m/ssize *([0-9]*)/) { 591.2Sperseant $ssize = $1; 601.2Sperseant } 611.2Sperseant if ($fsize == 0 && m/fsize *([0-9]*)/) { 621.2Sperseant $fsize = $1; 631.2Sperseant } 641.1Sperseant if (m/roll_id *([x0-9a-f]*)/) { 651.1Sperseant $rollid = $1; 661.1Sperseant last; 671.1Sperseant } 681.1Sperseant} 691.1Sperseant 701.1Sperseant# Now look for inodes and segment summaries. Build a hash table of these 711.1Sperseant# based on serial number. Ignore any with serial numbers lower than $sstart. 721.1Sperseant 731.1Sperseant%iloc = (); 741.2Sperseant%snloc = (); 751.2Sperseant%sumloc = (); 761.2Sperseantprint "Reading segments:"; 771.1Sperseantwhile (<DUMPLFS>) { 781.1Sperseant if (m/roll_id *([0-9a-f]*)/) { 791.1Sperseant # print "rollid $1\n"; 801.1Sperseant if ("0x$1" ne $rollid) { 811.1Sperseant # Skip the rest of this segment 821.3Sperseant print "{skip bad rollid 0x$1}"; 831.1Sperseant while(<DUMPLFS>) { 841.1Sperseant last if m/SEGMENT/; 851.1Sperseant } 861.2Sperseant # Fall through 871.1Sperseant } 881.1Sperseant } 891.2Sperseant if (m/roll_id.*serial *([0-9]*)/) { 901.1Sperseant $serno = $1; 911.2Sperseant $snloc{$serno} = $segnum; 921.2Sperseant $sumloc{$serno} = $sumloc; 931.3Sperseant print "($serno)"; 941.1Sperseant if ($serno < $sstart) { 951.1Sperseant # Skip the rest of this partial segment 961.3Sperseant #print "{skip bad serno $serno}"; 971.1Sperseant while(<DUMPLFS>) { 981.2Sperseant last if m/Segment Summary/ || 991.2Sperseant m/SEGMENT/; 1001.1Sperseant } 1011.2Sperseant # Fall through 1021.1Sperseant } 1031.1Sperseant } 1041.2Sperseant if (m/Segment Summary Info at 0x([0-9a-f]*)/) { 1051.2Sperseant $sumloc = $1; 1061.3Sperseant next; 1071.2Sperseant } 1081.1Sperseant if (m/0x([0-9a-f]*)/) { 1091.1Sperseant foreach $ss (split "0x", $_) { 1101.1Sperseant if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) { 1111.1Sperseant # print "iblk 0x$1\n"; 1121.1Sperseant $daddr = $1; 1131.1Sperseant if (m/[^0-9]1v1/) { 1141.1Sperseant # print "** ifblk 0x$daddr\n"; 1151.1Sperseant $iloc{$serno} = $daddr; 1161.2Sperseant $lastaddr = $daddr; 1171.1Sperseant } 1181.1Sperseant } 1191.1Sperseant } 1201.1Sperseant } 1211.2Sperseant if (m/SEGMENT *([0-9]*)/) { 1221.2Sperseant $segnum = $1; 1231.3Sperseant print "[$segnum]"; 1241.1Sperseant } 1251.1Sperseant} 1261.2Sperseantprint "\n"; 1271.1Sperseantclose(DUMPLFS); 1281.1Sperseant 1291.3Sperseant# Complain about missing partial-segments 1301.3Sperseantfor ($i = $sstart; $i < $serno; ++$i) { 1311.3Sperseant if (hex $sumloc{$i} == 0 && $i > 0) { 1321.3Sperseant print "Oops, couldn't find pseg $i\n"; 1331.3Sperseant } 1341.3Sperseant} 1351.3Sperseant 1361.2Sperseant# If there were no checkpoints, print *something* 1371.2Sperseantif ($#iloc == 0) { 1381.2Sperseant print "0 $sstart 0\n"; 1391.2Sperseant exit 0; 1401.2Sperseant} 1411.2Sperseant 1421.1Sperseant# 1431.1Sperseant# Now fsck each checkpoint in turn, beginning with $sstart. 1441.2Sperseant# Because the log wraps we will have to reconstruct the filesystem image 1451.2Sperseant# as it existed at each checkpoint before running fsck. 1461.2Sperseant# 1471.1Sperseant# Look for lines containing only caps or "!", but ignore known 1481.1Sperseant# false positives. 1491.1Sperseant# 1501.1Sperseant$error = 0; 1511.2Sperseant$lastgood = $sstart - 1; 1521.1Sperseantopen(LOG, ">>check-all.log"); 1531.2Sperseantprint "Available checkpoints:"; 1541.2Sperseantprint LOG "Available checkpoints:"; 1551.1Sperseantforeach $k (sort { $a <=> $b } keys %iloc) { 1561.1Sperseant $a = $iloc{$k}; 1571.2Sperseant print " $a"; 1581.2Sperseant print LOG " $a"; 1591.2Sperseant} 1601.2Sperseantprint "\n"; 1611.2Sperseantprint LOG "\n"; 1621.2Sperseant 1631.3Sperseant# 1641.3Sperseant# Copy the partial segments $_[0]--$_[1] from the raw device onto 1651.3Sperseant# the working file. Return the next partial-segment serial number 1661.3Sperseant# after the last one we copied (usually $_[1] + 1, except in case of 1671.3Sperseant# an error). 1681.3Sperseant# 1691.3Sperseantsub copypseg 1701.3Sperseant{ 1711.3Sperseant my ($blstart, $blstop, $segstop, $cmd); 1721.3Sperseant my ($totalstart, $totalstop); 1731.3Sperseant 1741.3Sperseant $totalstart = 0; 1751.3Sperseant $totalstop = 0; 1761.3Sperseant for ($i = $_[0]; $i <= $_[1]; ++$i) { 1771.3Sperseant $blstart = hex $sumloc{$i}; 1781.3Sperseant last if $blstart <= 0; 1791.3Sperseant $totalstart = $blstart if $totalstart == 0; 1801.3Sperseant $blstop = hex $sumloc{$i + 1}; 1811.3Sperseant $segstop = ((int ($blstart / $fps)) + 1) * $fps; 1821.3Sperseant if ($segstop < $blstop || $blstop < $blstart) { 1831.3Sperseant #print "Adjusting $blstop -> $segstop\n"; 1841.3Sperseant $blstop = $segstop; 1851.3Sperseant } 1861.3Sperseant $totalstop = $blstop; 1871.3Sperseant 1881.3Sperseant print "pseg $i: write blocks $blstart-", $blstop - 1, "\n"; 1891.3Sperseant $blstart = $blstop; 1901.3Sperseant } 1911.3Sperseant $cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$totalstart " . 1921.3Sperseant "skip=$totalstart conv=notrunc count=" . 1931.3Sperseant ($totalstop - $totalstart); 1941.3Sperseant# print "$cmd\n"; 1951.3Sperseant system("$cmd >/dev/null 2>&1"); 1961.3Sperseant 1971.3Sperseant return $i; 1981.3Sperseant} 1991.3Sperseant 2001.2Sperseantprint "Recreating filesystem image as of $sstart:\n"; 2011.2Sperseantif ($sstart == 0) { 2021.2Sperseant $cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage 2031.2Sperseant} else { 2041.2Sperseant $cmd = "dd if=$gfile of=$wfile bs=1m"; 2051.2Sperseant} 2061.2Sperseantprint "$cmd\n"; 2071.2Sperseantsystem("$cmd >/dev/null 2>&1"); 2081.2Sperseant 2091.3Sperseantprint "Copying over first superblock\n"; 2101.3Sperseantsystem("dd if=$rdev of=$wfile bs=8k count=2 conv=notrunc >/dev/null 2>&1"); 2111.3Sperseant 2121.2Sperseant$blstart = 0; 2131.2Sperseant$fps = $ssize / $fsize; 2141.3Sperseant$oind = ($sstart ? $sstart : 1); 2151.2Sperseantforeach $k (sort { $a <=> $b } keys %iloc) { 2161.2Sperseant $a = $iloc{$k}; 2171.2Sperseant 2181.2Sperseant if (hex($a) > hex($lastaddr)) { 2191.2Sperseant print "Skipping out-of-place checkpoint $k at $a\n"; 2201.2Sperseant next; 2211.2Sperseant } 2221.2Sperseant 2231.3Sperseant print "Recreate fs state at checkpoint pseg $k\n"; 2241.3Sperseant $oind = ©pseg($oind, $k); 2251.2Sperseant 2261.2Sperseant $cmd = "fsck_lfs -n -f -i 0x$a $wfile"; 2271.2Sperseant print "$cmd\n"; 2281.2Sperseant print LOG "$cmd\n"; 2291.2Sperseant open(FSCK, "$cmd 2>&1 |"); 2301.1Sperseant while(<FSCK>) { 2311.1Sperseant print LOG; 2321.1Sperseant chomp; 2331.1Sperseant 2341.1Sperseant # Known false positives (mismatch between sb and ifile, 2351.1Sperseant # which should be expected given we're using an arbitrarily 2361.3Sperseant # old version of the ifile) 2371.1Sperseant if (m/AVAIL GIVEN/ || 2381.1Sperseant m/BFREE GIVEN/ || 2391.1Sperseant m/DMETA GIVEN/ || 2401.2Sperseant m/NCLEAN GIVEN/ || 2411.2Sperseant m/FREE BUT NOT ON FREE LIST/ || # UNWRITTEN inodes OK 2421.2Sperseant m/FREE LIST HEAD IN SUPERBLOCK/ ) { 2431.1Sperseant next; 2441.1Sperseant } 2451.1Sperseant 2461.1Sperseant # Fsck reports errors in ALL CAPS 2471.1Sperseant # But don't count hex numbers as "lowercase". 2481.1Sperseant s/0x[0-9a-f]*//g; 2491.1Sperseant if (m/[A-Z]/ && ! m/[a-z]/) { 2501.1Sperseant $error = 1; 2511.2Sperseant $errsn = $k; 2521.2Sperseant $errstr = "1 $k 0x$a"; 2531.1Sperseant last; 2541.1Sperseant } 2551.1Sperseant } 2561.1Sperseant close(FSCK); 2571.1Sperseant last if $error; 2581.2Sperseant $lastgood = $k; # record last good serial number 2591.2Sperseant} 2601.3Sperseantif (!$errstr) { 2611.3Sperseant print "Bring filesystem state up to log wrap\n"; 2621.3Sperseant $lastgood = ©pseg($oind, 100000000000) - 1; 2631.2Sperseant 2641.2Sperseant print "Copying this good image to $gfile\n"; 2651.2Sperseant system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1"); 2661.3Sperseant print "0 $lastgood 0x$a\n"; 2671.2Sperseant exit 0; 2681.2Sperseant} 2691.2Sperseant 2701.2Sperseant# 2711.3Sperseant# Ifile write-checking paranoia. 2721.3Sperseant# 2731.2Sperseant# If we found an error, try to find which blocks of the Ifile inode changed 2741.2Sperseant# between the last good checkpoint and this checkpoint; and which blocks 2751.2Sperseant# *should* have changed. This means (1) which segments were written; and 2761.2Sperseant# (2) which inodes were written. The 0 block of the Ifile should always 2771.2Sperseant# have changed since lfs_avail is always in flux. 2781.2Sperseant# 2791.2Sperseant 2801.2Sperseant$cmd = "dumplfs"; 2811.2Sperseant$oseg = -1; 2821.2Sperseant%iblk = (); 2831.2Sperseant%iblk_done = (); 2841.2Sperseant%why = (); 2851.2Sperseant$iblk{0} = 1; 2861.2Sperseantfor ($i = $lastgood + 1; $i <= $errsn; $i++) { 2871.2Sperseant if ($oseg != $snloc{$i}) { 2881.2Sperseant $oseg = 0 + $snloc{$i}; 2891.2Sperseant $cmd .= " -s$oseg"; 2901.2Sperseant } 2911.1Sperseant} 2921.2Sperseant$cmd .= " $rdev"; 2931.1Sperseant 2941.2Sperseantopen(DUMPLFS, "$cmd |"); 2951.2Sperseantwhile(<DUMPLFS>) { 2961.2Sperseant if (m/ifpb *([0-9]*)/) { 2971.2Sperseant $ifpb = $1; 2981.2Sperseant } 2991.2Sperseant if (m/sepb *([0-9]*)/) { 3001.2Sperseant $sepb = $1; 3011.2Sperseant } 3021.2Sperseant if (m/cleansz *([0-9]*)/) { 3031.2Sperseant $cleansz = $1; 3041.2Sperseant } 3051.2Sperseant if (m/segtabsz *([0-9]*)/) { 3061.2Sperseant $segtabsz = $1; 3071.2Sperseant } 3081.2Sperseant last if m/SEGMENT/; 3091.2Sperseant} 3101.2Sperseantwhile(<DUMPLFS>) { 3111.2Sperseant chomp; 3121.2Sperseant 3131.2Sperseant # Skip over partial segments outside our range of interest 3141.2Sperseant if (m/roll_id.*serial *([0-9]*)/) { 3151.2Sperseant $serno = $1; 3161.2Sperseant if ($serno <= $lastgood || $serno > $errsn) { 3171.2Sperseant # Skip the rest of this partial segment 3181.2Sperseant while(<DUMPLFS>) { 3191.2Sperseant last if m/Segment Summary/ || m/SEGMENT/; 3201.2Sperseant } 3211.2Sperseant next; 3221.2Sperseant } 3231.2Sperseant } 3241.2Sperseant 3251.2Sperseant # Look for inodes 3261.2Sperseant if (m/Inode addresses/) { 3271.2Sperseant s/^[^{]*{/ /o; 3281.2Sperseant s/}[^{]*$/ /o; 3291.2Sperseant s/}[^{]*{/,/og; 3301.2Sperseant s/v[0-9]*//og; 3311.2Sperseant @ilist = split(','); 3321.2Sperseant foreach $i (@ilist) { 3331.2Sperseant $i =~ s/ *//og; 3341.2Sperseant next if $i == 1; 3351.2Sperseant $iaddr = $cleansz + $segtabsz + int ($i / $ifpb); 3361.2Sperseant $iblk{$iaddr} = 1; 3371.2Sperseant $why{$iaddr} .= " $i"; 3381.2Sperseant } 3391.2Sperseant } 3401.2Sperseant 3411.2Sperseant # Look for Ifile blocks actually written 3421.2Sperseant if (m/FINFO for inode: ([0-9]*) version/) { 3431.2Sperseant $i = $1; 3441.2Sperseant $inoblkmode = ($i == 1); 3451.2Sperseant } 3461.2Sperseant if ($inoblkmode && m/^[-\t 0-9]*$/) { 3471.2Sperseant s/\t/ /og; 3481.2Sperseant s/^ *//o; 3491.2Sperseant s/ *$//o; 3501.2Sperseant @bn = split(' '); 3511.2Sperseant foreach $b (@bn) { 3521.2Sperseant $iblk_done{$b} = 1; 3531.2Sperseant } 3541.2Sperseant } 3551.2Sperseant} 3561.2Sperseantclose(DUMPLFS); 3571.2Sperseant 3581.2Sperseant# Report found and missing Ifile blocks 3591.2Sperseantprint "Ifile blocks found:"; 3601.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) { 3611.2Sperseant if ($iblk_done{$b} == 1) { 3621.2Sperseant print " $b"; 3631.2Sperseant } 3641.2Sperseant} 3651.2Sperseantprint "\n"; 3661.2Sperseant 3671.2Sperseantprint "Ifile blocks missing:"; 3681.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) { 3691.2Sperseant if ($iblk_done{$b} == 0) { 3701.2Sperseant $why{$b} =~ s/^ *//o; 3711.2Sperseant print " $b ($why{$b})"; 3721.2Sperseant } 3731.1Sperseant} 3741.2Sperseantprint "\n"; 3751.2Sperseant 3761.2Sperseantprint "$errstr\n"; 3771.2Sperseantexit 0; 378