8

On the distribution that I am using right now, Arch Linux ARM, Linux is launched without an initial RAM disk, meaning the kernel does all the work in mounting the root filesystem. This system is installed on an SD card and it has a script on it that needs to determine the device node of the filesystem that it resides on. This device node can change depending on how I boot the device up (e.g., I attach it to another device that is already running).

I am able to determine the mount point using df -k --output=target ${0} | sed "1d". To determine the source device, I run df -k --output=source ${0} | sed "1d". If I've booted into Arch Linux, the result of that last command spits out /dev/root, which does not exist. Sure, on some systems, udev will create a symbolic link from /dev/root to the real root device, but mine doesn't do that. I could look at the contents of /proc/cmdline but that stays the same throughout a kernel's uptime, so that means it isn't reliable.

When I get /dev/root, how can I determine the real underlying block device it stands in place for?

3
  • (1) Obviously I’m not understanding a lot of this. AFAIK, the mount point for the root filesystem is always / — why do you need to have a command to “determine the mount point” for the root filesystem? (2) What is ${0} here? Why are you saying ${0} instead of $0, or, better yet, "$0"? (3) Your question is about filenames / pathnames.  Do those change if you run df without the -k?  If so, please explain how. If not, consider leaving off the -k, because it’s clutter. (4) If you say df /, what do you get? In what respect does that not answer your question? … (Cont’d) Jun 30, 2017 at 6:01
  • (Cont’d) …  (5) You say, “the contents of /proc/cmdline … stays the same throughout a kernel’s uptime, so that means it isn’t reliable.”  Huh?  How is a stable value unreliable? (6) Does the output of mount (or cat /proc/mounts) ever show the information you want?  If not, slow down, stop, go back and start over. If the output of mount never shows the information you want, then I have no idea what you want, and probably everybody else is confused too, … (Cont’d) Jun 30, 2017 at 6:01
  • (Cont’d) …  and you are highly unlikely to get a satisfactory answer until you explain your requirement more clearly.  OTOH, if the output of mount sometimes shows the information you want, please give us an example of mount output that doesn’t show the information you want, and explain when you get that instead of the useful output.  (7) Please do not respond in comments; edit your question to make it clearer and more complete. Jun 30, 2017 at 6:01

9 Answers 9

7

Answer from the comments here is very simple. Just call:

findmnt -n -o SOURCE /

Provided that findmnt is present...

4

The mount command will show it:

mount | sed -n 's|^/dev/\(.*\) on / .*|\1|p'
2
  • Nope! The output of mount is unreliable and difficult to process with a script.
    – Melab
    Jun 29, 2017 at 15:09
  • The above will give you the block device the root filesystem is on ... Jun 30, 2017 at 11:53
4

I found this blog post very helpful in finding root device and primary disk in Linux.

You'll need lsblk utility installed, which is provided by util-linux in CentOS/RHEL. For example, such a variant will give you the disk name of the root device:

lsblk -oMOUNTPOINT,PKNAME -rn | awk '$1 ~ /^\/$/ { print $2 }'
sda3

findmnt from the same package will give you actual underlying root device name:

findmnt -oTARGET,SOURCE -rn /
/ /dev/mapper/vg01-lv_root

Flag -P for both utilities will provide you the same information in the format, suitable for eval in the shell:

findmnt -oTARGET,SOURCE -Pn /
TARGET="/" SOURCE="/dev/mapper/vg01-lv_root"

lsblk -oMOUNTPOINT,PKNAME -Pn | grep 'MOUNTPOINT="/"'
MOUNTPOINT="/" PKNAME="sda3"
2
lsblk --noheadings --paths --output PKNAME $( findmnt --noheadings --output SOURCE --mountpoint / )

Example output:

/dev/sda

First, you use findmnt to find a device mounted as /, which is /dev/sda2 in my case. Then, you find its parent device using lsblk.

1

You can use stat / and inspect the device major/minor number or with the help of the rdev command get this interpreted automatically.

