~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to libmount/src/tab_parse.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#include <ctype.h>
 
9
#include <limits.h>
 
10
#include <dirent.h>
 
11
#include <fcntl.h>
 
12
 
 
13
#include "at.h"
 
14
#include "mangle.h"
 
15
#include "mountP.h"
 
16
#include "pathnames.h"
 
17
 
 
18
static inline char *skip_spaces(char *s)
 
19
{
 
20
        assert(s);
 
21
 
 
22
        while (*s == ' ' || *s == '\t')
 
23
                s++;
 
24
        return s;
 
25
}
 
26
 
 
27
static int next_number(char **s, int *num)
 
28
{
 
29
        char *end = NULL;
 
30
 
 
31
        assert(num);
 
32
        assert(s);
 
33
 
 
34
        *s = skip_spaces(*s);
 
35
        if (!**s)
 
36
                return -1;
 
37
        *num = strtol(*s, &end, 10);
 
38
        if (end == NULL || *s == end)
 
39
               return -1;
 
40
 
 
41
        *s = end;
 
42
 
 
43
        /* valid end of number is space or terminator */
 
44
        if (*end == ' ' || *end == '\t' || *end == '\0')
 
45
                return 0;
 
46
        return -1;
 
47
}
 
48
 
 
49
/*
 
50
 * Parses one line from {fs,m}tab
 
51
 */
 
52
static int mnt_parse_table_line(struct libmnt_fs *fs, char *s)
 
53
{
 
54
        int rc, n = 0;
 
55
        char *src, *fstype, *optstr;
 
56
 
 
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 */
 
62
 
 
63
                        &src,
 
64
                        &fs->target,
 
65
                        &fstype,
 
66
                        &optstr,
 
67
                        &n);
 
68
 
 
69
        if (rc == 4) {
 
70
                unmangle_string(src);
 
71
                unmangle_string(fs->target);
 
72
                unmangle_string(fstype);
 
73
                unmangle_string(optstr);
 
74
 
 
75
                rc = __mnt_fs_set_source_ptr(fs, src);
 
76
                if (!rc)
 
77
                        rc = __mnt_fs_set_fstype_ptr(fs, fstype);
 
78
                if (!rc)
 
79
                        rc = mnt_fs_set_options(fs, optstr);
 
80
                free(optstr);
 
81
        } else {
 
82
                DBG(TAB, mnt_debug("tab parse error: [sscanf rc=%d]: '%s'", rc, s));
 
83
                rc = -EINVAL;
 
84
        }
 
85
 
 
86
        if (rc)
 
87
                return rc;      /* error */
 
88
 
 
89
        fs->passno = fs->freq = 0;
 
90
        s = skip_spaces(s + n);
 
91
        if (*s) {
 
92
                if (next_number(&s, &fs->freq) != 0) {
 
93
                        if (*s) {
 
94
                                DBG(TAB, mnt_debug("tab parse error: [freq]"));
 
95
                                rc = -EINVAL;
 
96
                        }
 
97
                } else if (next_number(&s, &fs->passno) != 0 && *s) {
 
98
                        DBG(TAB, mnt_debug("tab parse error: [passno]"));
 
99
                        rc = -EINVAL;
 
100
                }
 
101
        }
 
102
 
 
103
        return rc;
 
104
}
 
105
 
 
106
/*
 
107
 * Parses one line from mountinfo file
 
108
 */
 
109
static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s)
 
110
{
 
111
        int rc, end = 0;
 
112
        unsigned int maj, min;
 
113
        char *fstype, *src, *p;
 
114
 
 
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 */
 
122
 
 
123
                        &fs->id,
 
124
                        &fs->parent,
 
125
                        &maj, &min,
 
126
                        &fs->root,
 
127
                        &fs->target,
 
128
                        &fs->vfs_optstr,
 
129
                        &end);
 
130
 
 
131
        if (rc >= 7 && end > 0)
 
132
                s += end;
 
133
 
 
134
        /* (7) optional fields, terminated by " - " */
 
135
        p = strstr(s, " - ");
 
136
        if (!p) {
 
137
                DBG(TAB, mnt_debug("mountinfo parse error: not found separator"));
 
138
                return -EINVAL;
 
139
        }
 
