278 lines
5.9 KiB
C
278 lines
5.9 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 2012 Intel Corporation; All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston MA 02110-1301, USA; either version 2 of the License, or
|
|
* (at your option) any later version; incorporated herein by reference.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysmacros.h>
|
|
#include "mountinfo.h"
|
|
|
|
/*
|
|
* Parse /proc/self/mountinfo
|
|
*/
|
|
static int get_string(FILE *f, char *string_buf, size_t string_len, char *ec)
|
|
{
|
|
int ch;
|
|
char *p = string_buf;
|
|
|
|
for (;;) {
|
|
if (!string_len)
|
|
return -2; /* String too long */
|
|
|
|
ch = getc(f);
|
|
if (ch == EOF) {
|
|
return -1; /* Got EOF */
|
|
} else if (ch == ' ' || ch == '\t' || ch == '\n') {
|
|
*ec = ch;
|
|
*p = '\0';
|
|
return p - string_buf;
|
|
} else if (ch == '\\') {
|
|
/* Should always be followed by 3 octal digits in 000..377 */
|
|
int oc = 0;
|
|
int i;
|
|
for (i = 0; i < 3; i++) {
|
|
ch = getc(f);
|
|
if (ch < '0' || ch > '7' || (i == 0 && ch > '3'))
|
|
return -1; /* Bad escape sequence */
|
|
oc = (oc << 3) + (ch - '0');
|
|
}
|
|
if (!oc)
|
|
return -1; /* We can't handle \000 */
|
|
*p++ = oc;
|
|
string_len--;
|
|
} else {
|
|
*p++ = ch;
|
|
string_len--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void free_mountinfo(struct mountinfo *m)
|
|
{
|
|
struct mountinfo *nx;
|
|
|
|
while (m) {
|
|
free((char *)m->root);
|
|
free((char *)m->path);
|
|
free((char *)m->fstype);
|
|
free((char *)m->devpath);
|
|
free((char *)m->mountopt);
|
|
nx = m->next;
|
|
free(m);
|
|
m = nx;
|
|
}
|
|
}
|
|
|
|
static struct mountinfo *head = NULL, **tail = &head;
|
|
|
|
static void parse_mountinfo(void)
|
|
{
|
|
FILE *f;
|
|
struct mountinfo *m, *mm;
|
|
char string_buf[PATH_MAX*8];
|
|
int n;
|
|
char ec, *ep;
|
|
unsigned int ma, mi;
|
|
|
|
f = fopen("/proc/self/mountinfo", "r");
|
|
if (!f)
|
|
return;
|
|
|
|
for (;;) {
|
|
m = malloc(sizeof(struct mountinfo));
|
|
if (!m)
|
|
break;
|
|
memset(m, 0, sizeof *m);
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 0 || ec == '\n')
|
|
break;
|
|
|
|
m->mountid = strtoul(string_buf, &ep, 10);
|
|
if (*ep)
|
|
break;
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 0 || ec == '\n')
|
|
break;
|
|
|
|
m->parentid = strtoul(string_buf, &ep, 10);
|
|
if (*ep)
|
|
break;
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 0 || ec == '\n')
|
|
break;
|
|
|
|
if (sscanf(string_buf, "%u:%u", &ma, &mi) != 2)
|
|
break;
|
|
|
|
m->dev = makedev(ma, mi);
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 1 || ec == '\n' || string_buf[0] != '/')
|
|
break;
|
|
|
|
m->root = strdup(string_buf);
|
|
if (!m->root)
|
|
break;
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 1 || ec == '\n' || string_buf[0] != '/')
|
|
break;
|
|
|
|
m->path = strdup(string_buf);
|
|
m->pathlen = (n == 1) ? 0 : n; /* Treat / as empty */
|
|
|
|
/* Skip tagged attributes */
|
|
do {
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 0 || ec == '\n')
|
|
goto quit;
|
|
} while (n != 1 || string_buf[0] != '-');
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 0 || ec == '\n')
|
|
break;
|
|
|
|
m->fstype = strdup(string_buf);
|
|
if (!m->fstype)
|
|
break;
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 0 || ec == '\n')
|
|
break;
|
|
|
|
m->devpath = strdup(string_buf);
|
|
if (!m->devpath)
|
|
break;
|
|
|
|
n = get_string(f, string_buf, sizeof string_buf, &ec);
|
|
if (n < 0)
|
|
break;
|
|
|
|
m->mountopt = strdup(string_buf);
|
|
if (!m->mountopt)
|
|
break;
|
|
|
|
/* Skip any previously unknown fields */
|
|
while (ec != '\n' && ec != EOF)
|
|
ec = getc(f);
|
|
|
|
*tail = m;
|
|
tail = &m->next;
|
|
}
|
|
quit:
|
|
fclose(f);
|
|
free_mountinfo(m);
|
|
|
|
/* Create parent links */
|
|
for (m = head; m; m = m->next) {
|
|
for (mm = head; mm; mm = mm->next) {
|
|
if (m->parentid == mm->mountid) {
|
|
m->parent = mm;
|
|
if (!strcmp(m->path, mm->path))
|
|
mm->hidden = 1; /* Hidden under another mount */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const struct mountinfo *find_mount(const char *path, char **subpath)
|
|
{
|
|
static int done_init;
|
|
char *real_path;
|
|
const struct mountinfo *m, *best;
|
|
struct stat st;
|
|
int len, matchlen;
|
|
|
|
if (!done_init) {
|
|
parse_mountinfo();
|
|
done_init = 1;
|
|
}
|
|
|
|
if (stat(path, &st))
|
|
return NULL;
|
|
|
|
real_path = realpath(path, NULL);
|
|
if (!real_path)
|
|
return NULL;
|
|
|
|
/*
|
|
* Tricky business: we need the longest matching subpath
|
|
* which isn't a parent of the same subpath.
|
|
*/
|
|
len = strlen(real_path);
|
|
matchlen = 0;
|
|
best = NULL;
|
|
for (m = head; m; m = m->next) {
|
|
if (m->hidden)
|
|
continue; /* Hidden underneath another mount */
|
|
|
|
if (m->pathlen > len)
|
|
continue; /* Cannot possibly match */
|
|
|
|
if (m->pathlen < matchlen)
|
|
continue; /* No point in testing this one */
|
|
|
|
if (st.st_dev == m->dev &&
|
|
!memcmp(m->path, real_path, m->pathlen) &&
|
|
(real_path[m->pathlen] == '/' || real_path[m->pathlen] == '\0')) {
|
|
matchlen = m->pathlen;
|
|
best = m;
|
|
}
|
|
}
|
|
|
|
if (best && subpath) {
|
|
if (real_path[best->pathlen] == '\0')
|
|
*subpath = strdup("/");
|
|
else
|
|
*subpath = strdup(real_path + best->pathlen);
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
#ifdef TEST
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
const struct mountinfo *m;
|
|
char *subpath;
|
|
|
|
parse_mountinfo();
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
m = find_mount(argv[i], &subpath);
|
|
if (!m) {
|
|
printf("%s: %s\n", argv[i], strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
printf("%s -> %s @ %s(%u,%u):%s %s %s\n",
|
|
argv[i], subpath, m->devpath, major(m->dev), minor(m->dev),
|
|
m->root, m->fstype, m->mountopt);
|
|
printf("Usable device: %s\n", find_device(m->dev, m->devpath));
|
|
free(subpath);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|