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