/* * mount(8) -- mount a filesystem * * Copyright (C) 2011 Red Hat, Inc. All rights reserved. * Written by Karel Zak * * 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. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "nls.h" #include "c.h" #include "env.h" #include "strutils.h" #include "closestream.h" #include "canonicalize.h" #define XALLOC_EXIT_CODE MNT_EX_SYSERR #include "xalloc.h" #define OPTUTILS_EXIT_CODE MNT_EX_USAGE #include "optutils.h" /*** TODO: DOCS: * * --options-mode={ignore,append,prepend,replace} MNT_OMODE_{IGNORE, ...} * --options-source={fstab,mtab,disable} MNT_OMODE_{FSTAB,MTAB,NOTAB} * --options-source-force MNT_OMODE_FORCE */ static int mk_exit_code(struct libmnt_context *cxt, int rc); static void __attribute__((__noreturn__)) exit_non_root(const char *option) { const uid_t ruid = getuid(); const uid_t euid = geteuid(); if (ruid == 0 && euid != 0) { /* user is root, but setuid to non-root */ if (option) errx(MNT_EX_USAGE, _("only root can use \"--%s\" option " "(effective UID is %u)"), option, euid); errx(MNT_EX_USAGE, _("only root can do that " "(effective UID is %u)"), euid); } if (option) errx(MNT_EX_USAGE, _("only root can use \"--%s\" option"), option); errx(MNT_EX_USAGE, _("only root can do that")); } 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; 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_pseudofs(fs) && !mnt_fs_is_netfs(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 #include static void selinux_warning(struct libmnt_context *cxt, const char *tgt) { if (tgt && mnt_context_is_verbose(cxt) && is_selinux_enabled() > 0) { security_context_t raw = NULL, def = NULL; if (getfilecon(tgt, &raw) > 0 && security_get_initial_context("file", &def) == 0) { if (!selinux_file_context_cmp(raw, def)) printf(_( "mount: %s does not contain SELinux labels.\n" " You just mounted an 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); } freecon(raw); freecon(def); } } #else # define selinux_warning(_x, _y) #endif /* * 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); if (*buf) { const char *spec = tgt; if (!spec) spec = mnt_context_get_source(cxt); if (!spec) spec = "???"; warnx("%s: %s.", spec, buf); } if (rc == MNT_EX_SUCCESS && mnt_context_get_status(cxt) == 1) { selinux_warning(cxt, tgt); } 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 him. */ static void sanitize_paths(struct libmnt_context *cxt) { const char *p; struct libmnt_fs *fs = mnt_context_get_fs(cxt); if (!fs) return; p = mnt_fs_get_target(fs); if (p) { char *np = canonicalize_path_restricted(p); if (!np) err(MNT_EX_USAGE, "%s", p); mnt_fs_set_target(fs, np); free(np); } p = mnt_fs_get_srcpath(fs); if (p) { char *np = canonicalize_path_restricted(p); if (!np) err(MNT_EX_USAGE, "%s", p); mnt_fs_set_source(fs, np); free(np); } } static void append_option(struct libmnt_context *cxt, const char *opt) { 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); } 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] | [--target] \n" " %1$s [options] \n" " %1$s []\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, out); 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 alternative file to /etc/fstab\n")); fprintf(out, _( " -i, --internal-only don't call the mount. 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 \n" " what to do with options loaded from fstab\n" " --options-source \n" " mount options source\n" " --options-source-force\n" " force use of options from fstab/mtab\n")); fprintf(out, _( " -o, --options comma-separated list of mount options\n" " -O, --test-opts limit the set of filesystems (use with -a)\n" " -r, --read-only mount the filesystem read-only (same as -o ro)\n" " -t, --types limit the set of filesystem types\n")); fprintf(out, _( " --source explicitly specifies source (path, label, uuid)\n" " --target explicitly specifies mountpoint\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 perform mount in another namespace\n")); fputs(USAGE_SEPARATOR, out); printf(USAGE_HELP_OPTIONS(25)); fprintf(out, _( "\nSource:\n" " -L, --label