check-all revision 1.2 1 1.1 perseant #!/usr/pkg/bin/perl
2 1.1 perseant
3 1.1 perseant #
4 1.1 perseant # Use dumplfs to find all locations of the Ifile inode on a given disk.
5 1.1 perseant # Order these by serial number and call fsck_lfs on the raw disk for each.
6 1.1 perseant # If any fsck gives errors (lines containing "!", with a few exceptions)
7 1.1 perseant # print an error code with the daddr of the failing Ifile inode location.
8 1.1 perseant #
9 1.1 perseant
10 1.2 perseant $| = 1;
11 1.1 perseant $rdev = $ARGV[0];
12 1.2 perseant $gfile = $ARGV[1];
13 1.2 perseant $wfile = $ARGV[2];
14 1.2 perseant $sstart = $ARGV[3];
15 1.1 perseant $rollid = 0;
16 1.1 perseant open(DUMPLFS, "dumplfs $rdev |");
17 1.1 perseant
18 1.1 perseant # Look for "roll_id" so we don't use garbage
19 1.1 perseant while (<DUMPLFS>) {
20 1.2 perseant if ($ssize == 0 && m/ssize *([0-9]*)/) {
21 1.2 perseant $ssize = $1;
22 1.2 perseant }
23 1.2 perseant if ($fsize == 0 && m/fsize *([0-9]*)/) {
24 1.2 perseant $fsize = $1;
25 1.2 perseant }
26 1.1 perseant if (m/roll_id *([x0-9a-f]*)/) {
27 1.1 perseant $rollid = $1;
28 1.1 perseant last;
29 1.1 perseant }
30 1.1 perseant }
31 1.1 perseant
32 1.1 perseant # Now look for inodes and segment summaries. Build a hash table of these
33 1.1 perseant # based on serial number. Ignore any with serial numbers lower than $sstart.
34 1.1 perseant
35 1.1 perseant %iloc = ();
36 1.2 perseant %snloc = ();
37 1.2 perseant %sumloc = ();
38 1.2 perseant print "Reading segments:";
39 1.1 perseant while (<DUMPLFS>) {
40 1.1 perseant if (m/roll_id *([0-9a-f]*)/) {
41 1.1 perseant # print "rollid $1\n";
42 1.1 perseant if ("0x$1" ne $rollid) {
43 1.1 perseant # Skip the rest of this segment
44 1.1 perseant while(<DUMPLFS>) {
45 1.1 perseant last if m/SEGMENT/;
46 1.1 perseant }
47 1.2 perseant # Fall through
48 1.1 perseant }
49 1.1 perseant }
50 1.2 perseant if (m/roll_id.*serial *([0-9]*)/) {
51 1.1 perseant $serno = $1;
52 1.2 perseant $snloc{$serno} = $segnum;
53 1.2 perseant $sumloc{$serno} = $sumloc;
54 1.1 perseant # print "serno $serno\n";
55 1.1 perseant if ($serno < $sstart) {
56 1.1 perseant # Skip the rest of this partial segment
57 1.1 perseant while(<DUMPLFS>) {
58 1.2 perseant last if m/Segment Summary/ ||
59 1.2 perseant m/SEGMENT/;
60 1.1 perseant }
61 1.2 perseant # Fall through
62 1.1 perseant }
63 1.1 perseant }
64 1.2 perseant if (m/Segment Summary Info at 0x([0-9a-f]*)/) {
65 1.2 perseant $sumloc = $1;
66 1.2 perseant }
67 1.1 perseant if (m/0x([0-9a-f]*)/) {
68 1.1 perseant foreach $ss (split "0x", $_) {
69 1.1 perseant if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) {
70 1.1 perseant # print "iblk 0x$1\n";
71 1.1 perseant $daddr = $1;
72 1.1 perseant if (m/[^0-9]1v1/) {
73 1.1 perseant # print "** ifblk 0x$daddr\n";
74 1.1 perseant $iloc{$serno} = $daddr;
75 1.2 perseant $lastaddr = $daddr;
76 1.1 perseant }
77 1.1 perseant }
78 1.1 perseant }
79 1.1 perseant }
80 1.2 perseant if (m/SEGMENT *([0-9]*)/) {
81 1.2 perseant $segnum = $1;
82 1.2 perseant print " $segnum";
83 1.1 perseant }
84 1.1 perseant }
85 1.2 perseant print "\n";
86 1.1 perseant close(DUMPLFS);
87 1.1 perseant
88 1.2 perseant # If there were no checkpoints, print *something*
89 1.2 perseant if ($#iloc == 0) {
90 1.2 perseant print "0 $sstart 0\n";
91 1.2 perseant exit 0;
92 1.2 perseant }
93 1.2 perseant
94 1.1 perseant #
95 1.1 perseant # Now fsck each checkpoint in turn, beginning with $sstart.
96 1.2 perseant # Because the log wraps we will have to reconstruct the filesystem image
97 1.2 perseant # as it existed at each checkpoint before running fsck.
98 1.2 perseant #
99 1.1 perseant # Look for lines containing only caps or "!", but ignore known
100 1.1 perseant # false positives.
101 1.1 perseant #
102 1.1 perseant $error = 0;
103 1.2 perseant $lastgood = $sstart - 1;
104 1.1 perseant open(LOG, ">>check-all.log");
105 1.2 perseant print "Available checkpoints:";
106 1.2 perseant print LOG "Available checkpoints:";
107 1.1 perseant foreach $k (sort { $a <=> $b } keys %iloc) {
108 1.1 perseant $a = $iloc{$k};
109 1.2 perseant print " $a";
110 1.2 perseant print LOG " $a";
111 1.2 perseant }
112 1.2 perseant print "\n";
113 1.2 perseant print LOG "\n";
114 1.2 perseant
115 1.2 perseant print "Recreating filesystem image as of $sstart:\n";
116 1.2 perseant if ($sstart == 0) {
117 1.2 perseant $cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage
118 1.2 perseant } else {
119 1.2 perseant $cmd = "dd if=$gfile of=$wfile bs=1m";
120 1.2 perseant }
121 1.2 perseant print "$cmd\n";
122 1.2 perseant system("$cmd >/dev/null 2>&1");
123 1.2 perseant
124 1.2 perseant $blstart = 0;
125 1.2 perseant $fps = $ssize / $fsize;
126 1.2 perseant foreach $k (sort { $a <=> $b } keys %iloc) {
127 1.2 perseant $a = $iloc{$k};
128 1.2 perseant
129 1.2 perseant if (hex($a) > hex($lastaddr)) {
130 1.2 perseant print "Skipping out-of-place checkpoint $k at $a\n";
131 1.2 perseant next;
132 1.2 perseant }
133 1.2 perseant
134 1.2 perseant # Copy the balance of the "new" fs image over the old one.
135 1.2 perseant $blstop = int ((hex $a) / $fps) + 1;
136 1.2 perseant $blstop *= $fps;
137 1.2 perseant if ($blstop != $blstart) {
138 1.2 perseant $cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$blstart " .
139 1.2 perseant "skip=$blstart conv=notrunc";
140 1.2 perseant if ($blstop) {
141 1.2 perseant $cmd .= " count=" . ($blstop - $blstart);
142 1.2 perseant }
143 1.2 perseant $blstart = $blstop;
144 1.2 perseant print "$cmd\n";
145 1.2 perseant system("$cmd >/dev/null 2>&1");
146 1.2 perseant }
147 1.2 perseant
148 1.2 perseant $cmd = "fsck_lfs -n -f -i 0x$a $wfile";
149 1.2 perseant print "$cmd\n";
150 1.2 perseant print LOG "$cmd\n";
151 1.2 perseant open(FSCK, "$cmd 2>&1 |");
152 1.1 perseant while(<FSCK>) {
153 1.1 perseant print LOG;
154 1.1 perseant chomp;
155 1.1 perseant
156 1.1 perseant # Known false positives (mismatch between sb and ifile,
157 1.1 perseant # which should be expected given we're using an arbitrarily
158 1.1 perseant # old version fo the ifile)
159 1.1 perseant if (m/AVAIL GIVEN/ ||
160 1.1 perseant m/BFREE GIVEN/ ||
161 1.1 perseant m/DMETA GIVEN/ ||
162 1.2 perseant m/NCLEAN GIVEN/ ||
163 1.2 perseant m/FREE BUT NOT ON FREE LIST/ || # UNWRITTEN inodes OK
164 1.2 perseant m/FREE LIST HEAD IN SUPERBLOCK/ ) {
165 1.1 perseant next;
166 1.1 perseant }
167 1.1 perseant
168 1.1 perseant # Fsck reports errors in ALL CAPS
169 1.1 perseant # But don't count hex numbers as "lowercase".
170 1.1 perseant s/0x[0-9a-f]*//g;
171 1.1 perseant if (m/[A-Z]/ && ! m/[a-z]/) {
172 1.1 perseant $error = 1;
173 1.2 perseant $errsn = $k;
174 1.2 perseant $errstr = "1 $k 0x$a";
175 1.1 perseant last;
176 1.1 perseant }
177 1.1 perseant }
178 1.1 perseant close(FSCK);
179 1.1 perseant last if $error;
180 1.2 perseant $lastgood = $k; # record last good serial number
181 1.2 perseant }
182 1.2 perseant $errstr = "0 $lastgood 0x$a" unless $errstr;
183 1.2 perseant
184 1.2 perseant if ($error == 0) {
185 1.2 perseant print "Copying this good image to $gfile\n";
186 1.2 perseant system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1");
187 1.2 perseant print "$errstr\n";
188 1.2 perseant exit 0;
189 1.2 perseant }
190 1.2 perseant
191 1.2 perseant #
192 1.2 perseant # If we found an error, try to find which blocks of the Ifile inode changed
193 1.2 perseant # between the last good checkpoint and this checkpoint; and which blocks
194 1.2 perseant # *should* have changed. This means (1) which segments were written; and
195 1.2 perseant # (2) which inodes were written. The 0 block of the Ifile should always
196 1.2 perseant # have changed since lfs_avail is always in flux.
197 1.2 perseant #
198 1.2 perseant
199 1.2 perseant $cmd = "dumplfs";
200 1.2 perseant $oseg = -1;
201 1.2 perseant %iblk = ();
202 1.2 perseant %iblk_done = ();
203 1.2 perseant %why = ();
204 1.2 perseant $iblk{0} = 1;
205 1.2 perseant for ($i = $lastgood + 1; $i <= $errsn; $i++) {
206 1.2 perseant if ($oseg != $snloc{$i}) {
207 1.2 perseant $oseg = 0 + $snloc{$i};
208 1.2 perseant $cmd .= " -s$oseg";
209 1.2 perseant }
210 1.1 perseant }
211 1.2 perseant $cmd .= " $rdev";
212 1.1 perseant
213 1.2 perseant open(DUMPLFS, "$cmd |");
214 1.2 perseant while(<DUMPLFS>) {
215 1.2 perseant if (m/ifpb *([0-9]*)/) {
216 1.2 perseant $ifpb = $1;
217 1.2 perseant }
218 1.2 perseant if (m/sepb *([0-9]*)/) {
219 1.2 perseant $sepb = $1;
220 1.2 perseant }
221 1.2 perseant if (m/cleansz *([0-9]*)/) {
222 1.2 perseant $cleansz = $1;
223 1.2 perseant }
224 1.2 perseant if (m/segtabsz *([0-9]*)/) {
225 1.2 perseant $segtabsz = $1;
226 1.2 perseant }
227 1.2 perseant last if m/SEGMENT/;
228 1.2 perseant }
229 1.2 perseant while(<DUMPLFS>) {
230 1.2 perseant chomp;
231 1.2 perseant
232 1.2 perseant # Skip over partial segments outside our range of interest
233 1.2 perseant if (m/roll_id.*serial *([0-9]*)/) {
234 1.2 perseant $serno = $1;
235 1.2 perseant if ($serno <= $lastgood || $serno > $errsn) {
236 1.2 perseant # Skip the rest of this partial segment
237 1.2 perseant while(<DUMPLFS>) {
238 1.2 perseant last if m/Segment Summary/ || m/SEGMENT/;
239 1.2 perseant }
240 1.2 perseant next;
241 1.2 perseant }
242 1.2 perseant }
243 1.2 perseant
244 1.2 perseant # Look for inodes
245 1.2 perseant if (m/Inode addresses/) {
246 1.2 perseant s/^[^{]*{/ /o;
247 1.2 perseant s/}[^{]*$/ /o;
248 1.2 perseant s/}[^{]*{/,/og;
249 1.2 perseant s/v[0-9]*//og;
250 1.2 perseant @ilist = split(',');
251 1.2 perseant foreach $i (@ilist) {
252 1.2 perseant $i =~ s/ *//og;
253 1.2 perseant next if $i == 1;
254 1.2 perseant $iaddr = $cleansz + $segtabsz + int ($i / $ifpb);
255 1.2 perseant $iblk{$iaddr} = 1;
256 1.2 perseant $why{$iaddr} .= " $i";
257 1.2 perseant }
258 1.2 perseant }
259 1.2 perseant
260 1.2 perseant # Look for Ifile blocks actually written
261 1.2 perseant if (m/FINFO for inode: ([0-9]*) version/) {
262 1.2 perseant $i = $1;
263 1.2 perseant $inoblkmode = ($i == 1);
264 1.2 perseant }
265 1.2 perseant if ($inoblkmode && m/^[-\t 0-9]*$/) {
266 1.2 perseant s/\t/ /og;
267 1.2 perseant s/^ *//o;
268 1.2 perseant s/ *$//o;
269 1.2 perseant @bn = split(' ');
270 1.2 perseant foreach $b (@bn) {
271 1.2 perseant $iblk_done{$b} = 1;
272 1.2 perseant }
273 1.2 perseant }
274 1.2 perseant }
275 1.2 perseant close(DUMPLFS);
276 1.2 perseant
277 1.2 perseant # Report found and missing Ifile blocks
278 1.2 perseant print "Ifile blocks found:";
279 1.2 perseant foreach $b (sort { $a <=> $b } keys %iblk) {
280 1.2 perseant if ($iblk_done{$b} == 1) {
281 1.2 perseant print " $b";
282 1.2 perseant }
283 1.2 perseant }
284 1.2 perseant print "\n";
285 1.2 perseant
286 1.2 perseant print "Ifile blocks missing:";
287 1.2 perseant foreach $b (sort { $a <=> $b } keys %iblk) {
288 1.2 perseant if ($iblk_done{$b} == 0) {
289 1.2 perseant $why{$b} =~ s/^ *//o;
290 1.2 perseant print " $b ($why{$b})";
291 1.2 perseant }
292 1.1 perseant }
293 1.2 perseant print "\n";
294 1.2 perseant
295 1.2 perseant print "$errstr\n";
296 1.2 perseant exit 0;
297