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 */ 26static 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 63static 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 79static struct mountinfo *head = NULL, **tail = &head; 80 81static 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 } 178quit: 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 195const 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 252int 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