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