(root)/
util-linux-2.39/
libmount/
src/
context_mount.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) 2010-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  
      13  /**
      14   * SECTION: context-mount
      15   * @title: Mount context
      16   * @short_description: high-level API to mount operation.
      17   */
      18  #include <sys/wait.h>
      19  #include <sys/mount.h>
      20  
      21  #include "mountP.h"
      22  #include "strutils.h"
      23  
      24  #if defined(HAVE_SMACK)
      25  static int is_option(const char *name, const char *const *names)
      26  {
      27  	const char *const *p;
      28  
      29  	for (p = names; p && *p; p++) {
      30  		if (strcmp(name, *p) == 0)
      31  			return 1;
      32  	}
      33  	return 0;
      34  }
      35  #endif /* HAVE_SMACK */
      36  
      37  /*
      38   * this has to be called after mnt_context_evaluate_permissions()
      39   */
      40  static int fix_optstr(struct libmnt_context *cxt)
      41  {
      42  	struct libmnt_optlist *ol;
      43  	struct libmnt_opt *opt;
      44  	struct libmnt_ns *ns_old;
      45  	const char *val;
      46  	int rc = 0;
      47  
      48  	assert(cxt);
      49  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
      50  
      51  	if (cxt->flags & MNT_FL_MOUNTOPTS_FIXED)
      52  		return 0;
      53  
      54  	DBG(CXT, ul_debugobj(cxt, "--> preparing options"));
      55  
      56  	ol = mnt_context_get_optlist(cxt);
      57  	if (!ol)
      58  		return -EINVAL;
      59  
      60  	ns_old = mnt_context_switch_origin_ns(cxt);
      61  	if (!ns_old)
      62  		return -MNT_ERR_NAMESPACE;
      63  
      64  	/* Fix user (convert "user" to "user=username") */
      65  	if (mnt_context_is_restricted(cxt)) {
      66  		opt = mnt_optlist_get_opt(ol, MNT_MS_USER, cxt->map_userspace);
      67  		if (opt) {
      68  			char *name = mnt_get_username(getuid());
      69  
      70  			if (!name)
      71  				rc = -ENOMEM;
      72  			else {
      73  				rc = mnt_opt_set_value(opt, name);
      74  				free(name);
      75  			}
      76  			if (rc)
      77  				goto done;
      78  		}
      79  	}
      80  
      81  	/* Fix UID */
      82  	opt = mnt_optlist_get_named(ol, "uid", NULL);
      83  	if (opt && (val = mnt_opt_get_value(opt)) && !isdigit_string(val)) {
      84  		uid_t id;
      85  
      86  		if (strcmp(val, "useruid") == 0)	/* UID of the current user */
      87  			id = getuid();
      88  		else
      89  			rc = mnt_get_uid(val, &id);	/* UID for the username */
      90  		if (!rc)
      91  			rc = mnt_opt_set_u64value(opt, id);
      92  		if (rc)
      93  			goto done;
      94  	}
      95  
      96  	/* Fix GID */
      97  	opt = mnt_optlist_get_named(ol, "gid", NULL);
      98  	if (opt && (val = mnt_opt_get_value(opt)) && !isdigit_string(val)) {
      99  		gid_t id;
     100  
     101  		if (strcmp(val, "usergid") == 0)	/* UID of the current user */
     102  			id = getgid();
     103  		else
     104  			rc = mnt_get_gid(val, &id);	/* UID for the groupname */
     105  		if (!rc)
     106  			rc = mnt_opt_set_u64value(opt, id);
     107  		if (rc)
     108  			goto done;
     109  	}
     110  
     111  	if (!mnt_context_switch_ns(cxt, ns_old))
     112  		return -MNT_ERR_NAMESPACE;
     113  
     114  #ifdef HAVE_SMACK
     115  	/* Fix Smack */
     116  	if (access("/sys/fs/smackfs", F_OK) != 0) {
     117  		struct libmnt_iter itr;
     118  
     119  		static const char *const smack_options[] = {
     120  			"smackfsdef",
     121  			"smackfsfloor",
     122  			"smackfshat",
     123  			"smackfsroot",
     124  			"smackfstransmute",
     125  			NULL,
     126  		};
     127  		mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     128  
     129  		while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
     130  			if (!is_option(mnt_opt_get_name(opt), smack_options))
     131  				continue;
     132  			rc = mnt_optlist_remove_opt(ol, opt);
     133  			if (rc)
     134  				goto done;
     135  		}
     136  	}
     137  #endif
     138  	rc = mnt_context_call_hooks(cxt, MNT_STAGE_PREP_OPTIONS);
     139  done:
     140  	DBG(CXT, ul_debugobj(cxt, "<-- preparing options done [rc=%d]", rc));
     141  	cxt->flags |= MNT_FL_MOUNTOPTS_FIXED;
     142  
     143  	if (rc)
     144  		rc = -MNT_ERR_MOUNTOPT;
     145  	return rc;
     146  }
     147  
     148  /*
     149   * this has to be called before fix_optstr()
     150   *
     151   * Note that user=<name> may be used by some filesystems as a filesystem
     152   * specific option (e.g. cifs). Yes, developers of such filesystems have
     153   * allocated pretty hot place in hell...
     154   */
     155  static int evaluate_permissions(struct libmnt_context *cxt)
     156  {
     157  	struct libmnt_optlist *ol;
     158  	struct libmnt_opt *opt = NULL;
     159  
     160  	unsigned long user_flags = 0;	/* userspace mount flags */
     161  	int rc;
     162  
     163  	assert(cxt);
     164  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     165  
     166  	if (!cxt->fs)
     167  		return 0;
     168  
     169  	DBG(CXT, ul_debugobj(cxt, "mount: evaluating permissions"));
     170  
     171  	ol = mnt_context_get_optlist(cxt);
     172  	if (!ol)
     173  		return -EINVAL;
     174  
     175  	/* get userspace mount flags (user[=<name>] etc.*/
     176  	rc = mnt_optlist_get_flags(ol, &user_flags, cxt->map_userspace, 0);
     177  	if (rc)
     178  		return rc;
     179  
     180  	if (!mnt_context_is_restricted(cxt)) {
     181  		/*
     182  		 * superuser mount
     183  		 *
     184  		 * Let's convert user, users, owenr and groups to MS_* flags
     185  		 * to be compatible with non-root execution.
     186  		 *
     187  		 * The old deprecated way is to use mnt_optstr_get_flags().
     188  		 */
     189  		if (user_flags & (MNT_MS_OWNER | MNT_MS_GROUP))
     190  			rc = mnt_optlist_remove_flags(ol,
     191  					MNT_MS_OWNER | MNT_MS_GROUP, cxt->map_userspace);
     192  
     193  		if (!rc && (user_flags & MNT_MS_OWNER))
     194  			rc = mnt_optlist_insert_flags(ol,
     195  					MS_OWNERSECURE, cxt->map_linux,
     196  					MNT_MS_OWNER, cxt->map_userspace);
     197  
     198  		if (!rc && (user_flags & MNT_MS_GROUP))
     199  			rc = mnt_optlist_insert_flags(ol,
     200  					MS_OWNERSECURE, cxt->map_linux,
     201  					MNT_MS_GROUP, cxt->map_userspace);
     202  
     203  		if (!rc && (user_flags & MNT_MS_USER)
     204  		    && (opt = mnt_optlist_get_opt(ol, MNT_MS_USER, cxt->map_userspace))
     205  		    && !mnt_opt_has_value(opt))
     206  			rc = mnt_optlist_insert_flags(ol, MS_SECURE, cxt->map_linux,
     207  					MNT_MS_USER, cxt->map_userspace);
     208  
     209  		if (!rc && (user_flags & MNT_MS_USERS))
     210  			rc = mnt_optlist_insert_flags(ol, MS_SECURE, cxt->map_linux,
     211  					MNT_MS_USERS, cxt->map_userspace);
     212  
     213  		DBG(CXT, ul_debugobj(cxt, "perms: superuser [rc=%d]", rc));
     214  		if (rc)
     215  			return rc;
     216  
     217  		if (user_flags & (MNT_MS_OWNER | MNT_MS_GROUP |
     218  				  MNT_MS_USER | MNT_MS_USERS))
     219  			mnt_optlist_merge_opts(ol);
     220  	} else {
     221  
     222  		/*
     223  		 * user mount
     224  		 */
     225  		if (!mnt_context_tab_applied(cxt))
     226  		{
     227  			DBG(CXT, ul_debugobj(cxt, "perms: fstab not applied, ignore user mount"));
     228  			return -EPERM;
     229  		}
     230  
     231  		/*
     232  		* Ignore user=<name> (if <name> is set). Let's keep it hidden
     233  		* for normal library operations, but visible for /sbin/mount.<type>
     234  		* helpers.
     235  		*/
     236  		if (user_flags & MNT_MS_USER
     237  		    && (opt = mnt_optlist_get_opt(ol, MNT_MS_USER, cxt->map_userspace))
     238  		    && mnt_opt_has_value(opt)) {
     239  			DBG(CXT, ul_debugobj(cxt, "perms: user=<name> detected, ignore"));
     240  
     241  			cxt->flags |= MNT_FL_SAVED_USER;
     242  
     243  			mnt_opt_set_external(opt, 1);
     244  			user_flags &= ~MNT_MS_USER;
     245  		}
     246  
     247  		/*
     248  		 * Insert MS_SECURE between system flags on position where is MNT_MS_USER
     249  		 */
     250  		if ((user_flags & MNT_MS_USER)
     251  		    && (rc = mnt_optlist_insert_flags(ol, MS_SECURE, cxt->map_linux,
     252  						    MNT_MS_USER, cxt->map_userspace)))
     253  			return rc;
     254  
     255  		if ((user_flags & MNT_MS_USERS)
     256  		    && (rc = mnt_optlist_insert_flags(ol, MS_SECURE, cxt->map_linux,
     257  						    MNT_MS_USERS, cxt->map_userspace)))
     258  			return rc;
     259  
     260  		/*
     261  		 * MS_OWNER: Allow owners to mount when fstab contains the
     262  		 * owner option.  Note that this should never be used in a high
     263  		 * security environment, but may be useful to give people at
     264  		 * the console the possibility of mounting a floppy.  MS_GROUP:
     265  		 * Allow members of device group to mount. (Martin Dickopp)
     266  		 */
     267  		if (user_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
     268  			struct stat sb;
     269  			struct libmnt_cache *cache = NULL;
     270  			char *xsrc = NULL;
     271  			const char *srcpath = mnt_fs_get_srcpath(cxt->fs);
     272  
     273  			DBG(CXT, ul_debugobj(cxt, "perms: owner/group"));
     274  
     275  			if (!srcpath) {					/* Ah... source is TAG */
     276  				cache = mnt_context_get_cache(cxt);
     277  				xsrc = mnt_resolve_spec(
     278  						mnt_context_get_source(cxt),
     279  						cache);
     280  				srcpath = xsrc;
     281  			}
     282  			if (!srcpath) {
     283  				DBG(CXT, ul_debugobj(cxt, "perms: src undefined"));
     284  				return -EPERM;
     285  			}
     286  
     287  			if (strncmp(srcpath, "/dev/", 5) == 0 &&
     288  			    stat(srcpath, &sb) == 0 &&
     289  			    (((user_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
     290  			     ((user_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid)))) {
     291  
     292  				/* insert MS_OWNERSECURE between system flags */
     293  				if (user_flags & MNT_MS_OWNER)
     294  					mnt_optlist_insert_flags(ol,
     295  							MS_OWNERSECURE, cxt->map_linux,
     296  							MNT_MS_OWNER, cxt->map_userspace);
     297  				if (user_flags & MNT_MS_GROUP)
     298  					mnt_optlist_insert_flags(ol,
     299  							MS_OWNERSECURE, cxt->map_linux,
     300  							MNT_MS_GROUP, cxt->map_userspace);
     301  
     302  				/* continue as like "user" was specified */
     303  				user_flags |= MNT_MS_USER;
     304  				mnt_optlist_append_flags(ol, MNT_MS_USER, cxt->map_userspace);
     305  			}
     306  
     307  			if (!cache)
     308  				free(xsrc);
     309  		}
     310  
     311  		if (!(user_flags & (MNT_MS_USER | MNT_MS_USERS))) {
     312  			DBG(CXT, ul_debugobj(cxt, "perms: evaluation ends with -EPERMS [flags=0x%08lx]", user_flags));
     313  			return -EPERM;
     314  		}
     315  
     316  		/* we have modified some flags (noexec, ...), let's cleanup the
     317  		 * options to remove duplicate stuff etc.*/
     318  		mnt_optlist_merge_opts(ol);
     319  	}
     320  
     321  	return 0;
     322  }
     323  
     324  /*
     325   * mnt_context_helper_setopt() backend
     326   *
     327   * This function applies the mount.type command line option (for example parsed
     328   * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
     329   * then 1 is returned.
     330   *
     331   * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
     332   */
     333  int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg)
     334  {
     335  	int rc = -EINVAL;
     336  
     337  	assert(cxt);
     338  	assert(cxt->action == MNT_ACT_MOUNT);
     339  
     340  	switch(c) {
     341  	case 'f':
     342  		rc = mnt_context_enable_fake(cxt, TRUE);
     343  		break;
     344  	case 'n':
     345  		rc = mnt_context_disable_mtab(cxt, TRUE);
     346  		break;
     347  	case 'r':
     348  		rc = mnt_context_append_options(cxt, "ro");
     349  		break;
     350  	case 'v':
     351  		rc = mnt_context_enable_verbose(cxt, TRUE);
     352  		break;
     353  	case 'w':
     354  		rc = mnt_context_append_options(cxt, "rw");
     355  		break;
     356  	case 'o':
     357  		if (arg)
     358  			rc = mnt_context_append_options(cxt, arg);
     359  		break;
     360  	case 's':
     361  		rc = mnt_context_enable_sloppy(cxt, TRUE);
     362  		break;
     363  	case 't':
     364  		if (arg)
     365  			rc = mnt_context_set_fstype(cxt, arg);
     366  		break;
     367  	case 'N':
     368  		if (arg)
     369  			rc = mnt_context_set_target_ns(cxt, arg);
     370  		break;
     371  	default:
     372  		return 1;
     373  	}
     374  
     375  	return rc;
     376  }
     377  
     378  static int exec_helper(struct libmnt_context *cxt)
     379  {
     380  	struct libmnt_optlist *ol;
     381  	struct libmnt_ns *ns_tgt = mnt_context_get_target_ns(cxt);
     382  	const char *o = NULL;
     383  	char *namespace = NULL;
     384  	int rc;
     385  	pid_t pid;
     386  
     387  	assert(cxt);
     388  	assert(cxt->fs);
     389  	assert(cxt->helper);
     390  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     391  
     392  	DBG(CXT, ul_debugobj(cxt, "mount: executing helper %s", cxt->helper));
     393  
     394  	ol = mnt_context_get_optlist(cxt);
     395  	if (!ol)
     396  		return -ENOMEM;
     397  
     398  	rc = mnt_optlist_get_optstr(ol, &o, NULL, MNT_OL_FLTR_HELPERS);
     399  	if (rc)
     400  		return rc;
     401  
     402  	if (ns_tgt->fd != -1
     403  	    && asprintf(&namespace, "/proc/%i/fd/%i",
     404  			getpid(), ns_tgt->fd) == -1) {
     405  		return -ENOMEM;
     406  	}
     407  
     408  	DBG_FLUSH;
     409  
     410  	pid = fork();
     411  	switch (pid) {
     412  	case 0:
     413  	{
     414  		const char *args[14], *type;
     415  		int i = 0;
     416  
     417  		if (drop_permissions() != 0)
     418  			_exit(EXIT_FAILURE);
     419  
     420  		if (!mnt_context_switch_origin_ns(cxt))
     421  			_exit(EXIT_FAILURE);
     422  
     423  		type = mnt_fs_get_fstype(cxt->fs);
     424  
     425  		args[i++] = cxt->helper;		/* 1 */
     426  		args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */
     427  		args[i++] = mnt_fs_get_target(cxt->fs);	/* 3 */
     428  
     429  		if (mnt_context_is_sloppy(cxt))
     430  			args[i++] = "-s";		/* 4 */
     431  		if (mnt_context_is_fake(cxt))
     432  			args[i++] = "-f";		/* 5 */
     433  		if (mnt_context_is_nomtab(cxt))
     434  			args[i++] = "-n";		/* 6 */
     435  		if (mnt_context_is_verbose(cxt))
     436  			args[i++] = "-v";		/* 7 */
     437  		if (o) {
     438  			args[i++] = "-o";		/* 8 */
     439  			args[i++] = o;			/* 9 */
     440  		}
     441  		if (type
     442  		    && strchr(type, '.')
     443  		    && !endswith(cxt->helper, type)) {
     444  			args[i++] = "-t";		/* 10 */
     445  			args[i++] = type;		/* 11 */
     446  		}
     447  		if (namespace) {
     448  			args[i++] = "-N";		/* 11 */
     449  			args[i++] = namespace;		/* 12 */
     450  		}
     451  		args[i] = NULL;				/* 13 */
     452  		for (i = 0; args[i]; i++)
     453  			DBG(CXT, ul_debugobj(cxt, "argv[%d] = \"%s\"",
     454  							i, args[i]));
     455  		DBG_FLUSH;
     456  		execv(cxt->helper, (char * const *) args);
     457  		_exit(EXIT_FAILURE);
     458  	}
     459  	default:
     460  	{
     461  		int st;
     462  
     463  		if (waitpid(pid, &st, 0) == (pid_t) -1) {
     464  			cxt->helper_status = -1;
     465  			rc = -errno;
     466  		} else {
     467  			cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
     468  			cxt->helper_exec_status = rc = 0;
     469  		}
     470  		DBG(CXT, ul_debugobj(cxt, "%s executed [status=%d, rc=%d%s]",
     471  				cxt->helper,
     472  				cxt->helper_status, rc,
     473  				rc ? " waitpid failed" : ""));
     474  		break;
     475  	}
     476  
     477  	case -1:
     478  		cxt->helper_exec_status = rc = -errno;
     479  		DBG(CXT, ul_debugobj(cxt, "fork() failed"));
     480  		break;
     481  	}
     482  
     483  	free(namespace);
     484  	return rc;
     485  }
     486  
     487  /*
     488   * The default is to use fstype from cxt->fs, this could be overwritten by
     489   * @try_type argument. If @try_type is specified then mount with MS_SILENT.
     490   *
     491   * Returns: 0 on success,
     492   *         >0 in case of mount(2) error (returns syscall errno),
     493   *         <0 in case of other errors.
     494   */
     495  static int do_mount(struct libmnt_context *cxt, const char *try_type)
     496  {
     497  	int rc = 0;
     498  	char *org_type = NULL;
     499  	struct libmnt_optlist *ol = NULL;
     500  
     501  	assert(cxt);
     502  	assert(cxt->fs);
     503  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     504  
     505  	if (try_type) {
     506  		rc = mnt_context_prepare_helper(cxt, "mount", try_type);
     507  		if (rc)
     508  			return rc;
     509  	}
     510  
     511  	if (cxt->helper)
     512  		return exec_helper(cxt);
     513  
     514  	if (try_type) {
     515  		ol = mnt_context_get_optlist(cxt);
     516  		assert(ol);
     517  
     518  		mnt_optlist_append_flags(ol, MS_SILENT, cxt->map_linux);
     519  		if (mnt_fs_get_fstype(cxt->fs)) {
     520  			org_type = strdup(mnt_fs_get_fstype(cxt->fs));
     521  			if (!org_type) {
     522  				rc = -ENOMEM;
     523  				goto done;
     524  			}
     525  		}
     526  		mnt_fs_set_fstype(cxt->fs, try_type);
     527  	}
     528  
     529  
     530  	/*
     531  	 * mount(2) or others syscalls
     532  	 */
     533  	if (!rc)
     534  		rc = mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT);
     535  
     536  	if (org_type && rc != 0)
     537  		__mnt_fs_set_fstype_ptr(cxt->fs, org_type);
     538  	org_type  = NULL;
     539  
     540  	if (rc == 0 && try_type && cxt->update) {
     541  		struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
     542  		if (fs)
     543  			rc = mnt_fs_set_fstype(fs, try_type);
     544  	}
     545  
     546  done:
     547  	if (try_type && ol)
     548  		mnt_optlist_remove_flags(ol, MS_SILENT, cxt->map_linux);
     549  	free(org_type);
     550  	return rc;
     551  }
     552  
     553  static int is_success_status(struct libmnt_context *cxt)
     554  {
     555  	if (mnt_context_helper_executed(cxt))
     556  		return mnt_context_get_helper_status(cxt) == 0;
     557  
     558  	if (mnt_context_syscall_called(cxt))
     559  		return mnt_context_get_status(cxt) == 1;
     560  
     561  	return 0;
     562  }
     563  
     564  /* try mount(2) for all items in comma separated list of the filesystem @types */
     565  static int do_mount_by_types(struct libmnt_context *cxt, const char *types)
     566  {
     567  	int rc = -EINVAL;
     568  	char *p, *p0;
     569  
     570  	assert(cxt);
     571  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     572  
     573  	DBG(CXT, ul_debugobj(cxt, "trying to mount by FS list '%s'", types));
     574  
     575  	p0 = p = strdup(types);
     576  	if (!p)
     577  		return -ENOMEM;
     578  	do {
     579  		char *autotype = NULL;
     580  		char *end = strchr(p, ',');
     581  
     582  		if (end)
     583  			*end = '\0';
     584  
     585  		DBG(CXT, ul_debugobj(cxt, "-->trying '%s'", p));
     586  
     587  		/* Let's support things like "udf,iso9660,auto" */
     588  		if (strcmp(p, "auto") == 0) {
     589  			rc = mnt_context_guess_srcpath_fstype(cxt, &autotype);
     590  			if (rc) {
     591  				DBG(CXT, ul_debugobj(cxt, "failed to guess FS type [rc=%d]", rc));
     592  				free(p0);
     593  				free(autotype);
     594  				return rc;
     595  			}
     596  			p = autotype;
     597  			DBG(CXT, ul_debugobj(cxt, "   --> '%s'", p));
     598  		}
     599  
     600  		if (p)
     601  			rc = do_mount(cxt, p);
     602  		p = end ? end + 1 : NULL;
     603  		free(autotype);
     604  	} while (!is_success_status(cxt) && p);
     605  
     606  	free(p0);
     607  	return rc;
     608  }
     609  
     610  
     611  static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
     612  {
     613  	int neg = pattern && strncmp(pattern, "no", 2) == 0;
     614  	int rc = -EINVAL;
     615  	char **filesystems, **fp;
     616  	struct libmnt_ns *ns_old;
     617  
     618  	assert(cxt);
     619  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     620  
     621  	/*
     622  	 * Use the pattern as list of the filesystems
     623  	 */
     624  	if (!neg && pattern) {
     625  		DBG(CXT, ul_debugobj(cxt, "use FS pattern as FS list"));
     626  		return do_mount_by_types(cxt, pattern);
     627  	}
     628  
     629  	DBG(CXT, ul_debugobj(cxt, "trying to mount by FS pattern '%s'", pattern));
     630  
     631  	/*
     632  	 * Apply pattern to /etc/filesystems and /proc/filesystems
     633  	 */
     634  	ns_old = mnt_context_switch_origin_ns(cxt);
     635  	if (!ns_old)
     636  		return -MNT_ERR_NAMESPACE;
     637  	rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL);
     638  	if (!mnt_context_switch_ns(cxt, ns_old))
     639  		return -MNT_ERR_NAMESPACE;
     640  	if (rc)
     641  		return rc;
     642  
     643  	if (filesystems == NULL)
     644  		return -MNT_ERR_NOFSTYPE;
     645  
     646  	for (fp = filesystems; *fp; fp++) {
     647  		DBG(CXT, ul_debugobj(cxt, " ##### trying '%s'", *fp));
     648  		rc = do_mount(cxt, *fp);
     649  		if (is_success_status(cxt))
     650  			break;
     651  		if (mnt_context_get_syscall_errno(cxt) != EINVAL &&
     652  		    mnt_context_get_syscall_errno(cxt) != ENODEV)
     653  			break;
     654  	}
     655  	mnt_free_filesystems(filesystems);
     656  	return rc;
     657  }
     658  
     659  static int prepare_target(struct libmnt_context *cxt)
     660  {
     661  	const char *tgt, *prefix;
     662  	int rc = 0;
     663  	struct libmnt_ns *ns_old;
     664  
     665  	assert(cxt);
     666  	assert(cxt->fs);
     667  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     668  
     669  	DBG(CXT, ul_debugobj(cxt, "--> preparing target path"));
     670  
     671  	tgt = mnt_fs_get_target(cxt->fs);
     672  	if (!tgt)
     673  		return 0;
     674  
     675  	/* apply prefix */
     676  	prefix = mnt_context_get_target_prefix(cxt);
     677  	if (prefix) {
     678  		const char *p = *tgt == '/' ? tgt + 1 : tgt;
     679  
     680  		if (!*p)
     681  			/* target is "/", use "/prefix" */
     682  			rc = mnt_fs_set_target(cxt->fs, prefix);
     683  		else {
     684  			char *path = NULL;
     685  
     686  			if (asprintf(&path, "%s/%s", prefix, p) <= 0)
     687  				rc = -ENOMEM;
     688  			else {
     689  				rc = mnt_fs_set_target(cxt->fs, path);
     690  				free(path);
     691  			}
     692  		}
     693  		if (rc)
     694  			return rc;
     695  		tgt = mnt_fs_get_target(cxt->fs);
     696  	}
     697  
     698  	ns_old = mnt_context_switch_target_ns(cxt);
     699  	if (!ns_old)
     700  		return -MNT_ERR_NAMESPACE;
     701  
     702  	/* canonicalize the path */
     703  	if (rc == 0) {
     704  		struct libmnt_cache *cache = mnt_context_get_cache(cxt);
     705  
     706  		if (cache) {
     707  			char *path = mnt_resolve_path(tgt, cache);
     708  			if (path && strcmp(path, tgt) != 0)
     709  				rc = mnt_fs_set_target(cxt->fs, path);
     710  		}
     711  	}
     712  
     713  	if (rc == 0)
     714  		rc = mnt_context_call_hooks(cxt, MNT_STAGE_PREP_TARGET);
     715  
     716  	if (!mnt_context_switch_ns(cxt, ns_old))
     717  		return -MNT_ERR_NAMESPACE;
     718  
     719  	DBG(CXT, ul_debugobj(cxt, "final target '%s' [rc=%d]",
     720  				mnt_fs_get_target(cxt->fs), rc));
     721  	return rc;
     722  }
     723  
     724  /**
     725   * mnt_context_prepare_mount:
     726   * @cxt: context
     727   *
     728   * Prepare context for mounting, unnecessary for mnt_context_mount().
     729   *
     730   * Returns: negative number on error, zero on success
     731   */
     732  int mnt_context_prepare_mount(struct libmnt_context *cxt)
     733  {
     734  	int rc = -EINVAL;
     735  	struct libmnt_ns *ns_old;
     736  
     737  	if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
     738  		return -EINVAL;
     739  	if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
     740  		return -EINVAL;
     741  	if (cxt->flags & MNT_FL_PREPARED)
     742  		return 0;
     743  
     744  	assert(cxt->helper_exec_status == 1);
     745  	assert(cxt->syscall_status == 1);
     746  
     747  	cxt->action = MNT_ACT_MOUNT;
     748  
     749  	ns_old = mnt_context_switch_target_ns(cxt);
     750  	if (!ns_old)
     751  		return -MNT_ERR_NAMESPACE;
     752  
     753  	DBG(CXT, ul_debugobj(cxt, "mount: preparing"));
     754  
     755  	rc = mnt_context_apply_fstab(cxt);
     756  	if (!rc)
     757  		rc = mnt_context_merge_mflags(cxt);
     758  	if (!rc && cxt->fs && cxt->optlist)
     759  		rc = mnt_fs_follow_optlist(cxt->fs, cxt->optlist);
     760  	if (!rc)
     761  		rc = evaluate_permissions(cxt);
     762  	if (!rc)
     763  		rc = fix_optstr(cxt);
     764  	if (!rc)
     765  		rc = mnt_context_prepare_srcpath(cxt);
     766  	if (!rc)
     767  		rc = mnt_context_guess_fstype(cxt);
     768  	if (!rc)
     769  		rc = prepare_target(cxt);
     770  	if (!rc)
     771  		rc = mnt_context_prepare_helper(cxt, "mount", NULL);
     772  
     773  	if (!rc && mnt_context_is_onlyonce(cxt)) {
     774  		int mounted = 0;
     775  		rc = mnt_context_is_fs_mounted(cxt, cxt->fs, &mounted);
     776  		if (rc == 0 && mounted == 1) {
     777  			rc = -MNT_ERR_ONLYONCE;
     778  			goto end;
     779  		}
     780  	}
     781  
     782  	if (!rc)
     783  		rc = mnt_context_call_hooks(cxt, MNT_STAGE_PREP);
     784  
     785  	if (rc) {
     786  		DBG(CXT, ul_debugobj(cxt, "mount: preparing failed"));
     787  		goto end;
     788  	}
     789  
     790  	cxt->flags |= MNT_FL_PREPARED;
     791  
     792  end:
     793  	if (!mnt_context_switch_ns(cxt, ns_old))
     794  		return -MNT_ERR_NAMESPACE;
     795  
     796  	return rc;
     797  }
     798  
     799  /**
     800   * mnt_context_do_mount
     801   * @cxt: context
     802   *
     803   * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
     804   *
     805   * Note that this function could be called only once. If you want to mount
     806   * another source or target, then you have to call mnt_reset_context().
     807   *
     808   * If you want to call mount(2) for the same source and target with different
     809   * mount flags or fstype, then call mnt_context_reset_status() and then try
     810   * again mnt_context_do_mount().
     811   *
     812   * WARNING: non-zero return code does not mean that mount(2) syscall or
     813   *          mount.type helper wasn't successfully called.
     814   *
     815   * Check mnt_context_get_status() after error! See mnt_context_mount() for more
     816   * details about errors and warnings.
     817   *
     818   * Returns: 0 on success;
     819   *         >0 in case of mount(2) error (returns syscall errno),
     820   *         <0 in case of other errors.
     821   */
     822  int mnt_context_do_mount(struct libmnt_context *cxt)
     823  {
     824  	const char *type;
     825  	int res = 0, rc = 0;
     826  	struct libmnt_ns *ns_old;
     827  
     828  	assert(cxt);
     829  	assert(cxt->fs);
     830  	assert(cxt->helper_exec_status == 1);
     831  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     832  	assert((cxt->flags & MNT_FL_PREPARED));
     833  	assert((cxt->action == MNT_ACT_MOUNT));
     834  
     835  	DBG(CXT, ul_debugobj(cxt, "mount: do mount"));
     836  
     837  	ns_old = mnt_context_switch_target_ns(cxt);
     838  	if (!ns_old)
     839  		return -MNT_ERR_NAMESPACE;
     840  
     841  	/* before mount stage */
     842  	rc = mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT_PRE);
     843  	if (rc)
     844  		return rc;
     845  
     846  	/* mount stage */
     847  	type = mnt_fs_get_fstype(cxt->fs);
     848  	if (type) {
     849  		if (strchr(type, ','))
     850  			/* this only happens if fstab contains a list of filesystems */
     851  			res = do_mount_by_types(cxt, type);
     852  		else
     853  			res = do_mount(cxt, NULL);
     854  	} else
     855  		res = do_mount_by_pattern(cxt, cxt->fstype_pattern);
     856  
     857  	/* after mount stage */
     858  	if (res == 0) {
     859  		rc = mnt_context_call_hooks(cxt, MNT_STAGE_MOUNT_POST);
     860  		if (rc)
     861  			return rc;
     862  	}
     863  
     864  	if (!mnt_context_switch_ns(cxt, ns_old))
     865  		return -MNT_ERR_NAMESPACE;
     866  
     867  	DBG(CXT, ul_debugobj(cxt, "mnt_context_do_mount() done [rc=%d]", res));
     868  	return res;
     869  }
     870  
     871  /*
     872   * Returns mountinfo FS entry of context source patch if the source is already
     873   * mounted. This function is used for "already mounted" message or to get FS of
     874   * re-used loop device.
     875   */
     876  static struct libmnt_fs *get_already_mounted_source(struct libmnt_context *cxt)
     877  {
     878  	const char *src;
     879  	struct libmnt_table *tb;
     880  
     881  	assert(cxt);
     882  
     883  	src = mnt_fs_get_srcpath(cxt->fs);
     884  
     885  	if (src && mnt_context_get_mountinfo(cxt, &tb) == 0) {
     886  		struct libmnt_iter itr;
     887  		struct libmnt_fs *fs;
     888  
     889  		mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     890  		while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
     891  			const char *s = mnt_fs_get_srcpath(fs),
     892  				   *t = mnt_fs_get_target(fs);
     893  
     894  			if (t && s && mnt_fs_streq_srcpath(fs, src))
     895  				return fs;
     896  		}
     897  	}
     898  	return NULL;
     899  }
     900  
     901  /*
     902   * Checks if source filesystem superblock is already ro-mounted. Note that we
     903   * care about FS superblock as VFS node is irrelevant here.
     904   */
     905  static int is_source_already_rdonly(struct libmnt_context *cxt)
     906  {
     907  	struct libmnt_fs *fs = get_already_mounted_source(cxt);
     908  	const char *opts = fs ? mnt_fs_get_fs_options(fs) : NULL;
     909  
     910  	return opts && mnt_optstr_get_option(opts, "ro", NULL, NULL) == 0;
     911  }
     912  
     913  /**
     914   * mnt_context_finalize_mount:
     915   * @cxt: context
     916   *
     917   * Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called
     918   * after mnt_context_do_mount(). See also mnt_context_set_syscall_status().
     919   *
     920   * Returns: negative number on error, 0 on success.
     921   */
     922  int mnt_context_finalize_mount(struct libmnt_context *cxt)
     923  {
     924  	int rc;
     925  
     926  	assert(cxt);
     927  	assert(cxt->fs);
     928  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     929  	assert((cxt->flags & MNT_FL_PREPARED));
     930  
     931  	rc = mnt_context_prepare_update(cxt);
     932  	if (!rc)
     933  		rc = mnt_context_update_tabs(cxt);
     934  	return rc;
     935  }
     936  
     937  /**
     938   * mnt_context_mount:
     939   * @cxt: mount context
     940   *
     941   * High-level, mounts the filesystem by mount(2) or fork()+exec(/sbin/mount.type).
     942   *
     943   * This is similar to:
     944   *
     945   *	mnt_context_prepare_mount(cxt);
     946   *	mnt_context_do_mount(cxt);
     947   *	mnt_context_finalize_mount(cxt);
     948   *
     949   * See also mnt_context_disable_helpers().
     950   *
     951   * Note that this function should be called only once. If you want to mount with
     952   * different settings, then you have to call mnt_reset_context(). It's NOT enough
     953   * to call mnt_context_reset_status(). If you want to call this function more than
     954   * once, the whole context has to be reset.
     955   *
     956   * WARNING: non-zero return code does not mean that mount(2) syscall or
     957   *          mount.type helper wasn't successfully called.
     958   *
     959   * Always use mnt_context_get_status():
     960   *
     961   * <informalexample>
     962   *   <programlisting>
     963   *       rc = mnt_context_mount(cxt);
     964   *
     965   *       if (mnt_context_helper_executed(cxt))
     966   *               return mnt_context_get_helper_status(cxt);
     967   *       if (rc == 0 && mnt_context_get_status(cxt) == 1)
     968   *               return MNT_EX_SUCCESS;
     969   *       return MNT_EX_FAIL;
     970   *   </programlisting>
     971   * </informalexample>
     972   *
     973   * or mnt_context_get_excode() to generate mount(8) compatible error
     974   * or warning message:
     975   *
     976   * <informalexample>
     977   *   <programlisting>
     978   *       rc = mnt_context_mount(cxt);
     979   *       rc = mnt_context_get_excode(cxt, rc, buf, sizeof(buf));
     980   *       if (buf)
     981   *               warnx(_("%s: %s"), mnt_context_get_target(cxt), buf);
     982   *	 return rc;   // MNT_EX_*
     983   *   </programlisting>
     984   * </informalexample>
     985   *
     986   * Returns: 0 on success;
     987   *         >0 in case of mount(2) error (returns syscall errno),
     988   *         <0 in case of other errors.
     989   */
     990  int mnt_context_mount(struct libmnt_context *cxt)
     991  {
     992  	int rc;
     993  	struct libmnt_ns *ns_old;
     994  
     995  	assert(cxt);
     996  	assert(cxt->fs);
     997  	assert(cxt->helper_exec_status == 1);
     998  
     999  	ns_old = mnt_context_switch_target_ns(cxt);
    1000  	if (!ns_old)
    1001  		return -MNT_ERR_NAMESPACE;
    1002  
    1003  again:
    1004  	rc = mnt_context_prepare_mount(cxt);
    1005  	if (!rc)
    1006  		rc = mnt_context_prepare_update(cxt);
    1007  	if (!rc)
    1008  		rc = mnt_context_do_mount(cxt);
    1009  	if (!rc)
    1010  		rc = mnt_context_update_tabs(cxt);
    1011  
    1012  	/*
    1013  	 * Read-only device or already read-only mounted FS.
    1014  	 * Try mount the filesystem read-only.
    1015  	 */
    1016  	if ((rc == -EROFS && !mnt_context_syscall_called(cxt))	/* before syscall; rdonly loopdev */
    1017  	     || mnt_context_get_syscall_errno(cxt) == EROFS	/* syscall failed with EROFS */
    1018  	     || mnt_context_get_syscall_errno(cxt) == EACCES	/* syscall failed with EACCES */
    1019  	     || (mnt_context_get_syscall_errno(cxt) == EBUSY	/* already ro-mounted FS */
    1020  		 && is_source_already_rdonly(cxt)))
    1021  	{
    1022  		unsigned long mflags = 0;
    1023  
    1024  		mnt_context_get_mflags(cxt, &mflags);
    1025  
    1026  		if (!(mflags & MS_RDONLY)			/* not yet RDONLY */
    1027  		    && !(mflags & MS_REMOUNT)			/* not remount */
    1028  		    && !(mflags & MS_BIND)			/* not bin mount */
    1029  		    && !mnt_context_is_rwonly_mount(cxt)) {	/* no explicit read-write */
    1030  
    1031  			assert(!(cxt->flags & MNT_FL_FORCED_RDONLY));
    1032  			DBG(CXT, ul_debugobj(cxt, "write-protected source, trying RDONLY."));
    1033  
    1034  			mnt_context_reset_status(cxt);
    1035  			mnt_context_set_mflags(cxt, mflags | MS_RDONLY);
    1036  			cxt->flags |= MNT_FL_FORCED_RDONLY;
    1037  			goto again;
    1038  		}
    1039  	}
    1040  
    1041  	if (rc == 0)
    1042  		rc = mnt_context_call_hooks(cxt, MNT_STAGE_POST);
    1043  
    1044  	mnt_context_deinit_hooksets(cxt);
    1045  
    1046  	if (!mnt_context_switch_ns(cxt, ns_old))
    1047  		rc = -MNT_ERR_NAMESPACE;
    1048  
    1049  	DBG(CXT, ul_debugobj(cxt, "mnt_context_mount() done [rc=%d]", rc));
    1050  	return rc;
    1051  }
    1052  
    1053  /**
    1054   * mnt_context_next_mount:
    1055   * @cxt: context
    1056   * @itr: iterator
    1057   * @fs: returns the current filesystem
    1058   * @mntrc: returns the return code from mnt_context_mount()
    1059   * @ignored: returns 1 for non-matching and 2 for already mounted filesystems
    1060   *
    1061   * This function tries to mount the next filesystem from fstab (as returned by
    1062   * mnt_context_get_fstab()). See also mnt_context_set_fstab().
    1063   *
    1064   * You can filter out filesystems by:
    1065   *	mnt_context_set_options_pattern() to simulate mount -a -O pattern
    1066   *	mnt_context_set_fstype_pattern()  to simulate mount -a -t pattern
    1067   *
    1068   * If the filesystem is already mounted or does not match defined criteria,
    1069   * then the mnt_context_next_mount() function returns zero, but the @ignored is
    1070   * non-zero. Note that the root filesystem and filesystems with "noauto" option
    1071   * are always ignored.
    1072   *
    1073   * If mount(2) syscall or mount.type helper failed, then the
    1074   * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
    1075   * Use also mnt_context_get_status() to check if the filesystem was
    1076   * successfully mounted.
    1077   *
    1078   * See mnt_context_mount() for more details about errors and warnings.
    1079   *
    1080   * Returns: 0 on success,
    1081   *         <0 in case of error (!= mount(2) errors)
    1082   *          1 at the end of the list.
    1083   */
    1084  int mnt_context_next_mount(struct libmnt_context *cxt,
    1085  			   struct libmnt_iter *itr,
    1086  			   struct libmnt_fs **fs,
    1087  			   int *mntrc,
    1088  			   int *ignored)
    1089  {
    1090  	struct libmnt_table *fstab, *mountinfo;
    1091  	const char *o, *tgt;
    1092  	int rc, mounted = 0;
    1093  
    1094  	if (ignored)
    1095  		*ignored = 0;
    1096  	if (mntrc)
    1097  		*mntrc = 0;
    1098  
    1099  	if (!cxt || !fs || !itr)
    1100  		return -EINVAL;
    1101  
    1102  	/* ingore --onlyonce, it's default behavior for --all */
    1103  	mnt_context_enable_onlyonce(cxt, 0);
    1104  
    1105  	rc = mnt_context_get_fstab(cxt, &fstab);
    1106  	if (rc)
    1107  		return rc;
    1108  
    1109  	rc = mnt_table_next_fs(fstab, itr, fs);
    1110  	if (rc != 0)
    1111  		return rc;	/* more filesystems (or error) */
    1112  
    1113  	o = mnt_fs_get_user_options(*fs);
    1114  	tgt = mnt_fs_get_target(*fs);
    1115  
    1116  	DBG(CXT, ul_debugobj(cxt, "next-mount: trying %s", tgt));
    1117  
    1118  	/*  ignore swap */
    1119  	if (mnt_fs_is_swaparea(*fs) ||
    1120  
    1121  	/* ignore root filesystem */
    1122  	   (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) ||
    1123  
    1124  	/* ignore noauto filesystems */
    1125  	   (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) ||
    1126  
    1127  	/* ignore filesystems which don't match options patterns */
    1128  	   (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
    1129  					cxt->fstype_pattern)) ||
    1130  
    1131  	/* ignore filesystems which don't match type patterns */
    1132  	   (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
    1133  					cxt->optstr_pattern))) {
    1134  		if (ignored)
    1135  			*ignored = 1;
    1136  		DBG(CXT, ul_debugobj(cxt, "next-mount: not-match "
    1137  				"[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
    1138  				mnt_fs_get_fstype(*fs),
    1139  				cxt->fstype_pattern,
    1140  				mnt_fs_get_options(*fs),
    1141  				cxt->optstr_pattern));
    1142  		return 0;
    1143  	}
    1144  
    1145  	/* ignore already mounted filesystems */
    1146  	rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted);
    1147  	if (rc) {
    1148  		if (mnt_table_is_empty(cxt->mountinfo)) {
    1149  			DBG(CXT, ul_debugobj(cxt, "next-mount: no mount table [rc=%d], ignore", rc));
    1150  			rc = 0;
    1151  			if (ignored)
    1152  				*ignored = 1;
    1153  		}
    1154  		return rc;
    1155  	}
    1156  	if (mounted) {
    1157  		if (ignored)
    1158  			*ignored = 2;
    1159  		return 0;
    1160  	}
    1161  
    1162  	/* Save mount options, etc. -- this is effective for the first
    1163  	 * mnt_context_next_mount() call only. Make sure that cxt has not set
    1164  	 * source, target or fstype.
    1165  	 */
    1166  	if (!mnt_context_has_template(cxt)) {
    1167  		mnt_context_set_source(cxt, NULL);
    1168  		mnt_context_set_target(cxt, NULL);
    1169  		mnt_context_set_fstype(cxt, NULL);
    1170  		mnt_context_save_template(cxt);
    1171  	}
    1172  
    1173  	/* reset context, but protect mountinfo */
    1174  	mountinfo = cxt->mountinfo;
    1175  	cxt->mountinfo = NULL;
    1176  	mnt_reset_context(cxt);
    1177  	cxt->mountinfo = mountinfo;
    1178  
    1179  	if (mnt_context_is_fork(cxt)) {
    1180  		rc = mnt_fork_context(cxt);
    1181  		if (rc)
    1182  			return rc;		/* fork error */
    1183  
    1184  		if (mnt_context_is_parent(cxt)) {
    1185  			return 0;		/* parent */
    1186  		}
    1187  	}
    1188  
    1189  	/*
    1190  	 * child or non-forked
    1191  	 */
    1192  
    1193  	/* copy stuff from fstab to context */
    1194  	rc = mnt_context_apply_fs(cxt, *fs);
    1195  	if (!rc) {
    1196  		/*
    1197  		 * "-t <pattern>" is used to filter out fstab entries, but for ordinary
    1198  		 * mount operation -t means "-t <type>". We have to zeroize the pattern
    1199  		 * to avoid misinterpretation.
    1200  		 */
    1201  		char *pattern = cxt->fstype_pattern;
    1202  		cxt->fstype_pattern = NULL;
    1203  
    1204  		rc = mnt_context_mount(cxt);
    1205  
    1206  		cxt->fstype_pattern = pattern;
    1207  
    1208  		if (mntrc)
    1209  			*mntrc = rc;
    1210  	}
    1211  
    1212  	if (mnt_context_is_child(cxt)) {
    1213  		DBG(CXT, ul_debugobj(cxt, "next-mount: child exit [rc=%d]", rc));
    1214  		DBG_FLUSH;
    1215  		_exit(rc);
    1216  	}
    1217  	return 0;
    1218  }
    1219  
    1220  
    1221  /**
    1222   * mnt_context_next_remount:
    1223   * @cxt: context
    1224   * @itr: iterator
    1225   * @fs: returns the current filesystem
    1226   * @mntrc: returns the return code from mnt_context_mount()
    1227   * @ignored: returns 1 for non-matching
    1228   *
    1229   * This function tries to remount the next mounted filesystem (as returned by
    1230   * mnt_context_get_mtab()).
    1231   *
    1232   * You can filter out filesystems by:
    1233   *	mnt_context_set_options_pattern() to simulate mount -a -O pattern
    1234   *	mnt_context_set_fstype_pattern()  to simulate mount -a -t pattern
    1235   *
    1236   * If the filesystem does not match defined criteria, then the
    1237   * mnt_context_next_remount() function returns zero, but the @ignored is
    1238   * non-zero.
    1239   *
    1240   * IMPORTANT -- the mount operation is performed in the current context.
    1241   * The context is reset before the next mount (see mnt_reset_context()).
    1242   * The context setting related to the filesystem (e.g. mount options,
    1243   * etc.) are protected.
    1244  
    1245   * If mount(2) syscall or mount.type helper failed, then the
    1246   * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
    1247   * Use also mnt_context_get_status() to check if the filesystem was
    1248   * successfully mounted.
    1249   *
    1250   * See mnt_context_mount() for more details about errors and warnings.
    1251   *
    1252   * Returns: 0 on success,
    1253   *         <0 in case of error (!= mount(2) errors)
    1254   *          1 at the end of the list.
    1255   *
    1256   * Since: 2.34
    1257   */
    1258  int mnt_context_next_remount(struct libmnt_context *cxt,
    1259  			   struct libmnt_iter *itr,
    1260  			   struct libmnt_fs **fs,
    1261  			   int *mntrc,
    1262  			   int *ignored)
    1263  {
    1264  	struct libmnt_table *mountinfo;
    1265  	const char *tgt;
    1266  	int rc;
    1267  
    1268  	if (ignored)
    1269  		*ignored = 0;
    1270  	if (mntrc)
    1271  		*mntrc = 0;
    1272  
    1273  	if (!cxt || !fs || !itr)
    1274  		return -EINVAL;
    1275  
    1276  	rc = mnt_context_get_mountinfo(cxt, &mountinfo);
    1277  	if (rc)
    1278  		return rc;
    1279  
    1280  	rc = mnt_table_next_fs(mountinfo, itr, fs);
    1281  	if (rc != 0)
    1282  		return rc;	/* more filesystems (or error) */
    1283  
    1284  	tgt = mnt_fs_get_target(*fs);
    1285  
    1286  	DBG(CXT, ul_debugobj(cxt, "next-remount: trying %s", tgt));
    1287  
    1288  	/* ignore filesystems which don't match options patterns */
    1289  	if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
    1290  					cxt->fstype_pattern)) ||
    1291  
    1292  	/* ignore filesystems which don't match type patterns */
    1293  	   (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
    1294  					cxt->optstr_pattern))) {
    1295  		if (ignored)
    1296  			*ignored = 1;
    1297  		DBG(CXT, ul_debugobj(cxt, "next-remount: not-match "
    1298  				"[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
    1299  				mnt_fs_get_fstype(*fs),
    1300  				cxt->fstype_pattern,
    1301  				mnt_fs_get_options(*fs),
    1302  				cxt->optstr_pattern));
    1303  		return 0;
    1304  	}
    1305  
    1306  	/* Save mount options, etc. -- this is effective for the first
    1307  	 * mnt_context_next_remount() call only. Make sure that cxt has not set
    1308  	 * source, target or fstype.
    1309  	 */
    1310  	if (!mnt_context_has_template(cxt)) {
    1311  		mnt_context_set_source(cxt, NULL);
    1312  		mnt_context_set_target(cxt, NULL);
    1313  		mnt_context_set_fstype(cxt, NULL);
    1314  		mnt_context_save_template(cxt);
    1315  	}
    1316  
    1317  	/* restore original, but protect mountinfo */
    1318  	cxt->mountinfo = NULL;
    1319  	mnt_reset_context(cxt);
    1320  	cxt->mountinfo = mountinfo;
    1321  
    1322  	rc = mnt_context_set_target(cxt, tgt);
    1323  	if (!rc) {
    1324  		/*
    1325  		 * "-t <pattern>" is used to filter out fstab entries, but for ordinary
    1326  		 * mount operation -t means "-t <type>". We have to zeroize the pattern
    1327  		 * to avoid misinterpretation.
    1328  		 */
    1329  		char *pattern = cxt->fstype_pattern;
    1330  		cxt->fstype_pattern = NULL;
    1331  
    1332  		rc = mnt_context_mount(cxt);
    1333  
    1334  		cxt->fstype_pattern = pattern;
    1335  
    1336  		if (mntrc)
    1337  			*mntrc = rc;
    1338  		rc = 0;
    1339  	}
    1340  
    1341  	return rc;
    1342  }
    1343  
    1344  /*
    1345   * Returns 1 if @dir parent is shared
    1346   */
    1347  static int is_shared_tree(struct libmnt_context *cxt, const char *dir)
    1348  {
    1349  	struct libmnt_table *tb = NULL;
    1350  	struct libmnt_fs *fs;
    1351  	unsigned long mflags = 0;
    1352  	char *mnt = NULL, *p;
    1353  	int rc = 0;
    1354  	struct libmnt_ns *ns_old;
    1355  
    1356  	ns_old = mnt_context_switch_target_ns(cxt);
    1357  	if (!ns_old)
    1358  		return -MNT_ERR_NAMESPACE;
    1359  
    1360  	if (!dir)
    1361  		return 0;
    1362  	if (mnt_context_get_mountinfo(cxt, &tb) || !tb)
    1363  		goto done;
    1364  
    1365  	mnt = strdup(dir);
    1366  	if (!mnt)
    1367  		goto done;
    1368  	p = strrchr(mnt, '/');
    1369  	if (!p)
    1370  		goto done;
    1371  	if (p > mnt)
    1372  		*p = '\0';
    1373  	fs = mnt_table_find_mountpoint(tb, mnt, MNT_ITER_BACKWARD);
    1374  
    1375  	rc = fs && mnt_fs_is_kernel(fs)
    1376  		&& mnt_fs_get_propagation(fs, &mflags) == 0
    1377  		&& (mflags & MS_SHARED);
    1378  done:
    1379  	free(mnt);
    1380  	if (!mnt_context_switch_ns(cxt, ns_old))
    1381  		return -MNT_ERR_NAMESPACE;
    1382  	return rc;
    1383  }
    1384  
    1385  int mnt_context_get_mount_excode(
    1386  			struct libmnt_context *cxt,
    1387  			int rc,
    1388  			char *buf,
    1389  			size_t bufsz)
    1390  {
    1391  	int syserr;
    1392  	struct stat st;
    1393  	unsigned long uflags = 0, mflags = 0;
    1394  
    1395  	int restricted = mnt_context_is_restricted(cxt);
    1396  	const char *tgt = mnt_context_get_target(cxt);
    1397  	const char *src = mnt_context_get_source(cxt);
    1398  
    1399  	if (mnt_context_helper_executed(cxt)) {
    1400  		/*
    1401  		 * /sbin/mount.<type> called, return status
    1402  		 */
    1403  		if (rc == -MNT_ERR_APPLYFLAGS && buf)
    1404  			snprintf(buf, bufsz, _("WARNING: failed to apply propagation flags"));
    1405  
    1406  		return mnt_context_get_helper_status(cxt);
    1407  	}
    1408  
    1409  	if (rc == 0 && mnt_context_get_status(cxt) == 1) {
    1410  		/*
    1411  		 * Libmount success && syscall success.
    1412  		 */
    1413  		if (buf && mnt_context_forced_rdonly(cxt))
    1414  			snprintf(buf, bufsz, _("WARNING: source write-protected, mounted read-only"));
    1415  		return MNT_EX_SUCCESS;
    1416  	}
    1417  
    1418  	mnt_context_get_mflags(cxt, &mflags);		/* mount(2) flags */
    1419  	mnt_context_get_user_mflags(cxt, &uflags);	/* userspace flags */
    1420  
    1421  	if (!mnt_context_syscall_called(cxt)) {
    1422  		/*
    1423  		 * libmount errors (extra library checks)
    1424  		 */
    1425  		switch (rc) {
    1426  		case -EPERM:
    1427  			if (buf)
    1428  				snprintf(buf, bufsz, _("operation permitted for root only"));
    1429  			return MNT_EX_USAGE;
    1430  		case -EBUSY:
    1431  			if (buf)
    1432  				snprintf(buf, bufsz, _("%s is already mounted"), src);
    1433  			return MNT_EX_USAGE;
    1434  		case -MNT_ERR_NOFSTAB:
    1435  			if (!buf)
    1436  				return MNT_EX_USAGE;
    1437  			if (mnt_context_is_swapmatch(cxt))
    1438  				snprintf(buf, bufsz, _("can't find in %s"),
    1439  						mnt_get_fstab_path());
    1440  			else if (tgt)
    1441  				snprintf(buf, bufsz, _("can't find mount point in %s"),
    1442  						mnt_get_fstab_path());
    1443  			else if (src)
    1444  				snprintf(buf, bufsz, _("can't find mount source %s in %s"),
    1445  						src, mnt_get_fstab_path());
    1446  			return MNT_EX_USAGE;
    1447  		case -MNT_ERR_AMBIFS:
    1448  			if (buf)
    1449  				snprintf(buf, bufsz, _("more filesystems detected on %s; use -t <type> or wipefs(8)"), src);
    1450  			return MNT_EX_USAGE;
    1451  		case -MNT_ERR_NOFSTYPE:
    1452  			if (buf)
    1453  				snprintf(buf, bufsz, restricted ?
    1454  						_("failed to determine filesystem type") :
    1455  						_("no valid filesystem type specified"));
    1456  			return MNT_EX_USAGE;
    1457  		case -MNT_ERR_NOSOURCE:
    1458  			if (uflags & MNT_MS_NOFAIL)
    1459  				return MNT_EX_SUCCESS;
    1460  			if (buf) {
    1461  				if (src)
    1462  					snprintf(buf, bufsz, _("can't find %s"), src);
    1463  				else
    1464  					snprintf(buf, bufsz, _("no mount source specified"));
    1465  			}
    1466  			return MNT_EX_USAGE;
    1467  		case -MNT_ERR_MOUNTOPT:
    1468  			if (buf) {
    1469  				const char *opts = mnt_context_get_options(cxt);
    1470  
    1471  				if (!opts)
    1472  					opts = "";
    1473  				if (opts)
    1474  					snprintf(buf, bufsz, errno ?
    1475  						_("failed to parse mount options '%s': %m") :
    1476  						_("failed to parse mount options '%s'"), opts);
    1477  				else
    1478  					snprintf(buf, bufsz, errno ?
    1479  						_("failed to parse mount options: %m") :
    1480  						_("failed to parse mount options"));
    1481  			}
    1482  			return MNT_EX_USAGE;
    1483  		case -MNT_ERR_LOOPDEV:
    1484  			if (buf)
    1485  				snprintf(buf, bufsz, _("failed to setup loop device for %s"), src);
    1486  			return MNT_EX_FAIL;
    1487  		case -MNT_ERR_LOOPOVERLAP:
    1488  			if (buf)
    1489  				snprintf(buf, bufsz, _("overlapping loop device exists for %s"), src);
    1490  			return MNT_EX_FAIL;
    1491  		case -MNT_ERR_LOCK:
    1492  			if (buf)
    1493  				snprintf(buf, bufsz, _("locking failed"));
    1494  			return MNT_EX_FILEIO;
    1495  		case -MNT_ERR_NAMESPACE:
    1496  			if (buf)
    1497  				snprintf(buf, bufsz, _("failed to switch namespace"));
    1498  			return MNT_EX_SYSERR;
    1499  		case -MNT_ERR_ONLYONCE:
    1500  			if (buf)
    1501  				snprintf(buf, bufsz, _("filesystem already mounted"));
    1502  			return MNT_EX_FAIL;
    1503  		default:
    1504  			return mnt_context_get_generic_excode(rc, buf, bufsz, _("mount failed: %m"));
    1505  		}
    1506  
    1507  	} else if (mnt_context_get_syscall_errno(cxt) == 0) {
    1508  		/*
    1509  		 * mount(2) syscall success, but something else failed
    1510  		 * (probably error in utab processing).
    1511  		 */
    1512  		if (rc == -MNT_ERR_LOCK) {
    1513  			if (buf)
    1514  				snprintf(buf, bufsz, _("filesystem was mounted, but failed to update userspace mount table"));
    1515  			return MNT_EX_FILEIO;
    1516  		}
    1517  
    1518  		if (rc == -MNT_ERR_NAMESPACE) {
    1519  			if (buf)
    1520  				snprintf(buf, bufsz, _("filesystem was mounted, but failed to switch namespace back"));
    1521  			return MNT_EX_SYSERR;
    1522  		}
    1523  
    1524  		if (rc == -MNT_ERR_CHOWN) {
    1525  			if (buf)
    1526  				snprintf(buf, bufsz, _("filesystem was mounted, but failed to change ownership: %m"));
    1527  			return MNT_EX_SYSERR;
    1528  		}
    1529  
    1530  		if (rc == -MNT_ERR_CHMOD) {
    1531  			if (buf)
    1532  				snprintf(buf, bufsz, _("filesystem was mounted, but failed to change mode: %m"));
    1533  			return MNT_EX_SYSERR;
    1534  		}
    1535  
    1536  		if (rc == -MNT_ERR_IDMAP) {
    1537  			if (buf)
    1538  				snprintf(buf, bufsz, _("filesystem was mounted, but failed to attach idmapping"));
    1539  			return MNT_EX_SYSERR;
    1540  		}
    1541  
    1542  		if (rc < 0)
    1543  			return mnt_context_get_generic_excode(rc, buf, bufsz,
    1544  				_("filesystem was mounted, but any subsequent operation failed: %m"));
    1545  
    1546  		return MNT_EX_SOFTWARE;	/* internal error */
    1547  
    1548  	}
    1549  
    1550  	/*
    1551  	 * mount(2) and other mount related syscalls errors
    1552  	 */
    1553  	syserr = mnt_context_get_syscall_errno(cxt);
    1554  
    1555  
    1556  	switch(syserr) {
    1557  	case EPERM:
    1558  		if (!buf)
    1559  			break;
    1560  		if (geteuid() == 0) {
    1561  			if (mnt_safe_stat(tgt, &st) || !S_ISDIR(st.st_mode))
    1562  				snprintf(buf, bufsz, _("mount point is not a directory"));
    1563  			else
    1564  				snprintf(buf, bufsz, _("permission denied"));
    1565  		} else
    1566  			snprintf(buf, bufsz, _("must be superuser to use mount"));
    1567  		break;
    1568  
    1569  	case EBUSY:
    1570  		if (!buf)
    1571  			break;
    1572  		if (mflags & MS_REMOUNT) {
    1573  			snprintf(buf, bufsz, _("mount point is busy"));
    1574  			break;
    1575  		}
    1576  		if (src) {
    1577  			struct libmnt_fs *fs = get_already_mounted_source(cxt);
    1578  
    1579  			if (fs && mnt_fs_get_target(fs))
    1580  				snprintf(buf, bufsz, _("%s already mounted on %s"),
    1581  						src, mnt_fs_get_target(fs));
    1582  		}
    1583  		if (!*buf)
    1584  			snprintf(buf, bufsz, _("%s already mounted or mount point busy"), src);
    1585  		break;
    1586  	case ENOENT:
    1587  		if (tgt && mnt_safe_lstat(tgt, &st)) {
    1588  			if (buf)
    1589  				snprintf(buf, bufsz, _("mount point does not exist"));
    1590  		} else if (tgt && mnt_safe_stat(tgt, &st)) {
    1591  			if (buf)
    1592  				snprintf(buf, bufsz, _("mount point is a symbolic link to nowhere"));
    1593  		} else if (src && !mnt_is_path(src)) {
    1594  			if (uflags & MNT_MS_NOFAIL)
    1595  				return MNT_EX_SUCCESS;
    1596  			if (buf)
    1597  				snprintf(buf, bufsz, _("special device %s does not exist"), src);
    1598  		} else if (buf) {
    1599  			errno = syserr;
    1600  			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
    1601  		}
    1602  		break;
    1603  
    1604  	case ENOTDIR:
    1605  		if (mnt_safe_stat(tgt, &st) || ! S_ISDIR(st.st_mode)) {
    1606  			if (buf)
    1607  				snprintf(buf, bufsz, _("mount point is not a directory"));
    1608  		} else if (src && !mnt_is_path(src)) {
    1609  			if (uflags & MNT_MS_NOFAIL)
    1610  				return MNT_EX_SUCCESS;
    1611  			if (buf)
    1612  				snprintf(buf, bufsz, _("special device %s does not exist "
    1613  					 "(a path prefix is not a directory)"), src);
    1614  		} else if (buf) {
    1615  			errno = syserr;
    1616  			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
    1617  		}
    1618  		break;
    1619  
    1620  	case EINVAL:
    1621  		if (!buf)
    1622  			break;
    1623  		if (mflags & MS_REMOUNT)
    1624  			snprintf(buf, bufsz, _("mount point not mounted or bad option"));
    1625  		else if (rc == -MNT_ERR_APPLYFLAGS)
    1626  			snprintf(buf, bufsz, _("not mount point or bad option"));
    1627  		else if ((mflags & MS_MOVE) && is_shared_tree(cxt, src))
    1628  			snprintf(buf, bufsz,
    1629  				_("bad option; moving a mount "
    1630  				  "residing under a shared mount is unsupported"));
    1631  		else if (mnt_fs_is_netfs(mnt_context_get_fs(cxt)))
    1632  			snprintf(buf, bufsz,
    1633  				_("bad option; for several filesystems (e.g. nfs, cifs) "
    1634  				  "you might need a /sbin/mount.<type> helper program"));
    1635  		else
    1636  			snprintf(buf, bufsz,
    1637  				_("wrong fs type, bad option, bad superblock on %s, "
    1638  				  "missing codepage or helper program, or other error"),
    1639  				src);
    1640  		break;
    1641  
    1642  	case EMFILE:
    1643  		if (buf)
    1644  			snprintf(buf, bufsz, _("mount table full"));
    1645  		break;
    1646  
    1647  	case EIO:
    1648  		if (buf)
    1649  			snprintf(buf, bufsz, _("can't read superblock on %s"), src);
    1650  		break;
    1651  
    1652  	case ENODEV:
    1653  		if (!buf)
    1654  			break;
    1655  		if (mnt_context_get_fstype(cxt))
    1656  			snprintf(buf, bufsz, _("unknown filesystem type '%s'"),
    1657  					mnt_context_get_fstype(cxt));
    1658  		else
    1659  			snprintf(buf, bufsz, _("unknown filesystem type"));
    1660  		break;
    1661  
    1662  	case ENOTBLK:
    1663  		if (uflags & MNT_MS_NOFAIL)
    1664  			return MNT_EX_SUCCESS;
    1665  		if (!buf)
    1666  			break;
    1667  		if (src && mnt_safe_stat(src, &st))
    1668  			snprintf(buf, bufsz, _("%s is not a block device, and stat(2) fails?"), src);
    1669  		else if (src && S_ISBLK(st.st_mode))
    1670  			snprintf(buf, bufsz,
    1671  				_("the kernel does not recognize %s as a block device; "
    1672  				  "maybe \"modprobe driver\" is necessary"), src);
    1673  		else if (src && S_ISREG(st.st_mode))
    1674  			snprintf(buf, bufsz, _("%s is not a block device; try \"-o loop\""), src);
    1675  		else
    1676  			snprintf(buf, bufsz, _("%s is not a block device"), src);
    1677  		break;
    1678  
    1679  	case ENXIO:
    1680  		if (uflags & MNT_MS_NOFAIL)
    1681  			return MNT_EX_SUCCESS;
    1682  		if (buf)
    1683  			snprintf(buf, bufsz, _("%s is not a valid block device"), src);
    1684  		break;
    1685  
    1686  	case EACCES:
    1687  	case EROFS:
    1688  		if (!buf)
    1689  			break;
    1690  		if (mflags & MS_RDONLY)
    1691  			snprintf(buf, bufsz, _("cannot mount %s read-only"), src);
    1692  		else if (mnt_context_is_rwonly_mount(cxt))
    1693  			snprintf(buf, bufsz, _("%s is write-protected but explicit read-write mode requested"), src);
    1694  		else if (mflags & MS_REMOUNT)
    1695  			snprintf(buf, bufsz, _("cannot remount %s read-write, is write-protected"), src);
    1696  		else if (mflags & MS_BIND)
    1697  			snprintf(buf, bufsz, _("bind %s failed"), src);
    1698  		else {
    1699  			errno = syserr;
    1700  			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
    1701  		}
    1702  		break;
    1703  
    1704  	case ENOMEDIUM:
    1705  		if (uflags & MNT_MS_NOFAIL)
    1706  			return MNT_EX_SUCCESS;
    1707  		if (buf)
    1708  			snprintf(buf, bufsz, _("no medium found on %s"), src);
    1709  		break;
    1710  
    1711  	case EBADMSG:
    1712  		/* Bad CRC for classic filesystems (e.g. extN or XFS) */
    1713  		if (buf && src && mnt_safe_stat(src, &st) == 0
    1714  		    && (S_ISBLK(st.st_mode) || S_ISREG(st.st_mode))) {
    1715  			snprintf(buf, bufsz, _("cannot mount; probably corrupted filesystem on %s"), src);
    1716  			break;
    1717  		}
    1718  		/* fallthrough */
    1719  
    1720  	default:
    1721  		if (buf) {
    1722  			errno = syserr;
    1723  			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
    1724  		}
    1725  		break;
    1726  	}
    1727  
    1728  	return MNT_EX_FAIL;
    1729  }
    1730  
    1731  #ifdef TEST_PROGRAM
    1732  
    1733  static int test_perms(struct libmnt_test *ts, int argc, char *argv[])
    1734  {
    1735  	struct libmnt_context *cxt;
    1736  	struct libmnt_optlist *ls;
    1737  	int rc;
    1738  
    1739  	cxt = mnt_new_context();
    1740  	if (!cxt)
    1741  		return -ENOMEM;
    1742  
    1743  	cxt->restricted = 1;		/* emulate suid mount(8) */
    1744  	mnt_context_get_fs(cxt);	/* due to assert() in evaluate_permissions() */
    1745  
    1746  	if (argc < 2) {
    1747  		 warn("missing fstab options");
    1748  		 return -EPERM;
    1749  	}
    1750  	if (argc == 3 && strcmp(argv[2], "--root") == 0)
    1751  		cxt->restricted = 0;
    1752  
    1753  	ls = mnt_context_get_optlist(cxt);
    1754  	if (!ls)
    1755  		return -ENOMEM;
    1756  	rc = mnt_optlist_set_optstr(ls, argv[1], NULL);
    1757  	if (rc) {
    1758  		warn("cannot apply fstab options");
    1759  		return rc;
    1760  	}
    1761  	cxt->flags |= MNT_FL_TAB_APPLIED;	/* emulate mnt_context_apply_fstab() */
    1762  
    1763  	mnt_context_merge_mflags(cxt);
    1764  
    1765  	rc = evaluate_permissions(cxt);
    1766  	if (rc) {
    1767  		warn("evaluate permission failed [rc=%d]", rc);
    1768  		return rc;
    1769  	}
    1770  	printf("user can mount\n");
    1771  
    1772  	mnt_free_context(cxt);
    1773  	return 0;
    1774  }
    1775  
    1776  static int test_fixopts(struct libmnt_test *ts, int argc, char *argv[])
    1777  {
    1778  	struct libmnt_context *cxt;
    1779  	struct libmnt_optlist *ls;
    1780  	unsigned long flags = 0;
    1781  	const char *p;
    1782  	int rc;
    1783  
    1784  	cxt = mnt_new_context();
    1785  	if (!cxt)
    1786  		return -ENOMEM;
    1787  
    1788  	cxt->restricted = 1;		/* emulate suid mount(8) */
    1789  	mnt_context_get_fs(cxt);	/* to fill fs->*_optstr */
    1790  
    1791  	if (argc < 2) {
    1792  		 warn("missing fstab options");
    1793  		 return -EPERM;
    1794  	}
    1795  	if (argc == 3 && strcmp(argv[2], "--root") == 0)
    1796  		cxt->restricted = 0;
    1797  
    1798  	ls = mnt_context_get_optlist(cxt);
    1799  	if (!ls)
    1800  		return -ENOMEM;
    1801  	rc = mnt_optlist_set_optstr(ls, argv[1], NULL);
    1802  	if (rc) {
    1803  		warn("cannot apply fstab options");
    1804  		return rc;
    1805  	}
    1806  	cxt->flags |= MNT_FL_TAB_APPLIED;	/* emulate mnt_context_apply_fstab() */
    1807  
    1808  	mnt_context_merge_mflags(cxt);
    1809  
    1810  	rc = evaluate_permissions(cxt);
    1811  	if (rc) {
    1812  		warn("evaluate permission failed [rc=%d]", rc);
    1813  		return rc;
    1814  	}
    1815  	rc = fix_optstr(cxt);
    1816  	if (rc) {
    1817  		warn("fix options failed [rc=%d]", rc);
    1818  		return rc;
    1819  	}
    1820  
    1821  	mnt_optlist_get_optstr(ls, &p, NULL, 0);
    1822  
    1823  	mnt_optlist_get_flags(ls, &flags, cxt->map_linux, 0);
    1824  	printf("options (dfl): '%s' [mount flags: %08lx]\n", p, flags);
    1825  
    1826  	mnt_optlist_get_optstr(ls, &p, NULL, MNT_OL_FLTR_ALL);
    1827  	printf("options (ex.): '%s'\n", p);
    1828  
    1829  	mnt_free_context(cxt);
    1830  	return 0;
    1831  }
    1832  
    1833  int main(int argc, char *argv[])
    1834  {
    1835  	struct libmnt_test tss[] = {
    1836  	{ "--perms",		test_perms,    "<fstab-options> [--root]" },
    1837  	{ "--fix-options",	test_fixopts,  "<fstab-options> [--root]" },
    1838  
    1839  	{ NULL }};
    1840  
    1841  	return mnt_run_test(tss, argc, argv);
    1842  }
    1843  
    1844  #endif /* TEST_PROGRAM */