#!/bin/sh # # mapsectorsh automatically generated from # mapsector # lib/00_version.sh # lib/00_utilities.sh # lib/10_map_raid.sh # lib/10_scan_mountpoint.sh # lib/50_map_crypt.sh # lib/50_map_lvm.sh # lib/50_map_partition.sh # lib/50_scan_extfs.sh # lib/50_scan_reiserfs.sh # # mapsector -- Map sector number to file name(s) # # Copyright (C) 2009,2010 Stefan Bund # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ########################################################################### mappers="" scanners="" version="unknown" name="`basename "$0"`" unset LANG register_mapper() { mappers="${mappers}${mappers:+ }$1" } register_scanner() { scanners="${scanners}${scanners:+ }$1" } load() { local X local lib X="`dirname "$0"`" if [ "`basename "$X"`" == "bin" ]; then # `" libdir="`dirname "$X"`/share/$name" #`" fi if [ -z "$libdir" -o ! -d "$libdir" ]; then libdir="$X/lib" fi if [ ! -d "$libdir" ]; then echo "! Library directory not found" 1>&2 exit 1 fi for lib in `ls "$libdir"/[0-9][0-9]_*.sh | sort`; do source $lib done } # Map device/sector through blockdevice mappings (e.g. lvm) map() { local found found=1 while [ -n "$found" ]; do found="" for name in $mappers; do if detect_$name; then found=1 if do_$name; then return fi break fi done done } scan() { for name in $scanners; do if detect_$name; then if do_$name; then return fi fi done } ########################################################################### #### Load library modules ################################################################ #### lib/00_version.sh version='v0.0.0-17-ga94c6f0' #### lib/00_utilities.sh deviceid() { ls -lL "$1" 2>/dev/null | awk -F '[ ,]+' '{print "(" $5 ", " $6 ")"}' } dmcryptmap() { devnums="`deviceid $1`" dmsetup ls --target crypt | awk '{print $1}' | while read dmdev; do if dmsetup deps $dmdev | grep -qF "$devnums"; then echo "/dev/mapper/$dmdev" fi done } #### lib/10_map_raid.sh detect_raid() { which mdadm >/dev/null 2>&1 && mdadm -Q $device | grep -qF -- "--examine" } describe_raid() { echo " RAID level 1" } do_raid() { local mddevice local mdlevel echo "# $device: RAID detected" 1>&2 mddevice="`mdadm -Q $device | sed -ne 's/.*\(raid[0-9] \/dev\/[^.]*\).*/\1/' -eT -ep`" if [ -z "$mddevice" ]; then echo "! raid master device for raid componentn device $device not found" 1>&2 exit 1 fi mdlevel="${mddevice% *}" mddevice="${mddevice#* }" echo "device $mddevice md" echo "raidlevel $mdlevel" echo "sector $sector" device="$mddevice" return 1 } register_mapper "raid" #### lib/10_scan_mountpoint.sh detect_mountpoint() { true } describe_mountpoint() { true } do_mountpoint() { local devnums devnums="`deviceid $device`" while read dev dir opts; do case "$dev" in *:*) ;; *) if [ "$devnums" == "`deviceid $dev`" ]; then echo "mountpoint $dir" break fi ;; esac done < /proc/mounts return 1 } register_scanner "mountpoint" #### lib/50_map_crypt.sh detect_crypt() { which cryptsetup >/dev/null 2>&1 && [ -n "`dmcryptmap $device`" ] } describe_crypt() { echo " cryptsetup (luks and dmcrypt)" } do_crypt() { # Step 2: Find the crypted volume defined for this partition local offset local devnums local cryptdev local cryptsector local type echo "# $device: cryptsetup detected" 1>&2 offset="`cryptsetup luksDump $device 2>/dev/null | awk '/^Payload offset/{print $3}'`" if [ -z "$offset" ]; then # Plain dmcrypt offset=0 type=dmcrypt else type=luks fi cryptdev="`dmcryptmap $device`" if [ -z "$cryptdev" ]; then echo "! Failed to find decrypted mapper device for $device" exit 1; fi cryptsector="`dc -e "$sector $offset - p"`" #`" echo "offset $offset" echo "device $cryptdev crypt" echo "type $type" echo "sector $cryptsector" device="$cryptdev" sector="$cryptsector" return 1 } register_mapper "crypt" #### lib/50_map_lvm.sh detect_lvm() { which pvdisplay >/dev/null 2>&1 && pvdisplay $device >/dev/null 2>&1 } describe_lvm() { echo " LVM (linear allocation scheme)" } do_lvm() { local pvname local pesize local vgname local pestart local penum local subsector local lestart local firstpe local lenum local fsdev local fssector echo "# $device: LVM detected" 1>&2 # Step 3: Get pysical extent number pvname="`pvdisplay -c $device 2>/dev/null | awk -F: '{print $1,$2,$8}' | sed -e 's/^ *//'`" if [ -z "$pvname" ]; then echo "! $device is not a physical volume" 1>&2 exit 1 fi pesize="${pvname##* }" vgname="${pvname% *}" pvname="${vgname% *}" vgname="${vgname#* }" pesize="`dc -e "$pesize 2 * p"`" #`" pestart="`pvs --unit s -ope_start $device 2>/dev/null | sed -n -e 's/ *//g' -e 's/S.*$//' -e '$p'`" penum="`dc -e "$sector $pestart - $pesize ~ n [ ] n p"`" #`" subsector="${penum% *}" penum="${penum#* }" echo "device $pvname pv" echo "offset $pestart" echo "pesize $pesize" echo "extent $penum" echo "subsector $subsector" echo "group $vgname" # Step 4: Find associated logical volume lestart="$(vgdisplay -v $vgname 2>/dev/null | awk '/LV Name/{print $3}' | while read lvname; do \ lvdisplay -m $lvname 2>/dev/null \ | awk -v RS="\n *\n( --- Segments ---\n)?" \ -F"[ \t\n:]+" \ -v pvname="$pvname" \ -v penum="$penum" \ -v lvname="$lvname" \ '$0 ~ "Physical volume[ \t]+" pvname && $14<=penum && $16>=penum{print lvname,$4,$14}'; \ done)" if [ -z "$lestart" ]; then echo "# pysical extent $penum of $pvname is not mapped in any logical volume" 1>&2 exit 0 fi lvname="${lestart%% *}" lestart="${lestart#* }" firstpe="${lestart#* }" lestart="${lestart% *}" lenum="`dc -e "$penum $firstpe - $lestart + p"`" #`" echo "device $lvname lv" echo "extent $lenum" fsdev="$lvname" fssector="`dc -e "$lenum $pesize * $subsector + p"`" #`" echo "sector $fssector" device="$fsdev" sector="$fssector" return 1 } register_mapper "lvm" #### lib/50_map_partition.sh detect_partition() { [ -z "`fdisk -l $device 2>&1 >/dev/null`" ] } describe_partition() { echo " partition tables" } do_partition() { # Step 1: Find partition to which this sector belongs local partdev local partstart local partsector echo "# $device: partition table detected" 1>&2 partdev="`fdisk -ul $device | awk -v sector="$sector" -F '[ *]+' '/^\// && !/Extended$/ && $2<=sector && $3>=sector {print $1,$2}'`" #`" if [ -z "$partdev" ]; then echo "# sector $sector is not part of any partition on $device" 1>&2 exit 0 fi partstart="${partdev#* }" partdev="${partdev% *}" partsector="`dc -e "$sector $partstart - p"`" # `" echo "offset $partstart" echo "device $partdev partition" echo "sector $partsector" device="$partdev" sector="$partsector" return 1 } register_mapper "partition" #### lib/50_scan_extfs.sh detect_extfs() { which tune2fs >/dev/null 2>&1 && tune2fs -l $device >/dev/null 2>&1 } describe_extfs() { echo " ext2-4" } do_extfs() { local fsblocksize local fsblock local fssubsector local fstype local inode echo "$device: ext2-4 filesystem detected" 1>&2 fstype="`file -s $device | sed -e 's/.*\(ext[0-9]\).*/\1/'`" echo "fstype $fstype" # Step 6: Get filesystem blocksize and convert sector number to filesystem block number fsblocksize="`tune2fs -l $device | awk '/Block size/{print $3/512}'`" if [ -z "$fsblocksize" ]; then echo "! $device is not ext2/ext3" 1>&2 exit 1 fi fsblock="`dc -e "$sector $fsblocksize ~ n [ ] n p"`" #`" fssubsector="${fsblock% *}" fsblock="${fsblock#* }" echo "blocksize $fsblocksize" echo "block $fsblock" echo "subsector $fssubsector" # Step 7: Check, whether block is in use if echo "testb $fsblock" | debugfs $device 2>/dev/null | grep -qF "not in use"; then echo "blockstate free" exit 0 fi echo "blockstate used" # Step 8: Find inode, to which the block belongs inode="`echo "icheck $fsblock" | debugfs $device 2>/dev/null | awk 'FNR>1{print $2}'`" #`" if [ -z "$inode" ]; then echo "blocktype meta?" exit 0 fi echo "inode $inode" # Step 9: Find file name(s) referencing the inode ( namefound="$(\ echo "ncheck $inode" \ | debugfs $device 2>/dev/null \ | sed -e '1d' -e 's/^[0-9]*[ ]*//' -e 's/^\/\//\//' \ | while read name; do \ if [ -z "$firstname" ]; then \ echo "blocktype data" 1>&3; \ echo "1"; \ firstname=1; \ fi; \ echo "name $name" 1>&3; \ done \ )" if [ -z "$namefound" ]; then echo "blocktype journal?" fi ) 3>&1 return 0 } register_scanner "extfs" #### lib/50_scan_reiserfs.sh detect_reiserfs() { which debugreiserfs >/dev/null 2>&1 && debugreiserfs $device >/dev/null 2>&1 } describe_reiserfs() { echo " reiserfs" } do_reiserfs() { local blocksize local block local subsector echo "# $device: reiserfs filesystem detected" 1>&2 echo "fstype reiserfs" # Step 6: Get filesystem blocksize and convert sector number to filesystem block number blocksize="`debugreiserfs $device 2>/dev/null | awk '/^Blocksize:/{print $2/512}'`" if [ -z "$blocksize" ]; then echo "! $device is not reiserfs" 1>&2 exit 1 fi block="`dc -e "$sector $blocksize ~ n [ ] n p"`" #`" subsector="${block% *}" block="${block#* }" echo "blocksize $blocksize" echo "block $block" echo "subsector $subsector" # Step 7: Check, whether block is in use if debugreiserfs -1 $block $device 2>&1 >/dev/null | grep -qF "free in ondisk bitmap"; then echo "blockstate free" exit 0 fi echo "blockstate used" # Use debugreiserfs -1 to check the block type. This however only works if the block is readable. type="`debugreiserfs -1 $block $device 2>/dev/null | sed -e '/^=*$/d' | head -1`" case "$type" in "Looks like unformatted") type="data" ;; "Reiserfs super block"*) type="superblock" ;; "LEAF NODE"*) type="meta" ;; "INTERNAL NODE"*) type="meta" ;; "Desc block"*) type="journal" ;; *) type="" ;; esac if [ -n "$type" ]; then echo "blocktype $type" fi # Step 8: Find object id to which this block belongs # Step 9: Find file name(s) referencing this object id # Currently we only look for $block in indirect blocks. python - $device $block </dev/null" % device) def parse_leafnode(): global fp l = fp.readline() #sys.stderr.write("> leafnode(1): %s\n" % repr(l)) if not l.startswith("LEAF NODE") : return for i in range(4) : fp.readline() while True: l = fp.readline() #sys.stderr.write("> leafnode(2): %s\n" % repr(l)) parts = l.split("|") if len(parts)<2 : return parts = parts[2].split() if len(parts)<4 : return obid=(int(parts[0]),int(parts[1])) if parts[3] == "DIR" : parse_dir(obid) elif parts[3] == "IND" : parse_indirect(obid) else: while True: l = fp.readline().strip() #sys.stderr.write("> leafnode(3): %s\n" % repr(l)) if l == 79*"-" or l == 67*"=" : break def parse_dir(obid): global fp global dirtree fp.readline() while True: try: l = fp.readline().rstrip() #sys.stderr.write("> dir: %s\n" % repr(l)) if l == 79*"-" or l == 67*"=": return if l.endswith("not set"): continue i = l.index("\"(") name = l[6:6+int(l[i+2:i+5])] if name == "." or name == ".." : continue i = l.index("[",len(name)+12) entryid = tuple(map(int,l[i+1:l.index("]",i+1)].split())) entry = dirtree.get(entryid) if entry is None : entry = dirtree[entryid] = set() entry.add((name,obid)) except ValueError: pass def parse_indirect(obid): global fp global blocknr global blockid fp.readline() for pointer in fp.readline().strip()[1:-1].split(): i = pointer.find("(") if i == -1: blkmin = blkmax = int(pointer) else: blkmin = int(pointer[:i]) blkmax = blkmin+int(pointer[i+1:-1])-1 if blocknr >= blkmin and blocknr <= blkmax: blockid = obid fp.readline() def scan_path(id): global dirtree entry = dirtree.get(id) #sys.stderr.write("> scan_path: %s->%s\n" % (id, entry)) if entry is None: return [ '/' ] else: rv = [] for name, parent in entry: for path in scan_path(parent): rv.append(os.path.join(path,name)) return rv def parse(): global fp while True: while True: l = fp.readline() #sys.stderr.write("> parse: %s\n" % repr(l)) if not(l) : return if l.strip() == 67*"=" : break parse_leafnode() parse() if blockid is None: sys.exit(0) sys.stdout.write("objectid %d %d\n" % blockid) for path in scan_path(blockid): if path != '/': sys.stdout.write("name %s\n" % path) EOF return 0 } register_scanner "reiserfs" ################################################################ #### Parse command line arguments X=`getopt -o "hV" --long "help,noscan,version" -n "$name" -- "$@"` if [ $? != 0 ]; then exit 1; fi eval set -- "$X" noscan="" help="" while true; do case "$1" in -h|--help) help="0"; shift ;; --noscan) noscan="0"; shift ;; -V|--version) echo "$name $version"; exit 0 ;; --) shift; break ;; *) echo "! internal error"; exit 1 ;; esac done if [ -n "$help" -o -z "$2" ]; then cat < " mapsector -- Map sector number to file name(s) Given a device and a sector number, mapsector will try to find the file name(s) mapping to this sector. It will try to gather as much information about the given sector as possible. mapsector currently has support for the following mapping schemes: EOF for mapper in $mappers; do describe_$mapper done cat <