1 /*
2  * mount(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 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #include <stdarg.h>
32 #include <libmount.h>
33 #include <ctype.h>
34 
35 #include "nls.h"
36 #include "c.h"
37 #include "env.h"
38 #include "strutils.h"
39 #include "closestream.h"
40 #include "canonicalize.h"
41 
42 #define XALLOC_EXIT_CODE MNT_EX_SYSERR
43 #include "xalloc.h"
44 
45 #define OPTUTILS_EXIT_CODE MNT_EX_USAGE
46 #include "optutils.h"
47 
48 static int mk_exit_code(struct libmnt_context *cxt, int rc);
49 
suid_drop(struct libmnt_context * cxt)50 static void suid_drop(struct libmnt_context *cxt)
51 {
52 	const uid_t ruid = getuid();
53 	const uid_t euid = geteuid();
54 
55 	if (ruid != 0 && euid == 0) {
56 		if (setgid(getgid()) < 0)
57 			err(MNT_EX_FAIL, _("setgid() failed"));
58 
59 		if (setuid(getuid()) < 0)
60 			err(MNT_EX_FAIL, _("setuid() failed"));
61 	}
62 
63 	/* be paranoid and check it, setuid(0) has to fail */
64 	if (ruid != 0 && setuid(0) == 0)
65 		errx(MNT_EX_FAIL, _("drop permissions failed."));
66 
67 	mnt_context_force_unrestricted(cxt);
68 }
69 
mount_print_version(void)70 static void __attribute__((__noreturn__)) mount_print_version(void)
71 {
72 	const char *ver = NULL;
73 	const char **features = NULL, **p;
74 
75 	mnt_get_library_version(&ver);
76 	mnt_get_library_features(&features);
77 
78 	printf(_("%s from %s (libmount %s"),
79 			program_invocation_short_name,
80 			PACKAGE_STRING,
81 			ver);
82 	p = features;
83 	while (p && *p) {
84 		fputs(p == features ? ": " : ", ", stdout);
85 		fputs(*p++, stdout);
86 	}
87 	fputs(")\n", stdout);
88 	exit(MNT_EX_SUCCESS);
89 }
90 
table_parser_errcb(struct libmnt_table * tb,const char * filename,int line)91 static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
92 			const char *filename, int line)
93 {
94 	if (filename)
95 		warnx(_("%s: parse error at line %d -- ignored"), filename, line);
96 	return 1;
97 }
98 
99 /*
100  * Replace control chars with '?' to be compatible with coreutils. For more
101  * robust solution use findmnt(1) where we use \x?? hex encoding.
102  */
safe_fputs(const char * data)103 static void safe_fputs(const char *data)
104 {
105 	const char *p;
106 
107 	for (p = data; p && *p; p++) {
108 		if (iscntrl((unsigned char) *p))
109 			fputc('?', stdout);
110 		else
111 			fputc(*p, stdout);
112 	}
113 }
114 
print_all(struct libmnt_context * cxt,char * pattern,int show_label)115 static void print_all(struct libmnt_context *cxt, char *pattern, int show_label)
116 {
117 	struct libmnt_table *tb;
118 	struct libmnt_iter *itr = NULL;
119 	struct libmnt_fs *fs;
120 	struct libmnt_cache *cache = NULL;
121 
122 	if (mnt_context_get_mtab(cxt, &tb))
123 		err(MNT_EX_SYSERR, _("failed to read mtab"));
124 
125 	itr = mnt_new_iter(MNT_ITER_FORWARD);
126 	if (!itr)
127 		err(MNT_EX_SYSERR, _("failed to initialize libmount iterator"));
128 	if (show_label)
129 		cache = mnt_new_cache();
130 
131 	while (mnt_table_next_fs(tb, itr, &fs) == 0) {
132 		const char *type = mnt_fs_get_fstype(fs);
133 		const char *src = mnt_fs_get_source(fs);
134 		const char *optstr = mnt_fs_get_options(fs);
135 		char *xsrc = NULL;
136 
137 		if (type && pattern && !mnt_match_fstype(type, pattern))
138 			continue;
139 
140 		if (!mnt_fs_is_pseudofs(fs) && !mnt_fs_is_netfs(fs))
141 			xsrc = mnt_pretty_path(src, cache);
142 		printf ("%s on ", xsrc ? xsrc : src);
143 		safe_fputs(mnt_fs_get_target(fs));
144 
145 		if (type)
146 			printf (" type %s", type);
147 		if (optstr)
148 			printf (" (%s)", optstr);
149 		if (show_label && src) {
150 			char *lb = mnt_cache_find_tag_value(cache, src, "LABEL");
151 			if (lb)
152 				printf (" [%s]", lb);
153 		}
154 		fputc('\n', stdout);
155 		free(xsrc);
156 	}
157 
158 	mnt_unref_cache(cache);
159 	mnt_free_iter(itr);
160 }
161 
162 /*
163  * mount -a [-F]
164  */
mount_all(struct libmnt_context * cxt)165 static int mount_all(struct libmnt_context *cxt)
166 {
167 	struct libmnt_iter *itr;
168 	struct libmnt_fs *fs;
169 	int mntrc, ignored, rc = MNT_EX_SUCCESS;
170 
171 	int nsucc = 0, nerrs = 0;
172 
173 	itr = mnt_new_iter(MNT_ITER_FORWARD);
174 	if (!itr) {
175 		warn(_("failed to initialize libmount iterator"));
176 		return MNT_EX_SYSERR;
177 	}
178 
179 	while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
180 
181 		const char *tgt = mnt_fs_get_target(fs);
182 
183 		if (ignored) {
184 			if (mnt_context_is_verbose(cxt))
185 				printf(ignored == 1 ? _("%-25s: ignored\n") :
186 						      _("%-25s: already mounted\n"),
187 						tgt);
188 		} else if (mnt_context_is_fork(cxt)) {
189 			if (mnt_context_is_verbose(cxt))
190 				printf("%-25s: mount successfully forked\n", tgt);
191 		} else {
192 			if (mk_exit_code(cxt, mntrc) == MNT_EX_SUCCESS) {
193 				nsucc++;
194 
195 				/* Note that MNT_EX_SUCCESS return code does
196 				 * not mean that FS has been really mounted
197 				 * (e.g. nofail option) */
198 				if (mnt_context_get_status(cxt)
199 				    && mnt_context_is_verbose(cxt))
200 					printf("%-25s: successfully mounted\n", tgt);
201 			} else
202 				nerrs++;
203 		}
204 	}
205 
206 	if (mnt_context_is_parent(cxt)) {
207 		/* wait for mount --fork children */
208 		int nchildren = 0;
209 
210 		nerrs = 0, nsucc = 0;
211 
212 		rc = mnt_context_wait_for_children(cxt, &nchildren, &nerrs);
213 		if (!rc && nchildren)
214 			nsucc = nchildren - nerrs;
215 	}
216 
217 	if (nerrs == 0)
218 		rc = MNT_EX_SUCCESS;		/* all success */
219 	else if (nsucc == 0)
220 		rc = MNT_EX_FAIL;		/* all failed */
221 	else
222 		rc = MNT_EX_SOMEOK;		/* some success, some failed */
223 
224 	mnt_free_iter(itr);
225 	return rc;
226 }
227 
228 
229 /*
230  * mount -a -o remount
231  */
remount_all(struct libmnt_context * cxt)232 static int remount_all(struct libmnt_context *cxt)
233 {
234 	struct libmnt_iter *itr;
235 	struct libmnt_fs *fs;
236 	int mntrc, ignored, rc = MNT_EX_SUCCESS;
237 
238 	int nsucc = 0, nerrs = 0;
239 
240 	itr = mnt_new_iter(MNT_ITER_FORWARD);
241 	if (!itr) {
242 		warn(_("failed to initialize libmount iterator"));
243 		return MNT_EX_SYSERR;
244 	}
245 
246 	while (mnt_context_next_remount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
247 
248 		const char *tgt = mnt_fs_get_target(fs);
249 
250 		if (ignored) {
251 			if (mnt_context_is_verbose(cxt))
252 				printf(_("%-25s: ignored\n"), tgt);
253 		} else {
254 			if (mk_exit_code(cxt, mntrc) == MNT_EX_SUCCESS) {
255 				nsucc++;
256 
257 				/* Note that MNT_EX_SUCCESS return code does
258 				 * not mean that FS has been really mounted
259 				 * (e.g. nofail option) */
260 				if (mnt_context_get_status(cxt)
261 				    && mnt_context_is_verbose(cxt))
262 					printf("%-25s: successfully remounted\n", tgt);
263 			} else
264 				nerrs++;
265 		}
266 	}
267 
268 	if (nerrs == 0)
269 		rc = MNT_EX_SUCCESS;		/* all success */
270 	else if (nsucc == 0)
271 		rc = MNT_EX_FAIL;		/* all failed */
272 	else
273 		rc = MNT_EX_SOMEOK;		/* some success, some failed */
274 
275 	mnt_free_iter(itr);
276 	return rc;
277 }
278 
success_message(struct libmnt_context * cxt)279 static void success_message(struct libmnt_context *cxt)
280 {
281 	unsigned long mflags = 0;
282 	const char *tgt, *src, *pr = program_invocation_short_name;
283 
284 	if (mnt_context_helper_executed(cxt)
285 	    || mnt_context_get_status(cxt) != 1)
286 		return;
287 
288 	mnt_context_get_mflags(cxt, &mflags);
289 	tgt = mnt_context_get_target(cxt);
290 	src = mnt_context_get_source(cxt);
291 
292 	if (mflags & MS_MOVE)
293 		printf(_("%s: %s moved to %s.\n"), pr, src, tgt);
294 	else if (mflags & MS_BIND)
295 		printf(_("%s: %s bound on %s.\n"), pr, src, tgt);
296 	else if (mflags & MS_PROPAGATION) {
297 		if (src && strcmp(src, "none") != 0 && tgt)
298 			printf(_("%s: %s mounted on %s.\n"), pr, src, tgt);
299 
300 		printf(_("%s: %s propagation flags changed.\n"), pr, tgt);
301 	} else
302 		printf(_("%s: %s mounted on %s.\n"), pr, src, tgt);
303 }
304 
305 #if defined(HAVE_LIBSELINUX) && defined(HAVE_SECURITY_GET_INITIAL_CONTEXT)
306 #include <selinux/selinux.h>
307 #include <selinux/context.h>
308 
selinux_warning(struct libmnt_context * cxt,const char * tgt)309 static void selinux_warning(struct libmnt_context *cxt, const char *tgt)
310 {
311 
312 	if (tgt && mnt_context_is_verbose(cxt) && is_selinux_enabled() > 0) {
313 		security_context_t raw = NULL, def = NULL;
314 
315 		if (getfilecon(tgt, &raw) > 0
316 		    && security_get_initial_context("file", &def) == 0) {
317 
318 		if (!selinux_file_context_cmp(raw, def))
319 			printf(_(
320 	"mount: %s does not contain SELinux labels.\n"
321 	"       You just mounted an file system that supports labels which does not\n"
322 	"       contain labels, onto an SELinux box. It is likely that confined\n"
323 	"       applications will generate AVC messages and not be allowed access to\n"
324 	"       this file system.  For more details see restorecon(8) and mount(8).\n"),
325 				tgt);
326 		}
327 		freecon(raw);
328 		freecon(def);
329 	}
330 }
331 #else
332 # define selinux_warning(_x, _y)
333 #endif
334 
335 /*
336  * Returns exit status (MNT_EX_*) and/or prints error message.
337  */
mk_exit_code(struct libmnt_context * cxt,int rc)338 static int mk_exit_code(struct libmnt_context *cxt, int rc)
339 {
340 	const char *tgt;
341 	char buf[BUFSIZ] = { 0 };
342 
343 	rc = mnt_context_get_excode(cxt, rc, buf, sizeof(buf));
344 	tgt = mnt_context_get_target(cxt);
345 
346 	if (*buf) {
347 		const char *spec = tgt;
348 		if (!spec)
349 			spec = mnt_context_get_source(cxt);
350 		if (!spec)
351 			spec = "???";
352 		warnx("%s: %s.", spec, buf);
353 	}
354 
355 	if (rc == MNT_EX_SUCCESS && mnt_context_get_status(cxt) == 1) {
356 		selinux_warning(cxt, tgt);
357 	}
358 	return rc;
359 }
360 
append_fstab(struct libmnt_context * cxt,struct libmnt_table * fstab,const char * path)361 static struct libmnt_table *append_fstab(struct libmnt_context *cxt,
362 					 struct libmnt_table *fstab,
363 					 const char *path)
364 {
365 
366 	if (!fstab) {
367 		fstab = mnt_new_table();
368 		if (!fstab)
369 			err(MNT_EX_SYSERR, _("failed to initialize libmount table"));
370 
371 		mnt_table_set_parser_errcb(fstab, table_parser_errcb);
372 		mnt_context_set_fstab(cxt, fstab);
373 
374 		mnt_unref_table(fstab);	/* reference is handled by @cxt now */
375 	}
376 
377 	if (mnt_table_parse_fstab(fstab, path))
378 		errx(MNT_EX_USAGE,_("%s: failed to parse"), path);
379 
380 	return fstab;
381 }
382 
383 /*
384  * Check source and target paths -- non-root user should not be able to
385  * resolve paths which are unreadable for him.
386  */
sanitize_paths(struct libmnt_context * cxt)387 static int sanitize_paths(struct libmnt_context *cxt)
388 {
389 	const char *p;
390 	struct libmnt_fs *fs = mnt_context_get_fs(cxt);
391 
392 	if (!fs)
393 		return 0;
394 
395 	p = mnt_fs_get_target(fs);
396 	if (p) {
397 		char *np = canonicalize_path_restricted(p);
398 		if (!np)
399 			return -EPERM;
400 		mnt_fs_set_target(fs, np);
401 		free(np);
402 	}
403 
404 	p = mnt_fs_get_srcpath(fs);
405 	if (p) {
406 		char *np = canonicalize_path_restricted(p);
407 		if (!np)
408 			return -EPERM;
409 		mnt_fs_set_source(fs, np);
410 		free(np);
411 	}
412 	return 0;
413 }
414 
append_option(struct libmnt_context * cxt,const char * opt)415 static void append_option(struct libmnt_context *cxt, const char *opt)
416 {
417 	if (opt && (*opt == '=' || *opt == '\'' || *opt == '\"' || isblank(*opt)))
418 		errx(MNT_EX_USAGE, _("unsupported option format: %s"), opt);
419 	if (mnt_context_append_options(cxt, opt))
420 		err(MNT_EX_SYSERR, _("failed to append option '%s'"), opt);
421 }
422 
has_remount_flag(struct libmnt_context * cxt)423 static int has_remount_flag(struct libmnt_context *cxt)
424 {
425 	unsigned long mflags = 0;
426 
427 	if (mnt_context_get_mflags(cxt, &mflags))
428 		return 0;
429 
430 	return mflags & MS_REMOUNT;
431 }
432 
usage(void)433 static void __attribute__((__noreturn__)) usage(void)
434 {
435 	FILE *out = stdout;
436 	fputs(USAGE_HEADER, out);
437 	fprintf(out, _(
438 		" %1$s [-lhV]\n"
439 		" %1$s -a [options]\n"
440 		" %1$s [options] [--source] <source> | [--target] <directory>\n"
441 		" %1$s [options] <source> <directory>\n"
442 		" %1$s <operation> <mountpoint> [<target>]\n"),
443 		program_invocation_short_name);
444 
445 	fputs(USAGE_SEPARATOR, out);
446 	fputs(_("Mount a filesystem.\n"), out);
447 
448 	fputs(USAGE_OPTIONS, out);
449 	fprintf(out, _(
450 	" -a, --all               mount all filesystems mentioned in fstab\n"
451 	" -c, --no-canonicalize   don't canonicalize paths\n"
452 	" -f, --fake              dry run; skip the mount(2) syscall\n"
453 	" -F, --fork              fork off for each device (use with -a)\n"
454 	" -T, --fstab <path>      alternative file to /etc/fstab\n"));
455 	fprintf(out, _(
456 	" -i, --internal-only     don't call the mount.<type> helpers\n"));
457 	fprintf(out, _(
458 	" -l, --show-labels       show also filesystem labels\n"));
459 	fprintf(out, _(
460 	" -n, --no-mtab           don't write to /etc/mtab\n"));
461 	fprintf(out, _(
462 	"     --options-mode <mode>\n"
463 	"                         what to do with options loaded from fstab\n"
464 	"     --options-source <source>\n"
465 	"                         mount options source\n"
466 	"     --options-source-force\n"
467 	"                         force use of options from fstab/mtab\n"));
468 	fprintf(out, _(
469 	" -o, --options <list>    comma-separated list of mount options\n"
470 	" -O, --test-opts <list>  limit the set of filesystems (use with -a)\n"
471 	" -r, --read-only         mount the filesystem read-only (same as -o ro)\n"
472 	" -t, --types <list>      limit the set of filesystem types\n"));
473 	fprintf(out, _(
474 	"     --source <src>      explicitly specifies source (path, label, uuid)\n"
475 	"     --target <target>   explicitly specifies mountpoint\n"));
476 	fprintf(out, _(
477 	"     --target-prefix <path>\n"
478 	"                         specifies path use for all mountpoints\n"));
479 	fprintf(out, _(
480 	" -v, --verbose           say what is being done\n"));
481 	fprintf(out, _(
482 	" -w, --rw, --read-write  mount the filesystem read-write (default)\n"));
483 	fprintf(out, _(
484 	" -N, --namespace <ns>    perform mount in another namespace\n"));
485 
486 	fputs(USAGE_SEPARATOR, out);
487 	printf(USAGE_HELP_OPTIONS(25));
488 
489 	fprintf(out, _(
490 	"\nSource:\n"
491 	" -L, --label <label>     synonym for LABEL=<label>\n"
492 	" -U, --uuid <uuid>       synonym for UUID=<uuid>\n"
493 	" LABEL=<label>           specifies device by filesystem label\n"
494 	" UUID=<uuid>             specifies device by filesystem UUID\n"
495 	" PARTLABEL=<label>       specifies device by partition label\n"
496 	" PARTUUID=<uuid>         specifies device by partition UUID\n"
497 	" ID=<id>                 specifies device by udev hardware ID\n"));
498 
499 	fprintf(out, _(
500 	" <device>                specifies device by path\n"
501 	" <directory>             mountpoint for bind mounts (see --bind/rbind)\n"
502 	" <file>                  regular file for loopdev setup\n"));
503 
504 	fprintf(out, _(
505 	"\nOperations:\n"
506 	" -B, --bind              mount a subtree somewhere else (same as -o bind)\n"
507 	" -M, --move              move a subtree to some other place\n"
508 	" -R, --rbind             mount a subtree and all submounts somewhere else\n"));
509 	fprintf(out, _(
510 	" --make-shared           mark a subtree as shared\n"
511 	" --make-slave            mark a subtree as slave\n"
512 	" --make-private          mark a subtree as private\n"
513 	" --make-unbindable       mark a subtree as unbindable\n"));
514 	fprintf(out, _(
515 	" --make-rshared          recursively mark a whole subtree as shared\n"
516 	" --make-rslave           recursively mark a whole subtree as slave\n"
517 	" --make-rprivate         recursively mark a whole subtree as private\n"
518 	" --make-runbindable      recursively mark a whole subtree as unbindable\n"));
519 
520 	printf(USAGE_MAN_TAIL("mount(8)"));
521 
522 	exit(MNT_EX_SUCCESS);
523 }
524 
525 struct flag_str {
526 	int value;
527 	char *str;
528 };
529 
omode2mask(const char * str)530 static int omode2mask(const char *str)
531 {
532 	size_t i;
533 
534 	static const struct flag_str flags[] = {
535 		{ MNT_OMODE_IGNORE, "ignore" },
536 		{ MNT_OMODE_APPEND, "append" },
537 		{ MNT_OMODE_PREPEND, "prepend" },
538 		{ MNT_OMODE_REPLACE, "replace" },
539 	};
540 
541 	for (i = 0; i < ARRAY_SIZE(flags); i++) {
542 		if (!strcmp(str, flags[i].str))
543 			return flags[i].value;
544 	}
545 	return -EINVAL;
546 }
547 
osrc2mask(const char * str,size_t len)548 static long osrc2mask(const char *str, size_t len)
549 {
550 	size_t i;
551 
552 	static const struct flag_str flags[] = {
553 		{ MNT_OMODE_FSTAB, "fstab" },
554 		{ MNT_OMODE_MTAB, "mtab" },
555 		{ MNT_OMODE_NOTAB, "disable" },
556 	};
557 
558 	for (i = 0; i < ARRAY_SIZE(flags); i++) {
559 		if (!strncmp(str, flags[i].str, len) && !flags[i].str[len])
560 			return flags[i].value;
561 	}
562 	return -EINVAL;
563 }
564 
parse_pid(const char * str)565 static pid_t parse_pid(const char *str)
566 {
567 	char *end;
568 	pid_t ret;
569 
570 	errno = 0;
571 	ret = strtoul(str, &end, 10);
572 
573 	if (ret < 0 || errno || end == str || (end && *end))
574 		return 0;
575 	return ret;
576 }
577 
main(int argc,char ** argv)578 int main(int argc, char **argv)
579 {
580 	int c, rc = MNT_EX_SUCCESS, all = 0, show_labels = 0;
581 	struct libmnt_context *cxt;
582 	struct libmnt_table *fstab = NULL;
583 	char *srcbuf = NULL;
584 	char *types = NULL;
585 	int oper = 0, is_move = 0;
586 	int propa = 0;
587 	int optmode = 0, optmode_mode = 0, optmode_src = 0;
588 
589 	enum {
590 		MOUNT_OPT_SHARED = CHAR_MAX + 1,
591 		MOUNT_OPT_SLAVE,
592 		MOUNT_OPT_PRIVATE,
593 		MOUNT_OPT_UNBINDABLE,
594 		MOUNT_OPT_RSHARED,
595 		MOUNT_OPT_RSLAVE,
596 		MOUNT_OPT_RPRIVATE,
597 		MOUNT_OPT_RUNBINDABLE,
598 		MOUNT_OPT_TARGET,
599 		MOUNT_OPT_TARGET_PREFIX,
600 		MOUNT_OPT_SOURCE,
601 		MOUNT_OPT_OPTMODE,
602 		MOUNT_OPT_OPTSRC,
603 		MOUNT_OPT_OPTSRC_FORCE
604 	};
605 
606 	static const struct option longopts[] = {
607 		{ "all",              no_argument,       NULL, 'a'                   },
608 		{ "fake",             no_argument,       NULL, 'f'                   },
609 		{ "fstab",            required_argument, NULL, 'T'                   },
610 		{ "fork",             no_argument,       NULL, 'F'                   },
611 		{ "help",             no_argument,       NULL, 'h'                   },
612 		{ "no-mtab",          no_argument,       NULL, 'n'                   },
613 		{ "read-only",        no_argument,       NULL, 'r'                   },
614 		{ "ro",               no_argument,       NULL, 'r'                   },
615 		{ "verbose",          no_argument,       NULL, 'v'                   },
616 		{ "version",          no_argument,       NULL, 'V'                   },
617 		{ "read-write",       no_argument,       NULL, 'w'                   },
618 		{ "rw",               no_argument,       NULL, 'w'                   },
619 		{ "options",          required_argument, NULL, 'o'                   },
620 		{ "test-opts",        required_argument, NULL, 'O'                   },
621 		{ "types",            required_argument, NULL, 't'                   },
622 		{ "uuid",             required_argument, NULL, 'U'                   },
623 		{ "label",            required_argument, NULL, 'L'                   },
624 		{ "bind",             no_argument,       NULL, 'B'                   },
625 		{ "move",             no_argument,       NULL, 'M'                   },
626 		{ "rbind",            no_argument,       NULL, 'R'                   },
627 		{ "make-shared",      no_argument,       NULL, MOUNT_OPT_SHARED      },
628 		{ "make-slave",       no_argument,       NULL, MOUNT_OPT_SLAVE       },
629 		{ "make-private",     no_argument,       NULL, MOUNT_OPT_PRIVATE     },
630 		{ "make-unbindable",  no_argument,       NULL, MOUNT_OPT_UNBINDABLE  },
631 		{ "make-rshared",     no_argument,       NULL, MOUNT_OPT_RSHARED     },
632 		{ "make-rslave",      no_argument,       NULL, MOUNT_OPT_RSLAVE      },
633 		{ "make-rprivate",    no_argument,       NULL, MOUNT_OPT_RPRIVATE    },
634 		{ "make-runbindable", no_argument,       NULL, MOUNT_OPT_RUNBINDABLE },
635 		{ "no-canonicalize",  no_argument,       NULL, 'c'                   },
636 		{ "internal-only",    no_argument,       NULL, 'i'                   },
637 		{ "show-labels",      no_argument,       NULL, 'l'                   },
638 		{ "target",           required_argument, NULL, MOUNT_OPT_TARGET      },
639 		{ "target-prefix",    required_argument, NULL, MOUNT_OPT_TARGET_PREFIX },
640 		{ "source",           required_argument, NULL, MOUNT_OPT_SOURCE      },
641 		{ "options-mode",     required_argument, NULL, MOUNT_OPT_OPTMODE     },
642 		{ "options-source",   required_argument, NULL, MOUNT_OPT_OPTSRC      },
643 		{ "options-source-force",   no_argument, NULL, MOUNT_OPT_OPTSRC_FORCE},
644 		{ "namespace",        required_argument, NULL, 'N'                   },
645 		{ NULL, 0, NULL, 0 }
646 	};
647 
648 	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
649 		{ 'B','M','R' },			/* bind,move,rbind */
650 		{ 'L','U', MOUNT_OPT_SOURCE },	/* label,uuid,source */
651 		{ 0 }
652 	};
653 	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
654 
655 	sanitize_env();
656 	setlocale(LC_ALL, "");
657 	bindtextdomain(PACKAGE, LOCALEDIR);
658 	textdomain(PACKAGE);
659 	close_stdout_atexit();
660 
661 	strutils_set_exitcode(MNT_EX_USAGE);
662 
663 	mnt_init_debug(0);
664 	cxt = mnt_new_context();
665 	if (!cxt)
666 		err(MNT_EX_SYSERR, _("libmount context allocation failed"));
667 
668 	mnt_context_set_tables_errcb(cxt, table_parser_errcb);
669 
670 	while ((c = getopt_long(argc, argv, "aBcfFhilL:Mno:O:rRsU:vVwt:T:N:",
671 					longopts, NULL)) != -1) {
672 
673 		/* only few options are allowed for non-root users */
674 		if (mnt_context_is_restricted(cxt) &&
675 		    !strchr("hlLUVvrist", c) &&
676 		    c != MOUNT_OPT_TARGET &&
677 		    c != MOUNT_OPT_SOURCE)
678 			suid_drop(cxt);
679 
680 		err_exclusive_options(c, longopts, excl, excl_st);
681 
682 		switch(c) {
683 		case 'a':
684 			all = 1;
685 			break;
686 		case 'c':
687 			mnt_context_disable_canonicalize(cxt, TRUE);
688 			break;
689 		case 'f':
690 			mnt_context_enable_fake(cxt, TRUE);
691 			break;
692 		case 'F':
693 			mnt_context_enable_fork(cxt, TRUE);
694 			break;
695 		case 'i':
696 			mnt_context_disable_helpers(cxt, TRUE);
697 			break;
698 		case 'n':
699 			mnt_context_disable_mtab(cxt, TRUE);
700 			break;
701 		case 'r':
702 			append_option(cxt, "ro");
703 			mnt_context_enable_rwonly_mount(cxt, FALSE);
704 			break;
705 		case 'v':
706 			mnt_context_enable_verbose(cxt, TRUE);
707 			break;
708 		case 'w':
709 			append_option(cxt, "rw");
710 			mnt_context_enable_rwonly_mount(cxt, TRUE);
711 			break;
712 		case 'o':
713 			/* "move" is not supported as option string in libmount
714 			 * to avoid use in fstab */
715 			if (mnt_optstr_get_option(optarg, "move", NULL, 0) == 0) {
716 				char *o = xstrdup(optarg);
717 
718 				mnt_optstr_remove_option(&o, "move");
719 				if (o && *o)
720 					append_option(cxt, o);
721 				oper = is_move = 1;
722 				free(o);
723 			} else
724 				append_option(cxt, optarg);
725 			break;
726 		case 'O':
727 			if (mnt_context_set_options_pattern(cxt, optarg))
728 				err(MNT_EX_SYSERR, _("failed to set options pattern"));
729 			break;
730 		case 'L':
731 			xasprintf(&srcbuf, "LABEL=\"%s\"", optarg);
732 			mnt_context_disable_swapmatch(cxt, 1);
733 			mnt_context_set_source(cxt, srcbuf);
734 			free(srcbuf);
735 			break;
736 		case 'U':
737 			xasprintf(&srcbuf, "UUID=\"%s\"", optarg);
738 			mnt_context_disable_swapmatch(cxt, 1);
739 			mnt_context_set_source(cxt, srcbuf);
740 			free(srcbuf);
741 			break;
742 		case 'l':
743 			show_labels = 1;
744 			break;
745 		case 't':
746 			types = optarg;
747 			break;
748 		case 'T':
749 			fstab = append_fstab(cxt, fstab, optarg);
750 			break;
751 		case 's':
752 			mnt_context_enable_sloppy(cxt, TRUE);
753 			break;
754 		case 'B':
755 			oper = 1;
756 			append_option(cxt, "bind");
757 			break;
758 		case 'M':
759 			oper = 1;
760 			is_move = 1;
761 			break;
762 		case 'R':
763 			oper = 1;
764 			append_option(cxt, "rbind");
765 			break;
766 		case 'N':
767 		{
768 			char path[PATH_MAX];
769 			pid_t pid = parse_pid(optarg);
770 
771 			if (pid)
772 				snprintf(path, sizeof(path), "/proc/%i/ns/mnt", pid);
773 
774 			if (mnt_context_set_target_ns(cxt, pid ? path : optarg))
775 				err(MNT_EX_SYSERR, _("failed to set target namespace to %s"), pid ? path : optarg);
776 			break;
777 		}
778 		case MOUNT_OPT_SHARED:
779 			append_option(cxt, "shared");
780 			propa = 1;
781 			break;
782 		case MOUNT_OPT_SLAVE:
783 			append_option(cxt, "slave");
784 			propa = 1;
785 			break;
786 		case MOUNT_OPT_PRIVATE:
787 			append_option(cxt, "private");
788 			propa = 1;
789 			break;
790 		case MOUNT_OPT_UNBINDABLE:
791 			append_option(cxt, "unbindable");
792 			propa = 1;
793 			break;
794 		case MOUNT_OPT_RSHARED:
795 			append_option(cxt, "rshared");
796 			propa = 1;
797 			break;
798 		case MOUNT_OPT_RSLAVE:
799 			append_option(cxt, "rslave");
800 			propa = 1;
801 			break;
802 		case MOUNT_OPT_RPRIVATE:
803 			append_option(cxt, "rprivate");
804 			propa = 1;
805 			break;
806 		case MOUNT_OPT_RUNBINDABLE:
807 			append_option(cxt, "runbindable");
808 			propa = 1;
809 			break;
810 		case MOUNT_OPT_TARGET:
811 			mnt_context_disable_swapmatch(cxt, 1);
812 			mnt_context_set_target(cxt, optarg);
813 			break;
814 		case MOUNT_OPT_TARGET_PREFIX:
815 			mnt_context_set_target_prefix(cxt, optarg);
816 			break;
817 		case MOUNT_OPT_SOURCE:
818 			mnt_context_disable_swapmatch(cxt, 1);
819 			mnt_context_set_source(cxt, optarg);
820 			break;
821 		case MOUNT_OPT_OPTMODE:
822 			optmode_mode = omode2mask(optarg);
823 			if (optmode_mode == -EINVAL) {
824 				warnx(_("bad usage"));
825 				errtryhelp(MNT_EX_USAGE);
826 			}
827 			break;
828 		case MOUNT_OPT_OPTSRC:
829 		{
830 			unsigned long tmp = 0;
831 			if (string_to_bitmask(optarg, &tmp, osrc2mask)) {
832 				warnx(_("bad usage"));
833 				errtryhelp(MNT_EX_USAGE);
834 			}
835 			optmode_src = tmp;
836 			break;
837 		}
838 		case MOUNT_OPT_OPTSRC_FORCE:
839 			optmode |= MNT_OMODE_FORCE;
840 			break;
841 
842 		case 'h':
843 			mnt_free_context(cxt);
844 			usage();
845 		case 'V':
846 			mnt_free_context(cxt);
847 			mount_print_version();
848 		default:
849 			errtryhelp(MNT_EX_USAGE);
850 		}
851 	}
852 
853 	argc -= optind;
854 	argv += optind;
855 
856 	optmode |= optmode_mode | optmode_src;
857 	if (optmode) {
858 		if (!optmode_mode)
859 			optmode |= MNT_OMODE_PREPEND;
860 		if (!optmode_src)
861 			optmode |= MNT_OMODE_FSTAB | MNT_OMODE_MTAB;
862 		mnt_context_set_optsmode(cxt, optmode);
863 	}
864 
865 	if (fstab && !mnt_context_is_nocanonicalize(cxt)) {
866 		/*
867 		 * We have external (context independent) fstab instance, let's
868 		 * make a connection between the fstab and the canonicalization
869 		 * cache.
870 		 */
871 		mnt_table_set_cache(fstab, mnt_context_get_cache(cxt));
872 	}
873 
874 	if (!mnt_context_get_source(cxt) &&
875 	    !mnt_context_get_target(cxt) &&
876 	    !argc &&
877 	    !all) {
878 		if (oper || mnt_context_get_options(cxt)) {
879 			warnx(_("bad usage"));
880 			errtryhelp(MNT_EX_USAGE);
881 		}
882 		print_all(cxt, types, show_labels);
883 		goto done;
884 	}
885 
886 	/* Non-root users are allowed to use -t to print_all(),
887 	   but not to mount */
888 	if (mnt_context_is_restricted(cxt) && types)
889 		suid_drop(cxt);
890 
891 	if (oper && (types || all || mnt_context_get_source(cxt))) {
892 		warnx(_("bad usage"));
893 		errtryhelp(MNT_EX_USAGE);
894 	}
895 
896 	if (types && (all || strchr(types, ',') ||
897 			     strncmp(types, "no", 2) == 0))
898 		mnt_context_set_fstype_pattern(cxt, types);
899 	else if (types)
900 		mnt_context_set_fstype(cxt, types);
901 
902 	if (all) {
903 		/*
904 		 * A) Mount all
905 		 */
906 		if (has_remount_flag(cxt))
907 			rc = remount_all(cxt);
908 		else
909 			rc = mount_all(cxt);
910 		goto done;
911 
912 	} else if (argc == 0 && (mnt_context_get_source(cxt) ||
913 				 mnt_context_get_target(cxt))) {
914 		/*
915 		 * B) mount -L|-U|--source|--target
916 		 *
917 		 * non-root may specify source *or* target, but not both
918 		 */
919 		if (mnt_context_is_restricted(cxt) &&
920 		    mnt_context_get_source(cxt) &&
921 		    mnt_context_get_target(cxt))
922 			suid_drop(cxt);
923 
924 	} else if (argc == 1 && (!mnt_context_get_source(cxt) ||
925 				 !mnt_context_get_target(cxt))) {
926 		/*
927 		 * C) mount [-L|-U|--source] <target>
928 		 *    mount [--target <dir>] <source>
929 		 *    mount <source|target>
930 		 *
931 		 * non-root may specify source *or* target, but not both
932 		 *
933 		 * It does not matter for libmount if we set source or target
934 		 * here (the library is able to swap it), but it matters for
935 		 * sanitize_paths().
936 		 */
937 		int istag = mnt_tag_is_valid(argv[0]);
938 
939 		if (istag && mnt_context_get_source(cxt))
940 			/* -L, -U or --source together with LABEL= or UUID= */
941 			errx(MNT_EX_USAGE, _("source specified more than once"));
942 		else if (istag || mnt_context_get_target(cxt))
943 			mnt_context_set_source(cxt, argv[0]);
944 		else
945 			mnt_context_set_target(cxt, argv[0]);
946 
947 		if (mnt_context_is_restricted(cxt) &&
948 		    mnt_context_get_source(cxt) &&
949 		    mnt_context_get_target(cxt))
950 			suid_drop(cxt);
951 
952 	} else if (argc == 2 && !mnt_context_get_source(cxt)
953 			     && !mnt_context_get_target(cxt)) {
954 		/*
955 		 * D) mount <source> <target>
956 		 */
957 		if (mnt_context_is_restricted(cxt))
958 			suid_drop(cxt);
959 
960 		mnt_context_set_source(cxt, argv[0]);
961 		mnt_context_set_target(cxt, argv[1]);
962 
963 	} else {
964 		warnx(_("bad usage"));
965 		errtryhelp(MNT_EX_USAGE);
966 	}
967 
968 	if (mnt_context_is_restricted(cxt) && sanitize_paths(cxt) != 0)
969 		suid_drop(cxt);
970 
971 	if (is_move)
972 		/* "move" as option string is not supported by libmount */
973 		mnt_context_set_mflags(cxt, MS_MOVE);
974 
975 	if ((oper && !has_remount_flag(cxt)) || propa)
976 		/* For --make-* or --bind is fstab/mtab unnecessary */
977 		mnt_context_set_optsmode(cxt, MNT_OMODE_NOTAB);
978 
979 	rc = mnt_context_mount(cxt);
980 
981 	if (rc == -EPERM
982 	    && mnt_context_is_restricted(cxt)
983 	    && !mnt_context_syscall_called(cxt)) {
984 		/* Try it again without permissions */
985 		suid_drop(cxt);
986 		rc = mnt_context_mount(cxt);
987 	}
988 	rc = mk_exit_code(cxt, rc);
989 
990 	if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
991 		success_message(cxt);
992 done:
993 	mnt_free_context(cxt);
994 	return rc;
995 }
996 
997