2
1

Assuming there's a file at /etc/fstab

sudo cat /etc/fstab | grep '\s\/\s'

will give you an fstab entry to the block device mounted on /.

2
  • That is not reliable at all. If I was asking about fstab, then this might be a good answer.
    – Melab
    Jun 29, 2017 at 15:08
  • Agreed, there is no less reliable source for mounted information than /etc/fstab, I never use it in any of my programs since it has nothing to do with what is actually mounted at this moment on the running system.
    – Lizardx
    Mar 18, 2018 at 20:41
0

Kernel logs

Do a:

dmesg

First, it might be explicitly given on the kernel CLI at the very start of boot:

Command line: root=/dev/vda ...

Otherwise, at the end of boot, if rootfs is a disk and not initrd / initramfs, the kernel says:

VFS: Mounted root (ext2 filesystem) on device 254:0.

so we then find that:

find /proc /sys -iname '*254:0*'

and then determine what it actually maps through with:

ls -l /sys/dev/block/254:0

output:

lrwxrwxrwx    1 root     root             0 Jan 30 15:59 /sys/dev/block/254:0 -> ../../devices/pci0000:00/0000:00:09.0/virtio4/block/vda

Tested with this QEMU + Buildroot setup.

0

If you have mounted /sys, then you can extract the real root device from it. The basic idea can be seen in this shell one-liner, if you can grok it:

eval `stat / | awk '/Device:/ { $A=strtonum(substr($2,index($2,"/")+1)); printf "grep DEVNAME /sys/dev/block/%d:%d/uevent\n", int($A/256), $a%256; }'`

Call stat /, then extract the decimal device value and convert it into major and minor numbers using awk, then grep the DEVNAME field from the file /sys/dev/block/<major>:<minor>/uevent. Example output:

# Generic Linux PC
DEVNAME=sda1
# Raspberry Pi
DEVNAME=mmcblk0p2

This works with gawk, but not with mawk. I'm open to suggestions how to make it compatible with both.

UPDATE: I've added a quick Python version also:

import os                                                                       
                                                                                
# Fetch major and minor numbers of root directory.                                                
st = os.stat("/")                                                              
major = os.major(st.st_dev)                                                     
minor = os.minor(st.st_dev)                                                     
# Search for device name in "/sys" filesystem.                                  
with open(f"/sys/dev/block/{major}:{minor}/uevent") as f:                       
    for line in f:                                                              
        key, value = line.rstrip().split("=")                                   
        if key == "DEVNAME":                                                    
            print(value)                                                        
            break                                                               
    else:                                                                       
        print("root device not found")                                          
0

In my case I have LVM and LUKS setup which looks like the following.

$ lsblk -oNAME,MOUNTPOINT -J
{
   "blockdevices": [
      {"name":"sda", "mountpoint":null,
         "children": [
            {"name":"sda1", "mountpoint":"/boot/efi"},
            {"name":"sda2", "mountpoint":"/boot"},
            {"name":"sda3", "mountpoint":null,
               "children": [
                  {"name":"sda3_crypt", "mountpoint":null,
                     "children": [
                        {"name":"vgmint-root", "mountpoint":"/"},
                        {"name":"vgmint-swap_1", "mountpoint":"[SWAP]"}
                     ]
                  }
               ]
            }
         ]
      },
      {"name":"zram0", "mountpoint":"[SWAP]"},
      {"name":"zram1", "mountpoint":"[SWAP]"},
      {"name":"zram2", "mountpoint":"[SWAP]"},
      {"name":"zram3", "mountpoint":"[SWAP]"}
   ]
}

I want to know that / is on sda. As lsblk can produce JSON with a bit of jq-fu I can be retrieved like this.

$ lsblk -oNAME,MOUNTPOINT -J | \
  jq -r '.blockdevices[] | . as $bd | .. | objects | select(.mountpoint == "/") | $bd.name'
sda

You must log in to answer this question.

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