(root)/
util-linux-2.39/
misc-utils/
findmnt-verify.c
       1  #include <stdio.h>
       2  #include <stdlib.h>
       3  #include <errno.h>
       4  #include <unistd.h>
       5  #include <stdarg.h>
       6  #include <string.h>
       7  #include <sys/types.h>
       8  #include <sys/stat.h>
       9  #include <libmount.h>
      10  #include <blkid.h>
      11  #include <sys/utsname.h>
      12  
      13  #include "nls.h"
      14  #include "c.h"
      15  #include "strutils.h"
      16  #include "xalloc.h"
      17  #include "pathnames.h"
      18  #include "match.h"
      19  
      20  #include "findmnt.h"
      21  
      22  struct verify_context {
      23  	struct libmnt_fs	*fs;
      24  	struct libmnt_table	*tb;
      25  
      26  	char	**fs_ary;
      27  	size_t	fs_num;
      28  	size_t  fs_alloc;
      29  
      30  	int	nwarnings;
      31  	int	nerrors;
      32  
      33  	unsigned int	target_printed : 1,
      34  			no_fsck : 1;
      35  };
      36  
      37  static void __attribute__ ((__format__ (__printf__, 3, 0)))
      38  	verify_mesg(struct verify_context *vfy, char type, const char *fmt, va_list ap)
      39  {
      40  	if (!vfy->target_printed && vfy->fs) {
      41  		fprintf(stdout, "%s\n", mnt_fs_get_target(vfy->fs));
      42  		vfy->target_printed = 1;
      43  	}
      44  
      45  	fprintf(stdout, "   [%c] ", type);
      46  	vfprintf(stdout, fmt, ap);
      47  	fputc('\n', stdout);
      48  }
      49  
      50  static int __attribute__ ((__format__ (__printf__, 2, 3)))
      51  	verify_warn(struct verify_context *vfy, const char *fmt, ...)
      52  {
      53  	va_list ap;
      54  	vfy->nwarnings++;
      55  	va_start(ap, fmt);
      56  	verify_mesg(vfy, 'W', fmt, ap);
      57  	va_end(ap);
      58  	return 0;
      59  }
      60  
      61  static int __attribute__ ((__format__ (__printf__, 2, 3)))
      62  	verify_err(struct verify_context *vfy, const char *fmt, ...)
      63  {
      64  	va_list ap;
      65  	vfy->nerrors++;
      66  	va_start(ap, fmt);
      67  	verify_mesg(vfy, 'E', fmt, ap);
      68  	va_end(ap);
      69  	return 0;
      70  }
      71  
      72  static int __attribute__ ((__format__ (__printf__, 2, 3)))
      73  	verify_ok(struct verify_context *vfy __attribute__((unused)),
      74  		      const char *fmt, ...)
      75  {
      76  	va_list ap;
      77  
      78  	if (!(flags & FL_VERBOSE))
      79  		return 0;
      80  
      81  	va_start(ap, fmt);
      82  	verify_mesg(vfy, ' ', fmt, ap);
      83  	va_end(ap);
      84  	return 0;
      85  }
      86  
      87  static int verify_order(struct verify_context *vfy)
      88  {
      89  	struct libmnt_iter *itr = NULL;
      90  	struct libmnt_fs *next;
      91  	const char *tgt;
      92  
      93  	tgt = mnt_fs_get_target(vfy->fs);
      94  	if (tgt && !(flags & FL_NOCACHE))
      95  		tgt  = mnt_resolve_target(tgt, cache);
      96  	else if (!tgt)
      97  		return 0;
      98  
      99  	itr = mnt_new_iter(MNT_ITER_FORWARD);
     100  	if (!itr) {
     101  		warn(_("failed to initialize libmount iterator"));
     102  		goto done;
     103  	}
     104  
     105  	/* set iterator position to 'fs' */
     106  	mnt_table_set_iter(vfy->tb, itr, vfy->fs);
     107  
     108  	if (mnt_table_next_fs(vfy->tb, itr, &next) != 0)
     109  		goto done;
     110  
     111  	/* scan all next filesystems */
     112  	while (mnt_table_next_fs(vfy->tb, itr, &next) == 0) {
     113  		const char *n_tgt;
     114  		size_t len;
     115  
     116  		n_tgt = mnt_fs_get_target(next);
     117  		if (n_tgt && !(flags & FL_NOCACHE))
     118  			n_tgt  = mnt_resolve_target(n_tgt, cache);
     119  		else if (!n_tgt)
     120  			continue;
     121  		len = strlen(n_tgt);
     122  
     123  		if (strncmp(n_tgt, tgt, len) == 0) {
     124  			if (*(tgt + len) == '\0')
     125  				verify_warn(vfy, _("target specified more than once"));
     126  			else if (*(tgt + len) == '/')
     127  				verify_err(vfy, _("wrong order: %s specified before %s"), tgt, n_tgt);
     128  		}
     129  	}
     130  done:
     131  	mnt_free_iter(itr);
     132  	return 0;
     133  }
     134  
     135  static int verify_target(struct verify_context *vfy)
     136  {
     137  	const char *tgt = mnt_fs_get_target(vfy->fs);
     138  	struct stat sb;
     139  
     140  	if (!tgt)
     141  		return verify_err(vfy, _("undefined target (fs_file)"));
     142  
     143  	if (!(flags & FL_NOCACHE)) {
     144  		const char *cn = mnt_resolve_target(tgt, cache);
     145  		if (!cn)
     146  			return -ENOMEM;
     147  		if (strcmp(cn, tgt) != 0)
     148  			verify_warn(vfy, _("non-canonical target path (real: %s)"), cn);
     149  		tgt = cn;
     150  	}
     151  	if (stat(tgt, &sb) != 0) {
     152  		if (mnt_fs_get_option(vfy->fs, "noauto", NULL, NULL) == 1)
     153  			verify_err(vfy, _("unreachable on boot required target: %m"));
     154  		else
     155  			verify_warn(vfy, _("unreachable target: %m"));
     156  
     157  	} else if (!S_ISDIR(sb.st_mode)
     158  		 && mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 1) {
     159  		verify_err(vfy, _("target is not a directory"));
     160  	} else
     161  		verify_ok(vfy, _("target exists"));
     162  
     163  	return 0;
     164  }
     165  
     166  static char *verify_tag(struct verify_context *vfy, const char *name,
     167  		      const char *value)
     168  {
     169  	char *src = NULL;
     170  
     171  	if (!(flags & FL_NOCACHE))
     172  		src = mnt_resolve_tag(name, value, cache);
     173  
     174  	if (!src) {
     175  		if (mnt_fs_get_option(vfy->fs, "noauto", NULL, NULL) == 1)
     176  			verify_err(vfy, _("unreachable on boot required source: %s=%s"), name, value);
     177  		else
     178  			verify_warn(vfy, _("unreachable: %s=%s"), name, value);
     179  	} else
     180  		verify_ok(vfy, _("%s=%s translated to %s"), name, value, src);
     181  
     182  	return src;
     183  }
     184  
     185  /* Note that mount source is very FS specific and we should not
     186   * interpret unreachable source as error. The exception is only
     187   * NAME=value, this has to be convertible to device name.
     188   */
     189  static int verify_source(struct verify_context *vfy)
     190  {
     191  	const char *src = mnt_fs_get_srcpath(vfy->fs);
     192  	char *t = NULL, *v = NULL;
     193  	struct stat sb;
     194  	int isbind, rc = 0;
     195  
     196  	/* source is NAME=value tag */
     197  	if (!src) {
     198  		const char *tag = NULL, *val = NULL;
     199  
     200  		if (mnt_fs_get_tag(vfy->fs, &tag, &val) != 0)
     201  			return verify_err(vfy, _("undefined source (fs_spec)"));
     202  
     203  		src = verify_tag(vfy, tag, val);
     204  		if (!src)
     205  			goto done;
     206  
     207  	/* blkid is able to parse it, but libmount does not see it as a tag --
     208  	 * it means unsupported tag */
     209  	} else if (blkid_parse_tag_string(src, &t, &v) == 0 && stat(src, &sb) != 0) {
     210  		rc = verify_err(vfy, _("unsupported source tag: %s"), src);
     211  		goto done;
     212  	}
     213  	isbind = mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 0;
     214  
     215  	/* source is path */
     216  	if (mnt_fs_is_pseudofs(vfy->fs) || mnt_fs_is_netfs(vfy->fs))
     217  		verify_ok(vfy, _("do not check %s source (pseudo/net)"), src);
     218  
     219  	else if (stat(src, &sb) != 0)
     220  		verify_warn(vfy, _("unreachable source: %s: %m"), src);
     221  
     222  	else if ((S_ISDIR(sb.st_mode) || S_ISREG(sb.st_mode)) && !isbind)
     223  		verify_warn(vfy, _("non-bind mount source %s is a directory or regular file"), src);
     224  
     225  	else if (!S_ISBLK(sb.st_mode) && !isbind)
     226  		verify_warn(vfy, _("source %s is not a block device"), src);
     227  	else
     228  		verify_ok(vfy, _("source %s exists"), src);
     229  done:
     230  	free(t);
     231  	free(v);
     232  	return rc;
     233  }
     234  
     235  static int verify_options(struct verify_context *vfy)
     236  {
     237  	const char *opts;
     238  
     239  	opts = mnt_fs_get_vfs_options(vfy->fs);
     240  	if (opts)
     241  		verify_ok(vfy, _("VFS options: %s"), opts);
     242  
     243  	opts = mnt_fs_get_fs_options(vfy->fs);
     244  	if (opts)
     245  		verify_ok(vfy, _("FS options: %s"), opts);
     246  
     247  	opts = mnt_fs_get_user_options(vfy->fs);
     248  	if (opts)
     249  		verify_ok(vfy, _("userspace options: %s"), opts);
     250  
     251  	return 0;
     252  }
     253  
     254  static int verify_swaparea(struct verify_context *vfy)
     255  {
     256  	char *arg;
     257  	size_t argsz = 0;
     258  
     259  	if (mnt_fs_get_option(vfy->fs, "discard", &arg, &argsz) == 0
     260  	    && arg
     261  	    && strncmp(arg, "once", argsz) != 0
     262  	    && strncmp(arg, "pages", argsz) != 0)
     263  		verify_err(vfy, _("unsupported swaparea discard policy: %s"), arg);
     264  
     265  	if (mnt_fs_get_option(vfy->fs, "pri", &arg, &argsz) == 0 && arg) {
     266  		char *p = arg;
     267  		if (*p == '-')
     268  			p++;
     269  		for (; p < arg + argsz; p++) {
     270  			if (!isdigit((unsigned char) *p)) {
     271  				verify_err(vfy, _("failed to parse swaparea priority option"));
     272  				break;
     273  			}
     274  		}
     275  	}
     276  
     277  	return 0;
     278  }
     279  
     280  static int is_supported_filesystem(struct verify_context *vfy, const char *name)
     281  {
     282  	size_t n;
     283  
     284  	if (!vfy->fs_num)
     285  		return 0;
     286  
     287  	for (n = 0; n < vfy->fs_num; n++ ) {
     288  		if (match_fstype(vfy->fs_ary[n], name))
     289  			return 1;
     290  	}
     291  
     292  	return 0;
     293  }
     294  
     295  static int add_filesystem(struct verify_context *vfy, const char *name)
     296  {
     297  	#define MYCHUNK	16
     298  
     299  	if (is_supported_filesystem(vfy, name))
     300  		return 0;
     301  
     302  	if (vfy->fs_alloc == 0 || vfy->fs_num + 1 <= vfy->fs_alloc) {
     303  		vfy->fs_alloc = ((vfy->fs_alloc + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
     304  		vfy->fs_ary = xrealloc(vfy->fs_ary, vfy->fs_alloc * sizeof(char *));
     305  	}
     306  
     307  	vfy->fs_ary[vfy->fs_num] = xstrdup(name);
     308  	vfy->fs_num++;
     309  
     310  	return 0;
     311  }
     312  
     313  static int read_proc_filesystems(struct verify_context *vfy)
     314  {
     315  	int rc = 0;
     316  	FILE *f;
     317  	char buf[80], *cp, *t;
     318  
     319  	f = fopen("/proc/filesystems", "r");
     320  	if (!f)
     321  		return -errno;
     322  
     323  	while (!feof(f)) {
     324  		if (!fgets(buf, sizeof(buf), f))
     325  			break;
     326  		cp = buf;
     327  		if (!isspace(*cp)) {
     328  			while (*cp && !isspace(*cp))
     329  				cp++;
     330  		}
     331  		while (*cp && isspace(*cp))
     332  			cp++;
     333  		if ((t = strchr(cp, '\n')) != NULL)
     334  			*t = 0;
     335  		if ((t = strchr(cp, '\t')) != NULL)
     336  			*t = 0;
     337  		if ((t = strchr(cp, ' ')) != NULL)
     338  			*t = 0;
     339  
     340  		rc = add_filesystem(vfy, cp);
     341  		if (rc)
     342  			break;
     343  	}
     344  	fclose(f);
     345  	return rc;
     346  }
     347  
     348  static void free_proc_filesystems(struct verify_context *vfy)
     349  {
     350  	size_t n;
     351  
     352  	if (!vfy->fs_ary)
     353  		return;
     354  
     355  	for (n = 0; n < vfy->fs_num; n++ )
     356  		free(vfy->fs_ary[n]);
     357  	free(vfy->fs_ary);
     358  }
     359  
     360  static int read_kernel_filesystems(struct verify_context *vfy)
     361  {
     362  	int rc = 0;
     363  #ifdef __linux__
     364  	struct utsname uts;
     365  	FILE *f;
     366  	char buf[1024];
     367  
     368  	if (uname(&uts))
     369  		return 0;
     370  	snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
     371  
     372  	f = fopen(buf, "r");
     373  	if (!f)
     374  		return 0;
     375  
     376  	while (!feof(f)) {
     377  		char *p, *name;
     378  
     379  		if (!fgets(buf, sizeof(buf), f))
     380  			break;
     381  
     382  		if (strncmp("kernel/fs/", buf, 10) != 0 ||
     383  		    strncmp("kernel/fs/nls/", buf, 14) == 0)
     384  			continue;
     385  
     386  		p = strchr(buf, ':');
     387  		if (!p)
     388  			continue;
     389  		*p = '\0';
     390  
     391  		name = strrchr(buf, '/');
     392  		if (!name)
     393  			continue;
     394  		name++;
     395  
     396  		p = strstr(name, ".ko");
     397  		if (!p)
     398  			continue;
     399  		*p = '\0';
     400  
     401  		rc = add_filesystem(vfy, name);
     402  		if (rc)
     403  			break;
     404  	}
     405  	fclose(f);
     406  #endif /* __linux__ */
     407  	return rc;
     408  }
     409  
     410  static int verify_fstype(struct verify_context *vfy)
     411  {
     412  	char *src = mnt_resolve_spec(mnt_fs_get_source(vfy->fs), cache);
     413  	char *realtype = NULL;
     414  	const char *type;
     415  	int ambi = 0, isauto = 0, isswap = 0;
     416  
     417  	if (!src)
     418  		return 0;
     419  
     420  	if (mnt_fs_is_pseudofs(vfy->fs) || mnt_fs_is_netfs(vfy->fs)) {
     421  		verify_ok(vfy, _("do not check %s FS type (pseudo/net)"), src);
     422  		goto done;
     423  	}
     424  
     425  	type = mnt_fs_get_fstype(vfy->fs);
     426  
     427  	if (type) {
     428  		int none = strcmp(type, "none") == 0;
     429  
     430  		if (none
     431  		    && mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 1
     432  		    && mnt_fs_get_option(vfy->fs, "move", NULL, NULL) == 1) {
     433  			verify_warn(vfy, _("\"none\" FS type is recommended for bind or move oprations only"));
     434  			goto done;
     435  		}
     436  
     437  		if (strcmp(type, "auto") == 0)
     438  			isauto = 1;
     439  		else if (strcmp(type, "swap") == 0)
     440  			isswap = 1;
     441  		else if (strcmp(type, "xfs") == 0 || strcmp(type, "btrfs") == 0)
     442  			vfy->no_fsck = 1;
     443  
     444  		if (!isswap && !isauto && !none && !is_supported_filesystem(vfy, type))
     445  			verify_warn(vfy, _("%s seems unsupported by the current kernel"), type);
     446  	}
     447  
     448  	errno = 0;
     449  	realtype = mnt_get_fstype(src, &ambi, cache);
     450  
     451  	if (!realtype) {
     452  		const char *reson = errno ? strerror(errno) : _("reason unknown");
     453  
     454  		if (isauto)
     455  			verify_err(vfy, _("cannot detect on-disk filesystem type (%s)"), reson);
     456  		else
     457  			verify_warn(vfy, _("cannot detect on-disk filesystem type (%s)"), reson);
     458  		goto done;
     459  	}
     460  
     461  	if (realtype) {
     462  		isswap = strcmp(realtype, "swap") == 0;
     463  		vfy->no_fsck = strcmp(realtype, "xfs") == 0
     464  				|| strcmp(realtype, "btrfs") == 0;
     465  
     466  		if (type && !isauto && strcmp(type, realtype) != 0) {
     467  			verify_warn(vfy, _("%s does not match with on-disk %s"), type, realtype);
     468  			goto done;
     469  		}
     470  		if (!isswap && !is_supported_filesystem(vfy, realtype)) {
     471  			verify_warn(vfy, _("on-disk %s seems unsupported by the current kernel"), realtype);
     472  			goto done;
     473  		}
     474  
     475  		verify_ok(vfy, _("FS type is %s"), realtype);
     476  	}
     477  
     478  done:
     479  	if (!cache) {
     480  		free(src);
     481  		free(realtype);
     482  	}
     483  	return 0;
     484  }
     485  
     486  static int verify_passno(struct verify_context *vfy)
     487  {
     488  	int passno = mnt_fs_get_passno(vfy->fs);
     489  	const char *tgt = mnt_fs_get_target(vfy->fs);
     490  
     491  	if (tgt && strcmp("/", tgt) == 0 && passno != 1 && !vfy->no_fsck)
     492  		return verify_warn(vfy, _("recommended root FS passno is 1 (current is %d)"), passno);
     493  
     494  	return 0;
     495  }
     496  
     497  static int verify_filesystem(struct verify_context *vfy)
     498  {
     499  	int rc = 0;
     500  
     501  	if (mnt_fs_is_swaparea(vfy->fs))
     502  		rc = verify_swaparea(vfy);
     503  	else {
     504  		rc = verify_target(vfy);
     505  		if (!rc)
     506  			rc = verify_options(vfy);
     507  	}
     508  
     509  	if (!rc)
     510  		rc = verify_source(vfy);
     511  	if (!rc)
     512  		rc = verify_fstype(vfy);
     513  	if (!rc)
     514  		rc = verify_passno(vfy);	/* depends on verify_fstype() */
     515  
     516  	return rc;
     517  }
     518  
     519  int verify_table(struct libmnt_table *tb)
     520  {
     521  	struct verify_context vfy = { .nerrors = 0 };
     522  	struct libmnt_iter *itr;
     523  	int rc = 0;		/* overall return code (alloc errors, etc.) */
     524  	int check_order = is_listall_mode();
     525  	static int has_read_fs = 0;
     526  
     527  	itr = mnt_new_iter(MNT_ITER_FORWARD);
     528  	if (!itr) {
     529  		warn(_("failed to initialize libmount iterator"));
     530  		goto done;
     531  	}
     532  
     533  	vfy.tb = tb;
     534  
     535  	if (has_read_fs == 0) {
     536  		read_proc_filesystems(&vfy);
     537  		read_kernel_filesystems(&vfy);
     538  		has_read_fs = 1;
     539  	}
     540  
     541  	while (rc == 0 && (vfy.fs = get_next_fs(tb, itr))) {
     542  		vfy.target_printed = 0;
     543  		vfy.no_fsck = 0;
     544  
     545  		if (check_order)
     546  			rc = verify_order(&vfy);
     547  		if (!rc)
     548  			rc = verify_filesystem(&vfy);
     549  
     550  		if (flags & FL_FIRSTONLY)
     551  			break;
     552  		flags |= FL_NOSWAPMATCH;
     553  	}
     554  
     555  #ifdef USE_SYSTEMD
     556  	{
     557  		struct stat a, b;
     558  
     559  		if (stat(_PATH_SD_UNITSLOAD, &a) == 0 &&
     560  		    stat(_PATH_MNTTAB, &b) == 0 &&
     561  		    cmp_stat_mtime(&a, &b, <))
     562  			verify_warn(&vfy, _(
     563  	"your fstab has been modified, but systemd still uses the old version;\n"
     564  	"       use 'systemctl daemon-reload' to reload"));
     565  	}
     566  #endif
     567  
     568  done:
     569  	mnt_free_iter(itr);
     570  
     571  	/* summary */
     572  	if (vfy.nerrors || parse_nerrors || vfy.nwarnings) {
     573  		fputc('\n', stderr);
     574  		fprintf(stderr, P_("%d parse error", "%d parse errors", parse_nerrors), parse_nerrors);
     575  		fprintf(stderr, P_(", %d error",     ", %d errors", vfy.nerrors), vfy.nerrors);
     576  		fprintf(stderr, P_(", %d warning",   ", %d warnings", vfy.nwarnings), vfy.nwarnings);
     577  		fputc('\n', stderr);
     578  	} else
     579  		fprintf(stdout, _("Success, no errors or warnings detected\n"));
     580  
     581  
     582  	free_proc_filesystems(&vfy);
     583  
     584  	return rc != 0 ? rc : vfy.nerrors + parse_nerrors;
     585  }