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