Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

While answering this U&L question titled: What command do I use to see the start and end block of a file in the file system?, I tried to figure out if it was possible to determine a file's LBA using it's inode.

My answer determined that I could use hdparm as one method for finding LBAs:

$ sudo hdparm --fibmap afile 

afile:
 filesystem blocksize 4096, begins at LBA 0; assuming 512 byte sectors.
 byte_offset  begin_LBA    end_LBA    sectors
           0  282439184  282439191          8

But I was curious if there was some method using a file's inode to also get the LBA's; without using hdparm.

I think there might be alternative methods hiding in the tools filefrag, stat, debugfs, and tune2fs but teasing it out is eluding me.

Can anyone think of alternatives?


Here's some of my research thus far that might be useful to those brave enough to attempt to answer this.

filefrag

I suspect you could use the tool filefrag to do it, specifically using the results from its -e switch, perhaps by performing several calculations to get there that I'm not that familiar with.

sample output

$ filefrag -e afile
Filesystem type is: ef53
File size of afile is 20 (1 block of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:   35304898..  35304898:      1:             eof
afile: 1 extent found

inodes

Another potential method I suspect might have potential is to use a file's inode information, either directly or through some complex math that is poorly documented on the interwebs.

Example

First we find out the file's inode. We can do this using either the stat command or ls -i.

stat

$ stat afile 
  File: ‘afile’
  Size: 20          Blocks: 8          IO Block: 4096   regular file
Device: fd02h/64770d    Inode: 6560281     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/    saml)   Gid: ( 1000/    saml)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2013-12-27 18:40:12.788333778 -0500
Modify: 2013-12-27 18:40:23.103333073 -0500
Change: 2013-12-27 18:44:03.697317989 -0500
 Birth: -

ls -i

$ ls -i 
6560281 afile

With the inode information in hand, we can now open up the filesystem this file resides on using the tool, debugfs.

NOTE: To determine the filesystem a file resides on you can use the command df <filename>.

Now if we run debugfs and run the command stat <inode #> we can get a list of extents that contain this file's data.

$ sudo debugfs -R "stat <6560281>" /dev/mapper/fedora_greeneggs-home
debugfs 1.42.7 (21-Jan-2013)
Inode: 6560281   Type: regular    Mode:  0664   Flags: 0x80000
Generation: 1999478298    Version: 0x00000000:00000001
User:  1000   Group:  1000   Size: 20
File ACL: 0    Directory ACL: 0
Links: 1   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x52be10c3:a640e994 -- Fri Dec 27 18:44:03 2013
 atime: 0x52be0fdc:bbf41348 -- Fri Dec 27 18:40:12 2013
 mtime: 0x52be0fe7:18a2f344 -- Fri Dec 27 18:40:23 2013
crtime: 0x52be0dd8:64394b00 -- Fri Dec 27 18:31:36 2013
Size of extra inode fields: 28
Extended attributes stored in inode body: 
  selinux = "unconfined_u:object_r:user_home_t:s0\000" (37)
EXTENTS:
(0):35304898

Now we have the extents information above, and this is where I get lost and do not know how to proceed.

References

share|improve this question

2 Answers 2

filefrag and debugfs report offset expressed in number of filesystem blocks.

To get the offset in number of 512 byte units, you need to multiply by the size of the block in 512 byte units. On ext4 FS, block size is often 4k, so you need to multiply by 8.

With filefrag, you can also use a -b 512 option to get the offset in 512 byte units.

You can get the block size with the stats command in debugfs, or with GNU stat:

stat -fc%s /mount/point

(or any file in that filesystem).

Note that hdparm is a hard-disk utility, it will try to give the offset within the disk as opposed to the block device the file system is mounted on (assuming that block device does reside on disk somehow). It only works that way for partitions (by adding the content of /sys/class/block/the-block-device/start to the actual offset), and md RAID 1 devices, but not other possibly disk backed block device types like device mapper devices, other RAID levels, dmraid devices, loop, nbd... Also note that older versions of hdparm relied on the FIBMAP ioctl which is limited in what block device it can be use on, while newer versions use FIEMAP like filefrag.

So, for instance, if you have an ext2 filesystem on /dev/sda1.

# hdparm --fibmap /file/in/there
/file/in/there:
 filesystem blocksize 1024, begins at LBA 2048; assuming 512 byte sectors.
 byte_offset  begin_LBA    end_LBA    sectors
           0     109766     109767          2