140
        s = p + 3;
 
141
 
 
142
        rc += sscanf(s, UL_SCNsA" "     /* (8) FS type */
 
143
                        UL_SCNsA" "     /* (9) source */
 
144
                        UL_SCNsA,       /* (10) fs options (fs specific) */
 
145
 
 
146
                        &fstype,
 
147
                        &src,
 
148
                        &fs->fs_optstr);
 
149
 
 
150
        if (rc >= 10) {
 
151
                fs->flags |= MNT_FS_KERNEL;
 
152
                fs->devno = makedev(maj, min);
 
153
 
 
154
                unmangle_string(fs->root);
 
155
                unmangle_string(fs->target);
 
156
                unmangle_string(fs->vfs_optstr);
 
157
                unmangle_string(fstype);
 
158
                unmangle_string(src);
 
159
 
 
160
                if (!strcmp(fs->fs_optstr, "none")) {
 
161
                        free(fs->fs_optstr);
 
162
                        fs->fs_optstr = NULL;
 
163
                } else
 
164
                        unmangle_string(fs->fs_optstr);
 
165
 
 
166
                rc = __mnt_fs_set_fstype_ptr(fs, fstype);
 
167
                if (!rc)
 
168
                        rc = __mnt_fs_set_source_ptr(fs, src);
 
169
 
 
170
                /* merge VFS and FS options to the one string */
 
171
                fs->optstr = mnt_fs_strdup_options(fs);
 
172
                if (!fs->optstr)
 
173
                        rc = -ENOMEM;
 
174
        } else {
 
175
                DBG(TAB, mnt_debug(
 
176
                        "mountinfo parse error [sscanf rc=%d]: '%s'", rc, s));
 
177
                rc = -EINVAL;
 
178
        }
 
179
        return rc;
 
180
}
 
181
 
 
182
/*
 
183
 * Parses one line from utab file
 
184
 */
 
185
static int mnt_parse_utab_line(struct libmnt_fs *fs, const char *s)
 
186
{
 
187
        const char *p = s;
 
188
 
 
189
        assert(fs);
 
190
        assert(s);
 
191
        assert(!fs->source);
 
192
        assert(!fs->target);
 
193
 
 
194
        while (p && *p) {
 
195
                char *end = NULL;
 
196
 
 
197
                while (*p == ' ') p++;
 
198
                if (!*p)
 
199
                        break;
 
200
 
 
201
                if (!fs->source && !strncmp(p, "SRC=", 4)) {
 
202
                        char *v = unmangle(p + 4, &end);
 
203
                        if (!v)
 
204
                                goto enomem;
 
205
                        __mnt_fs_set_source_ptr(fs, v);
 
206
 
 
207
                } else if (!fs->target && !strncmp(p, "TARGET=", 7)) {
 
208
                        fs->target = unmangle(p + 7, &end);
 
209
                        if (!fs->target)
 
210
                                goto enomem;
 
211
 
 
212
                } else if (!fs->root && !strncmp(p, "ROOT=", 5)) {
 
213
                        fs->root = unmangle(p + 5, &end);
 
214
                        if (!fs->root)
 
215
                                goto enomem;
 
216
 
 
217
                } else if (!fs->bindsrc && !strncmp(p, "BINDSRC=", 8)) {
 
218
                        fs->bindsrc = unmangle(p + 8, &end);
 
219
                        if (!fs->bindsrc)
 
220
                                goto enomem;
 
221
 
 
222
                } else if (!fs->user_optstr && !strncmp(p, "OPTS=", 5)) {
 
223
                        fs->user_optstr = unmangle(p + 5, &end);
 
224
                        if (!fs->user_optstr)
 
225
                                goto enomem;
 
226
 
 
227
                } else if (!fs->attrs && !strncmp(p, "ATTRS=", 6)) {
 
228
                        fs->attrs = unmangle(p + 6, &end);
 
229
                        if (!fs->attrs)
 
230
                                goto enomem;
 
231
 
 
232
                } else {
 
233
                        /* unknown variable */
 
234
                        while (*p && *p != ' ') p++;
 
235
                }
 
236
                if (end)
 
237
                        p = end;
 
238
        }
 
239
 
 
240
        return 0;
 
241
enomem:
 
242
        DBG(TAB, mnt_debug("utab parse error: ENOMEM"));
 
243
        return -ENOMEM;
 
244
}
 
