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