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