245
 
 
246
/*
 
247
 * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*)
 
248
 *
 
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
 
251
 * format.
 
252
 *
 
253
 * mountinfo: "<number> <number> ... "
 
254
 */
 
255
static int guess_table_format(char *line)
 
256
{
 
257
        unsigned int a, b;
 
258
 
 
259
        if (sscanf(line, "%u %u", &a, &b) == 2)
 
260
                return MNT_FMT_MOUNTINFO;
 
261
 
 
262
        return MNT_FMT_FSTAB;           /* fstab, mtab or /proc/mounts */
 
263
}
 
264
 
 
265
/*
 
266
 * Read and parse the next line from {fs,m}tab or mountinfo
 
267
 */
 
268
static int mnt_table_parse_next(struct libmnt_table *tb, FILE *f, struct libmnt_fs *fs,
 
269
                                const char *filename, int *nlines)
 
270
{
 
271
        char buf[BUFSIZ];
 
272
        char *s;
 
273
 
 
274
        assert(tb);
 
275
        assert(f);
 
276
        assert(fs);
 
277
 
 
278
        /* read the next non-blank non-comment line */
 
279
        do {
 
280
                if (fgets(buf, sizeof(buf), f) == NULL)
 
281
                        return -EINVAL;
 
282
                ++*nlines;
 
283
                s = index (buf, '\n');
 
284
                if (!s) {
 
285
                        /* Missing final newline?  Otherwise extremely */
 
286
                        /* long line - assume file was corrupted */
 
287
                        if (feof(f)) {
 
288
                                DBG(TAB, mnt_debug_h(tb,
 
289
                                        "%s: no final newline", filename));
 
290
                                s = index (buf, '\0');
 
291
                        } else {
 
292
                                DBG(TAB, mnt_debug_h(tb,
 
293
                                        "%s:%d: missing newline at line",
 
294
                                        filename, *nlines));
 
295
                                goto err;
 
296
                        }
 
297
                }
 
298
                *s = '\0';
 
299
                if (--s >= buf && *s == '\r')
 
300
                        *s = '\0';
 
301
                s = skip_spaces(buf);
 
302
        } while (*s == '\0' || *s == '#');
 
303
 
 
304
        if (tb->fmt == MNT_FMT_GUESS)
 
305
                tb->fmt = guess_table_format(s);
 
306
 
 
307
        if (tb->fmt == MNT_FMT_FSTAB) {
 
308
                if (mnt_parse_table_line(fs, s) != 0)
 
309
                        goto err;
 
310
 
 
311
        } else if (tb->fmt == MNT_FMT_MOUNTINFO) {
 
312
                if (mnt_parse_mountinfo_line(fs, s) != 0)
 
313
                        goto err;
 
314
 
 
315
        } else if (tb->fmt == MNT_FMT_UTAB) {
 
316
                if (mnt_parse_utab_line(fs, s) != 0)
 
317
                        goto err;
 
318
        }
 
319
 
 
320
 
 
321
        /*DBG(TAB, mnt_fs_print_debug(fs, stderr));*/
 
322
 
 
323
        return 0;
 
324
err:
 
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"));
 
328
 
 
329
        /* by default all errors are recoverable, otherwise behavior depends on
 
330
         * errcb() function. See mnt_table_set_parser_errcb().
 
331
         */
 
332
        return tb->errcb ? tb->errcb(tb, filename, *nlines) : 1;
 
333
}
 
334
 
 
335
/**
 
336
 * mnt_table_parse_stream:
 
337
 * @tb: tab pointer
 
338
 * @f: file stream
 
339
 * @filename: filename used for debug and error messages
 
340
 *
 
341
 * Returns: 0 on success, negative number in case of error.
 
342
 */
 
343
int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
 
344
{
 
345
        int nlines = 0;
 
346
        int rc = -1;
 
347
        int flags = 0;
 
348
 
 
349
        assert(tb);
 
350
        assert(f);
 
351
        assert(filename);
 
352
 
 
353
        DBG(TAB, mnt_debug_h(tb, "%s: start parsing (%d entries)",
 
354
                                filename, mnt_table_get_nents(tb)));
 
355
 
 
356
        /* necessary for /proc/mounts only, the /proc/self/mountinfo
 
357
         * parser sets the flag properly
 
358
         */
 
359
        if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0)
 