You can get those two sectors (but note that the file likely uses only part of it):

dd skip=109766 count=2 if=/dev/sda # not /dev/sda1

While with filefrag or debugfs.

# filefrag -v /file/in/there
Filesystem type is: ef53
Filesystem cylinder groups is approximately 12
File size of /file/in/there is 87 (1 block, blocksize 1024)
 ext logical physical expected length flags
   0       0    53859               1 merged,eof

You get it from the actual block device:

dd bs=1024 skip=53859 count=1 if=/dev/sda1
share|improve this answer
    
So if I understand you correctly, if filefrag -b512 -v .. says "physical_offset: 211787168.. 211795719" these would equate to the LBAs? This seems to jive with the same file with hdparm --fibmap, 211787168..211795719. If I drop the -b512 -v and use the def. 1024, and attempt to mult. by 8, 26473396⋅8..26474464⋅8, I get 211787168..211795712, which is close but a bit off. I'm thinking the 2nd value should be (26474465⋅8)-1 = 211795719, not sure why though. –  slm Dec 29 '13 at 6:47
    
Any thoughts on how to get the blocks in 512 units from debugfs? –  slm Dec 29 '13 at 16:41
    
I ended up doing the calculations to convert from extents to LBAs using the same math above. –  slm Dec 29 '13 at 17:48
up vote 1 down vote accepted

Turns out to convert from extents to LBAs is actually fairly straightforward once you understand where the numbers are coming from. @StephaneChazelas' answer was critical to gaining this understanding.

Original debugfs output

Using the following example that was mentioned in the question.

$ sudo debugfs -R "stat <6560281>" /dev/mapper/fedora_greeneggs-home
debugfs 1.42.7 (21-Jan-2013)
Inode: 6560281   Type: regular    Mode:  0664   Flags: 0x80000
Generation: 1999478298    Version: 0x00000000:00000001
User:  1000   Group:  1000   Size: 20
File ACL: 0    Directory ACL: 0
Links: 1   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x52be10c3:a640e994 -- Fri Dec 27 18:44:03 2013
 atime: 0x52be0fdc:bbf41348 -- Fri Dec 27 18:40:12 2013
 mtime: 0x52be0fe7:18a2f344 -- Fri Dec 27 18:40:23 2013
crtime: 0x52be0dd8:64394b00 -- Fri Dec 27 18:31:36 2013
Size of extra inode fields: 28
Extended attributes stored in inode body: 
  selinux = "unconfined_u:object_r:user_home_t:s0\000" (37)
EXTENTS:
(0):35304898

With the extents information we can do the following calculations. But we need one additional piece of information. The underlying filesystem's block size. You can use this command to get it.

Block size

$ sudo tune2fs -l /dev/mapper/fedora_greeneggs-home | grep "Block size"
Block size:               4096

Converting from Extents to LBAs

So the key transformation to recognize here is that LBAs are in 512 byte units and the above debugfs command that reported the number of extents, is reporting that in 4096 byte blocks.

So, 4096/512 = 8. So we need to multiply the extents by 8 to convert them to LBA values.

So the following math will give us our beginning LBA:

$ calc -d
; 35304898 * 8
    282439184
; 

So what's our ending LBA? To get that we need to recognize that our inode fits inside a single block so its ending extent is the same as its beginning extent. To calculate the ending LBA we can use this equation.

ending LBA = ( (extent + 1) * 8 ) - 1

So performing this calculation:

$ calc -d
; ( (35304898 + 1) * 8 ) - 1
    282439191

Confirming the results

Looking at the original hdparm output:

 byte_offset  begin_LBA    end_LBA    sectors
           0  282439184  282439191          8

We see that things match.

Another example

Just to make sure we're right here's a larger file as a 2nd example.

$ ls -i util-linux-2.19.tar.bz2 
6559005 util-linux-2.19.tar.bz2

Here's the inode's extents.

$ sudo debugfs -R "stat <6559005>" /dev/mapper/fedora_greeneggs-home
...
EXTENTS:
(0-1068):26473396-26474464

Now we do of conversions from extents to LBAs.

$ calc -d
; 26473396*8
    211787168
; (26474464+1)*8 - 1
    211795719

And we confirm.

$ sudo hdparm --fibmap util-linux-2.19.tar.bz2 
...
 byte_offset  begin_LBA    end_LBA    sectors
           0  211787168  211795719       8552

And we match again.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.