icheck.sh
#!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # This script is provided as is. No liability is accepted for it's # use. # # It makes no attempt to handle any log when enabled. # # Chris.Gerhard@sun.com # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Download the latest version of this script from: # # http://blogs.sun.com/roller/resources/chrisg/icheck.sh # #pragma ident "%Z%%M% %I% %E% SMI" # # list_inodes can use fsdb or find to do the job. # # find obviously requires that the file system is mounted but has the # advantage that it is more reliable on some systems as fsdb can be less # robust than you would want. # function list_inodes { if [[ ${USE_FIND:-0} == 0 ]] then print ":ls -l -R /" | fsdb -F ufs $1 |\ nawk '/^i#:/ && (inodes[$2] == 0) { printf("0x%s\n", $2); inodes[$2] = 1; next; }' else find $FS -xdev -ls |\ nawk '(inodes[$1] == 0) { printf("0x%x\n", $1); inodes[$1] = 1; next; }' fi } function isdebug { [[ ${debug} -ge $1 ]] return $? } function debug { if isdebug $1 then shift echo DEBUG $@ fi } # # fsdb does not expect to be driven by a script and buffers lots of output. # To workaround this push down plenty of commands so that the data that # is actually interesting gets flushed back to the script. # # Expect would be another workaround for this. # function flush { typeset -i i=0 while ((i < $flush_count )) do print -p ':base=0x10' || exit let i=i+1 done } # # Output a marker of some sort so you can tell when all the flush data # has ended. # function end_flush { print -p '0xfeedbede=X' || exit } function eat_flush { typeset one two three four while read -p one two three four do if [[ "$one" == "fffffffffeedbede" || $three == "fffffffffeedbede" ]] then break fi done } # # so that it can tell when the output of a command has completed # it writes down this fsdb command and then continues processing # until it finds the result in the output stream. # function end_command { print -p '0xfefeadbc=X' || exit } # # The number we pushed down gets sign extended. Hence all the extra "f"s. # function is_end { if [[ ${1:-1} = "fffffffffefeadbc" ]] then return 0 else : echo $1 return 1 fi } # # do_indirect0 # args: # $1 = inode # $2 = offset in file for this block of indrects # $3 = The block of indirects # # If the block offset is more then 7FFFFF then fsdb is going to # incorrectly display the offsets as they will wrap. # # This is bug 4658830. # # Since this script is also living in a 32 bit world this makes life # a little bit harder. # # Hence we do the calulation ((offset * $Fsize) & $Wmask) ) which # gets the low 20 bits of the real calculation. If the number # of words per block will not fit into Wmask there will be trouble. # # function do_indirect0 { debug 1 ib0 enter typeset buf typeset inode=$1 typeset file_off=$2 typeset -i16 offset=16#${3#16#} typeset -i16 start_offset=$(( ($offset * $Fsize) & $Wmask )) debug 2 "ib0 inode $inode $offset" flush end_flush print -p "0x${offset#16#}:block,${WordsPerBlock#16#}/X"|| exit end_command flush eat_flush debug 2 ib0 read -p buf while read -p buf do set - $buf [[ $# == 0 ]] && continue debug 1 ib0 $@ if [[ $1 == $RAW_DEVICE ]] then shift 2 fi [[ $# == 0 ]] && continue if is_end $1 || is_end $3 then break; fi debug 1 ib0 loop $buf if [[ ${1%:} != ${1} ]] then typeset -i16 tmp tmp=$(( 16#${1%:} & $Wmask )) tmp=$(($file_off + (( $tmp - ${start_offset} ) / $Wsize) )) debug 1 XX $file_off $offset $tmp ${start_offset} $(( $tmp - ${start_offset} )) shift $do_block $inode ${tmp#16#} ${1} let tmp=$tmp+1 shift $do_block $inode ${tmp#16#} ${1} let tmp=$tmp+1 shift $do_block $inode ${tmp#16#} ${1} let tmp=$tmp+1 shift $do_block $inode ${tmp#16#} ${1} else : debug ${1%:} ${offset} fi done } function do_indirect1 { debug 1 ib1 enter typeset buf typeset inode=$1 typeset file_off=$2 typeset -i16 offset=16#${3#16#} typeset -i16 start_offset=$(( ($offset * $Fsize) & $Wmask )) debug 3 spam3 $3 $offset typeset -i i=0; debug 2 "ib1 inode $inode $offset" flush end_flush print -p "0x${offset#16#}:block,${WordsPerBlock#16#}/X" || exit end_command flush while read -p buf do set - ${buf} if [[ $1 == $RAW_DEVICE ]] then shift 2 fi if is_end $1 || is_end $3 then break; fi debug 1 ib1 loop $buf if [[ ${1%:} != ${1} ]] then typeset -i16 tmp tmp=$(( 16#${1%:} & $Wmask )) tmp=$(( ($tmp - ${start_offset} ) / $Wsize)) debug 1 ib1 $offset $tmp shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 let tmp=tmp+1 shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 let tmp=tmp+1 shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 let tmp=tmp+1 debug 3 ib1 X $i shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 else : debug ${1%:} ${offset} fi done debug 2 ib1 loop done typeset -i j=0 debug 1 "in ib1 $i" while (( j < i )) do typeset -i16 tmp typeset -i16 tmp2 eval "set - \${blocks${j}}" tmp2=16#$2 tmp=$(( $file_off + ( $WordsPerBlock * $1 ) )) debug 3 ib1 spam $j $tmp $tmp2 if (( $tmp2 != 0 )) then debug 1 do_indirect0 $inode ${tmp} ${tmp2} $1 do_indirect0 $inode ${tmp} ${tmp2} fi let j=j+1 done } function do_indirect2 { debug 1 ib2 enter typeset buf typeset inode=$1 typeset file_off=$2 typeset -i16 offset=16#${3#16#} typeset -i16 start_offset=$(( ($offset * $Fsize) & $Wmask )) typeset -i i=0; set -f debug 2 "ib2 inode $inode $offset" flush end_flush print -p "0x${offset#16#}:block,${WordsPerBlock#16#}/X" || exit end_command flush debug 3 ib2 read -p buf while read -p buf do set - $buf debug 3 ib2 $@ if [[ $1 = $RAW_DEVICE ]] then shift 2 fi if is_end $1 || is_end $3 then break; fi debug 1 ib2 loop $buf if [[ ${1%:} != ${1} ]] then typeset -i16 tmp tmp=$(( 16#${1%:} & $Wmask )) tmp=$(( ($tmp - ${start_offset} ) / $Wsize)) shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 let tmp=tmp+1 shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 let tmp=tmp+1 shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 shift [[ $# == 0 ]] && continue eval "typeset blocks$i" eval "blocks$i=\"$tmp $1\"" let i=i+1 else : debug ${1%:} ${offset} fi done typeset -i j=0 while (( j < i )) do typeset -i16 tmp typeset -i16 tmp2 eval "set - \${blocks${j}}" tmp2=16#$2 tmp=$(( $file_off + ( $WordsPerBlock * $WordsPerBlock * $1 ) )) debug 1 spam $j $tmp $tmp2 if (( $tmp2 != 0 )) then debug 1 do_indirect1 $inode ${tmp} ${tmp2} $1 do_indirect1 $inode ${tmp} $tmp2 fi let j=j+1 done } # # This is the original do_block which is easier to read than the # second version. However the second version runs in about 2/3s the # time and since this is the most called routine in this script, being # called for every block that is checked speed is everything. # # Both routines should produce the same results. # function do_block { typeset inode=$1 typeset fileblk=$2 typeset blk=$3 if (( 16#$blk != 0 )) then typeset -i b=$(( 0x${blk} & $Bmask )) typeset x eval "x=\$BLOCKS_$b" if [[ "${x}" != "" ]] then print inode $inode: file block: 16#${fileblk} device block: 16#${blk} fi fi } function do_block { if (( 16#$3 != 0 )) then eval "[[ \${#BLOCKS_$(( 0x$3 & $Bmask ))} != 0 ]]" && \ print inode $1: file block: 16#${2} device block: 16#${3} fi } # # Routines that will copy a file. I just used this for testing purposes # figuring that if I can copy a file like this and the source and target # compare correctly they I have probably got this right! # # There is no attempt to cope with fragments. So the last block of the # target file is padded to the block size. # function do_dd { if (( ${DD_iseek} / $FragsPerBlock == 0 )) then dd conv=notrunc bs=${Bsize} \ iseek=$(( ${DD_iseek} / $FragsPerBlock )) \ oseek=${DD_oseek} \ of=$DD_ofile if=${RAW_DEVICE} \ count=$DD_count 2> /dev/null || exit else dd conv=notrunc bs=${Fsize} \ iseek=$(( ${DD_iseek})) \ oseek=${DD_oseek} \ of=$DD_ofile if=${RAW_DEVICE} \ count=$(( $DD_count * $FragsPerBlock )) 2> /dev/null || exit fi DD_count=0 } function do_block_cp { debug 1 dd_start $@ if (( 16#$3 != 0 )) then if (( 16#$2 != 0 )) && (( 16#$2 <= ${DD_oseek:-0} )) then echo ARg $@ fi if (( ${DD_count:-0} != 0 )) && \ (( 16#$2 == (${DD_oseek:-0} + ${DD_count:-0} ) )) && \ (( 16#$3 == (${DD_iseek:-0} + \ (${DD_count:-0} * $FragsPerBlock)) )) && \ (( 16#$2 != ${LastBlock:-0} )) then let DD_count=DD_count+1 elif (( ${DD_count:-0} == 0 )) then DD_count=1 DD_oseek=$(( 16#$2 )) DD_iseek=$(( 16#$3 )) else $do_dd DD_count=1 DD_oseek=$(( 16#$2 )) DD_iseek=$(( 16#$3 )) fi elif (( ${DD_count:-0} != 0)) then $do_dd fi debug 1 dd_end $@ } # # It would be silly not to allow the reporting of the file layout. # function do_extent { echo file off ${DD_oseek} dev off $(( ${DD_iseek} / $FragsPerBlock )) len $DD_count DD_count=0 } # # Note we don't trust the inode number returned by the :inode command. # this is because of bug: # # 6433317 fsdb_ufs's :inode command can display the incorrect inode number. # # function do_inode { typeset one two three four five six seven eight nine typeset buf typeset inode typeset inode_in=${1#0x} typeset ib0 ib1 ib2 typeset si end_flush print -p "0x${inode_in}:inode?i" || exit end_command flush eat_flush debug 1 do_inode read -p buf while read -p buf do set - $buf if is_end $1 || is_end $3 then return fi if [[ "$1" == "inode" && $2 == "not" && "$3" == "allocated" ]] then print "WARNING: $inode_in not allocated" >&2 break fi if [[ "$1" == "$RAW_DEVICE" ]] then shift 2 fi if [[ "$1" == 'i#:' ]] then inode=$2 break; fi if [[ "$1" == $RAW_DEVICE && $three == 'i#:' ]] then inode=$4 break fi done if [[ "$inode_in" != $inode ]] then print "WARNING: wrong inode. Asked for $inode_in, got $inode" >&2 print $@ >&2 else debug 1 "Right inode. Asked for $inode_in, got $inode" fi while read -p one two three four five six seven eight nine do debug 1 in inode $one $two $three $four $five $six $seven $eight if is_end $one || is_end $three then break fi if [[ ${eight#si:} != ${eight} ]] then si=${nine:-0} fi if [[ ${one#db#} != ${one} ]] then typeset tmp=${one#db#} $do_block $inode_in ${tmp%:} ${two} fi if [[ ${three#db#} != ${three} ]] then typeset tmp=${three#db#} $do_block $inode_in ${tmp%:} ${four} fi if [[ ${five#db#} != ${five} ]] then typeset tmp=${five#db#} $do_block $inode_in ${tmp%:} ${six} fi if [[ ${seven#db#} != ${seven} ]] then typeset tmp=${seven#db#} $do_block $inode_in ${tmp%:} ${eight} fi if [[ ${one#ib#} != ${one} ]] then typeset tmp=${one#ib#} eval "ib${tmp%:}=$two" if [[ ${tmp%:} != 0 ]] then print expect indirect 0 got ${tmp%:} >&2 else debug 2 ib0 = $two fi if [[ ${three#ib#} != ${three} ]] then tmp=${three#ib#} eval "ib${tmp%:}=$four" if [[ ${tmp%:} != 1 ]] then print expect indirect 1 got ${tmp%:} >&2 else debug 2 ib1 = $four fi if [[ ${five#ib#} != ${five} ]] then tmp=${five#ib#} eval "ib${tmp%:}=$six" if [[ ${tmp%:} != 2 ]] then print expect indirect 2 got ${tmp%:} >&2 fi fi fi fi unset one two three four five six seven eight done if [[ ${SHADOW:-0} != 0 ]] then if [[ ${si:-0} == ${SHADOW:-0} ]] then echo Shadow inode ${SHADOW} associated with inode $inode_in fi return fi if [[ ${ib0:-0} != 0 ]] then debug 2 in inode doing ib0 $inode_in 16#c $ib0 do_indirect0 $inode_in 16#c $ib0 fi if [[ ${ib1:-0} != 0 ]] then debug 2 in inode doing ib1 $inode_in 16#$(( 16#c + $WordsPerBlock )) $ib1 typeset -i16 x=$(( 16#c + $WordsPerBlock )) do_indirect1 $inode_in $x $ib1 fi if [[ ${ib2:-0} != 0 ]] then typeset -i16 x=$(( 16#c + $WordsPerBlock + ($WordsPerBlock * $WordsPerBlock) )) do_indirect2 $inode_in $x $ib2 fi } function do_sz { typeset buf end_flush print -p "0x${1#0x}:inode;:sz" || exit end_command flush eat_flush while read -p buf do set - $buf if is_end $1 || is_end $3 then break fi if [[ $1 == $RAW_DEVICE ]] then shift 2 fi if [[ $1 != '?' ]] then DD_filesize=$1 fi done } function do_superblock { typeset buf end_flush print -p ":sb" || exit end_command flush eat_flush while read -p buf do set - $buf if is_end $1 || is_end $3 then break fi if [[ $1 == "bsize" ]] then debug 1 bsize $2 Bsize=$2 fi if [[ $1 == "fsize" ]] then debug 1 fsize $2 Fsize=$2 fi if [[ $3 == "shift" ]] then debug 1 shift $4 Fshift=$4 fi done } function usage { printf "USAGE: %s: [-D level ][ -i inode ][ -f directory][[ -s shadow inode ]|[ -c target_file ]|[ -x ]] -d device blocks...\n" $0 } # # End of functions # # main() # # RAW_DEVICE=/dev/zvol/rdsk/tank/rootfs set -f typeset -i Fsize typeset -i Bsize do_block=do_block while getopts f:d:D:i:c:xs: name do case $name in c) do_block=do_block_cp do_dd=do_dd DD_ofile=$OPTARG [[ -f $DD_ofile ]] && : > $DD_ofile ;; x) do_block=do_block_cp do_dd=do_extent ;; f) USE_FIND=1 FS=$OPTARG;; d) RAW_DEVICE=$OPTARG ;; i) INODE=$OPTARG ;; s) SHADOW=$OPTARG ;; D) fsdb_debug=$OPTARG ;; ?) usage exit 2;; esac done shift $(($OPTIND - 1)) if [[ ! -c ${RAW_DEVICE:-""} || ( ${#INODE} == 0 && $# == 0 && ${SHADOW:-0} == 0) ]] then usage exit 2 fi debug=${fsdb_debug:-0} typeset -i Wsize=4 # # The number of commands that are sure to flush the output buffer of # fsdb. # flush_count=200 # # The mask to cope with wrapping. See the comment above do_indirect0 # typeset -i16 Wmask=$((16#fffff)) # # Now start the fsdb co-process # fsdb -F ufs $RAW_DEVICE |& print -p ':base=0x10' || exit flush do_superblock # # remove any fragments from this. # typeset -i Bmask=$(( (0xffffffff >> $Fshift) << $Fshift )) for i in $@ do typeset -i16 y=$((0x${i#0x} )) typeset -i x=$((y & Bmask )) if (( x != y )) then y=x echo $i is a fragment address. Rounding to block $y fi debug 1 BLOCKS_$x=$i eval "BLOCKS_$x=$i" unset x unset y done typeset -i16 WordsPerBlock=$(( $Bsize / $Wsize )) typeset -i16 FragsPerBlock=$(( $Bsize / $Fsize )) debug 1 We are off if [[ ${#INODE} != 0 ]] then do_inode $INODE print -p ':quit' if (( ${DD_count:-0} != 0 )) then $do_dd fi exit fi isdebug 2 && typeset -ft do_indirect0 isdebug 2 && typeset -ft do_indirect1 isdebug 2 && typeset -ft do_indirect2 isdebug 2 && typeset -ft do_inode debug 1 "go" if (( ${debug:-0} == 0 )) # # Get rid of the debug functions for extra speed # then function debug { : } fi list_inodes $RAW_DEVICE | while read inode do debug 1 Doing $inode do_inode $inode done print -p ':quit' || exit
Trackbacks & Pingbacks