3

I need to find (in C or C++) the used space on a filesystem (Linux) when being provided only with the disk block device name and the partition number.

For most filesystems I can go:

  • Craft a dev name as /dev/<block><part> or /dev/<block>p</part> whichever exists
  • Search /proc/mounts for that device and get the mount point
  • Use statvfs() on that mount point

Although that seems a bit long-winded it's adequate. However, there is a caveat to it:

My root is mounted from /dev/root rather than the real block device name. Except there isn't a /dev/root - the node doesn't exist. Now, I can dereference that to a /dev/<whatever> block name by using stat("/"...) to get the device ID and split it into major/minor numbers, then look up /sys/dev/block/<maj>:<min>/uevent for the DEVNAME= entry.

Keeping it a completely generic function the method would then expand to:

  1. Craft the right device name of /dev/<block><part> or /dev/<block>p<part>, whichever exists
  2. For each entry in /proc/mounts:
    1. Extract the mount point
    2. Get the major/minor pair from stat()
    3. Look up the parent device name from /sys/dev/block/<maj>:0/uevent
    4. Compare that device name with the block device name and the minor with the partition number
    5. If it's a match then get the used space from statvfs()

That is getting really long-winded now, and I don't like it. Yes, it should work, but it is far from ideal.

So - is there a simpler interface for doing this? Some way of interacting directly with the mount table in the kernel? Some way of getting, for each mounted filesystem, the block device (major/minor would be fine), and the path it's mounted on (along with other information probably) in a struct?

I know there are mount() and umount() functions, but is there a "tell me what is mounted"() function?

  • df has the ability to look this up somehow, you might check the source code for that. – Bratchley May 4 '15 at 20:51
  • @Bratchley, Nah, df just calls statvfs() on whatever mount point it finds in /etc/mtab. If the name of the block device in the first column of that file is incorrect, df neither knows nor cares. – Celada May 4 '15 at 20:59
2

My first comment is all of what you state will only work if the filesystem on the device you're interested in is currently mounted. But I guess you know that and accept that limitation.

The method you propose seems quite thorough and I think it will catch all cases.

About looking ip in /sys/dev/block:

  • You're not looking for <maj>:0 as you state. You're looking for <major>:<minor>.
  • Be prepared for the case where you don't find it. Some filesystems like /proc, and type tmpfs and nfs have no associated device. You'll want to ignore those since they can't possibly match the block device that interests you.

When looking up the device name, don't scan /sys/dev/block/<major>:<minor>/uevent. Instead to a readlink() on /sys/dev/block/<major>:<minor> itself and take the basename of the result. You should get the same result but it's slightly cleaner and more efficient.

If you want to look up multiple devices, you should scan /proc/mounts and do the whole /sys lookup only once, and cache the results for the next device lookup.

There is no show_me_mounts() system call. In Linux, that's what /proc/mounts is for. But as you've noticed, it's not perfect. Generally you won't see the virtual/nonexistent /dev/root problem though with fully modern initramfs-based booting.

Edit based on your requirement to find the whole-disk corresponding to each partition.

For getting the "outer" block device (= whole disk) given an "inner" block device (= partition) you cannot just change the minor device number to 0.

The correct way to do this if you found major 8 and minor 1 is to look in this file:

/sys/dev/block/8:1/../dev

That will produce 8:0, but without the incorrect assumption that the minor device ID can always be switched to 0.

If you get ENOENT trying to open that, it's because the type of block device in question does not have an inner/outer hierarchy or the minor device ID does not correspond to an inner device.

  • I need to look up <maj>:0 since I am being passed just the disk device name and a numeric partition, not the partition name - sda not sda1, mmcblk0 not mmcblk0p2 for example. Also this isn't an initramfs capable (as far as I know) system. It's an ARM based embedded linux environment based on Jessie. But good call on the readlink() - I will certainly do that. – Majenko May 4 '15 at 20:59
  • I see. But that's not reliable. For example, if it's a device-mapper device with say for example minor number 8, then minor number 0 of the same major number denotes a totally difference device-mapper device (namely /dev/dm-0)! In general you can't assume that minor number 0 corresponds to some kind of "master" block device. Even for normal hard disks: /dev/sdb1 has minor number 17 but minor number 0 of the same major number is /dev/sda. – Celada May 4 '15 at 21:02
  • Good point on that - I hadn't noticed that, but you're right. I have 8/0, 8/16, 8/32 and 8/64 for the hard drives in my PC. Never had more than one drive in this embedded system to notice it, and never needed to look before. – Majenko May 4 '15 at 21:04
  • So if I want to get the "disk" portion I can do a readlink of the major/minor pair, then go up a directory and get the name from that. sda1 is a subdirectory of sda in the /sys tree – Majenko May 4 '15 at 21:07
  • I added some info on how I think you can reliably get the "parent" device of a partition. – Celada May 4 '15 at 21:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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