9

Let's say I have a file in Linux with this path:

/path/to/file/test.mp3

I want to know the path to its device. For example I want to get something like:

/dev/sdb1

How do I do this with the C programming language?

I know the terminal command to do it, but I need C functions that will do the job.

EDIT: I have read this question before asking mine. It doesn't concretly mention code in C, it's more related to bash than to the C language.

Thanks.

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Mehdi Ijadnazar
  • 4,532
  • 4
  • 35
  • 35

3 Answers3

5

I just needed that inside a program I am writing...

So instead of running "df" and parsing the output, I wrote it from scratch.

Feel free to contribute!

To answer the question:

You first find the device inode using stat() then iterate and parse /proc/self/mountinfo to find the inode and get the device name.

/*
Get physical device from file or directory name.
By Zibri <zibri AT zibri DOT org>
https://github.com/Zibri/get_device
*/

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <libgen.h>

int get_device(char *name)
{
  struct stat fs;

  if (stat(name, &fs) < 0) {
    fprintf(stderr, "%s: No such file or directory\n", name);
    return -1;
  }

  FILE *f;
  char sline[256];
  char minmaj[128];

  sprintf(minmaj, "%d:%d ", (int) fs.st_dev >> 8, (int) fs.st_dev & 0xff);

  f = fopen("/proc/self/mountinfo", "r");

  if (f == NULL) {
    fprintf(stderr, "Failed to open /proc/self/mountinfo\n");
    exit(-1);
  }

  while (fgets(sline, 256, f)) {

    char *token;
    char *where;

    token = strtok(sline, "-");
    where = strstr(token, minmaj);
    if (where) {
      token = strtok(NULL, " -:");
      token = strtok(NULL, " -:");
      printf("%s\n", token);
      break;
    }

  }

  fclose(f);

  return -1;
}

int main(int argc, char **argv)
{

  if (argc != 2) {
    fprintf(stderr, "Usage:\n%s FILE OR DIRECTORY...\n", basename(argv[0]));
    return -1;
  }
  get_device(argv[1]);
  return 0;
}

output is just the device name.

Example:

$ gcc -O3 getdevice.c -o gd -Wall
$ ./gd .
/dev/sda4
$ ./gd /mnt/C
/dev/sda3
$ ./gd /mnt/D
/dev/sdb1
$
Phidelux
  • 2,043
  • 1
  • 32
  • 50
Zibri
  • 9,096
  • 3
  • 52
  • 44
  • Instead of a loop and string-matching, I think you can simply call `readlink` on `/sys/dev/block/:`. The major and minor numbers are in decimal representation in this path. – mxmlnkn Jun 04 '23 at 12:54
  • true, but that is not always present (missing for example in WSL1) – Zibri Jun 18 '23 at 11:23
4

You need to use stat on the file path, and get the device ID st_dev and match that to a device in /proc/partitions

Read this for how to interpret st_dev: https://web.archive.org/web/20171013194110/http://www.makelinux.net:80/ldd3/chp-3-sect-2

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Salgar
  • 7,687
  • 1
  • 25
  • 39
  • Beat me by seconds:D Anyhow duuno why this was closed as a duplicate, your answer is correct regarding C, voting to reopen:) – Rudolfs Bundulis Jul 27 '16 at 14:29
  • I know I should do the stat, but I don't know how to match the result to a device in /proc/partitions, could you edit your answer to cover that? – Mehdi Ijadnazar Jul 27 '16 at 14:44
  • Necro comment, but for future people browsing like me: this is how you iterate over /proc/mounts or /proc/partitions https://stackoverflow.com/questions/9280759/linux-function-to-get-mount-points – RichieSams Apr 25 '18 at 21:18
3

Use this command to print the partition path:

df -P <pathname> | awk 'END{print $1}'
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
mohtashami740
  • 336
  • 2
  • 6