check-all revision 1.2
11.1Sperseant#!/usr/pkg/bin/perl
21.1Sperseant
31.1Sperseant#
41.1Sperseant# Use dumplfs to find all locations of the Ifile inode on a given disk.
51.1Sperseant# Order these by serial number and call fsck_lfs on the raw disk for each.
61.1Sperseant# If any fsck gives errors (lines containing "!", with a few exceptions)
71.1Sperseant# print an error code with the daddr of the failing Ifile inode location.
81.1Sperseant#
91.1Sperseant
101.2Sperseant$| = 1;
111.1Sperseant$rdev = $ARGV[0];
121.2Sperseant$gfile = $ARGV[1];
131.2Sperseant$wfile = $ARGV[2];
141.2Sperseant$sstart = $ARGV[3];
151.1Sperseant$rollid = 0;
161.1Sperseantopen(DUMPLFS, "dumplfs $rdev |");
171.1Sperseant
181.1Sperseant# Look for "roll_id" so we don't use garbage
191.1Sperseantwhile (<DUMPLFS>) {
201.2Sperseant	if ($ssize == 0 && m/ssize *([0-9]*)/) {
211.2Sperseant		$ssize = $1;
221.2Sperseant	}
231.2Sperseant	if ($fsize == 0 && m/fsize *([0-9]*)/) {
241.2Sperseant		$fsize = $1;
251.2Sperseant	}
261.1Sperseant	if (m/roll_id *([x0-9a-f]*)/) {
271.1Sperseant		$rollid = $1;
281.1Sperseant		last;
291.1Sperseant	}
301.1Sperseant}
311.1Sperseant
321.1Sperseant# Now look for inodes and segment summaries.  Build a hash table of these
331.1Sperseant# based on serial number.  Ignore any with serial numbers lower than $sstart.
341.1Sperseant
351.1Sperseant%iloc = ();
361.2Sperseant%snloc = ();
371.2Sperseant%sumloc = ();
381.2Sperseantprint "Reading segments:";
391.1Sperseantwhile (<DUMPLFS>) {
401.1Sperseant	if (m/roll_id *([0-9a-f]*)/) {
411.1Sperseant		# print "rollid $1\n";
421.1Sperseant		if ("0x$1" ne $rollid) {
431.1Sperseant			# Skip the rest of this segment
441.1Sperseant			while(<DUMPLFS>) {
451.1Sperseant				last if m/SEGMENT/;
461.1Sperseant			}
471.2Sperseant			# Fall through
481.1Sperseant		}
491.1Sperseant	}
501.2Sperseant	if (m/roll_id.*serial *([0-9]*)/) {
511.1Sperseant		$serno = $1;
521.2Sperseant		$snloc{$serno} = $segnum;
531.2Sperseant		$sumloc{$serno} = $sumloc;
541.1Sperseant		# print "serno $serno\n";
551.1Sperseant		if ($serno < $sstart) {
561.1Sperseant			# Skip the rest of this partial segment
571.1Sperseant			while(<DUMPLFS>) {
581.2Sperseant				last if m/Segment Summary/ ||
591.2Sperseant					m/SEGMENT/;
601.1Sperseant			}
611.2Sperseant			# Fall through
621.1Sperseant		}
631.1Sperseant	}
641.2Sperseant	if (m/Segment Summary Info at 0x([0-9a-f]*)/) {
651.2Sperseant		$sumloc = $1;
661.2Sperseant	}
671.1Sperseant	if (m/0x([0-9a-f]*)/) {
681.1Sperseant		foreach $ss (split "0x", $_) {
691.1Sperseant			if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) {
701.1Sperseant				# print "iblk 0x$1\n";
711.1Sperseant				$daddr = $1;
721.1Sperseant				if (m/[^0-9]1v1/) {
731.1Sperseant					# print "** ifblk 0x$daddr\n";
741.1Sperseant					$iloc{$serno} = $daddr;
751.2Sperseant					$lastaddr = $daddr;
761.1Sperseant				}
771.1Sperseant			}
781.1Sperseant		}
791.1Sperseant	}
801.2Sperseant	if (m/SEGMENT *([0-9]*)/) {
811.2Sperseant		$segnum = $1;
821.2Sperseant		print " $segnum";
831.1Sperseant	}
841.1Sperseant}
851.2Sperseantprint "\n";
861.1Sperseantclose(DUMPLFS);
871.1Sperseant
881.2Sperseant# If there were no checkpoints, print *something*
891.2Sperseantif ($#iloc == 0) {
901.2Sperseant	print "0 $sstart 0\n";
911.2Sperseant	exit 0;
921.2Sperseant}
931.2Sperseant
941.1Sperseant#
951.1Sperseant# Now fsck each checkpoint in turn, beginning with $sstart.
961.2Sperseant# Because the log wraps we will have to reconstruct the filesystem image
971.2Sperseant# as it existed at each checkpoint before running fsck.
981.2Sperseant#
991.1Sperseant# Look for lines containing only caps or "!", but ignore known
1001.1Sperseant# false positives.
1011.1Sperseant#
1021.1Sperseant$error = 0;
1031.2Sperseant$lastgood = $sstart - 1;
1041.1Sperseantopen(LOG, ">>check-all.log");
1051.2Sperseantprint "Available checkpoints:";
1061.2Sperseantprint LOG "Available checkpoints:";
1071.1Sperseantforeach $k (sort { $a <=> $b } keys %iloc) {
1081.1Sperseant	$a = $iloc{$k};
1091.2Sperseant	print " $a";
1101.2Sperseant	print LOG " $a";
1111.2Sperseant}
1121.2Sperseantprint "\n";
1131.2Sperseantprint LOG "\n";
1141.2Sperseant
1151.2Sperseantprint "Recreating filesystem image as of $sstart:\n";
1161.2Sperseantif ($sstart == 0) {
1171.2Sperseant	$cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage
1181.2Sperseant} else {
1191.2Sperseant	$cmd = "dd if=$gfile of=$wfile bs=1m";
1201.2Sperseant}
1211.2Sperseantprint "$cmd\n";
1221.2Sperseantsystem("$cmd >/dev/null 2>&1");
1231.2Sperseant
1241.2Sperseant$blstart = 0;
1251.2Sperseant$fps = $ssize / $fsize;
1261.2Sperseantforeach $k (sort { $a <=> $b } keys %iloc) {
1271.2Sperseant	$a = $iloc{$k};
1281.2Sperseant
1291.2Sperseant	if (hex($a) > hex($lastaddr)) {
1301.2Sperseant		print "Skipping out-of-place checkpoint $k at $a\n";
1311.2Sperseant		next;
1321.2Sperseant	}
1331.2Sperseant
1341.2Sperseant	# Copy the balance of the "new" fs image over the old one.
1351.2Sperseant	$blstop = int ((hex $a) / $fps) + 1;
1361.2Sperseant	$blstop *= $fps;
1371.2Sperseant	if ($blstop != $blstart) {
1381.2Sperseant		$cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$blstart " .
1391.2Sperseant			"skip=$blstart conv=notrunc";
1401.2Sperseant		if ($blstop) {
1411.2Sperseant			$cmd .= " count=" . ($blstop - $blstart);
1421.2Sperseant		}
1431.2Sperseant		$blstart = $blstop;
1441.2Sperseant		print "$cmd\n";
1451.2Sperseant		system("$cmd >/dev/null 2>&1");
1461.2Sperseant	}
1471.2Sperseant
1481.2Sperseant	$cmd = "fsck_lfs -n -f -i 0x$a $wfile";
1491.2Sperseant	print "$cmd\n";
1501.2Sperseant	print LOG "$cmd\n";
1511.2Sperseant	open(FSCK, "$cmd 2>&1 |");
1521.1Sperseant	while(<FSCK>) {
1531.1Sperseant		print LOG;
1541.1Sperseant		chomp;
1551.1Sperseant
1561.1Sperseant		# Known false positives (mismatch between sb and ifile,
1571.1Sperseant		# which should be expected given we're using an arbitrarily
1581.1Sperseant		# old version fo the ifile)
1591.1Sperseant		if (m/AVAIL GIVEN/ ||
1601.1Sperseant		    m/BFREE GIVEN/ ||
1611.1Sperseant		    m/DMETA GIVEN/ ||
1621.2Sperseant		    m/NCLEAN GIVEN/ ||
1631.2Sperseant		    m/FREE BUT NOT ON FREE LIST/ ||	# UNWRITTEN inodes OK
1641.2Sperseant		    m/FREE LIST HEAD IN SUPERBLOCK/ ) {
1651.1Sperseant			next;
1661.1Sperseant		}
1671.1Sperseant
1681.1Sperseant		# Fsck reports errors in ALL CAPS
1691.1Sperseant		# But don't count hex numbers as "lowercase".
1701.1Sperseant		s/0x[0-9a-f]*//g;
1711.1Sperseant		if (m/[A-Z]/ && ! m/[a-z]/) {
1721.1Sperseant			$error = 1;
1731.2Sperseant			$errsn = $k;
1741.2Sperseant			$errstr = "1 $k 0x$a";
1751.1Sperseant			last;
1761.1Sperseant		}
1771.1Sperseant	}
1781.1Sperseant	close(FSCK);
1791.1Sperseant	last if $error;
1801.2Sperseant	$lastgood = $k;	# record last good serial number
1811.2Sperseant}
1821.2Sperseant$errstr = "0 $lastgood 0x$a" unless $errstr;
1831.2Sperseant
1841.2Sperseantif ($error == 0) {
1851.2Sperseant	print "Copying this good image to $gfile\n";
1861.2Sperseant	system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1");
1871.2Sperseant	print "$errstr\n";
1881.2Sperseant	exit 0;
1891.2Sperseant}
1901.2Sperseant
1911.2Sperseant#
1921.2Sperseant# If we found an error, try to find which blocks of the Ifile inode changed
1931.2Sperseant# between the last good checkpoint and this checkpoint; and which blocks
1941.2Sperseant# *should* have changed.  This means (1) which segments were written; and
1951.2Sperseant# (2) which inodes were written.  The 0 block of the Ifile should always
1961.2Sperseant# have changed since lfs_avail is always in flux.
1971.2Sperseant#
1981.2Sperseant
1991.2Sperseant$cmd = "dumplfs";
2001.2Sperseant$oseg = -1;
2011.2Sperseant%iblk = ();
2021.2Sperseant%iblk_done = ();
2031.2Sperseant%why = ();
2041.2Sperseant$iblk{0} = 1;
2051.2Sperseantfor ($i = $lastgood + 1; $i <= $errsn; $i++) {
2061.2Sperseant	if ($oseg != $snloc{$i}) {
2071.2Sperseant		$oseg = 0 + $snloc{$i};
2081.2Sperseant		$cmd .= " -s$oseg";
2091.2Sperseant	}
2101.1Sperseant}
2111.2Sperseant$cmd .= " $rdev";
2121.1Sperseant
2131.2Sperseantopen(DUMPLFS, "$cmd |");
2141.2Sperseantwhile(<DUMPLFS>) {
2151.2Sperseant	if (m/ifpb *([0-9]*)/) {
2161.2Sperseant		$ifpb = $1;
2171.2Sperseant	}
2181.2Sperseant	if (m/sepb *([0-9]*)/) {
2191.2Sperseant		$sepb = $1;
2201.2Sperseant	}
2211.2Sperseant	if (m/cleansz *([0-9]*)/) {
2221.2Sperseant		$cleansz = $1;
2231.2Sperseant	}
2241.2Sperseant	if (m/segtabsz *([0-9]*)/) {
2251.2Sperseant		$segtabsz = $1;
2261.2Sperseant	}
2271.2Sperseant	last if m/SEGMENT/;
2281.2Sperseant}
2291.2Sperseantwhile(<DUMPLFS>) {
2301.2Sperseant	chomp;
2311.2Sperseant
2321.2Sperseant	# Skip over partial segments outside our range of interest
2331.2Sperseant	if (m/roll_id.*serial *([0-9]*)/) {
2341.2Sperseant		$serno = $1;
2351.2Sperseant		if ($serno <= $lastgood || $serno > $errsn) {
2361.2Sperseant			# Skip the rest of this partial segment
2371.2Sperseant			while(<DUMPLFS>) {
2381.2Sperseant				last if m/Segment Summary/ || m/SEGMENT/;
2391.2Sperseant			}
2401.2Sperseant			next;
2411.2Sperseant		}
2421.2Sperseant	}
2431.2Sperseant
2441.2Sperseant	# Look for inodes
2451.2Sperseant	if (m/Inode addresses/) {
2461.2Sperseant		s/^[^{]*{/ /o;
2471.2Sperseant		s/}[^{]*$/ /o;
2481.2Sperseant		s/}[^{]*{/,/og;
2491.2Sperseant		s/v[0-9]*//og;
2501.2Sperseant		@ilist = split(',');
2511.2Sperseant		foreach $i (@ilist) {
2521.2Sperseant			$i =~ s/ *//og;
2531.2Sperseant			next if $i == 1;
2541.2Sperseant			$iaddr = $cleansz + $segtabsz + int ($i / $ifpb);
2551.2Sperseant			$iblk{$iaddr} = 1;
2561.2Sperseant			$why{$iaddr} .= " $i";
2571.2Sperseant		}
2581.2Sperseant	}
2591.2Sperseant
2601.2Sperseant	# Look for Ifile blocks actually written
2611.2Sperseant	if (m/FINFO for inode: ([0-9]*) version/) {
2621.2Sperseant		$i = $1;
2631.2Sperseant		$inoblkmode = ($i == 1);
2641.2Sperseant	}
2651.2Sperseant	if ($inoblkmode && m/^[-\t 0-9]*$/) {
2661.2Sperseant		s/\t/ /og;
2671.2Sperseant		s/^ *//o;
2681.2Sperseant		s/ *$//o;
2691.2Sperseant		@bn = split(' ');
2701.2Sperseant		foreach $b (@bn) {
2711.2Sperseant			$iblk_done{$b} = 1;
2721.2Sperseant		}
2731.2Sperseant	}
2741.2Sperseant}
2751.2Sperseantclose(DUMPLFS);
2761.2Sperseant
2771.2Sperseant# Report found and missing Ifile blocks
2781.2Sperseantprint "Ifile blocks found:";
2791.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) {
2801.2Sperseant	if ($iblk_done{$b} == 1) {
2811.2Sperseant		print " $b";
2821.2Sperseant	}
2831.2Sperseant}
2841.2Sperseantprint "\n";
2851.2Sperseant
2861.2Sperseantprint "Ifile blocks missing:";
2871.2Sperseantforeach $b (sort { $a <=> $b } keys %iblk) {
2881.2Sperseant	if ($iblk_done{$b} == 0) {
2891.2Sperseant		$why{$b} =~ s/^ *//o;
2901.2Sperseant		print " $b ($why{$b})";
2911.2Sperseant	}
2921.1Sperseant}
2931.2Sperseantprint "\n";
2941.2Sperseant
2951.2Sperseantprint "$errstr\n";
2961.2Sperseantexit 0;
297