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