(root)/
util-linux-2.39/
libmount/
src/
context_umount.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-umount
      15   * @title: Umount context
      16   * @short_description: high-level API to umount operation.
      17   */
      18  
      19  #include <sys/wait.h>
      20  #include <sys/mount.h>
      21  
      22  #include "pathnames.h"
      23  #include "loopdev.h"
      24  #include "strutils.h"
      25  #include "mountP.h"
      26  
      27  /*
      28   * umount2 flags
      29   */
      30  #ifndef MNT_FORCE
      31  # define MNT_FORCE        0x00000001	/* Attempt to forcibly umount */
      32  #endif
      33  
      34  #ifndef MNT_DETACH
      35  # define MNT_DETACH       0x00000002	/* Just detach from the tree */
      36  #endif
      37  
      38  #ifndef UMOUNT_NOFOLLOW
      39  # define UMOUNT_NOFOLLOW  0x00000008	/* Don't follow symlink on umount */
      40  #endif
      41  
      42  #ifndef UMOUNT_UNUSED
      43  # define UMOUNT_UNUSED    0x80000000	/* Flag guaranteed to be unused */
      44  #endif
      45  
      46  /* search in mountinfo */
      47  static int __mountinfo_find_umount_fs(struct libmnt_context *cxt,
      48  			    const char *tgt,
      49  			    struct libmnt_fs **pfs)
      50  {
      51  	int rc;
      52  	struct libmnt_ns *ns_old;
      53  	struct libmnt_table *mountinfo = NULL;
      54  	struct libmnt_fs *fs;
      55  	char *loopdev = NULL;
      56  
      57  	assert(cxt);
      58  	assert(tgt);
      59  	assert(pfs);
      60  
      61  	*pfs = NULL;
      62  	DBG(CXT, ul_debugobj(cxt, " search %s in mountinfo", tgt));
      63  
      64  	/*
      65  	 * The mount table may be huge, and on systems with utab we have to
      66  	 * merge userspace mount options into /proc/self/mountinfo. This all is
      67  	 * expensive. The tab filter can be used to filter out entries, then a mount
      68  	 * table and utab are very tiny files.
      69  	 *
      70  	 * The filter uses mnt_fs_streq_{target,srcpath} function where all
      71  	 * paths should be absolute and canonicalized. This is done within
      72  	 * mnt_context_get_mountinfo_for_target() where LABEL, UUID or symlinks are
      73  	 * canonicalized. If --no-canonicalize is enabled than the target path
      74  	 * is expected already canonical.
      75  	 *
      76  	 * Anyway it's better to read huge mount table than canonicalize target
      77  	 * paths. It means we use the filter only if --no-canonicalize enabled.
      78  	 *
      79  	 * It also means that we have to read mount table from kernel.
      80  	 */
      81  	if (mnt_context_is_nocanonicalize(cxt) && *tgt == '/')
      82  		rc = mnt_context_get_mountinfo_for_target(cxt, &mountinfo, tgt);
      83  	else
      84  		rc = mnt_context_get_mountinfo(cxt, &mountinfo);
      85  
      86  	if (rc) {
      87  		DBG(CXT, ul_debugobj(cxt, "umount: failed to read mountinfo"));
      88  		return rc;
      89  	}
      90  
      91  	if (mnt_table_get_nents(mountinfo) == 0) {
      92  		DBG(CXT, ul_debugobj(cxt, "umount: mountinfo empty"));
      93  		return 1;
      94  	}
      95  
      96  	ns_old = mnt_context_switch_target_ns(cxt);
      97  	if (!ns_old)
      98  		return -MNT_ERR_NAMESPACE;
      99  
     100  try_loopdev:
     101  	fs = mnt_table_find_target(mountinfo, tgt, MNT_ITER_BACKWARD);
     102  	if (!fs && mnt_context_is_swapmatch(cxt)) {
     103  		/*
     104  		 * Maybe the option is source rather than target (sometimes
     105  		 * people use e.g. "umount /dev/sda1")
     106  		 */
     107  		fs = mnt_table_find_source(mountinfo, tgt, MNT_ITER_BACKWARD);
     108  
     109  		if (fs) {
     110  			struct libmnt_fs *fs1 = mnt_table_find_target(mountinfo,
     111  							mnt_fs_get_target(fs),
     112  							MNT_ITER_BACKWARD);
     113  			if (!fs1) {
     114  				DBG(CXT, ul_debugobj(cxt, "mountinfo is broken?!?!"));
     115  				rc = -EINVAL;
     116  				goto err;
     117  			}
     118  			if (fs != fs1) {
     119  				/* Something was stacked over `file' on the
     120  				 * same mount point. */
     121  				DBG(CXT, ul_debugobj(cxt,
     122  						"umount: %s: %s is mounted "
     123  						"over it on the same point",
     124  						tgt, mnt_fs_get_source(fs1)));
     125  				rc = -EINVAL;
     126  				goto err;
     127  			}
     128  		}
     129  	}
     130  
     131  	if (!fs && !loopdev && mnt_context_is_swapmatch(cxt)) {
     132  		/*
     133  		 * Maybe the option is /path/file.img, try to convert to /dev/loopN
     134  		 */
     135  		struct stat st;
     136  
     137  		if (mnt_safe_stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) {
     138  			int count;
     139  			struct libmnt_cache *cache = mnt_context_get_cache(cxt);
     140  			const char *bf = cache ? mnt_resolve_path(tgt, cache) : tgt;
     141  
     142  			count = loopdev_count_by_backing_file(bf, &loopdev);
     143  			if (count == 1) {
     144  				DBG(CXT, ul_debugobj(cxt,
     145  					"umount: %s --> %s (retry)", tgt, loopdev));
     146  				tgt = loopdev;
     147  				goto try_loopdev;
     148  
     149  			} else if (count > 1)
     150  				DBG(CXT, ul_debugobj(cxt,
     151  					"umount: warning: %s is associated "
     152  					"with more than one loopdev", tgt));
     153  		}
     154  	}
     155  
     156  	*pfs = fs;
     157  	free(loopdev);
     158  	if (!mnt_context_switch_ns(cxt, ns_old))
     159  		return -MNT_ERR_NAMESPACE;
     160  
     161  	DBG(CXT, ul_debugobj(cxt, "umount fs: %s", fs ? mnt_fs_get_target(fs) :
     162  							"<not found>"));
     163  	return fs ? 0 : 1;
     164  err:
     165  	free(loopdev);
     166  	if (!mnt_context_switch_ns(cxt, ns_old))
     167  		return -MNT_ERR_NAMESPACE;
     168  	return rc;
     169  }
     170  
     171  /**
     172   * mnt_context_find_umount_fs:
     173   * @cxt: mount context
     174   * @tgt: mountpoint, device, ...
     175   * @pfs: returns point to filesystem
     176   *
     177   * Returns: 0 on success, <0 on error, 1 if target filesystem not found
     178   */
     179  int mnt_context_find_umount_fs(struct libmnt_context *cxt,
     180  			       const char *tgt,
     181  			       struct libmnt_fs **pfs)
     182  {
     183  	if (pfs)
     184  		*pfs = NULL;
     185  
     186  	if (!cxt || !tgt || !pfs)
     187  		return -EINVAL;
     188  
     189  	DBG(CXT, ul_debugobj(cxt, "umount: lookup FS for '%s'", tgt));
     190  
     191  	if (!*tgt)
     192  		return 1; /* empty string is not an error */
     193  
     194  	/* In future this function should be extended to support for example
     195  	 * fsinfo() (or another cheap way kernel will support), for now the
     196  	 * default is expensive mountinfo.
     197  	 */
     198  	return __mountinfo_find_umount_fs(cxt, tgt, pfs);
     199  }
     200  
     201  /* Check if there is something important in the utab file. The parsed utab is
     202   * stored in context->utab and deallocated by mnt_free_context().
     203   *
     204   * This function exists to avoid (if possible) /proc/self/mountinfo usage, so
     205   * don't use things like mnt_resolve_target(), mnt_context_get_mountinfo() etc here.
     206   * See lookup_umount_fs() for more details.
     207   */
     208  static int has_utab_entry(struct libmnt_context *cxt, const char *target)
     209  {
     210  	struct libmnt_cache *cache = NULL;
     211  	struct libmnt_fs *fs;
     212  	struct libmnt_iter itr;
     213  	char *cn = NULL;
     214  	int rc = 0;
     215  
     216  	assert(cxt);
     217  
     218  	if (!cxt->utab) {
     219  		const char *path = mnt_get_utab_path();
     220  
     221  		if (!path || is_file_empty(path))
     222  			return 0;
     223  		cxt->utab = mnt_new_table();
     224  		if (!cxt->utab)
     225  			return 0;
     226  		cxt->utab->fmt = MNT_FMT_UTAB;
     227  		if (mnt_table_parse_file(cxt->utab, path))
     228  			return 0;
     229  	}
     230  
     231  	/* paths in utab are canonicalized */
     232  	cache = mnt_context_get_cache(cxt);
     233  	cn = mnt_resolve_path(target, cache);
     234  	mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
     235  
     236  	while (mnt_table_next_fs(cxt->utab, &itr, &fs) == 0) {
     237  		if (mnt_fs_streq_target(fs, cn)) {
     238  			rc = 1;
     239  			break;
     240  		}
     241  	}
     242  
     243  	if (!cache)
     244  		free(cn);
     245  	return rc;
     246  }
     247  
     248  /* returns: 1 not found; <0 on error; 1 success */
     249  static int lookup_umount_fs_by_statfs(struct libmnt_context *cxt, const char *tgt)
     250  {
     251  	struct stat st;
     252  	const char *type;
     253  
     254  	assert(cxt);
     255  	assert(cxt->fs);
     256  
     257  	DBG(CXT, ul_debugobj(cxt, " lookup by statfs"));
     258  
     259  	/*
     260  	 * Let's try to avoid mountinfo usage at all to minimize performance
     261  	 * degradation. Don't forget that kernel has to compose *whole*
     262  	 * mountinfo about all mountpoints although we look for only one entry.
     263  	 *
     264  	 * All we need is fstype and to check if there is no userspace mount
     265  	 * options for the target (e.g. helper=udisks to call /sbin/umount.udisks).
     266  	 *
     267  	 * So, let's use statfs() if possible (it's bad idea for --lazy/--force
     268  	 * umounts as target is probably unreachable NFS, also for --detach-loop
     269  	 * as this additionally needs to know the name of the loop device).
     270  	 */
     271  	if (mnt_context_is_restricted(cxt)
     272  	    || *tgt != '/'
     273  	    || (cxt->flags & MNT_FL_HELPER)
     274  	    || mnt_context_is_force(cxt)
     275  	    || mnt_context_is_lazy(cxt)
     276  	    || mnt_context_is_nocanonicalize(cxt)
     277  	    || mnt_context_is_loopdel(cxt)
     278  	    || mnt_safe_stat(tgt, &st) != 0 || !S_ISDIR(st.st_mode)
     279  	    || has_utab_entry(cxt, tgt))
     280  		return 1; /* not found */
     281  
     282  	type = mnt_fs_get_fstype(cxt->fs);
     283  	if (!type) {
     284  		struct statfs vfs;
     285  		int fd;
     286  
     287  		DBG(CXT, ul_debugobj(cxt, "  trying fstatfs()"));
     288  
     289  		/* O_PATH avoids triggering automount points. */
     290  		fd = open(tgt, O_PATH);
     291  		if (fd >= 0) {
     292  			if (fstatfs(fd, &vfs) == 0)
     293  				type = mnt_statfs_get_fstype(&vfs);
     294  			close(fd);
     295  		}
     296  		if (type) {
     297  			int rc = mnt_fs_set_fstype(cxt->fs, type);
     298  			if (rc)
     299  				return rc;
     300  		}
     301  	}
     302  	if (type) {
     303  		DBG(CXT, ul_debugobj(cxt, "  umount: disabling mountinfo"));
     304  		mnt_context_disable_mtab(cxt, TRUE);
     305  
     306  		DBG(CXT, ul_debugobj(cxt,
     307  			"  mountinfo unnecessary [type=%s]", type));
     308  		return 0;
     309  	}
     310  
     311  	return 1; /* not found */
     312  }
     313  
     314  /* returns: 1 not found; <0 on error; 1 success */
     315  static int lookup_umount_fs_by_mountinfo(struct libmnt_context *cxt, const char *tgt)
     316  {
     317  	struct libmnt_fs *fs = NULL;
     318  	int rc;
     319  
     320  	assert(cxt);
     321  	assert(cxt->fs);
     322  
     323  	DBG(CXT, ul_debugobj(cxt, " lookup by mountinfo"));
     324  
     325  	/* search */
     326  	rc = __mountinfo_find_umount_fs(cxt, tgt, &fs);
     327  	if (rc != 0)
     328  		return rc;
     329  
     330  	/* apply result */
     331  	if (fs != cxt->fs) {
     332  		mnt_fs_set_source(cxt->fs, NULL);
     333  		mnt_fs_set_target(cxt->fs, NULL);
     334  
     335  		if (!mnt_copy_fs(cxt->fs, fs)) {
     336  			DBG(CXT, ul_debugobj(cxt, "  failed to copy FS"));
     337  			return -errno;
     338  		}
     339  		DBG(CXT, ul_debugobj(cxt, "  mountinfo applied"));
     340  	}
     341  
     342  	cxt->flags |= MNT_FL_TAB_APPLIED;
     343  	return 0;
     344  }
     345  
     346  /* This function searchs for FS according to cxt->fs->target,
     347   * apply result to cxt->fs and it's umount replacement to
     348   * mnt_context_apply_fstab(), use mnt_context_tab_applied()
     349   * to check result.
     350   *
     351   * The goal is to minimize situations when we need to parse
     352   * /proc/self/mountinfo.
     353   */
     354  static int lookup_umount_fs(struct libmnt_context *cxt)
     355  {
     356  	const char *tgt;
     357  	int rc = 0;
     358  
     359  	assert(cxt);
     360  	assert(cxt->fs);
     361  
     362  	DBG(CXT, ul_debugobj(cxt, "umount: lookup FS"));
     363  
     364  	tgt = mnt_fs_get_target(cxt->fs);
     365  	if (!tgt) {
     366  		DBG(CXT, ul_debugobj(cxt, " undefined target"));
     367  		return -EINVAL;
     368  	}
     369  
     370  	/* try get fs type by statfs() */
     371  	rc = lookup_umount_fs_by_statfs(cxt, tgt);
     372  	if (rc <= 0)
     373  		goto done;
     374  
     375  	/* get complete fs from fs entry from mountinfo */
     376  	rc = lookup_umount_fs_by_mountinfo(cxt, tgt);
     377  	if (rc <= 0)
     378  		goto done;
     379  
     380  	DBG(CXT, ul_debugobj(cxt, " cannot find '%s'", tgt));
     381  	return 0;	/* this is correct! */
     382  
     383  done:
     384  	if (rc == 0 && cxt->fs) {
     385  		struct libmnt_optlist *ol = mnt_context_get_optlist(cxt);
     386  
     387  		if (!ol)
     388  			return -ENOMEM;
     389  
     390  		rc = mnt_optlist_set_optstr(ol, mnt_fs_get_options(cxt->fs), NULL);
     391  	}
     392  	DBG(CXT, ul_debugobj(cxt, "  lookup done [rc=%d]", rc));
     393  	return rc;
     394  }
     395  
     396  /* check if @devname is loopdev and if the device is associated
     397   * with a source from @fstab_fs
     398   */
     399  static int is_associated_fs(const char *devname, struct libmnt_fs *fs)
     400  {
     401  	uintmax_t offset = 0;
     402  	const char *src, *optstr;
     403  	char *val;
     404  	size_t valsz;
     405  	int flags = 0;
     406  
     407  	/* check if it begins with /dev/loop */
     408  	if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP) - 1) != 0)
     409  		return 0;
     410  
     411  	src = mnt_fs_get_srcpath(fs);
     412  	if (!src)
     413  		return 0;
     414  
     415  	/* check for the offset option in @fs */
     416  	optstr = mnt_fs_get_user_options(fs);
     417  
     418  	if (optstr &&
     419  	    mnt_optstr_get_option(optstr, "offset", &val, &valsz) == 0) {
     420  		flags |= LOOPDEV_FL_OFFSET;
     421  
     422  		if (mnt_parse_offset(val, valsz, &offset) != 0)
     423  			return 0;
     424  	}
     425  
     426  	return loopdev_is_used(devname, src, offset, 0, flags);
     427  }
     428  
     429  /* returns: <0 on error; 1 not found (not wanted) */
     430  static int prepare_helper_from_option(struct libmnt_context *cxt,
     431  				       const char *name)
     432  {
     433  	struct libmnt_optlist *ol;
     434  	struct libmnt_opt *opt;
     435  	const char *suffix;
     436  
     437  	ol = mnt_context_get_optlist(cxt);
     438  	if (!ol)
     439  		return -ENOMEM;
     440  
     441  	opt = mnt_optlist_get_named(ol, name, cxt->map_userspace);
     442  	if (!opt || !mnt_opt_has_value(opt))
     443  		return 1;
     444  
     445  	suffix = mnt_opt_get_value(opt);
     446  	DBG(CXT, ul_debugobj(cxt, "umount: umount.%s %s requested", suffix, name));
     447  
     448  	return mnt_context_prepare_helper(cxt, "umount", suffix);
     449  }
     450  
     451  static int is_fuse_usermount(struct libmnt_context *cxt, int *errsv)
     452  {
     453  	struct libmnt_ns *ns_old;
     454  	struct libmnt_optlist *ol;
     455  	struct libmnt_opt *opt;
     456  	const char *type = mnt_fs_get_fstype(cxt->fs);
     457  	const char *val = NULL;;
     458  	uid_t uid, entry_uid;
     459  
     460  	*errsv = 0;
     461  
     462  	if (!type)
     463  		return 0;
     464  
     465  	if (strcmp(type, "fuse") != 0 &&
     466  	    strcmp(type, "fuseblk") != 0 &&
     467  	    strncmp(type, "fuse.", 5) != 0 &&
     468  	    strncmp(type, "fuseblk.", 8) != 0)
     469  		return 0;
     470  
     471  	ol = mnt_context_get_optlist(cxt);
     472  	if (!ol)
     473  		return 0;
     474  
     475  	opt = mnt_optlist_get_named(ol, "user_id", NULL);
     476  	if (opt)
     477  		val = mnt_opt_get_value(opt);
     478  	if (!val || mnt_opt_get_map(opt))
     479  		return 0;
     480  
     481  	if (mnt_parse_uid(val, strlen(val), &entry_uid) != 0)
     482  		return 0;
     483  
     484  	/* get current user */
     485  	ns_old = mnt_context_switch_origin_ns(cxt);
     486  	if (!ns_old) {
     487  		*errsv = -MNT_ERR_NAMESPACE;
     488  		return 0;
     489  	}
     490  
     491  	uid = getuid();
     492  
     493  	if (!mnt_context_switch_ns(cxt, ns_old)) {
     494  		*errsv = -MNT_ERR_NAMESPACE;
     495  		return 0;
     496  	}
     497  
     498  	return uid == entry_uid;
     499  }
     500  
     501  /*
     502   * Note that cxt->fs contains relevant mountinfo entry!
     503   */
     504  static int evaluate_permissions(struct libmnt_context *cxt)
     505  {
     506  	unsigned long fstab_flags = 0;
     507  	struct libmnt_table *fstab;
     508  	const char *tgt, *src, *optstr;
     509  	int rc = 0, ok = 0;
     510  	struct libmnt_fs *fs;
     511  
     512  	assert(cxt);
     513  	assert(cxt->fs);
     514  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     515  
     516  	if (!mnt_context_is_restricted(cxt))
     517  		 return 0;		/* superuser mount */
     518  
     519  	DBG(CXT, ul_debugobj(cxt, "umount: evaluating permissions"));
     520  
     521  	if (!mnt_context_tab_applied(cxt)) {
     522  		DBG(CXT, ul_debugobj(cxt,
     523  				"cannot find %s in mountinfo and you are not root",
     524  				mnt_fs_get_target(cxt->fs)));
     525  		goto eperm;
     526  	}
     527  
     528  	if (!mnt_context_is_nohelpers(cxt)) {
     529  		rc = prepare_helper_from_option(cxt, "uhelper");
     530  		if (rc < 0)
     531  			return rc;	/* error */
     532  		if (rc == 0 && cxt->helper)
     533  			return 0;	/* we'll call /sbin/umount.<uhelper> */
     534  	}
     535  
     536  	/*
     537  	 * Check if this is a fuse mount for the current user,
     538  	 * if so then unmounting is allowed
     539  	 */
     540  	if (is_fuse_usermount(cxt, &rc)) {
     541  		DBG(CXT, ul_debugobj(cxt, "fuse user mount, umount is allowed"));
     542  		return 0;
     543  	}
     544  	if (rc)
     545  		return rc;
     546  
     547  	/*
     548  	 * User mounts have to be in /etc/fstab
     549  	 */
     550  	rc = mnt_context_get_fstab(cxt, &fstab);
     551  	if (rc)
     552  		return rc;
     553  
     554  	tgt = mnt_fs_get_target(cxt->fs);
     555  	src = mnt_fs_get_source(cxt->fs);
     556  
     557  	if (mnt_fs_get_bindsrc(cxt->fs)) {
     558  		src = mnt_fs_get_bindsrc(cxt->fs);
     559  		DBG(CXT, ul_debugobj(cxt,
     560  				"umount: using bind source: %s", src));
     561  	}
     562  
     563  	/* If fstab contains the two lines
     564  	 *	/dev/sda1 /mnt/zip auto user,noauto  0 0
     565  	 *	/dev/sda4 /mnt/zip auto user,noauto  0 0
     566  	 * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
     567  	 * So, we must not look for the file, but for the pair (dev,file) in fstab.
     568  	  */
     569  	fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
     570  	if (!fs) {
     571  		/*
     572  		 * It's possible that there is /path/file.img in fstab and
     573  		 * /dev/loop0 in mountinfo -- then we have to check the relation
     574  		 * between loopdev and the file.
     575  		 */
     576  		fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD);
     577  		if (fs) {
     578  			struct libmnt_cache *cache = mnt_context_get_cache(cxt);
     579  			const char *sp = mnt_fs_get_srcpath(cxt->fs);		/* devname from mountinfo */
     580  			const char *dev = sp && cache ? mnt_resolve_path(sp, cache) : sp;
     581  
     582  			if (!dev || !is_associated_fs(dev, fs))
     583  				fs = NULL;
     584  		}
     585  		if (!fs) {
     586  			DBG(CXT, ul_debugobj(cxt,
     587  					"umount %s: mountinfo disagrees with fstab",
     588  					tgt));
     589  			goto eperm;
     590  		}
     591  	}
     592  
     593  	/*
     594  	 * User mounting and unmounting is allowed only if fstab contains one
     595  	 * of the options `user', `users' or `owner' or `group'.
     596  	 *
     597  	 * The option `users' allows arbitrary users to mount and unmount -
     598  	 * this may be a security risk.
     599  	 *
     600  	 * The options `user', `owner' and `group' only allow unmounting by the
     601  	 * user that mounted (visible in mountinfo).
     602  	 */
     603  	optstr = mnt_fs_get_user_options(fs);	/* FSTAB mount options! */
     604  	if (!optstr)
     605  		goto eperm;
     606  
     607  	if (mnt_optstr_get_flags(optstr, &fstab_flags,
     608  				mnt_get_builtin_optmap(MNT_USERSPACE_MAP)))
     609  		goto eperm;
     610  
     611  	if (fstab_flags & MNT_MS_USERS) {
     612  		DBG(CXT, ul_debugobj(cxt,
     613  			"umount: promiscuous setting ('users') in fstab"));
     614  		return 0;
     615  	}
     616  	/*
     617  	 * Check user=<username> setting from utab if there is a user, owner or
     618  	 * group option in /etc/fstab
     619  	 */
     620  	if (fstab_flags & (MNT_MS_USER | MNT_MS_OWNER | MNT_MS_GROUP)) {
     621  
     622  		struct libmnt_optlist *ol;
     623  		struct libmnt_opt *opt;
     624  		char *curr_user = NULL;
     625  		struct libmnt_ns *ns_old;
     626  
     627  		DBG(CXT, ul_debugobj(cxt,
     628  				"umount: checking user=<username> from mountinfo"));
     629  
     630  		ns_old = mnt_context_switch_origin_ns(cxt);
     631  		if (!ns_old)
     632  			return -MNT_ERR_NAMESPACE;
     633  
     634  		curr_user = mnt_get_username(getuid());
     635  
     636  		if (!mnt_context_switch_ns(cxt, ns_old)) {
     637  			free(curr_user);
     638  			return -MNT_ERR_NAMESPACE;
     639  		}
     640  		if (!curr_user) {
     641  			DBG(CXT, ul_debugobj(cxt, "umount %s: cannot "
     642  				"convert %d to username", tgt, getuid()));
     643  			goto eperm;
     644  		}
     645  
     646  		/* get "user=" from utab */
     647  		ol = mnt_context_get_optlist(cxt);
     648  		if (!ol) {
     649  			free(curr_user);
     650  			return -ENOMEM;
     651  		}
     652  		opt = mnt_optlist_get_named(ol, "user", cxt->map_userspace);
     653  		if (opt && mnt_opt_has_value(opt))
     654  			ok = !strcmp(curr_user, mnt_opt_get_value(opt));
     655  
     656  		free(curr_user);
     657  	}
     658  
     659  	if (ok) {
     660  		DBG(CXT, ul_debugobj(cxt, "umount %s is allowed", tgt));
     661  		return 0;
     662  	}
     663  eperm:
     664  	DBG(CXT, ul_debugobj(cxt, "umount is not allowed for you"));
     665  	return -EPERM;
     666  }
     667  
     668  static int exec_helper(struct libmnt_context *cxt)
     669  {
     670  	char *namespace = NULL;
     671  	struct libmnt_ns *ns_tgt = mnt_context_get_target_ns(cxt);
     672  	int rc;
     673  	pid_t pid;
     674  
     675  	assert(cxt);
     676  	assert(cxt->fs);
     677  	assert(cxt->helper);
     678  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     679  	assert(cxt->helper_exec_status == 1);
     680  
     681  	if (mnt_context_is_fake(cxt)) {
     682  		DBG(CXT, ul_debugobj(cxt, "fake mode: does not execute helper"));
     683  		cxt->helper_exec_status = rc = 0;
     684  		return rc;
     685  	}
     686  
     687  	if (ns_tgt->fd != -1
     688  	    && asprintf(&namespace, "/proc/%i/fd/%i",
     689  			getpid(), ns_tgt->fd) == -1) {
     690  		return -ENOMEM;
     691  	}
     692  
     693  	DBG_FLUSH;
     694  
     695  	pid = fork();
     696  	switch (pid) {
     697  	case 0:
     698  	{
     699  		const char *args[12], *type;
     700  		int i = 0;
     701  
     702  		if (drop_permissions() != 0)
     703  			_exit(EXIT_FAILURE);
     704  
     705  		if (!mnt_context_switch_origin_ns(cxt))
     706  			_exit(EXIT_FAILURE);
     707  
     708  		type = mnt_fs_get_fstype(cxt->fs);
     709  
     710  		args[i++] = cxt->helper;			/* 1 */
     711  		args[i++] = mnt_fs_get_target(cxt->fs);		/* 2 */
     712  
     713  		if (mnt_context_is_nomtab(cxt))
     714  			args[i++] = "-n";			/* 3 */
     715  		if (mnt_context_is_lazy(cxt))
     716  			args[i++] = "-l";			/* 4 */
     717  		if (mnt_context_is_force(cxt))
     718  			args[i++] = "-f";			/* 5 */
     719  		if (mnt_context_is_verbose(cxt))
     720  			args[i++] = "-v";			/* 6 */
     721  		if (mnt_context_is_rdonly_umount(cxt))
     722  			args[i++] = "-r";			/* 7 */
     723  		if (type
     724  		    && strchr(type, '.')
     725  		    && !endswith(cxt->helper, type)) {
     726  			args[i++] = "-t";			/* 8 */
     727  			args[i++] = type;			/* 9 */
     728  		}
     729  		if (namespace) {
     730  			args[i++] = "-N";			/* 10 */
     731  			args[i++] = namespace;			/* 11 */
     732  		}
     733  
     734  		args[i] = NULL;					/* 12 */
     735  		for (i = 0; args[i]; i++)
     736  			DBG(CXT, ul_debugobj(cxt, "argv[%d] = \"%s\"",
     737  							i, args[i]));
     738  		DBG_FLUSH;
     739  		execv(cxt->helper, (char * const *) args);
     740  		_exit(EXIT_FAILURE);
     741  	}
     742  	default:
     743  	{
     744  		int st;
     745  
     746  		if (waitpid(pid, &st, 0) == (pid_t) -1) {
     747  			cxt->helper_status = -1;
     748  			rc = -errno;
     749  		} else {
     750  			cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
     751  			cxt->helper_exec_status = rc = 0;
     752  		}
     753  		DBG(CXT, ul_debugobj(cxt, "%s executed [status=%d, rc=%d%s]",
     754  				cxt->helper,
     755  				cxt->helper_status, rc,
     756  				rc ? " waitpid failed" : ""));
     757  		break;
     758  	}
     759  
     760  	case -1:
     761  		cxt->helper_exec_status = rc = -errno;
     762  		DBG(CXT, ul_debugobj(cxt, "fork() failed"));
     763  		break;
     764  	}
     765  
     766  	free(namespace);
     767  	return rc;
     768  }
     769  
     770  /*
     771   * mnt_context_helper_setopt() backend.
     772   *
     773   * This function applies umount.type command line option (for example parsed
     774   * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
     775   * then 1 is returned.
     776   *
     777   * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
     778   */
     779  int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg)
     780  {
     781  	int rc = -EINVAL;
     782  
     783  	assert(cxt);
     784  	assert(cxt->action == MNT_ACT_UMOUNT);
     785  
     786  	switch(c) {
     787  	case 'n':
     788  		rc = mnt_context_disable_mtab(cxt, TRUE);
     789  		break;
     790  	case 'l':
     791  		rc = mnt_context_enable_lazy(cxt, TRUE);
     792  		break;
     793  	case 'f':
     794  		rc = mnt_context_enable_force(cxt, TRUE);
     795  		break;
     796  	case 'v':
     797  		rc = mnt_context_enable_verbose(cxt, TRUE);
     798  		break;
     799  	case 'r':
     800  		rc = mnt_context_enable_rdonly_umount(cxt, TRUE);
     801  		break;
     802  	case 't':
     803  		if (arg)
     804  			rc = mnt_context_set_fstype(cxt, arg);
     805  		break;
     806  	case 'N':
     807  		if (arg)
     808  			rc = mnt_context_set_target_ns(cxt, arg);
     809  		break;
     810  	default:
     811  		return 1;
     812  	}
     813  
     814  	return rc;
     815  }
     816  
     817  /* Check whether the kernel supports the UMOUNT_NOFOLLOW flag */
     818  static int umount_nofollow_support(void)
     819  {
     820  	int res = umount2("", UMOUNT_UNUSED);
     821  	if (res != -1 || errno != EINVAL)
     822  		return 0;
     823  
     824  	res = umount2("", UMOUNT_NOFOLLOW);
     825  	if (res != -1 || errno != ENOENT)
     826  		return 0;
     827  
     828  	return 1;
     829  }
     830  
     831  static int do_umount(struct libmnt_context *cxt)
     832  {
     833  	int rc = 0, flags = 0;
     834  	const char *src, *target;
     835  	char *tgtbuf = NULL;
     836  
     837  	assert(cxt);
     838  	assert(cxt->fs);
     839  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
     840  	assert(cxt->syscall_status == 1);
     841  
     842  	if (cxt->helper)
     843  		return exec_helper(cxt);
     844  
     845  	src = mnt_fs_get_srcpath(cxt->fs);
     846  	target = mnt_fs_get_target(cxt->fs);
     847  
     848  	if (!target)
     849  		return -EINVAL;
     850  
     851  	DBG(CXT, ul_debugobj(cxt, "do umount"));
     852  
     853  	if (mnt_context_is_restricted(cxt) && !mnt_context_is_fake(cxt)) {
     854  		/*
     855  		 * extra paranoia for non-root users
     856  		 * -- chdir to the parent of the mountpoint and use NOFOLLOW
     857  		 *    flag to avoid races and symlink attacks.
     858  		 */
     859  		if (umount_nofollow_support())
     860  			flags |= UMOUNT_NOFOLLOW;
     861  
     862  		rc = mnt_chdir_to_parent(target, &tgtbuf);
     863  		if (rc)
     864  			return rc;
     865  		target = tgtbuf;
     866  	}
     867  
     868  	if (mnt_context_is_lazy(cxt))
     869  		flags |= MNT_DETACH;
     870  
     871  	if (mnt_context_is_force(cxt))
     872  		flags |= MNT_FORCE;
     873  
     874  	DBG(CXT, ul_debugobj(cxt, "umount(2) [target='%s', flags=0x%08x]%s",
     875  				target, flags,
     876  				mnt_context_is_fake(cxt) ? " (FAKE)" : ""));
     877  
     878  	if (mnt_context_is_fake(cxt))
     879  		rc = 0;
     880  	else {
     881  		rc = flags ? umount2(target, flags) : umount(target);
     882  		if (rc < 0)
     883  			cxt->syscall_status = -errno;
     884  		free(tgtbuf);
     885  	}
     886  
     887  	/*
     888  	 * try remount read-only
     889  	 */
     890  	if (rc < 0
     891  	    && cxt->syscall_status == -EBUSY
     892  	    && mnt_context_is_rdonly_umount(cxt)
     893  	    && src) {
     894  		struct libmnt_optlist *ol = mnt_context_get_optlist(cxt);
     895  
     896  		/* keep info about remount in mount flags */
     897  		assert(ol);
     898  		mnt_optlist_append_flags(ol, MS_REMOUNT | MS_RDONLY, cxt->map_linux);
     899  
     900  		mnt_context_enable_loopdel(cxt, FALSE);
     901  
     902  		DBG(CXT, ul_debugobj(cxt,
     903  			"umount(2) failed [errno=%d] -- trying to remount read-only",
     904  			-cxt->syscall_status));
     905  
     906  		rc = mount(src, mnt_fs_get_target(cxt->fs), NULL,
     907  			    MS_REMOUNT | MS_RDONLY, NULL);
     908  		if (rc < 0) {
     909  			cxt->syscall_status = -errno;
     910  			DBG(CXT, ul_debugobj(cxt,
     911  				"read-only re-mount(2) failed [errno=%d]",
     912  				-cxt->syscall_status));
     913  
     914  			return -cxt->syscall_status;
     915  		}
     916  		cxt->syscall_status = 0;
     917  		DBG(CXT, ul_debugobj(cxt, "read-only re-mount(2) success"));
     918  		return 0;
     919  	}
     920  
     921  	if (rc < 0) {
     922  		DBG(CXT, ul_debugobj(cxt, "umount(2) failed [errno=%d]",
     923  			-cxt->syscall_status));
     924  		return -cxt->syscall_status;
     925  	}
     926  
     927  	cxt->syscall_status = 0;
     928  	DBG(CXT, ul_debugobj(cxt, "umount(2) success"));
     929  	return 0;
     930  }
     931  
     932  /**
     933   * mnt_context_prepare_umount:
     934   * @cxt: mount context
     935   *
     936   * Prepare context for umounting, unnecessary for mnt_context_umount().
     937   *
     938   * Returns: 0 on success, and negative number in case of error.
     939   */
     940  int mnt_context_prepare_umount(struct libmnt_context *cxt)
     941  {
     942  	int rc;
     943  	unsigned long flags = 0;
     944  	struct libmnt_ns *ns_old;
     945  
     946  	if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
     947  		return -EINVAL;
     948  	if (!mnt_context_get_source(cxt) && !mnt_context_get_target(cxt))
     949  		return -EINVAL;
     950  	if (cxt->flags & MNT_FL_PREPARED)
     951  		return 0;
     952  
     953  	assert(cxt->helper_exec_status == 1);
     954  	assert(cxt->syscall_status == 1);
     955  
     956  	free(cxt->helper);	/* be paranoid */
     957  	cxt->helper = NULL;
     958  	cxt->action = MNT_ACT_UMOUNT;
     959  
     960  	ns_old = mnt_context_switch_target_ns(cxt);
     961  	if (!ns_old)
     962  		return -MNT_ERR_NAMESPACE;
     963  
     964  	rc = lookup_umount_fs(cxt);
     965  	if (!rc)
     966  		rc = mnt_context_merge_mflags(cxt);
     967  	if (!rc)
     968  		rc = evaluate_permissions(cxt);
     969  
     970  	if (!rc && !mnt_context_is_nohelpers(cxt) && !cxt->helper) {
     971  		/* on helper= mount option based helper */
     972  		rc = prepare_helper_from_option(cxt, "helper");
     973  		if (rc < 0)
     974  			return rc;
     975  		if (!cxt->helper)
     976  			/* on fstype based helper */
     977  			rc = mnt_context_prepare_helper(cxt, "umount", NULL);
     978  	}
     979  
     980  	if (!rc)
     981  		rc = mnt_context_get_user_mflags(cxt, &flags);
     982  
     983  	if (!rc && (flags & MNT_MS_LOOP))
     984  		/* loop option explicitly specified in utab, detach this loop */
     985  		mnt_context_enable_loopdel(cxt, TRUE);
     986  
     987  	if (!rc && mnt_context_is_loopdel(cxt) && cxt->fs) {
     988  		const char *src = mnt_fs_get_srcpath(cxt->fs);
     989  
     990  		if (src && (!is_loopdev(src) || loopdev_is_autoclear(src)))
     991  			mnt_context_enable_loopdel(cxt, FALSE);
     992  	}
     993  
     994  	if (rc) {
     995  		DBG(CXT, ul_debugobj(cxt, "umount: preparing failed"));
     996  		return rc;
     997  	}
     998  	cxt->flags |= MNT_FL_PREPARED;
     999  
    1000  	if (!mnt_context_switch_ns(cxt, ns_old))
    1001  		return -MNT_ERR_NAMESPACE;
    1002  
    1003  	return rc;
    1004  }
    1005  
    1006  /**
    1007   * mnt_context_do_umount:
    1008   * @cxt: mount context
    1009   *
    1010   * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type).
    1011   * Unnecessary for mnt_context_umount().
    1012   *
    1013   * See also mnt_context_disable_helpers().
    1014   *
    1015   * WARNING: non-zero return code does not mean that umount(2) syscall or
    1016   *          umount.type helper wasn't successfully called.
    1017   *
    1018   *          Check mnt_context_get_status() after error!
    1019  *
    1020   * Returns: 0 on success;
    1021   *         >0 in case of umount(2) error (returns syscall errno),
    1022   *         <0 in case of other errors.
    1023   */
    1024  int mnt_context_do_umount(struct libmnt_context *cxt)
    1025  {
    1026  	int rc;
    1027  	struct libmnt_ns *ns_old;
    1028  
    1029  	assert(cxt);
    1030  	assert(cxt->fs);
    1031  	assert(cxt->helper_exec_status == 1);
    1032  	assert(cxt->syscall_status == 1);
    1033  	assert((cxt->flags & MNT_FL_PREPARED));
    1034  	assert((cxt->action == MNT_ACT_UMOUNT));
    1035  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
    1036  
    1037  	ns_old = mnt_context_switch_target_ns(cxt);
    1038  	if (!ns_old)
    1039  		return -MNT_ERR_NAMESPACE;
    1040  
    1041  	rc = do_umount(cxt);
    1042  	if (rc)
    1043  		goto end;
    1044  
    1045  	if (mnt_context_get_status(cxt) && !mnt_context_is_fake(cxt)) {
    1046  		/*
    1047  		 * Umounted, do some post-umount operations
    1048  		 *	- remove loopdev
    1049  		 *	- refresh in-memory utab stuff if remount rather than
    1050  		 *	  umount has been performed
    1051  		 */
    1052  		if (mnt_context_is_loopdel(cxt)
    1053  		    && !mnt_optlist_is_remount(cxt->optlist))
    1054  			rc = mnt_context_delete_loopdev(cxt);
    1055  	}
    1056  end:
    1057  	if (!mnt_context_switch_ns(cxt, ns_old))
    1058  		return -MNT_ERR_NAMESPACE;
    1059  
    1060  	return rc;
    1061  }
    1062  
    1063  /**
    1064   * mnt_context_finalize_umount:
    1065   * @cxt: context
    1066   *
    1067   * Mtab update, etc. Unnecessary for mnt_context_umount(), but should be called
    1068   * after mnt_context_do_umount(). See also mnt_context_set_syscall_status().
    1069   *
    1070   * Returns: negative number on error, 0 on success.
    1071   */
    1072  int mnt_context_finalize_umount(struct libmnt_context *cxt)
    1073  {
    1074  	int rc;
    1075  
    1076  	assert(cxt);
    1077  	assert(cxt->fs);
    1078  	assert((cxt->flags & MNT_FL_PREPARED));
    1079  	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
    1080  
    1081  	rc = mnt_context_prepare_update(cxt);
    1082  	if (!rc)
    1083  		rc = mnt_context_update_tabs(cxt);
    1084  	return rc;
    1085  }
    1086  
    1087  
    1088  /**
    1089   * mnt_context_umount:
    1090   * @cxt: umount context
    1091   *
    1092   * High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type).
    1093   *
    1094   * This is similar to:
    1095   *
    1096   *	mnt_context_prepare_umount(cxt);
    1097   *	mnt_context_do_umount(cxt);
    1098   *	mnt_context_finalize_umount(cxt);
    1099   *
    1100   * See also mnt_context_disable_helpers().
    1101   *
    1102   * WARNING: non-zero return code does not mean that umount(2) syscall or
    1103   *          umount.type helper wasn't successfully called.
    1104   *
    1105   *          Check mnt_context_get_status() after error!
    1106   *
    1107   * Returns: 0 on success;
    1108   *         >0 in case of umount(2) error (returns syscall errno),
    1109   *         <0 in case of other errors.
    1110   */
    1111  int mnt_context_umount(struct libmnt_context *cxt)
    1112  {
    1113  	int rc;
    1114  	struct libmnt_ns *ns_old;
    1115  
    1116  	assert(cxt);
    1117  	assert(cxt->fs);
    1118  	assert(cxt->helper_exec_status == 1);
    1119  	assert(cxt->syscall_status == 1);
    1120  
    1121  	DBG(CXT, ul_debugobj(cxt, "umount: %s", mnt_context_get_target(cxt)));
    1122  
    1123  	ns_old = mnt_context_switch_target_ns(cxt);
    1124  	if (!ns_old)
    1125  		return -MNT_ERR_NAMESPACE;
    1126  
    1127  	rc = mnt_context_prepare_umount(cxt);
    1128  	if (!rc)
    1129  		rc = mnt_context_prepare_update(cxt);
    1130  	if (!rc)
    1131  		rc = mnt_context_do_umount(cxt);
    1132  	if (!rc)
    1133  		rc = mnt_context_update_tabs(cxt);
    1134  
    1135  	if (!mnt_context_switch_ns(cxt, ns_old))
    1136  		return -MNT_ERR_NAMESPACE;
    1137  
    1138  	return rc;
    1139  }
    1140  
    1141  
    1142  /**
    1143   * mnt_context_next_umount:
    1144   * @cxt: context
    1145   * @itr: iterator
    1146   * @fs: returns the current filesystem
    1147   * @mntrc: returns the return code from mnt_context_umount()
    1148   * @ignored: returns 1 for not matching
    1149   *
    1150   * This function tries to umount the next filesystem from mountinfo file.
    1151   *
    1152   * You can filter out filesystems by:
    1153   *	mnt_context_set_options_pattern() to simulate umount -a -O pattern
    1154   *	mnt_context_set_fstype_pattern()  to simulate umount -a -t pattern
    1155   *
    1156   * If the filesystem is not mounted or does not match the defined criteria,
    1157   * then the function mnt_context_next_umount() returns zero, but the @ignored is
    1158   * non-zero. Note that the root filesystem is always ignored.
    1159   *
    1160   * If umount(2) syscall or umount.type helper failed, then the
    1161   * mnt_context_next_umount() function returns zero, but the @mntrc is non-zero.
    1162   * Use also mnt_context_get_status() to check if the filesystem was
    1163   * successfully umounted.
    1164   *
    1165   * Returns: 0 on success,
    1166   *         <0 in case of error (!= umount(2) errors)
    1167   *          1 at the end of the list.
    1168   */
    1169  int mnt_context_next_umount(struct libmnt_context *cxt,
    1170  			   struct libmnt_iter *itr,
    1171  			   struct libmnt_fs **fs,
    1172  			   int *mntrc,
    1173  			   int *ignored)
    1174  {
    1175  	struct libmnt_table *mountinfo;
    1176  	const char *tgt;
    1177  	int rc;
    1178  
    1179  	if (ignored)
    1180  		*ignored = 0;
    1181  	if (mntrc)
    1182  		*mntrc = 0;
    1183  
    1184  	if (!cxt || !fs || !itr)
    1185  		return -EINVAL;
    1186  
    1187  	rc = mnt_context_get_mountinfo(cxt, &mountinfo);
    1188  	cxt->mountinfo = NULL;		/* do not reset mountinfo */
    1189  	mnt_reset_context(cxt);
    1190  
    1191  	if (rc)
    1192  		return rc;
    1193  
    1194  	cxt->mountinfo = mountinfo;
    1195  
    1196  	do {
    1197  		rc = mnt_table_next_fs(mountinfo, itr, fs);
    1198  		if (rc != 0)
    1199  			return rc;	/* no more filesystems (or error) */
    1200  
    1201  		tgt = mnt_fs_get_target(*fs);
    1202  	} while (!tgt);
    1203  
    1204  	DBG(CXT, ul_debugobj(cxt, "next-umount: trying %s [fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", tgt,
    1205  				 mnt_fs_get_fstype(*fs), cxt->fstype_pattern, mnt_fs_get_options(*fs), cxt->optstr_pattern));
    1206  
    1207  	/* ignore filesystems which don't match options patterns */
    1208  	if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
    1209  					cxt->fstype_pattern)) ||
    1210  
    1211  	/* ignore filesystems which don't match type patterns */
    1212  	   (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
    1213  					cxt->optstr_pattern))) {
    1214  		if (ignored)
    1215  			*ignored = 1;
    1216  
    1217  		DBG(CXT, ul_debugobj(cxt, "next-umount: not-match"));
    1218  		return 0;
    1219  	}
    1220  
    1221  	rc = mnt_context_set_fs(cxt, *fs);
    1222  	if (rc)
    1223  		return rc;
    1224  	rc = mnt_context_umount(cxt);
    1225  	if (mntrc)
    1226  		*mntrc = rc;
    1227  	return 0;
    1228  }
    1229  
    1230  
    1231  int mnt_context_get_umount_excode(
    1232  			struct libmnt_context *cxt,
    1233  			int rc,
    1234  			char *buf,
    1235  			size_t bufsz)
    1236  {
    1237  	if (mnt_context_helper_executed(cxt))
    1238  		/*
    1239  		 * /sbin/umount.<type> called, return status
    1240  		 */
    1241  		return mnt_context_get_helper_status(cxt);
    1242  
    1243  	if (rc == 0 && mnt_context_get_status(cxt) == 1)
    1244  		/*
    1245  		 * Libmount success && syscall success.
    1246  		 */
    1247  		return MNT_EX_SUCCESS;
    1248  
    1249  	if (!mnt_context_syscall_called(cxt)) {
    1250  		/*
    1251  		 * libmount errors (extra library checks)
    1252  		 */
    1253  		if (rc == -EPERM && !mnt_context_tab_applied(cxt)) {
    1254  			/* failed to evaluate permissions because not found
    1255  			 * relevant entry in mountinfo */
    1256  			if (buf)
    1257  				snprintf(buf, bufsz, _("not mounted"));
    1258  			return MNT_EX_USAGE;
    1259  		}
    1260  
    1261  		if (rc == -MNT_ERR_LOCK) {
    1262  			if (buf)
    1263  				snprintf(buf, bufsz, _("locking failed"));
    1264  			return MNT_EX_FILEIO;
    1265  		}
    1266  
    1267  		if (rc == -MNT_ERR_NAMESPACE) {
    1268  			if (buf)
    1269  				snprintf(buf, bufsz, _("failed to switch namespace"));
    1270  			return MNT_EX_SYSERR;
    1271  		}
    1272  		return mnt_context_get_generic_excode(rc, buf, bufsz,
    1273  					_("umount failed: %m"));
    1274  
    1275  	} if (mnt_context_get_syscall_errno(cxt) == 0) {
    1276  		/*
    1277  		 * umount(2) syscall success, but something else failed
    1278  		 * (probably error in utab processing).
    1279  		 */
    1280  		if (rc == -MNT_ERR_LOCK) {
    1281  			if (buf)
    1282  				snprintf(buf, bufsz, _("filesystem was unmounted, but failed to update userspace mount table"));
    1283  			return MNT_EX_FILEIO;
    1284  		}
    1285  
    1286  		if (rc == -MNT_ERR_NAMESPACE) {
    1287  			if (buf)
    1288  				snprintf(buf, bufsz, _("filesystem was unmounted, but failed to switch namespace back"));
    1289  			return MNT_EX_SYSERR;
    1290  
    1291  		}
    1292  
    1293  		if (rc < 0)
    1294  			return mnt_context_get_generic_excode(rc, buf, bufsz,
    1295  				_("filesystem was unmounted, but any subsequent operation failed: %m"));
    1296  
    1297  		return MNT_EX_SOFTWARE;	/* internal error */
    1298  	}
    1299  
    1300  	/*
    1301  	 * umount(2) errors
    1302  	 */
    1303  	if (buf) {
    1304  		int syserr = mnt_context_get_syscall_errno(cxt);
    1305  
    1306  		switch (syserr) {
    1307  		case ENXIO:
    1308  			snprintf(buf, bufsz, _("invalid block device"));	/* ??? */
    1309  			break;
    1310  		case EINVAL:
    1311  			snprintf(buf, bufsz, _("not mounted"));
    1312  			break;
    1313  		case EIO:
    1314  			snprintf(buf, bufsz, _("can't write superblock"));
    1315  			break;
    1316  		case EBUSY:
    1317  			snprintf(buf, bufsz, _("target is busy"));
    1318  			break;
    1319  		case ENOENT:
    1320  			snprintf(buf, bufsz, _("no mount point specified"));
    1321  			break;
    1322  		case EPERM:
    1323  			snprintf(buf, bufsz, _("must be superuser to unmount"));
    1324  			break;
    1325  		case EACCES:
    1326  			snprintf(buf, bufsz, _("block devices are not permitted on filesystem"));
    1327  			break;
    1328  		default:
    1329  			return mnt_context_get_generic_excode(syserr, buf, bufsz,_("umount(2) system call failed: %m"));
    1330  		}
    1331  	}
    1332  	return MNT_EX_FAIL;
    1333  }