3 ###########################################################################
9 ls -lL "$1" 2>/dev/null | awk -F '[ ,]+' '{print "(" $5 ", " $6 ")"}'
14 devnums="`deviceid $1`"
16 dmsetup ls --target crypt | awk '{print $1}' | while read dmdev; do
17 if dmsetup deps $dmdev | grep -qF "$devnums"; then
18 echo "/dev/mapper/$dmdev"
23 ###########################################################################
25 # Map device/sector through blockdevice mappings (e.g. lvm)
31 echo "# $device: Raid detected" 1>&2
33 elif detect_partition; then
34 echo "# $device: partition table detected" 1>&2
36 elif detect_crypt; then
37 echo "# $device: LUKS/cryptsetup detected" 1>&2
40 echo "# $device: LVM detected" 1>&2
48 ########################################
53 [ -z "`fdisk -l $device 2>&1 >/dev/null`" ]
58 # Step 1: Find partition to which this sector belongs
64 partdev="`fdisk -ul $device | awk -v sector="$sector" -F '[ *]+' '/^\// && !/Extended$/ && $2<=sector && $3>=sector {print $1,$2}'`" #`"
66 if [ -z "$partdev" ]; then
67 echo "# sector $sector is not part of any partition on $device" 1>&2
71 partstart="${partdev#* }"
72 partdev="${partdev% *}"
73 partsector="`dc -e "$sector $partstart - p"`" # `"
75 echo "offset $partstart"
76 echo "device $partdev partition"
77 echo "sector $partsector"
83 ########################################
84 #### LUKS / cryptsetup
88 which cryptsetup >/dev/null 2>&1 && [ -n "`dmcryptmap $device`" ]
93 # Step 2: Find the crypted volume defined for this partition
101 offset="`cryptsetup luksDump $device 2>/dev/null | awk '/^Payload offset/{print $3}'`"
103 if [ -z "$offset" ]; then
111 cryptdev="`dmcryptmap $device`"
113 if [ -z "$cryptdev" ]; then
114 echo "! Failed to find decrypted mapper device for $device"
118 cryptsector="`dc -e "$sector $offset - p"`" #`"
120 echo "offset $offset"
121 echo "device $cryptdev crypt"
123 echo "sector $cryptsector"
126 sector="$cryptsector"
129 ########################################
134 which pvdisplay >/dev/null 2>&1 && pvdisplay $device >/dev/null 2>&1
151 # Step 3: Get pysical extent number
153 pvname="`pvdisplay -c $device 2>/dev/null | awk -F: '{print $1,$2,$8}' | sed -e 's/^ *//'`"
155 if [ -z "$pvname" ]; then
156 echo "! $device is not a physical volume" 1>&2
160 pesize="${pvname##* }"
161 vgname="${pvname% *}"
162 pvname="${vgname% *}"
163 vgname="${vgname#* }"
164 pesize="`dc -e "$pesize 2 * p"`" #`"
165 pestart="`pvs --unit s -ope_start $device 2>/dev/null | sed -n -e 's/ *//g' -e 's/S.*$//' -e '$p'`"
166 penum="`dc -e "$sector $pestart - $pesize ~ n [ ] n p"`" #`"
167 subsector="${penum% *}"
170 echo "device $pvname pv"
171 echo "offset $pestart"
172 echo "pesize $pesize"
174 echo "subsector $subsector"
177 # Step 4: Find associated logical volume
179 lestart="$(vgdisplay -v $vgname 2>/dev/null | awk '/LV Name/{print $3}' | while read lvname; do \
180 lvdisplay -m $lvname 2>/dev/null \
181 | awk -v RS="\n *\n( --- Segments ---\n)?" \
183 -v pvname="$pvname" \
185 -v lvname="$lvname" \
186 '$0 ~ "Physical volume[ \t]+" pvname && $14<=penum && $16>=penum{print lvname,$4,$14}'; \
189 if [ -z "$lestart" ]; then
190 echo "# pysical extent $penum of $pvname is not mapped in any logical volume" 1>&2
194 lvname="${lestart%% *}"
195 lestart="${lestart#* }"
196 firstpe="${lestart#* }"
197 lestart="${lestart% *}"
198 lenum="`dc -e "$penum $firstpe - $lestart + p"`" #`"
200 echo "device $lvname lv"
204 fssector="`dc -e "$lenum $pesize * $subsector + p"`" #`"
206 echo "sector $fssector"
212 ########################################
217 which mdadm >/dev/null 2>&1 && mdadm -Q $device | grep -qF -- "--examine"
225 mddevice="`mdadm -Q $device | sed -ne 's/.*\(raid[0-9] \/dev\/[^.]*\).*/\1/' -eT -ep`"
227 if [ -z "$mddevice" ]; then
228 echo "! raid master device for raid componentn device $device not found" 1>&2
232 mdlevel="${mddevice% *}"
233 mddevice="${mddevice#* }"
235 echo "device $mddevice md"
236 echo "raidlevel $mdlevel"
237 echo "sector $sector"
242 ###########################################################################
244 # Scan the final blockdevice for additional information.
245 # Information includes mountpoint, filesystem type and filesystem type
246 # specific information: filesysetm block, inode number and filename
251 if detect_ext2fs; then
252 echo "# $device: ext2/3/4 filesystem detected" 1>&2
254 elif detect_reiserfs; then
255 echo "# $device: reiserfs filesystem detected" 1>&2
260 ########################################
266 devnums="`deviceid $device`"
268 # Step 5: Find filesystem mount point
270 while read dev dir opts; do
274 if [ "$devnums" == "`deviceid $dev`" ]; then
275 echo "mountpoint $dir"
283 ########################################
288 which tune2fs >/dev/null 2>&1 && tune2fs -l $device >/dev/null 2>&1
299 fstype="`file -s $device | sed -e 's/.*\(ext[0-9]\).*/\1/'`"
300 echo "fstype $fstype"
302 # Step 6: Get filesystem blocksize and convert sector number to filesystem block number
304 fsblocksize="`tune2fs -l $device | awk '/Block size/{print $3/512}'`"
306 if [ -z "$fsblocksize" ]; then
307 echo "! $device is not ext2/ext3" 1>&2
311 fsblock="`dc -e "$sector $fsblocksize ~ n [ ] n p"`" #`"
312 fssubsector="${fsblock% *}"
313 fsblock="${fsblock#* }"
315 echo "blocksize $fsblocksize"
316 echo "block $fsblock"
317 echo "subsector $fssubsector"
319 # Step 7: Check, whether block is in use
321 if echo "testb $fsblock" | debugfs $device 2>/dev/null | grep -qF "not in use"; then
322 echo "blockstate free"
325 echo "blockstate used"
327 # Step 8: Find inode, to which the block belongs
329 inode="`echo "icheck $fsblock" | debugfs $device 2>/dev/null | awk 'FNR>1{print $2}'`" #`"
331 if [ -z "$inode" ]; then
332 echo "blocktype meta?"
338 # Step 9: Find file name(s) referencing the inode
342 echo "ncheck $inode" \
343 | debugfs $device 2>/dev/null \
344 | sed -e '1d' -e 's/^[0-9]*[ ]*//' -e 's/^\/\//\//' \
345 | while read name; do \
346 if [ -z "$firstname" ]; then \
347 echo "blocktype data" 1>&3; \
351 echo "name $name" 1>&3; \
354 if [ -z "$namefound" ]; then
355 echo "blocktype journal?"
360 ########################################
365 which debugreiserfs >/dev/null 2>&1 && debugreiserfs $device >/dev/null 2>&1
374 echo "fstype reiserfs"
376 # Step 6: Get filesystem blocksize and convert sector number to filesystem block number
378 blocksize="`debugreiserfs $device 2>/dev/null | awk '/^Blocksize:/{print $2/512}'`"
380 if [ -z "$blocksize" ]; then
381 echo "! $device is not reiserfs" 1>&2
385 block="`dc -e "$sector $blocksize ~ n [ ] n p"`" #`"
386 subsector="${block% *}"
389 echo "blocksize $blocksize"
391 echo "subsector $subsector"
393 # Step 7: Check, whether block is in use
395 if debugreiserfs -1 $block $device 2>&1 >/dev/null | grep -qF "free in ondisk bitmap"; then
396 echo "blockstate free"
399 echo "blockstate used"
401 # Use debugreiserfs -1 to check the block type. This however only works if the block is readable.
402 type="`debugreiserfs -1 $block $device 2>/dev/null | sed -e '/^=*$/d' | head -1`"
405 "Looks like unformatted") type="data" ;;
406 "Reiserfs super block"*) type="superblock" ;;
407 "LEAF NODE"*) type="meta" ;;
408 "INTERNAL NODE"*) type="meta" ;;
409 "Desc block"*) type="journal" ;;
413 if [ -n "$type" ]; then
414 echo "blocktype $type"
417 # Step 8: Find object id to which this block belongs
418 # Step 9: Find file name(s) referencing this object id
420 # Currently we only look for $block in indirect blocks.
422 python - $device $block <<EOF
428 blocknr = int(sys.argv[2])
433 fp = os.popen("debugreiserfs -d %s 2>/dev/null" % device)
435 def parse_leafnode():
438 #sys.stderr.write("> leafnode(1): %s\n" % repr(l))
439 if not l.startswith("LEAF NODE") : return
440 for i in range(4) : fp.readline()
443 #sys.stderr.write("> leafnode(2): %s\n" % repr(l))
445 if len(parts)<2 : return
446 parts = parts[2].split()
447 if len(parts)<4 : return
448 obid=(int(parts[0]),int(parts[1]))
449 if parts[3] == "DIR" : parse_dir(obid)
450 elif parts[3] == "IND" : parse_indirect(obid)
453 l = fp.readline().strip()
454 #sys.stderr.write("> leafnode(3): %s\n" % repr(l))
455 if l == 79*"-" or l == 67*"=" : break
463 l = fp.readline().rstrip()
464 #sys.stderr.write("> dir: %s\n" % repr(l))
465 if l == 79*"-" or l == 67*"=": return
466 if l.endswith("not set"): continue
468 name = l[6:6+int(l[i+2:i+5])]
469 if name == "." or name == ".." : continue
470 i = l.index("[",len(name)+12)
471 entryid = tuple(map(int,l[i+1:l.index("]",i+1)].split()))
472 entry = dirtree.get(entryid)
473 if entry is None : entry = dirtree[entryid] = set()
474 entry.add((name,obid))
478 def parse_indirect(obid):
483 for pointer in fp.readline().strip()[1:-1].split():
484 i = pointer.find("(")
486 blkmin = blkmax = int(pointer)
488 blkmin = int(pointer[:i])
489 blkmax = blkmin+int(pointer[i+1:-1])-1
490 if blocknr >= blkmin and blocknr <= blkmax:
496 entry = dirtree.get(id)
497 #sys.stderr.write("> scan_path: %s->%s\n" % (id, entry))
502 for name, parent in entry:
503 for path in scan_path(parent):
504 rv.append(os.path.join(path,name))
512 #sys.stderr.write("> parse: %s\n" % repr(l))
514 if l.strip() == 67*"=" : break
522 sys.stdout.write("objectid %d %d\n" % blockid)
524 for path in scan_path(blockid):
526 sys.stdout.write("name %s\n" % path)
531 ###########################################################################
536 if [ "$1" == "--noscan" ]; then
543 Usage: $0 [--noscan] <device> <sector>"
545 mapsector -- Map sector numbers to file name(s)
547 Given a device and a sector number, mapsector will try to find the
548 file name(s) mapping to this sector. It will try to gather as much
549 information about the given sector as possible.
551 mapsector currently has support for the following mapping schemes:
555 cryptsetup (luks and plain dmcrypt)
556 LVM in linear allocation mode
558 mapsector currently supports the following filesystems
563 mapsector will try it's best to find an associated file name but
564 depending on the filesystem state and the type of sector (e.g. if
565 the sector is part of a filesystem metadata block) this may not be
568 For mapsector to work, the filesystem must be currently active
569 (e.g. LVM must be running, crypted devices must have been set up). The
570 filesystem must not necessarily be mounted (though if mounted,
571 mapsector will give you the mountpoint).
573 if '--noscan' is given, the possibly lengthy (!!) filesystem scan for
574 filenames is skipped.
582 echo "device $device"
583 echo "sector $sector"
586 if [ -z "$noscan" ]; then