]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/mount.c
b4b8fb63a53e2ea3035b2c6584a792a75dd45e71
[thirdparty/util-linux.git] / sys-utils / mount.c
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * mount(8) -- mount a filesystem
5 *
6 * Copyright (C) 2011 Red Hat, Inc. All rights reserved.
7 * Written by Karel Zak <kzak@redhat.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <getopt.h>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <stdarg.h>
24 #include <libmount.h>
25 #include <ctype.h>
26
27 #include "nls.h"
28 #include "c.h"
29 #include "env.h"
30 #include "strutils.h"
31 #include "closestream.h"
32 #include "canonicalize.h"
33 #include "pathnames.h"
34 #include "strv.h"
35
36 #define XALLOC_EXIT_CODE MNT_EX_SYSERR
37 #include "xalloc.h"
38
39 #define OPTUTILS_EXIT_CODE MNT_EX_USAGE
40 #include "optutils.h"
41
42 static struct ul_env_list *envs_removed;
43
44 static int mk_exit_code(struct libmnt_context *cxt, int rc);
45
46 static void suid_drop(struct libmnt_context *cxt)
47 {
48 const uid_t ruid = getuid();
49 const uid_t euid = geteuid();
50
51 if (ruid != 0 && euid == 0 && drop_permissions() != 0)
52 err(MNT_EX_FAIL, _("drop permissions failed"));
53
54 /* be paranoid and check it, setuid(0) has to fail */
55 if (ruid != 0 && setuid(0) == 0)
56 errx(MNT_EX_FAIL, _("drop permissions failed."));
57
58 mnt_context_force_unrestricted(cxt);
59
60 /* restore "bad" environment variables */
61 if (envs_removed) {
62 env_list_setenv(envs_removed, 0);
63 env_list_free(envs_removed);
64 envs_removed = NULL;
65 }
66 }
67
68 static void __attribute__((__noreturn__)) mount_print_version(void)
69 {
70 const char *ver = NULL;
71 const char **features = NULL, **p;
72
73 mnt_get_library_version(&ver);
74 mnt_get_library_features(&features);
75
76 printf(_("%s from %s (libmount %s"),
77 program_invocation_short_name,
78 PACKAGE_STRING,
79 ver);
80 p = features;
81 while (p && *p) {
82 fputs(p == features ? ": " : ", ", stdout);
83 fputs(*p++, stdout);
84 }
85 fputs(")\n", stdout);
86 exit(MNT_EX_SUCCESS);
87 }
88
89 static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
90 const char *filename, int line)
91 {
92 if (filename)
93 warnx(_("%s: parse error at line %d -- ignored"), filename, line);
94 return 1;
95 }
96
97 /*
98 * Replace control chars with '?' to be compatible with coreutils. For more
99 * robust solution use findmnt(1) where we use \x?? hex encoding.
100 */
101 static void safe_fputs(const char *data)
102 {
103 const char *p;
104
105 for (p = data; p && *p; p++) {
106 if (iscntrl((unsigned char) *p))
107 fputc('?', stdout);
108 else
109 fputc(*p, stdout);
110 }
111 }
112
113 static void print_all(struct libmnt_context *cxt, char *pattern, int show_label)
114 {
115 struct libmnt_table *tb;
116 struct libmnt_iter *itr = NULL;
117 struct libmnt_fs *fs;
118 struct libmnt_cache *cache = NULL;
119
120 mnt_context_enable_noautofs(cxt, 1);
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_regularfs(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 */
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 */
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
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
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 char *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 a 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 #ifdef USE_SYSTEMD
337 /*
338 * Note that this mount(8) message may generate thousands of lines of output
339 * when mount(8) is called from any script in systems with large fstab, etc.
340 *
341 * The goal is to avoid spamming system logs (don't print on stderr) and hide
342 * the hint if stderr is redirected/piped (in this case mount(8) is probably
343 * executed in a script).
344 *
345 * The target audience is users on a terminal who directly use mount(8).
346 */
347 static void systemd_hint(void)
348 {
349 static int fstab_check_done = 0;
350
351 if (fstab_check_done == 0) {
352 struct stat a, b;
353
354 if (isatty(STDERR_FILENO) &&
355 isatty(STDOUT_FILENO) &&
356 stat(_PATH_SD_UNITSLOAD, &a) == 0 &&
357 stat(_PATH_MNTTAB, &b) == 0 &&
358 cmp_stat_mtime(&a, &b, <))
359 printf(_(
360 "mount: (hint) your fstab has been modified, but systemd still uses\n"
361 " the old version; use 'systemctl daemon-reload' to reload.\n"));
362
363 fstab_check_done = 1;
364 }
365 }
366 #else
367 # define systemd_hint()
368 #endif
369
370 static size_t libmount_mesgs(struct libmnt_context *cxt, char type)
371 {
372 size_t n = mnt_context_get_nmesgs(cxt, type);
373 char **mesgs = mnt_context_get_mesgs(cxt);
374 char **s;
375
376 if (!n)
377 return 0;
378
379 /* Header */
380 switch (type) {
381 case 'e':
382 fputs(P_("mount error:\n", "mount errors:\n", n), stderr);
383 break;
384 case 'w':
385 fputs(P_("mount warning:\n", "mount warnings:\n", n), stdout);
386 break;
387 case 'i':
388 fputs(P_("mount info:\n", "mount infos:\n", n), stdout);
389 break;
390 }
391
392 /* Messages */
393 UL_STRV_FOREACH(s, mesgs) {
394 switch (type) {
395 case 'e':
396 if (!ul_startswith(*s, "e "))
397 break;
398 fprintf(stderr, " * %s\n", (*s) + 2);
399 break;
400 case 'w':
401 if (!ul_startswith(*s, "w "))
402 break;
403 fprintf(stdout, " * %s\n", (*s) + 2);
404 break;
405 case 'i':
406 if (!ul_startswith(*s, "i "))
407 break;
408 fprintf(stdout, " * %s\n", (*s) + 2);
409 break;
410 }
411 }
412
413 return n;
414 }
415
416 /*
417 * Returns exit status (MNT_EX_*) and/or prints error message.
418 */
419 static int mk_exit_code(struct libmnt_context *cxt, int rc)
420 {
421 const char *tgt;
422 char buf[BUFSIZ] = { 0 };
423
424 rc = mnt_context_get_excode(cxt, rc, buf, sizeof(buf));
425 tgt = mnt_context_get_target(cxt);
426
427 /* error messages
428 *
429 * Note that mnt_context_get_excode() is used for backward compatibility and
430 * will fill @buf with error messages from mnt_context_get_mesgs(). Therefore,
431 * calling libmount_mesgs(cxt, 'e') is currently unnecessary.
432 */
433 if (*buf) {
434 const char *spec = tgt;
435 if (!spec)
436 spec = mnt_context_get_source(cxt);
437 if (!spec)
438 spec = "???";
439 warnx("%s: %s.", spec, buf);
440
441 if (mnt_context_syscall_called(cxt) &&
442 mnt_context_get_syscall_errno(cxt) != 0)
443 fprintf(stderr, _(" dmesg(1) may have more information after failed mount system call.\n"));
444 }
445
446 /* warning messages */
447 libmount_mesgs(cxt, 'w');
448
449 /* info messages */
450 if (mnt_context_is_verbose(cxt))
451 libmount_mesgs(cxt, 'i');
452
453 /* extra mount(8) messages */
454 if (rc == MNT_EX_SUCCESS && mnt_context_get_status(cxt) == 1) {
455 selinux_warning(cxt, tgt);
456 }
457
458 systemd_hint();
459
460 return rc;
461 }
462
463 static struct libmnt_table *append_fstab(struct libmnt_context *cxt,
464 struct libmnt_table *fstab,
465 const char *path)
466 {
467
468 if (!fstab) {
469 fstab = mnt_new_table();
470 if (!fstab)
471 err(MNT_EX_SYSERR, _("failed to initialize libmount table"));
472
473 mnt_table_set_parser_errcb(fstab, table_parser_errcb);
474 mnt_context_set_fstab(cxt, fstab);
475
476 mnt_unref_table(fstab); /* reference is handled by @cxt now */
477 }
478
479 if (mnt_table_parse_fstab(fstab, path))
480 errx(MNT_EX_USAGE,_("%s: failed to parse"), path);
481
482 return fstab;
483 }
484
485 /*
486 * Check source and target paths -- non-root user should not be able to
487 * resolve paths which are unreadable for them.
488 */
489 static int sanitize_paths(struct libmnt_context *cxt)
490 {
491 const char *p;
492 struct libmnt_fs *fs = mnt_context_get_fs(cxt);
493
494 if (!fs)
495 return 0;
496
497 p = mnt_fs_get_target(fs);
498 if (p) {
499 char *np = canonicalize_path_restricted(p);
500 if (!np)
501 return -EPERM;
502 mnt_fs_set_target(fs, np);
503 free(np);
504 }
505
506 p = mnt_fs_get_srcpath(fs);
507 if (p) {
508 char *np = canonicalize_path_restricted(p);
509 if (!np)
510 return -EPERM;
511 mnt_fs_set_source(fs, np);
512 free(np);
513 }
514 return 0;
515 }
516
517 static void append_option(struct libmnt_context *cxt, const char *opt, const char *arg)
518 {
519 char *o = NULL;
520
521 if (opt && !ul_optstr_is_valid(opt))
522 errx(MNT_EX_USAGE, _("unsupported option format: %s"), opt);
523
524 if (opt && arg && *arg)
525 xasprintf(&o, "%s=\"%s\"", opt, arg);
526
527 if (mnt_context_append_options(cxt, o ? : opt))
528 err(MNT_EX_SYSERR, _("failed to append option '%s'"), o ? : opt);
529
530 free(o);
531 }
532
533 static int has_remount_flag(struct libmnt_context *cxt)
534 {
535 unsigned long mflags = 0;
536
537 if (mnt_context_get_mflags(cxt, &mflags))
538 return 0;
539
540 return mflags & MS_REMOUNT;
541 }
542
543 static void __attribute__((__noreturn__)) usage(void)
544 {
545 FILE *out = stdout;
546
547 fputs(USAGE_HEADER, out);
548 fprintf(out, _(
549 " %1$s [-lhV]\n"
550 " %1$s -a [options]\n"
551 " %1$s [options] [--source] <source> | [--target] <directory>\n"
552 " %1$s [options] <source> <directory>\n"
553 " %1$s <operation> <mountpoint> [<target>]\n"),
554 program_invocation_short_name);
555
556 fputs(USAGE_SEPARATOR, out);
557 fputs(_("Mount a filesystem.\n"), out);
558
559 fputs(USAGE_OPTIONS, out);
560 fputs(_(" -a, --all mount all filesystems mentioned in fstab\n"), out);
561 fputs(_(" -c, --no-canonicalize don't canonicalize paths\n"), out);
562 fputs(_(" -f, --fake dry run; skip the mount(2) syscall\n"), out);
563 fputs(_(" -F, --fork fork off for each device (use with -a)\n"), out);
564 fputs(_(" -T, --fstab <path> alternative file to /etc/fstab\n"), out);
565 fputs(_(" -i, --internal-only don't call the mount.<type> helpers\n"), out);
566 fputs(_(" -l, --show-labels show also filesystem labels\n"), out);
567 fputs(_(" --map-groups <inner>:<outer>:<count>\n"
568 " add the specified GID map to an ID-mapped mount\n"), out);
569 fputs(_(" --map-users <inner>:<outer>:<count>\n"
570 " add the specified UID map to an ID-mapped mount\n"), out);
571 fputs(_(" --map-users /proc/<pid>/ns/user\n"
572 " specify the user namespace for an ID-mapped mount\n"), out);
573 fputs(_(" -m, --mkdir[=<mode>] alias to '-o X-mount.mkdir[=<mode>]'\n"), out);
574 fputs(_(" -n, --no-mtab don't write to /etc/mtab\n"), out);
575 fputs(_(" --options-mode <mode>\n"
576 " what to do with options loaded from fstab\n"), out);
577 fputs(_(" --options-source <source>\n"
578 " mount options source\n"), out);
579 fputs(_(" --options-source-force\n"
580 " force use of options from fstab/mtab\n"), out);
581 fputs(_(" --onlyonce check if filesystem is already mounted\n"), out);
582 fputs(_(" -o, --options <list> comma-separated list of mount options\n"), out);
583 fputs(_(" -O, --test-opts <list> limit the set of filesystems (use with -a)\n"), out);
584 fputs(_(" -r, --read-only mount the filesystem read-only (same as -o ro)\n"), out);
585 fputs(_(" -t, --types <list> limit the set of filesystem types\n"), out);
586 fputs(_(" --source <src> explicitly specifies source (path, label, uuid)\n"), out);
587 fputs(_(" --target <target> explicitly specifies mountpoint\n"), out);
588 fputs(_(" --target-prefix <path>\n"
589 " specifies path used for all mountpoints\n"), out);
590 fputs(_(" -v, --verbose say what is being done\n"), out);
591 fputs(_(" -w, --rw, --read-write mount the filesystem read-write (default)\n"), out);
592 fputs(_(" -N, --namespace <ns> perform mount in another namespace\n"), out);
593
594 fputs(USAGE_SEPARATOR, out);
595 fprintf(out, USAGE_HELP_OPTIONS(25));
596
597 fputs(USAGE_SEPARATOR, out);
598 fputs(_("Source:\n"), out);
599 fputs(_(" -L, --label <label> synonym for LABEL=<label>\n"), out);
600 fputs(_(" -U, --uuid <uuid> synonym for UUID=<uuid>\n"), out);
601 fputs(_(" LABEL=<label> specifies device by filesystem label\n"), out);
602 fputs(_(" UUID=<uuid> specifies device by filesystem UUID\n"), out);
603 fputs(_(" PARTLABEL=<label> specifies device by partition label\n"), out);
604 fputs(_(" PARTUUID=<uuid> specifies device by partition UUID\n"), out);
605 fputs(_(" ID=<id> specifies device by udev hardware ID\n"), out);
606 fputs(_(" <device> specifies device by path\n"), out);
607 fputs(_(" <directory> mountpoint for bind mounts (see --bind/rbind)\n"), out);
608 fputs(_(" <file> regular file for loopdev setup\n"), out);
609
610 fputs(USAGE_SEPARATOR, out);
611 fputs(_("Operations:\n"), out);
612 fputs(_(" -B, --bind mount a subtree somewhere else (same as -o bind)\n"), out);
613 fputs(_(" -M, --move move a subtree to some other place\n"), out);
614 fputs(_(" -R, --rbind mount a subtree and all submounts somewhere else\n"), out);
615 fputs(_(" --make-shared mark a subtree as shared\n"), out);
616 fputs(_(" --make-slave mark a subtree as slave\n"), out);
617 fputs(_(" --make-private mark a subtree as private\n"), out);
618 fputs(_(" --make-unbindable mark a subtree as unbindable\n"), out);
619 fputs(_(" --make-rshared recursively mark a whole subtree as shared\n"), out);
620 fputs(_(" --make-rslave recursively mark a whole subtree as slave\n"), out);
621 fputs(_(" --make-rprivate recursively mark a whole subtree as private\n"), out);
622 fputs(_(" --make-runbindable recursively mark a whole subtree as unbindable\n"), out);
623
624 fprintf(out, USAGE_MAN_TAIL("mount(8)"));
625
626 exit(MNT_EX_SUCCESS);
627 }
628
629 struct flag_str {
630 int value;
631 char *str;
632 };
633
634 static int omode2mask(const char *str)
635 {
636 size_t i;
637
638 static const struct flag_str flags[] = {
639 { MNT_OMODE_IGNORE, "ignore" },
640 { MNT_OMODE_APPEND, "append" },
641 { MNT_OMODE_PREPEND, "prepend" },
642 { MNT_OMODE_REPLACE, "replace" },
643 };
644
645 for (i = 0; i < ARRAY_SIZE(flags); i++) {
646 if (!strcmp(str, flags[i].str))
647 return flags[i].value;
648 }
649 return -EINVAL;
650 }
651
652 static long osrc2mask(const char *str, size_t len)
653 {
654 size_t i;
655
656 static const struct flag_str flags[] = {
657 { MNT_OMODE_FSTAB, "fstab" },
658 { MNT_OMODE_MTAB, "mtab" },
659 { MNT_OMODE_NOTAB, "disable" },
660 };
661
662 for (i = 0; i < ARRAY_SIZE(flags); i++) {
663 if (!strncmp(str, flags[i].str, len) && !flags[i].str[len])
664 return flags[i].value;
665 }
666 return -EINVAL;
667 }
668
669 static pid_t parse_pid(const char *str)
670 {
671 char *end;
672 pid_t ret;
673
674 errno = 0;
675 ret = strtoul(str, &end, 10);
676
677 if (ret < 0 || errno || end == str || (end && *end))
678 return 0;
679 return ret;
680 }
681
682 int main(int argc, char **argv)
683 {
684 int c, rc = MNT_EX_SUCCESS, all = 0, show_labels = 0;
685 struct libmnt_context *cxt;
686 struct libmnt_table *fstab = NULL;
687 char *idmap = NULL;
688 char *srcbuf = NULL;
689 char *types = NULL;
690 int oper = 0, is_move = 0;
691 int propa = 0;
692 int optmode = 0, optmode_mode = 0, optmode_src = 0;
693
694 enum {
695 MOUNT_OPT_SHARED = CHAR_MAX + 1,
696 MOUNT_OPT_SLAVE,
697 MOUNT_OPT_PRIVATE,
698 MOUNT_OPT_UNBINDABLE,
699 MOUNT_OPT_RSHARED,
700 MOUNT_OPT_RSLAVE,
701 MOUNT_OPT_RPRIVATE,
702 MOUNT_OPT_RUNBINDABLE,
703 MOUNT_OPT_MAP_GROUPS,
704 MOUNT_OPT_MAP_USERS,
705 MOUNT_OPT_TARGET,
706 MOUNT_OPT_TARGET_PREFIX,
707 MOUNT_OPT_SOURCE,
708 MOUNT_OPT_OPTMODE,
709 MOUNT_OPT_OPTSRC,
710 MOUNT_OPT_OPTSRC_FORCE,
711 MOUNT_OPT_ONLYONCE
712 };
713
714 static const struct option longopts[] = {
715 { "all", no_argument, NULL, 'a' },
716 { "fake", no_argument, NULL, 'f' },
717 { "fstab", required_argument, NULL, 'T' },
718 { "fork", no_argument, NULL, 'F' },
719 { "help", no_argument, NULL, 'h' },
720 { "no-mtab", no_argument, NULL, 'n' },
721 { "read-only", no_argument, NULL, 'r' },
722 { "ro", no_argument, NULL, 'r' },
723 { "verbose", no_argument, NULL, 'v' },
724 { "version", no_argument, NULL, 'V' },
725 { "read-write", no_argument, NULL, 'w' },
726 { "rw", no_argument, NULL, 'w' },
727 { "options", required_argument, NULL, 'o' },
728 { "test-opts", required_argument, NULL, 'O' },
729 { "types", required_argument, NULL, 't' },
730 { "uuid", required_argument, NULL, 'U' },
731 { "label", required_argument, NULL, 'L' },
732 { "bind", no_argument, NULL, 'B' },
733 { "move", no_argument, NULL, 'M' },
734 { "rbind", no_argument, NULL, 'R' },
735 { "make-shared", no_argument, NULL, MOUNT_OPT_SHARED },
736 { "make-slave", no_argument, NULL, MOUNT_OPT_SLAVE },
737 { "make-private", no_argument, NULL, MOUNT_OPT_PRIVATE },
738 { "make-unbindable", no_argument, NULL, MOUNT_OPT_UNBINDABLE },
739 { "make-rshared", no_argument, NULL, MOUNT_OPT_RSHARED },
740 { "make-rslave", no_argument, NULL, MOUNT_OPT_RSLAVE },
741 { "make-rprivate", no_argument, NULL, MOUNT_OPT_RPRIVATE },
742 { "make-runbindable", no_argument, NULL, MOUNT_OPT_RUNBINDABLE },
743 { "map-groups", required_argument, NULL, MOUNT_OPT_MAP_GROUPS },
744 { "map-users", required_argument, NULL, MOUNT_OPT_MAP_USERS },
745 { "mkdir", optional_argument, NULL, 'm' },
746 { "no-canonicalize", no_argument, NULL, 'c' },
747 { "internal-only", no_argument, NULL, 'i' },
748 { "show-labels", no_argument, NULL, 'l' },
749 { "target", required_argument, NULL, MOUNT_OPT_TARGET },
750 { "target-prefix", required_argument, NULL, MOUNT_OPT_TARGET_PREFIX },
751 { "source", required_argument, NULL, MOUNT_OPT_SOURCE },
752 { "onlyonce", no_argument, NULL, MOUNT_OPT_ONLYONCE },
753 { "options-mode", required_argument, NULL, MOUNT_OPT_OPTMODE },
754 { "options-source", required_argument, NULL, MOUNT_OPT_OPTSRC },
755 { "options-source-force", no_argument, NULL, MOUNT_OPT_OPTSRC_FORCE},
756 { "namespace", required_argument, NULL, 'N' },
757 { NULL, 0, NULL, 0 }
758 };
759
760 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
761 { 'B','M','R' }, /* bind,move,rbind */
762 { 'L','U', MOUNT_OPT_SOURCE }, /* label,uuid,source */
763 { 0 }
764 };
765 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
766
767 __sanitize_env(&envs_removed);
768 setlocale(LC_ALL, "");
769 bindtextdomain(PACKAGE, LOCALEDIR);
770 textdomain(PACKAGE);
771 close_stdout_atexit();
772
773 strutils_set_exitcode(MNT_EX_USAGE);
774
775 mnt_init_debug(0);
776 cxt = mnt_new_context();
777 if (!cxt)
778 err(MNT_EX_SYSERR, _("libmount context allocation failed"));
779
780 mnt_context_set_tables_errcb(cxt, table_parser_errcb);
781
782 while ((c = getopt_long(argc, argv, "aBcfFhilL:m::Mno:O:rRsU:vVwt:T:N:",
783 longopts, NULL)) != -1) {
784
785 /* only few options are allowed for non-root users */
786 if (mnt_context_is_restricted(cxt) &&
787 !strchr("hlLUVvrist", c) &&
788 c != MOUNT_OPT_TARGET &&
789 c != MOUNT_OPT_SOURCE)
790 suid_drop(cxt);
791
792 err_exclusive_options(c, longopts, excl, excl_st);
793
794 switch(c) {
795 case 'a':
796 all = 1;
797 break;
798 case 'c':
799 mnt_context_disable_canonicalize(cxt, TRUE);
800 break;
801 case 'f':
802 mnt_context_enable_fake(cxt, TRUE);
803 break;
804 case 'F':
805 mnt_context_enable_fork(cxt, TRUE);
806 break;
807 case 'i':
808 mnt_context_disable_helpers(cxt, TRUE);
809 break;
810 case 'n':
811 mnt_context_disable_mtab(cxt, TRUE);
812 break;
813 case 'r':
814 append_option(cxt, "ro", NULL);
815 mnt_context_enable_rwonly_mount(cxt, FALSE);
816 break;
817 case 'v':
818 mnt_context_enable_verbose(cxt, TRUE);
819 break;
820 case 'w':
821 append_option(cxt, "rw", NULL);
822 mnt_context_enable_rwonly_mount(cxt, TRUE);
823 break;
824 case 'o':
825 /* "move" is not supported as option string in libmount
826 * to avoid use in fstab */
827 if (mnt_optstr_get_option(optarg, "move", NULL, 0) == 0) {
828 char *o = xstrdup(optarg);
829
830 mnt_optstr_remove_option(&o, "move");
831 if (o && *o)
832 append_option(cxt, o, NULL);
833 oper = is_move = 1;
834 free(o);
835 } else
836 append_option(cxt, optarg, NULL);
837 break;
838 case 'O':
839 if (mnt_context_set_options_pattern(cxt, optarg))
840 err(MNT_EX_SYSERR, _("failed to set options pattern"));
841 break;
842 case 'L':
843 xasprintf(&srcbuf, "LABEL=\"%s\"", optarg);
844 mnt_context_disable_swapmatch(cxt, 1);
845 mnt_context_set_source(cxt, srcbuf);
846 free(srcbuf);
847 break;
848 case 'U':
849 xasprintf(&srcbuf, "UUID=\"%s\"", optarg);
850 mnt_context_disable_swapmatch(cxt, 1);
851 mnt_context_set_source(cxt, srcbuf);
852 free(srcbuf);
853 break;
854 case 'l':
855 show_labels = 1;
856 break;
857 case 't':
858 types = optarg;
859 break;
860 case 'T':
861 fstab = append_fstab(cxt, fstab, optarg);
862 break;
863 case 's':
864 mnt_context_enable_sloppy(cxt, TRUE);
865 break;
866 case 'B':
867 oper = 1;
868 append_option(cxt, "bind", NULL);
869 break;
870 case 'M':
871 oper = 1;
872 is_move = 1;
873 break;
874 case 'm':
875 if (optarg && *optarg == '=')
876 optarg++;
877 append_option(cxt, "X-mount.mkdir", optarg);
878 break;
879 case 'R':
880 oper = 1;
881 append_option(cxt, "rbind", NULL);
882 break;
883 case 'N':
884 {
885 char path[PATH_MAX];
886 pid_t pid = parse_pid(optarg);
887
888 if (pid)
889 snprintf(path, sizeof(path), "/proc/%i/ns/mnt", pid);
890
891 if (mnt_context_set_target_ns(cxt, pid ? path : optarg))
892 err(MNT_EX_SYSERR, _("failed to set target namespace to %s"), pid ? path : optarg);
893 break;
894 }
895 case MOUNT_OPT_SHARED:
896 append_option(cxt, "shared", NULL);
897 propa = 1;
898 break;
899 case MOUNT_OPT_SLAVE:
900 append_option(cxt, "slave", NULL);
901 propa = 1;
902 break;
903 case MOUNT_OPT_PRIVATE:
904 append_option(cxt, "private", NULL);
905 propa = 1;
906 break;
907 case MOUNT_OPT_UNBINDABLE:
908 append_option(cxt, "unbindable", NULL);
909 propa = 1;
910 break;
911 case MOUNT_OPT_RSHARED:
912 append_option(cxt, "rshared", NULL);
913 propa = 1;
914 break;
915 case MOUNT_OPT_RSLAVE:
916 append_option(cxt, "rslave", NULL);
917 propa = 1;
918 break;
919 case MOUNT_OPT_RPRIVATE:
920 append_option(cxt, "rprivate", NULL);
921 propa = 1;
922 break;
923 case MOUNT_OPT_RUNBINDABLE:
924 append_option(cxt, "runbindable", NULL);
925 propa = 1;
926 break;
927 case MOUNT_OPT_MAP_GROUPS:
928 case MOUNT_OPT_MAP_USERS:
929 if (*optarg == '=')
930 optarg++;
931 if (idmap && (*idmap == '/' || *optarg == '/')) {
932 warnx(_("bad usage"));
933 errtryhelp(MNT_EX_USAGE);
934 } else if (*optarg == '/') {
935 idmap = xstrdup(optarg);
936 } else {
937 char *tmp;
938 xasprintf(&tmp, "%s%s%s%s", idmap ? idmap : "", idmap ? " " : "",
939 c == MOUNT_OPT_MAP_GROUPS ? "g:" : "u:", optarg);
940 free(idmap);
941 idmap = tmp;
942 }
943 break;
944 case MOUNT_OPT_TARGET:
945 mnt_context_disable_swapmatch(cxt, 1);
946 mnt_context_set_target(cxt, optarg);
947 break;
948 case MOUNT_OPT_TARGET_PREFIX:
949 mnt_context_set_target_prefix(cxt, optarg);
950 break;
951 case MOUNT_OPT_SOURCE:
952 mnt_context_disable_swapmatch(cxt, 1);
953 mnt_context_set_source(cxt, optarg);
954 break;
955 case MOUNT_OPT_OPTMODE:
956 optmode_mode = omode2mask(optarg);
957 if (optmode_mode == -EINVAL) {
958 warnx(_("bad usage"));
959 errtryhelp(MNT_EX_USAGE);
960 }
961 break;
962 case MOUNT_OPT_OPTSRC:
963 {
964 unsigned long tmp = 0;
965 if (string_to_bitmask(optarg, &tmp, osrc2mask)) {
966 warnx(_("bad usage"));
967 errtryhelp(MNT_EX_USAGE);
968 }
969 optmode_src = tmp;
970 break;
971 }
972 case MOUNT_OPT_OPTSRC_FORCE:
973 optmode |= MNT_OMODE_FORCE;
974 break;
975 case MOUNT_OPT_ONLYONCE:
976 mnt_context_enable_onlyonce(cxt, 1);
977 break;
978 case 'h':
979 mnt_free_context(cxt);
980 usage();
981 case 'V':
982 mnt_free_context(cxt);
983 mount_print_version();
984 default:
985 errtryhelp(MNT_EX_USAGE);
986 }
987 }
988
989 argc -= optind;
990 argv += optind;
991
992 if (idmap)
993 append_option(cxt, "X-mount.idmap", idmap);
994
995 optmode |= optmode_mode | optmode_src;
996 if (optmode) {
997 if (!optmode_mode)
998 optmode |= MNT_OMODE_PREPEND;
999 if (!optmode_src)
1000 optmode |= MNT_OMODE_FSTAB | MNT_OMODE_MTAB;
1001 mnt_context_set_optsmode(cxt, optmode);
1002 }
1003
1004 if (fstab && !mnt_context_is_nocanonicalize(cxt)) {
1005 /*
1006 * We have external (context independent) fstab instance, let's
1007 * make a connection between the fstab and the canonicalization
1008 * cache.
1009 */
1010 mnt_table_set_cache(fstab, mnt_context_get_cache(cxt));
1011 }
1012
1013 if (!mnt_context_get_source(cxt) &&
1014 !mnt_context_get_target(cxt) &&
1015 !argc &&
1016 !all) {
1017 if (oper || mnt_context_get_options(cxt)) {
1018 warnx(_("bad usage"));
1019 errtryhelp(MNT_EX_USAGE);
1020 }
1021 print_all(cxt, types, show_labels);
1022 goto done;
1023 }
1024
1025 /* Non-root users are allowed to use -t to print_all(),
1026 but not to mount */
1027 if (mnt_context_is_restricted(cxt) && types)
1028 suid_drop(cxt);
1029
1030 if (oper && (types || all || mnt_context_get_source(cxt))) {
1031 warnx(_("bad usage"));
1032 errtryhelp(MNT_EX_USAGE);
1033 }
1034
1035 if (types && (all || strchr(types, ',') ||
1036 strncmp(types, "no", 2) == 0))
1037 mnt_context_set_fstype_pattern(cxt, types);
1038 else if (types)
1039 mnt_context_set_fstype(cxt, types);
1040
1041 if (all) {
1042 /*
1043 * A) Mount all
1044 */
1045 if (has_remount_flag(cxt))
1046 rc = remount_all(cxt);
1047 else
1048 rc = mount_all(cxt);
1049 goto done;
1050
1051 } else if (argc == 0 && (mnt_context_get_source(cxt) ||
1052 mnt_context_get_target(cxt))) {
1053 /*
1054 * B) mount -L|-U|--source|--target
1055 *
1056 * non-root may specify source *or* target, but not both
1057 */
1058 if (mnt_context_is_restricted(cxt) &&
1059 mnt_context_get_source(cxt) &&
1060 mnt_context_get_target(cxt))
1061 suid_drop(cxt);
1062
1063 } else if (argc == 1 && (!mnt_context_get_source(cxt) ||
1064 !mnt_context_get_target(cxt))) {
1065 /*
1066 * C) mount [-L|-U|--source] <target>
1067 * mount [--target <dir>] <source>
1068 * mount <source|target>
1069 *
1070 * non-root may specify source *or* target, but not both
1071 *
1072 * It does not matter for libmount if we set source or target
1073 * here (the library is able to swap it), but it matters for
1074 * sanitize_paths().
1075 */
1076 int istag = mnt_tag_is_valid(argv[0]);
1077
1078 if (istag && mnt_context_get_source(cxt))
1079 /* -L, -U or --source together with LABEL= or UUID= */
1080 errx(MNT_EX_USAGE, _("source specified more than once"));
1081 else if (istag || mnt_context_get_target(cxt))
1082 mnt_context_set_source(cxt, argv[0]);
1083 else
1084 mnt_context_set_target(cxt, argv[0]);
1085
1086 if (mnt_context_is_restricted(cxt) &&
1087 mnt_context_get_source(cxt) &&
1088 mnt_context_get_target(cxt))
1089 suid_drop(cxt);
1090
1091 } else if (argc == 2 && !mnt_context_get_source(cxt)
1092 && !mnt_context_get_target(cxt)) {
1093 /*
1094 * D) mount <source> <target>
1095 */
1096 if (mnt_context_is_restricted(cxt))
1097 suid_drop(cxt);
1098
1099 mnt_context_set_source(cxt, argv[0]);
1100 mnt_context_set_target(cxt, argv[1]);
1101
1102 } else {
1103 warnx(_("bad usage"));
1104 errtryhelp(MNT_EX_USAGE);
1105 }
1106
1107 if (mnt_context_is_restricted(cxt) && sanitize_paths(cxt) != 0)
1108 suid_drop(cxt);
1109
1110 if (is_move)
1111 /* "move" as option string is not supported by libmount */
1112 mnt_context_set_mflags(cxt, MS_MOVE);
1113
1114 if ((oper && !has_remount_flag(cxt)) || propa)
1115 /* For --make-* or --bind is fstab/mtab unnecessary */
1116 mnt_context_set_optsmode(cxt, MNT_OMODE_NOTAB);
1117
1118 rc = mnt_context_mount(cxt);
1119
1120 if (rc == -EPERM
1121 && mnt_context_is_restricted(cxt)
1122 && !mnt_context_syscall_called(cxt)) {
1123 /* Try it again without permissions */
1124 suid_drop(cxt);
1125 rc = mnt_context_mount(cxt);
1126 }
1127 rc = mk_exit_code(cxt, rc);
1128
1129 if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
1130 success_message(cxt);
1131 done:
1132 mnt_free_context(cxt);
1133 env_list_free(envs_removed);
1134 return rc;
1135 }