check-all revision 1.5 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