Greg Knox's answer is correct, but could be easier. I've written a shell script, lba2file, which performs all the arithmetic for you, source code below.
[Update: Script no longer depends on udisks binary].
Example usage of lba2file
Solving the problem posed in the question (with address specified in bytes):
kremvax$ sudo lba2file -b 1000000 /dev/sda
Disk Byte 1000000 is at filesystem block 124744 in /dev/sda1
Block is used by inode 21762939
Searching for filename(s)...
Inode Pathname
21762939 /home/lilnjn/backups/adhumbla_pics_2.zip
Example usage with S.M.A.R.T.
If your hard drive has a bad sector, you may want to find out what file is corrupted before you remap the sector by writing zeros to it. You can do so easily using smartctl and lba2file.
kremvax$ sudo smartctl -C -t short /dev/sdd
kremvax$ sudo smartctl -a /dev/sdd | grep '^# 1'
# 1 Short captive Completed: read failure 90% 20444 1218783739
The final number 1218783739 is the disk address in sectors, not bytes:
kremvax$ sudo lba2file 1218783739 /dev/sdd
Disk Sector 1218783739 is at filesystem block 152347711 in /dev/sdd1
Block is used by inode 31219834
Searching for filename(s)...
Inode Pathname
31219834 /home/mryuk/2020-11-03-3045-us-la-msy.jpg
31219834 /home/mryuk/web/2020-11-03-3045-us-la-msy.jpg
Discussion
My script defaults to a sector address (often called "LBA") rather than bytes. This is because LBA is what tools like smartctl will report when there is a bad block on the drive. However, if you want to specify bytes instead of sectors, just give the -b flag.
Source Code
Cut and paste into a file or click here to download from https://github.com/hackerb9/lba2file/
#!/bin/bash
# lba2file: Given an LBA number and a drive in /dev/, print which
# filename(s), if any, use that sector.
# This is the opposite of `hdparm --fibmap /foo/bar`
# B9 May 2020
if [[ "$1" == "-b" ]]; then
BYTESFLAG=Byte
shift
fi
if [[ $# -lt 2 ]]; then
echo "Usage: lba2file [-b] <sector number> /dev/sdX"
echo " -b: Use byte address instead of sector"
exit 1
fi
if [[ $(id -u) -ne 0 ]]; then
echo "Please run as root using 'sudo $@'" >&2
exit 1
fi
lba=$1
drive=$2
drive=${drive#/dev/} # Remove /dev/ prefix, if any.
if [[ "$drive" =~ ^(.*)[0-9]$ ]]; then # Either user specified a partition.
searchparts="/sys/class/block/$drive"
drive=${BASH_REMATCH[1]}
else # Or user specified a drive.
shopt -s nullglob # Don't use '?' literally.
searchparts=$(eval echo /sys/class/block/${drive}?)
fi
for partition in $searchparts; do
device=/dev/${partition#/sys/class/block/}
cd "$partition" || continue
start=$(cat "$partition/start")
partitionsize=$(cat "$partition/size")
hwsectorsize=$(cat "/sys/class/block/$drive/queue/hw_sector_size")
# Typically: e2blocksize==4096, hwsectorsize==512
# Example: start=1048576, partitionsize=640133980160
# Do a sanity check.
if [[ -z "$start" || -z "$partitionsize" || -z "$hwsectorsize" ]]; then
echo "Error reading data for $device" >&2
continue
fi
# Scale everything to bytes since we'll use that for debugfs.
start=$((start * hwsectorsize))
partitionsize=$((partitionsize * hwsectorsize))
# If not using byte flag, scale the address, too.
if [[ -z "$BYTESFLAG" ]]; then
byteaddress=$((lba * hwsectorsize))
else
byteaddress=$lba
fi
if [[ $byteaddress -lt $start ||
$byteaddress -ge $((start+partitionsize)) ]]; then
#echo "Address $byteaddress is not within $partition"
continue # Not in this partition
fi
if ! e2blocksize=$(tune2fs -l $device 2>/dev/null |
grep '^Block size' | egrep -o '[0-9]+'); then
echo "Skipping $device, not an Ext2/3/4 partition"
continue
fi
# Scale address by filesystem blocksize to find filesystem block number
e2blockaddress=$(( (byteaddress - start) / e2blocksize))
Sector=${BYTESFLAG:-Sector}
echo "Disk $Sector $lba is at filesystem block $e2blockaddress in $device"
inode=$(debugfs -R "icheck $e2blockaddress" $device 2>/dev/null |
tail -1 | cut -f2)
if [[ "$inode" && "$inode" != "<block not found>" ]]; then
echo "$Sector is used by inode $inode"
echo "Searching for filename(s)..."
debugfs -R "ncheck $inode" $device 2>/dev/null
else
echo "$Sector is not in use."
fi
done