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