/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
* mount(8) -- mount a filesystem
*
* Copyright (C) 2011 Red Hat, Inc. All rights reserved.
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "strutils.h"
#include "closestream.h"
#include "canonicalize.h"
+#include "pathnames.h"
#define XALLOC_EXIT_CODE MNT_EX_SYSERR
#include "xalloc.h"
const uid_t ruid = getuid();
const uid_t euid = geteuid();
- if (ruid != 0 && euid == 0) {
- if (setgid(getgid()) < 0)
- err(MNT_EX_FAIL, _("setgid() failed"));
-
- if (setuid(getuid()) < 0)
- err(MNT_EX_FAIL, _("setuid() failed"));
- }
+ if (ruid != 0 && euid == 0 && drop_permissions() != 0)
+ err(MNT_EX_FAIL, _("drop permissions failed"));
/* be paranoid and check it, setuid(0) has to fail */
if (ruid != 0 && setuid(0) == 0)
struct libmnt_fs *fs;
struct libmnt_cache *cache = NULL;
+ mnt_context_enable_noautofs(cxt, 1);
+
if (mnt_context_get_mtab(cxt, &tb))
err(MNT_EX_SYSERR, _("failed to read mtab"));
if (type && pattern && !mnt_match_fstype(type, pattern))
continue;
- if (!mnt_fs_is_pseudofs(fs) && !mnt_fs_is_netfs(fs))
+ if (mnt_fs_is_regularfs(fs))
xsrc = mnt_pretty_path(src, cache);
printf ("%s on ", xsrc ? xsrc : src);
safe_fputs(mnt_fs_get_target(fs));
{
if (tgt && mnt_context_is_verbose(cxt) && is_selinux_enabled() > 0) {
-# ifdef HAVE_SELINUX_CONTEXT_T
- security_context_t raw = NULL, def = NULL; /* deprecated */
-# else
- char *raw = NULL, *def = NULL; /* since libselinux >= 3.1 */
-# endif
+ char *raw = NULL, *def = NULL;
if (getfilecon(tgt, &raw) > 0
&& security_get_initial_context("file", &def) == 0) {
# define selinux_warning(_x, _y)
#endif
+
+#ifdef USE_SYSTEMD
+static void systemd_hint(void)
+{
+ static int fstab_check_done = 0;
+
+ if (fstab_check_done == 0) {
+ struct stat a, b;
+
+ if (isatty(STDERR_FILENO) &&
+ stat(_PATH_SD_UNITSLOAD, &a) == 0 &&
+ stat(_PATH_MNTTAB, &b) == 0 &&
+ cmp_stat_mtime(&a, &b, <))
+ printf(_(
+ "mount: (hint) your fstab has been modified, but systemd still uses\n"
+ " the old version; use 'systemctl daemon-reload' to reload.\n"));
+
+ fstab_check_done = 1;
+ }
+}
+#else
+# define systemd_hint()
+#endif
+
+
/*
* Returns exit status (MNT_EX_*) and/or prints error message.
*/
if (!spec)
spec = "???";
warnx("%s: %s.", spec, buf);
+
+ if (mnt_context_syscall_called(cxt) &&
+ mnt_context_get_syscall_errno(cxt) != 0)
+ fprintf(stderr, _(" dmesg(1) may have more information after failed mount system call.\n"));
}
if (rc == MNT_EX_SUCCESS && mnt_context_get_status(cxt) == 1) {
selinux_warning(cxt, tgt);
}
+
+ systemd_hint();
+
return rc;
}
return 0;
}
-static void append_option(struct libmnt_context *cxt, const char *opt)
+static void append_option(struct libmnt_context *cxt, const char *opt, const char *arg)
{
+ char *o = NULL;
+
if (opt && (*opt == '=' || *opt == '\'' || *opt == '\"' || isblank(*opt)))
errx(MNT_EX_USAGE, _("unsupported option format: %s"), opt);
- if (mnt_context_append_options(cxt, opt))
- err(MNT_EX_SYSERR, _("failed to append option '%s'"), opt);
+
+ if (arg && *arg)
+ xasprintf(&o, "%s=\"%s\"", opt, arg);
+
+ if (mnt_context_append_options(cxt, o ? : opt))
+ err(MNT_EX_SYSERR, _("failed to append option '%s'"), o ? : opt);
+
+ free(o);
}
static int has_remount_flag(struct libmnt_context *cxt)
static void __attribute__((__noreturn__)) usage(void)
{
FILE *out = stdout;
+
fputs(USAGE_HEADER, out);
fprintf(out, _(
" %1$s [-lhV]\n"
fputs(_("Mount a filesystem.\n"), out);
fputs(USAGE_OPTIONS, out);
- fprintf(out, _(
- " -a, --all mount all filesystems mentioned in fstab\n"
- " -c, --no-canonicalize don't canonicalize paths\n"
- " -f, --fake dry run; skip the mount(2) syscall\n"
- " -F, --fork fork off for each device (use with -a)\n"
- " -T, --fstab <path> alternative file to /etc/fstab\n"));
- fprintf(out, _(
- " -i, --internal-only don't call the mount.<type> helpers\n"));
- fprintf(out, _(
- " -l, --show-labels show also filesystem labels\n"));
- fprintf(out, _(
- " -n, --no-mtab don't write to /etc/mtab\n"));
- fprintf(out, _(
- " --options-mode <mode>\n"
- " what to do with options loaded from fstab\n"
- " --options-source <source>\n"
- " mount options source\n"
- " --options-source-force\n"
- " force use of options from fstab/mtab\n"));
- fprintf(out, _(
- " -o, --options <list> comma-separated list of mount options\n"
- " -O, --test-opts <list> limit the set of filesystems (use with -a)\n"
- " -r, --read-only mount the filesystem read-only (same as -o ro)\n"
- " -t, --types <list> limit the set of filesystem types\n"));
- fprintf(out, _(
- " --source <src> explicitly specifies source (path, label, uuid)\n"
- " --target <target> explicitly specifies mountpoint\n"));
- fprintf(out, _(
- " --target-prefix <path>\n"
- " specifies path use for all mountpoints\n"));
- fprintf(out, _(
- " -v, --verbose say what is being done\n"));
- fprintf(out, _(
- " -w, --rw, --read-write mount the filesystem read-write (default)\n"));
- fprintf(out, _(
- " -N, --namespace <ns> perform mount in another namespace\n"));
+ fputs(_(" -a, --all mount all filesystems mentioned in fstab\n"), out);
+ fputs(_(" -c, --no-canonicalize don't canonicalize paths\n"), out);
+ fputs(_(" -f, --fake dry run; skip the mount(2) syscall\n"), out);
+ fputs(_(" -F, --fork fork off for each device (use with -a)\n"), out);
+ fputs(_(" -T, --fstab <path> alternative file to /etc/fstab\n"), out);
+ fputs(_(" -i, --internal-only don't call the mount.<type> helpers\n"), out);
+ fputs(_(" -l, --show-labels show also filesystem labels\n"), out);
+ fputs(_(" --map-groups <inner>:<outer>:<count>\n"
+ " add the specified GID map to an ID-mapped mount\n"), out);
+ fputs(_(" --map-users <inner>:<outer>:<count>\n"
+ " add the specified UID map to an ID-mapped mount\n"), out);
+ fputs(_(" --map-users /proc/<pid>/ns/user\n"
+ " specify the user namespace for an ID-mapped mount\n"), out);
+ fputs(_(" -m, --mkdir[=<mode>] alias to '-o X-mount.mkdir[=<mode>]'\n"), out);
+ fputs(_(" -n, --no-mtab don't write to /etc/mtab\n"), out);
+ fputs(_(" --options-mode <mode>\n"
+ " what to do with options loaded from fstab\n"), out);
+ fputs(_(" --options-source <source>\n"
+ " mount options source\n"), out);
+ fputs(_(" --options-source-force\n"
+ " force use of options from fstab/mtab\n"), out);
+ fputs(_(" --onlyonce check if filesystem is already mounted\n"), out);
+ fputs(_(" -o, --options <list> comma-separated list of mount options\n"), out);
+ fputs(_(" -O, --test-opts <list> limit the set of filesystems (use with -a)\n"), out);
+ fputs(_(" -r, --read-only mount the filesystem read-only (same as -o ro)\n"), out);
+ fputs(_(" -t, --types <list> limit the set of filesystem types\n"), out);
+ fputs(_(" --source <src> explicitly specifies source (path, label, uuid)\n"), out);
+ fputs(_(" --target <target> explicitly specifies mountpoint\n"), out);
+ fputs(_(" --target-prefix <path>\n"
+ " specifies path used for all mountpoints\n"), out);
+ fputs(_(" -v, --verbose say what is being done\n"), out);
+ fputs(_(" -w, --rw, --read-write mount the filesystem read-write (default)\n"), out);
+ fputs(_(" -N, --namespace <ns> perform mount in another namespace\n"), out);
fputs(USAGE_SEPARATOR, out);
- printf(USAGE_HELP_OPTIONS(25));
-
- fprintf(out, _(
- "\nSource:\n"
- " -L, --label <label> synonym for LABEL=<label>\n"
- " -U, --uuid <uuid> synonym for UUID=<uuid>\n"
- " LABEL=<label> specifies device by filesystem label\n"
- " UUID=<uuid> specifies device by filesystem UUID\n"
- " PARTLABEL=<label> specifies device by partition label\n"
- " PARTUUID=<uuid> specifies device by partition UUID\n"
- " ID=<id> specifies device by udev hardware ID\n"));
-
- fprintf(out, _(
- " <device> specifies device by path\n"
- " <directory> mountpoint for bind mounts (see --bind/rbind)\n"
- " <file> regular file for loopdev setup\n"));
+ fprintf(out, USAGE_HELP_OPTIONS(25));
- fprintf(out, _(
- "\nOperations:\n"
- " -B, --bind mount a subtree somewhere else (same as -o bind)\n"
- " -M, --move move a subtree to some other place\n"
- " -R, --rbind mount a subtree and all submounts somewhere else\n"));
- fprintf(out, _(
- " --make-shared mark a subtree as shared\n"
- " --make-slave mark a subtree as slave\n"
- " --make-private mark a subtree as private\n"
- " --make-unbindable mark a subtree as unbindable\n"));
- fprintf(out, _(
- " --make-rshared recursively mark a whole subtree as shared\n"
- " --make-rslave recursively mark a whole subtree as slave\n"
- " --make-rprivate recursively mark a whole subtree as private\n"
- " --make-runbindable recursively mark a whole subtree as unbindable\n"));
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Source:\n"), out);
+ fputs(_(" -L, --label <label> synonym for LABEL=<label>\n"), out);
+ fputs(_(" -U, --uuid <uuid> synonym for UUID=<uuid>\n"), out);
+ fputs(_(" LABEL=<label> specifies device by filesystem label\n"), out);
+ fputs(_(" UUID=<uuid> specifies device by filesystem UUID\n"), out);
+ fputs(_(" PARTLABEL=<label> specifies device by partition label\n"), out);
+ fputs(_(" PARTUUID=<uuid> specifies device by partition UUID\n"), out);
+ fputs(_(" ID=<id> specifies device by udev hardware ID\n"), out);
+ fputs(_(" <device> specifies device by path\n"), out);
+ fputs(_(" <directory> mountpoint for bind mounts (see --bind/rbind)\n"), out);
+ fputs(_(" <file> regular file for loopdev setup\n"), out);
- printf(USAGE_MAN_TAIL("mount(8)"));
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Operations:\n"), out);
+ fputs(_(" -B, --bind mount a subtree somewhere else (same as -o bind)\n"), out);
+ fputs(_(" -M, --move move a subtree to some other place\n"), out);
+ fputs(_(" -R, --rbind mount a subtree and all submounts somewhere else\n"), out);
+ fputs(_(" --make-shared mark a subtree as shared\n"), out);
+ fputs(_(" --make-slave mark a subtree as slave\n"), out);
+ fputs(_(" --make-private mark a subtree as private\n"), out);
+ fputs(_(" --make-unbindable mark a subtree as unbindable\n"), out);
+ fputs(_(" --make-rshared recursively mark a whole subtree as shared\n"), out);
+ fputs(_(" --make-rslave recursively mark a whole subtree as slave\n"), out);
+ fputs(_(" --make-rprivate recursively mark a whole subtree as private\n"), out);
+ fputs(_(" --make-runbindable recursively mark a whole subtree as unbindable\n"), out);
+
+ fprintf(out, USAGE_MAN_TAIL("mount(8)"));
exit(MNT_EX_SUCCESS);
}
int c, rc = MNT_EX_SUCCESS, all = 0, show_labels = 0;
struct libmnt_context *cxt;
struct libmnt_table *fstab = NULL;
+ char *idmap = NULL;
char *srcbuf = NULL;
char *types = NULL;
int oper = 0, is_move = 0;
MOUNT_OPT_RSLAVE,
MOUNT_OPT_RPRIVATE,
MOUNT_OPT_RUNBINDABLE,
+ MOUNT_OPT_MAP_GROUPS,
+ MOUNT_OPT_MAP_USERS,
MOUNT_OPT_TARGET,
MOUNT_OPT_TARGET_PREFIX,
MOUNT_OPT_SOURCE,
MOUNT_OPT_OPTMODE,
MOUNT_OPT_OPTSRC,
- MOUNT_OPT_OPTSRC_FORCE
+ MOUNT_OPT_OPTSRC_FORCE,
+ MOUNT_OPT_ONLYONCE
};
static const struct option longopts[] = {
{ "make-rslave", no_argument, NULL, MOUNT_OPT_RSLAVE },
{ "make-rprivate", no_argument, NULL, MOUNT_OPT_RPRIVATE },
{ "make-runbindable", no_argument, NULL, MOUNT_OPT_RUNBINDABLE },
+ { "map-groups", required_argument, NULL, MOUNT_OPT_MAP_GROUPS },
+ { "map-users", required_argument, NULL, MOUNT_OPT_MAP_USERS },
+ { "mkdir", optional_argument, NULL, 'm' },
{ "no-canonicalize", no_argument, NULL, 'c' },
{ "internal-only", no_argument, NULL, 'i' },
{ "show-labels", no_argument, NULL, 'l' },
{ "target", required_argument, NULL, MOUNT_OPT_TARGET },
{ "target-prefix", required_argument, NULL, MOUNT_OPT_TARGET_PREFIX },
{ "source", required_argument, NULL, MOUNT_OPT_SOURCE },
+ { "onlyonce", no_argument, NULL, MOUNT_OPT_ONLYONCE },
{ "options-mode", required_argument, NULL, MOUNT_OPT_OPTMODE },
{ "options-source", required_argument, NULL, MOUNT_OPT_OPTSRC },
{ "options-source-force", no_argument, NULL, MOUNT_OPT_OPTSRC_FORCE},
mnt_context_set_tables_errcb(cxt, table_parser_errcb);
- while ((c = getopt_long(argc, argv, "aBcfFhilL:Mno:O:rRsU:vVwt:T:N:",
+ while ((c = getopt_long(argc, argv, "aBcfFhilL:m::Mno:O:rRsU:vVwt:T:N:",
longopts, NULL)) != -1) {
/* only few options are allowed for non-root users */
mnt_context_disable_mtab(cxt, TRUE);
break;
case 'r':
- append_option(cxt, "ro");
+ append_option(cxt, "ro", NULL);
mnt_context_enable_rwonly_mount(cxt, FALSE);
break;
case 'v':
mnt_context_enable_verbose(cxt, TRUE);
break;
case 'w':
- append_option(cxt, "rw");
+ append_option(cxt, "rw", NULL);
mnt_context_enable_rwonly_mount(cxt, TRUE);
break;
case 'o':
mnt_optstr_remove_option(&o, "move");
if (o && *o)
- append_option(cxt, o);
+ append_option(cxt, o, NULL);
oper = is_move = 1;
free(o);
} else
- append_option(cxt, optarg);
+ append_option(cxt, optarg, NULL);
break;
case 'O':
if (mnt_context_set_options_pattern(cxt, optarg))
break;
case 'B':
oper = 1;
- append_option(cxt, "bind");
+ append_option(cxt, "bind", NULL);
break;
case 'M':
oper = 1;
is_move = 1;
break;
+ case 'm':
+ if (optarg && *optarg == '=')
+ optarg++;
+ append_option(cxt, "X-mount.mkdir", optarg);
+ break;
case 'R':
oper = 1;
- append_option(cxt, "rbind");
+ append_option(cxt, "rbind", NULL);
break;
case 'N':
{
break;
}
case MOUNT_OPT_SHARED:
- append_option(cxt, "shared");
+ append_option(cxt, "shared", NULL);
propa = 1;
break;
case MOUNT_OPT_SLAVE:
- append_option(cxt, "slave");
+ append_option(cxt, "slave", NULL);
propa = 1;
break;
case MOUNT_OPT_PRIVATE:
- append_option(cxt, "private");
+ append_option(cxt, "private", NULL);
propa = 1;
break;
case MOUNT_OPT_UNBINDABLE:
- append_option(cxt, "unbindable");
+ append_option(cxt, "unbindable", NULL);
propa = 1;
break;
case MOUNT_OPT_RSHARED:
- append_option(cxt, "rshared");
+ append_option(cxt, "rshared", NULL);
propa = 1;
break;
case MOUNT_OPT_RSLAVE:
- append_option(cxt, "rslave");
+ append_option(cxt, "rslave", NULL);
propa = 1;
break;
case MOUNT_OPT_RPRIVATE:
- append_option(cxt, "rprivate");
+ append_option(cxt, "rprivate", NULL);
propa = 1;
break;
case MOUNT_OPT_RUNBINDABLE:
- append_option(cxt, "runbindable");
+ append_option(cxt, "runbindable", NULL);
propa = 1;
break;
+ case MOUNT_OPT_MAP_GROUPS:
+ case MOUNT_OPT_MAP_USERS:
+ if (*optarg == '=')
+ optarg++;
+ if (idmap && (*idmap == '/' || *optarg == '/')) {
+ warnx(_("bad usage"));
+ errtryhelp(MNT_EX_USAGE);
+ } else if (*optarg == '/') {
+ idmap = xstrdup(optarg);
+ } else {
+ char *tmp;
+ xasprintf(&tmp, "%s%s%s%s", idmap ? idmap : "", idmap ? " " : "",
+ c == MOUNT_OPT_MAP_GROUPS ? "g:" : "u:", optarg);
+ free(idmap);
+ idmap = tmp;
+ }
+ break;
case MOUNT_OPT_TARGET:
mnt_context_disable_swapmatch(cxt, 1);
mnt_context_set_target(cxt, optarg);
case MOUNT_OPT_OPTSRC_FORCE:
optmode |= MNT_OMODE_FORCE;
break;
-
+ case MOUNT_OPT_ONLYONCE:
+ mnt_context_enable_onlyonce(cxt, 1);
+ break;
case 'h':
mnt_free_context(cxt);
usage();
argc -= optind;
argv += optind;
+ if (idmap)
+ append_option(cxt, "X-mount.idmap", idmap);
+
optmode |= optmode_mode | optmode_src;
if (optmode) {
if (!optmode_mode)