]>
Commit | Line | Data |
---|---|---|
6bd8b7a7 KZ |
1 | /* |
2 | * Copyright (C) 2009 Karel Zak <kzak@redhat.com> | |
3 | * | |
4 | * This file may be redistributed under the terms of the | |
5 | * GNU Lesser General Public License. | |
6 | */ | |
7 | ||
485a8bfa MY |
8 | #ifdef HAVE_SCANDIRAT |
9 | #ifndef __USE_GNU | |
10 | #define __USE_GNU | |
11 | #endif /* !__USE_GNU */ | |
12 | #endif /* HAVE_SCANDIRAT */ | |
13 | ||
6bd8b7a7 KZ |
14 | #include <ctype.h> |
15 | #include <limits.h> | |
9826a637 KZ |
16 | #include <dirent.h> |
17 | #include <fcntl.h> | |
2208b3cc | 18 | #include <sys/stat.h> |
6bd8b7a7 | 19 | |
1a048dc5 | 20 | #include "fileutils.h" |
7043ced3 | 21 | #include "mangle.h" |
6bd8b7a7 | 22 | #include "mountP.h" |
9826a637 | 23 | #include "pathnames.h" |
b106d052 | 24 | #include "strutils.h" |
6bd8b7a7 | 25 | |
d92b8c3b KZ |
26 | struct libmnt_parser { |
27 | FILE *f; /* fstab, mtab, swaps or mountinfo ... */ | |
928f209c | 28 | const char *filename; /* file name or NULL */ |
d92b8c3b KZ |
29 | char *buf; /* buffer (the current line content) */ |
30 | size_t bufsiz; /* size of the buffer */ | |
31 | size_t line; /* current line */ | |
32 | }; | |
33 | ||
34 | static void parser_cleanup(struct libmnt_parser *pa) | |
35 | { | |
36 | if (!pa) | |
37 | return; | |
38 | free(pa->buf); | |
39 | memset(pa, 0, sizeof(*pa)); | |
40 | } | |
41 | ||
c3b0d5b3 KZ |
42 | static int next_number(char **s, int *num) |
43 | { | |
44 | char *end = NULL; | |
45 | ||
46 | assert(num); | |
47 | assert(s); | |
48 | ||
675de3f5 | 49 | *s = (char *) skip_blank(*s); |
c3b0d5b3 KZ |
50 | if (!**s) |
51 | return -1; | |
52 | *num = strtol(*s, &end, 10); | |
53 | if (end == NULL || *s == end) | |
54 | return -1; | |
55 | ||
56 | *s = end; | |
57 | ||
d58b3157 | 58 | /* valid end of number is a space or a terminator */ |
c3b0d5b3 KZ |
59 | if (*end == ' ' || *end == '\t' || *end == '\0') |
60 | return 0; | |
61 | return -1; | |
62 | } | |
63 | ||
6bd8b7a7 KZ |
64 | /* |
65 | * Parses one line from {fs,m}tab | |
66 | */ | |
68164f6c | 67 | static int mnt_parse_table_line(struct libmnt_fs *fs, char *s) |
6bd8b7a7 | 68 | { |
1e8c9369 KZ |
69 | int rc, n = 0, xrc; |
70 | char *src = NULL, *fstype = NULL, *optstr = NULL; | |
8f3f6383 | 71 | |
640fc1b8 KZ |
72 | rc = sscanf(s, UL_SCNsA" " /* (1) source */ |
73 | UL_SCNsA" " /* (2) target */ | |
74 | UL_SCNsA" " /* (3) FS type */ | |
75 | UL_SCNsA" " /* (4) options */ | |
76 | "%n", /* byte count */ | |
77 | ||
8f3f6383 KZ |
78 | &src, |
79 | &fs->target, | |
80 | &fstype, | |
81 | &optstr, | |
c3b0d5b3 | 82 | &n); |
1e8c9369 | 83 | xrc = rc; |
8f3f6383 | 84 | |
1e8c9369 | 85 | if (rc == 3 || rc == 4) { /* options are optional */ |
8f3f6383 KZ |
86 | unmangle_string(src); |
87 | unmangle_string(fs->target); | |
88 | unmangle_string(fstype); | |
1e8c9369 KZ |
89 | |
90 | if (optstr && *optstr) | |
91 | unmangle_string(optstr); | |
8f3f6383 | 92 | |
d58b3157 | 93 | /* note that __foo functions do not reallocate the string |
53d91134 | 94 | */ |
8f3f6383 | 95 | rc = __mnt_fs_set_source_ptr(fs, src); |
53d91134 KZ |
96 | if (!rc) { |
97 | src = NULL; | |
8f3f6383 | 98 | rc = __mnt_fs_set_fstype_ptr(fs, fstype); |
53d91134 KZ |
99 | if (!rc) |
100 | fstype = NULL; | |
101 | } | |
1e8c9369 | 102 | if (!rc && optstr) |
76a06ca4 KZ |
103 | rc = mnt_fs_set_options(fs, optstr); |
104 | free(optstr); | |
53d91134 | 105 | optstr = NULL; |
8f3f6383 | 106 | } else { |
83a78332 | 107 | DBG(TAB, ul_debug("tab parse error: [sscanf rc=%d]: '%s'", rc, s)); |
8f3f6383 | 108 | rc = -EINVAL; |
b2d0b74d | 109 | } |
6bd8b7a7 | 110 | |
1e8c9369 | 111 | if (rc) { |
53d91134 KZ |
112 | free(src); |
113 | free(fstype); | |
114 | free(optstr); | |
83a78332 | 115 | DBG(TAB, ul_debug("tab parse error: [set vars, rc=%d]\n", rc)); |
c3b0d5b3 | 116 | return rc; /* error */ |
1e8c9369 | 117 | } |
c3b0d5b3 KZ |
118 | |
119 | fs->passno = fs->freq = 0; | |
1e8c9369 KZ |
120 | |
121 | if (xrc == 4 && n) | |
675de3f5 | 122 | s = (char *) skip_blank(s + n); |
1e8c9369 | 123 | if (xrc == 4 && *s) { |
c3b0d5b3 | 124 | if (next_number(&s, &fs->freq) != 0) { |
dd369652 | 125 | if (*s) { |
83a78332 | 126 | DBG(TAB, ul_debug("tab parse error: [freq]")); |
c3b0d5b3 | 127 | rc = -EINVAL; |
dd369652 KZ |
128 | } |
129 | } else if (next_number(&s, &fs->passno) != 0 && *s) { | |
83a78332 | 130 | DBG(TAB, ul_debug("tab parse error: [passno]")); |
c3b0d5b3 | 131 | rc = -EINVAL; |
dd369652 | 132 | } |
c3b0d5b3 KZ |
133 | } |
134 | ||
b2d0b74d | 135 | return rc; |
6bd8b7a7 KZ |
136 | } |
137 | ||
138 | /* | |
d58b3157 | 139 | * Parses one line from a mountinfo file |
6bd8b7a7 | 140 | */ |
68164f6c | 141 | static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) |
6bd8b7a7 | 142 | { |
fb9d90d5 | 143 | int rc, end = 0; |
efe73c3e | 144 | unsigned int maj, min; |
53d91134 | 145 | char *fstype = NULL, *src = NULL, *p; |
8f3f6383 | 146 | |
ea97feab KZ |
147 | rc = sscanf(s, "%d " /* (1) id */ |
148 | "%d " /* (2) parent */ | |
8f3f6383 | 149 | "%u:%u " /* (3) maj:min */ |
640fc1b8 KZ |
150 | UL_SCNsA" " /* (4) mountroot */ |
151 | UL_SCNsA" " /* (5) target */ | |
152 | UL_SCNsA /* (6) vfs options (fs-independent) */ | |
fb9d90d5 | 153 | "%n", /* number of read bytes */ |
8f3f6383 KZ |
154 | |
155 | &fs->id, | |
156 | &fs->parent, | |
157 | &maj, &min, | |
158 | &fs->root, | |
159 | &fs->target, | |
160 | &fs->vfs_optstr, | |
fb9d90d5 KZ |
161 | &end); |
162 | ||
163 | if (rc >= 7 && end > 0) | |
164 | s += end; | |
165 | ||
166 | /* (7) optional fields, terminated by " - " */ | |
167 | p = strstr(s, " - "); | |
168 | if (!p) { | |
83a78332 | 169 | DBG(TAB, ul_debug("mountinfo parse error: separator not found")); |
fb9d90d5 KZ |
170 | return -EINVAL; |
171 | } | |
e47a1931 OO |
172 | if (p > s + 1) |
173 | fs->opt_fields = strndup(s + 1, p - s - 1); | |
fb9d90d5 KZ |
174 | s = p + 3; |
175 | ||
640fc1b8 KZ |
176 | rc += sscanf(s, UL_SCNsA" " /* (8) FS type */ |
177 | UL_SCNsA" " /* (9) source */ | |
178 | UL_SCNsA, /* (10) fs options (fs specific) */ | |
fb9d90d5 | 179 | |
8f3f6383 KZ |
180 | &fstype, |
181 | &src, | |
182 | &fs->fs_optstr); | |
183 | ||
fb9d90d5 | 184 | if (rc >= 10) { |
5980048e KZ |
185 | size_t sz; |
186 | ||
309139c7 | 187 | fs->flags |= MNT_FS_KERNEL; |
8f3f6383 | 188 | fs->devno = makedev(maj, min); |
efe73c3e | 189 | |
5980048e KZ |
190 | /* remove "(deleted)" suffix */ |
191 | sz = strlen(fs->target); | |
192 | if (sz > PATH_DELETED_SUFFIX_SZ) { | |
7ee26cbf | 193 | char *ptr = fs->target + (sz - PATH_DELETED_SUFFIX_SZ); |
5980048e | 194 | |
7ee26cbf SK |
195 | if (strcmp(ptr, PATH_DELETED_SUFFIX) == 0) |
196 | *ptr = '\0'; | |
5980048e KZ |
197 | } |
198 | ||
8f3f6383 KZ |
199 | unmangle_string(fs->root); |
200 | unmangle_string(fs->target); | |
201 | unmangle_string(fs->vfs_optstr); | |
202 | unmangle_string(fstype); | |
5af0769d | 203 | unmangle_string(src); |
d0ce7c07 | 204 | unmangle_string(fs->fs_optstr); |
b2d0b74d | 205 | |
8f3f6383 | 206 | rc = __mnt_fs_set_fstype_ptr(fs, fstype); |
53d91134 KZ |
207 | if (!rc) { |
208 | fstype = NULL; | |
8f3f6383 | 209 | rc = __mnt_fs_set_source_ptr(fs, src); |
53d91134 KZ |
210 | if (!rc) |
211 | src = NULL; | |
212 | } | |
f2b3a3a3 | 213 | |
d58b3157 | 214 | /* merge VFS and FS options to one string */ |
f2b3a3a3 KZ |
215 | fs->optstr = mnt_fs_strdup_options(fs); |
216 | if (!fs->optstr) | |
217 | rc = -ENOMEM; | |
8f3f6383 | 218 | } else { |
53d91134 KZ |
219 | free(fstype); |
220 | free(src); | |
83a78332 | 221 | DBG(TAB, ul_debug( |
dd369652 | 222 | "mountinfo parse error [sscanf rc=%d]: '%s'", rc, s)); |
8f3f6383 | 223 | rc = -EINVAL; |
be1a5180 | 224 | } |
b2d0b74d | 225 | return rc; |
6bd8b7a7 KZ |
226 | } |
227 | ||
dd369652 KZ |
228 | /* |
229 | * Parses one line from utab file | |
230 | */ | |
68164f6c | 231 | static int mnt_parse_utab_line(struct libmnt_fs *fs, const char *s) |
dd369652 KZ |
232 | { |
233 | const char *p = s; | |
234 | ||
235 | assert(fs); | |
236 | assert(s); | |
237 | assert(!fs->source); | |
238 | assert(!fs->target); | |
239 | ||
240 | while (p && *p) { | |
5ac6a133 KZ |
241 | char *end = NULL; |
242 | ||
dd369652 KZ |
243 | while (*p == ' ') p++; |
244 | if (!*p) | |
245 | break; | |
246 | ||
247 | if (!fs->source && !strncmp(p, "SRC=", 4)) { | |
5ac6a133 | 248 | char *v = unmangle(p + 4, &end); |
dd369652 KZ |
249 | if (!v) |
250 | goto enomem; | |
5af0769d | 251 | __mnt_fs_set_source_ptr(fs, v); |
dd369652 KZ |
252 | |
253 | } else if (!fs->target && !strncmp(p, "TARGET=", 7)) { | |
5ac6a133 | 254 | fs->target = unmangle(p + 7, &end); |
dd369652 KZ |
255 | if (!fs->target) |
256 | goto enomem; | |
257 | ||
258 | } else if (!fs->root && !strncmp(p, "ROOT=", 5)) { | |
5ac6a133 | 259 | fs->root = unmangle(p + 5, &end); |
dd369652 KZ |
260 | if (!fs->root) |
261 | goto enomem; | |
262 | ||
263 | } else if (!fs->bindsrc && !strncmp(p, "BINDSRC=", 8)) { | |
5ac6a133 | 264 | fs->bindsrc = unmangle(p + 8, &end); |
dd369652 KZ |
265 | if (!fs->bindsrc) |
266 | goto enomem; | |
267 | ||
76a06ca4 KZ |
268 | } else if (!fs->user_optstr && !strncmp(p, "OPTS=", 5)) { |
269 | fs->user_optstr = unmangle(p + 5, &end); | |
270 | if (!fs->user_optstr) | |
dd369652 | 271 | goto enomem; |
76a06ca4 KZ |
272 | |
273 | } else if (!fs->attrs && !strncmp(p, "ATTRS=", 6)) { | |
274 | fs->attrs = unmangle(p + 6, &end); | |
275 | if (!fs->attrs) | |
276 | goto enomem; | |
277 | ||
dd369652 KZ |
278 | } else { |
279 | /* unknown variable */ | |
280 | while (*p && *p != ' ') p++; | |
281 | } | |
5ac6a133 KZ |
282 | if (end) |
283 | p = end; | |
dd369652 KZ |
284 | } |
285 | ||
286 | return 0; | |
287 | enomem: | |
83a78332 | 288 | DBG(TAB, ul_debug("utab parse error: ENOMEM")); |
dd369652 KZ |
289 | return -ENOMEM; |
290 | } | |
291 | ||
ce4dd666 KZ |
292 | /* |
293 | * Parses one line from /proc/swaps | |
294 | */ | |
295 | static int mnt_parse_swaps_line(struct libmnt_fs *fs, char *s) | |
296 | { | |
297 | uintmax_t fsz, usz; | |
298 | int rc; | |
299 | char *src = NULL; | |
300 | ||
301 | rc = sscanf(s, UL_SCNsA" " /* (1) source */ | |
302 | UL_SCNsA" " /* (2) type */ | |
ea97feab KZ |
303 | "%ju" /* (3) size */ |
304 | "%ju" /* (4) used */ | |
ce4dd666 KZ |
305 | "%d", /* priority */ |
306 | ||
307 | &src, | |
308 | &fs->swaptype, | |
309 | &fsz, | |
310 | &usz, | |
311 | &fs->priority); | |
312 | ||
313 | if (rc == 5) { | |
314 | size_t sz; | |
315 | ||
316 | fs->size = fsz; | |
317 | fs->usedsize = usz; | |
318 | ||
319 | unmangle_string(src); | |
320 | ||
321 | /* remove "(deleted)" suffix */ | |
322 | sz = strlen(src); | |
323 | if (sz > PATH_DELETED_SUFFIX_SZ) { | |
324 | char *p = src + (sz - PATH_DELETED_SUFFIX_SZ); | |
325 | if (strcmp(p, PATH_DELETED_SUFFIX) == 0) | |
326 | *p = '\0'; | |
327 | } | |
328 | ||
329 | rc = mnt_fs_set_source(fs, src); | |
330 | if (!rc) | |
331 | mnt_fs_set_fstype(fs, "swap"); | |
332 | free(src); | |
333 | } else { | |
83a78332 | 334 | DBG(TAB, ul_debug("tab parse error: [sscanf rc=%d]: '%s'", rc, s)); |
ce4dd666 KZ |
335 | rc = -EINVAL; |
336 | } | |
337 | ||
338 | return rc; | |
339 | } | |
340 | ||
341 | ||
6bd8b7a7 KZ |
342 | /* |
343 | * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*) | |
344 | * | |
d58b3157 OO |
345 | * Note that we aren't trying to guess the utab file format, because this file |
346 | * always has to be parsed by private libmount routines with an explicitly defined | |
dd369652 KZ |
347 | * format. |
348 | * | |
349 | * mountinfo: "<number> <number> ... " | |
6bd8b7a7 | 350 | */ |
68164f6c | 351 | static int guess_table_format(char *line) |
6bd8b7a7 | 352 | { |
8f3f6383 | 353 | unsigned int a, b; |
6bd8b7a7 | 354 | |
83a78332 | 355 | DBG(TAB, ul_debug("trying to guess table type")); |
ce4dd666 | 356 | |
dd369652 KZ |
357 | if (sscanf(line, "%u %u", &a, &b) == 2) |
358 | return MNT_FMT_MOUNTINFO; | |
581a4ad9 | 359 | |
ce4dd666 KZ |
360 | if (strncmp(line, "Filename\t", 9) == 0) |
361 | return MNT_FMT_SWAPS; | |
362 | ||
581a4ad9 | 363 | return MNT_FMT_FSTAB; /* fstab, mtab or /proc/mounts */ |
6bd8b7a7 KZ |
364 | } |
365 | ||
cb90e24e OO |
366 | static int is_comment_line(char *line) |
367 | { | |
675de3f5 | 368 | char *p = (char *) skip_blank(line); |
cb90e24e OO |
369 | |
370 | if (p && (*p == '#' || *p == '\n')) | |
371 | return 1; | |
372 | return 0; | |
373 | } | |
374 | ||
375 | /* returns 1 if the last line in the @str is blank */ | |
376 | static int is_terminated_by_blank(const char *str) | |
377 | { | |
378 | size_t sz = str ? strlen(str) : 0; | |
379 | const char *p = sz ? str + (sz - 1) : NULL; | |
380 | ||
381 | if (!sz || !p || *p != '\n') | |
382 | return 0; /* empty or not terminated by '\n' */ | |
383 | if (p == str) | |
384 | return 1; /* only '\n' */ | |
385 | p--; | |
386 | while (p >= str && (*p == ' ' || *p == '\t')) | |
387 | p--; | |
388 | return *p == '\n' ? 1 : 0; | |
389 | } | |
390 | ||
391 | /* | |
392 | * Reads the next line from the file. | |
393 | * | |
d58b3157 OO |
394 | * Returns 0 if the line is a comment |
395 | * 1 if the line is not a comment | |
cb90e24e OO |
396 | * <0 on error |
397 | */ | |
d92b8c3b | 398 | static int next_comment_line(struct libmnt_parser *pa, char **last) |
cb90e24e | 399 | { |
d92b8c3b KZ |
400 | if (getline(&pa->buf, &pa->bufsiz, pa->f) < 0) |
401 | return feof(pa->f) ? 1 : -errno; | |
cb90e24e | 402 | |
d92b8c3b KZ |
403 | pa->line++; |
404 | *last = strchr(pa->buf, '\n'); | |
cb90e24e | 405 | |
d92b8c3b | 406 | return is_comment_line(pa->buf) ? 0 : 1; |
cb90e24e OO |
407 | } |
408 | ||
409 | static int append_comment(struct libmnt_table *tb, | |
410 | struct libmnt_fs *fs, | |
411 | const char *comm, | |
412 | int eof) | |
413 | { | |
414 | int rc, intro = mnt_table_get_nents(tb) == 0; | |
415 | ||
416 | if (intro && is_terminated_by_blank(mnt_table_get_intro_comment(tb))) | |
417 | intro = 0; | |
418 | ||
83a78332 | 419 | DBG(TAB, ul_debugobj(tb, "appending %s comment", |
cb90e24e | 420 | intro ? "intro" : |
d58b3157 | 421 | eof ? "trailing" : "fs")); |
cb90e24e OO |
422 | if (intro) |
423 | rc = mnt_table_append_intro_comment(tb, comm); | |
424 | else if (eof) { | |
3035ba93 | 425 | rc = mnt_table_set_trailing_comment(tb, |
cb90e24e OO |
426 | mnt_fs_get_comment(fs)); |
427 | if (!rc) | |
3035ba93 | 428 | rc = mnt_table_append_trailing_comment(tb, comm); |
cb90e24e OO |
429 | if (!rc) |
430 | rc = mnt_fs_set_comment(fs, NULL); | |
431 | } else | |
432 | rc = mnt_fs_append_comment(fs, comm); | |
433 | return rc; | |
434 | } | |
435 | ||
6bd8b7a7 KZ |
436 | /* |
437 | * Read and parse the next line from {fs,m}tab or mountinfo | |
438 | */ | |
d92b8c3b KZ |
439 | static int mnt_table_parse_next(struct libmnt_parser *pa, |
440 | struct libmnt_table *tb, | |
441 | struct libmnt_fs *fs) | |
6bd8b7a7 | 442 | { |
6bd8b7a7 | 443 | char *s; |
7104bedb | 444 | int rc; |
6bd8b7a7 KZ |
445 | |
446 | assert(tb); | |
d92b8c3b | 447 | assert(pa); |
6bd8b7a7 KZ |
448 | assert(fs); |
449 | ||
450 | /* read the next non-blank non-comment line */ | |
ce4dd666 | 451 | next_line: |
6bd8b7a7 | 452 | do { |
d92b8c3b | 453 | if (getline(&pa->buf, &pa->bufsiz, pa->f) < 0) |
d8a30749 | 454 | return -EINVAL; |
d92b8c3b KZ |
455 | pa->line++; |
456 | s = strchr(pa->buf, '\n'); | |
6bd8b7a7 | 457 | if (!s) { |
d58b3157 | 458 | /* Missing final newline? Otherwise an extremely */ |
6bd8b7a7 | 459 | /* long line - assume file was corrupted */ |
d92b8c3b | 460 | if (feof(pa->f)) { |
83a78332 | 461 | DBG(TAB, ul_debugobj(tb, |
d92b8c3b KZ |
462 | "%s: no final newline", pa->filename)); |
463 | s = strchr(pa->buf, '\0'); | |
6bd8b7a7 | 464 | } else { |
83a78332 | 465 | DBG(TAB, ul_debugobj(tb, |
d92b8c3b KZ |
466 | "%s:%zu: missing newline at line", |
467 | pa->filename, pa->line)); | |
6bd8b7a7 KZ |
468 | goto err; |
469 | } | |
470 | } | |
cb90e24e OO |
471 | |
472 | /* comments parser */ | |
473 | if (tb->comms | |
474 | && (tb->fmt == MNT_FMT_GUESS || tb->fmt == MNT_FMT_FSTAB) | |
d92b8c3b | 475 | && is_comment_line(pa->buf)) { |
cb90e24e | 476 | do { |
d92b8c3b | 477 | rc = append_comment(tb, fs, pa->buf, feof(pa->f)); |
cb90e24e | 478 | if (!rc) |
d92b8c3b | 479 | rc = next_comment_line(pa, &s); |
cb90e24e OO |
480 | } while (rc == 0); |
481 | ||
d92b8c3b | 482 | if (rc == 1 && feof(pa->f)) |
cb90e24e OO |
483 | rc = append_comment(tb, fs, NULL, 1); |
484 | if (rc < 0) | |
485 | return rc; | |
486 | ||
487 | } | |
488 | ||
6bd8b7a7 | 489 | *s = '\0'; |
d92b8c3b | 490 | if (--s >= pa->buf && *s == '\r') |
6bd8b7a7 | 491 | *s = '\0'; |
d92b8c3b | 492 | s = (char *) skip_blank(pa->buf); |
6bd8b7a7 KZ |
493 | } while (*s == '\0' || *s == '#'); |
494 | ||
ce4dd666 | 495 | if (tb->fmt == MNT_FMT_GUESS) { |
68164f6c | 496 | tb->fmt = guess_table_format(s); |
ce4dd666 KZ |
497 | if (tb->fmt == MNT_FMT_SWAPS) |
498 | goto next_line; /* skip swap header */ | |
499 | } | |
6bd8b7a7 | 500 | |
7104bedb KZ |
501 | switch (tb->fmt) { |
502 | case MNT_FMT_FSTAB: | |
503 | rc = mnt_parse_table_line(fs, s); | |
504 | break; | |
505 | case MNT_FMT_MOUNTINFO: | |
506 | rc = mnt_parse_mountinfo_line(fs, s); | |
507 | break; | |
508 | case MNT_FMT_UTAB: | |
509 | rc = mnt_parse_utab_line(fs, s); | |
510 | break; | |
ce4dd666 KZ |
511 | case MNT_FMT_SWAPS: |
512 | if (strncmp(s, "Filename\t", 9) == 0) | |
513 | goto next_line; /* skip swap header */ | |
514 | rc = mnt_parse_swaps_line(fs, s); | |
515 | break; | |
7104bedb KZ |
516 | default: |
517 | rc = -1; /* unknown format */ | |
518 | break; | |
6bd8b7a7 KZ |
519 | } |
520 | ||
7104bedb KZ |
521 | if (rc == 0) |
522 | return 0; | |
6bd8b7a7 | 523 | err: |
d92b8c3b | 524 | DBG(TAB, ul_debugobj(tb, "%s:%zu: %s parse error", pa->filename, pa->line, |
dd369652 | 525 | tb->fmt == MNT_FMT_MOUNTINFO ? "mountinfo" : |
ce4dd666 | 526 | tb->fmt == MNT_FMT_SWAPS ? "swaps" : |
581a4ad9 | 527 | tb->fmt == MNT_FMT_FSTAB ? "tab" : "utab")); |
9fd75d76 | 528 | |
c3b0d5b3 | 529 | /* by default all errors are recoverable, otherwise behavior depends on |
d58b3157 | 530 | * the errcb() function. See mnt_table_set_parser_errcb(). |
c3b0d5b3 | 531 | */ |
d92b8c3b | 532 | return tb->errcb ? tb->errcb(tb, pa->filename, pa->line) : 1; |
6bd8b7a7 KZ |
533 | } |
534 | ||
f7f29b56 KZ |
535 | static pid_t path_to_tid(const char *filename) |
536 | { | |
537 | char *path = mnt_resolve_path(filename, NULL); | |
538 | char *p, *end = NULL; | |
539 | pid_t tid = 0; | |
540 | ||
541 | if (!path) | |
542 | goto done; | |
543 | p = strrchr(path, '/'); | |
544 | if (!p) | |
545 | goto done; | |
546 | *p = '\0'; | |
547 | p = strrchr(path, '/'); | |
548 | if (!p) | |
549 | goto done; | |
550 | p++; | |
551 | ||
552 | errno = 0; | |
553 | tid = strtol(p, &end, 10); | |
554 | if (errno || p == end || (end && *end)) { | |
555 | tid = 0; | |
556 | goto done; | |
557 | } | |
83a78332 | 558 | DBG(TAB, ul_debug("TID for %s is %d", filename, tid)); |
f7f29b56 KZ |
559 | done: |
560 | free(path); | |
561 | return tid; | |
562 | } | |
563 | ||
3da7f698 KZ |
564 | static int kernel_fs_postparse(struct libmnt_table *tb, |
565 | struct libmnt_fs *fs, pid_t *tid, | |
566 | const char *filename) | |
567 | { | |
568 | int rc = 0; | |
569 | const char *src = mnt_fs_get_srcpath(fs); | |
570 | ||
d58b3157 | 571 | /* This is a filesystem description from /proc, so we're in some process |
3da7f698 KZ |
572 | * namespace. Let's remember the process PID. |
573 | */ | |
574 | if (filename && *tid == -1) | |
575 | *tid = path_to_tid(filename); | |
576 | ||
577 | fs->tid = *tid; | |
578 | ||
579 | /* | |
580 | * Convert obscure /dev/root to something more usable | |
581 | */ | |
582 | if (src && strcmp(src, "/dev/root") == 0) { | |
3da7f698 KZ |
583 | char *real = NULL; |
584 | ||
40f00b4f KZ |
585 | rc = mnt_guess_system_root(fs->devno, tb->cache, &real); |
586 | if (rc < 0) | |
587 | return rc; | |
588 | ||
589 | if (rc == 0 && real) { | |
83a78332 | 590 | DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real)); |
40f00b4f | 591 | rc = __mnt_fs_set_source_ptr(fs, real); |
a772281d KZ |
592 | |
593 | } else if (rc == 1) { | |
9e930041 | 594 | /* mnt_guess_system_root() returns 1 if not able to convert to |
a772281d KZ |
595 | * the real devname; ignore this problem */ |
596 | rc = 0; | |
3da7f698 | 597 | } |
3da7f698 KZ |
598 | } |
599 | ||
600 | return rc; | |
601 | } | |
602 | ||
6bd8b7a7 | 603 | /** |
68164f6c | 604 | * mnt_table_parse_stream: |
6bd8b7a7 | 605 | * @tb: tab pointer |
45d6713e KZ |
606 | * @f: file stream |
607 | * @filename: filename used for debug and error messages | |
9fd75d76 | 608 | * |
d8a30749 | 609 | * Returns: 0 on success, negative number in case of error. |
6bd8b7a7 | 610 | */ |
68164f6c | 611 | int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename) |
6bd8b7a7 | 612 | { |
d8a30749 | 613 | int rc = -1; |
309139c7 | 614 | int flags = 0; |
f7f29b56 | 615 | pid_t tid = -1; |
e713e11d | 616 | struct libmnt_fs *fs = NULL; |
d92b8c3b | 617 | struct libmnt_parser pa = { .line = 0 }; |
6bd8b7a7 KZ |
618 | |
619 | assert(tb); | |
45d6713e | 620 | assert(f); |
9ed7507c | 621 | assert(filename); |
6bd8b7a7 | 622 | |
83a78332 | 623 | DBG(TAB, ul_debugobj(tb, "%s: start parsing [entries=%d, filter=%s]", |
9be1607f KZ |
624 | filename, mnt_table_get_nents(tb), |
625 | tb->fltrcb ? "yes" : "not")); | |
9ed7507c | 626 | |
d92b8c3b KZ |
627 | pa.filename = filename; |
628 | pa.f = f; | |
629 | ||
309139c7 | 630 | /* necessary for /proc/mounts only, the /proc/self/mountinfo |
6ad929bb | 631 | * parser sets the flag properly |
309139c7 KZ |
632 | */ |
633 | if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0) | |
634 | flags = MNT_FS_KERNEL; | |
635 | ||
6bd8b7a7 | 636 | while (!feof(f)) { |
e713e11d KZ |
637 | if (!fs) { |
638 | fs = mnt_new_fs(); | |
639 | if (!fs) | |
640 | goto err; | |
641 | } | |
6bd8b7a7 | 642 | |
d92b8c3b | 643 | rc = mnt_table_parse_next(&pa, tb, fs); |
4709c9e6 KZ |
644 | |
645 | if (!rc && tb->fltrcb && tb->fltrcb(fs, tb->fltrcb_data)) | |
646 | rc = 1; /* filtered out by callback... */ | |
647 | ||
309139c7 | 648 | if (!rc) { |
68164f6c | 649 | rc = mnt_table_add_fs(tb, fs); |
309139c7 | 650 | fs->flags |= flags; |
3da7f698 | 651 | |
4b85aef3 | 652 | if (rc == 0 && tb->fmt == MNT_FMT_MOUNTINFO) { |
3da7f698 | 653 | rc = kernel_fs_postparse(tb, fs, &tid, filename); |
4b85aef3 KZ |
654 | if (rc) |
655 | mnt_table_remove_fs(tb, fs); | |
656 | } | |
309139c7 | 657 | } |
26d0c0ae | 658 | |
6bd8b7a7 | 659 | if (rc) { |
1bb02a2d | 660 | if (rc > 0) { |
e713e11d KZ |
661 | mnt_reset_fs(fs); |
662 | assert(fs->refcount == 1); | |
663 | continue; /* recoverable error, reuse fs*/ | |
664 | } | |
665 | ||
666 | mnt_unref_fs(fs); | |
fbb3eb85 KZ |
667 | if (feof(f)) |
668 | break; | |
d8a30749 | 669 | goto err; /* fatal error */ |
6bd8b7a7 | 670 | } |
e713e11d KZ |
671 | mnt_unref_fs(fs); |
672 | fs = NULL; | |
6bd8b7a7 KZ |
673 | } |
674 | ||
83a78332 | 675 | DBG(TAB, ul_debugobj(tb, "%s: stop parsing (%d entries)", |
0983b5f7 | 676 | filename, mnt_table_get_nents(tb))); |
d92b8c3b | 677 | parser_cleanup(&pa); |
6bd8b7a7 | 678 | return 0; |
d8a30749 | 679 | err: |
83a78332 | 680 | DBG(TAB, ul_debugobj(tb, "%s: parse error (rc=%d)", filename, rc)); |
d92b8c3b | 681 | parser_cleanup(&pa); |
d8a30749 | 682 | return rc; |
6bd8b7a7 KZ |
683 | } |
684 | ||
685 | /** | |
68164f6c | 686 | * mnt_table_parse_file: |
45d6713e KZ |
687 | * @tb: tab pointer |
688 | * @filename: file | |
689 | * | |
e778642a | 690 | * Parses the whole table (e.g. /etc/fstab) and appends new records to the @tab. |
45d6713e | 691 | * |
45d6713e | 692 | * The libmount parser ignores broken (syntax error) lines, these lines are |
d58b3157 | 693 | * reported to the caller by the errcb() function (see mnt_table_set_parser_errcb()). |
45d6713e | 694 | * |
d8a30749 | 695 | * Returns: 0 on success, negative number in case of error. |
45d6713e | 696 | */ |
68164f6c | 697 | int mnt_table_parse_file(struct libmnt_table *tb, const char *filename) |
45d6713e KZ |
698 | { |
699 | FILE *f; | |
d8a30749 | 700 | int rc; |
45d6713e | 701 | |
45d6713e | 702 | if (!filename || !tb) |
d8a30749 | 703 | return -EINVAL; |
45d6713e | 704 | |
1eb8539d | 705 | f = fopen(filename, "r" UL_CLOEXECSTR); |
45d6713e | 706 | if (f) { |
68164f6c | 707 | rc = mnt_table_parse_stream(tb, f, filename); |
45d6713e | 708 | fclose(f); |
d8a30749 | 709 | } else |
60d29f82 | 710 | rc = -errno; |
d8a30749 | 711 | |
60d29f82 | 712 | DBG(TAB, ul_debugobj(tb, "parsing done [filename=%s, rc=%d]", filename, rc)); |
45d6713e KZ |
713 | return rc; |
714 | } | |
715 | ||
9deeef8e MY |
716 | static int mnt_table_parse_dir_filter(const struct dirent *d) |
717 | { | |
718 | size_t namesz; | |
719 | ||
720 | #ifdef _DIRENT_HAVE_D_TYPE | |
721 | if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG && | |
722 | d->d_type != DT_LNK) | |
723 | return 0; | |
724 | #endif | |
725 | if (*d->d_name == '.') | |
726 | return 0; | |
727 | ||
728 | #define MNT_MNTTABDIR_EXTSIZ (sizeof(MNT_MNTTABDIR_EXT) - 1) | |
729 | ||
730 | namesz = strlen(d->d_name); | |
731 | if (!namesz || namesz < MNT_MNTTABDIR_EXTSIZ + 1 || | |
732 | strcmp(d->d_name + (namesz - MNT_MNTTABDIR_EXTSIZ), | |
733 | MNT_MNTTABDIR_EXT)) | |
734 | return 0; | |
735 | ||
736 | /* Accept this */ | |
737 | return 1; | |
738 | } | |
739 | ||
485a8bfa | 740 | #ifdef HAVE_SCANDIRAT |
52f83628 | 741 | static int __mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) |
485a8bfa MY |
742 | { |
743 | int n = 0, i; | |
744 | int dd; | |
745 | struct dirent **namelist = NULL; | |
746 | ||
747 | dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY); | |
748 | if (dd < 0) | |
749 | return -errno; | |
9deeef8e MY |
750 | |
751 | n = scandirat(dd, ".", &namelist, mnt_table_parse_dir_filter, versionsort); | |
485a8bfa MY |
752 | if (n <= 0) { |
753 | close(dd); | |
754 | return 0; | |
755 | } | |
756 | ||
757 | for (i = 0; i < n; i++) { | |
758 | struct dirent *d = namelist[i]; | |
759 | struct stat st; | |
485a8bfa MY |
760 | FILE *f; |
761 | ||
2208b3cc | 762 | if (fstatat(dd, d->d_name, &st, 0) || |
485a8bfa MY |
763 | !S_ISREG(st.st_mode)) |
764 | continue; | |
765 | ||
2208b3cc | 766 | f = fopen_at(dd, d->d_name, O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); |
485a8bfa MY |
767 | if (f) { |
768 | mnt_table_parse_stream(tb, f, d->d_name); | |
769 | fclose(f); | |
770 | } | |
771 | } | |
772 | ||
773 | for (i = 0; i < n; i++) | |
774 | free(namelist[i]); | |
775 | free(namelist); | |
776 | close(dd); | |
777 | return 0; | |
778 | } | |
779 | #else | |
52f83628 | 780 | static int __mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) |
70bf97ae | 781 | { |
b28890b0 | 782 | int n = 0, i, r = 0; |
70bf97ae KZ |
783 | DIR *dir = NULL; |
784 | struct dirent **namelist = NULL; | |
785 | ||
9deeef8e | 786 | n = scandir(dirname, &namelist, mnt_table_parse_dir_filter, versionsort); |
70bf97ae KZ |
787 | if (n <= 0) |
788 | return 0; | |
789 | ||
d58b3157 | 790 | /* let's use "at" functions rather than playing crazy games with paths... */ |
70bf97ae | 791 | dir = opendir(dirname); |
b28890b0 MY |
792 | if (!dir) { |
793 | r = -errno; | |
794 | goto out; | |
795 | } | |
70bf97ae KZ |
796 | |
797 | for (i = 0; i < n; i++) { | |
798 | struct dirent *d = namelist[i]; | |
799 | struct stat st; | |
70bf97ae KZ |
800 | FILE *f; |
801 | ||
2208b3cc | 802 | if (fstatat(dirfd(dir), d->d_name, &st, 0) || |
70bf97ae KZ |
803 | !S_ISREG(st.st_mode)) |
804 | continue; | |
805 | ||
2208b3cc | 806 | f = fopen_at(dirfd(dir), d->d_name, |
1eb8539d | 807 | O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); |
70bf97ae | 808 | if (f) { |
68164f6c | 809 | mnt_table_parse_stream(tb, f, d->d_name); |
70bf97ae KZ |
810 | fclose(f); |
811 | } | |
812 | } | |
813 | ||
b28890b0 | 814 | out: |
70bf97ae KZ |
815 | for (i = 0; i < n; i++) |
816 | free(namelist[i]); | |
817 | free(namelist); | |
818 | if (dir) | |
819 | closedir(dir); | |
b28890b0 | 820 | return r; |
70bf97ae | 821 | } |
485a8bfa | 822 | #endif |
70bf97ae | 823 | |
52f83628 KZ |
824 | /** |
825 | * mnt_table_parse_dir: | |
826 | * @tb: mount table | |
827 | * @dirname: directory | |
828 | * | |
829 | * The directory: | |
830 | * - files are sorted by strverscmp(3) | |
d58b3157 | 831 | * - files that start with "." are ignored (e.g. ".10foo.fstab") |
52f83628 KZ |
832 | * - files without the ".fstab" extension are ignored |
833 | * | |
834 | * Returns: 0 on success or negative number in case of error. | |
835 | */ | |
836 | int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) | |
837 | { | |
838 | return __mnt_table_parse_dir(tb, dirname); | |
839 | } | |
840 | ||
68164f6c | 841 | struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt) |
dd369652 | 842 | { |
68164f6c | 843 | struct libmnt_table *tb; |
dd369652 KZ |
844 | struct stat st; |
845 | ||
dd369652 KZ |
846 | if (!filename) |
847 | return NULL; | |
f84fa6f7 | 848 | if (stat(filename, &st)) |
dd369652 | 849 | return NULL; |
68164f6c | 850 | tb = mnt_new_table(); |
dd369652 | 851 | if (tb) { |
dc7c3d65 | 852 | DBG(TAB, ul_debugobj(tb, "new tab for file: %s", filename)); |
dd369652 | 853 | tb->fmt = fmt; |
68164f6c | 854 | if (mnt_table_parse_file(tb, filename) != 0) { |
c9f1585e | 855 | mnt_unref_table(tb); |
dd369652 KZ |
856 | tb = NULL; |
857 | } | |
858 | } | |
859 | return tb; | |
860 | } | |
70bf97ae | 861 | |
45d6713e | 862 | /** |
68164f6c | 863 | * mnt_new_table_from_file: |
6bd8b7a7 KZ |
864 | * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path |
865 | * | |
68164f6c | 866 | * Same as mnt_new_table() + mnt_table_parse_file(). Use this function for private |
d58b3157 | 867 | * files only. This function does not allow using the error callback, so you |
9826a637 KZ |
868 | * cannot provide any feedback to end-users about broken records in files (e.g. |
869 | * fstab). | |
6bd8b7a7 | 870 | * |
192c6aad | 871 | * Returns: newly allocated tab on success and NULL in case of error. |
6bd8b7a7 | 872 | */ |
68164f6c | 873 | struct libmnt_table *mnt_new_table_from_file(const char *filename) |
6bd8b7a7 | 874 | { |
37290a53 KZ |
875 | if (!filename) |
876 | return NULL; | |
877 | ||
68164f6c | 878 | return __mnt_new_table_from_file(filename, MNT_FMT_GUESS); |
6bd8b7a7 KZ |
879 | } |
880 | ||
70bf97ae | 881 | /** |
68164f6c | 882 | * mnt_new_table_from_dir |
52f83628 | 883 | * @dirname: directory with *.fstab files |
70bf97ae KZ |
884 | * |
885 | * Returns: newly allocated tab on success and NULL in case of error. | |
886 | */ | |
68164f6c | 887 | struct libmnt_table *mnt_new_table_from_dir(const char *dirname) |
70bf97ae | 888 | { |
68164f6c | 889 | struct libmnt_table *tb; |
70bf97ae | 890 | |
70bf97ae KZ |
891 | if (!dirname) |
892 | return NULL; | |
68164f6c KZ |
893 | tb = mnt_new_table(); |
894 | if (tb && mnt_table_parse_dir(tb, dirname) != 0) { | |
c9f1585e | 895 | mnt_unref_table(tb); |
70bf97ae KZ |
896 | tb = NULL; |
897 | } | |
898 | return tb; | |
899 | } | |
900 | ||
9fd75d76 | 901 | /** |
68164f6c | 902 | * mnt_table_set_parser_errcb: |
0f32f1e2 | 903 | * @tb: pointer to table |
9fd75d76 KZ |
904 | * @cb: pointer to callback function |
905 | * | |
68164f6c | 906 | * The error callback function is called by table parser (mnt_table_parse_file()) |
d58b3157 | 907 | * in case of a syntax error. The callback function could be used for error |
d8a30749 KZ |
908 | * evaluation, libmount will continue/stop parsing according to callback return |
909 | * codes: | |
910 | * | |
911 | * <0 : fatal error (abort parsing) | |
d58b3157 OO |
912 | * 0 : success (parsing continues) |
913 | * >0 : recoverable error (the line is ignored, parsing continues). | |
9fd75d76 | 914 | * |
d8a30749 | 915 | * Returns: 0 on success or negative number in case of error. |
9fd75d76 | 916 | */ |
68164f6c KZ |
917 | int mnt_table_set_parser_errcb(struct libmnt_table *tb, |
918 | int (*cb)(struct libmnt_table *tb, const char *filename, int line)) | |
9fd75d76 | 919 | { |
ba2bdf41 KZ |
920 | if (!tb) |
921 | return -EINVAL; | |
9fd75d76 KZ |
922 | tb->errcb = cb; |
923 | return 0; | |
924 | } | |
925 | ||
4709c9e6 | 926 | /* |
d58b3157 | 927 | * Filter out entries during tab file parsing. If @cb returns 1, then the entry |
4709c9e6 KZ |
928 | * is ignored. |
929 | */ | |
930 | int mnt_table_set_parser_fltrcb(struct libmnt_table *tb, | |
931 | int (*cb)(struct libmnt_fs *, void *), | |
932 | void *data) | |
933 | { | |
ba2bdf41 KZ |
934 | if (!tb) |
935 | return -EINVAL; | |
4709c9e6 | 936 | |
83a78332 | 937 | DBG(TAB, ul_debugobj(tb, "%s table parser filter", cb ? "set" : "unset")); |
4709c9e6 KZ |
938 | tb->fltrcb = cb; |
939 | tb->fltrcb_data = data; | |
940 | return 0; | |
941 | } | |
942 | ||
ce4dd666 KZ |
943 | /** |
944 | * mnt_table_parse_swaps: | |
945 | * @tb: table | |
946 | * @filename: overwrites default (/proc/swaps or $LIBMOUNT_SWAPS) or NULL | |
947 | * | |
948 | * This function parses /proc/swaps and appends new lines to the @tab. | |
949 | * | |
950 | * See also mnt_table_set_parser_errcb(). | |
951 | * | |
952 | * Returns: 0 on success or negative number in case of error. | |
953 | */ | |
954 | int mnt_table_parse_swaps(struct libmnt_table *tb, const char *filename) | |
955 | { | |
ce4dd666 KZ |
956 | if (!tb) |
957 | return -EINVAL; | |
958 | if (!filename) { | |
959 | filename = mnt_get_swaps_path(); | |
960 | if (!filename) | |
961 | return -EINVAL; | |
962 | } | |
963 | ||
964 | tb->fmt = MNT_FMT_SWAPS; | |
965 | ||
966 | return mnt_table_parse_file(tb, filename); | |
967 | } | |
968 | ||
9826a637 | 969 | /** |
68164f6c | 970 | * mnt_table_parse_fstab: |
9826a637 | 971 | * @tb: table |
3a5b1b1d | 972 | * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL |
9826a637 | 973 | * |
52f83628 | 974 | * This function parses /etc/fstab and appends new lines to the @tab. If the |
d58b3157 | 975 | * @filename is a directory, then mnt_table_parse_dir() is called. |
9826a637 | 976 | * |
68164f6c | 977 | * See also mnt_table_set_parser_errcb(). |
9826a637 | 978 | * |
70bf97ae | 979 | * Returns: 0 on success or negative number in case of error. |
9826a637 | 980 | */ |
68164f6c | 981 | int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename) |
9826a637 | 982 | { |
52f83628 KZ |
983 | struct stat st; |
984 | int rc = 0; | |
9826a637 | 985 | |
9826a637 | 986 | if (!tb) |
d8a30749 | 987 | return -EINVAL; |
3a5b1b1d KZ |
988 | if (!filename) |
989 | filename = mnt_get_fstab_path(); | |
990 | ||
52f83628 KZ |
991 | if (!filename || stat(filename, &st)) |
992 | return -EINVAL; | |
dd369652 | 993 | |
52f83628 | 994 | tb->fmt = MNT_FMT_FSTAB; |
1bd1cd2c | 995 | |
52f83628 KZ |
996 | if (S_ISREG(st.st_mode)) |
997 | rc = mnt_table_parse_file(tb, filename); | |
998 | else if (S_ISDIR(st.st_mode)) | |
999 | rc = mnt_table_parse_dir(tb, filename); | |
1000 | else | |
1001 | rc = -EINVAL; | |
70bf97ae | 1002 | |
52f83628 | 1003 | return rc; |
9826a637 | 1004 | } |
0532ba1d KZ |
1005 | |
1006 | /* | |
d58b3157 | 1007 | * This function uses @uf to find a corresponding record in @tb, then the record |
68164f6c | 1008 | * from @tb is updated (user specific mount options are added). |
0532ba1d | 1009 | * |
68164f6c | 1010 | * Note that @uf must contain only user specific mount options instead of |
6a493fa3 | 1011 | * VFS options (note that FS options are ignored). |
0532ba1d | 1012 | * |
569f95b7 | 1013 | * Returns modified filesystem (from @tb) or NULL. |
0532ba1d | 1014 | */ |
68164f6c | 1015 | static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf) |
0532ba1d | 1016 | { |
68164f6c KZ |
1017 | struct libmnt_fs *fs; |
1018 | struct libmnt_iter itr; | |
76a06ca4 | 1019 | const char *optstr, *src, *target, *root, *attrs; |
0532ba1d | 1020 | |
0532ba1d KZ |
1021 | if (!tb || !uf) |
1022 | return NULL; | |
1023 | ||
83a78332 | 1024 | DBG(TAB, ul_debugobj(tb, "merging user fs")); |
dd369652 | 1025 | |
0532ba1d KZ |
1026 | src = mnt_fs_get_srcpath(uf); |
1027 | target = mnt_fs_get_target(uf); | |
68164f6c | 1028 | optstr = mnt_fs_get_user_options(uf); |
76a06ca4 | 1029 | attrs = mnt_fs_get_attributes(uf); |
0532ba1d KZ |
1030 | root = mnt_fs_get_root(uf); |
1031 | ||
76a06ca4 | 1032 | if (!src || !target || !root || (!attrs && !optstr)) |
0532ba1d KZ |
1033 | return NULL; |
1034 | ||
1035 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); | |
1036 | ||
68164f6c | 1037 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
6699e742 | 1038 | const char *r = mnt_fs_get_root(fs); |
0532ba1d | 1039 | |
309139c7 KZ |
1040 | if (fs->flags & MNT_FS_MERGED) |
1041 | continue; | |
1042 | ||
6699e742 KZ |
1043 | if (r && strcmp(r, root) == 0 |
1044 | && mnt_fs_streq_target(fs, target) | |
1045 | && mnt_fs_streq_srcpath(fs, src)) | |
0532ba1d KZ |
1046 | break; |
1047 | } | |
1048 | ||
dd369652 | 1049 | if (fs) { |
83a78332 | 1050 | DBG(TAB, ul_debugobj(tb, "found fs -- appending user optstr")); |
f2b3a3a3 | 1051 | mnt_fs_append_options(fs, optstr); |
76a06ca4 | 1052 | mnt_fs_append_attributes(fs, attrs); |
dd369652 | 1053 | mnt_fs_set_bindsrc(fs, mnt_fs_get_bindsrc(uf)); |
309139c7 | 1054 | fs->flags |= MNT_FS_MERGED; |
dd369652 | 1055 | |
83a78332 | 1056 | DBG(TAB, ul_debugobj(tb, "found fs:")); |
dd369652 KZ |
1057 | DBG(TAB, mnt_fs_print_debug(fs, stderr)); |
1058 | } | |
0532ba1d KZ |
1059 | return fs; |
1060 | } | |
1061 | ||
e778642a KZ |
1062 | /* default filename is /proc/self/mountinfo |
1063 | */ | |
6a52473e KZ |
1064 | int __mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename, |
1065 | struct libmnt_table *u_tb) | |
0532ba1d | 1066 | { |
6a52473e | 1067 | int rc = 0, priv_utab = 0; |
0532ba1d | 1068 | |
4569bbea KZ |
1069 | assert(tb); |
1070 | ||
b529694f | 1071 | if (filename) |
9e930041 | 1072 | DBG(TAB, ul_debugobj(tb, "%s requested as mtab", filename)); |
b529694f | 1073 | |
e778642a | 1074 | #ifdef USE_LIBMOUNT_SUPPORT_MTAB |
70bf97ae | 1075 | if (mnt_has_regular_mtab(&filename, NULL)) { |
b0bb8fb6 | 1076 | |
60d29f82 | 1077 | DBG(TAB, ul_debugobj(tb, "force mtab usage [filename=%s]", filename)); |
b0bb8fb6 | 1078 | |
68164f6c | 1079 | rc = mnt_table_parse_file(tb, filename); |
60d29f82 KZ |
1080 | |
1081 | /* | |
1082 | * If @filename forces us to read from /proc then also read | |
1083 | * utab file to merge userspace mount options. | |
1084 | */ | |
1085 | if (rc == 0 && is_mountinfo(tb)) | |
1086 | goto read_utab; | |
1087 | ||
0532ba1d | 1088 | if (!rc) |
3a5b1b1d | 1089 | return 0; |
70bf97ae | 1090 | filename = NULL; /* failed */ |
b529694f KZ |
1091 | } else |
1092 | filename = NULL; /* mtab useless */ | |
e3f72275 | 1093 | #endif |
60d29f82 | 1094 | |
b529694f KZ |
1095 | if (!filename || strcmp(filename, _PATH_PROC_MOUNTINFO) == 0) { |
1096 | filename = _PATH_PROC_MOUNTINFO; | |
1097 | tb->fmt = MNT_FMT_MOUNTINFO; | |
1098 | DBG(TAB, ul_debugobj(tb, "mtab parse: #1 read mountinfo")); | |
1099 | } else | |
1100 | tb->fmt = MNT_FMT_GUESS; | |
1101 | ||
1102 | rc = mnt_table_parse_file(tb, filename); | |
dd369652 | 1103 | if (rc) { |
0532ba1d | 1104 | /* hmm, old kernel? ...try /proc/mounts */ |
dd369652 | 1105 | tb->fmt = MNT_FMT_MTAB; |
68164f6c | 1106 | return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS); |
dd369652 | 1107 | } |
0532ba1d | 1108 | |
e778642a KZ |
1109 | if (!is_mountinfo(tb)) |
1110 | return 0; | |
edeb6223 | 1111 | #ifdef USE_LIBMOUNT_SUPPORT_MTAB |
60d29f82 | 1112 | read_utab: |
edeb6223 | 1113 | #endif |
60d29f82 KZ |
1114 | DBG(TAB, ul_debugobj(tb, "mtab parse: #2 read utab")); |
1115 | ||
dad88cb3 KZ |
1116 | if (mnt_table_get_nents(tb) == 0) |
1117 | return 0; /* empty, ignore utab */ | |
3a5b1b1d | 1118 | /* |
d58b3157 | 1119 | * try to read the user specific information from /run/mount/utabs |
3a5b1b1d | 1120 | */ |
6a52473e KZ |
1121 | if (!u_tb) { |
1122 | const char *utab = mnt_get_utab_path(); | |
60d29f82 | 1123 | |
6a52473e KZ |
1124 | if (!utab || is_file_empty(utab)) |
1125 | return 0; | |
1126 | ||
1127 | u_tb = mnt_new_table(); | |
1128 | if (!u_tb) | |
1129 | return -ENOMEM; | |
0532ba1d | 1130 | |
6a52473e KZ |
1131 | u_tb->fmt = MNT_FMT_UTAB; |
1132 | mnt_table_set_parser_fltrcb(u_tb, tb->fltrcb, tb->fltrcb_data); | |
0532ba1d | 1133 | |
6a52473e KZ |
1134 | rc = mnt_table_parse_file(u_tb, utab); |
1135 | priv_utab = 1; | |
1136 | } | |
0532ba1d | 1137 | |
60d29f82 KZ |
1138 | DBG(TAB, ul_debugobj(tb, "mtab parse: #3 merge utab")); |
1139 | ||
6a52473e | 1140 | if (rc == 0) { |
dad88cb3 KZ |
1141 | struct libmnt_fs *u_fs; |
1142 | struct libmnt_iter itr; | |
3a5b1b1d | 1143 | |
dad88cb3 KZ |
1144 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); |
1145 | ||
d58b3157 | 1146 | /* merge user options into mountinfo from the kernel */ |
dad88cb3 KZ |
1147 | while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0) |
1148 | mnt_table_merge_user_fs(tb, u_fs); | |
3a5b1b1d | 1149 | } |
dad88cb3 | 1150 | |
6a52473e KZ |
1151 | |
1152 | if (priv_utab) | |
1153 | mnt_unref_table(u_tb); | |
0532ba1d KZ |
1154 | return 0; |
1155 | } | |
6a52473e KZ |
1156 | /** |
1157 | * mnt_table_parse_mtab: | |
1158 | * @tb: table | |
e778642a | 1159 | * @filename: overwrites default or NULL |
6a52473e | 1160 | * |
b529694f KZ |
1161 | * The default filename is /proc/self/mountinfo. If the mount table is a |
1162 | * mountinfo file then /run/mount/utabs is parsed too and both files are merged | |
1163 | * to the one libmnt_table. | |
1164 | * | |
1165 | * If libmount is compiled with classic mtab file support, and the /etc/mtab is | |
1166 | * a regular file then this file is parsed. | |
1167 | * | |
1168 | * It's strongly recommended to use NULL as a @filename to keep code portable. | |
6a52473e KZ |
1169 | * |
1170 | * See also mnt_table_set_parser_errcb(). | |
1171 | * | |
1172 | * Returns: 0 on success or negative number in case of error. | |
1173 | */ | |
1174 | int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename) | |
1175 | { | |
1176 | return __mnt_table_parse_mtab(tb, filename, NULL); | |
1177 | } |