360
                flags = MNT_FS_KERNEL;
 
361
 
 
362
        while (!feof(f)) {
 
363
                struct libmnt_fs *fs = mnt_new_fs();
 
364
 
 
365
                if (!fs)
 
366
                        goto err;
 
367
 
 
368
                rc = mnt_table_parse_next(tb, f, fs, filename, &nlines);
 
369
                if (!rc) {
 
370
                        rc = mnt_table_add_fs(tb, fs);
 
371
                        fs->flags |= flags;
 
372
                }
 
373
                if (rc) {
 
374
                        mnt_free_fs(fs);
 
375
                        if (rc == 1)
 
376
                                continue;       /* recoverable error */
 
377
                        if (feof(f))
 
378
                                break;
 
379
                        goto err;               /* fatal error */
 
380
                }
 
381
        }
 
382
 
 
383
        DBG(TAB, mnt_debug_h(tb, "%s: stop parsing (%d entries)",
 
384
                                filename, mnt_table_get_nents(tb)));
 
385
        return 0;
 
386
err:
 
387
        DBG(TAB, mnt_debug_h(tb, "%s: parse error (rc=%d)", filename, rc));
 
388
        return rc;
 
389
}
 
390
 
 
391
/**
 
392
 * mnt_table_parse_file:
 
393
 * @tb: tab pointer
 
394
 * @filename: file
 
395
 *
 
396
 * Parses whole table (e.g. /etc/mtab) and appends new records to the @tab.
 
397
 *
 
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()).
 
400
 *
 
401
 * Returns: 0 on success, negative number in case of error.
 
402
 */
 
403
int mnt_table_parse_file(struct libmnt_table *tb, const char *filename)
 
404
{
 
405
        FILE *f;
 
406
        int rc;
 
407
 
 
408
        assert(tb);
 
409
        assert(filename);
 
410
 
 
411
        if (!filename || !tb)
 
412
                return -EINVAL;
 
413
 
 
414
        f = fopen(filename, "r");
 
415
        if (f) {
 
416
                rc = mnt_table_parse_stream(tb, f, filename);
 
417
                fclose(f);
 
418
        } else
 
419
                return -errno;
 
420
 
 
421
        return rc;
 
422
}
 
423
 
 
424
static int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname)
 
425
{
 
426
        int n = 0, i;
 
427
        DIR *dir = NULL;
 
428
        struct dirent **namelist = NULL;
 
429
 
 
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);
 
433
        if (n <= 0)
 
434
                return 0;
 
435
 
 
436
        /* let use "at" functions rather than play crazy games with paths... */
 
437
        dir = opendir(dirname);
 
438
        if (!dir)
 
439
                return -errno;
 
440
 
 
441
        for (i = 0; i < n; i++) {
 
442
                struct dirent *d = namelist[i];
 
443
                struct stat st;
 
444
                size_t namesz;
 
445
                FILE *f;
 
446
 
 
447
#ifdef _DIRENT_HAVE_D_TYPE
 
448
                if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG &&
 
449
                    d->d_type != DT_LNK)
 
450
                        continue;
 
451
#endif
 
452
                if (*d->d_name == '.')
 
453
                        continue;
 
454
 
 
455
#define MNT_MNTTABDIR_EXTSIZ    (sizeof(MNT_MNTTABDIR_EXT) - 1)
 
456
 
 
457
                namesz = strlen(d->d_name);
 
458
                if (!namesz || namesz < MNT_MNTTABDIR_EXTSIZ + 1 ||
 
459
                    strcmp(d->d_name + (namesz - MNT_MNTTABDIR_EXTSIZ),
 
460
                            MNT_MNTTABDIR_EXT))
 
461
                                continue;
 
462
 
 
463
                if (fstat_at(dirfd(dir), _PATH_MNTTAB_DIR, d->d_name, &st, 0) ||
 
464
                    !S_ISREG(st.st_mode))
 
465
                        continue;
 
