#!/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
Like this:
Like Loading...
Trackbacks & Pingbacks