diff options
Diffstat (limited to 'mount-deprecated/mount.c')
-rw-r--r-- | mount-deprecated/mount.c | 2799 |
1 files changed, 2799 insertions, 0 deletions
diff --git a/mount-deprecated/mount.c b/mount-deprecated/mount.c new file mode 100644 index 000000000..2e9b2a4e3 --- /dev/null +++ b/mount-deprecated/mount.c @@ -0,0 +1,2799 @@ +/* + * A mount(8) for Linux. + * + * Modifications by many people. Distributed under GPL. + */ + +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <stdio.h> + +#include <pwd.h> +#include <grp.h> + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/mount.h> + +#include <mntent.h> + +#ifdef HAVE_LIBSELINUX +#include <selinux/selinux.h> +#include <selinux/context.h> +#endif + +#include "pathnames.h" +#include "fsprobe.h" +#include "devname.h" +#include "mount_constants.h" +#include "sundries.h" +#include "mount_mntent.h" +#include "fstab.h" +#include "loopdev.h" +#include "linux_version.h" +#include "getusername.h" +#include "env.h" +#include "nls.h" +#include "blkdev.h" +#include "strutils.h" +#include "closestream.h" +#include "xgetpass.h" + +#define DO_PS_FIDDLING + +#ifdef DO_PS_FIDDLING +#include "setproctitle.h" +#endif + +/* True for fake mount (-f). */ +static int fake = 0; + +/* True if we are allowed to call /sbin/mount.${FSTYPE} */ +static int external_allowed = 1; + +/* Don't write an entry in /etc/mtab (-n). */ +static int nomtab = 0; + +/* True for explicit readonly (-r). */ +static int readonly = 0; + +/* Nonzero for sloppy (-s). */ +static int sloppy = 0; + +/* True for explicit read/write (-w). */ +static int readwrite = 0; + +/* True for all mount (-a). */ +static int mount_all = 0; + +/* True for fork() during all mount (-F). */ +static int optfork = 0; + +/* Add volumelabel in a listing of mounted devices (-l). */ +static int list_with_volumelabel = 0; + +/* Nonzero for mount {bind|move|make-shared|make-private| + * make-unbindable|make-slave} + */ +static int mounttype = 0; + +/* True if (ruid != euid) or (0 != ruid), i.e. only "user" mounts permitted. */ +static int restricted = 1; + +/* Contains the fd to read the passphrase from, if any. */ +static int pfd = -1; + +#ifdef HAVE_LIBMOUNT_MOUNT +static struct libmnt_update *mtab_update; +static char *mtab_opts; +static unsigned long mtab_flags; + +static void prepare_mtab_entry(const char *spec, const char *node, + const char *type, const char *opts, unsigned long flags); +#endif + +/* mount(2) options */ +struct mountargs { + const char *spec; + const char *node; + const char *type; + int flags; + void *data; +}; + +/* Map from -o and fstab option strings to the flag argument to mount(2). */ +struct opt_map { + const char *opt; /* option name */ + int skip; /* skip in mtab option string */ + int inv; /* true if flag value should be inverted */ + int mask; /* flag mask value */ + int cmask; /* comments mask */ +}; + +/* Custom mount options for our own purposes. */ +/* Maybe these should now be freed for kernel use again */ +#define MS_NOAUTO 0x80000000 +#define MS_USERS 0x40000000 +#define MS_USER 0x20000000 +#define MS_OWNER 0x10000000 +#define MS_GROUP 0x08000000 +#define MS_COMMENT 0x02000000 +#define MS_LOOP 0x00010000 + +#define MS_COMMENT_NOFAIL (1 << 1) +#define MS_COMMENT_NETDEV (1 << 2) + +/* Options that we keep the mount system call from seeing. */ +#define MS_NOSYS (MS_NOAUTO|MS_USERS|MS_USER|MS_COMMENT|MS_LOOP) + +/* Options that we keep from appearing in the options field in the mtab. */ +#define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USERS|MS_USER) + +#define MS_PROPAGATION (MS_SHARED|MS_SLAVE|MS_UNBINDABLE|MS_PRIVATE) + +/* Options that we make ordinary users have by default. */ +#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV) + +/* Options that we make owner-mounted devices have by default */ +#define MS_OWNERSECURE (MS_NOSUID|MS_NODEV) + +static const struct opt_map opt_map[] = { + { "defaults", 0, 0, 0 }, /* default options */ + { "ro", 1, 0, MS_RDONLY }, /* read-only */ + { "rw", 1, 1, MS_RDONLY }, /* read-write */ + { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */ + { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */ + { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */ + { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */ + { "dev", 0, 1, MS_NODEV }, /* interpret device files */ + { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */ + { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */ + { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ + { "dirsync", 0, 0, MS_DIRSYNC}, /* synchronous directory modifications */ + { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */ + { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */ + { "rbind", 0, 0, MS_BIND|MS_REC }, /* Idem, plus mounted subtrees */ + { "auto", 0, 1, MS_NOAUTO }, /* Can be mounted using -a */ + { "noauto", 0, 0, MS_NOAUTO }, /* Can only be mounted explicitly */ + { "users", 0, 0, MS_USERS }, /* Allow ordinary user to mount */ + { "nousers", 0, 1, MS_USERS }, /* Forbid ordinary user to mount */ + { "user", 0, 0, MS_USER }, /* Allow ordinary user to mount */ + { "nouser", 0, 1, MS_USER }, /* Forbid ordinary user to mount */ + { "owner", 0, 0, MS_OWNER }, /* Let the owner of the device mount */ + { "noowner", 0, 1, MS_OWNER }, /* Device owner has no special privs */ + { "group", 0, 0, MS_GROUP }, /* Let the group of the device mount */ + { "nogroup", 0, 1, MS_GROUP }, /* Device group has no special privs */ + { "_netdev", 0, 0, MS_COMMENT, MS_COMMENT_NETDEV }, /* Device requires network */ + { "comment", 0, 0, MS_COMMENT}, /* fstab comment only (kudzu,_netdev)*/ + + /* add new options here */ +#ifdef MS_NOSUB + { "sub", 0, 1, MS_NOSUB }, /* allow submounts */ + { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */ +#endif +#ifdef MS_SILENT + { "silent", 0, 0, MS_SILENT }, /* be quiet */ + { "loud", 0, 1, MS_SILENT }, /* print out messages. */ +#endif +#ifdef MS_MANDLOCK + { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */ + { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */ +#endif + { "loop", 1, 0, MS_LOOP }, /* use a loop device */ +#ifdef MS_NOATIME + { "atime", 0, 1, MS_NOATIME }, /* Update access time */ + { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */ +#endif +#ifdef MS_I_VERSION + { "iversion", 0, 0, MS_I_VERSION }, /* Update inode I_version time */ + { "noiversion", 0, 1, MS_I_VERSION }, /* Don't update inode I_version time */ +#endif +#ifdef MS_NODIRATIME + { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */ + { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */ +#endif +#ifdef MS_RELATIME + { "relatime", 0, 0, MS_RELATIME }, /* Update access times relative to + mtime/ctime */ + { "norelatime", 0, 1, MS_RELATIME }, /* Update access time without regard + to mtime/ctime */ +#endif +#ifdef MS_STRICTATIME + { "strictatime", 0, 0, MS_STRICTATIME }, /* Strict atime semantics */ + { "nostrictatime", 0, 1, MS_STRICTATIME }, /* kernel default atime */ +#endif + { "nofail", 0, 0, MS_COMMENT, MS_COMMENT_NOFAIL }, /* Do not fail if ENOENT on dev */ + { NULL, 0, 0, 0 } +}; + +static int opt_nofail; +static int invuser_flags; +static int comment_flags; + +static const char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_sizelimit, + *opt_encryption, *opt_speed, *opt_comment, *opt_uhelper, *opt_helper; + +static int is_readonly(const char *node); +static int mounted (const char *spec0, const char *node0, struct mntentchn *fstab_mc); +static int check_special_mountprog(const char *spec, const char *node, + const char *type, int flags, char *extra_opts, int *status); + +static struct string_opt_map { + char *tag; + int skip; + const char **valptr; +} string_opt_map[] = { + { "loop=", 0, &opt_loopdev }, + { "vfs=", 1, &opt_vfstype }, + { "offset=", 0, &opt_offset }, + { "sizelimit=", 0, &opt_sizelimit }, + { "encryption=", 0, &opt_encryption }, + { "speed=", 0, &opt_speed }, + { "comment=", 1, &opt_comment }, + { "uhelper=", 0, &opt_uhelper }, + { "helper=", 0, &opt_helper }, + { NULL, 0, NULL } +}; + +static void +clear_string_opts(void) { + struct string_opt_map *m; + + for (m = &string_opt_map[0]; m->tag; m++) + *(m->valptr) = NULL; +} + +static void +clear_flags_opts(void) { + invuser_flags = 0; + comment_flags = 0; + opt_nofail = 0; +} + +static int +parse_string_opt(char *s) { + struct string_opt_map *m; + int lth; + + for (m = &string_opt_map[0]; m->tag; m++) { + lth = strlen(m->tag); + if (!strncmp(s, m->tag, lth)) { + *(m->valptr) = xstrdup(s + lth); + return 1; + } + } + return 0; +} + +/* Report on a single mount. */ +static void +print_one (const struct my_mntent *me) { + + char *fsname = NULL; + + if (mount_quiet) + return; + + /* users assume backing file name rather than /dev/loopN in + * mount(8) output if the device has been initialized by mount(8). + */ + if (strncmp(me->mnt_fsname, "/dev/loop", 9) == 0 && + loopdev_is_autoclear(me->mnt_fsname)) + fsname = loopdev_get_backing_file(me->mnt_fsname); + + if (!fsname) + fsname = (char *) me->mnt_fsname; + + printf ("%s on %s", fsname, me->mnt_dir); + if (me->mnt_type != NULL && *(me->mnt_type) != '\0') + printf (" type %s", me->mnt_type); + if (me->mnt_opts != NULL) + printf (" (%s)", me->mnt_opts); + if (list_with_volumelabel && is_pseudo_fs(me->mnt_type) == 0) { + const char *devname = spec_to_devname(me->mnt_fsname); + + if (devname) { + const char *label; + + label = fsprobe_get_label_by_devname(devname); + my_free(devname); + + if (label) { + printf (" [%s]", label); + my_free(label); + } + } + } + printf ("\n"); +} + +/* Report on everything in mtab (of the specified types if any). */ +static int +print_all (char *types) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + if (matching_type (mc->m.mnt_type, types)) + print_one (&(mc->m)); + } + + if (!mtab_does_not_exist() && !mtab_is_a_symlink() && is_readonly(_PATH_MOUNTED)) + printf(_("\n" + "mount: warning: /etc/mtab is not writable (e.g. read-only filesystem).\n" + " It's possible that information reported by mount(8) is not\n" + " up to date. For actual information about system mount points\n" + " check the /proc/mounts file.\n\n")); + + exit (0); +} + +/* reallocates its first arg */ +static char * +append_opt(char *s, const char *opt, const char *val) +{ + if (!opt) + return s; + if (!s) { + if (!val) + return xstrdup(opt); /* opt */ + + return xstrconcat3(NULL, opt, val); /* opt=val */ + } + if (!val) + return xstrconcat3(s, ",", opt); /* s,opt */ + + return xstrconcat4(s, ",", opt, val); /* s,opt=val */ +} + +static char * +append_numopt(char *s, const char *opt, unsigned int num) +{ + char buf[32]; + + snprintf(buf, sizeof(buf), "%u", num); + return append_opt(s, opt, buf); +} + +#ifdef HAVE_LIBSELINUX +/* strip quotes from a "string" + * Warning: This function modify the "str" argument. + */ +static char * +strip_quotes(char *str) +{ + char *end = NULL; + + if (*str != '"') + return str; + + end = strrchr(str, '"'); + if (end == NULL || end == str) + die (EX_USAGE, _("mount: improperly quoted option string '%s'"), str); + + *end = '\0'; + return str+1; +} + +/* translates SELinux context from human to raw format and + * appends it to the mount extra options. + * + * returns -1 on error and 0 on success + */ +static int +append_context(const char *optname, char *optdata, char **extra_opts) +{ + security_context_t raw = NULL; + char *data = NULL; + + if (is_selinux_enabled() != 1) + /* ignore the option if we running without selinux */ + return 0; + + if (optdata==NULL || *optdata=='\0' || optname==NULL) + return -1; + + /* TODO: use strip_quotes() for all mount options? */ + data = *optdata =='"' ? strip_quotes(optdata) : optdata; + + if (selinux_trans_to_raw_context( + (security_context_t) data, &raw) == -1 || + raw == NULL) + return -1; + + if (verbose) + printf(_("mount: translated %s '%s' to '%s'\n"), + optname, data, (char *) raw); + + *extra_opts = append_opt(*extra_opts, optname, NULL); + *extra_opts = xstrconcat4(*extra_opts, "\"", (char *) raw, "\""); + + freecon(raw); + return 0; +} + +/* returns newly allocated string without *context= options */ +static char *remove_context_options(char *opts) +{ + char *begin = NULL, *end = NULL, *p; + int open_quote = 0, changed = 0; + + if (!opts) + return NULL; + + opts = xstrdup(opts); + + for (p = opts; p && *p; p++) { + if (!begin) + begin = p; /* begin of the option item */ + if (*p == '"') + open_quote ^= 1; /* reverse the status */ + if (open_quote) + continue; /* still in quoted block */ + if (*p == ',') + end = p; /* terminate the option item */ + else if (*(p + 1) == '\0') + end = p + 1; /* end of optstr */ + if (!begin || !end) + continue; + + if (strncmp(begin, "context=", 8) == 0 || + strncmp(begin, "fscontext=", 10) == 0 || + strncmp(begin, "defcontext=", 11) == 0 || + strncmp(begin, "rootcontext=", 12) == 0 || + strncmp(begin, "seclabel", 8) == 0) { + size_t sz; + + if ((begin == opts || *(begin - 1) == ',') && *end == ',') + end++; + sz = strlen(end); + + memmove(begin, end, sz + 1); + if (!*begin && *(begin - 1) == ',') + *(begin - 1) = '\0'; + + p = begin; + changed = 1; + } + begin = end = NULL; + } + + if (changed && verbose) + printf (_("mount: SELinux *context= options are ignore on remount.\n")); + + return opts; +} + +static int has_context_option(char *opts) +{ + if (get_option("context=", opts, NULL) || + get_option("fscontext=", opts, NULL) || + get_option("defcontext=", opts, NULL) || + get_option("rootcontext=", opts, NULL)) + return 1; + + return 0; +} + +#endif + +/* + * Look for OPT in opt_map table and return mask value. + * If OPT isn't found, tack it onto extra_opts (which is non-NULL). + * For the options uid= and gid= replace user or group name by its value. + */ +static inline void +parse_opt(char *opt, int *mask, int *inv_user, char **extra_opts) { + const struct opt_map *om; + + for (om = opt_map; om->opt != NULL; om++) + if (streq (opt, om->opt)) { + if (om->inv) + *mask &= ~om->mask; + else + *mask |= om->mask; + if (om->inv && ((*mask & MS_USER) || (*mask & MS_USERS)) + && (om->mask & MS_SECURE)) + *inv_user |= om->mask; + if ((om->mask == MS_USER || om->mask == MS_USERS) + && !om->inv) + *mask |= MS_SECURE; + if ((om->mask == MS_OWNER || om->mask == MS_GROUP) + && !om->inv) + *mask |= MS_OWNERSECURE; +#ifdef MS_SILENT + if (om->mask == MS_SILENT && om->inv) { + mount_quiet = 1; + verbose = 0; + } +#endif + if (om->mask == MS_COMMENT) { + comment_flags |= om->cmask; + if (om->cmask == MS_COMMENT_NOFAIL) + opt_nofail = 1; + } + return; + } + + /* convert nonnumeric ids to numeric */ + if (!strncmp(opt, "uid=", 4) && !isdigit(opt[4])) { + struct passwd *pw = getpwnam(opt+4); + + if (pw) { + *extra_opts = append_numopt(*extra_opts, + "uid=", pw->pw_uid); + return; + } + } + if (!strncmp(opt, "gid=", 4) && !isdigit(opt[4])) { + struct group *gr = getgrnam(opt+4); + + if (gr) { + *extra_opts = append_numopt(*extra_opts, + "gid=", gr->gr_gid); + return; + } + } + +#ifdef HAVE_LIBSELINUX + if (strncmp(opt, "context=", 8) == 0 && *(opt+8)) { + if (append_context("context=", opt+8, extra_opts) == 0) + return; + } + if (strncmp(opt, "fscontext=", 10) == 0 && *(opt+10)) { + if (append_context("fscontext=", opt+10, extra_opts) == 0) + return; + } + if (strncmp(opt, "defcontext=", 11) == 0 && *(opt+11)) { + if (append_context("defcontext=", opt+11, extra_opts) == 0) + return; + } + if (strncmp(opt, "rootcontext=", 12) == 0 && *(opt+12)) { + if (append_context("rootcontext=", opt+12, extra_opts) == 0) + return; + } +#endif + *extra_opts = append_opt(*extra_opts, opt, NULL); +} + + +/* Take -o options list and compute 4th and 5th args to mount(2). flags + gets the standard options (indicated by bits) and extra_opts all the rest */ +static void +parse_opts (const char *options, int *flags, char **extra_opts) { + *flags = 0; + *extra_opts = NULL; + + clear_string_opts(); + clear_flags_opts(); + + if (options != NULL) { + char *opts = xstrdup(options); + int open_quote = 0; + char *opt, *p; + + for (p=opts, opt=NULL; p && *p; p++) { + if (!opt) + opt = p; /* begin of the option item */ + if (*p == '"') + open_quote ^= 1; /* reverse the status */ + if (open_quote) + continue; /* still in quoted block */ + if (*p == ',') + *p = '\0'; /* terminate the option item */ + /* end of option item or last item */ + if (*p == '\0' || *(p+1) == '\0') { + if (!parse_string_opt(opt)) + parse_opt(opt, flags, &invuser_flags, extra_opts); + opt = NULL; + } + } + free(opts); + } + + if (readonly) + *flags |= MS_RDONLY; + if (readwrite) + *flags &= ~MS_RDONLY; + + *flags |= mounttype; + + /* The propagation flags should not be used together with any + * other flags (except MS_REC and MS_SILENT) */ + if (*flags & MS_PROPAGATION) + *flags &= (MS_PROPAGATION | MS_REC | MS_SILENT); +} + +/* Try to build a canonical options string. */ +static char * +fix_opts_string (int flags, const char *extra_opts, + const char *user, int inv_user) +{ + const struct opt_map *om; + const struct string_opt_map *m; + char *new_opts; + + new_opts = append_opt(NULL, (flags & MS_RDONLY) ? "ro" : "rw", NULL); + for (om = opt_map; om->opt != NULL; om++) { + if (om->skip) + continue; + if (om->inv || !om->mask || (flags & om->mask) != om->mask) + continue; + if (om->mask == MS_COMMENT && !(comment_flags & om->cmask)) + continue; + new_opts = append_opt(new_opts, om->opt, NULL); + flags &= ~om->mask; + } + for (m = &string_opt_map[0]; m->tag; m++) { + if (!m->skip && *(m->valptr)) + new_opts = append_opt(new_opts, m->tag, *(m->valptr)); + } + if (extra_opts && *extra_opts) + new_opts = append_opt(new_opts, extra_opts, NULL); + + if (user) + new_opts = append_opt(new_opts, "user=", user); + + if (inv_user) { + for (om = opt_map; om->opt != NULL; om++) { + if (om->mask && om->inv + && (inv_user & om->mask) == om->mask) { + new_opts = append_opt(new_opts, om->opt, NULL); + inv_user &= ~om->mask; + } + } + } + + return new_opts; +} + +static int +already (const char *spec0, const char *node0) { + struct mntentchn *mc; + int ret = 1; + char *spec = canonicalize_spec(spec0); + char *node = canonicalize(node0); + + if ((mc = getmntfile(node)) != NULL) + error (_("mount: according to mtab, " + "%s is already mounted on %s"), + mc->m.mnt_fsname, node); + else if (spec && strcmp (spec, "none") && + (mc = getmntfile(spec)) != NULL) + error (_("mount: according to mtab, %s is mounted on %s"), + spec, mc->m.mnt_dir); + else + ret = 0; + + free(spec); + free(node); + + return ret; +} + +/* Create mtab with a root entry. */ +static void +create_mtab (void) { + struct mntentchn *fstab; + struct my_mntent mnt; + int flags; + mntFILE *mfp; + + lock_mtab(); + + mfp = my_setmntent (_PATH_MOUNTED, "a+"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + int errsv = errno; + die (EX_FILEIO, _("mount: can't open %s for writing: %s"), + _PATH_MOUNTED, strerror (errsv)); + } + + /* Find the root entry by looking it up in fstab */ + if ((fstab = getfs_by_dir ("/")) || (fstab = getfs_by_dir ("root"))) { + char *extra_opts; + parse_opts (fstab->m.mnt_opts, &flags, &extra_opts); + mnt.mnt_dir = "/"; + mnt.mnt_fsname = spec_to_devname(fstab->m.mnt_fsname); + mnt.mnt_type = fstab->m.mnt_type; + mnt.mnt_opts = fix_opts_string (flags, extra_opts, NULL, 0); + mnt.mnt_freq = mnt.mnt_passno = 0; + free(extra_opts); + + if (my_addmntent (mfp, &mnt) == 1) { + int errsv = errno; + die (EX_FILEIO, _("mount: error writing %s: %s"), + _PATH_MOUNTED, strerror (errsv)); + } + } + if (fchmod (fileno (mfp->mntent_fp), 0644) < 0) + if (errno != EROFS) { + int errsv = errno; + die (EX_FILEIO, + _("mount: error changing mode of %s: %s"), + _PATH_MOUNTED, strerror (errsv)); + } + my_endmntent (mfp); + + unlock_mtab(); + + reset_mtab_info(); +} + +/* count successful mount system calls */ +static int mountcount = 0; + +/* + * do_mount_syscall() + * Mount a single file system. Keep track of successes. + * returns: 0: OK, -1: error in errno + */ +static int +do_mount_syscall (struct mountargs *args) { + int flags = args->flags; + + if ((flags & MS_MGC_MSK) == 0) + flags |= MS_MGC_VAL; + + if (verbose > 2) + printf("mount: mount(2) syscall: source: \"%s\", target: \"%s\", " + "filesystemtype: \"%s\", mountflags: %d, data: %s\n", + args->spec, args->node, args->type, flags, (char *) args->data); + + return mount (args->spec, args->node, args->type, flags, args->data); +} + +/* + * do_mount() + * Mount a single file system, possibly invoking an external handler to + * do so. Keep track of successes. + * returns: 0: OK, -1: error in errno + */ +static int +do_mount (struct mountargs *args, int *special, int *status) { + int ret; + if (check_special_mountprog(args->spec, args->node, args->type, + args->flags, args->data, status)) { + *special = 1; + ret = 0; + } else { +#ifdef HAVE_LIBMOUNT_MOUNT + prepare_mtab_entry(args->spec, args->node, args->type, + mtab_opts, mtab_flags); +#endif + ret = do_mount_syscall(args); + } + if (ret == 0) + mountcount++; + return ret; +} + +/* + * check_special_mountprog() + * If there is a special mount program for this type, exec it. + * returns: 0: no exec was done, 1: exec was done, status has result + */ +static int +check_special_mountprog(const char *spec, const char *node, const char *type, int flags, + char *extra_opts, int *status) { + char search_path[] = FS_SEARCH_PATH; + char *path, mountprog[150]; + struct stat statbuf; + int res; + + if (!external_allowed) + return 0; + + if (type == NULL || strcmp(type, "none") == 0) + return 0; + + path = strtok(search_path, ":"); + while (path) { + int type_opt = 0; + + res = snprintf(mountprog, sizeof(mountprog), "%s/mount.%s", + path, type); + path = strtok(NULL, ":"); + if (res < 0 || (size_t) res >= sizeof(mountprog)) + continue; + + res = stat(mountprog, &statbuf); + if (res == -1 && errno == ENOENT && strchr(type, '.')) { + /* If type ends with ".subtype" try without it */ + *strrchr(mountprog, '.') = '\0'; + type_opt = 1; + res = stat(mountprog, &statbuf); + } + if (res) + continue; + + if (verbose) + fflush(stdout); + + switch (fork()) { + case 0: { /* child */ + char *oo, *mountargs[12]; + int i = 0; + + if (setgid(getgid()) < 0) + die(EX_FAIL, _("mount: cannot set group id: %m")); + + if (setuid(getuid()) < 0) + die(EX_FAIL, _("mount: cannot set user id: %m")); + + oo = fix_opts_string(flags, extra_opts, NULL, invuser_flags); + mountargs[i++] = mountprog; /* 1 */ + mountargs[i++] = (char *) spec; /* 2 */ + mountargs[i++] = (char *) node; /* 3 */ + if (sloppy && strncmp(type, "nfs", 3) == 0) + mountargs[i++] = "-s"; /* 4 */ + if (fake) + mountargs[i++] = "-f"; /* 5 */ + if (nomtab) + mountargs[i++] = "-n"; /* 6 */ + if (verbose) + mountargs[i++] = "-v"; /* 7 */ + if (oo && *oo) { + mountargs[i++] = "-o"; /* 8 */ + mountargs[i++] = oo; /* 9 */ + } + if (type_opt) { + mountargs[i++] = "-t"; /* 10 */ + mountargs[i++] = (char *) type; /* 11 */ + } + mountargs[i] = NULL; /* 12 */ + + if (verbose > 2) { + i = 0; + while (mountargs[i]) { + printf("mount: external mount: argv[%d] = \"%s\"\n", + i, mountargs[i]); + i++; + } + fflush(stdout); + } + + execv(mountprog, mountargs); + exit(1); /* exec failed */ + } + + default: { /* parent */ + int st; + wait(&st); + *status = (WIFEXITED(st) ? WEXITSTATUS(st) : EX_SYSERR); + return 1; + } + + case -1: { /* error */ + int errsv = errno; + error(_("mount: cannot fork: %s"), strerror(errsv)); + } + } + } + + return 0; +} + + +/* list of already tested filesystems by procfsloop_mount() */ +static struct tried { + struct tried *next; + char *type; +} *tried = NULL; + +static int +was_tested(const char *fstype) { + struct tried *t; + + for (t = tried; t; t = t->next) { + if (!strcmp(t->type, fstype)) + return 1; + } + return 0; +} + +static void +set_tested(const char *fstype) { + struct tried *t = xmalloc(sizeof(struct tried)); + + t->next = tried; + t->type = xstrdup(fstype); + tried = t; +} + +static void +free_tested(void) { + struct tried *t, *tt; + + t = tried; + while(t) { + free(t->type); + tt = t->next; + free(t); + t = tt; + } + tried = NULL; +} + +static char * +procfsnext(FILE *procfs) { + char line[100]; + char fsname[100]; + + while (fgets(line, sizeof(line), procfs)) { + if (sscanf (line, "nodev %[^#\n]\n", fsname) == 1) continue; + if (sscanf (line, " %[^# \n]\n", fsname) != 1) continue; + return xstrdup(fsname); + } + return 0; +} + +/* Only use /proc/filesystems here, this is meant to test what + the kernel knows about, so /etc/filesystems is irrelevant. + Return: 1: yes, 0: no, -1: cannot open procfs */ +static int +known_fstype_in_procfs(const char *type) +{ + FILE *procfs; + char *fsname; + int ret = -1; + + procfs = fopen(_PATH_PROC_FILESYSTEMS, "r"); + if (procfs) { + ret = 0; + while ((fsname = procfsnext(procfs)) != NULL) + if (!strcmp(fsname, type)) { + ret = 1; + break; + } + fclose(procfs); + procfs = NULL; + } + return ret; +} + +/* Try all types in FILESYSTEMS, except those in *types, + in case *types starts with "no" */ +/* return: 0: OK, -1: error in errno, 1: type not found */ +/* when 0 or -1 is returned, *types contains the type used */ +/* when 1 is returned, *types is NULL */ +static int +procfsloop_mount(int (*mount_fn)(struct mountargs *, int *, int *), + struct mountargs *args, + const char **types, + int *special, int *status) +{ + char *files[2] = { _PATH_FILESYSTEMS, _PATH_PROC_FILESYSTEMS }; + FILE *procfs; + char *fsname; + const char *notypes = NULL; + int no = 0; + int ret = 1; + int errsv = 0; + int i; + + if (*types && !strncmp(*types, "no", 2)) { + no = 1; + notypes = (*types) + 2; + } + *types = NULL; + + /* Use _PATH_PROC_FILESYSTEMS only when _PATH_FILESYSTEMS + * (/etc/filesystems) does not exist. In some cases trying a + * filesystem that the kernel knows about on the wrong data will crash + * the kernel; in such cases _PATH_FILESYSTEMS can be used to list the + * filesystems that we are allowed to try, and in the order they should + * be tried. End _PATH_FILESYSTEMS with a line containing a single '*' + * only, if _PATH_PROC_FILESYSTEMS should be tried afterwards. + */ + for (i=0; i<2; i++) { + procfs = fopen(files[i], "r"); + if (!procfs) + continue; + while ((fsname = procfsnext(procfs)) != NULL) { + if (!strcmp(fsname, "*")) { + fclose(procfs); + goto nexti; + } + if (was_tested (fsname)) + continue; + if (no && matching_type(fsname, notypes)) + continue; + set_tested (fsname); + args->type = fsname; + if (verbose) + printf(_("Trying %s\n"), fsname); + if ((*mount_fn) (args, special, status) == 0) { + *types = fsname; + ret = 0; + break; + } else if (errno != EINVAL && + known_fstype_in_procfs(fsname) == 1) { + *types = "guess"; + ret = -1; + errsv = errno; + break; + } + } + free_tested(); + fclose(procfs); + errno = errsv; + return ret; + nexti:; + } + return 1; +} + +static const char * +guess_fstype_by_devname(const char *devname, int *ambivalent) +{ + const char *type = fsprobe_get_fstype_by_devname_ambi(devname, ambivalent); + + if (verbose) { + printf (_("mount: you didn't specify a filesystem type for %s\n"), devname); + + if (!type) + printf (_(" I will try all types mentioned in %s or %s\n"), + _PATH_FILESYSTEMS, _PATH_PROC_FILESYSTEMS); + else if (!strcmp(type, MNTTYPE_SWAP)) + printf (_(" and it looks like this is swapspace\n")); + else + printf (_(" I will try type %s\n"), type); + } + return type; +} + +/* + * guess_fstype_and_mount() + * Mount a single file system. Guess the type when unknown. + * returns: 0: OK, -1: error in errno, 1: other error + * don't exit on non-fatal errors. + * on return types is filled with the type used. + */ +static int +guess_fstype_and_mount(const char *spec, const char *node, const char **types, + int flags, char *mount_opts, int *special, int *status) { + struct mountargs args = { spec, node, NULL, flags & ~MS_NOSYS, mount_opts }; + int ambivalent = 0; + + if (*types && strcasecmp (*types, "auto") == 0) + *types = NULL; + + if (!*types && !(flags & MS_REMOUNT)) { + *types = guess_fstype_by_devname(spec, &ambivalent); + if (*types) { + if (!strcmp(*types, MNTTYPE_SWAP)) { + error(_("%s looks like swapspace - not mounted"), spec); + *types = NULL; + return 1; + } else { + args.type = *types; + return do_mount (&args, special, status); + } + } else if (ambivalent) { + error(_("mount: %s: more filesystems detected. This should not happen,\n" + " use -t <type> to explicitly specify the filesystem type or\n" + " use wipefs(8) to clean up the device.\n"), spec); + return 1; + } + } + + /* Accept a comma-separated list of types, and try them one by one */ + /* A list like "nonfs,.." indicates types not to use */ + if (*types && strncmp(*types, "no", 2) && strchr(*types,',')) { + char *t = strdup(*types); + char *p; + + while((p = strchr(t,',')) != NULL) { + *p = 0; + args.type = *types = t; + if (do_mount (&args, special, status) == 0) + return 0; + t = p+1; + } + /* do last type below */ + *types = t; + } + + if (*types || (flags & MS_REMOUNT)) { + args.type = *types; + return do_mount (&args, special, status); + } + + return procfsloop_mount(do_mount, &args, types, special, status); +} + +/* + * restricted_check() + * Die if the user is not allowed to do this. + */ +static void +restricted_check(const char *spec, const char *node, int *flags, char **user) { + if (restricted) { + /* + * MS_OWNER: Allow owners to mount when fstab contains + * the owner option. Note that this should never be used + * in a high security environment, but may be useful to give + * people at the console the possibility of mounting a floppy. + * MS_GROUP: Allow members of device group to mount. (Martin Dickopp) + */ + if (*flags & (MS_OWNER | MS_GROUP)) { + struct stat sb; + + if (!strncmp(spec, "/dev/", 5) && stat(spec, &sb) == 0) { + + if (*flags & MS_OWNER) { + if (getuid() == sb.st_uid) + *flags |= MS_USER; + } + + if (*flags & MS_GROUP) { + if (getgid() == sb.st_gid) + *flags |= MS_USER; + else { + int n = getgroups(0, NULL); + + if (n > 0) { + gid_t *groups = xmalloc(n * sizeof(*groups)); + if (getgroups(n, groups) == n) { + int i; + for (i = 0; i < n; i++) { + if (groups[i] == sb.st_gid) { + *flags |= MS_USER; + break; + } + } + } + free(groups); + } + } + } + } + } + + /* James Kehl <mkehl@gil.com.au> came with a similar patch: + allow an arbitrary user to mount when he is the owner of + the mount-point and has write-access to the device. + This is even less secure. Let me skip it for the time being; + there should be an explicit fstab line allowing such things. */ + + if (!(*flags & (MS_USER | MS_USERS))) { + if (already (spec, node)) + die (EX_USAGE, _("mount failed")); + else + die (EX_USAGE, _("mount: only root can mount %s on %s"), spec, node); + } + if (*flags & MS_USER) + *user = getusername(); + } + + *flags &= ~(MS_OWNER | MS_GROUP); +} + +/* Check, if there already exists a mounted loop device on the mountpoint node + * with the same parameters. + */ +static int +is_mounted_same_loopfile(const char *node0, const char *loopfile, unsigned long long offset) +{ + struct mntentchn *mnt = NULL; + char *node; + int res = 0; + + node = canonicalize(node0); + + /* Search for mountpoint node in mtab, + * procceed if any of these has the loop option set or + * the device is a loop device + */ + mnt = getmntdirbackward(node, mnt); + if (!mnt) { + free(node); + return 0; + } + for(; mnt && res == 0; mnt = getmntdirbackward(node, mnt)) { + char *p; + + if (strncmp(mnt->m.mnt_fsname, "/dev/loop", 9) == 0) + res = loopdev_is_used((char *) mnt->m.mnt_fsname, + loopfile, offset, LOOPDEV_FL_OFFSET); + + else if (mnt->m.mnt_opts && + (p = strstr(mnt->m.mnt_opts, "loop="))) + { + char *dev = xstrdup(p+5); + if ((p = strchr(dev, ','))) + *p = '\0'; + res = loopdev_is_used(dev, + loopfile, offset, LOOPDEV_FL_OFFSET); + free(dev); + } + } + + free(node); + return res; +} + +static int +parse_offset(const char **opt, uintmax_t *val) +{ + char *tmp; + + if (strtosize(*opt, val)) + return -1; + + tmp = xmalloc(32); + snprintf(tmp, 32, "%jd", *val); + my_free(*opt); + *opt = tmp; + return 0; +} + +static int +loop_check(const char **spec, const char **type, int *flags, + int *loop, const char **loopdev, const char **loopfile, + const char *node) { + int looptype; + uintmax_t offset = 0, sizelimit = 0; + struct loopdev_cxt lc; + char *pwd = NULL; + int ret = EX_FAIL; + + /* + * In the case of a loop mount, either type is of the form lo@/dev/loop5 + * or the option "-o loop=/dev/loop5" or just "-o loop" is given, or + * mount just has to figure things out for itself from the fact that + * spec is not a block device. We do not test for a block device + * immediately: maybe later other types of mountable objects will occur. + */ + + *loopdev = opt_loopdev; + + looptype = (*type && strncmp("lo@", *type, 3) == 0); + if (looptype) { + if (*loopdev) + error(_("mount: loop device specified twice")); + *loopdev = *type + 3; + *type = opt_vfstype; + } else if (opt_vfstype) { + if (*type) + error(_("mount: type specified twice")); + else + *type = opt_vfstype; + } + + *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_sizelimit || opt_encryption); + *loopfile = *spec; + + /* Automatically create a loop device from a regular file if a filesystem + * is not specified or the filesystem is known for libblkid (these + * filesystems work with block devices only). + * + * Note that there is not a restriction (on kernel side) that prevents regular + * file as a mount(2) source argument. A filesystem that is able to mount + * regular files could be implemented. + */ + if (!*loop && !(*flags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) && + (!*type || strcmp(*type, "auto") == 0 || fsprobe_known_fstype(*type))) { + + struct stat st; + if (stat(*loopfile, &st) == 0) + *loop = S_ISREG(st.st_mode); + } + + if (*loop) { + *flags |= MS_LOOP; + if (fake) { + if (verbose) + printf(_("mount: skipping the setup of a loop device\n")); + } else { + int loop_opts = 0; + + /* since 2.6.37 we don't have to store backing filename to mtab + * because kernel provides the name in /sys + */ + if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) || + mtab_is_writable() == 0) { + + if (verbose) + printf(_("mount: enabling autoclear loopdev flag\n")); + loop_opts = LO_FLAGS_AUTOCLEAR; + } + + if (*flags & MS_RDONLY) + loop_opts |= LO_FLAGS_READ_ONLY; + + if (opt_offset && parse_offset(&opt_offset, &offset)) { + error(_("mount: invalid offset '%s' specified"), opt_offset); + return EX_FAIL; + } + if (opt_sizelimit && parse_offset(&opt_sizelimit, &sizelimit)) { + error(_("mount: invalid sizelimit '%s' specified"), opt_sizelimit); + return EX_FAIL; + } + + if (is_mounted_same_loopfile(node, *loopfile, offset)) { + error(_("mount: according to mtab %s is already mounted on %s as loop"), *loopfile, node); + return EX_FAIL; + } + + if (opt_encryption) { +#ifdef MCL_FUTURE + if (mlockall(MCL_CURRENT | MCL_FUTURE)) { + error(_("mount: couldn't lock into memory")); + return EX_FAIL; + } +#endif + pwd = xgetpass(pfd, _("Password: ")); + } + + loopcxt_init(&lc, 0); + /*loopcxt_enable_debug(&lc, 1);*/ + + if (*loopdev && **loopdev) + loopcxt_set_device(&lc, *loopdev); /* use loop=<devname> */ + + do { + int rc; + + if ((!*loopdev || !**loopdev) && loopcxt_find_unused(&lc) == 0) + *loopdev = loopcxt_strdup_device(&lc); + + if (!*loopdev) { + error(_("mount: failed to found free loop device")); + loopcxt_deinit(&lc); + goto err; /* no more loop devices */ + } + if (verbose) + printf(_("mount: going to use the loop device %s\n"), *loopdev); + + rc = loopcxt_set_backing_file(&lc, *loopfile); + + if (!rc && offset) + rc = loopcxt_set_offset(&lc, offset); + if (!rc && sizelimit) + rc = loopcxt_set_sizelimit(&lc, sizelimit); + if (!rc && opt_encryption && pwd) + loopcxt_set_encryption(&lc, opt_encryption, pwd); + if (!rc) + loopcxt_set_flags(&lc, loop_opts); + + if (rc) { + error(_("mount: %s: failed to set loopdev attributes"), *loopdev); + loopcxt_deinit(&lc); + goto err; + } + + /* setup the device */ + rc = loopcxt_setup_device(&lc); + if (!rc) + break; /* success */ + + if (rc != -EBUSY) { + error(_("mount: %s: failed setting up loop device: %m"), *loopfile); + if (!opt_loopdev) { + my_free(*loopdev); + *loopdev = NULL; + } + loopcxt_deinit(&lc); + goto err; + } + + if (!opt_loopdev) { + if (verbose) + printf(_("mount: stolen loop=%s ...trying again\n"), *loopdev); + my_free(*loopdev); + *loopdev = NULL; + continue; + } + error(_("mount: stolen loop=%s"), *loopdev); + loopcxt_deinit(&lc); + goto err; + + } while (!*loopdev); + + if (verbose > 1) + printf(_("mount: setup loop device successfully\n")); + *spec = *loopdev; + + if (loopcxt_is_readonly(&lc)) + *flags |= MS_RDONLY; + + if (loopcxt_is_autoclear(&lc)) + /* Prevent recording loop dev in mtab for cleanup on umount */ + *loop = 0; + + /* We have to keep the device open until mount(2), otherwise it will + * be auto-cleared by kernel (because LO_FLAGS_AUTOCLEAR) */ + loopcxt_set_fd(&lc, -1, 0); + loopcxt_deinit(&lc); + } + } + + ret = 0; +err: + if (pwd) { + char *p = pwd; + while (*p) + *p++ = '\0'; + free(pwd); + } + return ret; +} + + +#ifdef HAVE_LIBMOUNT_MOUNT +static void +verbose_mount_info(const char *spec, const char *node, const char *type, + const char *opts) +{ + struct my_mntent mnt; + + mnt.mnt_fsname = is_pseudo_fs(type) ? spec : canonicalize(spec); + mnt.mnt_dir = canonicalize (node); + mnt.mnt_type = type; + mnt.mnt_opts = opts; + + print_one (&mnt); + + if (spec != mnt.mnt_fsname) + my_free(mnt.mnt_fsname); + my_free(mnt.mnt_dir); +} + +static void +prepare_mtab_entry(const char *spec, const char *node, const char *type, + const char *opts, unsigned long flags) +{ + struct libmnt_fs *fs = mnt_new_fs(); + int rc = -1; + + if (!mtab_update) + mtab_update = mnt_new_update(); + + if (mtab_update && fs) { + const char *cn_spec = is_pseudo_fs(type) ? spec : canonicalize(spec); + const char *cn_node = canonicalize(node); + + mnt_fs_set_source(fs, cn_spec); + mnt_fs_set_target(fs, cn_node); + mnt_fs_set_fstype(fs, type); + mnt_fs_set_options(fs, opts); + + rc = mnt_update_set_fs(mtab_update, flags, NULL, fs); + + if (spec != cn_spec) + my_free(cn_spec); + my_free(cn_node); + } + + mnt_free_fs(fs); + + if (rc) { + mnt_free_update(mtab_update); + mtab_update = NULL; + } +} + +static void update_mtab_entry(int flags) +{ + unsigned long fl; + + if (!mtab_update) + return; + + fl = mnt_update_get_mflags(mtab_update); + + if ((flags & MS_RDONLY) != (fl & MS_RDONLY)) + mnt_update_force_rdonly(mtab_update, flags & MS_RDONLY); + + if (!nomtab) { + if (mtab_does_not_exist()) { + if (verbose > 1) + printf(_("mount: no %s found - creating it..\n"), + _PATH_MOUNTED); + create_mtab (); + } + + mnt_update_table(mtab_update, NULL); + } + + mnt_free_update(mtab_update); + mtab_update = NULL; +} + +#else /*!HAVE_LIBMOUNT_MOUNT */ +static void +update_mtab_entry(const char *spec, const char *node, const char *type, + const char *opts, int flags, int freq, int pass) { + struct my_mntent mnt; + + mnt.mnt_fsname = is_pseudo_fs(type) ? xstrdup(spec) : canonicalize(spec); + mnt.mnt_dir = canonicalize (node); + mnt.mnt_type = type; + mnt.mnt_opts = opts; + mnt.mnt_freq = freq; + mnt.mnt_passno = pass; + + /* We get chatty now rather than after the update to mtab since the + mount succeeded, even if the write to /etc/mtab should fail. */ + if (verbose) + print_one (&mnt); + + if (!nomtab && mtab_does_not_exist()) { + if (verbose > 1) + printf(_("mount: no %s found - creating it..\n"), + _PATH_MOUNTED); + create_mtab (); + + } + + if (!nomtab && mtab_is_writable()) { + if (flags & MS_REMOUNT) + update_mtab (mnt.mnt_dir, &mnt); + else if (flags & MS_MOVE) + update_mtab(mnt.mnt_fsname, &mnt); + else + update_mtab(NULL, &mnt); + } + my_free(mnt.mnt_fsname); + my_free(mnt.mnt_dir); +} +#endif /* !HAVE_LIBMOUNT_MOUNT */ + +static void +set_pfd(char *s) { + if (!isdigit(*s)) + die(EX_USAGE, + _("mount: argument to -p or --pass-fd must be a number")); + pfd = atoi(optarg); +} + +static void +cdrom_setspeed(const char *spec) { +#define CDROM_SELECT_SPEED 0x5322 /* Set the CD-ROM speed */ + if (opt_speed) { + int cdrom; + int speed = atoi(opt_speed); + + if ((cdrom = open(spec, O_RDONLY | O_NONBLOCK)) < 0) + die(EX_FAIL, + _("mount: cannot open %s for setting speed"), + spec); + if (ioctl(cdrom, CDROM_SELECT_SPEED, speed) < 0) + die(EX_FAIL, _("mount: cannot set speed: %m")); + close(cdrom); + } +} + +/* + * Check if @path is on read-only filesystem independently on file permissions. + */ +static int +is_readonly(const char *path) +{ + if (access(path, W_OK) == 0) + return 0; + if (errno == EROFS) + return 1; + if (errno != EACCES) + return 0; + +#ifdef HAVE_FUTIMENS + /* + * access(2) returns EACCES on read-only FS: + * + * - for set-uid application if one component of the path is not + * accessible for the current rUID. (Note that euidaccess(2) does not + * check for EROFS at all). + * + * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind) + */ + { + struct timespec times[2]; + + times[0].tv_nsec = UTIME_NOW; /* atime */ + times[1].tv_nsec = UTIME_OMIT; /* mtime */ + + if (utimensat(AT_FDCWD, path, times, 0) == -1) + return errno == EROFS; + } +#endif + return 0; +} + +/* + * try_mount_one() + * Try to mount one file system. + * + * returns: 0: OK, EX_SYSERR, EX_FAIL, return code from nfsmount, + * return status from wait + */ +static int +try_mount_one (const char *spec0, const char *node0, const char *types0, + const char *opts0, int freq, int pass, int ro) { + int res = 0, status = 0, special = 0; + int mnt5_res = 0; /* only for gcc */ + int mnt_err; + int flags; + char *extra_opts; /* written in mtab */ + char *mount_opts; /* actually used on system call */ + const char *opts, *spec, *node, *types; + char *user = 0; + int loop = 0; + const char *loopdev = 0, *loopfile = 0; + struct stat statbuf; + + /* copies for freeing on exit */ + const char *opts1, *spec1, *node1, *types1; + + if (verbose > 2) { + printf("mount: spec: \"%s\"\n", spec0); + printf("mount: node: \"%s\"\n", node0); + printf("mount: types: \"%s\"\n", types0); + printf("mount: opts: \"%s\"\n", opts0); + } + + spec = spec1 = xstrdup(spec0); + node = node1 = xstrdup(node0); + types = types1 = xstrdup(types0); + opts = opts1 = xstrdup(opts0); + + parse_opts (opts, &flags, &extra_opts); + mount_opts = xstrdup(extra_opts); + + /* quietly succeed for fstab entries that don't get mounted automatically */ + if (mount_all && (flags & MS_NOAUTO)) + goto out; + + restricted_check(spec, node, &flags, &user); + + /* The "mount -f" checks for for existing record in /etc/mtab (with + * regular non-fake mount this is usually done by kernel) + */ + if (!(flags & MS_REMOUNT) && fake && mounted (spec, node, NULL)) + die(EX_USAGE, _("mount: according to mtab, " + "%s is already mounted on %s\n"), + spec, node); + + if (opt_speed) + cdrom_setspeed(spec); + + if (!(flags & MS_REMOUNT)) { + /* + * Don't set up a (new) loop device if we only remount - this left + * stale assignments of files to loop devices. Nasty when used for + * encryption. + */ + res = loop_check(&spec, &types, &flags, &loop, &loopdev, &loopfile, node); + if (res) + goto out; + } + + if (loop) + opt_loopdev = loopdev; + + if (flags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) + types = "none"; + +#ifdef HAVE_LIBSELINUX + if (flags & MS_REMOUNT) { + /* + * Linux kernel does not accept any selinux context option on remount + */ + if (mount_opts) { + char *tmp = mount_opts; + mount_opts = remove_context_options(mount_opts); + my_free(tmp); + } + + } else if (types && strcmp(types, "tmpfs") == 0 && is_selinux_enabled() > 0 && + !has_context_option(mount_opts)) { + /* + * Add rootcontext= mount option for tmpfs + * https://bugzilla.redhat.com/show_bug.cgi?id=476964 + */ + security_context_t sc = NULL; + + if (getfilecon(node, &sc) > 0 && strcmp("unlabeled", sc)) + append_context("rootcontext=", (char *) sc, &mount_opts); + freecon(sc); + } +#endif + + /* + * Call mount.TYPE for types that require a separate mount program. + * For the moment these types are ncpfs and smbfs. Maybe also vxfs. + * All such special things must occur isolated in the types string. + */ + if (check_special_mountprog(spec, node, types, flags, mount_opts, &status)) { + res = status; + goto out; + } + +#ifdef HAVE_LIBMOUNT_MOUNT + mtab_opts = fix_opts_string(flags & ~MS_NOMTAB, extra_opts, user, 0); + mtab_flags = flags; + + if (fake) + prepare_mtab_entry(spec, node, types, mtab_opts, mtab_flags); +#endif + + block_signals (SIG_BLOCK); + + if (!fake) { + mnt5_res = guess_fstype_and_mount (spec, node, &types, flags & ~MS_NOSYS, + mount_opts, &special, &status); + + if (special) { + block_signals (SIG_UNBLOCK); + res = status; + goto out; + } + } + + /* Kernel allows to use MS_RDONLY for bind mounts, but the read-only request + * could be silently ignored. Check it to avoid 'ro' in mtab and 'rw' in + * /proc/mounts. + */ + if (!fake && mnt5_res == 0 && + (flags & MS_BIND) && (flags & MS_RDONLY) && !is_readonly(node)) { + + printf(_("mount: warning: %s seems to be mounted read-write.\n"), node); + flags &= ~MS_RDONLY; + } + + /* Kernel can silently add MS_RDONLY flag when mounting file system that + * does not have write support. Check this to avoid 'ro' in /proc/mounts + * and 'rw' in mtab. + */ + if (!fake && mnt5_res == 0 && + !(flags & (MS_RDONLY | MS_PROPAGATION | MS_MOVE)) && + is_readonly(node)) { + + printf(_("mount: warning: %s seems to be mounted read-only.\n"), node); + flags |= MS_RDONLY; + } + + if (fake || mnt5_res == 0) { + char *mo = fix_opts_string (flags & ~MS_NOMTAB, extra_opts, user, 0); + const char *tp = types ? types : "unknown"; + + /* Mount succeeded, report this (if verbose) and write mtab entry. */ +#ifdef HAVE_LIBMOUNT_MOUNT + update_mtab_entry(flags); + if (verbose) + verbose_mount_info(loop ? loopfile : spec, node, tp, mo); +#else + if (!(mounttype & MS_PROPAGATION)) + update_mtab_entry(loop ? loopfile : spec, + node, + tp, + mo, + flags, + freq, + pass); +#endif + block_signals (SIG_UNBLOCK); + free(mo); + + res = 0; + goto out; + } + + mnt_err = errno; + + if (loop) + loopdev_delete(spec); + + block_signals (SIG_UNBLOCK); + + /* Mount failed, complain, but don't die. */ + + if (types == 0) { + if (restricted) + error (_("mount: I could not determine the filesystem type, " + "and none was specified")); + else + error (_("mount: you must specify the filesystem type")); + } else if (mnt5_res != -1) { + /* should not happen */ + error (_("mount: mount failed")); + } else { + switch (mnt_err) { + case EPERM: + if (geteuid() == 0) { + if (stat (node, &statbuf) || !S_ISDIR(statbuf.st_mode)) + error (_("mount: mount point %s is not a directory"), node); + else + error (_("mount: permission denied")); + } else + error (_("mount: must be superuser to use mount")); + break; + case EBUSY: + if (flags & MS_REMOUNT) { + error (_("mount: %s is busy"), node); + } else if (!strcmp(types, "proc") && !strcmp(node, "/proc")) { + /* heuristic: if /proc/version exists, then probably proc is mounted */ + if (stat ("/proc/version", &statbuf)) /* proc mounted? */ + error (_("mount: %s is busy"), node); /* no */ + else if (!mount_all || verbose) /* yes, don't mention it */ + error (_("mount: proc already mounted")); + } else { + error (_("mount: %s already mounted or %s busy"), spec, node); + already (spec, node); + } + break; + case ENOENT: + if (lstat (node, &statbuf)) + error (_("mount: mount point %s does not exist"), node); + else if (stat (node, &statbuf)) + error (_("mount: mount point %s is a symbolic link to nowhere"), + node); + else if (stat (spec, &statbuf)) { + if (opt_nofail) + goto out; + error (_("mount: special device %s does not exist"), spec); + } else { + errno = mnt_err; + perror("mount"); + } + break; + case ENOTDIR: + if (stat (node, &statbuf) || ! S_ISDIR(statbuf.st_mode)) + error (_("mount: mount point %s is not a directory"), node); + else if (stat (spec, &statbuf) && errno == ENOTDIR) { + if (opt_nofail) + goto out; + error (_("mount: special device %s does not exist\n" + " (a path prefix is not a directory)\n"), spec); + } else { + errno = mnt_err; + perror("mount"); + } + break; + case EINVAL: + { int fd; + unsigned long long size = 0; + + if (flags & MS_REMOUNT) { + error (_("mount: %s not mounted or bad option"), node); + } else { + error (_("mount: wrong fs type, bad option, bad superblock on %s,\n" + " missing codepage or helper program, or other error"), + spec); + + if (stat(spec, &statbuf) < 0) { + if (errno == ENOENT) /* network FS? */ + error(_( + " (for several filesystems (e.g. nfs, cifs) you might\n" + " need a /sbin/mount.<type> helper program)")); + + } else if (S_ISBLK(statbuf.st_mode) + && (fd = open(spec, O_RDONLY | O_NONBLOCK)) >= 0) { + + if (blkdev_get_size(fd, &size) == 0) { + if (size == 0 && !loop) + error(_( + " (could this be the IDE device where you in fact use\n" + " ide-scsi so that sr0 or sda or so is needed?)")); + + if (size && size <= 2) + error(_( + " (aren't you trying to mount an extended partition,\n" + " instead of some logical partition inside?)")); + + close(fd); + } + } + error(_( + " In some cases useful info is found in syslog - try\n" + " dmesg | tail or so\n")); + } + break; + } + case EMFILE: + error (_("mount table full")); break; + case EIO: + error (_("mount: %s: can't read superblock"), spec); break; + case ENODEV: + { + int pfs = known_fstype_in_procfs(types); + + if (pfs == 1 || !strcmp(types, "guess")) + error(_("mount: %s: unknown device"), spec); + else if (pfs == 0) { + char *lowtype, *p; + int u; + + error (_("mount: unknown filesystem type '%s'"), types); + + /* maybe this loser asked for FAT or ISO9660 or isofs */ + lowtype = xstrdup(types); + u = 0; + for(p=lowtype; *p; p++) { + if(tolower(*p) != *p) { + *p = tolower(*p); + u++; + } + } + if (u && known_fstype_in_procfs(lowtype) == 1) + error (_("mount: probably you meant %s"), lowtype); + else if (!strncmp(lowtype, "iso", 3) && + known_fstype_in_procfs("iso9660") == 1) + error (_("mount: maybe you meant 'iso9660'?")); + else if (!strncmp(lowtype, "fat", 3) && + known_fstype_in_procfs("vfat") == 1) + error (_("mount: maybe you meant 'vfat'?")); + free(lowtype); + } else + error (_("mount: %s has wrong device number or fs type %s not supported"), + spec, types); + break; + } + case ENOTBLK: + if (opt_nofail) + goto out; + if (stat (spec, &statbuf)) /* strange ... */ + error (_("mount: %s is not a block device, and stat fails?"), spec); + else if (S_ISBLK(statbuf.st_mode)) + error (_("mount: the kernel does not recognize %s as a block device\n" + " (maybe `modprobe driver'?)"), spec); + else if (S_ISREG(statbuf.st_mode)) + error (_("mount: %s is not a block device (maybe try `-o loop'?)"), + spec); + else + error (_("mount: %s is not a block device"), spec); + break; + case ENXIO: + if (opt_nofail) + goto out; + error (_("mount: %s is not a valid block device"), spec); break; + case EACCES: /* pre-linux 1.1.38, 1.1.41 and later */ + case EROFS: /* linux 1.1.38 and later */ + { char *bd = (loop ? "" : _("block device ")); + if (ro || (flags & MS_RDONLY)) { + error (_("mount: cannot mount %s%s read-only"), + bd, spec); + break; + } else if (readwrite) { + error (_("mount: %s%s is write-protected but explicit `-w' flag given"), + bd, spec); + break; + } else if (flags & MS_REMOUNT) { + error (_("mount: cannot remount %s%s read-write, is write-protected"), + bd, spec); + break; + } else { + opts = opts0; + types = types0; + + if (opts) { + char *opts2 = append_opt(xstrdup(opts), "ro", NULL); + my_free(opts1); + opts = opts1 = opts2; + } else + opts = "ro"; + if (types && !strcmp(types, "guess")) + types = 0; + error (_("mount: %s%s is write-protected, mounting read-only"), + bd, spec0); + res = try_mount_one (spec0, node0, types, opts, freq, pass, 1); + goto out; + } + break; + } + case ENOMEDIUM: + error(_("mount: no medium found on %s"), spec); + break; + default: + error ("mount: %s", strerror (mnt_err)); break; + } + } + res = EX_FAIL; + + out: + +#if defined(HAVE_LIBSELINUX) && defined(HAVE_SECURITY_GET_INITIAL_CONTEXT) + if (res != EX_FAIL && verbose && is_selinux_enabled() > 0) { + security_context_t raw = NULL, def = NULL; + + if (getfilecon(node, &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"), + node); + } + freecon(raw); + freecon(def); + } +#endif + + my_free(mount_opts); + my_free(extra_opts); + my_free(spec1); + my_free(node1); + my_free(opts1); + my_free(types1); + + return res; +} + +static char * +subst_string(const char *s, const char *sub, int sublen, const char *repl) { + char *n; + + n = (char *) xmalloc(strlen(s)-sublen+strlen(repl)+1); + strncpy (n, s, sub-s); + strcpy (n + (sub-s), repl); + strcat (n, sub+sublen); + return n; +} + +static char * +usersubst(const char *opts) { + char *s, *w; + char id[40]; + + if (!opts) + return NULL; + + s = "uid=useruid"; + if (opts && (w = strstr(opts, s)) != NULL) { + sprintf(id, "uid=%u", getuid()); + opts = subst_string(opts, w, strlen(s), id); + } + s = "gid=usergid"; + if (opts && (w = strstr(opts, s)) != NULL) { + sprintf(id, "gid=%u", getgid()); + opts = subst_string(opts, w, strlen(s), id); + } + return xstrdup(opts); +} + +static int +is_existing_file (const char *s) { + struct stat statbuf; + + return (stat(s, &statbuf) == 0); +} + +/* + * Return 0 for success (either mounted sth or -a and NOAUTO was given) + */ +static int +mount_one (const char *spec, const char *node, const char *types, + const char *fstabopts, char *cmdlineopts, int freq, int pass) { + const char *nspec = NULL; + char *opts; + + /* Substitute values in opts, if required */ + opts = usersubst(fstabopts); + + /* Merge the fstab and command line options. */ + opts = append_opt(opts, cmdlineopts, NULL); + + if (types == NULL && !mounttype && !is_existing_file(spec)) { + if (strchr (spec, ':') != NULL) { + types = "nfs"; + if (verbose) + printf(_("mount: no type was given - " + "I'll assume nfs because of " + "the colon\n")); + } else if(!strncmp(spec, "//", 2)) { + types = "cifs"; + if (verbose) + printf(_("mount: no type was given - " + "I'll assume cifs because of " + "the // prefix\n")); + } + } + + /* Handle possible LABEL= and UUID= forms of spec */ + if (types == NULL || (strncmp(types, "9p", 2) && + strncmp(types, "nfs", 3) && + strncmp(types, "cifs", 4) && + strncmp(types, "smbfs", 5))) { + if (!is_pseudo_fs(types)) + nspec = spec_to_devname(spec); + if (nspec) + spec = nspec; + } + + return try_mount_one (spec, node, types, opts, freq, pass, 0); +} + +#ifdef HAVE_LIBMOUNT_MOUNT +static struct libmnt_table *minfo; /* parsed mountinfo file */ + +/* Check if an fsname/dir pair was already in the old mtab. */ +static int +mounted (const char *spec0, const char *node0, struct mntentchn *fstab_mc) { +#else +static int +mounted (const char *spec0, const char *node0, + struct mntentchn *fstab_mc __attribute__((__unused__))) { +#endif + struct mntentchn *mc, *mc0; + const char *spec, *node; + int ret = 0; + +#ifdef HAVE_LIBMOUNT_MOUNT + /* + * Use libmount to check for already mounted bind mounts on systems + * without mtab. + */ + if (fstab_mc && fstab_mc->m.mnt_opts && + mtab_is_a_symlink() && strstr(fstab_mc->m.mnt_opts, "bind")) { + + struct libmnt_fs *fs = mnt_new_fs(); + int rc = fs ? 0 : -1; + + if (!rc) + rc = mnt_fs_set_fstype(fs, fstab_mc->m.mnt_type); + if (!rc) + rc = mnt_fs_set_source(fs, fstab_mc->m.mnt_fsname); + if (!rc) + rc = mnt_fs_set_target(fs, fstab_mc->m.mnt_dir); + if (!rc) + rc = mnt_fs_set_options(fs, fstab_mc->m.mnt_opts); + if (!rc && !minfo) + minfo = mnt_new_table_from_file("/proc/self/mountinfo"); + if (!rc && minfo) + rc = mnt_table_is_fs_mounted(minfo, fs); + + mnt_free_fs(fs); + if (rc == 1) + return 1; + } +#endif + /* Handle possible UUID= and LABEL= in spec */ + spec = spec_to_devname(spec0); + if (!spec) + return ret; + + node = canonicalize(node0); + + + mc0 = mtab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if (streq (spec, mc->m.mnt_fsname) && + streq (node, mc->m.mnt_dir)) { + ret = 1; + break; + } + + my_free(spec); + my_free(node); + + return ret; +} + +/* returns 0 if not mounted, 1 if mounted and -1 in case of error */ +static int +is_fstab_entry_mounted(struct mntentchn *mc, int verbose) +{ + struct stat st; + + if (mounted(mc->m.mnt_fsname, mc->m.mnt_dir, mc)) + goto yes; + + /* extra care for loop devices */ + if ((mc->m.mnt_opts && strstr(mc->m.mnt_opts, "loop=")) || + (stat(mc->m.mnt_fsname, &st) == 0 && S_ISREG(st.st_mode))) { + + char *p = get_option_value(mc->m.mnt_opts, "offset="); + uintmax_t offset = 0; + + if (p && strtosize(p, &offset) != 0) { + if (verbose) + printf(_("mount: ignore %s " + "(unparsable offset= option)\n"), + mc->m.mnt_fsname); + return -1; + } + free(p); + if (is_mounted_same_loopfile(mc->m.mnt_dir, mc->m.mnt_fsname, offset)) + goto yes; + } + + return 0; +yes: + if (verbose) + printf(_("mount: %s already mounted on %s\n"), + mc->m.mnt_fsname, mc->m.mnt_dir); + return 1; +} + +/* avoid using stat() on things we are not going to mount anyway.. */ +static int +has_noauto (const char *opts) { + char *s; + + if (!opts) + return 0; + s = strstr(opts, "noauto"); + if (!s) + return 0; + return (s == opts || s[-1] == ',') && (s[6] == 0 || s[6] == ','); +} + +/* Mount all filesystems of the specified types except swap and root. */ +/* With the --fork option: fork and let different incarnations of + mount handle different filesystems. However, try to avoid several + simultaneous mounts on the same physical disk, since that is very slow. */ +#define DISKMAJOR(m) (((int) m) & ~0xf) + +static int +do_mount_all (char *types, char *options, char *test_opts) { + struct mntentchn *mc, *mc0, *mtmp; + int status = 0; + struct stat statbuf; + struct child { + pid_t pid; + char *group; + struct mntentchn *mec; + struct mntentchn *meclast; + struct child *nxt; + } childhead, *childtail, *cp; + char major[22]; + char *g, *colon; + + /* build a chain of what we have to do, or maybe + several chains, one for each major or NFS host */ + childhead.nxt = 0; + childtail = &childhead; + mc0 = fstab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + if (has_noauto (mc->m.mnt_opts)) + continue; + if (matching_type (mc->m.mnt_type, types) + && matching_opts (mc->m.mnt_opts, test_opts) + && !streq (mc->m.mnt_dir, "/") + && !streq (mc->m.mnt_dir, "root") + && !is_fstab_entry_mounted(mc, verbose)) { + + mtmp = (struct mntentchn *) xmalloc(sizeof(*mtmp)); + *mtmp = *mc; + mtmp->nxt = 0; + g = NULL; + if (optfork) { + if (stat(mc->m.mnt_fsname, &statbuf) == 0 && + S_ISBLK(statbuf.st_mode)) { + sprintf(major, "#%x", + DISKMAJOR(statbuf.st_rdev)); + g = major; + } + if (strcmp(mc->m.mnt_type, "nfs") == 0) { + g = xstrdup(mc->m.mnt_fsname); + colon = strchr(g, ':'); + if (colon) + *colon = '\0'; + } + } + if (g) { + for (cp = childhead.nxt; cp; cp = cp->nxt) + if (cp->group && + strcmp(cp->group, g) == 0) { + cp->meclast->nxt = mtmp; + cp->meclast = mtmp; + goto fnd; + } + } + cp = (struct child *) xmalloc(sizeof *cp); + cp->nxt = 0; + cp->mec = cp->meclast = mtmp; + cp->group = xstrdup(g); + cp->pid = 0; + childtail->nxt = cp; + childtail = cp; + fnd:; + + } + } + + /* now do everything */ + for (cp = childhead.nxt; cp; cp = cp->nxt) { + pid_t p = -1; + if (optfork) { + p = fork(); + if (p == -1) { + int errsv = errno; + error(_("mount: cannot fork: %s"), + strerror (errsv)); + } + else if (p != 0) + cp->pid = p; + } + + /* if child, or not forked, do the mounting */ + if (p == 0 || p == -1) { + for (mc = cp->mec; mc; mc = mc->nxt) { + status |= mount_one (mc->m.mnt_fsname, + mc->m.mnt_dir, + mc->m.mnt_type, + mc->m.mnt_opts, + options, 0, 0); + } + if (mountcount) + status |= EX_SOMEOK; + if (p == 0) + exit(status); + } + } + + /* wait for children, if any */ + while ((cp = childhead.nxt) != NULL) { + childhead.nxt = cp->nxt; + if (cp->pid) { + int ret; + keep_waiting: + if(waitpid(cp->pid, &ret, 0) == -1) { + if (errno == EINTR) + goto keep_waiting; + perror("waitpid"); + } else if (WIFEXITED(ret)) + status |= WEXITSTATUS(ret); + else + status |= EX_SYSERR; + } + } + if (mountcount) + status |= EX_SOMEOK; + return status; +} + +static struct option longopts[] = { + { "all", 0, 0, 'a' }, + { "fake", 0, 0, 'f' }, + { "fork", 0, 0, 'F' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "read-only", 0, 0, 'r' }, + { "ro", 0, 0, 'r' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-write", 0, 0, 'w' }, + { "rw", 0, 0, 'w' }, + { "options", 1, 0, 'o' }, + { "test-opts", 1, 0, 'O' }, + { "pass-fd", 1, 0, 'p' }, + { "types", 1, 0, 't' }, + { "bind", 0, 0, 'B' }, + { "move", 0, 0, 'M' }, + { "guess-fstype", 1, 0, 134 }, + { "rbind", 0, 0, 'R' }, + { "make-shared", 0, 0, 136 }, + { "make-slave", 0, 0, 137 }, + { "make-private", 0, 0, 138 }, + { "make-unbindable", 0, 0, 139 }, + { "make-rshared", 0, 0, 140 }, + { "make-rslave", 0, 0, 141 }, + { "make-rprivate", 0, 0, 142 }, + { "make-runbindable", 0, 0, 143 }, + { "no-canonicalize", 0, 0, 144 }, + { "internal-only", 0, 0, 'i' }, + { NULL, 0, 0, 0 } +}; + +/* Keep the usage message at max 22 lines, each at most 70 chars long. + The user should not need a pager to read it. */ +static void +usage (FILE *fp, int n) { + fprintf(fp, _( + "Usage: mount -V : print version\n" + " mount -h : print this help\n" + " mount : list mounted filesystems\n" + " mount -l : idem, including volume labels\n" + "So far the informational part. Next the mounting.\n" + "The command is `mount [-t fstype] something somewhere'.\n" + "Details found in /etc/fstab may be omitted.\n" + " mount -a [-t|-O] ... : mount all stuff from /etc/fstab\n" + " mount device : mount device at the known place\n" + " mount directory : mount known device here\n" + " mount -t type dev dir : ordinary mount command\n" + "Note that one does not really mount a device, one mounts\n" + "a filesystem (of the given type) found on the device.\n" + "One can also mount an already visible directory tree elsewhere:\n" + " mount --bind olddir newdir\n" + "or move a subtree:\n" + " mount --move olddir newdir\n" + "One can change the type of mount containing the directory dir:\n" + " mount --make-shared dir\n" + " mount --make-slave dir\n" + " mount --make-private dir\n" + " mount --make-unbindable dir\n" + "One can change the type of all the mounts in a mount subtree\n" + "containing the directory dir:\n" + " mount --make-rshared dir\n" + " mount --make-rslave dir\n" + " mount --make-rprivate dir\n" + " mount --make-runbindable dir\n" + "A device can be given by name, say /dev/hda1 or /dev/cdrom,\n" + "or by label, using -L label or by uuid, using -U uuid .\n" + "Other options: [-nfFrsvw] [-o options] [-p passwdfd].\n" + "For many more details, say man 8 mount .\n" + )); + + unlock_mtab(); + exit (n); +} + +/* returns mount entry from fstab */ +static struct mntentchn * +getfs(const char *spec, const char *uuid, const char *label) +{ + struct mntentchn *mc = NULL; + const char *devname = NULL; + + if (!spec && !uuid && !label) + return NULL; + + /* + * A) 99% of all cases, the spec on cmdline matches + * with spec in fstab + */ + if (uuid) + mc = getfs_by_uuid(uuid); + else if (label) + mc = getfs_by_label(label); + else { + mc = getfs_by_dir(spec); + + if (!mc) + mc = getfs_by_spec(spec); + } + if (mc) + return mc; + + /* + * B) UUID or LABEL on cmdline, but devname in fstab + */ + if (uuid) + devname = fsprobe_get_devname_by_uuid(uuid); + else if (label) + devname = fsprobe_get_devname_by_label(label); + else + devname = spec_to_devname(spec); + + if (devname) + mc = getfs_by_devname(devname); + + /* + * C) mixed + */ + if (!mc && devname) { + const char *id = NULL; + + if (!label && (!spec || strncmp(spec, "LABEL=", 6))) { + id = fsprobe_get_label_by_devname(devname); + if (id) + mc = getfs_by_label(id); + } + if (!mc && !uuid && (!spec || strncmp(spec, "UUID=", 5))) { + id = fsprobe_get_uuid_by_devname(devname); + if (id) + mc = getfs_by_uuid(id); + } + my_free(id); + + if (mc) { + /* use real device name to avoid repetitional + * conversion from LABEL/UUID to devname + */ + my_free(mc->m.mnt_fsname); + mc->m.mnt_fsname = xstrdup(devname); + } + } + + /* + * D) remount -- try /etc/mtab + * Earlier mtab was tried first, but this would sometimes try the + * wrong mount in case mtab had the root device entry wrong. Try + * the last occurrence first, since that is the visible mount. + */ + if (!mc && (devname || spec)) + mc = getmntfilebackward (devname ? devname : spec, NULL); + + my_free(devname); + return mc; +} + + +static void +print_version(int rc) { + printf( "mount from %s (with " +#ifdef HAVE_LIBBLKID + "libblkid" +#else + "libvolume_id" +#endif +#ifdef HAVE_LIBSELINUX + " and selinux" +#endif + " support)\n", PACKAGE_STRING); + exit(rc); +} + +int +main(int argc, char *argv[]) { + int c, result = 0, specseen; + char *options = NULL, *test_opts = NULL, *node; + const char *spec = NULL; + char *label = NULL; + char *uuid = NULL; + char *types = NULL; + char *p; + struct mntentchn *mc; + int fd; + + sanitize_env(); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + progname = argv[0]; + if ((p = strrchr(progname, '/')) != NULL) + progname = p+1; + + umask(022); + + /* People report that a mount called from init without console + writes error messages to /etc/mtab + Let us try to avoid getting fd's 0,1,2 */ + while((fd = open("/dev/null", O_RDWR)) == 0 || fd == 1 || fd == 2) ; + if (fd > 2) + close(fd); + + fsprobe_init(); + +#ifdef DO_PS_FIDDLING + initproctitle(argc, argv); +#endif + + while ((c = getopt_long (argc, argv, "aBfFhilL:Mno:O:p:rRsU:vVwt:", + longopts, NULL)) != -1) { + switch (c) { + case 'a': /* mount everything in fstab */ + ++mount_all; + break; + case 'B': /* bind */ + mounttype = MS_BIND; + break; + case 'f': /* fake: don't actually call mount(2) */ + ++fake; + break; + case 'F': + ++optfork; + break; + case 'h': /* help */ + usage (stdout, 0); + break; + case 'i': + external_allowed = 0; + break; + case 'l': + list_with_volumelabel = 1; + break; + case 'L': + label = optarg; + break; + case 'M': /* move */ + mounttype = MS_MOVE; + break; + case 'n': /* do not write /etc/mtab */ + ++nomtab; + break; + case 'o': /* specify mount options */ + options = append_opt(options, optarg, NULL); + break; + case 'O': /* with -t: mount only if (not) opt */ + test_opts = append_opt(test_opts, optarg, NULL); + break; + case 'p': /* fd on which to read passwd */ + set_pfd(optarg); + break; + case 'r': /* mount readonly */ + readonly = 1; + readwrite = 0; + break; + case 'R': /* rbind */ + mounttype = (MS_BIND | MS_REC); + break; + case 's': /* allow sloppy mount options */ + sloppy = 1; + break; + case 't': /* specify file system types */ + types = optarg; + break; + case 'U': + uuid = optarg; + break; + case 'v': /* be chatty - more so if repeated */ + ++verbose; + break; + case 'V': /* version */ + print_version(EXIT_SUCCESS); + break; + case 'w': /* mount read/write */ + readwrite = 1; + readonly = 0; + break; + case 0: + break; + + case 134: + /* undocumented, may go away again: + call: mount --guess-fstype device + use only for testing purposes - + the guessing is not reliable at all */ + { + const char *fstype; + fstype = fsprobe_get_fstype_by_devname(optarg); + printf("%s\n", fstype ? fstype : "unknown"); + exit(fstype ? 0 : EX_FAIL); + } + + case 136: + mounttype = MS_SHARED; + break; + + case 137: + mounttype = MS_SLAVE; + break; + + case 138: + mounttype = MS_PRIVATE; + break; + + case 139: + mounttype = MS_UNBINDABLE; + break; + + case 140: + mounttype = (MS_SHARED | MS_REC); + break; + + case 141: + mounttype = (MS_SLAVE | MS_REC); + break; + + case 142: + mounttype = (MS_PRIVATE | MS_REC); + break; + + case 143: + mounttype = (MS_UNBINDABLE | MS_REC); + break; + case 144: + nocanonicalize = 1; + break; + case '?': + default: + usage (stderr, EX_USAGE); + } + } + + if (verbose > 2) { + printf("mount: fstab path: \"%s\"\n", _PATH_MNTTAB); + printf("mount: mtab path: \"%s\"\n", _PATH_MOUNTED); + printf("mount: lock path: \"%s\"\n", _PATH_MOUNTED_LOCK); + printf("mount: temp path: \"%s\"\n", _PATH_MOUNTED_TMP); + printf("mount: UID: %u\n", getuid()); + printf("mount: eUID: %u\n", geteuid()); + } + +#ifdef HAVE_LIBMOUNT_MOUNT + mnt_init_debug(0); +#endif + argc -= optind; + argv += optind; + + specseen = (uuid || label) ? 1 : 0; /* yes, .. i know */ + + if (argc+specseen == 0 && !mount_all) { + if (options || mounttype) + usage (stderr, EX_USAGE); + return print_all (types); + } + + { + const uid_t ruid = getuid(); + const uid_t euid = geteuid(); + + /* if we're really root and aren't running setuid */ + if (((uid_t)0 == ruid) && (ruid == euid)) { + restricted = 0; + } + + if (restricted && + (types || options || readwrite || nomtab || mount_all || + nocanonicalize || fake || mounttype || + (argc + specseen) != 1)) { + + if (ruid == 0 && euid != 0) + /* user is root, but setuid to non-root */ + die (EX_USAGE, _("mount: only root can do that " + "(effective UID is %u)"), euid); + + die (EX_USAGE, _("mount: only root can do that")); + } + } + + atexit(unlock_mtab); + + switch (argc+specseen) { + case 0: + /* mount -a */ + result = do_mount_all (types, options, test_opts); + if (result == 0 && verbose && !fake) + error(_("nothing was mounted")); + break; + + case 1: + /* mount [-nfrvw] [-o options] special | node + * mount -L label (or -U uuid) + * (/etc/fstab is necessary) + */ + if (types != NULL) + usage (stderr, EX_USAGE); + + if (uuid || label) + mc = getfs(NULL, uuid, label); + else + mc = getfs(*argv, NULL, NULL); + + if (!mc) { + if (uuid || label) + die (EX_USAGE, _("mount: no such partition found")); + + die (EX_USAGE, + _("mount: can't find %s in %s or %s"), + *argv, _PATH_MNTTAB, _PATH_MOUNTED); + } + + result = mount_one (xstrdup (mc->m.mnt_fsname), + xstrdup (mc->m.mnt_dir), + xstrdup (mc->m.mnt_type), + mc->m.mnt_opts, options, 0, 0); + break; + + case 2: + /* mount special node (/etc/fstab is not necessary) */ + if (specseen) { + /* mount -L label node (or -U uuid) */ + spec = uuid ? fsprobe_get_devname_by_uuid(uuid) : + fsprobe_get_devname_by_label(label); + node = argv[0]; + } else { + /* mount special node */ + spec = argv[0]; + node = argv[1]; + } + if (!spec) + die (EX_USAGE, _("mount: no such partition found")); + + result = mount_one (spec, node, types, NULL, options, 0, 0); + break; + + default: + usage (stderr, EX_USAGE); + } + + if (result == EX_SOMEOK) + result = 0; + + fsprobe_exit(); + + exit (result); +} |