11

Starting with an absolute file path, I want to obtain the following information:

  1. The mount point of the filesystem on which the file is stored (in order to compute the path relative to the mount point)
  2. The UUID and label of the file system
  3. The type (or vendor name) and the serial number of the hard drive that contains the partition

I am aware that 2 and 3 may be undefined in many cases (e.g. for loopback, ramfs, encyrpted devices), which is totally fine. I also know how to obtain that information using a shell and system tools like df and the /sys or /proc filesystem. See this question for reference.

However, I am searching for the least cumbersone method to do that programmatically with Python 3.5. That means:

  • Prefer system calls instead of parsing contents of /proc or /sys (which may be subject to change or depend on kernel configuration?)
  • Avoid calling subprocesses and parsing their output (the definition of cumbersome)

So far, I am using os.stat() on the path to get the block device's major and minor number from stat_result.st_dev. But what's the proper way to proceed?

There is for example

  • /proc/mounts
  • /proc/partitions
  • /sys/dev/block/<major>:<minor>

Notes: Regarding mounted block devices an partitions, /proc/mounts and /proc/partitions seem to be the canonical information source (which is OK). For UUIDs, labels, serials etc. I currently use udevadm and parse the output:

def get_udev_properties(dev_name):        
    cmd = ["udevadm", "info", "--query=property", "--name", dev_name]
    result = subprocess.run(cmd, stdout=subprocess.PIPE)
    return parse_properties(result.stdout)

Further note: Abstracting from my acutal problem, one could ask more general:

  • What's the canonical identification or representation of a block device with respect to linux system calls and the kernel filesystems?
  • What's the proper way to obtain that representation by major and minor number?
  • What's the proper way to obtain detailed information about a block device?
Community
  • 1
  • 1
code_onkel
  • 2,759
  • 1
  • 16
  • 31
  • 1
    Current versions of `/bin/mount` work by reading `/proc/self/mountinfo`. [The file's structure is explained here](https://www.kernel.org/doc/Documentation/filesystems/proc.txt). [Answers to questions how to do the same using C](http://stackoverflow.com/questions/9280759/linux-function-to-get-mount-points) indicate that there is no better alternative. – Phillip Sep 12 '16 at 08:55
  • @Phillip If parsing files from `/proc` is the canonical way, that's a valid answer. I find it quite hard to find normative references / best practices regarding the data I would like to acquire. – code_onkel Sep 12 '16 at 10:05
  • `os.stat` is fine, there's nothing improper about it. – Dima Tisnek Sep 13 '16 at 11:42
  • one other system call you may use is `statfs`, there are wrapper for that in libc `statfs/fstatfs/statfs64/fstatfs64` which you can reach via `ctypes.CDLL(None)` as libc is already loaded in the current process. – Dima Tisnek Sep 13 '16 at 11:44
  • @qarma `statfs` does not return useful information in my case. BTW I especially like that quote from the man page: "Nobody knows what f_fsid is supposed to contain (but see below)." – code_onkel Sep 13 '16 at 11:49

4 Answers4

3

This is a script on GitHub I came across earlier today that uses Python to fetch information about drive make and model (and lots of others).

/proc/partitions holds info on partitions; for more detailed information you'll have either to run a subprocess as you do now, or for example for GPT do the parsing yourself.

LSerni
  • 55,617
  • 10
  • 65
  • 107
  • I awarded the bounty to you, because your answer contained the most useful information for me (or pointed me to it). However, I will not accept it, because there is no definitive answer regarding best practices or a canonical solution. By now, I even doubt that I will get such an answer on SO. Mainly because it is such a specific topic. – code_onkel Sep 19 '16 at 09:22
  • Thanks, and I agree on not accepting the answer. Possibly you could ask on SuperUser? – LSerni Sep 19 '16 at 09:28
  • I will probably leave it at that for now. My current solution seems to work well, though I do not like it very much. By taking a look at the alternatives (e.g. doing fancy `ioctl` calls like `hdparm` or interacting with `dbus` or `udev` directly) parsing `/proc` and the output of `udevadm` seems like an OK idea. – code_onkel Sep 19 '16 at 09:43
2

The best way to do that is via pyparted. Pyparted are python bindings to Parted maintained by one of the Parted developers. You can find the source code on github. Unfortunately UUID's cannot be found from parted so you have to parse blkid

Andrei Tumbar
  • 490
  • 6
  • 15
1

That would imply some utilities and anlyze output. Launched using os.popen, you can use bklkid to find uuids, reading /etc/mtab file permits to know which filesystem are mounted in your system and thus mountpoints. sfdisk -l output gives you disk anatomy.hdparm -I /dev/xxx will give you serial numbers.

pacholik
  • 8,607
  • 9
  • 43
  • 55
sancelot
  • 1,905
  • 12
  • 31
0

Get disk infos from a filepath, say /dir/file001.txt

#!/usr/bin/python3

import os
from os.path import expanduser


import shutil
from sh import mount

from subprocess import *
import subprocess




def get_disk_info_of_a_filepath():

  #example1
  #current path='/'
  device = subprocess.check_output("awk '$2 == \"/\" { print $1}' /proc/mounts", shell=True)
  device_decode=str(device.decode())
  #device_decode = device_decode.split('\n')
  device_decode = device_decode[0:-1]
  print('root. device_decode: ', device_decode)
  print('root. sd: ', device_decode[-5:])
  print('root. letter: ', device_decode[-2])



  #example2
  #current path='/media/ubuntu/workspace_001_p2'

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

  device_decode=str(device.decode())
  #device_decode = device_decode.split('\n')
  device_decode = device_decode[0:-1]
  print('root. device_decode: ', device_decode)
  print('root. sd: ', device_decode[-5:])
  print('root. letter: ', device_decode[-2])

  return



get_disk_info_of_a_filepath()

quine9997
  • 685
  • 7
  • 13