Cerin
Cerin

Reputation: 64709

Determine Device of Filesystem in Python

How do you use Python to determine which Linux device/partition contains a given filesystem?

e.g.

>>> get_filesystem_device('/')
/dev/sda
>>> get_filesystem_partition('/')
/dev/sda1

Upvotes: 8

Views: 10514

Answers (9)

Tcll
Tcll

Reputation: 7382

You can do it without importing anything, though this only works for mounted partitions

mountpoint.py:

with open("/proc/mounts",'r') as mounts:
    mountpoints = dict( line.split()[1::-1] for line in mounts )

print(mountpoints['/'])

output:

ws2:[tcll]:~$ python3 ./mountpoint.py
/dev/sda6
ws2:[tcll]:~$ 

If you want to include some unmounted partitions, you'll have to scan fstab, however this won't get target mountpoints for things like USB drives.

you can also:

import os

for label in os.listdir('/dev/disk/by-label'):

    mountpoint = '/media/%s/%s'%( os.environ('USER'), label ) # not guaranteed

    if mountpoint not in mountpoints:
        mountpoints[mountpoint] = '/dev/%s'%(
            os.readlink( '/dev/disk/by-label/%s'%label )[6:]
        )

for partitions with labels that aren't in fstab.

Lastly, for partitions that don't have labels and aren't in fstab, you can also get their potential mountpoints:

for uuid in os.listdir('/dev/disk/by-uuid'):

    mountpoint = '/run/media/%s/%s'%( os.environ('USER'), uuid ) # also not guaranteed

    if mountpoint not in mountpoints:
        mountpoints[mountpoint] = '/dev/%s'%(
            os.readlink( '/dev/disk/by-uuid/%s'%uuid )[6:]
        )

Sadly Linux doesn't make it easy to get details for unmounted partitions such as mountpoint and filesystem-type, you usually need to depend on some root program that can read /dev/sd(x)# to determine the filesystem, and need to guess the target mountpoint.

Upvotes: 1

quine9997
quine9997

Reputation: 834

My code

#file /dir1/check.py
import os
from subprocess import *
import subprocess


#function  definition
def check():
  device = subprocess.check_output("awk '$2 == \"/\" { print $1}' /proc/mounts", shell=True)
  device_decode=device.decode()
  print(device_decode[-5:-1])
  print(device_decode[-2])
  return

#function  invocation
check()

Run in the bash shell

python3 /dir1/check.py

Upvotes: 0

Amir
Amir

Reputation: 6176

Here is how you can simply get the devices major and minor numbers:

import os
major, minor = divmod(os.stat('/').st_dev, 256)

Upvotes: 0

AAAfarmclub
AAAfarmclub

Reputation: 2360

How about using the (linux) blkid command (/sbin/blkid)

$ uname --kernel-name --kernel-release
Linux 3.2.0-4-amd64

$ python --version
Python 2.7.3

-

#!/usr/bin/env python                                           

import subprocess

sys_command = "/sbin/blkid"
proc = subprocess.Popen(sys_command,
                        stdout=subprocess.PIPE,
                        shell=True)

# proc.communicate() returns a tuple (stdout,stderr)                          
blkid_output = proc.communicate()[0]

print blkid_output

Here's the output on a dual-boot laptop with an (unmounted) USB drive (sdb1)

$ ./blkid.py
/dev/sda1: LABEL="RECOVERY" UUID="xxxx-xxxx" TYPE="vfat"
/dev/sda2: LABEL="OS" UUID="xxxxxxxxxxxxxxx" TYPE="ntfs"
/dev/sda5: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4"
/dev/sda6: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="swap"
/dev/sda7: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4"
/dev/sdb1: LABEL="CrunchBang" TYPE="iso9660"

Upvotes: 0

John Haxby
John Haxby

Reputation: 69

There are problems with quite a few of the above solutions. There's actually a problem with the question as well.

The last answer (searching /proc/mounts) just doesn't work: searching for "/" will match every line in /proc/mounts. Even correcting this like this won't work:

import subprocess
device = subprocess.check_output("awk '$2 == \"/filesystem\" { print $1}' /proc/mounts", shell=True)
print device

When "/filesystem" is "/" you'll typically get two entries, one for "rootfs" and one for the actual device. It also won't work when the mounted file system name has spaces in it (the space appears as \040 in /proc/mounts).

The problem is made worse with btrfs subvolumes. Each subvolume is mounted separately but they all share the same device. If you're trying to use a btrfs snapshot for backups (as I was) then you need the subvolume name and an indication of the filesystem type.

This function returns a tuple of (device, mountpoint, filesystem) and seems to work:

