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