/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include "nls.h" #include "c.h" #include "env.h" #include "optutils.h" #include "strutils.h" #include "xgetpass.h" /*** TODO: DOCS: * * --guess-fstype is unsupported * * --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 */ /* exit status */ #define EX_SUCCESS 0 #define EX_USAGE 1 /* incorrect invocation or permission */ #define EX_SYSERR 2 /* out of memory, cannot fork, ... */ #define EX_SOFTWARE 4 /* internal mount bug or wrong version */ #define EX_USER 8 /* user interrupt */ #define EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */ #define EX_FAIL 32 /* mount failure */ #define EX_SOMEOK 64 /* some mount succeeded */ static int passfd = -1; static int readwrite; 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(EX_USAGE, _("only root can use \"--%s\" option " "(effective UID is %u)"), option, euid); errx(EX_USAGE, _("only root can do that " "(effective UID is %u)"), euid); } if (option) errx(EX_USAGE, _("only root can use \"--%s\" option"), option); errx(EX_USAGE, _("only root can do that")); } static void __attribute__((__noreturn__)) print_version(void) { const char *ver = NULL; mnt_get_library_version(&ver); printf(_("%s from %s (libmount %s)\n"), program_invocation_short_name, PACKAGE_STRING, ver); exit(EX_SUCCESS); } static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)), const char *filename, int line) { if (filename) warnx(_("%s: parse error: ignore entry at line %d."), filename, line); return 0; } static char *encrypt_pass_get(struct libmnt_context *cxt) { if (!cxt) return 0; #ifdef MCL_FUTURE if (mlockall(MCL_CURRENT | MCL_FUTURE)) { warn(_("couldn't lock into memory")); return NULL; } #endif return xgetpass(passfd, _("Password: ")); } static void encrypt_pass_release(struct libmnt_context *cxt __attribute__((__unused__)), char *pwd) { char *p = pwd; while (p && *p) *p++ = '\0'; free(pwd); munlockall(); } 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(EX_SYSERR, _("failed to read mtab")); itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) err(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)) xsrc = mnt_pretty_path(src, cache); printf ("%s on %s", xsrc ? xsrc : src, 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_free_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 = EX_SUCCESS; itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) { warn(_("failed to initialize libmount iterator")); return 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 { rc |= mk_exit_code(cxt, mntrc); if (mnt_context_get_status(cxt)) { rc |= EX_SOMEOK; if (mnt_context_is_verbose(cxt)) printf("%-25s: successfully mounted\n", tgt); } } } if (mnt_context_is_parent(cxt)) { /* wait for mount --fork children */ int nerrs = 0, nchildren = 0; rc = mnt_context_wait_for_children(cxt, &nchildren, &nerrs); if (!rc && nchildren) rc = nchildren == nerrs ? EX_FAIL : EX_SOMEOK; } return rc; } /* * Handles generic errors like ENOMEM, ... * * rc = 0 success * <0 error (usually -errno) * * Returns exit status (EX_*) and prints error message. */ static int handle_generic_errors(int rc, const char *msg) { errno = -rc; switch(errno) { case EINVAL: case EPERM: warn(msg); return EX_USAGE; case ENOMEM: warn(msg); return EX_SYSERR; default: break; } warn(msg); return EX_FAIL; } #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) #endif /* * rc = 0 success * <0 error (usually -errno or -1) * * Returns exit status (EX_*) and/or prints error message. */ static int mk_exit_code(struct libmnt_context *cxt, int rc) { int syserr; struct stat st; unsigned long uflags = 0, mflags = 0; int restricted = mnt_context_is_restricted(cxt); const char *tgt = mnt_context_get_target(cxt); const char *src = mnt_context_get_source(cxt); try_readonly: if (mnt_context_helper_executed(cxt)) /* * /sbin/mount. called, return status */ return mnt_context_get_helper_status(cxt); if (rc == 0 && mnt_context_get_status(cxt) == 1) { /* * Libmount success && syscall success. */ selinux_warning(cxt, tgt); return EX_SUCCESS; /* mount(2) success */ } if (!mnt_context_syscall_called(cxt)) { /* * libmount errors (extra library checks) */ switch (rc) { case -EPERM: warnx(_("only root can mount %s on %s"), src, tgt); return EX_USAGE; case -EBUSY: warnx(_("%s is already mounted"), src); return EX_USAGE; } if (src == NULL || tgt == NULL) { if (mflags & MS_REMOUNT) warnx(_("%s not mounted"), src ? src : tgt); else warnx(_("can't find %s in %s"), src ? src : tgt, mnt_get_fstab_path()); return EX_USAGE; } if (!mnt_context_get_fstype(cxt)) { if (restricted) warnx(_("I could not determine the filesystem type, " "and none was specified")); else warnx(_("you must specify the filesystem type")); return EX_USAGE; } return handle_generic_errors(rc, _("mount failed")); } else if (mnt_context_get_syscall_errno(cxt) == 0) { /* * mount(2) syscall success, but something else failed * (probably error in mtab processing). */ if (rc < 0) return handle_generic_errors(rc, _("filesystem mounted, but mount(8) failed")); return EX_SOFTWARE; /* internal error */ } /* * mount(2) errors */ syserr = mnt_context_get_syscall_errno(cxt); mnt_context_get_mflags(cxt, &mflags); /* mount(2) flags */ mnt_context_get_user_mflags(cxt, &uflags); /* userspace flags */ switch(syserr) { case EPERM: if (geteuid() == 0) { if (stat(tgt, &st) || !S_ISDIR(st.st_mode)) warnx(_("mount point %s is not a directory"), tgt); else warnx(_("permission denied")); } else warnx(_("must be superuser to use mount")); break; case EBUSY: { struct libmnt_table *tb; if (mflags & MS_REMOUNT) { warnx(_("%s is busy"), tgt); break; } warnx(_("%s is already mounted or %s busy"), src, tgt); if (src && mnt_context_get_mtab(cxt, &tb) == 0) { struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD); struct libmnt_fs *fs; while(mnt_table_next_fs(tb, itr, &fs) == 0) { const char *s = mnt_fs_get_srcpath(fs), *t = mnt_fs_get_target(fs); if (t && s && streq_except_trailing_slash(s, src)) fprintf(stderr, _( " %s is already mounted on %s\n"), s, t); } mnt_free_iter(itr); } break; } case ENOENT: if (lstat(tgt, &st)) warnx(_("mount point %s does not exist"), tgt); else if (stat(tgt, &st)) warnx(_("mount point %s is a symbolic link to nowhere"), tgt); else if (stat(src, &st)) { if (uflags & MNT_MS_NOFAIL) return EX_SUCCESS; warnx(_("special device %s does not exist"), src); } else { errno = syserr; warn(_("mount(2) failed")); /* print errno */ } break; case ENOTDIR: if (stat(tgt, &st) || ! S_ISDIR(st.st_mode)) warnx(_("mount point %s is not a directory"), tgt); else if (stat(src, &st) && errno == ENOTDIR) { if (uflags & MNT_MS_NOFAIL) return EX_SUCCESS; warnx(_("special device %s does not exist " "(a path prefix is not a directory)"), src); } else { errno = syserr; warn(_("mount(2) failed")); /* print errno */ } break; case EINVAL: if (mflags & MS_REMOUNT) warnx(_("%s not mounted or bad option"), tgt); else warnx(_("wrong fs type, bad option, bad superblock on %s,\n" " missing codepage or helper program, or other error"), src); if (mnt_fs_is_netfs(mnt_context_get_fs(cxt))) fprintf(stderr, _( " (for several filesystems (e.g. nfs, cifs) you might\n" " need a /sbin/mount. helper program)\n")); fprintf(stderr, _( " In some cases useful info is found in syslog - try\n" " dmesg | tail or so\n")); break; case EMFILE: warnx(_("mount table full")); break; case EIO: warnx(_("%s: can't read superblock"), src); break; case ENODEV: warnx(_("unknown filesystem type '%s'"), mnt_context_get_fstype(cxt)); break; case ENOTBLK: if (uflags & MNT_MS_NOFAIL) return EX_SUCCESS; if (stat(src, &st)) warnx(_("%s is not a block device, and stat(2) fails?"), src); else if (S_ISBLK(st.st_mode)) warnx(_("the kernel does not recognize %s as a block device\n" " (maybe `modprobe driver'?)"), src); else if (S_ISREG(st.st_mode)) warnx(_("%s is not a block device (maybe try `-o loop'?)"), src); else warnx(_(" %s is not a block device"), src); break; case ENXIO: if (uflags & MNT_MS_NOFAIL) return EX_SUCCESS; warnx(_("%s is not a valid block device"), src); break; case EACCES: case EROFS: if (mflags & MS_RDONLY) warnx(_("cannot mount %s read-only"), src); else if (readwrite) warnx(_("%s is write-protected but explicit `-w' flag given"), src); else if (mflags & MS_REMOUNT) warnx(_("cannot remount %s read-write, is write-protected"), src); else { warnx(_("%s is write-protected, mounting read-only"), src); mnt_context_reset_status(cxt); mnt_context_set_mflags(cxt, mflags | MS_RDONLY); rc = mnt_context_do_mount(cxt); if (!rc) rc = mnt_context_finalize_mount(cxt); goto try_readonly; } break; case ENOMEDIUM: warnx(_("no medium found on %s"), src); break; default: warn(_("mount %s on %s failed"), src, tgt); break; } return EX_FAIL; } static void __attribute__((__noreturn__)) usage(FILE *out) { fputs(USAGE_HEADER, out); fprintf(out, _( " %1$s [-lhV]\n" " %1$s -a [options]\n" " %1$s [options] | \n" " %1$s [options] \n" " %1$s []\n"), program_invocation_short_name); 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")); fprintf(out, _( " -h, --help display this help text and exit\n" " -i, --internal-only don't call the mount. helpers\n" " -l, --show-labels lists all mounts with LABELs\n" " -n, --no-mtab don't write to /etc/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" " -p, --pass-fd read the passphrase from file descriptor\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, _( " -v, --verbose say what is being done\n" " -V, --version display version information and exit\n" " -w, --read-write mount the filesystem read-write (default)\n")); fputs(USAGE_SEPARATOR, out); fputs(USAGE_HELP, out); fputs(USAGE_VERSION, out); fprintf(out, _( "\nSource:\n" " -L, --label