efi/udp.c: Save UDP Port in core_udp_open()
[syslinux/sherbszt.git] / extlinux / mountinfo.c
blob2be87580cd241dad73b8b81b33115b6298457d1b
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2012 Intel Corporation; All Rights Reserved
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.
11 * ----------------------------------------------------------------------- */
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"
24 * Parse /proc/self/mountinfo
26 static int get_string(FILE *f, char *string_buf, size_t string_len, char *ec)
28 int ch;
29 char *p = string_buf;
31 for (;;) {
32 if (!string_len)
33 return -2; /* String too long */
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');
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--;
63 static void free_mountinfo(struct mountinfo *m)
65 struct mountinfo *nx;
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;
79 static struct mountinfo *head = NULL, **tail = &head;
81 static void parse_mountinfo(void)
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;
90 f = fopen("/proc/self/mountinfo", "r");
91 if (!f)
92 return;
94 for (;;) {
95 m = malloc(sizeof(struct mountinfo));
96 if (!m)
97 break;
98 memset(m, 0, sizeof *m);
100 n = get_string(f, string_buf, sizeof string_buf, &ec);
101 if (n < 0 || ec == '\n')
102 break;
104 m->mountid = strtoul(string_buf, &ep, 10);
105 if (*ep)
106 break;
108 n = get_string(f, string_buf, sizeof string_buf, &ec);
109 if (n < 0 || ec == '\n')
110 break;
112 m->parentid = strtoul(string_buf, &ep, 10);
113 if (*ep)
114 break;
116 n = get_string(f, string_buf, sizeof string_buf, &ec);
117 if (n < 0 || ec == '\n')
118 break;
120 if (sscanf(string_buf, "%u:%u", &ma, &mi) != 2)
121 break;
123 m->dev = makedev(ma, mi);
125 n = get_string(f, string_buf, sizeof string_buf, &ec);
126 if (n < 1 || ec == '\n' || string_buf[0] != '/')
127 break;
129 m->root = strdup(string_buf);
130 if (!m->root)
131 break;
133 n = get_string(f, string_buf, sizeof string_buf, &ec);
134 if (n < 1 || ec == '\n' || string_buf[0] != '/')
135 break;
137 m->path = strdup(string_buf);
138 m->pathlen = (n == 1) ? 0 : n; /* Treat / as empty */
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] != '-');
147 n = get_string(f, string_buf, sizeof string_buf, &ec);
148 if (n < 0 || ec == '\n')
149 break;
151 m->fstype = strdup(string_buf);
152 if (!m->fstype)
153 break;
155 n = get_string(f, string_buf, sizeof string_buf, &ec);
156 if (n < 0 || ec == '\n')
157 break;
159 m->devpath = strdup(string_buf);
160 if (!m->devpath)
161 break;
163 n = get_string(f, string_buf, sizeof string_buf, &ec);
164 if (n < 0)
165 break;
167 m->mountopt = strdup(string_buf);
168 if (!m->mountopt)
169 break;
171 /* Skip any previously unknown fields */
172 while (ec != '\n' && ec != EOF)
173 ec = getc(f);
175 *tail = m;
176 tail = &m->next;
178 quit:
179 fclose(f);
180 free_mountinfo(m);
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;
195 const struct mountinfo *find_mount(const char *path, char **subpath)
197 static int done_init;
198 char *real_path;
199 const struct mountinfo *m, *best;
200 struct stat st;
201 int len, matchlen;
203 if (!done_init) {
204 parse_mountinfo();
205 done_init = 1;
208 if (stat(path, &st))
209 return NULL;
211 real_path = realpath(path, NULL);
212 if (!real_path)
213 return NULL;
216 * Tricky business: we need the longest matching subpath
217 * which isn't a parent of the same subpath.
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 */
226 if (m->pathlen > len)
227 continue; /* Cannot possibly match */
229 if (m->pathlen < matchlen)
230 continue; /* No point in testing this one */
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;
240 if (best && subpath) {
241 if (real_path[best->pathlen] == '\0')
242 *subpath = strdup("/");
243 else
244 *subpath = strdup(real_path + best->pathlen);
247 return best;
250 #ifdef TEST
252 int main(int argc, char *argv[])
254 int i;
255 const struct mountinfo *m;
256 char *subpath;
258 parse_mountinfo();
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;
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);
274 return 0;
277 #endif