/
niceSOFT
/
util-linux
Обзор
Документация
Войти
/
niceSOFT
/
util-linux
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
sys-utils/mount.c
1 149 строк
32 KB
Karel Zak
tools: switch SELinux to runtime optional via dlopen
14 июл 2026, 15:39
14 июл 2026, 15:39
feaacdb
Код
Авторство
О чём код?
/* * SPDX-License-Identifier: GPL-2.0-or-later * * mount(8) -- mount a filesystem * * Copyright (C) 2011 Red Hat, Inc. All rights reserved. * Written by Karel Zak <kzak@redhat.com> * * This program is free software; you can redistribute it and/or modify * 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. */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <getopt.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <stdarg.h> #include <libmount.h> #include <ctype.h> #include "nls.h" #include "c.h" #include "env.h" #include "strutils.h" #include "closestream.h" #include "canonicalize.h" #include "pathnames.h" #include "strv.h" #define XALLOC_EXIT_CODE MNT_EX_SYSERR #include "xalloc.h" #define OPTUTILS_EXIT_CODE MNT_EX_USAGE #include "optutils.h" static struct ul_env_list *envs_removed; static int mk_exit_code(struct libmnt_context *cxt, int rc); static void suid_drop(struct libmnt_context *cxt) { const uid_t ruid = getuid(); const uid_t euid = geteuid(); 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) errx(MNT_EX_FAIL, _("drop permissions failed.")); mnt_context_force_unrestricted(cxt); /* restore "bad" environment variables */ if (envs_removed) { env_list_setenv(envs_removed, 0); env_list_free(envs_removed); envs_removed = NULL; } } static void __attribute__((__noreturn__)) mount_print_version(void) { const char *ver = NULL; const char **features = NULL, **p; mnt_get_library_version(&ver); mnt_get_library_features(&features); printf(_("%s from %s (libmount %s"), program_invocation_short_name, PACKAGE_STRING, ver); p = features; while (p && *p) { fputs(p == features ? ": " : ", ", stdout); fputs(*p++, stdout); } fputs(")\n", stdout); exit(MNT_EX_SUCCESS); } static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)), const char *filename, int line) { if (filename) warnx(_("%s: parse error at line %d -- ignored"), filename, line); return 1; } /* * Replace control chars with '?' to be compatible with coreutils. For more * robust solution use findmnt(1) where we use \x?? hex encoding. */ static void safe_fputs(const char *data) { const char *p; for (p = data; p && *p; p++) { if (iscntrl((unsigned char) *p)) fputc('?', stdout); else fputc(*p, stdout); } } static void print_all(struct libmnt_context *cxt, char *pattern, int show_label) { struct libmnt_table *tb; struct libmnt_iter *itr = NULL; 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")); itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) err(MNT_EX_SYSERR, _("failed to initialize libmount iterator")); if (show_label) cache = mnt_new_cache(); while (mnt_table_next_fs(tb, itr, &fs) == 0) { const char *type = mnt_fs_get_fstype(fs); const char *src = mnt_fs_get_source(fs); const char *optstr = mnt_fs_get_options(fs); char *xsrc = NULL; if (type && pattern && !mnt_match_fstype(type, pattern)) continue; 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 (type) printf (" type %s", type); if (optstr) printf (" (%s)", optstr); if (show_label && src) { char *lb = mnt_cache_find_tag_value(cache, src, "LABEL"); if (lb) printf (" [%s]", lb); } fputc('\n', stdout); free(xsrc); } mnt_unref_cache(cache); mnt_free_iter(itr); } /* * mount -a [-F] */ static int mount_all(struct libmnt_context *cxt) { struct libmnt_iter *itr; struct libmnt_fs *fs; int mntrc, ignored, rc = MNT_EX_SUCCESS; int nsucc = 0, nerrs = 0; itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) { warn(_("failed to initialize libmount iterator")); return MNT_EX_SYSERR; } while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) { const char *tgt = mnt_fs_get_target(fs); if (ignored) { if (mnt_context_is_verbose(cxt)) printf(ignored == 1 ? _("%-25s: ignored\n") : _("%-25s: already mounted\n"), tgt); } else if (mnt_context_is_fork(cxt)) { if (mnt_context_is_verbose(cxt)) printf("%-25s: mount successfully forked\n", tgt); } else { if (mk_exit_code(cxt, mntrc) == MNT_EX_SUCCESS) { nsucc++; /* Note that MNT_EX_SUCCESS return code does * not mean that FS has been really mounted * (e.g. nofail option) */ if (mnt_context_get_status(cxt) && mnt_context_is_verbose(cxt)) printf("%-25s: successfully mounted\n", tgt); } else nerrs++; } } if (mnt_context_is_parent(cxt)) { /* wait for mount --fork children */ int nchildren = 0; nerrs = 0, nsucc = 0; rc = mnt_context_wait_for_children(cxt, &nchildren, &nerrs); if (!rc && nchildren) nsucc = nchildren - nerrs; } if (nerrs == 0) rc = MNT_EX_SUCCESS; /* all success */ else if (nsucc == 0) rc = MNT_EX_FAIL; /* all failed */ else rc = MNT_EX_SOMEOK; /* some success, some failed */ mnt_free_iter(itr); return rc; } /* * mount -a -o remount */ static int remount_all(struct libmnt_context *cxt) { struct libmnt_iter *itr; struct libmnt_fs *fs; int mntrc, ignored, rc = MNT_EX_SUCCESS; int nsucc = 0, nerrs = 0; itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) { warn(_("failed to initialize libmount iterator")); return MNT_EX_SYSERR; } while (mnt_context_next_remount(cxt, itr, &fs, &mntrc, &ignored) == 0) { const char *tgt = mnt_fs_get_target(fs); if (ignored) { if (mnt_context_is_verbose(cxt)) printf(_("%-25s: ignored\n"), tgt); } else { if (mk_exit_code(cxt, mntrc) == MNT_EX_SUCCESS) { nsucc++; /* Note that MNT_EX_SUCCESS return code does * not mean that FS has been really mounted * (e.g. nofail option) */ if (mnt_context_get_status(cxt) && mnt_context_is_verbose(cxt)) printf("%-25s: successfully remounted\n", tgt); } else nerrs++; } } if (nerrs == 0) rc = MNT_EX_SUCCESS; /* all success */ else if (nsucc == 0) rc = MNT_EX_FAIL; /* all failed */ else rc = MNT_EX_SOMEOK; /* some success, some failed */ mnt_free_iter(itr); return rc; } static void success_message(struct libmnt_context *cxt) { unsigned long mflags = 0; const char *tgt, *src, *pr = program_invocation_short_name; if (mnt_context_helper_executed(cxt) || mnt_context_get_status(cxt) != 1) return; mnt_context_get_mflags(cxt, &mflags); tgt = mnt_context_get_target(cxt); src = mnt_context_get_source(cxt); if (mflags & MS_MOVE) printf(_("%s: %s moved to %s.\n"), pr, src, tgt); else if (mflags & MS_BIND) printf(_("%s: %s bound on %s.\n"), pr, src, tgt); else if (mflags & MS_PROPAGATION) { if (src && strcmp(src, "none") != 0 && tgt) printf(_("%s: %s mounted on %s.\n"), pr, src, tgt); printf(_("%s: %s propagation flags changed.\n"), pr, tgt); } else printf(_("%s: %s mounted on %s.\n"), pr, src, tgt); } #if defined(HAVE_LIBSELINUX) && defined(HAVE_SECURITY_GET_INITIAL_CONTEXT) # include "dl-selinux.h" static void selinux_warning(struct libmnt_context *cxt, const char *tgt) { if (tgt && mnt_context_is_verbose(cxt) && ul_dlopen_libselinux() == 0 && selinux_call(is_selinux_enabled)() > 0) { char *raw = NULL, *def = NULL; if (selinux_call(getfilecon)(tgt, &raw) > 0 && selinux_call(security_get_initial_context)("file", &def) == 0) { if (!selinux_call(selinux_file_context_cmp)(raw, def)) printf(_( "mount: %s does not contain SELinux labels.\n" " You just mounted a file system that supports labels which does not\n" " contain labels, onto an SELinux box. It is likely that confined\n" " applications will generate AVC messages and not be allowed access to\n" " this file system. For more details see restorecon(8) and mount(8).\n"), tgt); } selinux_call(freecon)(raw); selinux_call(freecon)(def); } } #else # define selinux_warning(_x, _y) #endif #ifdef USE_SYSTEMD /* * Note that this mount(8) message may generate thousands of lines of output * when mount(8) is called from any script in systems with large fstab, etc. * * The goal is to avoid spamming system logs (don't print on stderr) and hide * the hint if stderr is redirected/piped (in this case mount(8) is probably * executed in a script). * * The target audience is users on a terminal who directly use mount(8). */ static void systemd_hint(void) { static int fstab_check_done = 0; if (fstab_check_done == 0) { struct stat a, b; if (isatty(STDERR_FILENO) && isatty(STDOUT_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 static size_t libmount_mesgs(struct libmnt_context *cxt, char type) { size_t n = mnt_context_get_nmesgs(cxt, type); char **mesgs = mnt_context_get_mesgs(cxt); char **s; if (!n) return 0; /* Header */ switch (type) { case 'e': fputs(P_("mount error:\n", "mount errors:\n", n), stderr); break; case 'w': fputs(P_("mount warning:\n", "mount warnings:\n", n), stdout); break; case 'i': fputs(P_("mount info:\n", "mount infos:\n", n), stdout); break; } /* Messages */ UL_STRV_FOREACH(s, mesgs) { switch (type) { case 'e': if (!ul_startswith(*s, "e ")) break; fprintf(stderr, " * %s\n", (*s) + 2); break; case 'w': if (!ul_startswith(*s, "w ")) break; fprintf(stdout, " * %s\n", (*s) + 2); break; case 'i': if (!ul_startswith(*s, "i ")) break; fprintf(stdout, " * %s\n", (*s) + 2); break; } } return n; } /* * Returns exit status (MNT_EX_*) and/or prints error message. */ static int mk_exit_code(struct libmnt_context *cxt, int rc) { const char *tgt; char buf[BUFSIZ] = { 0 }; rc = mnt_context_get_excode(cxt, rc, buf, sizeof(buf)); tgt = mnt_context_get_target(cxt); /* error messages * * Note that mnt_context_get_excode() is used for backward compatibility and * will fill @buf with error messages from mnt_context_get_mesgs(). Therefore, * calling libmount_mesgs(cxt, 'e') is currently unnecessary. */ if (*buf) { const char *spec = tgt; if (!spec) spec = mnt_context_get_source(cxt); 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")); } /* warning messages */ libmount_mesgs(cxt, 'w'); /* info messages */ if (mnt_context_is_verbose(cxt)) libmount_mesgs(cxt, 'i'); /* extra mount(8) messages */ if (rc == MNT_EX_SUCCESS && mnt_context_get_status(cxt) == 1) { selinux_warning(cxt, tgt); } systemd_hint(); return rc; } static struct libmnt_table *append_fstab(struct libmnt_context *cxt, struct libmnt_table *fstab, const char *path) { if (!fstab) { fstab = mnt_new_table(); if (!fstab) err(MNT_EX_SYSERR, _("failed to initialize libmount table")); mnt_table_set_parser_errcb(fstab, table_parser_errcb); mnt_context_set_fstab(cxt, fstab); mnt_unref_table(fstab); /* reference is handled by @cxt now */ } if (mnt_table_parse_fstab(fstab, path)) errx(MNT_EX_USAGE,_("%s: failed to parse"), path); return fstab; } /* * Check source and target paths -- non-root user should not be able to * resolve paths which are unreadable for them. */ static int sanitize_paths(struct libmnt_context *cxt) { const char *p; struct libmnt_fs *fs = mnt_context_get_fs(cxt); if (!fs) return 0; p = mnt_fs_get_target(fs); if (p) { char *np = ul_canonicalize_path_restricted(p); if (!np) return -EPERM; mnt_fs_set_target(fs, np); free(np); } p = mnt_fs_get_srcpath(fs); if (p) { char *np = ul_canonicalize_path_restricted(p); if (!np) return -EPERM; mnt_fs_set_source(fs, np); free(np); } return 0; } static void append_option(struct libmnt_context *cxt, const char *opt, const char *arg) { char *o = NULL; if (opt && !ul_optstr_is_valid(opt)) errx(MNT_EX_USAGE, _("unsupported option format: %s"), opt); if (opt && 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) { unsigned long mflags = 0; if (mnt_context_get_mflags(cxt, &mflags)) return 0; return mflags & MS_REMOUNT; } static void __attribute__((__noreturn__)) usage(void) { FILE *out = stdout; fputs(USAGE_HEADER, out); fprintf(out, _( " %1$s [-lhV]\n" " %1$s -a [options]\n" " %1$s [options] [--source] <source> | [--target] <directory>\n" " %1$s [options] <source> <directory>\n" " %1$s <operation> <mountpoint> [<target>]\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, out); fputs(_("Mount a filesystem.\n"), out); fputs(USAGE_OPTIONS, out); fputs(_(" -a, --all mount all filesystems mentioned in fstab\n"), out); fputs(_(" -c, --no-canonicalize don't canonicalize paths\n"), out); fputs(_(" --beneath attach the filesystem beneath the top mount\n"), out); fputs(_(" --exclusive allow only one filesystem instance\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 on target\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, --ro, --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); fprintf(out, USAGE_HELP_OPTIONS(25)); 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); 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); } struct flag_str { int value; char *str; }; static int omode2mask(const char *str) { size_t i; static const struct flag_str flags[] = { { MNT_OMODE_IGNORE, "ignore" }, { MNT_OMODE_APPEND, "append" }, { MNT_OMODE_PREPEND, "prepend" }, { MNT_OMODE_REPLACE, "replace" }, }; for (i = 0; i < ARRAY_SIZE(flags); i++) { if (!strcmp(str, flags[i].str)) return flags[i].value; } return -EINVAL; } static long osrc2mask(const char *str, size_t len) { size_t i; static const struct flag_str flags[] = { { MNT_OMODE_FSTAB, "fstab" }, { MNT_OMODE_MTAB, "mtab" }, { MNT_OMODE_NOTAB, "disable" }, }; for (i = 0; i < ARRAY_SIZE(flags); i++) { if (!strncmp(str, flags[i].str, len) && !flags[i].str[len]) return flags[i].value; } return -EINVAL; } static pid_t parse_pid(const char *str) { char *end; pid_t ret; errno = 0; ret = strtoul(str, &end, 10); if (ret < 0 || errno || end == str || (end && *end)) return 0; return ret; } int main(int argc, char **argv) { 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; int propa = 0; int optmode = 0, optmode_mode = 0, optmode_src = 0; enum { MOUNT_OPT_SHARED = CHAR_MAX + 1, MOUNT_OPT_SLAVE, MOUNT_OPT_PRIVATE, MOUNT_OPT_UNBINDABLE, MOUNT_OPT_RSHARED, 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_ONLYONCE, MOUNT_OPT_EXCL, MOUNT_OPT_BENEATH, }; static const struct option longopts[] = { { "all", no_argument, NULL, 'a' }, { "beneath", no_argument, NULL, MOUNT_OPT_BENEATH }, { "exclusive", no_argument, NULL, MOUNT_OPT_EXCL }, { "fake", no_argument, NULL, 'f' }, { "fstab", required_argument, NULL, 'T' }, { "fork", no_argument, NULL, 'F' }, { "help", no_argument, NULL, 'h' }, { "no-mtab", no_argument, NULL, 'n' }, { "read-only", no_argument, NULL, 'r' }, { "ro", no_argument, NULL, 'r' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { "read-write", no_argument, NULL, 'w' }, { "rw", no_argument, NULL, 'w' }, { "options", required_argument, NULL, 'o' }, { "test-opts", required_argument, NULL, 'O' }, { "types", required_argument, NULL, 't' }, { "uuid", required_argument, NULL, 'U' }, { "label", required_argument, NULL, 'L' }, { "bind", no_argument, NULL, 'B' }, { "move", no_argument, NULL, 'M' }, { "rbind", no_argument, NULL, 'R' }, { "make-shared", no_argument, NULL, MOUNT_OPT_SHARED }, { "make-slave", no_argument, NULL, MOUNT_OPT_SLAVE }, { "make-private", no_argument, NULL, MOUNT_OPT_PRIVATE }, { "make-unbindable", no_argument, NULL, MOUNT_OPT_UNBINDABLE }, { "make-rshared", no_argument, NULL, MOUNT_OPT_RSHARED }, { "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}, { "namespace", required_argument, NULL, 'N' }, { NULL, 0, NULL, 0 } }; static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ { 'B','M','R' }, /* bind,move,rbind */ { 'L','U', MOUNT_OPT_SOURCE }, /* label,uuid,source */ { 0 } }; int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; __sanitize_env(&envs_removed); setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); close_stdout_atexit(); strutils_set_exitcode(MNT_EX_USAGE); mnt_init_debug(0); cxt = mnt_new_context(); if (!cxt) err(MNT_EX_SYSERR, _("libmount context allocation failed")); mnt_context_set_tables_errcb(cxt, table_parser_errcb); 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 */ if (mnt_context_is_restricted(cxt) && !strchr("hlLUVvrist", c) && c != MOUNT_OPT_TARGET && c != MOUNT_OPT_SOURCE && c != MOUNT_OPT_EXCL) suid_drop(cxt); err_exclusive_options(c, longopts, excl, excl_st); switch(c) { case 'a': all = 1; break; case 'c': mnt_context_disable_canonicalize(cxt, TRUE); break; case 'f': mnt_context_enable_fake(cxt, TRUE); break; case 'F': mnt_context_enable_fork(cxt, TRUE); break; case 'i': mnt_context_disable_helpers(cxt, TRUE); break; case 'n': mnt_context_disable_mtab(cxt, TRUE); break; case 'r': 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", NULL); mnt_context_enable_rwonly_mount(cxt, TRUE); break; case 'o': /* "move" is not supported as option string in libmount * to avoid use in fstab */ if (mnt_optstr_get_option(optarg, "move", NULL, 0) == 0) { char *o = xstrdup(optarg); mnt_optstr_remove_option(&o, "move"); if (o && *o) append_option(cxt, o, NULL); oper = is_move = 1; free(o); } else append_option(cxt, optarg, NULL); break; case 'O': if (mnt_context_set_options_pattern(cxt, optarg)) err(MNT_EX_SYSERR, _("failed to set options pattern")); break; case 'L': xasprintf(&srcbuf, "LABEL=\"%s\"", optarg); mnt_context_disable_swapmatch(cxt, 1); mnt_context_set_source(cxt, srcbuf); free(srcbuf); break; case 'U': xasprintf(&srcbuf, "UUID=\"%s\"", optarg); mnt_context_disable_swapmatch(cxt, 1); mnt_context_set_source(cxt, srcbuf); free(srcbuf); break; case 'l': show_labels = 1; break; case 't': types = optarg; break; case 'T': fstab = append_fstab(cxt, fstab, optarg); break; case 's': mnt_context_enable_sloppy(cxt, TRUE); break; case 'B': oper = 1; 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", NULL); break; case 'N': { char path[PATH_MAX]; pid_t pid = parse_pid(optarg); if (pid) snprintf(path, sizeof(path), "/proc/%i/ns/mnt", pid); if (mnt_context_set_target_ns(cxt, pid ? path : optarg)) err(MNT_EX_SYSERR, _("failed to set target namespace to %s"), pid ? path : optarg); break; } case MOUNT_OPT_SHARED: append_option(cxt, "shared", NULL); propa = 1; break; case MOUNT_OPT_SLAVE: append_option(cxt, "slave", NULL); propa = 1; break; case MOUNT_OPT_PRIVATE: append_option(cxt, "private", NULL); propa = 1; break; case MOUNT_OPT_UNBINDABLE: append_option(cxt, "unbindable", NULL); propa = 1; break; case MOUNT_OPT_RSHARED: append_option(cxt, "rshared", NULL); propa = 1; break; case MOUNT_OPT_RSLAVE: append_option(cxt, "rslave", NULL); propa = 1; break; case MOUNT_OPT_RPRIVATE: append_option(cxt, "rprivate", NULL); propa = 1; break; case MOUNT_OPT_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); break; case MOUNT_OPT_TARGET_PREFIX: mnt_context_set_target_prefix(cxt, optarg); break; case MOUNT_OPT_SOURCE: mnt_context_disable_swapmatch(cxt, 1); mnt_context_set_source(cxt, optarg); break; case MOUNT_OPT_OPTMODE: optmode_mode = omode2mask(optarg); if (optmode_mode == -EINVAL) { warnx(_("bad usage")); errtryhelp(MNT_EX_USAGE); } break; case MOUNT_OPT_OPTSRC: { unsigned long tmp = 0; if (string_to_bitmask(optarg, &tmp, osrc2mask)) { warnx(_("bad usage")); errtryhelp(MNT_EX_USAGE); } optmode_src = tmp; break; } case MOUNT_OPT_OPTSRC_FORCE: optmode |= MNT_OMODE_FORCE; break; case MOUNT_OPT_ONLYONCE: mnt_context_enable_onlyonce(cxt, 1); break; case MOUNT_OPT_EXCL: mnt_context_enable_exclusive(cxt, 1); break; case MOUNT_OPT_BENEATH: mnt_context_enable_beneath(cxt, 1); break; case 'h': mnt_free_context(cxt); usage(); case 'V': mnt_free_context(cxt); mount_print_version(); default: errtryhelp(MNT_EX_USAGE); } } argc -= optind; argv += optind; if (idmap) append_option(cxt, "X-mount.idmap", idmap); optmode |= optmode_mode | optmode_src; if (optmode) { if (!optmode_mode) optmode |= MNT_OMODE_PREPEND; if (!optmode_src) optmode |= MNT_OMODE_FSTAB | MNT_OMODE_MTAB; mnt_context_set_optsmode(cxt, optmode); } if (fstab && !mnt_context_is_nocanonicalize(cxt)) { /* * We have external (context independent) fstab instance, let's * make a connection between the fstab and the canonicalization * cache. */ mnt_table_set_cache(fstab, mnt_context_get_cache(cxt)); } if (!mnt_context_get_source(cxt) && !mnt_context_get_target(cxt) && !argc && !all) { if (oper || mnt_context_get_options(cxt)) { warnx(_("bad usage")); errtryhelp(MNT_EX_USAGE); } print_all(cxt, types, show_labels); goto done; } /* Non-root users are allowed to use -t to print_all(), but not to mount */ if (mnt_context_is_restricted(cxt) && types) suid_drop(cxt); if (oper && (types || all || mnt_context_get_source(cxt))) { warnx(_("bad usage")); errtryhelp(MNT_EX_USAGE); } if (types && (all || strchr(types, ',') || strncmp(types, "no", 2) == 0)) mnt_context_set_fstype_pattern(cxt, types); else if (types) mnt_context_set_fstype(cxt, types); if (all) { /* * A) Mount all */ if (has_remount_flag(cxt)) rc = remount_all(cxt); else rc = mount_all(cxt); goto done; } else if (argc == 0 && (mnt_context_get_source(cxt) || mnt_context_get_target(cxt))) { /* * B) mount -L|-U|--source|--target * * non-root may specify source *or* target, but not both */ if (mnt_context_is_restricted(cxt) && mnt_context_get_source(cxt) && mnt_context_get_target(cxt)) suid_drop(cxt); } else if (argc == 1 && (!mnt_context_get_source(cxt) || !mnt_context_get_target(cxt))) { /* * C) mount [-L|-U|--source] <target> * mount [--target <dir>] <source> * mount <source|target> * * non-root may specify source *or* target, but not both * * It does not matter for libmount if we set source or target * here (the library is able to swap it), but it matters for * sanitize_paths(). */ int istag = mnt_tag_is_valid(argv[0]); if (istag && mnt_context_get_source(cxt)) /* -L, -U or --source together with LABEL= or UUID= */ errx(MNT_EX_USAGE, _("source specified more than once")); else if (istag || mnt_context_get_target(cxt)) mnt_context_set_source(cxt, argv[0]); else mnt_context_set_target(cxt, argv[0]); if (mnt_context_is_restricted(cxt) && mnt_context_get_source(cxt) && mnt_context_get_target(cxt)) suid_drop(cxt); } else if (argc == 2 && !mnt_context_get_source(cxt) && !mnt_context_get_target(cxt)) { /* * D) mount <source> <target> */ if (mnt_context_is_restricted(cxt)) suid_drop(cxt); mnt_context_set_source(cxt, argv[0]); mnt_context_set_target(cxt, argv[1]); } else { warnx(_("bad usage")); errtryhelp(MNT_EX_USAGE); } if (mnt_context_is_restricted(cxt) && sanitize_paths(cxt) != 0) suid_drop(cxt); if (is_move) /* "move" as option string is not supported by libmount */ mnt_context_set_mflags(cxt, MS_MOVE); if ((oper && !has_remount_flag(cxt)) || propa) /* For --make-* or --bind is fstab/mtab unnecessary */ mnt_context_set_optsmode(cxt, MNT_OMODE_NOTAB); rc = mnt_context_mount(cxt); if (rc == -EPERM && mnt_context_is_restricted(cxt) && !mnt_context_syscall_called(cxt)) { /* Try it again without permissions */ suid_drop(cxt); rc = mnt_context_mount(cxt); } rc = mk_exit_code(cxt, rc); if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt)) success_message(cxt); done: mnt_free_context(cxt); env_list_free(envs_removed); return rc; }