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.
16
#include "pathnames.h"
18
static inline char *skip_spaces(char *s)
22
while (*s == ' ' || *s == '\t')
27
static int next_number(char **s, int *num)
37
*num = strtol(*s, &end, 10);
38
if (end == NULL || *s == end)
43
/* valid end of number is space or terminator */
44
if (*end == ' ' || *end == '\t' || *end == '\0')
50
* Parses one line from {fs,m}tab
52
static int mnt_parse_table_line(struct libmnt_fs *fs, char *s)
55
char *src, *fstype, *optstr;
57
rc = sscanf(s, UL_SCNsA" " /* (1) source */
58
UL_SCNsA" " /* (2) target */
59
UL_SCNsA" " /* (3) FS type */
60
UL_SCNsA" " /* (4) options */
61
"%n", /* byte count */
71
unmangle_string(fs->target);
72
unmangle_string(fstype);
73
unmangle_string(optstr);
75
rc = __mnt_fs_set_source_ptr(fs, src);
77
rc = __mnt_fs_set_fstype_ptr(fs, fstype);
79
rc = mnt_fs_set_options(fs, optstr);
82
DBG(TAB, mnt_debug("tab parse error: [sscanf rc=%d]: '%s'", rc, s));
87
return rc; /* error */
89
fs->passno = fs->freq = 0;
90
s = skip_spaces(s + n);
92
if (next_number(&s, &fs->freq) != 0) {
94
DBG(TAB, mnt_debug("tab parse error: [freq]"));
97
} else if (next_number(&s, &fs->passno) != 0 && *s) {
98
DBG(TAB, mnt_debug("tab parse error: [passno]"));
107
* Parses one line from mountinfo file
109
static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s)
112
unsigned int maj, min;
113
char *fstype, *src, *p;
115
rc = sscanf(s, "%u " /* (1) id */
116
"%u " /* (2) parent */
117
"%u:%u " /* (3) maj:min */
118
UL_SCNsA" " /* (4) mountroot */
119
UL_SCNsA" " /* (5) target */
120
UL_SCNsA /* (6) vfs options (fs-independent) */
121
"%n", /* number of read bytes */
131
if (rc >= 7 && end > 0)
134
/* (7) optional fields, terminated by " - " */
135
p = strstr(s, " - ");
137
DBG(TAB, mnt_debug("mountinfo parse error: not found separator"));
142
rc += sscanf(s, UL_SCNsA" " /* (8) FS type */
143
UL_SCNsA" " /* (9) source */
144
UL_SCNsA, /* (10) fs options (fs specific) */
151
fs->flags |= MNT_FS_KERNEL;
152
fs->devno = makedev(maj, min);
154
unmangle_string(fs->root);
155
unmangle_string(fs->target);
156
unmangle_string(fs->vfs_optstr);
157
unmangle_string(fstype);
158
unmangle_string(src);
160
if (!strcmp(fs->fs_optstr, "none")) {
162
fs->fs_optstr = NULL;
164
unmangle_string(fs->fs_optstr);
166
rc = __mnt_fs_set_fstype_ptr(fs, fstype);
168
rc = __mnt_fs_set_source_ptr(fs, src);
170
/* merge VFS and FS options to the one string */
171
fs->optstr = mnt_fs_strdup_options(fs);
176
"mountinfo parse error [sscanf rc=%d]: '%s'", rc, s));
183
* Parses one line from utab file
185
static int mnt_parse_utab_line(struct libmnt_fs *fs, const char *s)
197
while (*p == ' ') p++;
201
if (!fs->source && !strncmp(p, "SRC=", 4)) {
202
char *v = unmangle(p + 4, &end);
205
__mnt_fs_set_source_ptr(fs, v);
207
} else if (!fs->target && !strncmp(p, "TARGET=", 7)) {
208
fs->target = unmangle(p + 7, &end);
212
} else if (!fs->root && !strncmp(p, "ROOT=", 5)) {
213
fs->root = unmangle(p + 5, &end);
217
} else if (!fs->bindsrc && !strncmp(p, "BINDSRC=", 8)) {
218
fs->bindsrc = unmangle(p + 8, &end);
222
} else if (!fs->user_optstr && !strncmp(p, "OPTS=", 5)) {
223
fs->user_optstr = unmangle(p + 5, &end);
224
if (!fs->user_optstr)
227
} else if (!fs->attrs && !strncmp(p, "ATTRS=", 6)) {
228
fs->attrs = unmangle(p + 6, &end);
233
/* unknown variable */
234
while (*p && *p != ' ') p++;
242
DBG(TAB, mnt_debug("utab parse error: ENOMEM"));
247
* Returns {m,fs}tab or mountinfo file format (MNT_FMT_*)
249
* Note that we aren't trying to guess utab file format, because this file has
250
* to be always parsed by private libmount routines with explicitly defined
253
* mountinfo: "<number> <number> ... "
255
static int guess_table_format(char *line)
259
if (sscanf(line, "%u %u", &a, &b) == 2)
260
return MNT_FMT_MOUNTINFO;
262
return MNT_FMT_FSTAB; /* fstab, mtab or /proc/mounts */
266
* Read and parse the next line from {fs,m}tab or mountinfo
268
static int mnt_table_parse_next(struct libmnt_table *tb, FILE *f, struct libmnt_fs *fs,
269
const char *filename, int *nlines)
278
/* read the next non-blank non-comment line */
280
if (fgets(buf, sizeof(buf), f) == NULL)
283
s = index (buf, '\n');
285
/* Missing final newline? Otherwise extremely */
286
/* long line - assume file was corrupted */
288
DBG(TAB, mnt_debug_h(tb,
289
"%s: no final newline", filename));
290
s = index (buf, '\0');
292
DBG(TAB, mnt_debug_h(tb,
293
"%s:%d: missing newline at line",
299
if (--s >= buf && *s == '\r')
301
s = skip_spaces(buf);
302
} while (*s == '\0' || *s == '#');
304
if (tb->fmt == MNT_FMT_GUESS)
305
tb->fmt = guess_table_format(s);
307
if (tb->fmt == MNT_FMT_FSTAB) {
308
if (mnt_parse_table_line(fs, s) != 0)
311
} else if (tb->fmt == MNT_FMT_MOUNTINFO) {
312
if (mnt_parse_mountinfo_line(fs, s) != 0)
315
} else if (tb->fmt == MNT_FMT_UTAB) {
316
if (mnt_parse_utab_line(fs, s) != 0)
321
/*DBG(TAB, mnt_fs_print_debug(fs, stderr));*/
325
DBG(TAB, mnt_debug_h(tb, "%s:%d: %s parse error", filename, *nlines,
326
tb->fmt == MNT_FMT_MOUNTINFO ? "mountinfo" :
327
tb->fmt == MNT_FMT_FSTAB ? "tab" : "utab"));
329
/* by default all errors are recoverable, otherwise behavior depends on
330
* errcb() function. See mnt_table_set_parser_errcb().
332
return tb->errcb ? tb->errcb(tb, filename, *nlines) : 1;
336
* mnt_table_parse_stream:
339
* @filename: filename used for debug and error messages
341
* Returns: 0 on success, negative number in case of error.
343
int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
353
DBG(TAB, mnt_debug_h(tb, "%s: start parsing (%d entries)",
354
filename, mnt_table_get_nents(tb)));
356
/* necessary for /proc/mounts only, the /proc/self/mountinfo
357
* parser sets the flag properly
359
if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0)
360
flags = MNT_FS_KERNEL;
363
struct libmnt_fs *fs = mnt_new_fs();
368
rc = mnt_table_parse_next(tb, f, fs, filename, &nlines);
370
rc = mnt_table_add_fs(tb, fs);
376
continue; /* recoverable error */
379
goto err; /* fatal error */
383
DBG(TAB, mnt_debug_h(tb, "%s: stop parsing (%d entries)",
384
filename, mnt_table_get_nents(tb)));
387
DBG(TAB, mnt_debug_h(tb, "%s: parse error (rc=%d)", filename, rc));
392
* mnt_table_parse_file:
396
* Parses whole table (e.g. /etc/mtab) and appends new records to the @tab.
398
* The libmount parser ignores broken (syntax error) lines, these lines are
399
* reported to caller by errcb() function (see mnt_table_set_parser_errcb()).
401
* Returns: 0 on success, negative number in case of error.
403
int mnt_table_parse_file(struct libmnt_table *tb, const char *filename)
411
if (!filename || !tb)
414
f = fopen(filename, "r");
416
rc = mnt_table_parse_stream(tb, f, filename);
424
static int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname)
428
struct dirent **namelist = NULL;
430
/* TODO: it would be nice to have a scandir() implementation that
431
* is able to use already opened directory */
432
n = scandir(dirname, &namelist, NULL, versionsort);
436
/* let use "at" functions rather than play crazy games with paths... */
437
dir = opendir(dirname);
441
for (i = 0; i < n; i++) {
442
struct dirent *d = namelist[i];
447
#ifdef _DIRENT_HAVE_D_TYPE
448
if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG &&
452
if (*d->d_name == '.')
455
#define MNT_MNTTABDIR_EXTSIZ (sizeof(MNT_MNTTABDIR_EXT) - 1)
457
namesz = strlen(d->d_name);
458
if (!namesz || namesz < MNT_MNTTABDIR_EXTSIZ + 1 ||
459
strcmp(d->d_name + (namesz - MNT_MNTTABDIR_EXTSIZ),
463
if (fstat_at(dirfd(dir), _PATH_MNTTAB_DIR, d->d_name, &st, 0) ||
464
!S_ISREG(st.st_mode))
467
f = fopen_at(dirfd(dir), _PATH_MNTTAB_DIR,
468
d->d_name, O_RDONLY, "r");
470
mnt_table_parse_stream(tb, f, d->d_name);
475
for (i = 0; i < n; i++)
483
struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt)
485
struct libmnt_table *tb;
492
if (stat(filename, &st))
494
tb = mnt_new_table();
497
if (mnt_table_parse_file(tb, filename) != 0) {
506
* mnt_new_table_from_file:
507
* @filename: /etc/{m,fs}tab or /proc/self/mountinfo path
509
* Same as mnt_new_table() + mnt_table_parse_file(). Use this function for private
510
* files only. This function does not allow to use error callback, so you
511
* cannot provide any feedback to end-users about broken records in files (e.g.
514
* Returns: newly allocated tab on success and NULL in case of error.
516
struct libmnt_table *mnt_new_table_from_file(const char *filename)
518
return __mnt_new_table_from_file(filename, MNT_FMT_GUESS);
522
* mnt_new_table_from_dir
523
* @dirname: for example /etc/fstab.d
525
* Returns: newly allocated tab on success and NULL in case of error.
527
struct libmnt_table *mnt_new_table_from_dir(const char *dirname)
529
struct libmnt_table *tb;
535
tb = mnt_new_table();
536
if (tb && mnt_table_parse_dir(tb, dirname) != 0) {
544
* mnt_table_set_parser_errcb:
545
* @tb: pointer to table
546
* @cb: pointer to callback function
548
* The error callback function is called by table parser (mnt_table_parse_file())
549
* in case of syntax error. The callback function could be used for errors
550
* evaluation, libmount will continue/stop parsing according to callback return
553
* <0 : fatal error (abort parsing)
554
* 0 : success (parsing continue)
555
* >0 : recoverable error (the line is ignored, parsing continue).
557
* Returns: 0 on success or negative number in case of error.
559
int mnt_table_set_parser_errcb(struct libmnt_table *tb,
560
int (*cb)(struct libmnt_table *tb, const char *filename, int line))
568
* mnt_table_parse_fstab:
570
* @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL
572
* This function parses /etc/fstab or /etc/fstab.d and appends new lines to the
573
* @tab. If the system contains classic fstab file and also fstab.d directory
574
* then the fstab file is parsed before the fstab.d directory.
576
* The fstab.d directory:
577
* - files are sorted by strverscmp(3)
578
* - files that starts with "." are ignored (e.g. ".10foo.fstab")
579
* - files without the ".fstab" extension are ignored
581
* See also mnt_table_set_parser_errcb().
583
* Returns: 0 on success or negative number in case of error.
585
int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename)
594
filename = mnt_get_fstab_path();
596
tb->fmt = MNT_FMT_FSTAB;
598
f = fopen(filename, "r");
600
int rc = mnt_table_parse_stream(tb, f, filename);
606
if (strcmp(filename, _PATH_MNTTAB))
607
/* /etc/fstab.d sould be used together with /etc/fstab only */
611
if (!access(_PATH_MNTTAB_DIR, R_OK))
612
return mnt_table_parse_dir(tb, _PATH_MNTTAB_DIR);
617
* This function uses @uf to found corresponding record in @tb, then the record
618
* from @tb is updated (user specific mount options are added).
620
* Note that @uf must contain only user specific mount options instead of
621
* VFS options (note that FS options are ignored).
623
* Returns modified filesystem (from @tb) or NULL.
625
static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf)
627
struct libmnt_fs *fs;
628
struct libmnt_iter itr;
629
const char *optstr, *src, *target, *root, *attrs;
636
DBG(TAB, mnt_debug_h(tb, "merging user fs"));
638
src = mnt_fs_get_srcpath(uf);
639
target = mnt_fs_get_target(uf);
640
optstr = mnt_fs_get_user_options(uf);
641
attrs = mnt_fs_get_attributes(uf);
642
root = mnt_fs_get_root(uf);
644
if (!src || !target || !root || (!attrs && !optstr))
647
mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
649
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
650
const char *s = mnt_fs_get_srcpath(fs),
651
*t = mnt_fs_get_target(fs),
652
*r = mnt_fs_get_root(fs);
654
if (fs->flags & MNT_FS_MERGED)
657
if (s && t && r && !strcmp(t, target) &&
658
!strcmp(s, src) && !strcmp(r, root))
663
DBG(TAB, mnt_debug_h(tb, "found fs -- appending user optstr"));
664
mnt_fs_append_options(fs, optstr);
665
mnt_fs_append_attributes(fs, attrs);
666
mnt_fs_set_bindsrc(fs, mnt_fs_get_bindsrc(uf));
667
fs->flags |= MNT_FS_MERGED;
669
DBG(TAB, mnt_debug_h(tb, "found fs:"));
670
DBG(TAB, mnt_fs_print_debug(fs, stderr));
676
* mnt_table_parse_mtab:
678
* @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL
680
* This function parses /etc/mtab or /proc/self/mountinfo +
681
* /run/mount/utabs or /proc/mounts.
683
* See also mnt_table_set_parser_errcb().
685
* Returns: 0 on success or negative number in case of error.
687
int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename)
690
const char *utab = NULL;
692
if (mnt_has_regular_mtab(&filename, NULL)) {
694
DBG(TAB, mnt_debug_h(tb, "force %s usage", filename));
696
rc = mnt_table_parse_file(tb, filename);
699
filename = NULL; /* failed */
704
* -- read kernel information from /proc/self/mountinfo
706
tb->fmt = MNT_FMT_MOUNTINFO;
707
rc = mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO);
709
/* hmm, old kernel? ...try /proc/mounts */
710
tb->fmt = MNT_FMT_MTAB;
711
return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS);
715
* try to read user specific information from /run/mount/utabs
717
utab = mnt_get_utab_path();
719
struct libmnt_table *u_tb = __mnt_new_table_from_file(utab, MNT_FMT_UTAB);
722
struct libmnt_fs *u_fs;
723
struct libmnt_iter itr;
725
mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
727
/* merge user options into mountinfo from kernel */
728
while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0)
729
mnt_table_merge_user_fs(tb, u_fs);
731
mnt_free_table(u_tb);