• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2012 Intel Corporation; All Rights Reserved
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12 
13 #include <stdio.h>
14 #include <string.h>
15 #include <limits.h>
16 #include <stdlib.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <sys/sysmacros.h>
21 #include "mountinfo.h"
22 
23 /*
24  * Parse /proc/self/mountinfo
25  */
get_string(FILE * f,char * string_buf,size_t string_len,char * ec)26 static int get_string(FILE *f, char *string_buf, size_t string_len, char *ec)
27 {
28     int ch;
29     char *p = string_buf;
30 
31     for (;;) {
32 	if (!string_len)
33 	    return -2;		/* String too long */
34 
35 	ch = getc(f);
36 	if (ch == EOF) {
37 	    return -1;		/* Got EOF */
38 	} else if (ch == ' ' || ch == '\t' || ch == '\n') {
39 	    *ec = ch;
40 	    *p = '\0';
41 	    return p - string_buf;
42 	} else if (ch == '\\') {
43 	    /* Should always be followed by 3 octal digits in 000..377 */
44 	    int oc = 0;
45 	    int i;
46 	    for (i = 0; i < 3; i++) {
47 		ch = getc(f);
48 		if (ch < '0' || ch > '7' || (i == 0 && ch > '3'))
49 		    return -1;	/* Bad escape sequence */
50 		oc = (oc << 3) + (ch - '0');
51 	    }
52 	    if (!oc)
53 		return -1;	/* We can't handle \000 */
54 	    *p++ = oc;
55 	    string_len--;
56 	} else {
57 	    *p++ = ch;
58 	    string_len--;
59 	}
60     }
61 }
62 
free_mountinfo(struct mountinfo * m)63 static void free_mountinfo(struct mountinfo *m)
64 {
65     struct mountinfo *nx;
66 
67     while (m) {
68 	free((char *)m->root);
69 	free((char *)m->path);
70 	free((char *)m->fstype);
71 	free((char *)m->devpath);
72 	free((char *)m->mountopt);
73 	nx = m->next;
74 	free(m);
75 	m = nx;
76     }
77 }
78 
79 static struct mountinfo *head = NULL, **tail = &head;
80 
parse_mountinfo(void)81 static void parse_mountinfo(void)
82 {
83     FILE *f;
84     struct mountinfo *m, *mm;
85     char string_buf[PATH_MAX*8];
86     int n;
87     char ec, *ep;
88     unsigned int ma, mi;
89 
90     f = fopen("/proc/self/mountinfo", "r");
91     if (!f)
92 	return;
93 
94     for (;;) {
95 	m = malloc(sizeof(struct mountinfo));
96 	if (!m)
97 	    break;
98 	memset(m, 0, sizeof *m);
99 
100 	n = get_string(f, string_buf, sizeof string_buf, &ec);
101 	if (n < 0 || ec == '\n')
102 	    break;
103 
104 	m->mountid = strtoul(string_buf, &ep, 10);
105 	if (*ep)
106 	    break;
107 
108 	n = get_string(f, string_buf, sizeof string_buf, &ec);
109 	if (n < 0 || ec == '\n')
110 	    break;
111 
112 	m->parentid = strtoul(string_buf, &ep, 10);
113 	if (*ep)
114 	    break;
115 
116 	n = get_string(f, string_buf, sizeof string_buf, &ec);
117 	if (n < 0 || ec == '\n')
118 	    break;
119 
120 	if (sscanf(string_buf, "%u:%u", &ma, &mi) != 2)
121 	    break;
122 
123 	m->dev = makedev(ma, mi);
124 
125 	n = get_string(f, string_buf, sizeof string_buf, &ec);
126 	if (n < 1 || ec == '\n' || string_buf[0] != '/')
127 	    break;
128 
129 	m->root = strdup(string_buf);
130 	if (!m->root)
131 	    break;
132 
133 	n = get_string(f, string_buf, sizeof string_buf, &ec);
134 	if (n < 1 || ec == '\n' || string_buf[0] != '/')
135 	    break;
136 
137 	m->path = strdup(string_buf);
138 	m->pathlen = (n == 1) ? 0 : n; /* Treat / as empty */
139 
140 	/* Skip tagged attributes */
141 	do {
142 	    n = get_string(f, string_buf, sizeof string_buf, &ec);
143 	    if (n < 0 || ec == '\n')
144 		goto quit;
145 	} while (n != 1 || string_buf[0] != '-');
146 
147 	n = get_string(f, string_buf, sizeof string_buf, &ec);
148 	if (n < 0 || ec == '\n')
149 	    break;
150 
151 	m->fstype = strdup(string_buf);
152 	if (!m->fstype)
153 	    break;
154 
155 	n = get_string(f, string_buf, sizeof string_buf, &ec);
156 	if (n < 0 || ec == '\n')
157 	    break;
158 
159 	m->devpath = strdup(string_buf);
160 	if (!m->devpath)
161 	    break;
162 
163 	n = get_string(f, string_buf, sizeof string_buf, &ec);
164 	if (n < 0)
165 	    break;
166 
167 	m->mountopt = strdup(string_buf);
168 	if (!m->mountopt)
169 	    break;
170 
171 	/* Skip any previously unknown fields */
172 	while (ec != '\n' && ec != EOF)
173 	    ec = getc(f);
174 
175 	*tail = m;
176 	tail = &m->next;
177     }
178 quit:
179     fclose(f);
180     free_mountinfo(m);
181 
182     /* Create parent links */
183     for (m = head; m; m = m->next) {
184 	for (mm = head; mm; mm = mm->next) {
185 	    if (m->parentid == mm->mountid) {
186 		m->parent = mm;
187 		if (!strcmp(m->path, mm->path))
188 		    mm->hidden = 1; /* Hidden under another mount */
189 		break;
190 	    }
191 	}
192     }
193 }
194 
find_mount(const char * path,char ** subpath)195 const struct mountinfo *find_mount(const char *path, char **subpath)
196 {
197     static int done_init;
198     char *real_path;
199     const struct mountinfo *m, *best;
200     struct stat st;
201     int len, matchlen;
202 
203     if (!done_init) {
204 	parse_mountinfo();
205 	done_init = 1;
206     }
207 
208     if (stat(path, &st))
209 	return NULL;
210 
211     real_path = realpath(path, NULL);
212     if (!real_path)
213 	return NULL;
214 
215     /*
216      * Tricky business: we need the longest matching subpath
217      * which isn't a parent of the same subpath.
218      */
219     len = strlen(real_path);
220     matchlen = 0;
221     best = NULL;
222     for (m = head; m; m = m->next) {
223 	if (m->hidden)
224 	    continue;		/* Hidden underneath another mount */
225 
226 	if (m->pathlen > len)
227 	    continue;		/* Cannot possibly match */
228 
229 	if (m->pathlen < matchlen)
230 	    continue;		/* No point in testing this one */
231 
232 	if (st.st_dev == m->dev &&
233 	    !memcmp(m->path, real_path, m->pathlen) &&
234 	    (real_path[m->pathlen] == '/' || real_path[m->pathlen] == '\0')) {
235 	    matchlen = m->pathlen;
236 	    best = m;
237 	}
238     }
239 
240     if (best && subpath) {
241 	if (real_path[best->pathlen] == '\0')
242 	    *subpath = strdup("/");
243 	else
244 	    *subpath = strdup(real_path + best->pathlen);
245     }
246 
247     return best;
248 }
249 
250 #ifdef TEST
251 
main(int argc,char * argv[])252 int main(int argc, char *argv[])
253 {
254     int i;
255     const struct mountinfo *m;
256     char *subpath;
257 
258     parse_mountinfo();
259 
260     for (i = 1; i < argc; i++) {
261 	m = find_mount(argv[i], &subpath);
262 	if (!m) {
263 	    printf("%s: %s\n", argv[i], strerror(errno));
264 	    continue;
265 	}
266 
267 	printf("%s -> %s @ %s(%u,%u):%s %s %s\n",
268 	       argv[i], subpath, m->devpath, major(m->dev), minor(m->dev),
269 	       m->root, m->fstype, m->mountopt);
270 	printf("Usable device: %s\n", find_device(m->dev, m->devpath));
271 	free(subpath);
272     }
273 
274     return 0;
275 }
276 
277 #endif
278