| /* |
| * 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 "xmalloc.h" |
| #include "mount_mntent.h" |
| #include "fstab.h" |
| #include "lomount.h" |
| #include "loop.h" |
| #include "getusername.h" |
| #include "env.h" |
| #include "nls.h" |
| #include "blkdev.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; |
| |
| /* 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 */ |
| }; |
| |
| /* 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 |
| |
| /* 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}, /* 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 |
| { "quiet", 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}, /* Do not fail if ENOENT on dev */ |
| { NULL, 0, 0, 0 } |
| }; |
| |
| static int opt_nofail = 0; |
| |
| static const char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_sizelimit, |
| *opt_encryption, *opt_speed, *opt_comment, *opt_uhelper; |
| |
| static int is_readonly(const char *node); |
| static int mounted (const char *spec0, const char *node0); |
| 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 }, |
| { 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 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) { |
| if (mount_quiet) |
| return; |
| printf ("%s on %s", me->mnt_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, long num) |
| { |
| char buf[32]; |
| |
| snprintf(buf, sizeof(buf), "%ld", 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; |
| } |
| #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, 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->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 (streq(opt, "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(); |
| |
| 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, extra_opts); |
| opt = NULL; |
| } |
| } |
| free(opts); |
| } |
| |
| if (readonly) |
| *flags |= MS_RDONLY; |
| if (readwrite) |
| *flags &= ~MS_RDONLY; |
| |
| if (mounttype & MS_PROPAGATION) |
| *flags &= ~MS_BIND; |
| *flags |= mounttype; |
| } |
| |
| /* Try to build a canonical options string. */ |
| static char * |
| fix_opts_string (int flags, const char *extra_opts, const char *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; |
| 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); |
| |
| 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); |
| 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 |
| 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 mountprog[120]; |
| struct stat statbuf; |
| int res; |
| |
| if (!external_allowed) |
| return 0; |
| |
| if (type == NULL || strcmp(type, "none") == 0) |
| return 0; |
| |
| if (strlen(type) < 100) { |
| sprintf(mountprog, "/sbin/mount.%s", type); |
| if (stat(mountprog, &statbuf) == 0) { |
| if (verbose) |
| fflush(stdout); |
| res = fork(); |
| if (res == 0) { |
| char *oo, *mountargs[10]; |
| int i = 0; |
| |
| if(setgid(getgid()) < 0) |
| die(EX_FAIL, _("mount: cannot set group id: %s"), strerror(errno)); |
| |
| if(setuid(getuid()) < 0) |
| die(EX_FAIL, _("mount: cannot set user id: %s"), strerror(errno)); |
| |
| oo = fix_opts_string (flags, extra_opts, NULL); |
| 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 */ |
| } |
| mountargs[i] = NULL; /* 10 */ |
| |
| 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 */ |
| } else if (res != -1) { |
| int st; |
| wait(&st); |
| *status = (WIFEXITED(st) ? WEXITSTATUS(st) : EX_SYSERR); |
| return 1; |
| } else { |
| 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; |
| |
| if (fsprobe_known_fstype(fstype)) |
| return 1; |
| 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) |
| { |
| const char *type = fsprobe_get_fstype_by_devname(devname); |
| |
| 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 }; |
| |
| if (*types && strcasecmp (*types, "auto") == 0) |
| *types = NULL; |
| |
| if (!*types && !(flags & MS_REMOUNT)) { |
| *types = guess_fstype_by_devname(spec); |
| 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); |
| } |
| } |
| } |
| |
| /* 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 = loopfile_used_with((char *) mnt->m.mnt_fsname, |
| loopfile, offset); |
| |
| else if ((p = strstr(mnt->m.mnt_opts, "loop="))) { |
| char *dev = xstrdup(p+5); |
| if ((p = strchr(dev, ','))) |
| *p = '\0'; |
| res = loopfile_used_with(dev, loopfile, offset); |
| free(dev); |
| } |
| } |
| |
| free(node); |
| return res; |
| } |
| |
| 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; |
| unsigned long long offset, sizelimit; |
| |
| /* |
| * 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; |
| |
| if (*loop) { |
| *flags |= MS_LOOP; |
| if (fake) { |
| if (verbose) |
| printf(_("mount: skipping the setup of a loop device\n")); |
| } else { |
| int loop_opts = SETLOOP_AUTOCLEAR; /* always attempt autoclear */ |
| int res; |
| |
| if (*flags & MS_RDONLY) |
| loop_opts |= SETLOOP_RDONLY; |
| |
| offset = opt_offset ? strtoull(opt_offset, NULL, 0) : 0; |
| sizelimit = opt_sizelimit ? strtoull(opt_sizelimit, NULL, 0) : 0; |
| |
| 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; |
| } |
| |
| do { |
| if (!*loopdev || !**loopdev) |
| *loopdev = find_unused_loop_device(); |
| if (!*loopdev) |
| return EX_SYSERR; /* no more loop devices */ |
| if (verbose) |
| printf(_("mount: going to use the loop device %s\n"), *loopdev); |
| |
| if ((res = set_loop(*loopdev, *loopfile, offset, sizelimit, |
| opt_encryption, pfd, &loop_opts))) { |
| if (res == 2) { |
| /* loop dev has been grabbed by some other process, |
| try again, if not given explicitly */ |
| 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); |
| return EX_FAIL; |
| |
| } else { |
| if (verbose) |
| printf(_("mount: failed setting up loop device\n")); |
| if (!opt_loopdev) { |
| my_free(*loopdev); |
| *loopdev = NULL; |
| } |
| return EX_FAIL; |
| } |
| } |
| } while (!*loopdev); |
| |
| if (verbose > 1) |
| printf(_("mount: setup loop device successfully\n")); |
| *spec = *loopdev; |
| |
| if (loop_opts & SETLOOP_RDONLY) |
| *flags |= MS_RDONLY; |
| |
| if (loop_opts & SETLOOP_AUTOCLEAR) |
| /* Prevent recording loop dev in mtab for cleanup on umount */ |
| *loop = 0; |
| } |
| } |
| |
| return 0; |
| } |
| |
| 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 { |
| mntFILE *mfp; |
| |
| lock_mtab(); |
| mfp = my_setmntent(_PATH_MOUNTED, "a+"); |
| if (mfp == NULL || mfp->mntent_fp == NULL) { |
| int errsv = errno; |
| error(_("mount: can't open %s: %s"), _PATH_MOUNTED, |
| strerror (errsv)); |
| } else { |
| if ((my_addmntent (mfp, &mnt)) == 1) { |
| int errsv = errno; |
| error(_("mount: error writing %s: %s"), |
| _PATH_MOUNTED, strerror (errsv)); |
| } |
| } |
| my_endmntent(mfp); |
| unlock_mtab(); |
| } |
| } |
| my_free(mnt.mnt_fsname); |
| my_free(mnt.mnt_dir); |
| } |
| |
| 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: %s"), |
| strerror(errno)); |
| close(cdrom); |
| } |
| } |
| |
| /* |
| * Check if @node is read-only filesystem by access() or futimens(). |
| * |
| * Note that access(2) uses real-UID (= useless for suid programs) |
| * and euidaccess(2) does not check for read-only FS. |
| */ |
| static int |
| is_readonly(const char *node) |
| { |
| int res = 0; |
| |
| if (getuid() == geteuid()) { |
| if (access(node, W_OK) == -1 && errno == EROFS) |
| res = 1; |
| } |
| #ifdef HAVE_FUTIMENS |
| else { |
| struct timespec times[2]; |
| int fd = open(node, O_RDONLY); |
| |
| if (fd < 0) |
| goto done; |
| |
| times[0].tv_nsec = UTIME_NOW; /* atime */ |
| times[1].tv_nsec = UTIME_OMIT; /* mtime */ |
| |
| if (futimens(fd, times) == -1 && errno == EROFS) |
| res = 1; |
| close(fd); |
| } |
| done: |
| #endif |
| return res; |
| } |
| |
| /* |
| * 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; |
| int retries = 0; /* Nr of retries for mount in case of ENOMEDIUM */ |
| |
| /* copies for freeing on exit */ |
| const char *opts1, *spec1, *node1, *types1, *extra_opts1; |
| |
| 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); |
| extra_opts1 = 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)) |
| die(EX_USAGE, _("mount: according to mtab, " |
| "%s is already mounted on %s\n"), |
| spec, node); |
| |
| mount_opts = extra_opts; |
| |
| 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"; |
| |
| /* |
| * 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, extra_opts, &status)) { |
| res = status; |
| goto out; |
| } |
| |
| mount_retry: |
| 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 ntab 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; |
| } |
| |
| if (fake || mnt5_res == 0) { |
| /* Mount succeeded, report this (if verbose) and write mtab entry. */ |
| |
| if (!(mounttype & MS_PROPAGATION)) { |
| update_mtab_entry(loop ? loopfile : spec, |
| node, |
| types ? types : "unknown", |
| fix_opts_string (flags & ~MS_NOMTAB, extra_opts, user), |
| flags, |
| freq, |
| pass); |
| } |
| |
| block_signals (SIG_UNBLOCK); |
| res = 0; |
| goto out; |
| } |
| |
| mnt_err = errno; |
| |
| if (loop) |
| del_loop(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 already, 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: |
| if (retries < CRDOM_NOMEDIUM_RETRIES) { |
| if (verbose) |
| printf(_("mount: no medium found on %s ...trying again\n"), |
| spec); |
| sleep(3); |
| ++retries; |
| goto mount_retry; |
| } |
| 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(extra_opts1); |
| 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=%d", getuid()); |
| opts = subst_string(opts, w, strlen(s), id); |
| } |
| s = "gid=usergid"; |
| if (opts && (w = strstr(opts, s)) != NULL) { |
| sprintf(id, "gid=%d", 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; |
| 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, "nfs", 3) && |
| strncmp(types, "cifs", 4) && |
| strncmp(types, "smbfs", 5))) { |
| nspec = spec_to_devname(spec); |
| if (nspec) |
| spec = nspec; |
| } |
| |
| return try_mount_one (spec, node, types, opts, freq, pass, 0); |
| } |
| |
| /* Check if an fsname/dir pair was already in the old mtab. */ |
| static int |
| mounted (const char *spec0, const char *node0) { |
| struct mntentchn *mc, *mc0; |
| const char *spec, *node; |
| int ret = 0; |
| |
| /* 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; |
| } |
| |
| /* 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")) { |
| |
| if (mounted (mc->m.mnt_fsname, mc->m.mnt_dir)) { |
| if (verbose) |
| printf(_("mount: %s already mounted " |
| "on %s\n"), |
| mc->m.mnt_fsname, |
| mc->m.mnt_dir); |
| continue; |
| } |
| |
| 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_spec(spec); |
| |
| if (!mc) |
| mc = getfs_by_dir(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. |
| */ |
| if (!mc && (devname || spec)) |
| mc = getmntfile (devname ? devname : spec); |
| |
| 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); |
| |
| 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: %d\n", getuid()); |
| printf("mount: eUID: %d\n", geteuid()); |
| } |
| |
| 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 %d)"), 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); |
| } |