(root)/
util-linux-2.39/
libmount/
src/
context.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
      15   * @title: Library high-level context
      16   * @short_description: high-level API to mount/umount devices.
      17   *
      18   * <informalexample>
      19   *   <programlisting>
      20   *	struct libmnt_context *cxt = mnt_new_context();
      21   *
      22   *	mnt_context_set_options(cxt, "aaa,bbb,ccc=CCC");
      23   *	mnt_context_set_mflags(cxt, MS_NOATIME|MS_NOEXEC);
      24   *	mnt_context_set_target(cxt, "/mnt/foo");
      25   *
      26   *	if (!mnt_context_mount(cxt))
      27   *		printf("successfully mounted\n");
      28   *	mnt_free_context(cxt);
      29   *
      30   *   </programlisting>
      31   * </informalexample>
      32   *
      33   * This code is similar to:
      34   *
      35   *   mount -o aaa,bbb,ccc=CCC,noatime,noexec /mnt/foo
      36   *
      37   */
      38  
      39  #include "mountP.h"
      40  #include "strutils.h"
      41  #include "namespace.h"
      42  #include "match.h"
      43  
      44  #include <sys/wait.h>
      45  
      46  /**
      47   * mnt_new_context:
      48   *
      49   * Returns: newly allocated mount context
      50   */
      51  struct libmnt_context *mnt_new_context(void)
      52  {
      53  	struct libmnt_context *cxt;
      54  	uid_t ruid, euid;
      55  
      56  	cxt = calloc(1, sizeof(*cxt));
      57  	if (!cxt)
      58  		return NULL;
      59  
      60  	ruid = getuid();
      61  	euid = geteuid();
      62  
      63  	mnt_context_reset_status(cxt);
      64  
      65  	cxt->ns_orig.fd = -1;
      66  	cxt->ns_tgt.fd = -1;
      67  	cxt->ns_cur = &cxt->ns_orig;
      68  
      69  	cxt->map_linux = mnt_get_builtin_optmap(MNT_LINUX_MAP);
      70  	cxt->map_userspace = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
      71  
      72  	INIT_LIST_HEAD(&cxt->hooksets_hooks);
      73  	INIT_LIST_HEAD(&cxt->hooksets_datas);
      74  
      75  	/* if we're really root and aren't running setuid */
      76  	cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
      77  
      78  	cxt->noautofs = 0;
      79  
      80  	DBG(CXT, ul_debugobj(cxt, "----> allocate %s",
      81  				cxt->restricted ? "[RESTRICTED]" : ""));
      82  
      83  	return cxt;
      84  }
      85  
      86  /**
      87   * mnt_free_context:
      88   * @cxt: mount context
      89   *
      90   * Deallocates context struct.
      91   */
      92  void mnt_free_context(struct libmnt_context *cxt)
      93  {
      94  	if (!cxt)
      95  		return;
      96  
      97  	mnt_reset_context(cxt);
      98  
      99  	free(cxt->fstype_pattern);
     100  	free(cxt->optstr_pattern);
     101  	free(cxt->tgt_prefix);
     102  
     103  	mnt_unref_table(cxt->fstab);
     104  	mnt_unref_cache(cxt->cache);
     105  	mnt_unref_fs(cxt->fs);
     106  
     107  	mnt_unref_optlist(cxt->optlist_saved);
     108  	mnt_unref_optlist(cxt->optlist);
     109  
     110  	mnt_free_lock(cxt->lock);
     111  	mnt_free_update(cxt->update);
     112  
     113  	mnt_context_set_target_ns(cxt, NULL);
     114  
     115  	free(cxt->children);
     116  
     117  	DBG(CXT, ul_debugobj(cxt, "free"));
     118  	free(cxt);
     119  }
     120  
     121  /**
     122   * mnt_reset_context:
     123   * @cxt: mount context
     124   *
     125   * Resets all information in the context that is directly related to
     126   * the latest mount (spec, source, target, mount options, ...).
     127   *
     128   * The match patterns, target namespace, prefix, cached fstab, cached canonicalized
     129   * paths and tags and [e]uid are not reset. You have to use
     130   *
     131   *	mnt_context_set_fstab(cxt, NULL);
     132   *	mnt_context_set_cache(cxt, NULL);
     133   *	mnt_context_set_fstype_pattern(cxt, NULL);
     134   *	mnt_context_set_options_pattern(cxt, NULL);
     135   *	mnt_context_set_target_ns(cxt, NULL);
     136   *
     137   * to reset this stuff.
     138   *
     139   * Returns: 0 on success, negative number in case of error.
     140   */
     141  int mnt_reset_context(struct libmnt_context *cxt)
     142  {
     143  	int fl;
     144  
     145  	if (!cxt)
     146  		return -EINVAL;
     147  
     148  	DBG(CXT, ul_debugobj(cxt, "<---- reset [status=%d] ---->",
     149  				mnt_context_get_status(cxt)));
     150  
     151  	fl = cxt->flags;
     152  
     153  	mnt_unref_fs(cxt->fs);
     154  	mnt_unref_table(cxt->mountinfo);
     155  	mnt_unref_table(cxt->utab);
     156  	mnt_unref_optlist(cxt->optlist);
     157  
     158  	free(cxt->helper);
     159  
     160  	cxt->fs = NULL;
     161  	cxt->mountinfo = NULL;
     162  	cxt->optlist = NULL;
     163  	cxt->utab = NULL;
     164  	cxt->helper = NULL;
     165  	cxt->mountdata = NULL;
     166  	cxt->flags = MNT_FL_DEFAULT;
     167  	cxt->noautofs = 0;
     168  	cxt->has_selinux_opt = 0;
     169  
     170  	cxt->map_linux = mnt_get_builtin_optmap(MNT_LINUX_MAP);
     171  	cxt->map_userspace = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
     172  
     173  	mnt_context_reset_status(cxt);
     174  	mnt_context_deinit_hooksets(cxt);
     175  
     176  	if (cxt->table_fltrcb)
     177  		mnt_context_set_tabfilter(cxt, NULL, NULL);
     178  
     179  	/* restore non-resettable flags */
     180  	cxt->flags |= (fl & MNT_FL_NOMTAB);
     181  	cxt->flags |= (fl & MNT_FL_FAKE);
     182  	cxt->flags |= (fl & MNT_FL_SLOPPY);
     183  	cxt->flags |= (fl & MNT_FL_VERBOSE);
     184  	cxt->flags |= (fl & MNT_FL_NOHELPERS);
     185  	cxt->flags |= (fl & MNT_FL_LOOPDEL);
     186  	cxt->flags |= (fl & MNT_FL_LAZY);
     187  	cxt->flags |= (fl & MNT_FL_FORK);
     188  	cxt->flags |= (fl & MNT_FL_FORCE);
     189  	cxt->flags |= (fl & MNT_FL_NOCANONICALIZE);
     190  	cxt->flags |= (fl & MNT_FL_RDONLY_UMOUNT);
     191  	cxt->flags |= (fl & MNT_FL_RWONLY_MOUNT);
     192  	cxt->flags |= (fl & MNT_FL_NOSWAPMATCH);
     193  	cxt->flags |= (fl & MNT_FL_TABPATHS_CHECKED);
     194  
     195  	mnt_context_apply_template(cxt);
     196  
     197  	return 0;
     198  }
     199  
     200  /*
     201   * Saves the current context setting (mount options, etc) to make it usable after
     202   * mnt_reset_context() or by mnt_context_apply_template(). This is usable for
     203   * example for mnt_context_next_mount() where for the next mount operation we
     204   * need to restore to the original context setting.
     205   *
     206   * Returns: 0 on success, negative number in case of error.
     207   */
     208  int mnt_context_save_template(struct libmnt_context *cxt)
     209  {
     210  	if (!cxt)
     211  		return -EINVAL;
     212  
     213  	DBG(CXT, ul_debugobj(cxt, "saving template"));
     214  
     215  	/* reset old saved data */
     216  	mnt_unref_optlist(cxt->optlist_saved);
     217  	cxt->optlist_saved = NULL;
     218  
     219  	if (cxt->optlist)
     220  		cxt->optlist_saved = mnt_copy_optlist(cxt->optlist);
     221  
     222  	return 0;
     223  }
     224  
     225  /*
     226   * Restores context FS setting from previously saved template (see
     227   * mnt_context_save_template()).
     228   *
     229   * Returns: 0 on success, negative number in case of error.
     230   */
     231  int mnt_context_apply_template(struct libmnt_context *cxt)
     232  {
     233  	if (!cxt)
     234  		return -EINVAL;
     235  
     236  	if (cxt->optlist) {
     237  		mnt_unref_optlist(cxt->optlist);
     238  		cxt->optlist = NULL;
     239  	}
     240  
     241  	if (cxt->optlist_saved) {
     242  		DBG(CXT, ul_debugobj(cxt, "restoring template"));
     243  		cxt->optlist = mnt_copy_optlist(cxt->optlist_saved);
     244  	}
     245  
     246  	return 0;
     247  }
     248  
     249  int mnt_context_has_template(struct libmnt_context *cxt)
     250  {
     251  	return cxt && cxt->optlist_saved ? 1 : 0;
     252  }
     253  
     254  struct libmnt_context *mnt_copy_context(struct libmnt_context *o)
     255  {
     256  	struct libmnt_context *n;
     257  
     258  	n = mnt_new_context();
     259  	if (!n)
     260  		return NULL;
     261  
     262  	DBG(CXT, ul_debugobj(n, "<---- clone ---->"));
     263  
     264  	n->flags = o->flags;
     265  
     266  	if (o->fs) {
     267  		n->fs = mnt_copy_fs(NULL, o->fs);
     268  		if (!n->fs)
     269  			goto failed;
     270  	}
     271  
     272  	n->mountinfo = o->mountinfo;
     273  	mnt_ref_table(n->mountinfo);
     274  
     275  	n->utab = o->utab;
     276  	mnt_ref_table(n->utab);
     277  
     278  	if (strdup_between_structs(n, o, tgt_prefix))
     279  		goto failed;
     280  	if (strdup_between_structs(n, o, helper))
     281  		goto failed;
     282  
     283  	n->map_linux = o->map_linux;
     284  	n->map_userspace = o->map_userspace;
     285  
     286  	mnt_context_reset_status(n);
     287  
     288  	n->table_fltrcb = o->table_fltrcb;
     289  	n->table_fltrcb_data = o->table_fltrcb_data;
     290  
     291  	n->noautofs = o->noautofs;
     292  	n->has_selinux_opt = o->has_selinux_opt;
     293  
     294  	return n;
     295  failed:
     296  	mnt_free_context(n);
     297  	return NULL;
     298  }
     299  
     300  /**
     301   * mnt_context_reset_status:
     302   * @cxt: context
     303   *
     304   * Resets mount(2) and mount.type statuses, so mnt_context_do_mount() or
     305   * mnt_context_do_umount() could be again called with the same settings.
     306   *
     307   * BE CAREFUL -- after this soft reset the libmount will NOT parse mount
     308   * options, evaluate permissions or apply stuff from fstab.
     309   *
     310   * Returns: 0 on success, negative number in case of error.
     311   */
     312  int mnt_context_reset_status(struct libmnt_context *cxt)
     313  {
     314  	if (!cxt)
     315  		return -EINVAL;
     316  
     317  	cxt->syscall_status = 1;		/* means not called yet */
     318  	cxt->helper_exec_status = 1;
     319  	cxt->helper_status = 0;
     320  	return 0;
     321  }
     322  
     323  static int context_init_paths(struct libmnt_context *cxt, int writable)
     324  {
     325  	struct libmnt_ns *ns_old;
     326  
     327  	assert(cxt);
     328  
     329  	if (!cxt->utab_path) {
     330  		cxt->utab_path = mnt_get_utab_path();
     331  		DBG(CXT, ul_debugobj(cxt, "utab path initialized to: %s", cxt->utab_path));
     332  	}
     333  
     334  	if (!writable)
     335  		return 0;		/* only paths wanted */
     336  	if (mnt_context_is_nomtab(cxt))
     337  		return 0;		/* write mode overridden by mount -n */
     338  	if (cxt->flags & MNT_FL_TABPATHS_CHECKED)
     339  		return 0;
     340  
     341  	DBG(CXT, ul_debugobj(cxt, "checking for writable tab files"));
     342  
     343  	ns_old = mnt_context_switch_target_ns(cxt);
     344  	if (!ns_old)
     345  		return -MNT_ERR_NAMESPACE;
     346  
     347  	mnt_has_regular_utab(&cxt->utab_path, &cxt->utab_writable);
     348  
     349  	if (!mnt_context_switch_ns(cxt, ns_old))
     350  		return -MNT_ERR_NAMESPACE;
     351  
     352  	cxt->flags |= MNT_FL_TABPATHS_CHECKED;
     353  	return 0;
     354  }
     355  
     356  int mnt_context_utab_writable(struct libmnt_context *cxt)
     357  {
     358  	assert(cxt);
     359  
     360  	context_init_paths(cxt, 1);
     361  	return cxt->utab_writable == 1;
     362  }
     363  
     364  const char *mnt_context_get_writable_tabpath(struct libmnt_context *cxt)
     365  {
     366  	assert(cxt);
     367  
     368  	context_init_paths(cxt, 1);
     369  	return cxt->utab_path;
     370  }
     371  
     372  
     373  static int set_flag(struct libmnt_context *cxt, int flag, int enable)
     374  {
     375  	if (!cxt)
     376  		return -EINVAL;
     377  	if (enable) {
     378  		DBG(CXT, ul_debugobj(cxt, "enabling flag %04x", flag));
     379  		cxt->flags |= flag;
     380  	} else {
     381  		DBG(CXT, ul_debugobj(cxt, "disabling flag %04x", flag));
     382  		cxt->flags &= ~flag;
     383  	}
     384  	return 0;
     385  }
     386  
     387  /**
     388   * mnt_context_is_restricted:
     389   * @cxt: mount context
     390   *
     391   * Returns: 0 for an unrestricted mount (user is root), or 1 for non-root mounts
     392   */
     393  int mnt_context_is_restricted(struct libmnt_context *cxt)
     394  {
     395  	return cxt->restricted;
     396  }
     397  
     398  /**
     399   * mnt_context_force_unrestricted:
     400   * @cxt: mount context
     401   *
     402   * This function is DANGEROURS as it disables all security policies in libmount.
     403   * Don't use if not sure. It removes "restricted" flag from the context, so
     404   * libmount will use the current context as for root user.
     405   *
     406   * This function is designed for case you have no any suid permissions, so you
     407   * can depend on kernel.
     408   *
     409   * Returns: 0 on success, negative number in case of error.
     410   *
     411   * Since: 2.35
     412   */
     413  int mnt_context_force_unrestricted(struct libmnt_context *cxt)
     414  {
     415  	if (mnt_context_is_restricted(cxt)) {
     416  		DBG(CXT, ul_debugobj(cxt, "force UNRESTRICTED"));
     417  		cxt->restricted = 0;
     418  	}
     419  
     420  	return 0;
     421  }
     422  
     423  /**
     424   * mnt_context_set_optsmode
     425   * @cxt: mount context
     426   * @mode: MNT_OMODE_* flags
     427   *
     428   * Controls how to use mount optionssource and target paths from fstab/mountinfo.
     429   *
     430   * @MNT_OMODE_IGNORE: ignore fstab options
     431   *
     432   * @MNT_OMODE_APPEND: append fstab options to existing options
     433   *
     434   * @MNT_OMODE_PREPEND: prepend fstab options to existing options
     435   *
     436   * @MNT_OMODE_REPLACE: replace existing options with options from fstab
     437   *
     438   * @MNT_OMODE_FORCE: always read fstab (although source and target are defined)
     439   *
     440   * @MNT_OMODE_FSTAB: read from fstab
     441   *
     442   * @MNT_OMODE_MTAB: read from mountinfo if fstab not enabled or failed
     443   *
     444   * @MNT_OMODE_NOTAB: do not read fstab/mountinfoat all
     445   *
     446   * @MNT_OMODE_AUTO: default mode (MNT_OMODE_PREPEND | MNT_OMODE_FSTAB | MNT_OMODE_MTAB)
     447   *
     448   * @MNT_OMODE_USER: default for non-root users (MNT_OMODE_REPLACE | MNT_OMODE_FORCE | MNT_OMODE_FSTAB)
     449   *
     450   * Notes:
     451   *
     452   * - MNT_OMODE_USER is always used if mount context is in restricted mode
     453   * - MNT_OMODE_AUTO is used if nothing else is defined
     454   * - the flags are evaluated in this order: MNT_OMODE_NOTAB, MNT_OMODE_FORCE,
     455   *   MNT_OMODE_FSTAB, MNT_OMODE_MTAB and then the mount options from fstab/mountinfo
     456   *   are set according to MNT_OMODE_{IGNORE,APPEND,PREPEND,REPLACE}
     457   *
     458   * Returns: 0 on success, negative number in case of error.
     459   */
     460  int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode)
     461  {
     462  	if (!cxt)
     463  		return -EINVAL;
     464  	cxt->optsmode = mode;
     465  	return 0;
     466  }
     467  
     468  /**
     469   * mnt_context_get_optsmode
     470   * @cxt: mount context
     471   *
     472   * Returns: MNT_OMODE_* mask or zero.
     473   */
     474  
     475  int mnt_context_get_optsmode(struct libmnt_context *cxt)
     476  {
     477  	return cxt->optsmode;
     478  }
     479  
     480  /**
     481   * mnt_context_disable_canonicalize:
     482   * @cxt: mount context
     483   * @disable: TRUE or FALSE
     484   *
     485   * Enable/disable paths canonicalization and tags evaluation. The libmount context
     486   * canonicalizes paths when searching in fstab and when preparing source and target paths
     487   * for mount(2) syscall.
     488   *
     489   * This function has an effect on the private (within context) fstab instance only
     490   * (see mnt_context_set_fstab()). If you want to use an external fstab then you
     491   * need to manage your private struct libmnt_cache (see mnt_table_set_cache(fstab,
     492   * NULL).
     493   *
     494   * Returns: 0 on success, negative number in case of error.
     495   */
     496  int mnt_context_disable_canonicalize(struct libmnt_context *cxt, int disable)
     497  {
     498  	return set_flag(cxt, MNT_FL_NOCANONICALIZE, disable);
     499  }
     500  
     501  /**
     502   * mnt_context_is_nocanonicalize:
     503   * @cxt: mount context
     504   *
     505   * Returns: 1 if no-canonicalize mode is enabled or 0.
     506   */
     507  int mnt_context_is_nocanonicalize(struct libmnt_context *cxt)
     508  {
     509  	return cxt->flags & MNT_FL_NOCANONICALIZE ? 1 : 0;
     510  }
     511  
     512  /**
     513   * mnt_context_enable_lazy:
     514   * @cxt: mount context
     515   * @enable: TRUE or FALSE
     516   *
     517   * Enable/disable lazy umount (see umount(8) man page, option -l).
     518   *
     519   * Returns: 0 on success, negative number in case of error.
     520   */
     521  int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable)
     522  {
     523  	return set_flag(cxt, MNT_FL_LAZY, enable);
     524  }
     525  
     526  /**
     527   * mnt_context_is_lazy:
     528   * @cxt: mount context
     529   *
     530   * Returns: 1 if lazy umount is enabled or 0
     531   */
     532  int mnt_context_is_lazy(struct libmnt_context *cxt)
     533  {
     534  	return cxt->flags & MNT_FL_LAZY ? 1 : 0;
     535  }
     536  
     537  /**
     538   * mnt_context_enable_onlyonce:
     539   * @cxt: mount context
     540   * @enable: TRUE or FALSE
     541   *
     542   * Enable/disable only-once mount (check if FS is not already mounted).
     543   *
     544   * Returns: 0 on success, negative number in case of error.
     545   */
     546  int mnt_context_enable_onlyonce(struct libmnt_context *cxt, int enable)
     547  {
     548  	return set_flag(cxt, MNT_FL_ONLYONCE, enable);
     549  }
     550  
     551  /**
     552   * mnt_context_is_lazy:
     553   * @cxt: mount context
     554   *
     555   * Returns: 1 if lazy umount is enabled or 0
     556   */
     557  int mnt_context_is_onlyonce(struct libmnt_context *cxt)
     558  {
     559  	return cxt->flags & MNT_FL_ONLYONCE ? 1 : 0;
     560  }
     561  
     562  /**
     563   * mnt_context_enable_fork:
     564   * @cxt: mount context
     565   * @enable: TRUE or FALSE
     566   *
     567   * Enable/disable fork(2) call in mnt_context_next_mount() (see mount(8) man
     568   * page, option -F).
     569   *
     570   * Returns: 0 on success, negative number in case of error.
     571   */
     572  int mnt_context_enable_fork(struct libmnt_context *cxt, int enable)
     573  {
     574  	return set_flag(cxt, MNT_FL_FORK, enable);
     575  }
     576  
     577  /**
     578   * mnt_context_is_fork:
     579   * @cxt: mount context
     580   *
     581   * Returns: 1 if fork (mount -F) is enabled or 0
     582   */
     583  int mnt_context_is_fork(struct libmnt_context *cxt)
     584  {
     585  	return cxt->flags & MNT_FL_FORK ? 1 : 0;
     586  }
     587  
     588  /**
     589   * mnt_context_is_parent:
     590   * @cxt: mount context
     591   *
     592   * Return: 1 if mount -F enabled and the current context is parent, or 0
     593   */
     594  int mnt_context_is_parent(struct libmnt_context *cxt)
     595  {
     596  	return mnt_context_is_fork(cxt) && cxt->pid == 0;
     597  }
     598  
     599  /**
     600   * mnt_context_is_child:
     601   * @cxt: mount context
     602   *
     603   * Return: 1 f the current context is child, or 0
     604   */
     605  int mnt_context_is_child(struct libmnt_context *cxt)
     606  {
     607  	/* See mnt_fork_context(), the for fork flag is always disabled
     608  	 * for children to avoid recursive forking.
     609  	 */
     610  	return !mnt_context_is_fork(cxt) && cxt->pid;
     611  }
     612  
     613  /**
     614   * mnt_context_enable_rdonly_umount:
     615   * @cxt: mount context
     616   * @enable: TRUE or FALSE
     617   *
     618   * Enable/disable read-only remount on failed umount(2)
     619   * (see umount(8) man page, option -r).
     620   *
     621   * Returns: 0 on success, negative number in case of error.
     622   */
     623  int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable)
     624  {
     625  	return set_flag(cxt, MNT_FL_RDONLY_UMOUNT, enable);
     626  }
     627  
     628  /**
     629   * mnt_context_is_rdonly_umount
     630   * @cxt: mount context
     631   *
     632   * See also mnt_context_enable_rdonly_umount() and umount(8) man page,
     633   * option -r.
     634   *
     635   * Returns: 1 if read-only remount failed umount(2) is enables or 0
     636   */
     637  int mnt_context_is_rdonly_umount(struct libmnt_context *cxt)
     638  {
     639  	return cxt->flags & MNT_FL_RDONLY_UMOUNT ? 1 : 0;
     640  }
     641  
     642  /**
     643   * mnt_context_enable_rwonly_mount:
     644   * @cxt: mount context
     645   * @enable: TRUE or FALSE
     646   *
     647   * Force read-write mount; if enabled libmount will never try MS_RDONLY
     648   * after failed mount(2) EROFS. (See mount(8) man page, option -w).
     649   *
     650   * Since: 2.30
     651   *
     652   * Returns: 0 on success, negative number in case of error.
     653   */
     654  int mnt_context_enable_rwonly_mount(struct libmnt_context *cxt, int enable)
     655  {
     656  	return set_flag(cxt, MNT_FL_RWONLY_MOUNT, enable);
     657  }
     658  
     659  /**
     660   * mnt_context_is_rwonly_mount
     661   * @cxt: mount context
     662   *
     663   * See also mnt_context_enable_rwonly_mount() and mount(8) man page,
     664   * option -w.
     665   *
     666   * Since: 2.30
     667   *
     668   * Returns: 1 if only read-write mount is allowed.
     669   */
     670  int mnt_context_is_rwonly_mount(struct libmnt_context *cxt)
     671  {
     672  	return cxt->flags & MNT_FL_RWONLY_MOUNT ? 1 : 0;
     673  }
     674  
     675  /**
     676   * mnt_context_forced_rdonly:
     677   * @cxt: mount context
     678   *
     679   * See also mnt_context_enable_rwonly_mount().
     680   *
     681   * Since: 2.30
     682   *
     683   * Returns: 1 if mounted read-only on write-protected device.
     684   */
     685  int mnt_context_forced_rdonly(struct libmnt_context *cxt)
     686  {
     687  	return cxt->flags & MNT_FL_FORCED_RDONLY ? 1 : 0;
     688  }
     689  
     690  /**
     691   * mnt_context_disable_helpers:
     692   * @cxt: mount context
     693   * @disable: TRUE or FALSE
     694   *
     695   * Enable/disable /sbin/[u]mount.* helpers (see mount(8) man page, option -i).
     696   *
     697   * Returns: 0 on success, negative number in case of error.
     698   */
     699  int mnt_context_disable_helpers(struct libmnt_context *cxt, int disable)
     700  {
     701  	return set_flag(cxt, MNT_FL_NOHELPERS, disable);
     702  }
     703  
     704  /**
     705   * mnt_context_is_nohelpers
     706   * @cxt: mount context
     707   *
     708   * Returns: 1 if helpers are disabled (mount -i) or 0
     709   */
     710  int mnt_context_is_nohelpers(struct libmnt_context *cxt)
     711  {
     712  	return cxt->flags & MNT_FL_NOHELPERS ? 1 : 0;
     713  }
     714  
     715  
     716  /**
     717   * mnt_context_enable_sloppy:
     718   * @cxt: mount context
     719   * @enable: TRUE or FALSE
     720   *
     721   * Set/unset sloppy mounting (see mount(8) man page, option -s).
     722   *
     723   * Returns: 0 on success, negative number in case of error.
     724   */
     725  int mnt_context_enable_sloppy(struct libmnt_context *cxt, int enable)
     726  {
     727  	return set_flag(cxt, MNT_FL_SLOPPY, enable);
     728  }
     729  
     730  /**
     731   * mnt_context_is_sloppy:
     732   * @cxt: mount context
     733   *
     734   * Returns: 1 if sloppy flag is enabled or 0
     735   */
     736  int mnt_context_is_sloppy(struct libmnt_context *cxt)
     737  {
     738  	return cxt->flags & MNT_FL_SLOPPY ? 1 : 0;
     739  }
     740  
     741  /**
     742   * mnt_context_enable_fake:
     743   * @cxt: mount context
     744   * @enable: TRUE or FALSE
     745   *
     746   * Enable/disable fake mounting (see mount(8) man page, option -f).
     747   *
     748   * Returns: 0 on success, negative number in case of error.
     749   */
     750  int mnt_context_enable_fake(struct libmnt_context *cxt, int enable)
     751  {
     752  	return set_flag(cxt, MNT_FL_FAKE, enable);
     753  }
     754  
     755  /**
     756   * mnt_context_is_fake:
     757   * @cxt: mount context
     758   *
     759   * Returns: 1 if fake flag is enabled or 0
     760   */
     761  int mnt_context_is_fake(struct libmnt_context *cxt)
     762  {
     763  	return cxt->flags & MNT_FL_FAKE ? 1 : 0;
     764  }
     765  
     766  /**
     767   * mnt_context_disable_mtab:
     768   * @cxt: mount context
     769   * @disable: TRUE or FALSE
     770   *
     771   * Disable/enable userspace mount table update (see mount(8) man page,
     772   * option -n). Originally /etc/mtab, now /run/mount/utab.
     773   *
     774   * Returns: 0 on success, negative number in case of error.
     775   */
     776  int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable)
     777  {
     778  	return set_flag(cxt, MNT_FL_NOMTAB, disable);
     779  }
     780  
     781  /**
     782   * mnt_context_is_nomtab:
     783   * @cxt: mount context
     784   *
     785   * Returns: 1 if no-mtab is enabled or 0
     786   */
     787  int mnt_context_is_nomtab(struct libmnt_context *cxt)
     788  {
     789  	return cxt->flags & MNT_FL_NOMTAB ? 1 : 0;
     790  }
     791  
     792  /**
     793   * mnt_context_disable_swapmatch:
     794   * @cxt: mount context
     795   * @disable: TRUE or FALSE
     796   *
     797   * Disable/enable swap between source and target for mount(8) if only one path
     798   * is specified.
     799   *
     800   * Returns: 0 on success, negative number in case of error.
     801   */
     802  int mnt_context_disable_swapmatch(struct libmnt_context *cxt, int disable)
     803  {
     804  	return set_flag(cxt, MNT_FL_NOSWAPMATCH, disable);
     805  }
     806  
     807  /**
     808   * mnt_context_is_swapmatch:
     809   * @cxt: mount context
     810   *
     811   * Returns: 1 if swap between source and target is allowed (default is 1) or 0.
     812   */
     813  int mnt_context_is_swapmatch(struct libmnt_context *cxt)
     814  {
     815  	return cxt->flags & MNT_FL_NOSWAPMATCH ? 0 : 1;
     816  }
     817  
     818  /**
     819   * mnt_context_enable_force:
     820   * @cxt: mount context
     821   * @enable: TRUE or FALSE
     822   *
     823   * Enable/disable force umounting (see umount(8) man page, option -f).
     824   *
     825   * Returns: 0 on success, negative number in case of error.
     826   */
     827  int mnt_context_enable_force(struct libmnt_context *cxt, int enable)
     828  {
     829  	return set_flag(cxt, MNT_FL_FORCE, enable);
     830  }
     831  
     832  /**
     833   * mnt_context_is_force
     834   * @cxt: mount context
     835   *
     836   * Returns: 1 if force umounting flag is enabled or 0
     837   */
     838  int mnt_context_is_force(struct libmnt_context *cxt)
     839  {
     840  	return cxt->flags & MNT_FL_FORCE ? 1 : 0;
     841  }
     842  
     843  /**
     844   * mnt_context_enable_verbose:
     845   * @cxt: mount context
     846   * @enable: TRUE or FALSE
     847   *
     848   * Enable/disable verbose output (TODO: not implemented yet)
     849   *
     850   * Returns: 0 on success, negative number in case of error.
     851   */
     852  int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable)
     853  {
     854  	return set_flag(cxt, MNT_FL_VERBOSE, enable);
     855  }
     856  
     857  /**
     858   * mnt_context_is_verbose
     859   * @cxt: mount context
     860   *
     861   * Returns: 1 if verbose flag is enabled or 0
     862   */
     863  int mnt_context_is_verbose(struct libmnt_context *cxt)
     864  {
     865  	return cxt->flags & MNT_FL_VERBOSE ? 1 : 0;
     866  }
     867  
     868  /**
     869   * mnt_context_enable_loopdel:
     870   * @cxt: mount context
     871   * @enable: TRUE or FALSE
     872   *
     873   * Enable/disable the loop delete (destroy) after umount (see umount(8), option -d)
     874   *
     875   * Returns: 0 on success, negative number in case of error.
     876   */
     877  int mnt_context_enable_loopdel(struct libmnt_context *cxt, int enable)
     878  {
     879  	return set_flag(cxt, MNT_FL_LOOPDEL, enable);
     880  }
     881  
     882  /**
     883   * mnt_context_is_loopdel:
     884   * @cxt: mount context
     885   *
     886   * Returns: 1 if loop device should be deleted after umount (umount -d) or 0.
     887   */
     888  int mnt_context_is_loopdel(struct libmnt_context *cxt)
     889  {
     890  	return cxt->flags & MNT_FL_LOOPDEL ? 1 : 0;
     891  }
     892  
     893  /**
     894   * mnt_context_set_fs:
     895   * @cxt: mount context
     896   * @fs: filesystem description
     897   *
     898   * The mount context uses private @fs by default. This function can be used to
     899   * overwrite the private @fs with an external instance. This function
     900   * increments @fs reference counter (and decrement reference counter of the
     901   * old fs).
     902   *
     903   * The @fs will be modified by mnt_context_set_{source,target,options,fstype}
     904   * functions, If the @fs is NULL, then all current FS specific settings (source,
     905   * target, etc., exclude spec) are reset.
     906   *
     907   * Returns: 0 on success, negative number in case of error.
     908   */
     909  int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs)
     910  {
     911  	if (!cxt)
     912  		return -EINVAL;
     913  
     914  	DBG(CXT, ul_debugobj(cxt, "setting new FS"));
     915  	mnt_ref_fs(fs);			/* new */
     916  	mnt_unref_fs(cxt->fs);		/* old */
     917  	cxt->fs = fs;
     918  	return 0;
     919  }
     920  
     921  /**
     922   * mnt_context_get_fs:
     923   * @cxt: mount context
     924   *
     925   * The FS contains the basic description of mountpoint, fs type and so on.
     926   * Note that the FS is modified by mnt_context_set_{source,target,options,fstype}
     927   * functions.
     928   *
     929   * Returns: pointer to FS description or NULL in case of a calloc() error.
     930   */
     931  struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt)
     932  {
     933  	if (!cxt)
     934  		return NULL;
     935  	if (!cxt->fs)
     936  		cxt->fs = mnt_new_fs();
     937  	return cxt->fs;
     938  }
     939  
     940  /**
     941   * mnt_context_get_fs_userdata:
     942   * @cxt: mount context
     943   *
     944   * Returns: pointer to userdata or NULL.
     945   */
     946  void *mnt_context_get_fs_userdata(struct libmnt_context *cxt)
     947  {
     948  	return cxt->fs ? mnt_fs_get_userdata(cxt->fs) : NULL;
     949  }
     950  
     951  /**
     952   * mnt_context_get_fstab_userdata:
     953   * @cxt: mount context
     954   *
     955   * Returns: pointer to userdata or NULL.
     956   */
     957  void *mnt_context_get_fstab_userdata(struct libmnt_context *cxt)
     958  {
     959  	return cxt->fstab ? mnt_table_get_userdata(cxt->fstab) : NULL;
     960  }
     961  
     962  /**
     963   * mnt_context_get_mtab_userdata:
     964   * @cxt: mount context
     965   *
     966   * The file /etc/mtab is no more used, @context points always to mountinfo
     967   * (/proc/self/mountinfo). The function uses "mtab" in the name for backward
     968   * compatibility only.
     969   *
     970   * Returns: pointer to userdata or NULL.
     971   */
     972  void *mnt_context_get_mtab_userdata(struct libmnt_context *cxt)
     973  {
     974  	return cxt->mountinfo ? mnt_table_get_userdata(cxt->mountinfo) : NULL;
     975  }
     976  
     977  /**
     978   * mnt_context_set_source:
     979   * @cxt: mount context
     980   * @source: mount source (device, directory, UUID, LABEL, ...)
     981   *
     982   * Note that libmount does not interpret "nofail" (MNT_MS_NOFAIL)
     983   * mount option. The real return code is always returned, when
     984   * the device does not exist then it's usually MNT_ERR_NOSOURCE
     985   * from libmount or ENOENT, ENOTDIR, ENOTBLK, ENXIO from mount(2).
     986   *
     987   * Returns: 0 on success, negative number in case of error.
     988   */
     989  int mnt_context_set_source(struct libmnt_context *cxt, const char *source)
     990  {
     991  	return mnt_fs_set_source(mnt_context_get_fs(cxt), source);
     992  }
     993  
     994  /**
     995   * mnt_context_get_source:
     996   * @cxt: mount context
     997   *
     998   * Returns: returns pointer or NULL in case of error or if not set.
     999   */
    1000  const char *mnt_context_get_source(struct libmnt_context *cxt)
    1001  {
    1002  	return mnt_fs_get_source(mnt_context_get_fs(cxt));
    1003  }
    1004  
    1005  /**
    1006   * mnt_context_set_target:
    1007   * @cxt: mount context
    1008   * @target: mountpoint
    1009   *
    1010   * Returns: 0 on success, negative number in case of error.
    1011   */
    1012  int mnt_context_set_target(struct libmnt_context *cxt, const char *target)
    1013  {
    1014  	return mnt_fs_set_target(mnt_context_get_fs(cxt), target);
    1015  }
    1016  
    1017  /**
    1018   * mnt_context_get_target:
    1019   * @cxt: mount context
    1020   *
    1021   * Returns: returns pointer or NULL in case of error or if not set.
    1022   */
    1023  const char *mnt_context_get_target(struct libmnt_context *cxt)
    1024  {
    1025  	return mnt_fs_get_target(mnt_context_get_fs(cxt));
    1026  }
    1027  
    1028  /**
    1029   * mnt_context_set_target_prefix:
    1030   * @cxt: mount context
    1031   * @path: mountpoint prefix
    1032   *
    1033   * Returns: 0 on success, negative number in case of error.
    1034   */
    1035  int mnt_context_set_target_prefix(struct libmnt_context *cxt, const char *path)
    1036  {
    1037  	char *p = NULL;
    1038  
    1039  	if (!cxt)
    1040  		return -EINVAL;
    1041  	if (path) {
    1042  		p = strdup(path);
    1043  		if (!p)
    1044  			return -ENOMEM;
    1045  	}
    1046  	free(cxt->tgt_prefix);
    1047  	cxt->tgt_prefix = p;
    1048  
    1049  	return 0;
    1050  }
    1051  
    1052  /**
    1053   * mnt_context_get_target_prefix:
    1054   * @cxt: mount context
    1055   *
    1056   * Returns: returns pointer or NULL in case of error or if not set.
    1057   */
    1058  const char *mnt_context_get_target_prefix(struct libmnt_context *cxt)
    1059  {
    1060  	return cxt ? cxt->tgt_prefix : NULL;
    1061  }
    1062  
    1063  
    1064  /**
    1065   * mnt_context_set_fstype:
    1066   * @cxt: mount context
    1067   * @fstype: filesystem type
    1068   *
    1069   * Note that the @fstype has to be a FS type. For patterns with
    1070   * comma-separated list of filesystems or for the "nofs" notation, use
    1071   * mnt_context_set_fstype_pattern().
    1072   *
    1073   * Returns: 0 on success, negative number in case of error.
    1074   */
    1075  int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype)
    1076  {
    1077  	return mnt_fs_set_fstype(mnt_context_get_fs(cxt), fstype);
    1078  }
    1079  
    1080  /**
    1081   * mnt_context_get_fstype:
    1082   * @cxt: mount context
    1083   *
    1084   * Returns: pointer or NULL in case of error or if not set.
    1085   */
    1086  const char *mnt_context_get_fstype(struct libmnt_context *cxt)
    1087  {
    1088  	return mnt_fs_get_fstype(mnt_context_get_fs(cxt));
    1089  }
    1090  
    1091  struct libmnt_optlist *mnt_context_get_optlist(struct libmnt_context *cxt)
    1092  {
    1093  	if (!cxt)
    1094  		return NULL;
    1095  	if (!cxt->optlist) {
    1096  		cxt->optlist = mnt_new_optlist();
    1097  		if (!cxt->optlist)
    1098  			return NULL;
    1099  		if (mnt_optlist_register_map(cxt->optlist, cxt->map_linux))
    1100  			goto fail;
    1101  		if (mnt_optlist_register_map(cxt->optlist, cxt->map_userspace))
    1102  			goto fail;
    1103  	}
    1104  
    1105  	return cxt->optlist;
    1106  fail:
    1107  	mnt_unref_optlist(cxt->optlist);
    1108  	return NULL;
    1109  }
    1110  
    1111  /**
    1112   * mnt_context_set_options:
    1113   * @cxt: mount context
    1114   * @optstr: comma delimited mount options
    1115   *
    1116   * Note that MS_MOVE cannot be specified as "string". It's operation that
    1117   * is no supported in fstab (etc.)
    1118   *
    1119   * Returns: 0 on success, negative number in case of error.
    1120   */
    1121  int mnt_context_set_options(struct libmnt_context *cxt, const char *optstr)
    1122  {
    1123  	struct libmnt_optlist *ls = mnt_context_get_optlist(cxt);
    1124  
    1125  	if (!ls)
    1126  		return -ENOMEM;
    1127  	return mnt_optlist_set_optstr(ls, optstr, NULL);
    1128  }
    1129  
    1130  /**
    1131   * mnt_context_append_options:
    1132   * @cxt: mount context
    1133   * @optstr: comma delimited mount options
    1134   *
    1135   * Returns: 0 on success, negative number in case of error.
    1136   */
    1137  int mnt_context_append_options(struct libmnt_context *cxt, const char *optstr)
    1138  {
    1139  	struct libmnt_optlist *ls = mnt_context_get_optlist(cxt);
    1140  
    1141  	if (!ls)
    1142  		return -ENOMEM;
    1143  	return mnt_optlist_append_optstr(ls, optstr, NULL);
    1144  }
    1145  
    1146  /**
    1147   * mnt_context_get_options:
    1148   * @cxt: mount context
    1149   *
    1150   * This function returns mount options set by mnt_context_set_options(),
    1151   * mnt_context_append_options() or mnt_context_set_mflags();
    1152   *
    1153   * Before v2.39 this function ignored options specified by flags (see
    1154   * mnt_context_set_mflags()) before mnt_context_prepare_mount() call. Now this
    1155   * function always returns all mount options.
    1156   *
    1157   * Returns: pointer or NULL
    1158   */
    1159  const char *mnt_context_get_options(struct libmnt_context *cxt)
    1160  {
    1161  	const char *str = NULL;
    1162  
    1163  	if (cxt->optlist && !mnt_optlist_is_empty(cxt->optlist))
    1164  		mnt_optlist_get_optstr(cxt->optlist, &str, NULL, 0);
    1165  	return str;
    1166  }
    1167  
    1168  /**
    1169   * mnt_context_set_fstype_pattern:
    1170   * @cxt: mount context
    1171   * @pattern: FS name pattern (or NULL to reset the current setting)
    1172   *
    1173   * See mount(8), option -t.
    1174   *
    1175   * Returns: 0 on success, negative number in case of error.
    1176   */
    1177  int mnt_context_set_fstype_pattern(struct libmnt_context *cxt, const char *pattern)
    1178  {
    1179  	char *p = NULL;
    1180  
    1181  	if (!cxt)
    1182  		return -EINVAL;
    1183  	if (pattern) {
    1184  		p = strdup(pattern);
    1185  		if (!p)
    1186  			return -ENOMEM;
    1187  	}
    1188  	free(cxt->fstype_pattern);
    1189  	cxt->fstype_pattern = p;
    1190  	return 0;
    1191  }
    1192  
    1193  /**
    1194   * mnt_context_set_options_pattern:
    1195   * @cxt: mount context
    1196   * @pattern: options pattern (or NULL to reset the current setting)
    1197   *
    1198   * See mount(8), option -O.
    1199   *
    1200   * Returns: 0 on success, negative number in case of error.
    1201   */
    1202  int mnt_context_set_options_pattern(struct libmnt_context *cxt, const char *pattern)
    1203  {
    1204  	char *p = NULL;
    1205  
    1206  	if (!cxt)
    1207  		return -EINVAL;
    1208  	if (pattern) {
    1209  		p = strdup(pattern);
    1210  		if (!p)
    1211  			return -ENOMEM;
    1212  	}
    1213  	free(cxt->optstr_pattern);
    1214  	cxt->optstr_pattern = p;
    1215  	return 0;
    1216  }
    1217  
    1218  /**
    1219   * mnt_context_set_fstab:
    1220   * @cxt: mount context
    1221   * @tb: fstab
    1222   *
    1223   * The mount context reads /etc/fstab to the private struct libmnt_table by default.
    1224   * This function can be used to overwrite the private fstab with an external
    1225   * instance.
    1226   *
    1227   * This function modify the @tb reference counter. This function does not set
    1228   * the cache for the @tb. You have to explicitly call mnt_table_set_cache(tb,
    1229   * mnt_context_get_cache(cxt));
    1230   *
    1231   * The fstab is used read-only and is not modified, it should be possible to
    1232   * share the fstab between more mount contexts (TODO: test it.)
    1233   *
    1234   * If the @tb argument is NULL, then the current private fstab instance is
    1235   * reset.
    1236   *
    1237   * Returns: 0 on success, negative number in case of error.
    1238   */
    1239  int mnt_context_set_fstab(struct libmnt_context *cxt, struct libmnt_table *tb)
    1240  {
    1241  	if (!cxt)
    1242  		return -EINVAL;
    1243  
    1244  	mnt_ref_table(tb);		/* new */
    1245  	mnt_unref_table(cxt->fstab);	/* old */
    1246  
    1247  	cxt->fstab = tb;
    1248  	return 0;
    1249  }
    1250  
    1251  /**
    1252   * mnt_context_get_fstab:
    1253   * @cxt: mount context
    1254   * @tb: returns fstab
    1255   *
    1256   * See also mnt_table_parse_fstab() for more details about fstab.
    1257   *
    1258   * Returns: 0 on success, negative number in case of error.
    1259   */
    1260  int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb)
    1261  {
    1262  	struct libmnt_ns *ns_old;
    1263  
    1264  	if (!cxt)
    1265  		return -EINVAL;
    1266  	if (!cxt->fstab) {
    1267  		int rc;
    1268  
    1269  		cxt->fstab = mnt_new_table();
    1270  		if (!cxt->fstab)
    1271  			return -ENOMEM;
    1272  		if (cxt->table_errcb)
    1273  			mnt_table_set_parser_errcb(cxt->fstab, cxt->table_errcb);
    1274  
    1275  		ns_old = mnt_context_switch_target_ns(cxt);
    1276  		if (!ns_old)
    1277  			return -MNT_ERR_NAMESPACE;
    1278  
    1279  		mnt_table_set_cache(cxt->fstab, mnt_context_get_cache(cxt));
    1280  		rc = mnt_table_parse_fstab(cxt->fstab, NULL);
    1281  
    1282  		if (!mnt_context_switch_ns(cxt, ns_old))
    1283  			return -MNT_ERR_NAMESPACE;
    1284  
    1285  		if (rc)
    1286  			return rc;
    1287  	}
    1288  
    1289  	if (tb)
    1290  		*tb = cxt->fstab;
    1291  	return 0;
    1292  }
    1293  
    1294  int mnt_context_get_mountinfo(struct libmnt_context *cxt, struct libmnt_table **tb)
    1295  {
    1296  	int rc = 0;
    1297  	struct libmnt_ns *ns_old = NULL;
    1298  
    1299  	if (!cxt)
    1300  		return -EINVAL;
    1301  	if (!cxt->mountinfo) {
    1302  		ns_old = mnt_context_switch_target_ns(cxt);
    1303  		if (!ns_old)
    1304  			return -MNT_ERR_NAMESPACE;
    1305  
    1306  		context_init_paths(cxt, 0);
    1307  
    1308  		cxt->mountinfo = mnt_new_table();
    1309  		if (!cxt->mountinfo) {
    1310  			rc = -ENOMEM;
    1311  			goto end;
    1312  		}
    1313  
    1314  		mnt_table_enable_noautofs(cxt->mountinfo, cxt->noautofs);
    1315  
    1316  		if (cxt->table_errcb)
    1317  			mnt_table_set_parser_errcb(cxt->mountinfo, cxt->table_errcb);
    1318  		if (cxt->table_fltrcb)
    1319  			mnt_table_set_parser_fltrcb(cxt->mountinfo,
    1320  					cxt->table_fltrcb,
    1321  					cxt->table_fltrcb_data);
    1322  
    1323  		mnt_table_set_cache(cxt->mountinfo, mnt_context_get_cache(cxt));
    1324  	}
    1325  
    1326  	/* Read the table; it's empty, because this first mnt_context_get_mountinfo()
    1327  	 * call, or because /proc was not accessible in previous calls */
    1328  	if (mnt_table_is_empty(cxt->mountinfo)) {
    1329  		if (!ns_old) {
    1330  			ns_old = mnt_context_switch_target_ns(cxt);
    1331  			if (!ns_old)
    1332  				return -MNT_ERR_NAMESPACE;
    1333  		}
    1334  
    1335  		rc = __mnt_table_parse_mountinfo(cxt->mountinfo, NULL, cxt->utab);
    1336  		if (rc)
    1337  			goto end;
    1338  	}
    1339  
    1340  	if (tb)
    1341  		*tb = cxt->mountinfo;
    1342  
    1343  	DBG(CXT, ul_debugobj(cxt, "mountinfo requested [nents=%d]",
    1344  				mnt_table_get_nents(cxt->mountinfo)));
    1345  
    1346  end:
    1347  	if (ns_old && !mnt_context_switch_ns(cxt, ns_old))
    1348  		return -MNT_ERR_NAMESPACE;
    1349  
    1350  	return rc;
    1351  }
    1352  
    1353  /**
    1354   * mnt_context_get_mtab:
    1355   * @cxt: mount context
    1356   * @tb: returns mtab
    1357   *
    1358   * Parse /proc/self/mountinfo mount table.
    1359   *
    1360   * The file /etc/mtab is no more used, @context points always to mountinfo
    1361   * (/proc/self/mountinfo). The function uses "mtab" in the name for backward
    1362   * compatibility only.
    1363   *
    1364   * See also mnt_table_parse_mtab() for more details about mountinfo. The
    1365   * result will be deallocated by mnt_free_context(@cxt).
    1366   *
    1367   * Returns: 0 on success, negative number in case of error.
    1368   */
    1369  int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb)
    1370  {
    1371  	return mnt_context_get_mountinfo(cxt, tb);
    1372  }
    1373  
    1374  /*
    1375   * Called by mountinfo parser to filter out entries, non-zero means that
    1376   * an entry has to be filtered out.
    1377   */
    1378  static int mountinfo_filter(struct libmnt_fs *fs, void *data)
    1379  {
    1380  	if (!fs || !data)
    1381  		return 0;
    1382  	if (mnt_fs_streq_target(fs, data))
    1383  		return 0;
    1384  	if (mnt_fs_streq_srcpath(fs, data))
    1385  		return 0;
    1386  	return 1;
    1387  }
    1388  
    1389  /*
    1390   * The same like mnt_context_get_mountinfo(), but does not read all mountinfo
    1391   * file, but only entries relevant for @tgt.
    1392   */
    1393  int mnt_context_get_mountinfo_for_target(struct libmnt_context *cxt,
    1394  				    struct libmnt_table **mountinfo,
    1395  				    const char *tgt)
    1396  {
    1397  	struct stat st;
    1398  	struct libmnt_cache *cache = NULL;
    1399  	char *cn_tgt = NULL;
    1400  	int rc;
    1401  	struct libmnt_ns *ns_old;
    1402  
    1403  	ns_old = mnt_context_switch_target_ns(cxt);
    1404  	if (!ns_old)
    1405  		return -MNT_ERR_NAMESPACE;
    1406  
    1407  	if (mnt_context_is_nocanonicalize(cxt))
    1408  		mnt_context_set_tabfilter(cxt, mountinfo_filter, (void *) tgt);
    1409  
    1410  	else if (mnt_safe_stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)) {
    1411  		cache = mnt_context_get_cache(cxt);
    1412  		cn_tgt = mnt_resolve_path(tgt, cache);
    1413  		if (cn_tgt)
    1414  			mnt_context_set_tabfilter(cxt, mountinfo_filter, cn_tgt);
    1415  	}
    1416  
    1417  	rc = mnt_context_get_mountinfo(cxt, mountinfo);
    1418  	mnt_context_set_tabfilter(cxt, NULL, NULL);
    1419  
    1420  	if (!mnt_context_switch_ns(cxt, ns_old))
    1421  		return -MNT_ERR_NAMESPACE;
    1422  
    1423  	if (cn_tgt && !cache)
    1424  		free(cn_tgt);
    1425  
    1426  	return rc;
    1427  }
    1428  
    1429  /*
    1430   * Allows to specify a filter for tab file entries. The filter is called by
    1431   * the table parser. Currently used for utab only.
    1432   */
    1433  int mnt_context_set_tabfilter(struct libmnt_context *cxt,
    1434  			      int (*fltr)(struct libmnt_fs *, void *),
    1435  			      void *data)
    1436  {
    1437  	if (!cxt)
    1438  		return -EINVAL;
    1439  
    1440  	cxt->table_fltrcb = fltr;
    1441  	cxt->table_fltrcb_data = data;
    1442  
    1443  	if (cxt->mountinfo)
    1444  		mnt_table_set_parser_fltrcb(cxt->mountinfo,
    1445  				cxt->table_fltrcb,
    1446  				cxt->table_fltrcb_data);
    1447  
    1448  	DBG(CXT, ul_debugobj(cxt, "tabfilter %s", fltr ? "ENABLED!" : "disabled"));
    1449  	return 0;
    1450  }
    1451  
    1452  /**
    1453   * mnt_context_get_table:
    1454   * @cxt: mount context
    1455   * @filename: e.g. /proc/self/mountinfo
    1456   * @tb: returns the table
    1457   *
    1458   * This function allocates a new table and parses the @file. The parser error
    1459   * callback and cache for tags and paths is set according to the @cxt setting.
    1460   * See also mnt_table_parse_file().
    1461   *
    1462   * It's strongly recommended to use the mnt_context_get_mtab() and
    1463   * mnt_context_get_fstab() functions for mountinfo and fstab files. This function
    1464   * does not care about LIBMOUNT_* env.variables and does not merge userspace
    1465   * options.
    1466   *
    1467   * The result will NOT be deallocated by mnt_free_context(@cxt).
    1468   *
    1469   * Returns: 0 on success, negative number in case of error.
    1470   */
    1471  int mnt_context_get_table(struct libmnt_context *cxt,
    1472  			  const char *filename, struct libmnt_table **tb)
    1473  {
    1474  	int rc;
    1475  	struct libmnt_ns *ns_old;
    1476  
    1477  	if (!cxt || !tb)
    1478  		return -EINVAL;
    1479  
    1480  	*tb = mnt_new_table();
    1481  	if (!*tb)
    1482  		return -ENOMEM;
    1483  
    1484  	if (cxt->table_errcb)
    1485  		mnt_table_set_parser_errcb(*tb, cxt->table_errcb);
    1486  
    1487  	ns_old = mnt_context_switch_target_ns(cxt);
    1488  	if (!ns_old)
    1489  		return -MNT_ERR_NAMESPACE;
    1490  
    1491  	rc = mnt_table_parse_file(*tb, filename);
    1492  
    1493  	if (rc) {
    1494  		mnt_unref_table(*tb);
    1495  		goto end;
    1496  	}
    1497  
    1498  	mnt_table_set_cache(*tb, mnt_context_get_cache(cxt));
    1499  
    1500  end:
    1501  	if (!mnt_context_switch_ns(cxt, ns_old))
    1502  		return -MNT_ERR_NAMESPACE;
    1503  
    1504  	return rc;
    1505  }
    1506  
    1507  /**
    1508   * mnt_context_set_tables_errcb
    1509   * @cxt: mount context
    1510   * @cb: pointer to callback function
    1511   *
    1512   * The error callback is used for all tab files (e.g. mountinfo, fstab)
    1513   * parsed within the context.
    1514   *
    1515   * See also mnt_context_get_mtab(),
    1516   *          mnt_context_get_fstab(),
    1517   *          mnt_table_set_parser_errcb().
    1518   *
    1519   * Returns: 0 on success, negative number in case of error.
    1520   */
    1521  int mnt_context_set_tables_errcb(struct libmnt_context *cxt,
    1522  	int (*cb)(struct libmnt_table *tb, const char *filename, int line))
    1523  {
    1524  	if (!cxt)
    1525  		return -EINVAL;
    1526  
    1527  	if (cxt->mountinfo)
    1528  		mnt_table_set_parser_errcb(cxt->mountinfo, cb);
    1529  	if (cxt->fstab)
    1530  		mnt_table_set_parser_errcb(cxt->fstab, cb);
    1531  
    1532  	cxt->table_errcb = cb;
    1533  	return 0;
    1534  }
    1535  
    1536  /**
    1537   * mnt_context_set_cache:
    1538   * @cxt: mount context
    1539   * @cache: cache instance or NULL
    1540   *
    1541   * The mount context maintains a private struct libmnt_cache by default. This
    1542   * function can be used to overwrite the private cache with an external instance.
    1543   * This function increments cache reference counter.
    1544   *
    1545   * If the @cache argument is NULL, then the current cache instance is reset.
    1546   * This function apply the cache to fstab and mountinfo instances (if already
    1547   * exists).
    1548   *
    1549   * The old cache instance reference counter is de-incremented.
    1550   *
    1551   * Returns: 0 on success, negative number in case of error.
    1552   */
    1553  int mnt_context_set_cache(struct libmnt_context *cxt, struct libmnt_cache *cache)
    1554  {
    1555  	if (!cxt)
    1556  		return -EINVAL;
    1557  
    1558  	mnt_ref_cache(cache);			/* new */
    1559  	mnt_unref_cache(cxt->cache);		/* old */
    1560  
    1561  	cxt->cache = cache;
    1562  
    1563  	if (cxt->mountinfo)
    1564  		mnt_table_set_cache(cxt->mountinfo, cache);
    1565  	if (cxt->fstab)
    1566  		mnt_table_set_cache(cxt->fstab, cache);
    1567  
    1568  	return 0;
    1569  }
    1570  
    1571  /**
    1572   * mnt_context_get_cache
    1573   * @cxt: mount context
    1574   *
    1575   * See also mnt_context_set_cache().
    1576   *
    1577   * Returns: pointer to cache or NULL if canonicalization is disabled.
    1578   */
    1579  struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt)
    1580  {
    1581  	if (!cxt || mnt_context_is_nocanonicalize(cxt))
    1582  		return NULL;
    1583  
    1584  	if (!cxt->cache) {
    1585  		struct libmnt_cache *cache = mnt_new_cache();
    1586  		mnt_context_set_cache(cxt, cache);
    1587  		mnt_unref_cache(cache);
    1588  	}
    1589  	return cxt->cache;
    1590  }
    1591  
    1592  /**
    1593   * mnt_context_set_passwd_cb:
    1594   * @cxt: mount context
    1595   * @get: callback to get password
    1596   * @release: callback to release (deallocate) password
    1597   *
    1598   * Sets callbacks for encryption password (e.g encrypted loopdev). This
    1599   * function is deprecated (encrypted loops are no longer supported).
    1600   *
    1601   * Returns: 0 on success, negative number in case of error.
    1602   */
    1603  int mnt_context_set_passwd_cb(struct libmnt_context *cxt,
    1604  			      char *(*get)(struct libmnt_context *),
    1605  			      void (*release)(struct libmnt_context *, char *))
    1606  {
    1607  	if (!cxt)
    1608  		return -EINVAL;
    1609  	cxt->pwd_get_cb = get;
    1610  	cxt->pwd_release_cb = release;
    1611  	return 0;
    1612  }
    1613  
    1614  /**
    1615   * mnt_context_get_lock:
    1616   * @cxt: mount context
    1617   *
    1618   * The libmount applications don't have to care about utab locking, but with a
    1619   * small exception: the application has to be able to remove the lock file when
    1620   * interrupted by signal or signals have to be ignored when the lock is locked.
    1621   *
    1622   * The default behavior is to ignore all signals (except SIGALRM and
    1623   * SIGTRAP for utab update) when the lock is locked. If this behavior
    1624   * is unacceptable, then use:
    1625   *
    1626   *	lc = mnt_context_get_lock(cxt);
    1627   *	if (lc)
    1628   *		mnt_lock_block_signals(lc, FALSE);
    1629   *
    1630   * and don't forget to call mnt_unlock_file(lc) before exit.
    1631   *
    1632   * Returns: pointer to lock struct or NULL.
    1633   */
    1634  struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt)
    1635  {
    1636  	/*
    1637  	 * DON'T call this function within libmount, it will always allocate
    1638  	 * the lock. The mnt_update_* functions are able to allocate the lock
    1639  	 * only when utab update is really necessary.
    1640  	 */
    1641  	if (!cxt || mnt_context_is_nomtab(cxt))
    1642  		return NULL;
    1643  
    1644  	if (!cxt->lock) {
    1645  		cxt->lock = mnt_new_lock(
    1646  				mnt_context_get_writable_tabpath(cxt), 0);
    1647  		if (cxt->lock)
    1648  			mnt_lock_block_signals(cxt->lock, TRUE);
    1649  	}
    1650  	return cxt->lock;
    1651  }
    1652  
    1653  /**
    1654   * mnt_context_set_mflags:
    1655   * @cxt: mount context
    1656   * @flags: mount(2) flags (MS_* flags)
    1657   *
    1658   * Sets mount flags (see mount(2) man page).
    1659   *
    1660   * Note that order of mount options (strings) and flags matter if you mix
    1661   * mnt_context_append_options() and mnt_context_set_mflags().
    1662   *
    1663   * Be careful if use MS_REC flag -- this is flags is generic for
    1664   * all mask. In this case is better to use options string where
    1665   * mount options are independent and nothign is applied to all options.
    1666   *
    1667   * Returns: 0 on success, negative number in case of error.
    1668   */
    1669  int mnt_context_set_mflags(struct libmnt_context *cxt, unsigned long flags)
    1670  {
    1671  	struct libmnt_optlist *ls = mnt_context_get_optlist(cxt);
    1672  
    1673  	if (!ls)
    1674  		return -ENOMEM;
    1675  
    1676  	return mnt_optlist_set_flags(ls, flags, cxt->map_linux);
    1677  }
    1678  
    1679  /**
    1680   * mnt_context_get_mflags:
    1681   * @cxt: mount context
    1682   * @flags: returns MS_* mount flags
    1683   *
    1684   * Converts mount options string to MS_* flags and bitwise-OR the result with
    1685   * the already defined flags (see mnt_context_set_mflags()).
    1686   *
    1687   * Returns: 0 on success, negative number in case of error.
    1688   */
    1689  int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags)
    1690  {
    1691  	struct libmnt_optlist *ls = mnt_context_get_optlist(cxt);
    1692  
    1693  	if (!ls)
    1694  		return -ENOMEM;
    1695  
    1696  	return mnt_optlist_get_flags(ls, flags, cxt->map_linux, 0);
    1697  }
    1698  
    1699  /**
    1700   * mnt_context_set_user_mflags:
    1701   * @cxt: mount context
    1702   * @flags: mount(2) flags (MNT_MS_* flags, e.g. MNT_MS_LOOP)
    1703   *
    1704   * Sets userspace mount flags.
    1705   *
    1706   * See also notes for mnt_context_set_mflags().
    1707   *
    1708   * Returns: 0 on success, negative number in case of error.
    1709   */
    1710  int mnt_context_set_user_mflags(struct libmnt_context *cxt, unsigned long flags)
    1711  {
    1712  	struct libmnt_optlist *ls = mnt_context_get_optlist(cxt);
    1713  
    1714  	if (!ls)
    1715  		return -ENOMEM;
    1716  
    1717  	return mnt_optlist_set_flags(ls, flags, cxt->map_userspace);
    1718  }
    1719  
    1720  /**
    1721   * mnt_context_get_user_mflags:
    1722   * @cxt: mount context
    1723   * @flags: returns mount flags
    1724   *
    1725   * Converts mount options string to MNT_MS_* flags and bitwise-OR the result
    1726   * with the already defined flags (see mnt_context_set_user_mflags()).
    1727   *
    1728   * Returns: 0 on success, negative number in case of error.
    1729   */
    1730  int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags)
    1731  {
    1732  	struct libmnt_optlist *ls = mnt_context_get_optlist(cxt);
    1733  
    1734  	if (!ls)
    1735  		return -ENOMEM;
    1736  
    1737  	return mnt_optlist_get_flags(ls, flags, cxt->map_userspace, 0);
    1738  }
    1739  
    1740  /**
    1741   * mnt_context_set_mountdata:
    1742   * @cxt: mount context
    1743   * @data: mount(2) data
    1744   *
    1745   * The mount context generates mountdata from mount options by default. This
    1746   * function can be used to overwrite this behavior, and @data will be used instead
    1747   * of mount options.
    1748   *
    1749   * The libmount does not deallocate the data by mnt_free_context(). Note that
    1750   * NULL is also valid mount data.
    1751   *
    1752   * Returns: 0 on success, negative number in case of error.
    1753   */
    1754  int mnt_context_set_mountdata(struct libmnt_context *cxt, void *data)
    1755  {
    1756  	if (!cxt)
    1757  		return -EINVAL;
    1758  	cxt->mountdata = data;
    1759  	cxt->flags |= MNT_FL_MOUNTDATA;
    1760  	return 0;
    1761  }
    1762  
    1763  /*
    1764   * Translates LABEL/UUID/path to mountable path
    1765   */
    1766  int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
    1767  {
    1768  	const char *path = NULL;
    1769  	struct libmnt_cache *cache;
    1770  	const char *t, *v, *src, *type;
    1771  	int rc = 0;
    1772  	struct libmnt_ns *ns_old;
    1773  	struct libmnt_optlist *ol;
    1774  
    1775  	assert(cxt);
    1776  	assert(cxt->fs);
    1777  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
    1778  
    1779  	DBG(CXT, ul_debugobj(cxt, "--> preparing source path"));
    1780  
    1781  	src = mnt_fs_get_source(cxt->fs);
    1782  
    1783  	if (!src && mnt_context_propagation_only(cxt))
    1784  		/* mount --make-{shared,private,...} */
    1785  		return mnt_fs_set_source(cxt->fs, "none");
    1786  
    1787  	/* ignore filesystems without source or filesystems
    1788  	 * where the source is a quasi-path (//foo/bar)
    1789  	 */
    1790  	if (!src || mnt_fs_is_netfs(cxt->fs))
    1791  		return 0;
    1792  
    1793  	/* ZFS source is always "dataset", not a real path */
    1794  	type = mnt_fs_get_fstype(cxt->fs);
    1795  	if (type && strcmp(type, "zfs") == 0)
    1796  		return 0;
    1797  
    1798  	DBG(CXT, ul_debugobj(cxt, "srcpath '%s'", src));
    1799  
    1800  	ns_old = mnt_context_switch_target_ns(cxt);
    1801  	if (!ns_old)
    1802  		return -MNT_ERR_NAMESPACE;
    1803  
    1804  	cache = mnt_context_get_cache(cxt);
    1805  
    1806  	if (!mnt_fs_get_tag(cxt->fs, &t, &v)) {
    1807  		/*
    1808  		 * Source is TAG (evaluate)
    1809  		 */
    1810  		if (cache)
    1811  			path = mnt_resolve_tag(t, v, cache);
    1812  
    1813  		rc = path ? mnt_fs_set_source(cxt->fs, path) : -MNT_ERR_NOSOURCE;
    1814  
    1815  	} else if (cache && !mnt_fs_is_pseudofs(cxt->fs)) {
    1816  		/*
    1817  		 * Source is PATH (canonicalize)
    1818  		 */
    1819  		path = mnt_resolve_path(src, cache);
    1820  		if (path && strcmp(path, src) != 0)
    1821  			rc = mnt_fs_set_source(cxt->fs, path);
    1822  	 }
    1823  
    1824  	if (rc) {
    1825  		DBG(CXT, ul_debugobj(cxt, "failed to prepare srcpath [rc=%d]", rc));
    1826  		goto end;
    1827  	}
    1828  
    1829  	if (!path)
    1830  		path = src;
    1831  
    1832  	ol = mnt_context_get_optlist(cxt);
    1833  	if (!ol)
    1834  		return -ENOMEM;
    1835  
    1836  	if (mnt_optlist_is_bind(ol)
    1837  	    || mnt_optlist_is_move(ol)
    1838  	    || mnt_optlist_is_remount(ol)
    1839  	    || mnt_fs_is_pseudofs(cxt->fs)) {
    1840  		DBG(CXT, ul_debugobj(cxt, "REMOUNT/BIND/MOVE/pseudo FS source: %s", path));
    1841  		goto end;
    1842  	}
    1843  
    1844  	rc = mnt_context_call_hooks(cxt, MNT_STAGE_PREP_SOURCE);
    1845  	if (rc)
    1846  		goto end;
    1847  
    1848  	DBG(CXT, ul_debugobj(cxt, "final srcpath '%s'",
    1849  				mnt_fs_get_source(cxt->fs)));
    1850  
    1851  end:
    1852  	if (!mnt_context_switch_ns(cxt, ns_old))
    1853  		return -MNT_ERR_NAMESPACE;
    1854  	return rc;
    1855  }
    1856  
    1857  /* Guess type, but not set to cxt->fs, always use free() for the result. It's
    1858   * no error when we're not able to guess a filesystem type. Note that error
    1859   * does not mean that result in @type is NULL.
    1860   */
    1861  int mnt_context_guess_srcpath_fstype(struct libmnt_context *cxt, char **type)
    1862  {
    1863  	int rc = 0;
    1864  	struct libmnt_ns *ns_old;
    1865  	const char *dev;
    1866  
    1867  	assert(type);
    1868  	assert(cxt);
    1869  
    1870  	*type = NULL;
    1871  
    1872  	dev = mnt_fs_get_srcpath(cxt->fs);
    1873  	if (!dev)
    1874  		return 0;
    1875  
    1876  	ns_old = mnt_context_switch_target_ns(cxt);
    1877  	if (!ns_old)
    1878  		return -MNT_ERR_NAMESPACE;
    1879  
    1880  	if (access(dev, F_OK) == 0) {
    1881  		struct libmnt_cache *cache = mnt_context_get_cache(cxt);
    1882  		int ambi = 0;
    1883  
    1884  		*type = mnt_get_fstype(dev, &ambi, cache);
    1885  		if (ambi)
    1886  			rc = -MNT_ERR_AMBIFS;
    1887  
    1888  		if (cache && *type) {
    1889  			*type = strdup(*type);
    1890  			if (!*type)
    1891  				rc = -ENOMEM;
    1892  		}
    1893  	} else {
    1894  		DBG(CXT, ul_debugobj(cxt, "access(%s) failed [%m]", dev));
    1895  		if (strchr(dev, ':') != NULL) {
    1896  			*type = strdup("nfs");
    1897  			if (!*type)
    1898  				rc = -ENOMEM;
    1899  		} else if (!strncmp(dev, "//", 2)) {
    1900  			*type = strdup("cifs");
    1901  			if (!*type)
    1902  				rc = -ENOMEM;
    1903  		}
    1904  	}
    1905  
    1906  	if (!mnt_context_switch_ns(cxt, ns_old))
    1907  		return -MNT_ERR_NAMESPACE;
    1908  
    1909  	if (rc == 0 && *type) {
    1910  		struct libmnt_optlist *ol = mnt_context_get_optlist(cxt);
    1911  		struct libmnt_opt *opt;
    1912  		const char *allowed;
    1913  
    1914  		if (!ol)
    1915  			return -ENOMEM;
    1916  
    1917  		opt = mnt_optlist_get_named(ol,
    1918  				"X-mount.auto-fstypes", cxt->map_userspace);
    1919  
    1920  		if (opt
    1921  		    && (allowed = mnt_opt_get_value(opt))
    1922  		    && !match_fstype(*type, allowed)) {
    1923  			DBG(CXT, ul_debugobj(cxt, "%s is not allowed by auto-fstypes=%s",
    1924  						*type, allowed));
    1925  			free(*type);
    1926  			*type = NULL;
    1927  			rc = -MNT_ERR_NOFSTYPE;
    1928  		}
    1929  	}
    1930  
    1931  	return rc;
    1932  }
    1933  
    1934  /*
    1935   * It's usually no error when we're not able to detect the filesystem type -- we
    1936   * will try to use the types from /{etc,proc}/filesystems.
    1937   */
    1938  int mnt_context_guess_fstype(struct libmnt_context *cxt)
    1939  {
    1940  	struct libmnt_optlist *ol;
    1941  	char *type;
    1942  	int rc = 0;
    1943  
    1944  	assert(cxt);
    1945  	assert(cxt->fs);
    1946  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
    1947  
    1948  	DBG(CXT, ul_debugobj(cxt, "--> preparing fstype"));
    1949  
    1950  	ol = mnt_context_get_optlist(cxt);
    1951  	if (!ol)
    1952  		return -ENOMEM;
    1953  
    1954  	if (mnt_optlist_is_bind(ol)
    1955  	    || mnt_optlist_is_move(ol)
    1956  	    || mnt_context_propagation_only(cxt))
    1957  		goto none;
    1958  
    1959  	type = (char *) mnt_fs_get_fstype(cxt->fs);
    1960  	if (type && !strcmp(type, "auto")) {
    1961  		mnt_fs_set_fstype(cxt->fs, NULL);
    1962  		type = NULL;
    1963  	}
    1964  
    1965  	if (type)
    1966  		goto done;
    1967  	if (mnt_optlist_is_remount(ol))
    1968  		goto none;
    1969  	if (cxt->fstype_pattern)
    1970  		goto done;
    1971  
    1972  	rc = mnt_context_guess_srcpath_fstype(cxt, &type);
    1973  	if (rc == 0 && type)
    1974  		__mnt_fs_set_fstype_ptr(cxt->fs, type);
    1975  	else
    1976  		free(type);
    1977  done:
    1978  	DBG(CXT, ul_debugobj(cxt, "FS type: %s [rc=%d]",
    1979  				mnt_fs_get_fstype(cxt->fs), rc));
    1980  	return rc;
    1981  none:
    1982  	return mnt_fs_set_fstype(cxt->fs, "none");
    1983  }
    1984  
    1985  /*
    1986   * The default is to use fstype from cxt->fs, this could be overwritten by
    1987   * @type. The @act is MNT_ACT_{MOUNT,UMOUNT}.
    1988   *
    1989   * Returns: 0 on success or negative number in case of error. Note that success
    1990   * does not mean that there is any usable helper, you have to check cxt->helper.
    1991   */
    1992  int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name,
    1993  				const char *type)
    1994  {
    1995  	char search_path[] = FS_SEARCH_PATH;		/* from config.h */
    1996  	char *p = NULL, *path;
    1997  	struct libmnt_ns *ns_old;
    1998  	int rc = 0;
    1999  
    2000  	assert(cxt);
    2001  	assert(cxt->fs);
    2002  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
    2003  
    2004  	DBG(CXT, ul_debugobj(cxt, "checking for helper"));
    2005  
    2006  	if (cxt->helper) {
    2007  		free(cxt->helper);
    2008  		cxt->helper = NULL;
    2009  	}
    2010  
    2011  	if (!type)
    2012  		type = mnt_fs_get_fstype(cxt->fs);
    2013  
    2014  	if (type && strchr(type, ','))
    2015  		return 0;			/* type is fstype pattern */
    2016  
    2017  	if (mnt_context_is_nohelpers(cxt)
    2018  	    || !type
    2019  	    || !strcmp(type, "none")
    2020  	    || strstr(type, "/..")		/* don't try to smuggle path */
    2021  	    || mnt_fs_is_swaparea(cxt->fs))
    2022  		return 0;
    2023  
    2024  	ns_old = mnt_context_switch_origin_ns(cxt);
    2025  	if (!ns_old)
    2026  		return -MNT_ERR_NAMESPACE;
    2027  
    2028  	/* Ignore errors when search in $PATH and do not modify @rc
    2029  	 */
    2030  	path = strtok_r(search_path, ":", &p);
    2031  	while (path) {
    2032  		char helper[PATH_MAX];
    2033  		int len, found = 0;
    2034  
    2035  		len = snprintf(helper, sizeof(helper), "%s/%s.%s",
    2036  						path, name, type);
    2037  		path = strtok_r(NULL, ":", &p);
    2038  
    2039  		if (len < 0 || (size_t) len >= sizeof(helper))
    2040  			continue;
    2041  
    2042  		found = mnt_is_path(helper);
    2043  		if (!found && strchr(type, '.')) {
    2044  			/* If type ends with ".subtype" try without it */
    2045  			char *hs = strrchr(helper, '.');
    2046  			if (hs)
    2047  				*hs = '\0';
    2048  			found = mnt_is_path(helper);
    2049  		}
    2050  
    2051  		DBG(CXT, ul_debugobj(cxt, "%-25s ... %s", helper,
    2052  					found ? "found" : "not found"));
    2053  		if (!found)
    2054  			continue;
    2055  
    2056  		/* success */
    2057  		rc = strdup_to_struct_member(cxt, helper, helper);
    2058  		break;
    2059  	}
    2060  
    2061  	if (!mnt_context_switch_ns(cxt, ns_old))
    2062  		rc = -MNT_ERR_NAMESPACE;
    2063  
    2064  	/* make sure helper is not set on error */
    2065  	if (rc) {
    2066  		free(cxt->helper);
    2067  		cxt->helper = NULL;
    2068  	}
    2069  	return rc;
    2070  }
    2071  
    2072  /* stop differentiate between options defined by flags and strings */
    2073  int mnt_context_merge_mflags(struct libmnt_context *cxt)
    2074  {
    2075  	struct libmnt_optlist *ls = mnt_context_get_optlist(cxt);
    2076  
    2077  	if (!ls)
    2078  		return -ENOMEM;
    2079  
    2080  	/* TODO: optlist returns always flags as merged, so
    2081  	 * MNT_FL_MOUNTFLAGS_MERGED is unncessary anymore
    2082  	 */
    2083  	cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED;
    2084  	return mnt_optlist_merge_opts(ls);
    2085  }
    2086  
    2087  /*
    2088   * Prepare /run/mount/utab
    2089   */
    2090  int mnt_context_prepare_update(struct libmnt_context *cxt)
    2091  {
    2092  	int rc;
    2093  	const char *target, *name;
    2094  	unsigned long flags = 0;
    2095  
    2096  	assert(cxt);
    2097  	assert(cxt->fs);
    2098  	assert(cxt->action);
    2099  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
    2100  
    2101  	DBG(CXT, ul_debugobj(cxt, "--> prepare update"));
    2102  
    2103  	if (mnt_context_propagation_only(cxt)) {
    2104  		DBG(CXT, ul_debugobj(cxt, "skip update: only MS_PROPAGATION"));
    2105  		return 0;
    2106  	}
    2107  
    2108  	target = mnt_fs_get_target(cxt->fs);
    2109  
    2110  	if (cxt->action == MNT_ACT_UMOUNT && target && !strcmp(target, "/")) {
    2111  		DBG(CXT, ul_debugobj(cxt, "root umount: setting NOMTAB"));
    2112  		mnt_context_disable_mtab(cxt, TRUE);
    2113  	}
    2114  	if (mnt_context_is_nomtab(cxt)) {
    2115  		DBG(CXT, ul_debugobj(cxt, "skip update: NOMTAB flag"));
    2116  		return 0;
    2117  	}
    2118  	name = mnt_context_get_writable_tabpath(cxt);
    2119  	if (!name) {
    2120  		DBG(CXT, ul_debugobj(cxt, "skip update: no writable destination"));
    2121  		return 0;
    2122  	}
    2123  	/* 0 = success, 1 = not called yet */
    2124  	if (cxt->syscall_status != 1 && cxt->syscall_status != 0) {
    2125  		DBG(CXT, ul_debugobj(cxt,
    2126  				"skip update: syscall failed [status=%d]",
    2127  				cxt->syscall_status));
    2128  		return 0;
    2129  	}
    2130  
    2131  	if (!cxt->update) {
    2132  		if (cxt->action == MNT_ACT_UMOUNT && is_file_empty(name)) {
    2133  			DBG(CXT, ul_debugobj(cxt, "skip update: umount, no table"));
    2134  			return 0;
    2135  		}
    2136  
    2137  		cxt->update = mnt_new_update();
    2138  		if (!cxt->update)
    2139  			return -ENOMEM;
    2140  
    2141  		mnt_update_set_filename(cxt->update, name);
    2142  	}
    2143  
    2144  	mnt_context_get_mflags(cxt, &flags);
    2145  
    2146  	if (cxt->action == MNT_ACT_UMOUNT)
    2147  		rc = mnt_update_set_fs(cxt->update, flags,
    2148  					mnt_context_get_target(cxt), NULL);
    2149  	else
    2150  		rc = mnt_update_set_fs(cxt->update, flags,
    2151  					NULL, cxt->fs);
    2152  
    2153  	return rc < 0 ? rc : 0;
    2154  }
    2155  
    2156  int mnt_context_update_tabs(struct libmnt_context *cxt)
    2157  {
    2158  	int rc = 0;
    2159  	struct libmnt_ns *ns_old;
    2160  
    2161  	assert(cxt);
    2162  
    2163  	if (mnt_context_is_nomtab(cxt)) {
    2164  		DBG(CXT, ul_debugobj(cxt, "don't update: NOMTAB flag"));
    2165  		return 0;
    2166  	}
    2167  	if (!cxt->update || !mnt_update_is_ready(cxt->update)) {
    2168  		DBG(CXT, ul_debugobj(cxt, "don't update: no update prepared"));
    2169  		return 0;
    2170  	}
    2171  
    2172  	ns_old = mnt_context_switch_target_ns(cxt);
    2173  	if (!ns_old)
    2174  		return -MNT_ERR_NAMESPACE;
    2175  
    2176  	/* check utab update when external helper executed */
    2177  	if (mnt_context_helper_executed(cxt)
    2178  	    && mnt_context_get_helper_status(cxt) == 0
    2179  	    && mnt_context_utab_writable(cxt)) {
    2180  
    2181  		if (mnt_update_already_done(cxt->update, cxt->lock)) {
    2182  			DBG(CXT, ul_debugobj(cxt, "don't update: error evaluate or already updated"));
    2183  			goto end;
    2184  		}
    2185  	} else if (cxt->helper) {
    2186  		DBG(CXT, ul_debugobj(cxt, "don't update: external helper"));
    2187  		goto end;
    2188  	}
    2189  
    2190  	if (cxt->syscall_status != 0
    2191  	    && !(mnt_context_helper_executed(cxt) &&
    2192  		 mnt_context_get_helper_status(cxt) == 0)) {
    2193  
    2194  		DBG(CXT, ul_debugobj(cxt, "don't update: syscall/helper failed/not called"));
    2195  		goto end;
    2196  	}
    2197  
    2198  	rc = mnt_update_table(cxt->update, cxt->lock);
    2199  
    2200  end:
    2201  	if (!mnt_context_switch_ns(cxt, ns_old))
    2202  		return -MNT_ERR_NAMESPACE;
    2203  	return rc;
    2204  }
    2205  
    2206  /* apply @fs to @cxt;
    2207   *
    2208   * @mflags are mount flags as specified on command-line -- used only to save
    2209   * MS_RDONLY which is allowed for non-root users.
    2210   */
    2211  static int apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs, unsigned long mflags)
    2212  {
    2213  	struct libmnt_optlist *ls;
    2214  	int rc;
    2215  
    2216  
    2217  
    2218  	if (!cxt->optsmode) {
    2219  		if (mnt_context_is_restricted(cxt)) {
    2220  			DBG(CXT, ul_debugobj(cxt, "force fstab usage for non-root users!"));
    2221  			cxt->optsmode = MNT_OMODE_USER;
    2222  		} else {
    2223  			DBG(CXT, ul_debugobj(cxt, "use default optsmode"));
    2224  			cxt->optsmode = MNT_OMODE_AUTO;
    2225  		}
    2226  
    2227  	}
    2228  
    2229  	if (!mnt_context_get_fs(cxt))
    2230  		return -ENOMEM;
    2231  
    2232  	DBG(CXT, ul_debugobj(cxt, "apply entry:"));
    2233  	DBG(CXT, mnt_fs_print_debug(fs, stderr));
    2234  	DBG(CXT, ul_debugobj(cxt, "OPTSMODE (opt-part): ignore=%d, append=%d, prepend=%d, replace=%d",
    2235  				  cxt->optsmode & MNT_OMODE_IGNORE ? 1 : 0,
    2236  				  cxt->optsmode & MNT_OMODE_APPEND ? 1 : 0,
    2237  				  cxt->optsmode & MNT_OMODE_PREPEND ? 1 : 0,
    2238  				  cxt->optsmode & MNT_OMODE_REPLACE ? 1 : 0));
    2239  
    2240  	/* copy from fs to our FS description
    2241  	 */
    2242  	rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs));
    2243  	if (!rc)
    2244  		rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs));
    2245  
    2246  	if (!rc && !mnt_fs_get_fstype(cxt->fs))
    2247  		rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs));
    2248  
    2249  	if (!rc && !mnt_fs_get_root(cxt->fs) && mnt_fs_get_root(fs))
    2250  		rc = mnt_fs_set_root(cxt->fs, mnt_fs_get_root(fs));
    2251  
    2252  	if (rc)
    2253  		goto done;
    2254  
    2255  	ls = mnt_context_get_optlist(cxt);
    2256  	if (!ls) {
    2257  		rc = -ENOMEM;
    2258  		goto done;
    2259  	}
    2260  
    2261  	if (cxt->optsmode & MNT_OMODE_IGNORE)
    2262  		;
    2263  	else if (cxt->optsmode & MNT_OMODE_REPLACE) {
    2264  		rc = mnt_optlist_set_optstr(ls, mnt_fs_get_options(fs), NULL);
    2265  
    2266  		/* mount --read-only for non-root users is allowed */
    2267  		if (rc == 0 && (mflags & MS_RDONLY)
    2268  		    && mnt_context_is_restricted(cxt)
    2269  		    && cxt->optsmode == MNT_OMODE_USER)
    2270  			rc = mnt_optlist_append_optstr(ls, "ro", NULL);
    2271  	}
    2272  	else if (cxt->optsmode & MNT_OMODE_APPEND)
    2273  		rc = mnt_optlist_append_optstr(ls, mnt_fs_get_options(fs), NULL);
    2274  
    2275  	else if (cxt->optsmode & MNT_OMODE_PREPEND)
    2276  		rc = mnt_optlist_prepend_optstr(ls, mnt_fs_get_options(fs), NULL);
    2277  
    2278  	if (!rc)
    2279  		cxt->flags |= MNT_FL_TAB_APPLIED;
    2280  
    2281  done:
    2282  	DBG(CXT, ul_debugobj(cxt, "final entry [rc=%d]", rc));
    2283  	DBG(CXT, mnt_fs_print_debug(cxt->fs, stderr));
    2284  
    2285  	return rc;
    2286  }
    2287  
    2288  static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb,
    2289  		     int direction, unsigned long mflags)
    2290  {
    2291  	struct libmnt_fs *fs = NULL;
    2292  	const char *src, *tgt;
    2293  
    2294  	assert(cxt);
    2295  	assert(cxt->fs);
    2296  
    2297  	src = mnt_fs_get_source(cxt->fs);
    2298  	tgt = mnt_fs_get_target(cxt->fs);
    2299  
    2300  	if (tgt && src)
    2301  		fs = mnt_table_find_pair(tb, src, tgt, direction);
    2302  	else {
    2303  		if (src)
    2304  			fs = mnt_table_find_source(tb, src, direction);
    2305  		else if (tgt)
    2306  			fs = mnt_table_find_target(tb, tgt, direction);
    2307  
    2308  		if (!fs && mnt_context_is_swapmatch(cxt)) {
    2309  			/* swap source and target (if @src is not LABEL/UUID),
    2310  			 * for example in
    2311  			 *
    2312  			 *	mount /foo/bar
    2313  			 *
    2314  			 * the path could be a mountpoint as well as a source (for
    2315  			 * example bind mount, symlink to a device, ...).
    2316  			 */
    2317  			if (src && !mnt_fs_get_tag(cxt->fs, NULL, NULL))
    2318  				fs = mnt_table_find_target(tb, src, direction);
    2319  			if (!fs && tgt)
    2320  				fs = mnt_table_find_source(tb, tgt, direction);
    2321  		}
    2322  	}
    2323  
    2324  	if (!fs)
    2325  		return -MNT_ERR_NOFSTAB;	/* not found */
    2326  
    2327  	return apply_fs(cxt, fs, mflags);
    2328  }
    2329  
    2330  /* apply @fs to @cxt -- use mnt_context_apply_fstab() if not sure
    2331   */
    2332  int mnt_context_apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs)
    2333  {
    2334  	return apply_fs(cxt, fs, 0);
    2335  }
    2336  
    2337  /**
    2338   * mnt_context_apply_fstab:
    2339   * @cxt: mount context
    2340   *
    2341   * This function is optional.
    2342   *
    2343   * Returns: 0 on success, negative number in case of error.
    2344   */
    2345  int mnt_context_apply_fstab(struct libmnt_context *cxt)
    2346  {
    2347  	int rc = -1, isremount = 0, iscmdbind = 0;
    2348  	struct libmnt_ns *ns_old;
    2349  	struct libmnt_table *tab = NULL;
    2350  	const char *src = NULL, *tgt = NULL;
    2351  	unsigned long mflags = 0;
    2352  
    2353  	if (!cxt || !cxt->fs)
    2354  		return -EINVAL;
    2355  
    2356  	if (mnt_context_tab_applied(cxt)) {	/* already applied */
    2357  		DBG(CXT, ul_debugobj(cxt, "fstab already applied -- skip"));
    2358  		return 0;
    2359  	}
    2360  
    2361  	if (mnt_context_is_restricted(cxt)) {
    2362  		DBG(CXT, ul_debugobj(cxt, "force fstab usage for non-root users!"));
    2363  		cxt->optsmode = MNT_OMODE_USER;
    2364  	} else if (cxt->optsmode == 0) {
    2365  		DBG(CXT, ul_debugobj(cxt, "use default optsmode"));
    2366  		cxt->optsmode = MNT_OMODE_AUTO;
    2367  	} else if (cxt->optsmode & MNT_OMODE_NOTAB) {
    2368  		cxt->optsmode &= ~MNT_OMODE_FSTAB;
    2369  		cxt->optsmode &= ~MNT_OMODE_MTAB;
    2370  		cxt->optsmode &= ~MNT_OMODE_FORCE;
    2371  	}
    2372  
    2373  	if (mnt_context_get_mflags(cxt, &mflags) == 0) {
    2374  		isremount = !!(mflags & MS_REMOUNT);
    2375  		iscmdbind = !!(mflags & MS_BIND);
    2376  	}
    2377  
    2378  	if (cxt->fs) {
    2379  		src = mnt_fs_get_source(cxt->fs);
    2380  		tgt = mnt_fs_get_target(cxt->fs);
    2381  	}
    2382  
    2383  	DBG(CXT, ul_debugobj(cxt, "OPTSMODE (file-part): force=%d, fstab=%d, mtab=%d",
    2384  				  cxt->optsmode & MNT_OMODE_FORCE ? 1 : 0,
    2385  				  cxt->optsmode & MNT_OMODE_FSTAB ? 1 : 0,
    2386  				  cxt->optsmode & MNT_OMODE_MTAB ? 1 : 0));
    2387  
    2388  	/* fstab is not required if source and target are specified */
    2389  	if (src && tgt && !(cxt->optsmode & MNT_OMODE_FORCE)) {
    2390  		DBG(CXT, ul_debugobj(cxt, "fstab not required -- skip"));
    2391  		return 0;
    2392  	}
    2393  
    2394  	if (!src && tgt
    2395  	    && !(cxt->optsmode & MNT_OMODE_FSTAB)
    2396  	    && !(cxt->optsmode & MNT_OMODE_MTAB)) {
    2397  		DBG(CXT, ul_debugobj(cxt, "only target; fstab/mtab not required "
    2398  					  "-- skip, probably MS_PROPAGATION"));
    2399  		return 0;
    2400  	}
    2401  
    2402  	/* let's initialize cxt->fs */
    2403  	ignore_result( mnt_context_get_fs(cxt) );
    2404  
    2405  	ns_old = mnt_context_switch_target_ns(cxt);
    2406  	if (!ns_old)
    2407  		return -MNT_ERR_NAMESPACE;
    2408  
    2409  	/* try fstab */
    2410  	if (cxt->optsmode & MNT_OMODE_FSTAB) {
    2411  		DBG(CXT, ul_debugobj(cxt, "trying to apply fstab (src=%s, target=%s)", src, tgt));
    2412  		rc = mnt_context_get_fstab(cxt, &tab);
    2413  		if (!rc)
    2414  			rc = apply_table(cxt, tab, MNT_ITER_FORWARD, mflags);
    2415  	}
    2416  
    2417  	/* try mountinfo */
    2418  	if (rc < 0 && (cxt->optsmode & MNT_OMODE_MTAB)
    2419  	    && (isremount || cxt->action == MNT_ACT_UMOUNT)) {
    2420  		DBG(CXT, ul_debugobj(cxt, "trying to apply mountinfo (src=%s, target=%s)", src, tgt));
    2421  		if (tgt)
    2422  			rc = mnt_context_get_mountinfo_for_target(cxt, &tab, tgt);
    2423  		else
    2424  			rc = mnt_context_get_mountinfo(cxt, &tab);
    2425  		if (!rc)
    2426  			rc = apply_table(cxt, tab, MNT_ITER_BACKWARD, mflags);
    2427  	}
    2428  
    2429  	if (!mnt_context_switch_ns(cxt, ns_old))
    2430  		return -MNT_ERR_NAMESPACE;
    2431  
    2432  	if (rc) {
    2433  		if (!mnt_context_is_restricted(cxt)
    2434  		    && tgt && !src
    2435  		    && isremount) {
    2436  			DBG(CXT, ul_debugobj(cxt, "only target; ignore missing mountinfo entry on remount"));
    2437  			return 0;
    2438  		}
    2439  
    2440  		DBG(CXT, ul_debugobj(cxt, "failed to find entry in fstab/mountinfo [rc=%d]: %m", rc));
    2441  
    2442  		/* force to "not found in fstab/mountinfo" error, the details why
    2443  		 * not found are not so important and may be misinterpreted by
    2444  		 * applications... */
    2445  		rc = -MNT_ERR_NOFSTAB;
    2446  
    2447  
    2448  	} else if (isremount && !iscmdbind && cxt->optlist) {
    2449  
    2450  		/* ignore "bind" on remount when the flag is read from fstab */
    2451  		mnt_optlist_remove_named(cxt->optlist, "bind", NULL);
    2452  	}
    2453  	return rc;
    2454  }
    2455  
    2456  /**
    2457   * mnt_context_tab_applied:
    2458   * @cxt: mount context
    2459   *
    2460   * Returns: 1 if fstab (or mountinfo) has been applied to the context, or 0.
    2461   */
    2462  int mnt_context_tab_applied(struct libmnt_context *cxt)
    2463  {
    2464  	return cxt->flags & MNT_FL_TAB_APPLIED;
    2465  }
    2466  
    2467  /*
    2468   * This is not a public function!
    2469   *
    2470   * Returns 1 if *only propagation flags* change is requested.
    2471   */
    2472  int mnt_context_propagation_only(struct libmnt_context *cxt)
    2473  {
    2474  	struct libmnt_optlist *ls;
    2475  
    2476  	if (cxt->action != MNT_ACT_MOUNT)
    2477  		return 0;
    2478  	if (cxt->mountdata || cxt->fs == NULL)
    2479  		return 0;
    2480  	if (cxt->fs->fstype && strcmp(cxt->fs->fstype, "none") != 0)
    2481  		return 0;
    2482  	if (cxt->fs->source && strcmp(cxt->fs->source, "none") != 0)
    2483  		return 0;
    2484  
    2485  	ls = mnt_context_get_optlist(cxt);
    2486  	return ls ? mnt_optlist_is_propagation_only(ls) : 0;
    2487  }
    2488  
    2489  /**
    2490   * mnt_context_get_status:
    2491   * @cxt: mount context
    2492   *
    2493   * Global libmount status.
    2494   *
    2495   * The real exit code of the mount.type helper has to be tested by
    2496   * mnt_context_get_helper_status(). The mnt_context_get_status() only informs
    2497   * that exec() has been successful.
    2498   *
    2499   * Returns: 1 if mount.type or mount(2) syscall has been successfully called.
    2500   */
    2501  int mnt_context_get_status(struct libmnt_context *cxt)
    2502  {
    2503  	return !cxt->syscall_status || !cxt->helper_exec_status;
    2504  }
    2505  
    2506  /**
    2507   * mnt_context_helper_executed:
    2508   * @cxt: mount context
    2509   *
    2510   * Returns: 1 if mount.type helper has been executed, or 0.
    2511   */
    2512  int mnt_context_helper_executed(struct libmnt_context *cxt)
    2513  {
    2514  	return cxt->helper_exec_status != 1;
    2515  }
    2516  
    2517  /**
    2518   * mnt_context_get_helper_status:
    2519   * @cxt: mount context
    2520   *
    2521   * Return: mount.type helper exit status, result is reliable only if
    2522   *         mnt_context_helper_executed() returns 1.
    2523   */
    2524  int mnt_context_get_helper_status(struct libmnt_context *cxt)
    2525  {
    2526  	return cxt->helper_status;
    2527  }
    2528  
    2529  /**
    2530   * mnt_context_syscall_called:
    2531   * @cxt: mount context
    2532   *
    2533   * Returns: 1 if mount(2) syscall has been called, or 0.
    2534   */
    2535  int mnt_context_syscall_called(struct libmnt_context *cxt)
    2536  {
    2537  	return cxt->syscall_status != 1;
    2538  }
    2539  
    2540  /**
    2541   * mnt_context_get_syscall_errno:
    2542   * @cxt: mount context
    2543   *
    2544   * The result from this function is reliable only if
    2545   * mnt_context_syscall_called() returns 1.
    2546   *
    2547   * Returns: mount(2) errno if the syscall failed or 0.
    2548   */
    2549  int mnt_context_get_syscall_errno(struct libmnt_context *cxt)
    2550  {
    2551  	if (cxt->syscall_status < 0)
    2552  		return -cxt->syscall_status;
    2553  	return 0;
    2554  }
    2555  
    2556  /**
    2557   * mnt_context_set_syscall_status:
    2558   * @cxt: mount context
    2559   * @status: mount(2) status
    2560   *
    2561   * The @status should be 0 on success, or negative number on error (-errno).
    2562   *
    2563   * This function should only be used if the [u]mount(2) syscall is NOT called by
    2564   * libmount code.
    2565   *
    2566   * Returns: 0 or negative number in case of error.
    2567   */
    2568  int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status)
    2569  {
    2570  	if (!cxt)
    2571  		return -EINVAL;
    2572  
    2573  	DBG(CXT, ul_debugobj(cxt, "syscall status set to: %d", status));
    2574  	cxt->syscall_status = status;
    2575  	return 0;
    2576  }
    2577  
    2578  /**
    2579   * mnt_context_strerror
    2580   * @cxt: context
    2581   * @buf: buffer
    2582   * @bufsiz: size of the buffer
    2583   *
    2584   * Not implemented, deprecated in favor or mnt_context_get_excode().
    2585   *
    2586   * Returns: 0 or negative number in case of error.
    2587   */
    2588  int mnt_context_strerror(struct libmnt_context *cxt __attribute__((__unused__)),
    2589  			 char *buf __attribute__((__unused__)),
    2590  			 size_t bufsiz __attribute__((__unused__)))
    2591  {
    2592  	/* TODO: based on cxt->syscall_errno or cxt->helper_status */
    2593  	return 0;
    2594  }
    2595  
    2596  /**
    2597   * mnt_context_enable_noautofs:
    2598   * @cxt: context
    2599   * @ignore: ignore or don't ignore
    2600   *
    2601   * Enable/disable ignore autofs mount table entries on reading the
    2602   * mount table. Only effective if the "ignore" mount option is being
    2603   * used for autofs mounts (such as if automount(8) has been configured
    2604   * to do so).
    2605   */
    2606  int mnt_context_enable_noautofs(struct libmnt_context *cxt, int ignore)
    2607  {
    2608  	if (!cxt)
    2609  		return -EINVAL;
    2610  	cxt->noautofs = ignore ? 1 : 0;
    2611  	return 0;
    2612  }
    2613  
    2614  int mnt_context_get_generic_excode(int rc, char *buf, size_t bufsz, const char *fmt, ...)
    2615  {
    2616  	va_list va;
    2617  
    2618  	if (rc == 0)
    2619  		return MNT_EX_SUCCESS;
    2620  
    2621  	va_start(va, fmt);
    2622  
    2623  	/* we need to support "%m" */
    2624  	errno = rc < 0 ? -rc : rc;
    2625  
    2626  	if (buf && bufsz && vsnprintf(buf, bufsz, fmt, va) < 0)
    2627  		*buf = '\0';
    2628  
    2629  	switch (errno) {
    2630  	case EINVAL:
    2631  	case EPERM:
    2632  		rc = MNT_EX_USAGE;
    2633  		break;
    2634  	case ENOMEM:
    2635  		rc = MNT_EX_SYSERR;
    2636  		break;
    2637  	default:
    2638  		rc = MNT_EX_FAIL;
    2639  		break;
    2640  	}
    2641  	va_end(va);
    2642  	return rc;
    2643  }
    2644  
    2645  /**
    2646   * mnt_context_get_excode:
    2647   * @cxt: context
    2648   * @rc: return code of the previous operation
    2649   * @buf: buffer to print error message (optional)
    2650   * @bufsz: size of the buffer
    2651   *
    2652   * This function analyzes context, [u]mount syscall and external helper status
    2653   * and @mntrc and generates unified return code (see MNT_EX_*) as expected
    2654   * from mount(8) or umount(8).
    2655   *
    2656   * If the external helper (e.g. /sbin/mount.type) has been executed than it
    2657   * returns status from wait() of the helper. It's not libmount fail if helper
    2658   * returns some crazy undocumented codes...  See mnt_context_helper_executed()
    2659   * and mnt_context_get_helper_status(). Note that mount(8) and umount(8) utils
    2660   * always return code from helper without extra care about it.
    2661   *
    2662   * The current implementation does not read error message from external
    2663   * helper into @buf.
    2664   *
    2665   * If the argument @buf is not NULL then error message is generated (if
    2666   * anything failed).
    2667   *
    2668   * The @mntrc is usually return code from mnt_context_mount(),
    2669   * mnt_context_umount(), or 'mntrc' as returned by mnt_context_next_mount().
    2670   *
    2671   * Since: 2.30
    2672   *
    2673   * Returns: MNT_EX_* codes.
    2674   */
    2675  int mnt_context_get_excode(
    2676  			struct libmnt_context *cxt,
    2677  			int rc,
    2678  			char *buf,
    2679  			size_t bufsz)
    2680  {
    2681  	if (buf) {
    2682  		*buf = '\0'; /* for sure */
    2683  
    2684  		if (!cxt->enabled_textdomain) {
    2685  			bindtextdomain(LIBMOUNT_TEXTDOMAIN, LOCALEDIR);
    2686  			cxt->enabled_textdomain = 1;
    2687  		}
    2688  	}
    2689  
    2690  	switch (cxt->action) {
    2691  	case MNT_ACT_MOUNT:
    2692  		rc = mnt_context_get_mount_excode(cxt, rc, buf, bufsz);
    2693  		break;
    2694  	case MNT_ACT_UMOUNT:
    2695  		rc = mnt_context_get_umount_excode(cxt, rc, buf, bufsz);
    2696  		break;
    2697  	default:
    2698  		if (rc)
    2699  			rc = mnt_context_get_generic_excode(rc, buf, bufsz,
    2700  				_("operation failed: %m"));
    2701  		else
    2702  			rc = MNT_EX_SUCCESS;
    2703  		break;
    2704  	}
    2705  
    2706  	DBG(CXT, ul_debugobj(cxt, "excode: rc=%d message=\"%s\"", rc,
    2707  				buf ? buf : "<no-message>"));
    2708  	return rc;
    2709  }
    2710  
    2711  
    2712  /**
    2713   * mnt_context_init_helper
    2714   * @cxt: mount context
    2715   * @action: MNT_ACT_{UMOUNT,MOUNT}
    2716   * @flags: not used now
    2717   *
    2718   * This function informs libmount that used from [u]mount.type helper.
    2719   *
    2720   * The function also calls mnt_context_disable_helpers() to avoid recursive
    2721   * mount.type helpers calling. It you really want to call another
    2722   * mount.type helper from your helper, then you have to explicitly enable this
    2723   * feature by:
    2724   *
    2725   *	 mnt_context_disable_helpers(cxt, FALSE);
    2726   *
    2727   * Returns: 0 on success, negative number in case of error.
    2728   */
    2729  int mnt_context_init_helper(struct libmnt_context *cxt, int action,
    2730  			    int flags __attribute__((__unused__)))
    2731  {
    2732  	int rc;
    2733  
    2734  	if (!cxt)
    2735  		return -EINVAL;
    2736  
    2737  	rc = mnt_context_disable_helpers(cxt, TRUE);
    2738  	if (!rc)
    2739  		rc = set_flag(cxt, MNT_FL_HELPER, 1);
    2740  	if (!rc)
    2741  		cxt->action = action;
    2742  
    2743  	DBG(CXT, ul_debugobj(cxt, "initialized for [u]mount.<type> helper [rc=%d]", rc));
    2744  	return rc;
    2745  }
    2746  
    2747  /**
    2748   * mnt_context_helper_setopt:
    2749   * @cxt: context
    2750   * @c: getopt() result
    2751   * @arg: getopt() optarg
    2752   *
    2753   * This function applies the [u]mount.type command line option (for example parsed
    2754   * by getopt or getopt_long) to @cxt. All unknown options are ignored and
    2755   * then 1 is returned.
    2756   *
    2757   * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
    2758   */
    2759  int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg)
    2760  {
    2761  	if (cxt) {
    2762  		switch(cxt->action) {
    2763  		case MNT_ACT_MOUNT:
    2764  			return mnt_context_mount_setopt(cxt, c, arg);
    2765  		case MNT_ACT_UMOUNT:
    2766  			return mnt_context_umount_setopt(cxt, c, arg);
    2767  		}
    2768  	}
    2769  	return -EINVAL;
    2770  }
    2771  
    2772  /**
    2773   * mnt_context_is_fs_mounted:
    2774   * @cxt: context
    2775   * @fs: filesystem
    2776   * @mounted: returns 1 for mounted and 0 for non-mounted filesystems
    2777   *
    2778   * Please, read the mnt_table_is_fs_mounted() description!
    2779   *
    2780   * Returns: 0 on success and negative number in case of error.
    2781   */
    2782  int mnt_context_is_fs_mounted(struct libmnt_context *cxt,
    2783  			      struct libmnt_fs *fs, int *mounted)
    2784  {
    2785  	struct libmnt_table *mountinfo, *orig;
    2786  	int rc = 0;
    2787  	struct libmnt_ns *ns_old;
    2788  
    2789  	if (!cxt || !fs || !mounted)
    2790  		return -EINVAL;
    2791  
    2792  	ns_old = mnt_context_switch_target_ns(cxt);
    2793  	if (!ns_old)
    2794  		return -MNT_ERR_NAMESPACE;
    2795  
    2796  	orig = cxt->mountinfo;
    2797  	rc = mnt_context_get_mountinfo(cxt, &mountinfo);
    2798  	if (rc == -ENOENT && mnt_fs_streq_target(fs, "/proc")) {
    2799  		if (!orig) {
    2800  			mnt_unref_table(cxt->mountinfo);
    2801  			cxt->mountinfo = NULL;
    2802  		}
    2803  		*mounted = 0;
    2804  		rc = 0;			/* /proc not mounted */
    2805  
    2806  	} else if (rc == 0) {
    2807  		*mounted = __mnt_table_is_fs_mounted(mountinfo, fs,
    2808  				mnt_context_get_target_prefix(cxt));
    2809  
    2810  	}
    2811  
    2812  	if (!mnt_context_switch_ns(cxt, ns_old))
    2813  		return -MNT_ERR_NAMESPACE;
    2814  	return rc;
    2815  }
    2816  
    2817  static int mnt_context_add_child(struct libmnt_context *cxt, pid_t pid)
    2818  {
    2819  	pid_t *pids;
    2820  
    2821  	if (!cxt)
    2822  		return -EINVAL;
    2823  
    2824  	pids = realloc(cxt->children, sizeof(pid_t) * cxt->nchildren + 1);
    2825  	if (!pids)
    2826  		return -ENOMEM;
    2827  
    2828  	DBG(CXT, ul_debugobj(cxt, "add new child %d", pid));
    2829  	cxt->children = pids;
    2830  	cxt->children[cxt->nchildren++] = pid;
    2831  
    2832  	return 0;
    2833  }
    2834  
    2835  int mnt_fork_context(struct libmnt_context *cxt)
    2836  {
    2837  	int rc = 0;
    2838  	pid_t pid;
    2839  
    2840  	assert(cxt);
    2841  	if (!mnt_context_is_parent(cxt))
    2842  		return -EINVAL;
    2843  
    2844  	DBG(CXT, ul_debugobj(cxt, "forking context"));
    2845  
    2846  	DBG_FLUSH;
    2847  
    2848  	pid = fork();
    2849  
    2850  	switch (pid) {
    2851  	case -1: /* error */
    2852  		DBG(CXT, ul_debugobj(cxt, "fork failed %m"));
    2853  		return -errno;
    2854  
    2855  	case 0: /* child */
    2856  		cxt->pid = getpid();
    2857  		mnt_context_enable_fork(cxt, FALSE);
    2858  		DBG(CXT, ul_debugobj(cxt, "child created"));
    2859  		break;
    2860  
    2861  	default:
    2862  		rc = mnt_context_add_child(cxt, pid);
    2863  		break;
    2864  	}
    2865  
    2866  	return rc;
    2867  }
    2868  
    2869  int mnt_context_wait_for_children(struct libmnt_context *cxt,
    2870  				  int *nchildren, int *nerrs)
    2871  {
    2872  	int i;
    2873  
    2874  	if (!cxt)
    2875  		return -EINVAL;
    2876  
    2877  	assert(mnt_context_is_parent(cxt));
    2878  
    2879  	for (i = 0; i < cxt->nchildren; i++) {
    2880  		pid_t pid = cxt->children[i];
    2881  		int rc = 0, ret = 0;
    2882  
    2883  		if (!pid)
    2884  			continue;
    2885  		do {
    2886  			DBG(CXT, ul_debugobj(cxt,
    2887  					"waiting for child (%d/%d): %d",
    2888  					i + 1, cxt->nchildren, pid));
    2889  			errno = 0;
    2890  			rc = waitpid(pid, &ret, 0);
    2891  
    2892  		} while (rc == -1 && errno == EINTR);
    2893  
    2894  		if (nchildren)
    2895  			(*nchildren)++;
    2896  
    2897  		if (rc != -1 && nerrs) {
    2898  			if (WIFEXITED(ret))
    2899  				(*nerrs) += WEXITSTATUS(ret) == 0 ? 0 : 1;
    2900  			else
    2901  				(*nerrs)++;
    2902  		}
    2903  		cxt->children[i] = 0;
    2904  	}
    2905  
    2906  	cxt->nchildren = 0;
    2907  	free(cxt->children);
    2908  	cxt->children = NULL;
    2909  	return 0;
    2910  }
    2911  
    2912  static void close_ns(struct libmnt_ns *ns)
    2913  {
    2914  	if (ns->fd == -1)
    2915  		return;
    2916  
    2917  	close(ns->fd);
    2918  	ns->fd = -1;
    2919  
    2920  	mnt_unref_cache(ns->cache);
    2921  	ns->cache = NULL;
    2922  }
    2923  
    2924  /**
    2925   * mnt_context_set_target_ns:
    2926   * @cxt: mount context
    2927   * @path: path to target namespace or NULL
    2928   *
    2929   * Sets target namespace to namespace represented by @path. If @path is NULL,
    2930   * target namespace is cleared.
    2931   *
    2932   * This function sets errno to ENOSYS and returns error if libmount is
    2933   * compiled without namespaces support.
    2934   *
    2935   * Returns: 0 on success, negative number in case of error.
    2936   *
    2937   * Since: 2.33
    2938   */
    2939  int mnt_context_set_target_ns(struct libmnt_context *cxt, const char *path)
    2940  {
    2941  	if (!cxt)
    2942  		return -EINVAL;
    2943  
    2944  	DBG(CXT, ul_debugobj(cxt, "Setting %s as target namespace", path));
    2945  
    2946  	/* cleanup only */
    2947  	if (!path) {
    2948  		close_ns(&cxt->ns_orig);
    2949  		close_ns(&cxt->ns_tgt);
    2950  		return 0;
    2951  	}
    2952  
    2953  #ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
    2954  	int errsv = 0;
    2955  	int tmp;
    2956  
    2957  	errno = 0;
    2958  
    2959  	/* open original namespace */
    2960  	if (cxt->ns_orig.fd == -1) {
    2961  		cxt->ns_orig.fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
    2962  		if (cxt->ns_orig.fd == -1)
    2963  			return -errno;
    2964  		cxt->ns_orig.cache = NULL;
    2965  	}
    2966  
    2967  	/* open target (wanted) namespace */
    2968  	tmp = open(path, O_RDONLY | O_CLOEXEC);
    2969  	if (tmp == -1)
    2970  		return -errno;
    2971  
    2972  	/* test whether namespace switching works */
    2973  	DBG(CXT, ul_debugobj(cxt, "Trying whether namespace is valid"));
    2974  	if (setns(tmp, CLONE_NEWNS)
    2975  	    || setns(cxt->ns_orig.fd, CLONE_NEWNS)) {
    2976  		errsv = errno;
    2977  		DBG(CXT, ul_debugobj(cxt, "setns(2) failed [errno=%d %m]", errno));
    2978  		goto err;
    2979  	}
    2980  
    2981  	close_ns(&cxt->ns_tgt);
    2982  
    2983  	cxt->ns_tgt.fd = tmp;
    2984  	cxt->ns_tgt.cache = NULL;
    2985  
    2986  	return 0;
    2987  err:
    2988  	close(tmp);
    2989  	errno = errsv;
    2990  
    2991  #else /* ! USE_LIBMOUNT_SUPPORT_NAMESPACES */
    2992  	errno = ENOSYS;
    2993  #endif
    2994  	return -errno;
    2995  }
    2996  
    2997  /**
    2998   * mnt_context_get_target_ns:
    2999   * @cxt: mount context
    3000   *
    3001   * Returns: pointer to target namespace
    3002   *
    3003   * Since: 2.33
    3004   */
    3005  struct libmnt_ns *mnt_context_get_target_ns(struct libmnt_context *cxt)
    3006  {
    3007  	return &cxt->ns_tgt;
    3008  }
    3009  
    3010  /**
    3011   * mnt_context_get_origin_ns:
    3012   * @cxt: mount context
    3013   *
    3014   * Returns: pointer to original namespace
    3015   *
    3016   * Since: 2.33
    3017   */
    3018  struct libmnt_ns *mnt_context_get_origin_ns(struct libmnt_context *cxt)
    3019  {
    3020  	return &cxt->ns_orig;
    3021  }
    3022  
    3023  
    3024  /**
    3025   * mnt_context_switch_ns:
    3026   * @cxt: mount context
    3027   * @ns: namespace to switch to
    3028   *
    3029   * Switch to namespace specified by ns
    3030   *
    3031   * Typical usage:
    3032   * <informalexample>
    3033   *   <programlisting>
    3034   *	struct libmnt_ns *ns_old;
    3035   *	ns_old = mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt));
    3036   *	... code ...
    3037   *	mnt_context_switch_ns(cxt, ns_old);
    3038   *   </programlisting>
    3039   * </informalexample>
    3040   *
    3041   * Returns: pointer to previous namespace or NULL on error
    3042   *
    3043   * Since: 2.33
    3044   */
    3045  struct libmnt_ns *mnt_context_switch_ns(struct libmnt_context *cxt, struct libmnt_ns *ns)
    3046  {
    3047  	struct libmnt_ns *old = NULL;
    3048  
    3049  	if (!cxt || !ns)
    3050  		return NULL;
    3051  
    3052  	/*
    3053  	 * If mnt_context_set_target_ns() has never been used than @ns file
    3054  	 * descriptor is -1 and this function is noop.
    3055  	 */
    3056  	old = cxt->ns_cur;
    3057  	if (ns == old || ns->fd == -1)
    3058  		return old;
    3059  
    3060  #ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
    3061  	/* remember the current cache */
    3062  	if (old->cache != cxt->cache) {
    3063  		mnt_unref_cache(old->cache);
    3064  		old->cache = cxt->cache;
    3065  		mnt_ref_cache(old->cache);
    3066  	}
    3067  
    3068  	/* switch */
    3069  	DBG(CXT, ul_debugobj(cxt, "Switching to %s namespace",
    3070  		ns == mnt_context_get_target_ns(cxt) ? "target" :
    3071  		ns == mnt_context_get_origin_ns(cxt) ? "original" : "other"));
    3072  
    3073  	if (setns(ns->fd, CLONE_NEWNS)) {
    3074  		int errsv = errno;
    3075  
    3076  		DBG(CXT, ul_debugobj(cxt, "setns(2) failed [errno=%d %m]", errno));
    3077  		errno = errsv;
    3078  		return NULL;
    3079  	}
    3080  
    3081  	/* update pointer to the current namespace */
    3082  	cxt->ns_cur = ns;
    3083  
    3084  	/* update pointer to the cache */
    3085  	mnt_unref_cache(cxt->cache);
    3086  	cxt->cache = ns->cache;
    3087  	mnt_ref_cache(cxt->cache);
    3088  #endif /* USE_LIBMOUNT_SUPPORT_NAMESPACES */
    3089  
    3090  	return old;
    3091  }
    3092  
    3093  /**
    3094   * mnt_context_switch_origin_ns:
    3095   * @cxt: mount context
    3096   *
    3097   * Switch to original namespace
    3098   *
    3099   * This is shorthand for
    3100   * <informalexample>
    3101   *   <programlisting>
    3102   *	mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt));
    3103   *   </programlisting>
    3104   * </informalexample>
    3105   *
    3106   * Returns: pointer to previous namespace or NULL on error
    3107   *
    3108   * Since: 2.33
    3109   */
    3110  struct libmnt_ns *mnt_context_switch_origin_ns(struct libmnt_context *cxt)
    3111  {
    3112  	return mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt));
    3113  }
    3114  
    3115  /**
    3116   * mnt_context_switch_target_ns:
    3117   * @cxt: mount context
    3118   *
    3119   * Switch to target namespace
    3120   *
    3121   * This is shorthand for
    3122   * <informalexample>
    3123   *   <programlisting>
    3124   *	mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt));
    3125   *   </programlisting>
    3126   * </informalexample>
    3127   *
    3128   * Returns: pointer to previous namespace or NULL on error
    3129   *
    3130   * Since: 2.33
    3131   */
    3132  struct libmnt_ns *mnt_context_switch_target_ns(struct libmnt_context *cxt)
    3133  {
    3134  	return mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt));
    3135  }
    3136  
    3137  
    3138  #ifdef TEST_PROGRAM
    3139  
    3140  static int test_search_helper(struct libmnt_test *ts, int argc, char *argv[])
    3141  {
    3142  	struct libmnt_context *cxt;
    3143  	const char *type;
    3144  	int rc;
    3145  
    3146  	if (argc < 2)
    3147  		return -EINVAL;
    3148  
    3149  	cxt = mnt_new_context();
    3150  	if (!cxt)
    3151  		return -ENOMEM;
    3152  
    3153  	type = argv[1];
    3154  
    3155  	mnt_context_get_fs(cxt);		/* just to fill cxt->fs */
    3156  	cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED;	/* fake */
    3157  
    3158  	rc = mnt_context_prepare_helper(cxt, "mount", type);
    3159  	printf("helper is: %s\n", cxt->helper ? cxt->helper : "not found");
    3160  
    3161  	mnt_free_context(cxt);
    3162  	return rc;
    3163  }
    3164  
    3165  
    3166  static struct libmnt_lock *lock;
    3167  
    3168  static void lock_fallback(void)
    3169  {
    3170  	if (lock)
    3171  		mnt_unlock_file(lock);
    3172  }
    3173  
    3174  static int test_mount(struct libmnt_test *ts, int argc, char *argv[])
    3175  {
    3176  	int idx = 1, rc = 0;
    3177  	struct libmnt_context *cxt;
    3178  
    3179  	if (argc < 2)
    3180  		return -EINVAL;
    3181  
    3182  	cxt = mnt_new_context();
    3183  	if (!cxt)
    3184  		return -ENOMEM;
    3185  
    3186  	if (!strcmp(argv[idx], "-o")) {
    3187  		mnt_context_set_options(cxt, argv[idx + 1]);
    3188  		idx += 2;
    3189  	}
    3190  	if (!strcmp(argv[idx], "-t")) {
    3191  		/* TODO: use mnt_context_set_fstype_pattern() */
    3192  		mnt_context_set_fstype(cxt, argv[idx + 1]);
    3193  		idx += 2;
    3194  	}
    3195  
    3196  	if (argc == idx + 1)
    3197  		/* mount <mountpoint>|<device> */
    3198  		mnt_context_set_target(cxt, argv[idx++]);
    3199  
    3200  	else if (argc == idx + 2) {
    3201  		/* mount <device> <mountpoint> */
    3202  		mnt_context_set_source(cxt, argv[idx++]);
    3203  		mnt_context_set_target(cxt, argv[idx++]);
    3204  	}
    3205  
    3206  	/* this is unnecessary! -- libmount is able to internally
    3207  	 * create and manage the lock
    3208  	 */
    3209  	lock = mnt_context_get_lock(cxt);
    3210  	if (lock)
    3211  		atexit(lock_fallback);
    3212  
    3213  	rc = mnt_context_mount(cxt);
    3214  	if (rc)
    3215  		warn("failed to mount");
    3216  	else
    3217  		printf("successfully mounted\n");
    3218  
    3219  	lock = NULL;	/* because we use atexit lock_fallback */
    3220  	mnt_free_context(cxt);
    3221  	return rc;
    3222  }
    3223  
    3224  static int test_umount(struct libmnt_test *ts, int argc, char *argv[])
    3225  {
    3226  	int idx = 1, rc = 0;
    3227  	struct libmnt_context *cxt;
    3228  
    3229  	if (argc < 2)
    3230  		return -EINVAL;
    3231  
    3232  	cxt = mnt_new_context();
    3233  	if (!cxt)
    3234  		return -ENOMEM;
    3235  
    3236  	if (!strcmp(argv[idx], "-t")) {
    3237  		mnt_context_set_fstype(cxt, argv[idx + 1]);
    3238  		idx += 2;
    3239  	}
    3240  
    3241  	if (!strcmp(argv[idx], "-f")) {
    3242  		mnt_context_enable_force(cxt, TRUE);
    3243  		idx++;
    3244  	}
    3245  
    3246  	if (!strcmp(argv[idx], "-l")) {
    3247  		mnt_context_enable_lazy(cxt, TRUE);
    3248  		idx++;
    3249  	}
    3250  
    3251  	if (!strcmp(argv[idx], "-r")) {
    3252  		mnt_context_enable_rdonly_umount(cxt, TRUE);
    3253  		idx++;
    3254  	}
    3255  
    3256  	if (argc == idx + 1) {
    3257  		/* mount <mountpoint>|<device> */
    3258  		mnt_context_set_target(cxt, argv[idx++]);
    3259  	} else {
    3260  		rc = -EINVAL;
    3261  		goto err;
    3262  	}
    3263  
    3264  	lock = mnt_context_get_lock(cxt);
    3265  	if (lock)
    3266  		atexit(lock_fallback);
    3267  
    3268  	rc = mnt_context_umount(cxt);
    3269  	if (rc)
    3270  		printf("failed to umount\n");
    3271  	else
    3272  		printf("successfully umounted\n");
    3273  err:
    3274  	lock = NULL;	/* because we use atexit lock_fallback */
    3275  	mnt_free_context(cxt);
    3276  	return rc;
    3277  }
    3278  
    3279  static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
    3280  {
    3281  	int idx = 1, rc = 0;
    3282  	struct libmnt_context *cxt;
    3283  	const char *opt = NULL;
    3284  	unsigned long flags = 0;
    3285  
    3286  	if (argc < 2)
    3287  		return -EINVAL;
    3288  
    3289  	cxt = mnt_new_context();
    3290  	if (!cxt)
    3291  		return -ENOMEM;
    3292  
    3293  	if (!strcmp(argv[idx], "-o")) {
    3294  		mnt_context_set_options(cxt, argv[idx + 1]);
    3295  		idx += 2;
    3296  	}
    3297  
    3298  	if (argc == idx + 1)
    3299  		/* mount <mountpoint>|<device> */
    3300  		mnt_context_set_target(cxt, argv[idx++]);
    3301  
    3302  	rc = mnt_context_prepare_mount(cxt);
    3303  	if (rc)
    3304  		printf("failed to prepare mount %s\n", strerror(-rc));
    3305  
    3306  	opt = mnt_fs_get_options(cxt->fs);
    3307  	if (opt)
    3308  		fprintf(stdout, "options: %s\n", opt);
    3309  
    3310  	mnt_context_get_mflags(cxt, &flags);
    3311  	fprintf(stdout, "flags: %08lx\n", flags);
    3312  
    3313  	mnt_free_context(cxt);
    3314  	return rc;
    3315  }
    3316  
    3317  static int test_mountall(struct libmnt_test *ts, int argc, char *argv[])
    3318  {
    3319  	struct libmnt_context *cxt;
    3320  	struct libmnt_iter *itr;
    3321  	struct libmnt_fs *fs;
    3322  	int mntrc, ignored, idx = 1;
    3323  
    3324  	cxt = mnt_new_context();
    3325  	itr = mnt_new_iter(MNT_ITER_FORWARD);
    3326  
    3327  	if (!cxt || !itr)
    3328  		return -ENOMEM;
    3329  
    3330  	if (argc > 2) {
    3331  		if (argv[idx] && !strcmp(argv[idx], "-O")) {
    3332  			mnt_context_set_options_pattern(cxt, argv[idx + 1]);
    3333  			idx += 2;
    3334  		}
    3335  		if (argv[idx] && !strcmp(argv[idx], "-t")) {
    3336  			mnt_context_set_fstype_pattern(cxt, argv[idx + 1]);
    3337  			idx += 2;
    3338  		}
    3339  	}
    3340  
    3341  	while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
    3342  
    3343  		const char *tgt = mnt_fs_get_target(fs);
    3344  
    3345  		if (ignored == 1)
    3346  			printf("%s: ignored: not match\n", tgt);
    3347  		else if (ignored == 2)
    3348  			printf("%s: ignored: already mounted\n", tgt);
    3349  
    3350  		else if (!mnt_context_get_status(cxt)) {
    3351  			if (mntrc > 0) {
    3352  				errno = mntrc;
    3353  				warn("%s: mount failed", tgt);
    3354  			} else
    3355  				warnx("%s: mount failed", tgt);
    3356  		} else
    3357  			printf("%s: successfully mounted\n", tgt);
    3358  	}
    3359  
    3360  	mnt_free_context(cxt);
    3361  	return 0;
    3362  }
    3363  
    3364  int main(int argc, char *argv[])
    3365  {
    3366  	struct libmnt_test tss[] = {
    3367  	{ "--mount",  test_mount,  "[-o <opts>] [-t <type>] <spec>|<src> <target>" },
    3368  	{ "--umount", test_umount, "[-t <type>] [-f][-l][-r] <src>|<target>" },
    3369  	{ "--mount-all", test_mountall,  "[-O <pattern>] [-t <pattern] mount all filesystems from fstab" },
    3370  	{ "--flags", test_flags,   "[-o <opts>] <spec>" },
    3371  	{ "--search-helper", test_search_helper, "<fstype>" },
    3372  	{ NULL }};
    3373  
    3374  	umask(S_IWGRP|S_IWOTH);	/* to be compatible with mount(8) */
    3375  
    3376  	return mnt_run_test(tss, argc, argv);
    3377  }
    3378  
    3379  #endif /* TEST_PROGRAM */