466
 
 
467
                f = fopen_at(dirfd(dir), _PATH_MNTTAB_DIR,
 
468
                                        d->d_name, O_RDONLY, "r");
 
469
                if (f) {
 
470
                        mnt_table_parse_stream(tb, f, d->d_name);
 
471
                        fclose(f);
 
472
                }
 
473
        }
 
474
 
 
475
        for (i = 0; i < n; i++)
 
476
                free(namelist[i]);
 
477
        free(namelist);
 
478
        if (dir)
 
479
                closedir(dir);
 
480
        return 0;
 
481
}
 
482
 
 
483
struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt)
 
484
{
 
485
        struct libmnt_table *tb;
 
486
        struct stat st;
 
487
 
 
488
        assert(filename);
 
489
 
 
490
        if (!filename)
 
491
                return NULL;
 
492
        if (stat(filename, &st))
 
493
                return NULL;
 
494
        tb = mnt_new_table();
 
495
        if (tb) {
 
496
                tb->fmt = fmt;
 
497
                if (mnt_table_parse_file(tb, filename) != 0) {
 
498
                        mnt_free_table(tb);
 
499
                        tb = NULL;
 
500
                }
 
501
        }
 
502
        return tb;
 
503
}
 
504
 
 
505
/**
 
506
 * mnt_new_table_from_file:
 
507
 * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path
 
508
 *
 
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.
 
512
 * fstab).
 
513
 *
 
514
 * Returns: newly allocated tab on success and NULL in case of error.
 
515
 */
 
516
struct libmnt_table *mnt_new_table_from_file(const char *filename)
 
517
{
 
518
        return __mnt_new_table_from_file(filename, MNT_FMT_GUESS);
 
519
}
 
520
 
 
521
/**
 
522
 * mnt_new_table_from_dir
 
523
 * @dirname: for example /etc/fstab.d
 
524
 *
 
525
 * Returns: newly allocated tab on success and NULL in case of error.
 
526
 */
 
527
struct libmnt_table *mnt_new_table_from_dir(const char *dirname)
 
528
{
 
529
        struct libmnt_table *tb;
 
530
 
 
531
        assert(dirname);
 
532
 
 
533
        if (!dirname)
 
534
                return NULL;
 
535
        tb = mnt_new_table();
 
536
        if (tb && mnt_table_parse_dir(tb, dirname) != 0) {
 
537
                mnt_free_table(tb);
 
538
                tb = NULL;
 
539
        }
 
540
        return tb;
 
541
}
 
542
 
 
543
/**
 
544
 * mnt_table_set_parser_errcb:
 
545
 * @tb: pointer to table
 
546
 * @cb: pointer to callback function
 
547
 *
 
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
 
551
 * codes:
 
552
 *
 
553
 *   <0  : fatal error (abort parsing)
 
554
 *    0  : success (parsing continue)
 
555
 *   >0  : recoverable error (the line is ignored, parsing continue).
 
556
 *
 
557
 * Returns: 0 on success or negative number in case of error.
 
558
 */
 
559
int mnt_table_set_parser_errcb(struct libmnt_table *tb,
 
560
                int (*cb)(struct libmnt_table *tb, const char *filename, int line))
 
561
{
 
562
        assert(tb);
 
563
        tb->errcb = cb;
 
564
        return 0;
 
565
}
 
566
 
 
567
/**
 
568
 * mnt_table_parse_fstab:
 
569
 * @tb: table
 
570
 * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL
 
571
 *
 
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.
 
575
 *
 
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
 
580
 *
 
581
 * See also mnt_table_set_parser_errcb().
 
582
 *
 
583
 * Returns: 0 on success or negative number in case of error.
 
584
 */
 
585
int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename)
 
