2 * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
11 #endif /* !__USE_GNU */
12 #endif /* HAVE_SCANDIRAT */
22 #include "pathnames.h"
25 static inline char *skip_spaces(char *s
)
29 while (*s
== ' ' || *s
== '\t')
34 static int next_number(char **s
, int *num
)
44 *num
= strtol(*s
, &end
, 10);
45 if (end
== NULL
|| *s
== end
)
50 /* valid end of number is a space or a terminator */
51 if (*end
== ' ' || *end
== '\t' || *end
== '\0')
57 * Parses one line from {fs,m}tab
59 static int mnt_parse_table_line(struct libmnt_fs
*fs
, char *s
)
62 char *src
= NULL
, *fstype
= NULL
, *optstr
= NULL
;
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 */
77 if (rc
== 3 || rc
== 4) { /* options are optional */
79 unmangle_string(fs
->target
);
80 unmangle_string(fstype
);
82 if (optstr
&& *optstr
)
83 unmangle_string(optstr
);
85 /* note that __foo functions do not reallocate the string
87 rc
= __mnt_fs_set_source_ptr(fs
, src
);
90 rc
= __mnt_fs_set_fstype_ptr(fs
, fstype
);
95 rc
= mnt_fs_set_options(fs
, optstr
);
99 DBG(TAB
, mnt_debug("tab parse error: [sscanf rc=%d]: '%s'", rc
, s
));
107 DBG(TAB
, mnt_debug("tab parse error: [set vars, rc=%d]\n", rc
));
108 return rc
; /* error */
111 fs
->passno
= fs
->freq
= 0;
114 s
= skip_spaces(s
+ n
);
115 if (xrc
== 4 && *s
) {
116 if (next_number(&s
, &fs
->freq
) != 0) {
118 DBG(TAB
, mnt_debug("tab parse error: [freq]"));
121 } else if (next_number(&s
, &fs
->passno
) != 0 && *s
) {
122 DBG(TAB
, mnt_debug("tab parse error: [passno]"));
131 * Parses one line from a mountinfo file
133 static int mnt_parse_mountinfo_line(struct libmnt_fs
*fs
, char *s
)
136 unsigned int maj
, min
;
137 char *fstype
= NULL
, *src
= NULL
, *p
;
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 */
155 if (rc
>= 7 && end
> 0)
158 /* (7) optional fields, terminated by " - " */
159 p
= strstr(s
, " - ");
161 DBG(TAB
, mnt_debug("mountinfo parse error: separator not found"));
165 fs
->opt_fields
= strndup(s
+ 1, p
- s
- 1);
168 rc
+= sscanf(s
, UL_SCNsA
" " /* (8) FS type */
169 UL_SCNsA
" " /* (9) source */
170 UL_SCNsA
, /* (10) fs options (fs specific) */
177 fs
->flags
|= MNT_FS_KERNEL
;
178 fs
->devno
= makedev(maj
, min
);
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
);
187 rc
= __mnt_fs_set_fstype_ptr(fs
, fstype
);
190 rc
= __mnt_fs_set_source_ptr(fs
, src
);
195 /* merge VFS and FS options to one string */
196 fs
->optstr
= mnt_fs_strdup_options(fs
);
203 "mountinfo parse error [sscanf rc=%d]: '%s'", rc
, s
));
210 * Parses one line from utab file
212 static int mnt_parse_utab_line(struct libmnt_fs
*fs
, const char *s
)
224 while (*p
== ' ') p
++;
228 if (!fs
->source
&& !strncmp(p
, "SRC=", 4)) {
229 char *v
= unmangle(p
+ 4, &end
);
232 __mnt_fs_set_source_ptr(fs
, v
);
234 } else if (!fs
->target
&& !strncmp(p
, "TARGET=", 7)) {
235 fs
->target
= unmangle(p
+ 7, &end
);
239 } else if (!fs
->root
&& !strncmp(p
, "ROOT=", 5)) {
240 fs
->root
= unmangle(p
+ 5, &end
);
244 } else if (!fs
->bindsrc
&& !strncmp(p
, "BINDSRC=", 8)) {
245 fs
->bindsrc
= unmangle(p
+ 8, &end
);
249 } else if (!fs
->user_optstr
&& !strncmp(p
, "OPTS=", 5)) {
250 fs
->user_optstr
= unmangle(p
+ 5, &end
);
251 if (!fs
->user_optstr
)
254 } else if (!fs
->attrs
&& !strncmp(p
, "ATTRS=", 6)) {
255 fs
->attrs
= unmangle(p
+ 6, &end
);
260 /* unknown variable */
261 while (*p
&& *p
!= ' ') p
++;
269 DBG(TAB
, mnt_debug("utab parse error: ENOMEM"));
274 * Parses one line from /proc/swaps
276 static int mnt_parse_swaps_line(struct libmnt_fs
*fs
, char *s
)
282 rc
= sscanf(s
, UL_SCNsA
" " /* (1) source */
283 UL_SCNsA
" " /* (2) type */
300 unmangle_string(src
);
302 /* remove "(deleted)" suffix */
304 if (sz
> PATH_DELETED_SUFFIX_SZ
) {
305 char *p
= src
+ (sz
- PATH_DELETED_SUFFIX_SZ
);
306 if (strcmp(p
, PATH_DELETED_SUFFIX
) == 0)
310 rc
= mnt_fs_set_source(fs
, src
);
312 mnt_fs_set_fstype(fs
, "swap");
315 DBG(TAB
, mnt_debug("tab parse error: [sscanf rc=%d]: '%s'", rc
, s
));
324 * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*)
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
330 * mountinfo: "<number> <number> ... "
332 static int guess_table_format(char *line
)
336 DBG(TAB
, mnt_debug("trying to guess table type"));
338 if (sscanf(line
, "%u %u", &a
, &b
) == 2)
339 return MNT_FMT_MOUNTINFO
;
341 if (strncmp(line
, "Filename\t", 9) == 0)
342 return MNT_FMT_SWAPS
;
344 return MNT_FMT_FSTAB
; /* fstab, mtab or /proc/mounts */
347 static int is_comment_line(char *line
)
349 char *p
= skip_spaces(line
);
351 if (p
&& (*p
== '#' || *p
== '\n'))
356 /* returns 1 if the last line in the @str is blank */
357 static int is_terminated_by_blank(const char *str
)
359 size_t sz
= str
? strlen(str
) : 0;
360 const char *p
= sz
? str
+ (sz
- 1) : NULL
;
362 if (!sz
|| !p
|| *p
!= '\n')
363 return 0; /* empty or not terminated by '\n' */
365 return 1; /* only '\n' */
367 while (p
>= str
&& (*p
== ' ' || *p
== '\t'))
369 return *p
== '\n' ? 1 : 0;
373 * Reads the next line from the file.
375 * Returns 0 if the line is a comment
376 * 1 if the line is not a comment
379 static int next_comment_line(char *buf
, size_t bufsz
,
380 FILE *f
, char **last
, int *nlines
)
382 if (fgets(buf
, bufsz
, f
) == NULL
)
383 return feof(f
) ? 1 : -EINVAL
;
386 *last
= strchr(buf
, '\n');
388 return is_comment_line(buf
) ? 0 : 1;
391 static int append_comment(struct libmnt_table
*tb
,
392 struct libmnt_fs
*fs
,
396 int rc
, intro
= mnt_table_get_nents(tb
) == 0;
398 if (intro
&& is_terminated_by_blank(mnt_table_get_intro_comment(tb
)))
401 DBG(TAB
, mnt_debug_h(tb
, "appending %s comment",
403 eof
? "trailing" : "fs"));
405 rc
= mnt_table_append_intro_comment(tb
, comm
);
407 rc
= mnt_table_set_trailing_comment(tb
,
408 mnt_fs_get_comment(fs
));
410 rc
= mnt_table_append_trailing_comment(tb
, comm
);
412 rc
= mnt_fs_set_comment(fs
, NULL
);
414 rc
= mnt_fs_append_comment(fs
, comm
);
419 * Read and parse the next line from {fs,m}tab or mountinfo
421 static int mnt_table_parse_next(struct libmnt_table
*tb
, FILE *f
,
422 struct libmnt_fs
*fs
,
423 const char *filename
, int *nlines
)
433 /* read the next non-blank non-comment line */
436 if (fgets(buf
, sizeof(buf
), f
) == NULL
)
439 s
= strchr (buf
, '\n');
441 /* Missing final newline? Otherwise an extremely */
442 /* long line - assume file was corrupted */
444 DBG(TAB
, mnt_debug_h(tb
,
445 "%s: no final newline", filename
));
446 s
= strchr (buf
, '\0');
448 DBG(TAB
, mnt_debug_h(tb
,
449 "%s:%d: missing newline at line",
455 /* comments parser */
457 && (tb
->fmt
== MNT_FMT_GUESS
|| tb
->fmt
== MNT_FMT_FSTAB
)
458 && is_comment_line(buf
)) {
460 rc
= append_comment(tb
, fs
, buf
, feof(f
));
462 rc
= next_comment_line(buf
,
467 if (rc
== 1 && feof(f
))
468 rc
= append_comment(tb
, fs
, NULL
, 1);
475 if (--s
>= buf
&& *s
== '\r')
477 s
= skip_spaces(buf
);
478 } while (*s
== '\0' || *s
== '#');
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 */
488 rc
= mnt_parse_table_line(fs
, s
);
490 case MNT_FMT_MOUNTINFO
:
491 rc
= mnt_parse_mountinfo_line(fs
, s
);
494 rc
= mnt_parse_utab_line(fs
, s
);
497 if (strncmp(s
, "Filename\t", 9) == 0)
498 goto next_line
; /* skip swap header */
499 rc
= mnt_parse_swaps_line(fs
, s
);
502 rc
= -1; /* unknown format */
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"));
514 /* by default all errors are recoverable, otherwise behavior depends on
515 * the errcb() function. See mnt_table_set_parser_errcb().
517 return tb
->errcb
? tb
->errcb(tb
, filename
, *nlines
) : 1;
520 static pid_t
path_to_tid(const char *filename
)
522 char *path
= mnt_resolve_path(filename
, NULL
);
523 char *p
, *end
= NULL
;
528 p
= strrchr(path
, '/');
532 p
= strrchr(path
, '/');
538 tid
= strtol(p
, &end
, 10);
539 if (errno
|| p
== end
|| (end
&& *end
)) {
543 DBG(TAB
, mnt_debug("TID for %s is %d", filename
, tid
));
549 static int kernel_fs_postparse(struct libmnt_table
*tb
,
550 struct libmnt_fs
*fs
, pid_t
*tid
,
551 const char *filename
)
554 const char *src
= mnt_fs_get_srcpath(fs
);
556 /* This is a filesystem description from /proc, so we're in some process
557 * namespace. Let's remember the process PID.
559 if (filename
&& *tid
== -1)
560 *tid
= path_to_tid(filename
);
565 * Convert obscure /dev/root to something more usable
567 if (src
&& strcmp(src
, "/dev/root") == 0) {
568 char *spec
= mnt_get_kernel_cmdline_option("root=");
571 DBG(TAB
, mnt_debug_h(tb
, "root FS: %s", spec
));
573 real
= mnt_resolve_spec(spec
, tb
->cache
);
575 DBG(TAB
, mnt_debug_h(tb
, "canonical root FS: %s", real
));
576 rc
= mnt_fs_set_source(fs
, real
);
587 * mnt_table_parse_stream:
590 * @filename: filename used for debug and error messages
592 * Returns: 0 on success, negative number in case of error.
594 int mnt_table_parse_stream(struct libmnt_table
*tb
, FILE *f
, const char *filename
)
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"));
609 /* necessary for /proc/mounts only, the /proc/self/mountinfo
610 * parser sets the flag properly
612 if (filename
&& strcmp(filename
, _PATH_PROC_MOUNTS
) == 0)
613 flags
= MNT_FS_KERNEL
;
616 struct libmnt_fs
*fs
= mnt_new_fs();
621 rc
= mnt_table_parse_next(tb
, f
, fs
, filename
, &nlines
);
623 if (!rc
&& tb
->fltrcb
&& tb
->fltrcb(fs
, tb
->fltrcb_data
))
624 rc
= 1; /* filtered out by callback... */
627 rc
= mnt_table_add_fs(tb
, fs
);
630 if (rc
== 0 && tb
->fmt
== MNT_FMT_MOUNTINFO
)
631 rc
= kernel_fs_postparse(tb
, fs
, &tid
, filename
);
637 continue; /* recoverable error */
640 goto err
; /* fatal error */
644 DBG(TAB
, mnt_debug_h(tb
, "%s: stop parsing (%d entries)",
645 filename
, mnt_table_get_nents(tb
)));
648 DBG(TAB
, mnt_debug_h(tb
, "%s: parse error (rc=%d)", filename
, rc
));
653 * mnt_table_parse_file:
657 * Parses the whole table (e.g. /etc/mtab) and appends new records to the @tab.
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()).
662 * Returns: 0 on success, negative number in case of error.
664 int mnt_table_parse_file(struct libmnt_table
*tb
, const char *filename
)
672 if (!filename
|| !tb
)
675 f
= fopen(filename
, "r" UL_CLOEXECSTR
);
677 rc
= mnt_table_parse_stream(tb
, f
, filename
);
685 static int mnt_table_parse_dir_filter(const struct dirent
*d
)
689 #ifdef _DIRENT_HAVE_D_TYPE
690 if (d
->d_type
!= DT_UNKNOWN
&& d
->d_type
!= DT_REG
&&
694 if (*d
->d_name
== '.')
697 #define MNT_MNTTABDIR_EXTSIZ (sizeof(MNT_MNTTABDIR_EXT) - 1)
699 namesz
= strlen(d
->d_name
);
700 if (!namesz
|| namesz
< MNT_MNTTABDIR_EXTSIZ
+ 1 ||
701 strcmp(d
->d_name
+ (namesz
- MNT_MNTTABDIR_EXTSIZ
),
709 #ifdef HAVE_SCANDIRAT
710 static int __mnt_table_parse_dir(struct libmnt_table
*tb
, const char *dirname
)
714 struct dirent
**namelist
= NULL
;
716 dd
= open(dirname
, O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
720 n
= scandirat(dd
, ".", &namelist
, mnt_table_parse_dir_filter
, versionsort
);
726 for (i
= 0; i
< n
; i
++) {
727 struct dirent
*d
= namelist
[i
];
731 if (fstat_at(dd
, ".", d
->d_name
, &st
, 0) ||
732 !S_ISREG(st
.st_mode
))
735 f
= fopen_at(dd
, ".", d
->d_name
, O_RDONLY
|O_CLOEXEC
, "r" UL_CLOEXECSTR
);
737 mnt_table_parse_stream(tb
, f
, d
->d_name
);
742 for (i
= 0; i
< n
; i
++)
749 static int __mnt_table_parse_dir(struct libmnt_table
*tb
, const char *dirname
)
753 struct dirent
**namelist
= NULL
;
755 n
= scandir(dirname
, &namelist
, mnt_table_parse_dir_filter
, versionsort
);
759 /* let's use "at" functions rather than playing crazy games with paths... */
760 dir
= opendir(dirname
);
766 for (i
= 0; i
< n
; i
++) {
767 struct dirent
*d
= namelist
[i
];
771 if (fstat_at(dirfd(dir
), _PATH_MNTTAB_DIR
, d
->d_name
, &st
, 0) ||
772 !S_ISREG(st
.st_mode
))
775 f
= fopen_at(dirfd(dir
), _PATH_MNTTAB_DIR
, d
->d_name
,
776 O_RDONLY
|O_CLOEXEC
, "r" UL_CLOEXECSTR
);
778 mnt_table_parse_stream(tb
, f
, d
->d_name
);
784 for (i
= 0; i
< n
; i
++)
794 * mnt_table_parse_dir:
796 * @dirname: 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
803 * Returns: 0 on success or negative number in case of error.
805 int mnt_table_parse_dir(struct libmnt_table
*tb
, const char *dirname
)
807 return __mnt_table_parse_dir(tb
, dirname
);
810 struct libmnt_table
*__mnt_new_table_from_file(const char *filename
, int fmt
)
812 struct libmnt_table
*tb
;
818 if (stat(filename
, &st
))
820 tb
= mnt_new_table();
823 if (mnt_table_parse_file(tb
, filename
) != 0) {
832 * mnt_new_table_from_file:
833 * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path
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.
840 * Returns: newly allocated tab on success and NULL in case of error.
842 struct libmnt_table
*mnt_new_table_from_file(const char *filename
)
845 return __mnt_new_table_from_file(filename
, MNT_FMT_GUESS
);
849 * mnt_new_table_from_dir
850 * @dirname: directory with *.fstab files
852 * Returns: newly allocated tab on success and NULL in case of error.
854 struct libmnt_table
*mnt_new_table_from_dir(const char *dirname
)
856 struct libmnt_table
*tb
;
861 tb
= mnt_new_table();
862 if (tb
&& mnt_table_parse_dir(tb
, dirname
) != 0) {
870 * mnt_table_set_parser_errcb:
871 * @tb: pointer to table
872 * @cb: pointer to callback function
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
879 * <0 : fatal error (abort parsing)
880 * 0 : success (parsing continues)
881 * >0 : recoverable error (the line is ignored, parsing continues).
883 * Returns: 0 on success or negative number in case of error.
885 int mnt_table_set_parser_errcb(struct libmnt_table
*tb
,
886 int (*cb
)(struct libmnt_table
*tb
, const char *filename
, int line
))
896 * Filter out entries during tab file parsing. If @cb returns 1, then the entry
899 int mnt_table_set_parser_fltrcb(struct libmnt_table
*tb
,
900 int (*cb
)(struct libmnt_fs
*, void *),
907 DBG(TAB
, mnt_debug_h(tb
, "%s table parser filter", cb
? "set" : "unset"));
909 tb
->fltrcb_data
= data
;
914 * mnt_table_parse_swaps:
916 * @filename: overwrites default (/proc/swaps or $LIBMOUNT_SWAPS) or NULL
918 * This function parses /proc/swaps and appends new lines to the @tab.
920 * See also mnt_table_set_parser_errcb().
922 * Returns: 0 on success or negative number in case of error.
924 int mnt_table_parse_swaps(struct libmnt_table
*tb
, const char *filename
)
931 filename
= mnt_get_swaps_path();
936 tb
->fmt
= MNT_FMT_SWAPS
;
938 return mnt_table_parse_file(tb
, filename
);
942 * mnt_table_parse_fstab:
944 * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL
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.
949 * See also mnt_table_set_parser_errcb().
951 * Returns: 0 on success or negative number in case of error.
953 int mnt_table_parse_fstab(struct libmnt_table
*tb
, const char *filename
)
962 filename
= mnt_get_fstab_path();
964 if (!filename
|| stat(filename
, &st
))
967 tb
->fmt
= MNT_FMT_FSTAB
;
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
);
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).
983 * Note that @uf must contain only user specific mount options instead of
984 * VFS options (note that FS options are ignored).
986 * Returns modified filesystem (from @tb) or NULL.
988 static struct libmnt_fs
*mnt_table_merge_user_fs(struct libmnt_table
*tb
, struct libmnt_fs
*uf
)
990 struct libmnt_fs
*fs
;
991 struct libmnt_iter itr
;
992 const char *optstr
, *src
, *target
, *root
, *attrs
;
999 DBG(TAB
, mnt_debug_h(tb
, "merging user fs"));
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
);
1007 if (!src
|| !target
|| !root
|| (!attrs
&& !optstr
))
1010 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
1012 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1013 const char *r
= mnt_fs_get_root(fs
);
1015 if (fs
->flags
& MNT_FS_MERGED
)
1018 if (r
&& strcmp(r
, root
) == 0
1019 && mnt_fs_streq_target(fs
, target
)
1020 && mnt_fs_streq_srcpath(fs
, src
))
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
;
1031 DBG(TAB
, mnt_debug_h(tb
, "found fs:"));
1032 DBG(TAB
, mnt_fs_print_debug(fs
, stderr
));
1038 * mnt_table_parse_mtab:
1040 * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL
1042 * This function parses /etc/mtab or /proc/self/mountinfo +
1043 * /run/mount/utabs or /proc/mounts.
1045 * See also mnt_table_set_parser_errcb().
1047 * Returns: 0 on success or negative number in case of error.
1049 int mnt_table_parse_mtab(struct libmnt_table
*tb
, const char *filename
)
1052 const char *utab
= NULL
;
1053 struct libmnt_table
*u_tb
;
1057 if (mnt_has_regular_mtab(&filename
, NULL
)) {
1059 DBG(TAB
, mnt_debug_h(tb
, "force %s usage", filename
));
1061 rc
= mnt_table_parse_file(tb
, filename
);
1064 filename
= NULL
; /* failed */
1069 * -- read kernel information from /proc/self/mountinfo
1071 tb
->fmt
= MNT_FMT_MOUNTINFO
;
1072 rc
= mnt_table_parse_file(tb
, _PATH_PROC_MOUNTINFO
);
1074 /* hmm, old kernel? ...try /proc/mounts */
1075 tb
->fmt
= MNT_FMT_MTAB
;
1076 return mnt_table_parse_file(tb
, _PATH_PROC_MOUNTS
);
1079 if (mnt_table_get_nents(tb
) == 0)
1080 return 0; /* empty, ignore utab */
1082 * try to read the user specific information from /run/mount/utabs
1084 utab
= mnt_get_utab_path();
1085 if (!utab
|| is_file_empty(utab
))
1088 u_tb
= mnt_new_table();
1092 u_tb
->fmt
= MNT_FMT_UTAB
;
1093 mnt_table_set_parser_fltrcb(u_tb
, tb
->fltrcb
, tb
->fltrcb_data
);
1095 if (mnt_table_parse_file(u_tb
, utab
) == 0) {
1096 struct libmnt_fs
*u_fs
;
1097 struct libmnt_iter itr
;
1099 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
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
);
1106 mnt_free_table(u_tb
);