--- /dev/null
+detect_reiserfs()
+{
+ which debugreiserfs >/dev/null 2>&1 && debugreiserfs $device >/dev/null 2>&1
+}
+
+describe_reiserfs()
+{
+ echo " reiserfs"
+}
+
+scan_reiserfs()
+{
+ local blocksize
+ local block
+ local subsector
+
+ 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 <<EOF
+import sys
+import os
+import os.path
+
+device = sys.argv[1]
+blocknr = int(sys.argv[2])
+
+dirtree = {}
+blockid = None
+
+fp = os.popen("debugreiserfs -d %s 2>/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
+
+}
+
+register_scanner "reiserfs"
--- /dev/null
+#!/bin/sh
+
+###########################################################################
+
+mappers=""
+scanners=""
+
+register_mapper()
+{
+ mappers="${mappers}${mappers:+ }$1"
+}
+
+register_scanner()
+{
+ scanners="${scanners}${scanners:+ }$1"
+}
+
+load()
+{
+ for lib in `ls "$1"/[0-9][0-9]_*.sh | sort`; do
+ source $lib
+ done
+}
+
+# Map device/sector through blockdevice mappings (e.g. lvm)
+
+run()
+{
+ local found
+ foundm=1
+ while [ -n "$found" ]; do
+ found=""
+ for name in "$@"; do
+ if detect_$name; then
+ found=1
+ if do_$name; then
+ return
+ fi
+ break
+ fi
+ done
+ done
+}
+
+###########################################################################
+
+unset LANG
+
+#### Find library directory and load library files
+
+name="`basename "$0"`"
+X="`dirname "$0"`"
+if [ "`basename "$X"`" == "bin" ]; then # `"
+ libdir="`dirname "$X"`/lib/$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
+
+load "$libdir"
+
+#### Parse command line arguments
+
+X=`getopt -o "h" --long "help,noscan" -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 ;;
+ --) shift; break ;;
+ *) echo "! internal error"; exit 1 ;;
+ esac
+done
+
+if [ -n "$help" -o -z "$2" ]; then
+ cat <<EOF
+Usage: $name [-h|--help] [--noscan] <device> <sector>"
+
+mapsector -- Map sector numbers 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 <<EOF
+
+mapsector currently supports the following filesystems
+
+EOF
+ for scanner in $scanners; do
+ describe_$scanner
+ done
+cat <<EOF
+
+mapsector will try it's best to find an associated file name but
+depending on the filesystem state and the type of sector (e.g. if
+the sector is part of a filesystem metadata block) this may not be
+possible.
+
+For mapsector to work, the filesystem must be currently active
+(e.g. LVM must be running, crypted devices must have been set up). The
+filesystem must not necessarily be mounted (though if mounted,
+mapsector will give you the mountpoint).
+
+if '--noscan' is given, the possibly lengthy (!!) filesystem scan for
+filenames is skipped.
+EOF
+ exit "${help:-1}"
+fi
+
+device="$1"
+sector="$2"
+
+echo "device $device"
+echo "sector $sector"
+
+run $mappers
+if [ -z "$noscan" ]; then
+ run $scanners
+fi
+++ /dev/null
-#!/bin/sh
-
-###########################################################################
-
-# Utilities
-
-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
-}
-
-###########################################################################
-
-# Map device/sector through blockdevice mappings (e.g. lvm)
-
-map()
-{
- while true; do
- if detect_raid; then
- echo "# $device: Raid detected" 1>&2
- map_raid
- elif detect_partition; then
- echo "# $device: partition table detected" 1>&2
- map_partition
- elif detect_crypt; then
- echo "# $device: LUKS/cryptsetup detected" 1>&2
- map_crypt
- elif detect_lvm; then
- echo "# $device: LVM detected" 1>&2
- map_lvm
- else
- break
- fi
- done
-}
-
-########################################
-#### Partitions
-
-detect_partition()
-{
- [ -z "`fdisk -l $device 2>&1 >/dev/null`" ]
-}
-
-map_partition()
-{
- # Step 1: Find partition to which this sector belongs
-
- local partdev
- local partstart
- local partsector
-
- 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"
-}
-
-########################################
-#### LUKS / cryptsetup
-
-detect_crypt()
-{
- which cryptsetup >/dev/null 2>&1 && [ -n "`dmcryptmap $device`" ]
-}
-
-map_crypt()
-{
- # Step 2: Find the crypted volume defined for this partition
-
- local offset
- local devnums
- local cryptdev
- local cryptsector
- local type
-
- 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"
-}
-
-########################################
-#### LVM
-
-detect_lvm()
-{
- which pvdisplay >/dev/null 2>&1 && pvdisplay $device >/dev/null 2>&1
-}
-
-map_lvm()
-{
- local pvname
- local pesize
- local vgname
- local pestart
- local penum
- local subsector
- local lestart
- local firstpe
- local lenum
- local fsdev
- local fssector
-
- # 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"
-}
-
-########################################
-#### Raid-1
-
-detect_raid()
-{
- which mdadm >/dev/null 2>&1 && mdadm -Q $device | grep -qF -- "--examine"
-}
-
-map_raid()
-{
- local mddevice
- local mdlevel
-
- 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"
-}
-
-###########################################################################
-
-# Scan the final blockdevice for additional information.
-# Information includes mountpoint, filesystem type and filesystem type
-# specific information: filesysetm block, inode number and filename
-
-scan()
-{
- scan_mountpoint
- if detect_ext2fs; then
- echo "# $device: ext2/3/4 filesystem detected" 1>&2
- scan_ext2fs
- elif detect_reiserfs; then
- echo "# $device: reiserfs filesystem detected" 1>&2
- scan_reiserfs
- fi
-}
-
-########################################
-#### Mountpoint
-
-scan_mountpoint()
-{
- local devnums
- devnums="`deviceid $device`"
-
- # Step 5: Find filesystem mount point
-
- while read dev dir opts; do
- case "$dev" in
- *:*) ;;
- *)
- if [ "$devnums" == "`deviceid $dev`" ]; then
- echo "mountpoint $dir"
- break
- fi
- ;;
- esac
- done < /proc/mounts
-}
-
-########################################
-#### ext2/ext3
-
-detect_ext2fs()
-{
- which tune2fs >/dev/null 2>&1 && tune2fs -l $device >/dev/null 2>&1
-}
-
-scan_ext2fs()
-{
- local fsblocksize
- local fsblock
- local fssubsector
- local fstype
- local inode
-
- 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
-}
-
-########################################
-#### Reiser-FS
-
-detect_reiserfs()
-{
- which debugreiserfs >/dev/null 2>&1 && debugreiserfs $device >/dev/null 2>&1
-}
-
-scan_reiserfs()
-{
- local blocksize
- local block
- local subsector
-
- 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 <<EOF
-import sys
-import os
-import os.path
-
-device = sys.argv[1]
-blocknr = int(sys.argv[2])
-
-dirtree = {}
-blockid = None
-
-fp = os.popen("debugreiserfs -d %s 2>/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
-
-}
-
-###########################################################################
-
-unset LANG
-
-noscan=""
-if [ "$1" == "--noscan" ]; then
- noscan=1
- shift
-fi
-
-if [ -z "$2" ]; then
- cat <<EOF
-Usage: $0 [--noscan] <device> <sector>"
-
-mapsector -- Map sector numbers 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:
-
- partition table
- RAID-1
- cryptsetup (luks and plain dmcrypt)
- LVM in linear allocation mode
-
-mapsector currently supports the following filesystems
-
- ext2/3/4
- reiserfs
-
-mapsector will try it's best to find an associated file name but
-depending on the filesystem state and the type of sector (e.g. if
-the sector is part of a filesystem metadata block) this may not be
-possible.
-
-For mapsector to work, the filesystem must be currently active
-(e.g. LVM must be running, crypted devices must have been set up). The
-filesystem must not necessarily be mounted (though if mounted,
-mapsector will give you the mountpoint).
-
-if '--noscan' is given, the possibly lengthy (!!) filesystem scan for
-filenames is skipped.
-EOF
- exit 1
-fi
-
-device="$1"
-sector="$2"
-
-echo "device $device"
-echo "sector $sector"
-
-map
-if [ -z "$noscan" ]; then
- scan
-fi