586
{
 
587
        FILE *f;
 
588
 
 
589
        assert(tb);
 
590
 
 
591
        if (!tb)
 
592
                return -EINVAL;
 
593
        if (!filename)
 
594
                filename = mnt_get_fstab_path();
 
595
 
 
596
        tb->fmt = MNT_FMT_FSTAB;
 
597
 
 
598
        f = fopen(filename, "r");
 
599
        if (f) {
 
600
                int rc = mnt_table_parse_stream(tb, f, filename);
 
601
                fclose(f);
 
602
 
 
603
                if (rc)
 
604
                        return rc;
 
605
 
 
606
                if (strcmp(filename, _PATH_MNTTAB))
 
607
                        /* /etc/fstab.d sould be used together with /etc/fstab only */
 
608
                        return 0;
 
609
        }
 
610
 
 
611
        if (!access(_PATH_MNTTAB_DIR, R_OK))
 
612
                return mnt_table_parse_dir(tb, _PATH_MNTTAB_DIR);
 
613
        return 0;
 
614
}
 
615
 
 
616
/*
 
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).
 
619
 *
 
620
 * Note that @uf must contain only user specific mount options instead of
 
621
 * VFS options (note that FS options are ignored).
 
622
 *
 
623
 * Returns modified filesystem (from @tb) or NULL.
 
624
 */
 
625
static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf)
 
626
{
 
627
        struct libmnt_fs *fs;
 
628
        struct libmnt_iter itr;
 
629
        const char *optstr, *src, *target, *root, *attrs;
 
630
 
 
631
        assert(tb);
 
632
        assert(uf);
 
633
        if (!tb || !uf)
 
634
                return NULL;
 
635
 
 
636
        DBG(TAB, mnt_debug_h(tb, "merging user fs"));
 
637
 
 
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);
 
643
 
 
644
        if (!src || !target || !root || (!attrs && !optstr))
 
645
                return NULL;
 
646
 
 
647
        mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
 
648
 
 
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);
 
653
 
 
654
                if (fs->flags & MNT_FS_MERGED)
 
655
                        continue;
 
656
 
 
657
                if (s && t && r && !strcmp(t, target) &&
 
658
                    !strcmp(s, src) && !strcmp(r, root))
 
659
                        break;
 
660
        }
 
661
 
 
662
        if (fs) {
 
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;
 
668
 
 
669
                DBG(TAB, mnt_debug_h(tb, "found fs:"));
 
670
                DBG(TAB, mnt_fs_print_debug(fs, stderr));
 
671
        }
 
672
        return fs;
 
673
}
 
674
 
 
675
/**
 
676
 * mnt_table_parse_mtab:
 
677
 * @tb: table
 
678
 * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL
 
679
 *
 
680
 * This function parses /etc/mtab or /proc/self/mountinfo +
 
681
 * /run/mount/utabs or /proc/mounts.
 
682
 *
 
683
 * See also mnt_table_set_parser_errcb().
 
684
 *
 
685
 * Returns: 0 on success or negative number in case of error.
 
686
 */
 
687
int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename)
 
688
{
 
689
        int rc;
 
690
        const char *utab = NULL;
 
691
 
 
692
        if (mnt_has_regular_mtab(&filename, NULL)) {
 
693
 
 
694
                DBG(TAB, mnt_debug_h(tb, "force %s usage", filename));
 
695
 
 
696
                rc = mnt_table_parse_file(tb, filename);
 
697
                if (!rc)
 
698
                        return 0;
 
699
                filename = NULL;        /* failed */
 
700
        }
 
701
 
 
702
        /*
 
703
         * useless /etc/mtab
 
704
         * -- read kernel information from /proc/self/mountinfo
 
705
         */
 
706
        tb->fmt = MNT_FMT_MOUNTINFO;
 
707
        rc = mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO);
 
708
        if (rc) {
 
709
                /* hmm, old kernel? ...try /proc/mounts */
 
710
                tb->fmt = MNT_FMT_MTAB;
 
711
                return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS);
 
712
        }
 
713
 
 
714
        /*
 
715
         * try to read user specific information from /run/mount/utabs
 
716
         */
 
717
        utab = mnt_get_utab_path();
 
718
        if (utab) {
 
719
                struct libmnt_table *u_tb = __mnt_new_table_from_file(utab, MNT_FMT_UTAB);
 
720
 
 
721
                if (u_tb) {
 
722
                        struct libmnt_fs *u_fs;
 
723
                        struct libmnt_iter itr;
 
724
 
 
725
                        mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
 
726
 
 
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);
 
730
 
 
731
                        mnt_free_table(u_tb);
 
732
                }
 
733
        }
 
734
        return 0;
 
735
}