3 # $NetBSD: check-all,v 1.4 2006/07/21 00:29:23 perseant Exp $
5 # Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
8 # This code is derived from software contributed to The NetBSD Foundation
9 # by Konrad E. Schroder <perseant@hhhh.org>.
11 # Redistribution and use in source and binary forms, with or without
12 # modification, are permitted provided that the following conditions
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.
20 # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
34 # Use dumplfs to find all locations of the Ifile inode on a given disk.
35 # Order these by serial number and call fsck_lfs on the raw disk for each.
36 # If any fsck gives errors (any line of all capital letters, with a few
37 # exceptions) print an error code with the daddr of the failing Ifile inode
46 $test_rfw = 1; # $ARGV[4];
48 open(DUMPLFS
, "dumplfs $rdev |");
50 # Look for "roll_id" so we don't use garbage
52 if ($ssize == 0 && m/ssize *([0-9]*)/) {
55 if ($fsize == 0 && m/fsize *([0-9]*)/) {
58 if (m/roll_id *([x0-9a-f]*)/) {
64 # Now look for inodes and segment summaries. Build a hash table of these
65 # based on serial number. Ignore any with serial numbers lower than $sstart.
70 print "Reading segments:";
72 if (m/roll_id *([0-9a-f]*)/) {
73 # print "rollid $1\n";
74 if ("0x$1" ne $rollid) {
75 # Skip the rest of this segment
76 print "{skip bad rollid 0x$1}";
83 if (m/roll_id.*serial *([0-9]*)/) {
85 $snloc{$serno} = $segnum;
86 $sumloc{$serno} = $sumloc;
88 if ($serno < $sstart) {
89 # Skip the rest of this partial segment
90 #print "{skip bad serno $serno}";
92 last if m/Segment Summary/ ||
98 if (m/Segment Summary Info at 0x([0-9a-f]*)/) {
102 if (m/0x([0-9a-f]*)/) {
103 foreach $ss (split "0x", $_) {
104 if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) {
105 # print "iblk 0x$1\n";
108 # print "** ifblk 0x$daddr\n";
109 $iloc{$serno} = $daddr;
115 if (m/SEGMENT *([0-9]*)/) {
123 # Complain about missing partial-segments
124 for ($i = $sstart; $i < $serno; ++$i) {
125 if (hex $sumloc{$i} == 0 && $i > 0) {
126 print "Oops, couldn't find pseg $i\n";
130 # If there were no checkpoints, print *something*
132 print "0 $sstart 0\n";
137 # Now fsck each checkpoint in turn, beginning with $sstart.
138 # Because the log wraps we will have to reconstruct the filesystem image
139 # as it existed at each checkpoint before running fsck.
141 # Look for lines containing only caps or "!", but ignore known
145 $lastgood = $sstart - 1;
146 open(LOG
, ">>check-all.log");
147 print "Available checkpoints:";
148 print LOG
"Available checkpoints:";
149 foreach $k (sort { $a <=> $b } keys %iloc) {
158 # Copy the partial segments $_[0]--$_[1] from the raw device onto
159 # the working file. Return the next partial-segment serial number
160 # after the last one we copied (usually $_[1] + 1, except in case of
165 my ($blstart, $blstop, $segstop, $cmd);
166 my ($totalstart, $totalstop);
170 for ($i = $_[0]; $i <= $_[1]; ++$i) {
171 $blstart = hex $sumloc{$i};
172 last if $blstart <= 0;
173 $totalstart = $blstart if $totalstart == 0;
174 $blstop = hex $sumloc{$i + 1};
175 $segstop = ((int ($blstart / $fps)) + 1) * $fps;
176 if ($segstop < $blstop || $blstop < $blstart) {
177 #print "Adjusting $blstop -> $segstop\n";
180 $totalstop = $blstop;
182 print "pseg $i: write blocks ", hex $blstart, "-", hex ($blstop - 1), "\n";
185 $cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$totalstart " .
186 "skip=$totalstart conv=notrunc count=" .
187 ($totalstop - $totalstart);
189 system("$cmd >/dev/null 2>&1");
194 print "Recreating filesystem image as of $sstart:\n";
196 $cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage
198 $cmd = "dd if=$gfile of=$wfile bs=1m";
201 system("$cmd >/dev/null 2>&1");
203 print "Copying over first superblock\n";
204 system("dd if=$rdev of=$wfile bs=8k count=2 conv=notrunc >/dev/null 2>&1");
213 $flags = "-n -f -i 0x$a $wfile" unless $flags;
215 $cmd = "fsck_lfs $flags";
218 open(FSCK
, "$cmd 2>&1 |");
224 # Known false positives (mismatch between sb and ifile,
225 # which should be expected given we're using an arbitrarily
226 # old version of the ifile)
227 if (m/AVAIL GIVEN/ ||
231 m/FREE BUT NOT ON FREE LIST/ || # UNWRITTEN inodes OK
232 m/FILE SYSTEM WAS MODIFIED/ ||
233 m/FREE LIST HEAD IN SUPERBLOCK/ ) {
237 # Fsck reports errors in ALL CAPS
238 # But don't count hex numbers as "lowercase".
241 if (m/[A-Z]/ && ! m/[a-z]/) {
244 $errstr = "1 $k 0x$a $oline";
248 # Log everything we get, except for some things we
249 # will see every single time.
250 if (m/checkpoint invalid/ ||
251 m/skipping free list check/ ||
252 m/expect discrepancies/) {
262 $errstr = "1 $k 0x$a <" . (hex $?
) . ">";
265 if ($error || $printit) {
271 $fps = $ssize / $fsize;
272 $oind = ($sstart ?
$sstart : 1);
273 BIGLOOP
: foreach $k (sort { $a <=> $b } keys %iloc) {
276 if (hex($a) > hex($lastaddr)) {
277 print "Skipping out-of-place checkpoint $k at $a\n";
281 if ($test_rfw && $iloc{$oind - 1}) {
282 for ($tk = $oind; $tk < $k; $tk++) {
283 print "Test roll-forward agent at non-checkpoint pseg $tk\n";
284 print LOG
"Test roll-forward agent at non-checkpoint pseg $tk\n";
285 ©pseg
($oind, $tk);
286 # Add -d flag here for verbose debugging info
287 $flags = "-p -f -i 0x" . $iloc{$oind - 1} . " $wfile";
288 &test_fsck
($iloc{$oind - 1}, $flags, 1);
289 last BIGLOOP
if $error;
291 # note lack of -i flag, since the roll-forward
292 # will have rewritten the superblocks.
293 &test_fsck
($iloc{$oind - 1}, "-n -f $wfile", 0);
294 last BIGLOOP
if $error;
298 print "Recreate fs state at checkpoint pseg $k (from " . ($oind - 1) .
300 $oind = ©pseg
($oind, $k);
302 &test_fsck
($a, "", 0);
305 $lastgood = $k; # record last good serial number
314 print "Bring filesystem state up to log wrap\n";
315 $lastgood = ©pseg
($oind, 100000000000) - 1;
317 print "Copying this good image to $gfile\n";
318 system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1");
319 print "0 $lastgood 0x$a\n";
324 # Ifile write-checking paranoia.
326 # If we found an error, try to find which blocks of the Ifile inode changed
327 # between the last good checkpoint and this checkpoint; and which blocks
328 # *should* have changed. This means (1) which segments were written; and
329 # (2) which inodes were written. The 0 block of the Ifile should always
330 # have changed since lfs_avail is always in flux.
339 for ($i = $lastgood + 1; $i <= $errsn; $i++) {
340 if ($oseg != $snloc{$i}) {
341 $oseg = 0 + $snloc{$i};
347 open(DUMPLFS
, "$cmd |");
349 if (m/ifpb *([0-9]*)/) {
352 if (m/sepb *([0-9]*)/) {
355 if (m/cleansz *([0-9]*)/) {
358 if (m/segtabsz *([0-9]*)/) {
366 # Skip over partial segments outside our range of interest
367 if (m/roll_id.*serial *([0-9]*)/) {
369 if ($serno <= $lastgood || $serno > $errsn) {
370 # Skip the rest of this partial segment
372 last if m/Segment Summary/ || m/SEGMENT/;
379 if (m/Inode addresses/) {
385 foreach $i (@ilist) {
388 $iaddr = $cleansz + $segtabsz + int ($i / $ifpb);
390 $why{$iaddr} .= " $i";
394 # Look for Ifile blocks actually written
395 if (m/FINFO for inode: ([0-9]*) version/) {
397 $inoblkmode = ($i == 1);
399 if ($inoblkmode && m/^[-\t 0-9]*$/) {
411 # Report found and missing Ifile blocks
412 print "Ifile blocks found:";
413 foreach $b (sort { $a <=> $b } keys %iblk) {
414 if ($iblk_done{$b} == 1) {
420 print "Ifile blocks missing:";
421 foreach $b (sort { $a <=> $b } keys %iblk) {
422 if ($iblk_done{$b} == 0) {
423 $why{$b} =~ s/^ *//o;
424 print " $b ($why{$b})";