(root)/
util-linux-2.39/
sys-utils/
umount.c
       1  /*
       2   * umount(8) -- mount a filesystem
       3   *
       4   * Copyright (C) 2011 Red Hat, Inc. All rights reserved.
       5   * Written by Karel Zak <kzak@redhat.com>
       6   *
       7   * This program is free software; you can redistribute it and/or modify
       8   * it under the terms of the GNU General Public License as published by
       9   * the Free Software Foundation; either version 2 of the License, or
      10   * (at your option) any later version.
      11   *
      12   * This program is distributed in the hope that it would be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15   * GNU General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU General Public License along
      18   * with this program; if not, write to the Free Software Foundation, Inc.,
      19   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20   */
      21  
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <errno.h>
      25  #include <string.h>
      26  #include <getopt.h>
      27  #include <unistd.h>
      28  #include <sys/types.h>
      29  
      30  #include <libmount.h>
      31  
      32  #include "nls.h"
      33  #include "c.h"
      34  #include "env.h"
      35  #include "closestream.h"
      36  #include "pathnames.h"
      37  #include "canonicalize.h"
      38  
      39  #define XALLOC_EXIT_CODE MNT_EX_SYSERR
      40  #include "xalloc.h"
      41  
      42  #define OPTUTILS_EXIT_CODE MNT_EX_USAGE
      43  #include "optutils.h"
      44  
      45  static int quiet;
      46  static struct ul_env_list *envs_removed;
      47  
      48  static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
      49  			const char *filename, int line)
      50  {
      51  	if (filename)
      52  		warnx(_("%s: parse error at line %d -- ignored"), filename, line);
      53  	return 1;
      54  }
      55  
      56  
      57  static void __attribute__((__noreturn__)) umount_print_version(void)
      58  {
      59  	const char *ver = NULL;
      60  	const char **features = NULL, **p;
      61  
      62  	mnt_get_library_version(&ver);
      63  	mnt_get_library_features(&features);
      64  
      65  	printf(_("%s from %s (libmount %s"),
      66  			program_invocation_short_name,
      67  			PACKAGE_STRING,
      68  			ver);
      69  	p = features;
      70  	while (p && *p) {
      71  		fputs(p == features ? ": " : ", ", stdout);
      72  		fputs(*p++, stdout);
      73  	}
      74  	fputs(")\n", stdout);
      75  	exit(MNT_EX_SUCCESS);
      76  }
      77  static void __attribute__((__noreturn__)) usage(void)
      78  {
      79  	FILE *out = stdout;
      80  	fputs(USAGE_HEADER, out);
      81  	fprintf(out, _(
      82  		" %1$s [-hV]\n"
      83  		" %1$s -a [options]\n"
      84  		" %1$s [options] <source> | <directory>\n"),
      85  		program_invocation_short_name);
      86  
      87  	fputs(USAGE_SEPARATOR, out);
      88  	fputs(_("Unmount filesystems.\n"), out);
      89  
      90  	fputs(USAGE_OPTIONS, out);
      91  	fputs(_(" -a, --all               unmount all filesystems\n"), out);
      92  	fputs(_(" -A, --all-targets       unmount all mountpoints for the given device in the\n"
      93  	        "                           current namespace\n"), out);
      94  	fputs(_(" -c, --no-canonicalize   don't canonicalize paths\n"), out);
      95  	fputs(_(" -d, --detach-loop       if mounted loop device, also free this loop device\n"), out);
      96  	fputs(_("     --fake              dry run; skip the umount(2) syscall\n"), out);
      97  	fputs(_(" -f, --force             force unmount (in case of an unreachable NFS system)\n"), out);
      98  	fputs(_(" -i, --internal-only     don't call the umount.<type> helpers\n"), out);
      99  	fputs(_(" -n, --no-mtab           don't write to /etc/mtab\n"), out);
     100  	fputs(_(" -l, --lazy              detach the filesystem now, clean up things later\n"), out);
     101  	fputs(_(" -O, --test-opts <list>  limit the set of filesystems (use with -a)\n"), out);
     102  	fputs(_(" -R, --recursive         recursively unmount a target with all its children\n"), out);
     103  	fputs(_(" -r, --read-only         in case unmounting fails, try to remount read-only\n"), out);
     104  	fputs(_(" -t, --types <list>      limit the set of filesystem types\n"), out);
     105  	fputs(_(" -v, --verbose           say what is being done\n"), out);
     106  	fputs(_(" -q, --quiet             suppress 'not mounted' error messages\n"), out);
     107  	fputs(_(" -N, --namespace <ns>    perform umount in another namespace\n"), out);
     108  
     109  	fputs(USAGE_SEPARATOR, out);
     110  	printf(USAGE_HELP_OPTIONS(25));
     111  	printf(USAGE_MAN_TAIL("umount(8)"));
     112  
     113  	exit(MNT_EX_SUCCESS);
     114  }
     115  
     116  static void suid_drop(struct libmnt_context *cxt)
     117  {
     118  	const uid_t ruid = getuid();
     119  	const uid_t euid = geteuid();
     120  
     121  	if (ruid != 0 && euid == 0 && drop_permissions() != 0)
     122  		err(MNT_EX_FAIL, _("drop permissions failed"));
     123  
     124  	/* be paranoid and check it, setuid(0) has to fail */
     125  	if (ruid != 0 && setuid(0) == 0)
     126  		errx(MNT_EX_FAIL, _("drop permissions failed."));
     127  
     128  	mnt_context_force_unrestricted(cxt);
     129  
     130  	/* restore "bad" environment variables */
     131  	if (envs_removed) {
     132  		env_list_setenv(envs_removed);
     133  		env_list_free(envs_removed);
     134  		envs_removed = NULL;
     135  	}
     136  }
     137  
     138  static void success_message(struct libmnt_context *cxt)
     139  {
     140  	const char *tgt, *src;
     141  
     142  	if (mnt_context_helper_executed(cxt)
     143  	    || mnt_context_get_status(cxt) != 1)
     144  		return;
     145  
     146  	tgt = mnt_context_get_target(cxt);
     147  	if (!tgt)
     148  		return;
     149  
     150  	src = mnt_context_get_source(cxt);
     151  	if (src)
     152  		warnx(_("%s (%s) unmounted"), tgt, src);
     153  	else
     154  		warnx(_("%s unmounted"), tgt);
     155  }
     156  
     157  static int mk_exit_code(struct libmnt_context *cxt, int api_rc)
     158  {
     159  	char buf[BUFSIZ] = { 0 };
     160  	int rc;
     161  
     162  	rc = mnt_context_get_excode(cxt, api_rc, buf, sizeof(buf));
     163  
     164  	/* suppress "not mounted" error message */
     165  	if (quiet) {
     166  		switch (rc) {
     167  		case MNT_EX_USAGE:
     168  			if (api_rc == -EPERM)	/* non-root user */
     169  				return rc;
     170  			break;
     171  		case MNT_EX_FAIL:
     172  			if (mnt_context_syscall_called(cxt) &&
     173  			    mnt_context_get_syscall_errno(cxt) == EINVAL)
     174  				return rc;
     175  			break;
     176  		}
     177  	}
     178  
     179  	/* print errors/warnings */
     180  	if (*buf) {
     181  		const char *spec = mnt_context_get_target(cxt);
     182  		if (!spec)
     183  			spec = mnt_context_get_source(cxt);
     184  		if (!spec)
     185  			spec = "???";
     186  		warnx("%s: %s.", spec, buf);
     187  	}
     188  	return rc;
     189  }
     190  
     191  static int umount_all(struct libmnt_context *cxt)
     192  {
     193  	struct libmnt_iter *itr;
     194  	struct libmnt_fs *fs;
     195  	int mntrc, ignored, rc = 0;
     196  
     197  	itr = mnt_new_iter(MNT_ITER_BACKWARD);
     198  	if (!itr) {
     199  		warn(_("failed to initialize libmount iterator"));
     200  		return MNT_EX_SYSERR;
     201  	}
     202  
     203  	while (mnt_context_next_umount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
     204  
     205  		const char *tgt = mnt_fs_get_target(fs);
     206  
     207  		if (ignored) {
     208  			if (mnt_context_is_verbose(cxt))
     209  				printf(_("%-25s: ignored\n"), tgt);
     210  		} else {
     211  			int xrc = mk_exit_code(cxt, mntrc);
     212  
     213  			if (xrc == MNT_EX_SUCCESS
     214  			    && mnt_context_is_verbose(cxt))
     215  				printf("%-25s: successfully unmounted\n", tgt);
     216  			rc |= xrc;
     217  		}
     218  	}
     219  
     220  	mnt_free_iter(itr);
     221  	return rc;
     222  }
     223  
     224  static int umount_one(struct libmnt_context *cxt, const char *spec)
     225  {
     226  	int rc;
     227  
     228  	if (!spec)
     229  		return MNT_EX_SOFTWARE;
     230  
     231  	if (mnt_context_set_target(cxt, spec))
     232  		err(MNT_EX_SYSERR, _("failed to set umount target"));
     233  
     234  	rc = mnt_context_umount(cxt);
     235  
     236  	if (rc == -EPERM
     237  	    && mnt_context_is_restricted(cxt)
     238  	    && mnt_context_tab_applied(cxt)
     239  	    && !mnt_context_syscall_called(cxt)) {
     240  		/* Mountpoint exists, but failed something else in libmount,
     241  		 * drop perms and try it again */
     242  		suid_drop(cxt);
     243  		rc = mnt_context_umount(cxt);
     244  	}
     245  
     246  	rc = mk_exit_code(cxt, rc);
     247  
     248  	if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
     249  		success_message(cxt);
     250  
     251  	mnt_reset_context(cxt);
     252  	return rc;
     253  }
     254  
     255  static struct libmnt_table *new_mountinfo(struct libmnt_context *cxt)
     256  {
     257  	struct libmnt_table *tb;
     258  	struct libmnt_ns *ns_old = mnt_context_switch_target_ns(cxt);
     259  
     260  	if (!ns_old)
     261  		err(MNT_EX_SYSERR, _("failed to switch namespace"));
     262  
     263  	tb = mnt_new_table();
     264  	if (!tb)
     265  		err(MNT_EX_SYSERR, _("libmount table allocation failed"));
     266  
     267  	mnt_table_set_parser_errcb(tb, table_parser_errcb);
     268  	mnt_table_set_cache(tb, mnt_context_get_cache(cxt));
     269  
     270  	if (mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO)) {
     271  		warn(_("failed to parse %s"), _PATH_PROC_MOUNTINFO);
     272  		mnt_unref_table(tb);
     273  		tb = NULL;
     274  	}
     275  
     276  	if (!mnt_context_switch_ns(cxt, ns_old))
     277  		err(MNT_EX_SYSERR, _("failed to switch namespace"));
     278  
     279  	return tb;
     280  }
     281  
     282  /*
     283   * like umount_one() but does not return error is @spec not mounted
     284   */
     285  static int umount_one_if_mounted(struct libmnt_context *cxt, const char *spec)
     286  {
     287  	int rc;
     288  	struct libmnt_fs *fs;
     289  
     290  	rc = mnt_context_find_umount_fs(cxt, spec, &fs);
     291  	if (rc == 1) {
     292  		rc = MNT_EX_SUCCESS;		/* already unmounted */
     293  		mnt_reset_context(cxt);
     294  	} else if (rc < 0) {
     295  		rc = mk_exit_code(cxt, rc);	/* error */
     296  		mnt_reset_context(cxt);
     297  	} else
     298  		rc = umount_one(cxt, mnt_fs_get_target(fs));
     299  
     300  	return rc;
     301  }
     302  
     303  static int umount_do_recurse(struct libmnt_context *cxt,
     304  		struct libmnt_table *tb, struct libmnt_fs *fs)
     305  {
     306  	struct libmnt_fs *child, *over = NULL;
     307  	struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
     308  	int rc;
     309  
     310  	if (!itr)
     311  		err(MNT_EX_SYSERR, _("libmount iterator allocation failed"));
     312  
     313  	/* first try overmount */
     314  	if (mnt_table_over_fs(tb, fs, &over) == 0 && over) {
     315  		rc = umount_do_recurse(cxt, tb, over);
     316  		if (rc != MNT_EX_SUCCESS)
     317  			goto done;
     318  	}
     319  
     320  	/* umount all children */
     321  	for (;;) {
     322  		rc = mnt_table_next_child_fs(tb, itr, fs, &child);
     323  		if (rc < 0) {
     324  			warnx(_("failed to get child fs of %s"),
     325  					mnt_fs_get_target(fs));
     326  			rc = MNT_EX_SOFTWARE;
     327  			goto done;
     328  		} else if (rc == 1)
     329  			break;		/* no more children */
     330  
     331  		if (over && child == over)
     332  			continue;
     333  
     334  		rc = umount_do_recurse(cxt, tb, child);
     335  		if (rc != MNT_EX_SUCCESS)
     336  			goto done;
     337  	}
     338  
     339  	rc = umount_one_if_mounted(cxt, mnt_fs_get_target(fs));
     340  done:
     341  	mnt_free_iter(itr);
     342  	return rc;
     343  }
     344  
     345  static int umount_recursive(struct libmnt_context *cxt, const char *spec)
     346  {
     347  	struct libmnt_table *tb;
     348  	struct libmnt_fs *fs;
     349  	int rc;
     350  
     351  	tb = new_mountinfo(cxt);
     352  	if (!tb)
     353  		return MNT_EX_SOFTWARE;
     354  
     355  	/* it's always real mountpoint, don't assume that the target maybe a device */
     356  	mnt_context_disable_swapmatch(cxt, 1);
     357  
     358  	fs = mnt_table_find_target(tb, spec, MNT_ITER_BACKWARD);
     359  	if (fs)
     360  		rc = umount_do_recurse(cxt, tb, fs);
     361  	else {
     362  		rc = MNT_EX_USAGE;
     363  		if (!quiet)
     364  			warnx(access(spec, F_OK) == 0 ?
     365  				_("%s: not mounted") :
     366  				_("%s: not found"), spec);
     367  	}
     368  
     369  	mnt_unref_table(tb);
     370  	return rc;
     371  }
     372  
     373  static int umount_alltargets(struct libmnt_context *cxt, const char *spec, int rec)
     374  {
     375  	struct libmnt_fs *fs;
     376  	struct libmnt_table *tb;
     377  	struct libmnt_iter *itr = NULL;
     378  	dev_t devno = 0;
     379  	int rc;
     380  
     381  	/* Convert @spec to device name, Use the same logic like regular
     382  	 * "umount <spec>".
     383  	 */
     384  	rc = mnt_context_find_umount_fs(cxt, spec, &fs);
     385  	if (rc == 1) {
     386  		rc = MNT_EX_USAGE;
     387  		if (!quiet)
     388  			warnx(access(spec, F_OK) == 0 ?
     389  				_("%s: not mounted") :
     390  				_("%s: not found"), spec);
     391  		return rc;
     392  	}
     393  	if (rc < 0)
     394  		return mk_exit_code(cxt, rc);		/* error */
     395  
     396  	if (!mnt_fs_get_srcpath(fs) || !mnt_fs_get_devno(fs))
     397  		errx(MNT_EX_USAGE, _("%s: failed to determine source "
     398  				"(--all-targets is unsupported on systems with "
     399  				"regular mtab file)."), spec);
     400  
     401  	itr = mnt_new_iter(MNT_ITER_BACKWARD);
     402  	if (!itr)
     403  		err(MNT_EX_SYSERR, _("libmount iterator allocation failed"));
     404  
     405  	/* get on @cxt independent mountinfo */
     406  	tb = new_mountinfo(cxt);
     407  	if (!tb) {
     408  		rc = MNT_EX_SOFTWARE;
     409  		goto done;
     410  	}
     411  
     412  	/* Note that @fs is from mount context and the context will be reset
     413  	 * after each umount() call */
     414  	devno = mnt_fs_get_devno(fs);
     415  	fs = NULL;
     416  
     417  	mnt_reset_context(cxt);
     418  
     419  	while (mnt_table_next_fs(tb, itr, &fs) == 0) {
     420  		if (mnt_fs_get_devno(fs) != devno)
     421  			continue;
     422  		mnt_context_disable_swapmatch(cxt, 1);
     423  		if (rec)
     424  			rc = umount_do_recurse(cxt, tb, fs);
     425  		else
     426  			rc = umount_one_if_mounted(cxt, mnt_fs_get_target(fs));
     427  
     428  		if (rc != MNT_EX_SUCCESS)
     429  			break;
     430  	}
     431  
     432  done:
     433  	mnt_free_iter(itr);
     434  	mnt_unref_table(tb);
     435  
     436  	return rc;
     437  }
     438  
     439  /*
     440   * Check path -- non-root user should not be able to resolve path which is
     441   * unreadable for them.
     442   */
     443  static char *sanitize_path(const char *path)
     444  {
     445  	char *p;
     446  
     447  	if (!path)
     448  		return NULL;
     449  
     450  	p = canonicalize_path_restricted(path);
     451  	if (!p)
     452  		err(MNT_EX_USAGE, "%s", path);
     453  
     454  	return p;
     455  }
     456  
     457  static pid_t parse_pid(const char *str)
     458  {
     459  	char *end;
     460  	pid_t ret;
     461  
     462  	errno = 0;
     463  	ret = strtoul(str, &end, 10);
     464  
     465  	if (ret < 0 || errno || end == str || (end && *end))
     466  		return 0;
     467  	return ret;
     468  }
     469  
     470  int main(int argc, char **argv)
     471  {
     472  	int c, rc = 0, all = 0, recursive = 0, alltargets = 0;
     473  	struct libmnt_context *cxt;
     474  	char *types = NULL;
     475  
     476  	enum {
     477  		UMOUNT_OPT_FAKE = CHAR_MAX + 1,
     478  	};
     479  
     480  	static const struct option longopts[] = {
     481  		{ "all",             no_argument,       NULL, 'a'             },
     482  		{ "all-targets",     no_argument,       NULL, 'A'             },
     483  		{ "detach-loop",     no_argument,       NULL, 'd'             },
     484  		{ "fake",            no_argument,       NULL, UMOUNT_OPT_FAKE },
     485  		{ "force",           no_argument,       NULL, 'f'             },
     486  		{ "help",            no_argument,       NULL, 'h'             },
     487  		{ "internal-only",   no_argument,       NULL, 'i'             },
     488  		{ "lazy",            no_argument,       NULL, 'l'             },
     489  		{ "no-canonicalize", no_argument,       NULL, 'c'             },
     490  		{ "no-mtab",         no_argument,       NULL, 'n'             },
     491  		{ "quiet",           no_argument,       NULL, 'q'             },
     492  		{ "read-only",       no_argument,       NULL, 'r'             },
     493  		{ "recursive",       no_argument,       NULL, 'R'             },
     494  		{ "test-opts",       required_argument, NULL, 'O'             },
     495  		{ "types",           required_argument, NULL, 't'             },
     496  		{ "verbose",         no_argument,       NULL, 'v'             },
     497  		{ "version",         no_argument,       NULL, 'V'             },
     498  		{ "namespace",       required_argument, NULL, 'N'             },
     499  		{ NULL, 0, NULL, 0 }
     500  	};
     501  
     502  	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
     503  		{ 'A','a' },			/* all-targets,all */
     504  		{ 'R','a' },			/* recursive,all */
     505  		{ 'O','R','t'},			/* options,recursive,types */
     506  		{ 'R','r' },			/* recursive,read-only */
     507  		{ 0 }
     508  	};
     509  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     510  
     511  	__sanitize_env(&envs_removed);
     512  	setlocale(LC_ALL, "");
     513  	bindtextdomain(PACKAGE, LOCALEDIR);
     514  	textdomain(PACKAGE);
     515  	close_stdout_atexit();
     516  
     517  	mnt_init_debug(0);
     518  	cxt = mnt_new_context();
     519  	if (!cxt)
     520  		err(MNT_EX_SYSERR, _("libmount context allocation failed"));
     521  
     522  	mnt_context_set_tables_errcb(cxt, table_parser_errcb);
     523  
     524  	while ((c = getopt_long(argc, argv, "aAcdfhilnqRrO:t:vVN:",
     525  					longopts, NULL)) != -1) {
     526  
     527  
     528  		/* only few options are allowed for non-root users */
     529  		if (mnt_context_is_restricted(cxt) && !strchr("hdilqVv", c)) {
     530  
     531  			/* Silently ignore options without direct impact to the
     532  			 * umount operation, but with security sensitive
     533  			 * side-effects */
     534  			if (strchr("c", c))
     535  				continue;	/* ignore */
     536  
     537  			/* drop permissions, continue as regular user */
     538  			suid_drop(cxt);
     539  		}
     540  
     541  		err_exclusive_options(c, longopts, excl, excl_st);
     542  
     543  		switch(c) {
     544  		case 'a':
     545  			all = 1;
     546  			break;
     547  		case 'A':
     548  			alltargets = 1;
     549  			break;
     550  		case 'c':
     551  			mnt_context_disable_canonicalize(cxt, TRUE);
     552  			break;
     553  		case 'd':
     554  			mnt_context_enable_loopdel(cxt, TRUE);
     555  			break;
     556  		case UMOUNT_OPT_FAKE:
     557  			mnt_context_enable_fake(cxt, TRUE);
     558  			break;
     559  		case 'f':
     560  			mnt_context_enable_force(cxt, TRUE);
     561  			break;
     562  		case 'i':
     563  			mnt_context_disable_helpers(cxt, TRUE);
     564  			break;
     565  		case 'l':
     566  			mnt_context_enable_lazy(cxt, TRUE);
     567  			break;
     568  		case 'n':
     569  			mnt_context_disable_mtab(cxt, TRUE);
     570  			break;
     571  		case 'q':
     572  			quiet = 1;
     573  			break;
     574  		case 'r':
     575  			mnt_context_enable_rdonly_umount(cxt, TRUE);
     576  			break;
     577  		case 'R':
     578  			recursive = TRUE;
     579  			break;
     580  		case 'O':
     581  			if (mnt_context_set_options_pattern(cxt, optarg))
     582  				err(MNT_EX_SYSERR, _("failed to set options pattern"));
     583  			break;
     584  		case 't':
     585  			types = optarg;
     586  			break;
     587  		case 'v':
     588  			mnt_context_enable_verbose(cxt, TRUE);
     589  			break;
     590  		case 'N':
     591  		{
     592  			char path[PATH_MAX];
     593  			pid_t pid = parse_pid(optarg);
     594  
     595  			if (pid)
     596  				snprintf(path, sizeof(path), "/proc/%i/ns/mnt", pid);
     597  
     598  			if (mnt_context_set_target_ns(cxt, pid ? path : optarg))
     599  				err(MNT_EX_SYSERR, _("failed to set target namespace to %s"), pid ? path : optarg);
     600  			break;
     601  		}
     602  
     603  		case 'h':
     604  			mnt_free_context(cxt);
     605  			usage();
     606  		case 'V':
     607  			mnt_free_context(cxt);
     608  			umount_print_version();
     609  		default:
     610  			errtryhelp(MNT_EX_USAGE);
     611  		}
     612  	}
     613  
     614  	argc -= optind;
     615  	argv += optind;
     616  
     617  	if (all) {
     618  		if (argc) {
     619  			warnx(_("unexpected number of arguments"));
     620  			errtryhelp(MNT_EX_USAGE);
     621  		}
     622  		if (!types)
     623  			types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd,noselinuxfs";
     624  
     625  		mnt_context_set_fstype_pattern(cxt, types);
     626  		rc = umount_all(cxt);
     627  
     628  	} else if (argc < 1) {
     629  		warnx(_("bad usage"));
     630  		errtryhelp(MNT_EX_USAGE);
     631  
     632  	} else if (alltargets) {
     633  		while (argc--)
     634  			rc += umount_alltargets(cxt, *argv++, recursive);
     635  	} else if (recursive) {
     636  		while (argc--)
     637  			rc += umount_recursive(cxt, *argv++);
     638  	} else {
     639  		while (argc--) {
     640  			char *path = *argv;
     641  
     642  			if (mnt_context_is_restricted(cxt)
     643  			    && !mnt_tag_is_valid(path))
     644  				path = sanitize_path(path);
     645  
     646  			rc += umount_one(cxt, path);
     647  
     648  			if (path != *argv)
     649  				free(path);
     650  			argv++;
     651  		}
     652  	}
     653  
     654  	mnt_free_context(cxt);
     655  	env_list_free(envs_removed);
     656  
     657  	return (rc < 256) ? rc : 255;
     658  }
     659