import os
def get_filesystem_partition(fs):
    res = None
    dev = os.lstat(fs).st_dev
    for line in file('/proc/mounts'):
        # lines are device, mountpoint, filesystem, <rest>
        # later entries override earlier ones
        line = [s.decode('string_escape') for s in line.split()[:3]]
        if dev == os.lstat(line[1]).st_dev:
            res = tuple(line)
    return res

That seems to work for all the cases I can think of, although I expect that there are still pathological cases where it falls to bits.

Upvotes: 1

SunSparc
SunSparc

Reputation: 1922

I recently had a need for this solution also. After seeing all the convoluted methods of getting the result I wanted through pure python, I decided to turn to the shell for help.

import subprocess
device = subprocess.check_output("grep '/filesystem' /proc/mounts | awk '{printf $1}'", shell=True)
print device

This gives me exactly what I want, the device string for where my filesystem is mounted.

Short, sweet, and runs in python. :)

Upvotes: 1

Anders Waldenborg
Anders Waldenborg

Reputation: 3035

Your question was about Linux, so this is (more or less) linux specific.

Below is code example for three variants for mapping major/minor to a device name.

  • Parse /proc/partitions.
  • Ask hal. Hal also keeps track of "parent" device, meaning you can easily get the disk aswell as the partition.
  • Check sysfs yourself. This is where hal gets its information from.

I'd say that /proc/partitions is simplest - it is just one file to open and check. hal gives you most information, and abstracts away lots of details. sysfs may be viewed as more correct that /proc/partitions and doesn't require hal to be running.

For a desktop program I would go for hal. On an embedded system I'd go with sysfs.


import os

def main():
    dev = os.stat("/home/").st_dev
    major, minor = os.major(dev), os.minor(dev)

    print "/proc/partitions says:", ask_proc_partitions(major, minor)
    print "HAL says:", ask_hal(major, minor)
    print "/sys says:", ask_sysfs(major, minor)

def _parse_proc_partitions():
    res = {}
    for line in file("/proc/partitions"):
        fields = line.split()
        try:
            tmaj = int(fields[0])
            tmin = int(fields[1])
            name = fields[3]
            res[(tmaj, tmin)] = name
        except:
            # just ignore parse errors in header/separator lines
            pass

    return res

def ask_proc_partitions(major, minor):
    d = _parse_proc_partitions()
    return d[(major, minor)]

def ask_hal(major, minor):
    import dbus

    bus = dbus.SystemBus()
    halobj = bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager')
    hal = dbus.Interface(halobj, 'org.freedesktop.Hal.Manager')

    def getdevprops(p):
        bdevi = dbus.Interface(bus.get_object('org.freedesktop.Hal', p),
                               "org.freedesktop.Hal.Device")
        return bdevi.GetAllProperties()

    bdevs = hal.FindDeviceByCapability("block")
    for bdev in bdevs:
        props = getdevprops(bdev)
        if (props['block.major'], props['block.minor']) == (major, minor):
            parentprops = getdevprops(props['info.parent'])
            return (str(props['block.device']), 
                    str(parentprops['block.device']))

def ask_sysfs(major, minor):
    from glob import glob
    needle = "%d:%d" % (major, minor)

    files = glob("/sys/class/block/*/dev")
    for f in files:
        if file(f).read().strip() == needle:
            return os.path.dirname(f)

    return None

if __name__ == '__main__':
    main()

Upvotes: 3

dawg
dawg

Reputation: 103744

It is not the purdiest, but this will get you started:

#!/usr/bin/python

import os, stat, subprocess, shlex, re, sys

dev=os.stat('/')[stat.ST_DEV]
major=os.major(dev)
minor=os.minor(dev)

out = subprocess.Popen(shlex.split("df /"), stdout=subprocess.PIPE).communicate()
m=re.search(r'(/[^\s]+)\s',str(out))

if m:
    mp= m.group(1) 
else:
    print "cannot parse df"   
    sys.exit(2)

print "'/' mounted at '%s' with dev number %i, %i" % (mp,major,minor)   

On OS X:

'/' mounted at '/dev/disk0s2' with dev number 14, 2

On Ubuntu:

'/' mounted at '/dev/sda1' with dev number 8, 1

To get the device name, chop off the minor number from the partition name. On OS X, also chop the 's' + minor number.

Upvotes: 0

John Gaines Jr.
John Gaines Jr.

Reputation: 11524

It looks like this post has some of your answer (still not sure just how to grab the major/minor out of the /dev/sda2 entry to match it up with what os.stat() returns for /:

Device number in stat command output

>>> import os
>>> print hex(os.stat('/')[2])
0x802
  \ \minor device number
   \major device number

[me@server /]$ ls -l /dev/sda2
brw-rw----    1 root     disk       8,   2 Jun 24  2004 /dev/sda2
[me@server jgaines2]$               \    \minor device number
                                     \major device number

Upvotes: 1

Related Questions