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