3

Given a filesystem such as /dev/sda5 or /dev/mmcblk0p5 is it possible to get both the partition number (5) and block device (i.e. /dev/mmcblk0) without string manipulation, or by minimizing the amount of string parsing required?

Assume kernel 5.x. Examples below are from a device running 5.10, however if an answer is kernel or config specific please call it out in your answer.

Details/ Examples:

udevadm info (in v247) gives back a lot of valuable information including a PARTN property. However I don't believe there is a way to "print the value of property X." in v247 (in v251 possibly?) So we would be relegated to parsing output to find our attribute and value like:

udevadm info /dev/disk/by-label/rootfs | grep -e '^E: PARTN=' | awk -F '=' '{print $2}'    # prints '2'

udevadm also includes major number which may help us later but no "disk" property or similar.

We can also find the partition number by reading the sysfs file /sys/class/block/mmcblk0p2/partition assuming we are content to strip the /dev/ to leave us with mmcblk0p2:

$ cat /sys/class/block/mmcblk0p2/partition 
2

In the same fashion, it appears we can also get major:minor numbers (with minimal string tokenization):

$ cat /sys/class/block/mmcblk0p2/dev 
179:2

Unfortunately, there doesn't appear to be anything under /sys/class/block/mmcblk0p2 that directly points to the block device where the partition resides, i.e. mmcblk0 in this case.

To get the disk, we could search all paths under /sys/block/ to find one that has our partition; i.e. does the path /sys/block/mmcblk0/mmcblk0p2 exist? If yes, then the third path component i.e. mmcblk0 is our disk.

Or we can use the major number from our partition (179 in this case) like so:

$ realpath /dev/block/179\:0
/dev/mmcblk0

We could also parse /proc/partitions (again -- string parsing!)

$ cat /proc/partitions
major minor  #blocks  name

..snip..
 179        0    7782400 mmcblk0
 179        1      96632 mmcblk0p1
 179        2    3145728 mmcblk0p2

Assuming some string finagling is going to be necessary, what's the "easiest" / least fragile method?

Background

I have a generic ARM device image that supports multiple partition schemes. Some ARM devices require a particular partition scheme, while others might just use one partition. I have a script to expand the rootfs to fill the disk.

To resize the partition, we execute:

sfdisk --no-reread "$DISK" -N "$PART" <<EOF
, $SIZE, L
EOF

where, for instance, DISK=/dev/mmcblk0 and PART=2. Then we use resize2fs after that.

4
  • 2
    What's the benefit of avoiding string manipulation? Surely the difference in efficiency is negligible for something like this, so is it to ensure correctness?
    – terdon
    Sep 26, 2022 at 11:54
  • 1
    Also, do you have a /sys/class/block/mmcblk0/uevent file? What does that have for DEVNAME?
    – terdon
    Sep 26, 2022 at 11:58
  • 1
    Yes the goal is correctness not efficiency. Simple string manipulation is OK but I want to avoid i.e. splitting mmcblkXpY since it would be fragile and tied to the device naming scheme.
    – thom_nic
    Sep 26, 2022 at 16:39
  • @terdon you are right, /sys/class/block/$devname/uevent contains many of the same useful properties that udveadm info gives
    – thom_nic
    Sep 26, 2022 at 20:11

2 Answers 2

3

We could search all paths under /sys/block/ to find one that has our partition.

Instead of searching all paths, you could just read the symlink...

$ readlink /sys/class/block/sdb17
[…]/block/sdb/sdb17
$ basename "$(realpath /sys/class/block/sdb17/..)"
sdb
$ basename "$(realpath /sys/class/block/nvme0n1p2/..)"
nvme0n1

Or we can use the major number from our partition (179 in this case) like so:

That fails for large partition numbers.

$ cat /sys/block/sdb/sdb17/dev
259:1
$ realpath /dev/block/259:0
/dev/sdb16
5
2

If you create a udev rule, you can pass environment variables from it, to your script with arguments.

Where %n is the kernel number of the device (i.e %n from /dev/sda1 gives you 1).

And where %P is the node name of the parent device (i.e %P from /dev/sda1 gives you sda). So you can prepend %P with /dev like /dev/%P, in your script to achieve your task.

So a short example of doing this would first be to create a udev rule (you would need to identify your device better than shown below):

KERNEL=="sd[a-z][0-9]", ACTION=="add", RUN+="/path/to/script.sh %P %n"

And then edit your script accordingly:

sfdisk --no-reread "/dev/$1" -N "$2" <<EOF
, $SIZE, L
EOF

Where $1 would be %P and $2 would be %n

Although this assumes that you want to run the script every time the device is inserted (due to ACTION=="add"). And this only works with scripts that doesn't take long to execute (udev doesn't handle long-running processes).

To bypass this you can pass the enviromental variable to a systemd instance unit, which then calls the script instead.

1
  • Actually the goal is to run this once from systemd on boot Before=local-fs.target also I've noticed /dev/disk/by-label/rootfs isn't necessarily created yet since that is a udev rule.
    – thom_nic
    Sep 26, 2022 at 16:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .