1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 * This file is part of libmount from util-linux project.
5 * Copyright (C) 2009-2018 Karel Zak <kzak@redhat.com>
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.
15 #endif /* !__USE_GNU */
16 #endif /* HAVE_SCANDIRAT */
24 #include "fileutils.h"
27 #include "pathnames.h"
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 */
38 static void parser_cleanup(struct libmnt_parser
*pa
)
43 memset(pa
, 0, sizeof(*pa
));
46 static const char *next_number(const char *s
, int *num
, int *rc
)
57 *num
= strtol(s
, &end
, 10);
58 if (end
== NULL
|| s
== end
)
61 /* valid end of number is a space or a terminator */
62 if (*end
== ' ' || *end
== '\t' || *end
== '\0')
68 static inline const char *skip_separator(const char *p
)
70 while (p
&& (*p
== ' ' || *p
== '\t'))
75 static inline const char *skip_nonspearator(const char *p
)
77 while (p
&& *p
&& !(*p
== ' ' || *p
== '\t'))
83 * Parses one line from {fs,m}tab
85 static int mnt_parse_table_line(struct libmnt_fs
*fs
, const char *s
)
90 fs
->passno
= fs
->freq
= 0;
94 if (!p
|| (rc
= __mnt_fs_set_source_ptr(fs
, p
))) {
95 DBG(TAB
, ul_debug("tab parse error: [source]"));
99 s
= skip_separator(s
);
102 fs
->target
= unmangle(s
, &s
);
104 DBG(TAB
, ul_debug("tab parse error: [target]"));
108 s
= skip_separator(s
);
112 if (!p
|| (rc
= __mnt_fs_set_fstype_ptr(fs
, p
))) {
113 DBG(TAB
, ul_debug("tab parse error: [fstype]"));
117 s
= skip_separator(s
);
119 /* (4) options (optional) */
121 if (p
&& (rc
= mnt_fs_set_options(fs
, p
))) {
122 DBG(TAB
, ul_debug("tab parse error: [options]"));
128 s
= skip_separator(s
);
132 /* (5) freq (optional) */
133 s
= next_number(s
, &fs
->freq
, &rc
);
135 DBG(TAB
, ul_debug("tab parse error: [freq]"));
139 s
= skip_separator(s
);
143 /* (6) freq (optional) */
144 s
= next_number(s
, &fs
->passno
, &rc
);
146 DBG(TAB
, ul_debug("tab parse error: [passno]"));
155 DBG(TAB
, ul_debug("tab parse error on: '%s' [rc=%d]", s
, rc
));
161 * Parses one line from a mountinfo file
163 static int mnt_parse_mountinfo_line(struct libmnt_fs
*fs
, const char *s
)
166 unsigned int maj
, min
;
169 fs
->flags
|= MNT_FS_KERNEL
;
172 s
= next_number(s
, &fs
->id
, &rc
);
173 if (!s
|| !*s
|| rc
) {
174 DBG(TAB
, ul_debug("tab parse error: [id]"));
178 s
= skip_separator(s
);
181 s
= next_number(s
, &fs
->parent
, &rc
);
182 if (!s
|| !*s
|| rc
) {
183 DBG(TAB
, ul_debug("tab parse error: [parent]"));
187 s
= skip_separator(s
);
190 if (sscanf(s
, "%u:%u", &maj
, &min
) != 2) {
191 DBG(TAB
, ul_debug("tab parse error: [maj:min]"));
194 fs
->devno
= makedev(maj
, min
);
195 s
= skip_nonspearator(s
);
196 s
= skip_separator(s
);
199 fs
->root
= unmangle(s
, &s
);
201 DBG(TAB
, ul_debug("tab parse error: [mountroot]"));
205 s
= skip_separator(s
);
208 fs
->target
= unmangle(s
, &s
);
210 DBG(TAB
, ul_debug("tab parse error: [target]"));
214 /* remove "\040(deleted)" suffix */
215 p
= (char *) endswith(fs
->target
, PATH_DELETED_SUFFIX
);
219 s
= skip_separator(s
);
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]"));
228 /* (7) optional fields, terminated by " - " */
229 p
= strstr(s
, " - ");
231 DBG(TAB
, ul_debug("mountinfo parse error: separator not found"));
235 fs
->opt_fields
= strndup(s
+ 1, p
- s
- 1);
237 s
= skip_separator(p
+ 3);
241 if (!p
|| (rc
= __mnt_fs_set_fstype_ptr(fs
, p
))) {
242 DBG(TAB
, ul_debug("tab parse error: [fstype]"));
246 /* (9) source -- maybe empty string */
248 DBG(TAB
, ul_debug("tab parse error: [source]"));
250 } else if (*s
== ' ' && *(s
+1) == ' ') {
251 if ((rc
= mnt_fs_set_source(fs
, ""))) {
252 DBG(TAB
, ul_debug("tab parse error: [empty source]"));
256 s
= skip_separator(s
);
258 if (!p
|| (rc
= __mnt_fs_set_source_ptr(fs
, p
))) {
259 DBG(TAB
, ul_debug("tab parse error: [regular source]"));
264 s
= skip_separator(s
);
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]"));
273 /* merge VFS and FS options to one string */
274 fs
->optstr
= mnt_fs_strdup_options(fs
);
277 DBG(TAB
, ul_debug("tab parse error: [merge VFS and FS options]"));
285 DBG(TAB
, ul_debug("tab parse error on: '%s' [rc=%d]", s
, rc
));
290 * Parses one line from utab file
292 static int mnt_parse_utab_line(struct libmnt_fs
*fs
, const char *s
)
302 const char *end
= NULL
;
304 while (*p
== ' ') p
++;
308 if (!fs
->source
&& !strncmp(p
, "SRC=", 4)) {
309 char *v
= unmangle(p
+ 4, &end
);
312 __mnt_fs_set_source_ptr(fs
, v
);
314 } else if (!fs
->target
&& !strncmp(p
, "TARGET=", 7)) {
315 fs
->target
= unmangle(p
+ 7, &end
);
319 } else if (!fs
->root
&& !strncmp(p
, "ROOT=", 5)) {
320 fs
->root
= unmangle(p
+ 5, &end
);
324 } else if (!fs
->bindsrc
&& !strncmp(p
, "BINDSRC=", 8)) {
325 fs
->bindsrc
= unmangle(p
+ 8, &end
);
329 } else if (!fs
->user_optstr
&& !strncmp(p
, "OPTS=", 5)) {
330 fs
->user_optstr
= unmangle(p
+ 5, &end
);
331 if (!fs
->user_optstr
)
334 } else if (!fs
->attrs
&& !strncmp(p
, "ATTRS=", 6)) {
335 fs
->attrs
= unmangle(p
+ 6, &end
);
340 /* unknown variable */
341 while (*p
&& *p
!= ' ') p
++;
349 DBG(TAB
, ul_debug("utab parse error: ENOMEM"));
354 * Parses one line from /proc/swaps
356 static int mnt_parse_swaps_line(struct libmnt_fs
*fs
, const char *s
)
362 rc
= sscanf(s
, UL_SCNsA
" " /* (1) source */
363 UL_SCNsA
" " /* (2) type */
380 /* remove "\040(deleted)" suffix */
382 if (sz
> PATH_DELETED_SUFFIX_SZ
) {
383 char *p
= src
+ (sz
- PATH_DELETED_SUFFIX_SZ
);
384 if (strcmp(p
, PATH_DELETED_SUFFIX
) == 0)
388 unmangle_string(src
);
390 rc
= mnt_fs_set_source(fs
, src
);
392 mnt_fs_set_fstype(fs
, "swap");
394 DBG(TAB
, ul_debug("tab parse error: [sscanf rc=%d]: '%s'", rc
, s
));
405 * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*)
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
411 * mountinfo: "<number> <number> ... "
413 static int guess_table_format(const char *line
)
417 DBG(TAB
, ul_debug("trying to guess table type"));
419 if (sscanf(line
, "%u %u", &a
, &b
) == 2)
420 return MNT_FMT_MOUNTINFO
;
422 if (strncmp(line
, "Filename\t", 9) == 0)
423 return MNT_FMT_SWAPS
;
425 return MNT_FMT_FSTAB
; /* fstab, mtab or /proc/mounts */
428 static int is_comment_line(const char *line
)
430 const char *p
= skip_blank(line
);
432 if (p
&& (*p
== '#' || *p
== '\n'))
437 /* returns 1 if the last line in the @str is blank */
438 static int is_terminated_by_blank(const char *str
)
440 size_t sz
= str
? strlen(str
) : 0;
441 const char *p
= sz
? str
+ (sz
- 1) : NULL
;
443 if (!sz
|| !p
|| *p
!= '\n')
444 return 0; /* empty or not terminated by '\n' */
446 return 1; /* only '\n' */
448 while (p
>= str
&& (*p
== ' ' || *p
== '\t'))
450 return *p
== '\n' ? 1 : 0;
454 * Reads the next line from the file.
456 * Returns 0 if the line is a comment
457 * 1 if the line is not a comment
460 static int next_comment_line(struct libmnt_parser
*pa
, char **last
)
462 if (getline(&pa
->buf
, &pa
->bufsiz
, pa
->f
) < 0)
463 return feof(pa
->f
) ? 1 : -errno
;
466 *last
= strchr(pa
->buf
, '\n');
468 return is_comment_line(pa
->buf
) ? 0 : 1;
471 static int append_comment(struct libmnt_table
*tb
,
472 struct libmnt_fs
*fs
,
476 int rc
, intro
= mnt_table_get_nents(tb
) == 0;
478 if (intro
&& is_terminated_by_blank(mnt_table_get_intro_comment(tb
)))
481 DBG(TAB
, ul_debugobj(tb
, "appending %s comment",
483 eof
? "trailing" : "fs"));
485 rc
= mnt_table_append_intro_comment(tb
, comm
);
487 rc
= mnt_table_set_trailing_comment(tb
,
488 mnt_fs_get_comment(fs
));
490 rc
= mnt_table_append_trailing_comment(tb
, comm
);
492 rc
= mnt_fs_set_comment(fs
, NULL
);
494 rc
= mnt_fs_append_comment(fs
, comm
);
499 * Read and parse the next line from {fs,m}tab or mountinfo
501 static int mnt_table_parse_next(struct libmnt_parser
*pa
,
502 struct libmnt_table
*tb
,
503 struct libmnt_fs
*fs
)
512 /* read the next non-blank non-comment line */
515 if (getline(&pa
->buf
, &pa
->bufsiz
, pa
->f
) < 0)
518 s
= strchr(pa
->buf
, '\n');
520 /* Missing final newline? Otherwise an extremely */
521 /* long line - assume file was corrupted */
523 DBG(TAB
, ul_debugobj(tb
,
524 "%s: no final newline", pa
->filename
));
525 s
= strchr(pa
->buf
, '\0');
527 DBG(TAB
, ul_debugobj(tb
,
528 "%s:%zu: missing newline at line",
529 pa
->filename
, pa
->line
));
534 /* comments parser */
536 && (tb
->fmt
== MNT_FMT_GUESS
|| tb
->fmt
== MNT_FMT_FSTAB
)
537 && is_comment_line(pa
->buf
)) {
539 rc
= append_comment(tb
, fs
, pa
->buf
, feof(pa
->f
));
541 rc
= next_comment_line(pa
, &s
);
544 if (rc
== 1 && feof(pa
->f
))
545 rc
= append_comment(tb
, fs
, NULL
, 1);
552 if (--s
>= pa
->buf
&& *s
== '\r')
554 s
= (char *) skip_blank(pa
->buf
);
555 } while (*s
== '\0' || *s
== '#');
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 */
565 rc
= mnt_parse_table_line(fs
, s
);
567 case MNT_FMT_MOUNTINFO
:
568 rc
= mnt_parse_mountinfo_line(fs
, s
);
571 rc
= mnt_parse_utab_line(fs
, s
);
574 if (strncmp(s
, "Filename\t", 9) == 0)
575 goto next_line
; /* skip swap header */
576 rc
= mnt_parse_swaps_line(fs
, s
);
579 rc
= -1; /* unknown format */
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"));
591 /* by default all errors are recoverable, otherwise behavior depends on
592 * the errcb() function. See mnt_table_set_parser_errcb().
594 return tb
->errcb
? tb
->errcb(tb
, pa
->filename
, pa
->line
) : 1;
597 static pid_t
path_to_tid(const char *filename
)
599 char *path
= mnt_resolve_path(filename
, NULL
);
600 char *p
, *end
= NULL
;
605 p
= strrchr(path
, '/');
609 p
= strrchr(path
, '/');
615 tid
= strtol(p
, &end
, 10);
616 if (errno
|| p
== end
|| (end
&& *end
)) {
620 DBG(TAB
, ul_debug("TID for %s is %d", filename
, tid
));
626 static int kernel_fs_postparse(struct libmnt_table
*tb
,
627 struct libmnt_fs
*fs
, pid_t
*tid
,
628 const char *filename
)
631 const char *src
= mnt_fs_get_srcpath(fs
);
633 /* This is a filesystem description from /proc, so we're in some process
634 * namespace. Let's remember the process PID.
636 if (filename
&& *tid
== -1)
637 *tid
= path_to_tid(filename
);
642 * Convert obscure /dev/root to something more usable
644 if (src
&& strcmp(src
, "/dev/root") == 0) {
647 rc
= mnt_guess_system_root(fs
->devno
, tb
->cache
, &real
);
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
);
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 */
666 * mnt_table_parse_stream:
669 * @filename: filename used for debug and error messages
671 * Returns: 0 on success, negative number in case of error.
673 int mnt_table_parse_stream(struct libmnt_table
*tb
, FILE *f
, const char *filename
)
678 struct libmnt_fs
*fs
= NULL
;
679 struct libmnt_parser pa
= { .line
= 0 };
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"));
689 pa
.filename
= filename
;
692 /* necessary for /proc/mounts only, the /proc/self/mountinfo
693 * parser sets the flag properly
695 if (filename
&& strcmp(filename
, _PATH_PROC_MOUNTS
) == 0)
696 flags
= MNT_FS_KERNEL
;
705 rc
= mnt_table_parse_next(&pa
, tb
, fs
);
707 if (!rc
&& tb
->fltrcb
&& tb
->fltrcb(fs
, tb
->fltrcb_data
))
708 rc
= 1; /* filtered out by callback... */
711 rc
= mnt_table_add_fs(tb
, fs
);
714 if (rc
== 0 && tb
->fmt
== MNT_FMT_MOUNTINFO
) {
715 rc
= kernel_fs_postparse(tb
, fs
, &tid
, filename
);
717 mnt_table_remove_fs(tb
, fs
);
724 assert(fs
->refcount
== 1);
725 continue; /* recoverable error, reuse fs*/
731 goto err
; /* fatal error */
737 DBG(TAB
, ul_debugobj(tb
, "%s: stop parsing (%d entries)",
738 filename
, mnt_table_get_nents(tb
)));
742 DBG(TAB
, ul_debugobj(tb
, "%s: parse error (rc=%d)", filename
, rc
));
748 * mnt_table_parse_file:
752 * Parses the whole table (e.g. /etc/fstab) and appends new records to the @tab.
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()).
757 * Returns: 0 on success, negative number in case of error.
759 int mnt_table_parse_file(struct libmnt_table
*tb
, const char *filename
)
764 if (!filename
|| !tb
)
767 f
= fopen(filename
, "r" UL_CLOEXECSTR
);
769 rc
= mnt_table_parse_stream(tb
, f
, filename
);
774 DBG(TAB
, ul_debugobj(tb
, "parsing done [filename=%s, rc=%d]", filename
, rc
));
778 static int mnt_table_parse_dir_filter(const struct dirent
*d
)
782 #ifdef _DIRENT_HAVE_D_TYPE
783 if (d
->d_type
!= DT_UNKNOWN
&& d
->d_type
!= DT_REG
&&
787 if (*d
->d_name
== '.')
790 #define MNT_MNTTABDIR_EXTSIZ (sizeof(MNT_MNTTABDIR_EXT) - 1)
792 namesz
= strlen(d
->d_name
);
793 if (!namesz
|| namesz
< MNT_MNTTABDIR_EXTSIZ
+ 1 ||
794 strcmp(d
->d_name
+ (namesz
- MNT_MNTTABDIR_EXTSIZ
),
802 #ifdef HAVE_SCANDIRAT
803 static int __mnt_table_parse_dir(struct libmnt_table
*tb
, const char *dirname
)
807 struct dirent
**namelist
= NULL
;
809 dd
= open(dirname
, O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
813 n
= scandirat(dd
, ".", &namelist
, mnt_table_parse_dir_filter
, versionsort
);
819 for (i
= 0; i
< n
; i
++) {
820 struct dirent
*d
= namelist
[i
];
824 if (fstatat(dd
, d
->d_name
, &st
, 0) ||
825 !S_ISREG(st
.st_mode
))
828 f
= fopen_at(dd
, d
->d_name
, O_RDONLY
|O_CLOEXEC
, "r" UL_CLOEXECSTR
);
830 mnt_table_parse_stream(tb
, f
, d
->d_name
);
835 for (i
= 0; i
< n
; i
++)
842 static int __mnt_table_parse_dir(struct libmnt_table
*tb
, const char *dirname
)
846 struct dirent
**namelist
= NULL
;
848 n
= scandir(dirname
, &namelist
, mnt_table_parse_dir_filter
, versionsort
);
852 /* let's use "at" functions rather than playing crazy games with paths... */
853 dir
= opendir(dirname
);
859 for (i
= 0; i
< n
; i
++) {
860 struct dirent
*d
= namelist
[i
];
864 if (fstatat(dirfd(dir
), d
->d_name
, &st
, 0) ||
865 !S_ISREG(st
.st_mode
))
868 f
= fopen_at(dirfd(dir
), d
->d_name
,
869 O_RDONLY
|O_CLOEXEC
, "r" UL_CLOEXECSTR
);
871 mnt_table_parse_stream(tb
, f
, d
->d_name
);
877 for (i
= 0; i
< n
; i
++)
887 * mnt_table_parse_dir:
889 * @dirname: 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
896 * Returns: 0 on success or negative number in case of error.
898 int mnt_table_parse_dir(struct libmnt_table
*tb
, const char *dirname
)
900 return __mnt_table_parse_dir(tb
, dirname
);
903 struct libmnt_table
*__mnt_new_table_from_file(const char *filename
, int fmt
, int empty_for_enoent
)
905 struct libmnt_table
*tb
;
910 if (stat(filename
, &st
))
911 return empty_for_enoent
? mnt_new_table() : NULL
;
913 tb
= mnt_new_table();
915 DBG(TAB
, ul_debugobj(tb
, "new tab for file: %s", filename
));
917 if (mnt_table_parse_file(tb
, filename
) != 0) {
926 * mnt_new_table_from_file:
927 * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path
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.
934 * Returns: newly allocated tab on success and NULL in case of error.
936 struct libmnt_table
*mnt_new_table_from_file(const char *filename
)
941 return __mnt_new_table_from_file(filename
, MNT_FMT_GUESS
, 0);
945 * mnt_new_table_from_dir
946 * @dirname: directory with *.fstab files
948 * Returns: newly allocated tab on success and NULL in case of error.
950 struct libmnt_table
*mnt_new_table_from_dir(const char *dirname
)
952 struct libmnt_table
*tb
;
956 tb
= mnt_new_table();
957 if (tb
&& mnt_table_parse_dir(tb
, dirname
) != 0) {
965 * mnt_table_set_parser_errcb:
966 * @tb: pointer to table
967 * @cb: pointer to callback function
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
974 * <0 : fatal error (abort parsing)
975 * 0 : success (parsing continues)
976 * >0 : recoverable error (the line is ignored, parsing continues).
978 * Returns: 0 on success or negative number in case of error.
980 int mnt_table_set_parser_errcb(struct libmnt_table
*tb
,
981 int (*cb
)(struct libmnt_table
*tb
, const char *filename
, int line
))
990 * Filter out entries during tab file parsing. If @cb returns 1, then the entry
993 int mnt_table_set_parser_fltrcb(struct libmnt_table
*tb
,
994 int (*cb
)(struct libmnt_fs
*, void *),
1000 DBG(TAB
, ul_debugobj(tb
, "%s table parser filter", cb
? "set" : "unset"));
1002 tb
->fltrcb_data
= data
;
1007 * mnt_table_parse_swaps:
1009 * @filename: overwrites default (/proc/swaps or $LIBMOUNT_SWAPS) or NULL
1011 * This function parses /proc/swaps and appends new lines to the @tab.
1013 * See also mnt_table_set_parser_errcb().
1015 * Returns: 0 on success or negative number in case of error.
1017 int mnt_table_parse_swaps(struct libmnt_table
*tb
, const char *filename
)
1022 filename
= mnt_get_swaps_path();
1027 tb
->fmt
= MNT_FMT_SWAPS
;
1029 return mnt_table_parse_file(tb
, filename
);
1033 * mnt_table_parse_fstab:
1035 * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL
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.
1040 * See also mnt_table_set_parser_errcb().
1042 * Returns: 0 on success or negative number in case of error.
1044 int mnt_table_parse_fstab(struct libmnt_table
*tb
, const char *filename
)
1052 filename
= mnt_get_fstab_path();
1054 if (!filename
|| stat(filename
, &st
))
1057 tb
->fmt
= MNT_FMT_FSTAB
;
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
);
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).
1073 * Note that @uf must contain only user specific mount options instead of
1074 * VFS options (note that FS options are ignored).
1076 * Returns modified filesystem (from @tb) or NULL.
1078 static struct libmnt_fs
*mnt_table_merge_user_fs(struct libmnt_table
*tb
, struct libmnt_fs
*uf
)
1080 struct libmnt_fs
*fs
;
1081 struct libmnt_iter itr
;
1082 const char *optstr
, *src
, *target
, *root
, *attrs
;
1087 DBG(TAB
, ul_debugobj(tb
, "merging user fs"));
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
);
1095 if (!src
|| !target
|| !root
|| (!attrs
&& !optstr
))
1098 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
1100 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1101 const char *r
= mnt_fs_get_root(fs
);
1103 if (fs
->flags
& MNT_FS_MERGED
)
1106 if (r
&& strcmp(r
, root
) == 0
1107 && mnt_fs_streq_target(fs
, target
)
1108 && mnt_fs_streq_srcpath(fs
, src
))
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
;
1119 DBG(TAB
, ul_debugobj(tb
, "found fs:"));
1120 DBG(TAB
, mnt_fs_print_debug(fs
, stderr
));
1125 /* default filename is /proc/self/mountinfo
1127 int __mnt_table_parse_mtab(struct libmnt_table
*tb
, const char *filename
,
1128 struct libmnt_table
*u_tb
)
1130 int rc
= 0, priv_utab
= 0;
1135 DBG(TAB
, ul_debugobj(tb
, "%s requested as mtab", filename
));
1137 #ifdef USE_LIBMOUNT_SUPPORT_MTAB
1138 if (mnt_has_regular_mtab(&filename
, NULL
)) {
1140 DBG(TAB
, ul_debugobj(tb
, "force mtab usage [filename=%s]", filename
));
1142 rc
= mnt_table_parse_file(tb
, filename
);
1145 * If @filename forces us to read from /proc then also read
1146 * utab file to merge userspace mount options.
1148 if (rc
== 0 && is_mountinfo(tb
))
1153 filename
= NULL
; /* failed */
1155 filename
= NULL
; /* mtab useless */
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"));
1163 tb
->fmt
= MNT_FMT_GUESS
;
1165 rc
= mnt_table_parse_file(tb
, filename
);
1167 /* hmm, old kernel? ...try /proc/mounts */
1168 tb
->fmt
= MNT_FMT_MTAB
;
1169 return mnt_table_parse_file(tb
, _PATH_PROC_MOUNTS
);
1172 if (!is_mountinfo(tb
))
1174 #ifdef USE_LIBMOUNT_SUPPORT_MTAB
1177 DBG(TAB
, ul_debugobj(tb
, "mtab parse: #2 read utab"));
1179 if (mnt_table_get_nents(tb
) == 0)
1180 return 0; /* empty, ignore utab */
1182 * try to read the user specific information from /run/mount/utabs
1185 const char *utab
= mnt_get_utab_path();
1187 if (!utab
|| is_file_empty(utab
))
1190 u_tb
= mnt_new_table();
1194 u_tb
->fmt
= MNT_FMT_UTAB
;
1195 mnt_table_set_parser_fltrcb(u_tb
, tb
->fltrcb
, tb
->fltrcb_data
);
1197 rc
= mnt_table_parse_file(u_tb
, utab
);
1201 DBG(TAB
, ul_debugobj(tb
, "mtab parse: #3 merge utab"));
1204 struct libmnt_fs
*u_fs
;
1205 struct libmnt_iter itr
;
1207 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
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
);
1216 mnt_unref_table(u_tb
);
1220 * mnt_table_parse_mtab:
1222 * @filename: overwrites default or NULL
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.
1228 * If libmount is compiled with classic mtab file support, and the /etc/mtab is
1229 * a regular file then this file is parsed.
1231 * It's strongly recommended to use NULL as a @filename to keep code portable.
1233 * See also mnt_table_set_parser_errcb().
1235 * Returns: 0 on success or negative number in case of error.
1237 int mnt_table_parse_mtab(struct libmnt_table
*tb
, const char *filename
)
1239 return __mnt_table_parse_mtab(tb
, filename
, NULL
);