#!/bin/sh # # $NetBSD: fixsb,v 1.12 2004/12/30 09:32:13 dsainty Exp $ # # PROVIDE: fixsb # REQUIRE: localswap # BEFORE: fsck $_rc_subr_loaded . /etc/rc.subr name="fixsb" rcvar=$name start_cmd="fsck_start" stop_cmd=":" # This rc.d script attempts to correct problems with ffs1 filesystems # which may have been introduced by booting a netbsd-current kernel # from between April of 2003 and January 2004. For more information # see # This script was developed as a response to NetBSD pr install/25138 # Additional prs regarding the original issue include: # bin/17910 kern/21283 kern/21404 port-macppc/23925 port-macppc/23926 # fstab=/etc/fstab #verbose=1 verbose() { if [ -n "${verbose}" ]; then echo "$@" 1>&2 fi } # This reads a field from the ffs superblock # at the specified offset and length in bytes # from the start of the superblock readsbfield() { # The first dd command reads in the superblock using block aligned i/o # so that it works on a raw character device. The second dd extracts # the exact field from the superblock that we wish to read. (dd if="$1" bs=8192 count=1 skip=1 | dd bs=1 skip="$2" count="$3" | cat -v) 2> /dev/null } # This shell function extracts the `ffs superblock' of the file # provided as its argument and tests for the following condition: # ((fs_magic == FS_UFS1_MAGIC) || fs_magic == FS_UFS1_MAGIC_SWAPPED) && # (fs_sbsize == fs_maxbsize) && !(fs_old_flags & FS_FLAGS_UPDATED) # # return status is based on status of last filesystem checked: # 0 for botched superblock # 1 for filesystem does not appear to be ffs1 filesystem # 3 for ok fslevel 3 filesystem # 4 for ok fslevel 4 filesystem # # dbj@NetBSD.org 2004-04-12T18:15:06-0400 check_part() { verbose -n "Checking $1 ... " # The following are 'cat -v' representations of the ffs1 magic number: fsmagicn="^@^A^YT" # 0x00011954 FS_UFS1_MAGIC fsmagics="T^Y^A^@" # 0x54190100 FS_UFS1_MAGIC_SWAPPED # First we extract the superblock magic number field. # We use cat -v to avoid having binary data in shell strings. magic="$(readsbfield "$1" 1372 4)" # Then we check if the magic number is valid (swapped or unswapped): if [ "${magic}" != "${fsmagicn}" -a "${magic}" != "${fsmagics}" ]; then verbose "does not appear to be an ffs1 filesystem." return 1 fi # Then we read fs_old_flags fields from disk # And check the value of its high bit. oldflags="$(readsbfield "$1" 211 1)" case "${oldflags}" in # Since the shell variable is the cat -v output, the # high bit is indicated in the variable with the prefix M- M-*) verbose "file system looks ok at fslevel 4." return 4 ;; esac # Then we read fs_bsize, fs_maxbsize fields from the disk: bsize="$(readsbfield "$1" 48 4)" maxbsize="$(readsbfield "$1" 860 4)" # Compare the fs_bsize with fs_maxbsize to see if they are the same if [ "${bsize}" != "${maxbsize}" ]; then verbose "file system looks ok at fslevel 3." return 3 fi verbose "file system has botched superblock upgrade." return 0 } # This extracts raw ufs partitions to be fsck'ed from the file ${fstab} parse_fstab() { for l in 1 2; do cat "${fstab}" 2> /dev/null | while read d m t o b f err; do case "$d" in \#*) continue ;; /dev/*) d="/dev/r${d#/dev/}" ;; esac case "$t" in ffs|ufs) if [ "$f" = "$l" ]; then echo "$d" fi ;; esac done done } stop_boot() { # Terminate the process (which may include the parent /etc/rc) # if booting directly to multiuser mode. if [ "$autoboot" = "yes" ]; then kill -TERM $$ fi exit 1 } do_fsck() { # During fsck ignore SIGQUIT fsck_ffs "$@" case $? in 0) ;; 2) stop_boot ;; 4) echo "Rebooting..." reboot echo "Reboot failed; help!" stop_boot ;; 8) echo "Automatic file system check failed; help!" stop_boot ;; 12) echo "Boot interrupted." stop_boot ;; 130) stop_boot ;; *) echo "Unknown error; help!" stop_boot ;; esac } fsck_start() { if [ -e /fastboot ]; then echo "Fast boot: skipping disk checks." else # During fsck ignore SIGQUIT trap : 3 okfs=true echo -n "Checking for botched superblock upgrades:" for p in $(parse_fstab); do if check_part "$p"; then if $okfs; then echo okfs=false fi echo "Repairing partition $p" do_fsck -p -b 16 -c 4 "$p" do_fsck -p -c 3 "$p" fi done if $okfs; then echo " done." else echo "Superblock(s) updated successfully." fi fi } load_rc_config $name run_rc_command "$1"