* A mount(8) for Linux 0.99.
* mount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
*
- * Thu Jul 14 07:32:40 1994: faith@cs.unc.edu added changed from Adam
- * J. Richter (adam@adam.yggdrasil.com) so that /proc/filesystems is used
- * if no -t option is given. I modified his patches so that, if
- * /proc/filesystems is not available, the behavior of mount is the same as
- * it was previously.
- *
* Wed Sep 14 22:43:00 1994: Mitchum DSouza
* (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for mounting
* the "loop" device.
* Wed Sep 14 22:55:10 1994: Sander van Malssen (svm@kozmix.hacktic.nl)
* added support for remounting readonly file systems readonly.
*
- * Wed Feb 8 09:23:18 1995: Mike Grupenhoff <kashmir@umiacs.UMD.EDU> added
- * a probe of the superblock for the type before /proc/filesystems is
- * checked.
- *
* Wed Feb 8 12:27:00 1995: Andries.Brouwer@cwi.nl fixed up error messages.
+ * Sat Jun 3 20:44:38 1995: Patches from Andries.Brouwer@cwi.nl applied.
+ * Tue Sep 26 22:38:20 1995: aeb@cwi.nl, many changes
+ * Fri Feb 23 13:47:00 1996: aeb@cwi.nl, loop device related changes
+ *
+ * Since then, many changes - aeb.
+ *
+ * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * Tue Aug 4 15:54:31 1998: aeb@cwi.nl:
+ * Open fd 0,1,2 so that printf's do not clobber /etc/mtab or so.
+ * Mangle filenames with embedded spaces. Add ufsmagic. Add locking.
+ * Avoid unnecessary error messages about /proc.
+ * Improve support for noncanonical names in /etc/fstab.
+ * Add support for volume labels and UUIDs.
*
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ * 1999-03-21 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixed strerr(errno) in gettext calls
+ * 1999-07-05 Hirokazu Takahashi <h-takaha@sss.abk.nec.co.jp>
+ * - fixed use of nouser option
+ * 1999-09-09 Michael K. Johnson <johnsonm@redhat.com>
+ * - added `owner' mount option
+ * 2000-05-11 Mark A. Peloquin <peloquin@us.ibm.com>
+ * - check_special_mountprog now returns correct status
+ * 2000-11-08 aeb: accept nonnumeric uid=, gid= options
*/
-#include "sundries.h"
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdio.h>
-#include <linux/fs.h>
-#include <linux/minix_fs.h>
-#include <linux/ext_fs.h>
-#include <linux/ext2_fs.h>
-#include <linux/xia_fs.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
-#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
-int del_loop (const char *);
+#include "mount_constants.h"
+#include "sundries.h"
+#include "mntent.h"
+#include "fstab.h"
+#include "lomount.h"
+#include "loop.h"
+#include "linux_fs.h" /* for BLKGETSIZE */
+#include "mount_guess_rootdev.h"
+#include "mount_guess_fstype.h"
+#include "mount_by_label.h"
+#include "getusername.h"
+#include "nls.h"
+
+#define DO_PS_FIDDLING
+
+#ifdef DO_PS_FIDDLING
+#include "setproctitle.h"
+#endif
/* True for fake mount (-f). */
-int fake = 0;
+static int fake = 0;
/* Don't write a entry in /etc/mtab (-n). */
-int nomtab = 0;
+static int nomtab = 0;
-/* True for readonly (-r). */
-int readonly = 0;
+/* True for explicit readonly (-r). */
+static int readonly = 0;
/* Nonzero for chatty (-v). */
int verbose = 0;
-/* True for read/write (-w). */
-int readwrite = 0;
+/* Nonzero for sloppy (-s). */
+int sloppy = 0;
+
+/* True for explicit read/write (-w). */
+static int readwrite = 0;
/* True for all mount (-a). */
int 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 */
+static int bind = 0;
+
+/* Nonzero for mount {--replace|--before|--after|--over} */
+static int mounttype = 0;
+
/* True if ruid != euid. */
-int suid = 0;
+static int suid = 0;
/* Map from -o and fstab option strings to the flag argument to mount(2). */
-struct opt_map
-{
+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_USER 0x40000000
+#define MS_USERS 0x40000000
+#define MS_USER 0x20000000
+#define MS_OWNER 0x10000000
+#define MS_LOOP 0x00010000
/* Options that we keep the mount system call from seeing. */
-#define MS_NOSYS (MS_NOAUTO|MS_USER)
+#define MS_NOSYS (MS_NOAUTO|MS_USERS|MS_USER|MS_LOOP)
/* Options that we keep from appearing in the options field in the mtab. */
-#define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USER)
+#define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USERS|MS_USER)
-/* OPTIONS that we make ordinary users have by default. */
+/* Options that we make ordinary users have by default. */
#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV)
-const struct opt_map opt_map[] =
-{
- { "defaults", 0, 0 }, /* default options */
- { "ro", 0, MS_RDONLY }, /* read-only */
- { "rw", 1, MS_RDONLY }, /* read-write */
- { "exec", 1, MS_NOEXEC }, /* permit execution of binaries */
- { "noexec", 0, MS_NOEXEC }, /* don't execute binaries */
- { "suid", 1, MS_NOSUID }, /* honor suid executables */
- { "nosuid", 0, MS_NOSUID }, /* don't honor suid executables */
- { "dev", 1, MS_NODEV }, /* interpret device files */
- { "nodev", 0, MS_NODEV }, /* don't interpret devices */
- { "sync", 0, MS_SYNCHRONOUS}, /* synchronous I/O */
- { "async", 1, MS_SYNCHRONOUS}, /* asynchronous I/O */
- { "remount", 0, MS_REMOUNT }, /* Alter flags of mounted FS */
- { "auto", 1, MS_NOAUTO }, /* Can be mounted using -a */
- { "noauto", 0, MS_NOAUTO }, /* Can only be mounted explicitly */
- { "user", 0, MS_USER }, /* Allow ordinary user to mount */
- { "nouser", 1, MS_USER }, /* Forbid ordinary user to mount */
+/* 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 */
+ { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */
+ { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */
+ { "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 */
/* add new options here */
#ifdef MS_NOSUB
- { "sub", 1, MS_NOSUB }, /* allow submounts */
- { "nosub", 0, MS_NOSUB }, /* don't allow submounts */
+ { "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_NODIRATIME
+ { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */
+ { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */
#endif
- { NULL, 0, 0 }
+ { NULL, 0, 0, 0 }
};
+static char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption,
+ *opt_speed;
+
+static struct string_opt_map {
+ char *tag;
+ int skip;
+ char **valptr;
+} string_opt_map[] = {
+ { "loop=", 0, &opt_loopdev },
+ { "vfs=", 1, &opt_vfstype },
+ { "offset=", 0, &opt_offset },
+ { "encryption=", 0, &opt_encryption },
+ { "speed=", 0, &opt_speed },
+ { NULL, 0, NULL }
+};
-/* Report on a single mount. */
static void
-print_one (const struct mntent *mnt)
-{
- printf ("%s on %s", mnt->mnt_fsname, mnt->mnt_dir);
- if ((mnt->mnt_type != NULL) && *mnt->mnt_type != '\0')
- printf (" type %s", mnt->mnt_type);
- if (mnt->mnt_opts != NULL)
- printf (" (%s)", mnt->mnt_opts);
- printf ("\n");
+clear_string_opts(void) {
+ struct string_opt_map *m;
+
+ for (m = &string_opt_map[0]; m->tag; m++)
+ *(m->valptr) = NULL;
}
-/* Report on everything in mtab (of the specified types if any). */
static int
-print_all (string_list types)
-{
- struct mntent *mnt;
-
- open_mtab ("r");
+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;
+}
- while ((mnt = getmntent (F_mtab)) != NULL)
- if (matching_type (mnt->mnt_type, types))
- print_one (mnt);
+int mount_quiet=0;
- if (ferror (F_mtab))
- die (1, "mount: error reading %s: %s", MOUNTED, strerror (errno));
+/* Report on a single mount. */
+static void
+print_one (const struct 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) {
+ const char *label;
+ label = get_volume_label_by_spec(me->mnt_fsname);
+ if (label)
+ printf (" [%s]", label);
+ }
+ printf ("\n");
+}
- exit (0);
+/* Report on everything in mtab (of the specified types if any). */
+static int
+print_all (string_list 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));
+ }
+ exit (0);
}
-/* Look for OPT in opt_map table and return mask value. If OPT isn't found,
- tack it onto extra_opts. */
+/*
+ * 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 (const char *opt, int *mask, char *extra_opts)
-{
+parse_opt (const 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 (streq (opt, om->opt)) {
if (om->inv)
*mask &= ~om->mask;
else
*mask |= om->mask;
- if (om->mask == MS_USER)
+ if ((om->mask == MS_USER || om->mask == MS_USERS)
+ && !om->inv)
*mask |= MS_SECURE;
+ if ((om->mask == MS_OWNER) && !om->inv)
+ *mask |= MS_OWNERSECURE;
+#ifdef MS_SILENT
+ if (om->mask == MS_SILENT && om->inv) {
+ mount_quiet = 1;
+ verbose = 0;
+ }
+#endif
return;
}
+
if (*extra_opts)
strcat(extra_opts, ",");
+
+ /* convert nonnumeric ids to numeric */
+ if (!strncmp(opt, "uid=", 4) && !isdigit(opt[4])) {
+ struct passwd *pw = getpwnam(opt+4);
+ char uidbuf[20];
+
+ if (pw) {
+ sprintf(uidbuf, "uid=%d", pw->pw_uid);
+ strcat(extra_opts, uidbuf);
+ return;
+ }
+ }
+ if (!strncmp(opt, "gid=", 4) && !isdigit(opt[4])) {
+ struct group *gr = getgrnam(opt+4);
+ char gidbuf[20];
+
+ if (gr) {
+ sprintf(gidbuf, "gid=%d", gr->gr_gid);
+ strcat(extra_opts, gidbuf);
+ return;
+ }
+ }
+
strcat(extra_opts, opt);
}
/* Take -o options list and compute 4th and 5th args to mount(2). flags
- gets the standard options and extra_opts anything we don't recognize. */
+ gets the standard options (indicated by bits) and extra_opts all the rest */
static void
-parse_opts (char *opts, int *flags, char **extra_opts)
-{
- char *opt;
+parse_opts (char *opts, int *flags, char **extra_opts) {
+ char *opt;
- *flags = 0;
- *extra_opts = NULL;
+ *flags = 0;
+ *extra_opts = NULL;
- if (opts != NULL)
- {
- *extra_opts = xmalloc (strlen (opts) + 1);
- **extra_opts = '\0';
+ clear_string_opts();
- for (opt = strtok (opts, ",");
- opt != NULL;
- opt = strtok (NULL, ","))
- parse_opt (opt, flags, *extra_opts);
- }
+ if (opts != NULL) {
+ *extra_opts = xmalloc (strlen (opts) + 1);
+ **extra_opts = '\0';
+
+ for (opt = strtok (opts, ","); opt; opt = strtok (NULL, ","))
+ if (!parse_string_opt (opt))
+ parse_opt (opt, flags, *extra_opts);
+ }
- if (readonly)
- *flags |= MS_RDONLY;
- if (readwrite)
- *flags &= ~MS_RDONLY;
+ if (readonly)
+ *flags |= MS_RDONLY;
+ if (readwrite)
+ *flags &= ~MS_RDONLY;
+ *flags |= mounttype;
+ if (bind)
+ *flags |= MS_BIND;
}
/* Try to build a canonical options string. */
static char *
-fix_opts_string (int flags, char *extra_opts)
-{
+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;
- char *tmp;
new_opts = (flags & MS_RDONLY) ? "ro" : "rw";
- for (om = opt_map; om->opt != NULL; om++)
- {
- if (om->mask & MS_RDONLY)
+ for (om = opt_map; om->opt != NULL; om++) {
+ if (om->skip)
continue;
if (om->inv || !om->mask || (flags & om->mask) != om->mask)
continue;
- tmp = xmalloc(strlen(new_opts) + strlen(om->opt) + 2);
- sprintf(tmp, "%s,%s", new_opts, om->opt);
- new_opts = tmp;
+ new_opts = xstrconcat3(new_opts, ",", om->opt);
flags &= ~om->mask;
- }
- if (extra_opts && *extra_opts)
- {
- tmp = xmalloc(strlen(new_opts) + strlen(extra_opts) + 2);
- sprintf(tmp, "%s,%s", new_opts, extra_opts);
- new_opts = tmp;
- }
+ }
+ for (m = &string_opt_map[0]; m->tag; m++) {
+ if (!m->skip && *(m->valptr))
+ new_opts = xstrconcat4(new_opts, ",", m->tag, *(m->valptr));
+ }
+ if (extra_opts && *extra_opts) {
+ new_opts = xstrconcat3(new_opts, ",", extra_opts);
+ }
+ if (user) {
+ new_opts = xstrconcat3(new_opts, ",user=", user);
+ }
return new_opts;
}
+static int
+already (const char *spec, const char *node) {
+ struct mntentchn *mc;
+ int ret = 1;
+
+ 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;
+ return ret;
+}
-/*
- char *fstype(const char *device);
-
- probes the device and attempts to determine the type of filesystem
- contained within.
+/* Create mtab with a root entry. */
+static void
+create_mtab (void) {
+ struct mntentchn *fstab;
+ struct mntent mnt;
+ int flags;
+ char *extra_opts;
+ mntFILE *mfp;
- Original routine by <jmorriso@bogomips.ww.ubc.ca>; made into a function
- for mount(8) by Mike Grupenhoff <kashmir@umiacs.umd.edu>.
+ lock_mtab();
- Currently supports: minix, ext, ext2, xia
-*/
+ mfp = my_setmntent (MOUNTED, "a+");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ die (EX_FILEIO, _("mount: can't open %s for writing: %s"),
+ MOUNTED, strerror (errsv));
+ }
-static char *
-fstype(const char *device)
-{
- int fd;
-
- /* MINIX */
- struct minix_super_block ms;
- /* extended fs */
- struct ext_super_block es;
- /* 2nd extended fs */
- struct ext2_super_block e2s;
- /* xia fs */
- struct xiafs_super_block xfs;
-
- fd = open(device, O_RDONLY);
- if (fd < 0) {
- perror(device);
- return 0;
- }
- lseek(fd, BLOCK_SIZE, SEEK_SET);
- read(fd, (char *) &ms, sizeof(ms));
- if (ms.s_magic == MINIX_SUPER_MAGIC || ms.s_magic == MINIX_SUPER_MAGIC2) {
- close(fd);
- return("minix");
+ /* Find the root entry by looking it up in fstab */
+ if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) {
+ parse_opts (xstrdup (fstab->m.mnt_opts), &flags, &extra_opts);
+ mnt.mnt_dir = "/";
+ mnt.mnt_fsname = canonicalize (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;
+
+ if (my_addmntent (mfp, &mnt) == 1) {
+ int errsv = errno;
+ die (EX_FILEIO, _("mount: error writing %s: %s"),
+ MOUNTED, strerror (errsv));
+ }
+ }
+ if (fchmod (fileno (mfp->mntent_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
+ if (errno != EROFS) {
+ int errsv = errno;
+ die (EX_FILEIO, _("mount: error changing mode of %s: %s"),
+ MOUNTED, strerror (errsv));
}
+ my_endmntent (mfp);
- lseek(fd, BLOCK_SIZE, SEEK_SET);
- read(fd, (char *) &es, sizeof(es));
- if (es.s_magic == EXT_SUPER_MAGIC) {
- close(fd);
- return("ext");
- }
+ unlock_mtab();
+}
- lseek(fd, BLOCK_SIZE, SEEK_SET);
- read(fd, (char *) &e2s, sizeof(e2s));
- if (e2s.s_magic == EXT2_SUPER_MAGIC || e2s.s_magic == EXT2_PRE_02B_MAGIC) {
- close(fd);
- return("ext2");
- }
+/* count successful mount system calls */
+static int mountcount = 0;
- lseek(fd, 0, SEEK_SET);
- read(fd, (char *) &xfs, sizeof(xfs));
- if (xfs.s_magic == _XIAFS_SUPER_MAGIC) {
- close(fd);
- return("xiafs");
- }
+/*
+ * 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 ret = mount (args->spec, args->node, args->type,
+ MS_MGC_VAL | (args->flags), args->data);
+ if (ret == 0)
+ mountcount++;
+ return ret;
+}
- close(fd);
+/*
+ * 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.
+ */
+static int
+guess_fstype_and_mount (char *spec, char *node, char **type,
+ int flags, char *mount_opts) {
+ struct mountargs args = { spec, node, NULL, flags & ~MS_NOSYS, mount_opts };
+
+ if (*type && strcasecmp (*type, "auto") == 0)
+ *type = NULL;
+
+ if (!*type && (flags & MS_BIND))
+ *type = "none"; /* random, but not "bind" */
+
+ if (!*type && !(flags & MS_REMOUNT)) {
+ *type = guess_fstype_from_superblock(spec);
+ if (*type && !strcmp(*type, "swap")) {
+ error(_("%s looks like swapspace - not mounted"), spec);
+ *type = NULL;
+ return 1;
+ }
+ }
- return(0);
+ if (*type || (flags & MS_REMOUNT)) {
+ args.type = *type;
+ return do_mount_syscall (&args);
+ }
+ return procfsloop(do_mount_syscall, &args, type);
}
+/*
+ * suid_check()
+ * Die if the user is not allowed to do this.
+ */
+static void
+suid_check(char *spec, char *node, int *flags, char **user) {
+ if (suid) {
+ /* RedHat patch: 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. */
+ if (*flags & MS_OWNER) {
+ if (!strncmp(spec, "/dev/", 5)) {
+ struct stat sb;
+
+ if (!stat(spec, &sb)) {
+ if (getuid() == sb.st_uid)
+ *flags |= MS_USER;
+ }
+ }
+ }
+ /* 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();
+ }
+
+ if (*flags & MS_OWNER)
+ *flags &= ~MS_OWNER;
+}
-/* Mount a single file system. Return status,
- so don't exit on non-fatal errors. */
-
static int
-try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) {
- FILE *procfs_file;
- char line[100];
- char fsname[50];
-
- if (*type) return mount5 (spec, node, *type, flags & ~MS_NOSYS, mount_opts);
- if (( procfs_file = fopen("/proc/filesystems", "r")) == NULL) {
- /* If /proc/filesystems is not available,
- preserve the old behavior of mount. */
- return mount5 (spec,
- node,
- FSTYPE_DEFAULT,
- flags & ~MS_NOSYS, mount_opts);
- }
- while (fgets(line, sizeof(line), procfs_file)) {
- if (sscanf (line, "nodev %[^\n]\n", fsname) == 1) continue;
- if (sscanf (line, " %[^ \n]\n", fsname) != 1) continue;
- if (mount5 (spec, node, fsname, flags & ~MS_NOSYS, mount_opts) == 0) {
- *type=xstrdup(fsname);
- fclose(procfs_file);
- return 0;
+loop_check(char **spec, char **type, int *flags,
+ int *loop, char **loopdev, char **loopfile) {
+ int looptype, offset;
+
+ /*
+ * 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_encryption);
+ *loopfile = *spec;
+
+ if (*loop) {
+ *flags |= MS_LOOP;
+ if (fake) {
+ if (verbose)
+ printf(_("mount: skipping the setup of a loop device\n"));
+ } else {
+ int loopro = (*flags & MS_RDONLY);
+
+ 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);
+ offset = opt_offset ? strtoul(opt_offset, NULL, 0) : 0;
+ if (set_loop (*loopdev, *loopfile, offset, opt_encryption, &loopro)) {
+ if (verbose)
+ printf(_("mount: failed setting up loop device\n"));
+ return EX_FAIL;
}
- }
- fclose(procfs_file);
- return -1;
+ if (verbose > 1)
+ printf(_("mount: setup loop device successfully\n"));
+ *spec = *loopdev;
+ if (loopro)
+ *flags |= MS_RDONLY;
+ }
+ }
+
+ return 0;
}
+static void
+update_mtab_entry(char *spec, char *node, char *type, char *opts,
+ int flags, int freq, int pass) {
+ struct mntent mnt;
+
+ mnt.mnt_fsname = 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_is_writable()) {
+ if (flags & MS_REMOUNT)
+ update_mtab (mnt.mnt_dir, &mnt);
+ else {
+ mntFILE *mfp;
+
+ lock_mtab();
+ mfp = my_setmntent(MOUNTED, "a+");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ error(_("mount: can't open %s: %s"), MOUNTED,
+ strerror (errsv));
+ } else {
+ if ((my_addmntent (mfp, &mnt)) == 1) {
+ int errsv = errno;
+ error(_("mount: error writing %s: %s"), MOUNTED,
+ strerror (errsv));
+ }
+ my_endmntent(mfp);
+ }
+ unlock_mtab();
+ }
+ }
+}
+
+static void
+cdrom_setspeed(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 not 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_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
+ */
+#define ALWAYS_STAT
static int
-mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
-{
- struct mntent mnt;
+check_special_mountprog(char *spec, char *node, char *type, int flags,
+ char *extra_opts, int *status) {
+ char mountprog[120];
+ struct stat statbuf;
+ int res;
+
+ if (type &&
+#ifndef ALWAYS_STAT
+ (streq (type, "smb") || streq (type, "ncp")
+ /* these are incorrect but perhaps used by smbmount or so */
+ || streq (type, "smbfs") || streq (type, "ncpfs")
+ )
+#else
+ (strlen (type) < 100)
+#endif
+ ) {
+ sprintf(mountprog, "/sbin/mount.%s", type);
+ if (stat(mountprog, &statbuf) == 0) {
+ res = fork();
+ if (res == 0) {
+ char *oo, *mountargs[10];
+ int i = 0;
+
+ setuid(getuid());
+ setgid(getgid());
+ oo = fix_opts_string (flags, extra_opts, NULL);
+ mountargs[i++] = mountprog;
+ mountargs[i++] = spec;
+ mountargs[i++] = node;
+ if (nomtab)
+ mountargs[i++] = "-n";
+ if (verbose)
+ mountargs[i++] = "-v";
+ if (oo && *oo) {
+ mountargs[i++] = "-o";
+ mountargs[i++] = oo;
+ }
+ mountargs[i] = NULL;
+ 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;
+}
+
+/*
+ * try_mount_one()
+ * Try to mount one file system. When "bg" is 1, this is a retry
+ * in the background. One additional exit code EX_BG is used here.
+ * It is used to instruct the caller to retry the mount in the
+ * background.
+ * 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, char *type0,
+ const char *opts0, int freq, int pass, int bg, int ro) {
+ int res, status;
+ int mnt5_res = 0; /* only for gcc */
int mnt_err;
int flags;
- char *extra_opts;
- char *mount_opts;
- int anti_recurse = 0;
- int loop=0;
+ char *extra_opts; /* written in mtab */
+ char *mount_opts; /* actually used on system call */
+ const char *opts;
+ char *spec, *node, *type;
+ char *user = 0;
+ int loop = 0;
+ char *loopdev = 0, *loopfile = 0;
+ struct stat statbuf;
- if (type == NULL)
- {
- if (strchr (spec, ':') != NULL)
- type = "nfs";
- }
+ spec = xstrdup(spec0);
+ node = xstrdup(node0);
+ type = xstrdup(type0);
+ opts = xstrdup(opts0);
parse_opts (xstrdup (opts), &flags, &extra_opts);
- /* root may allow certain types of mounts by ordinary users */
- if (suid && !(flags & MS_USER))
- die (3, "mount: only root can mount %s on %s", spec, node);
+ suid_check (spec, node, &flags, &user);
/* quietly succeed for fstab entries that don't get mounted automatically */
if (all && (flags & MS_NOAUTO))
mount_opts = extra_opts;
- if (!fake && type && strncmp("lo@", type, 3)==0) {
- extern int lomount (char *, char *, char *, char **,
- int *, char **, char **);
- char *dev=type+3;
-
- loop=1;
- if (lomount (spec, node, dev, &type,
- &flags, &opts, &mount_opts) != 0)
- return 1;
- spec=dev;
- mount_opts=NULL;
+ 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, &type, &flags, &loop, &loopdev, &loopfile);
+ if (res)
+ return res;
}
- if (!fake && type && streq (type, "nfs"))
+ /*
+ * Call mount.TYPE for types that require a separate mount program.
+ * For the moment these types are ncp and smb.
+ */
+ if (check_special_mountprog (spec, node, type, flags, extra_opts, &status))
+ return status;
+
+ if (!fake && type && streq (type, "nfs")) {
#ifdef HAVE_NFS
- if (nfsmount (spec, node, &flags, &extra_opts, &mount_opts) != 0)
- return 1;
+retry_nfs:
+ mnt_err = nfsmount (spec, node, &flags, &extra_opts, &mount_opts, bg);
+ if (mnt_err)
+ return mnt_err;
#else
- die (1, "mount: this version doesn't support the type `nfs'");
+ die (EX_SOFTWARE, _("mount: this version was compiled "
+ "without support for the type `nfs'"));
#endif
-
- if (!type && !(type = fstype(spec)))
- return 1;
+ }
block_signals (SIG_BLOCK);
- if (fake
- || (try_mount5 (spec, node, &type, flags & ~MS_NOSYS, mount_opts)) == 0)
- /* Mount succeeded, write mtab entry. */
- {
- if (!nomtab)
- {
- mnt.mnt_fsname = canonicalize (spec);
- mnt.mnt_dir = canonicalize (node);
- mnt.mnt_type = loop?"loop":type;
- mnt.mnt_opts = fix_opts_string (flags & ~MS_NOMTAB,
- loop?opts:extra_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 (!fake)
+ mnt5_res = guess_fstype_and_mount (spec, node, &type, flags & ~MS_NOSYS,
+ mount_opts);
- if (flags & MS_REMOUNT)
- {
- close_mtab ();
- update_mtab (mnt.mnt_dir, &mnt);
- open_mtab ("a+");
- }
- else
- if ((addmntent (F_mtab, &mnt)) == 1)
- die (1, "mount: error writing %s: %s",
- MOUNTED, strerror (errno));
- }
+ if (fake || mnt5_res == 0) {
+ /* Mount succeeded, report this (if verbose) and write mtab entry. */
+ if (loop)
+ opt_loopdev = loopdev;
+
+ update_mtab_entry(loop ? loopfile : spec,
+ node,
+ type ? type : "unknown",
+ fix_opts_string (flags & ~MS_NOMTAB, extra_opts, user),
+ flags,
+ freq,
+ pass);
block_signals (SIG_UNBLOCK);
return 0;
- }
+ }
+
+ mnt_err = errno;
if (loop)
del_loop(spec);
- mnt_err = errno; /* work around for errno bug in sigprocmask */
-
block_signals (SIG_UNBLOCK);
+#ifdef HAVE_NFS
+ if (mnt_err && type && streq (type, "nfs")) {
+ extern int nfs_mount_version;
+ if (nfs_mount_version == 4) {
+ if (verbose)
+ printf(_("mount: failed with nfs mount version 4, trying 3..\n"));
+ nfs_mount_version = 3;
+ goto retry_nfs;
+ }
+ }
+#endif
+
/* Mount failed, complain, but don't die. */
- switch (mnt_err)
- {
+
+ if (type == 0) {
+ if (suid)
+ 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)
- error ("mount: mount point %s is not a directory", node);
- else
- error ("mount: must be superuser to use mount");
+ 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:
- error ("mount: %s already mounted or %s busy", spec, node);
+ if (flags & MS_REMOUNT) {
+ error (_("mount: %s is busy"), node);
+ } else if (!strcmp(type, "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(!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:
- { struct stat statbuf;
- if (stat (node, &statbuf))
- error ("mount: mount point %s does not exist", node);
- else if (stat (spec, &statbuf))
- error ("mount: special device %s does not exist", spec);
- else {
+ 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))
+ error (_("mount: special device %s does not exist"), spec);
+ else {
errno = mnt_err;
perror("mount");
- }
- break;
- }
+ }
+ break;
case ENOTDIR:
- error ("mount: mount point %s is not a directory", node); break;
+ 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)
+ 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:
- error ("mount: wrong fs type or bad superblock on %s", spec); break;
+ { int fd;
+ long size;
+ int warned=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"
+ " or too many mounted file systems"),
+ spec);
+
+ if (stat (spec, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)
+ && (fd = open(spec, O_RDONLY | O_NONBLOCK)) >= 0) {
+ if(ioctl(fd, BLKGETSIZE, &size) == 0) {
+ if (size == 0) {
+ warned++;
+ 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) {
+ warned++;
+ error (" (aren't you trying to mount an extended partition,\n"
+ " instead of some logical partition inside?)");
+ }
+ close(fd);
+ }
+#if 0
+ /* 0xf for SCSI, 0x3f for IDE. One might check /proc/partitions
+ to see whether this thing really is partitioned.
+ Do not suggest partitions for /dev/fd0. */
+ if (!warned && (statbuf.st_rdev & 0xf) == 0) {
+ warned++;
+ error (" (could this be the whole disk device\n"
+ " where you need a partition?)");
+ }
+#endif
+ }
+ }
+ break;
+ }
case EMFILE:
- error ("mount table full"); break;
+ error (_("mount table full")); break;
case EIO:
- error ("mount: %s: can't read superblock", spec); break;
+ error (_("mount: %s: can't read superblock"), spec); break;
case ENODEV:
- error ("mount: fs type %s not supported by kernel", type); break;
+ { int pfs;
+ if ((pfs = is_in_procfs(type)) == 1 || !strcmp(type, "guess"))
+ error(_("mount: %s: unknown device"), spec);
+ else if (pfs == 0) {
+ char *lowtype, *p;
+ int u;
+
+ error (_("mount: fs type %s not supported by kernel"), type);
+
+ /* maybe this loser asked for FAT or ISO9660 or isofs */
+ lowtype = xstrdup(type);
+ u = 0;
+ for(p=lowtype; *p; p++) {
+ if(tolower(*p) != *p) {
+ *p = tolower(*p);
+ u++;
+ }
+ }
+ if (u && is_in_procfs(lowtype) == 1)
+ error (_("mount: probably you meant %s"), lowtype);
+ else if (!strncmp(lowtype, "iso", 3) && is_in_procfs("iso9660") == 1)
+ error (_("mount: maybe you meant iso9660 ?"));
+ free(lowtype);
+ } else
+ error (_("mount: %s has wrong device number or fs type %s not supported"),
+ spec, type);
+ break;
+ }
case ENOTBLK:
- error ("mount: %s is not a block device", spec); break;
+ 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 `insmod 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:
- error ("mount: %s is not a valid block device", spec); break;
- case EACCES: /* pre-linux 1.1.38 */
+ 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 */
- if (anti_recurse)
- {
- error ("mount: block device %s is not permitted on its filesystem", spec);
+ { char *bd = (loop ? "" : _("block device "));
+ if (ro || (flags & MS_RDONLY)) {
+ error (_("mount: cannot mount %s%s read-only"),
+ bd, spec);
break;
- }
- else
- {
- anti_recurse++;
- if (opts)
- {
- opts = realloc(xstrdup(opts), strlen(opts)+3);
- strcat(opts, ",ro");
- }
- else
- opts = "ro";
- error ("mount: block device %s is write-protected, mounting read-only", spec);
- return mount_one (spec, node, type, opts, freq, pass);
- }
+ } else if (readwrite) {
+ error (_("mount: %s%s is write-protected but explicit `-w' flag given"),
+ bd, spec);
+ break;
+ } else {
+ if (loop) {
+ opts = opts0;
+ type = type0;
+ }
+ if (opts) {
+ char *opts1 = realloc(xstrdup(opts), strlen(opts)+4);
+ strcat(opts1, ",ro");
+ opts = opts1;
+ } else
+ opts = "ro";
+ if (type && !strcmp(type, "guess"))
+ type = 0;
+ error (_("mount: %s%s is write-protected, mounting read-only"),
+ bd, spec0);
+ return try_mount_one (spec0, node0, type, opts, freq, pass, bg, 1);
+ }
break;
+ }
default:
error ("mount: %s", strerror (mnt_err)); break;
}
- return 1;
+ }
+ return EX_FAIL;
}
-/* Check if an fsname/dir pair was already in the old mtab. */
-static int
-mounted (char *spec, char *node, string_list spec_list, string_list node_list)
+/*
+ * set_proc_name()
+ * Update the argument vector, so that this process may be easily
+ * identified in a "ps" listing.
+ */
+static void
+set_proc_name (const char *spec)
{
- spec = canonicalize (spec);
- node = canonicalize (node);
-
- while (spec_list != NULL)
- {
- if (streq (spec, car (spec_list)) && streq (node, car (node_list)))
- return 1;
- spec_list = cdr (spec_list);
- node_list = cdr (node_list);
- }
- return 0;
+#ifdef DO_PS_FIDDLING
+ setproctitle ("mount", spec);
+#endif
}
-/* Mount all filesystems of the specified types except swap and root. */
+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 const char *
+usersubst(const char *opts) {
+ char *s, *w;
+ char id[40];
+
+ 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 opts;
+}
+
+
+/*
+ * Return 0 for success (either mounted sth or -a and NOAUTO was given)
+ */
static int
-mount_all (string_list types)
-{
- struct mntent *fstab;
- struct mntent *mnt;
- string_list spec_list = NULL;
- string_list node_list = NULL;
+mount_one (const char *spec, const char *node, char *type, const char *opts,
+ char *cmdlineopts, int freq, int pass) {
int status;
+ int status2;
+ int specset = 0;
+ char *nspec;
+
+ /* Substitute values in opts, if required */
+ opts = usersubst(opts);
+
+ /* Merge the fstab and command line options. */
+ if (opts == NULL)
+ opts = cmdlineopts;
+ else if (cmdlineopts != NULL)
+ opts = xstrconcat3(opts, ",", cmdlineopts);
+
+ if (!strncmp(spec, "UUID=", 5)) {
+ nspec = get_spec_by_uuid(spec+5);
+ specset = 1;
+ } else if (!strncmp(spec, "LABEL=", 6)) {
+ nspec = get_spec_by_volume_label(spec+6);
+ specset = 2;
+ } else
+ nspec = 0; /* just for gcc */
+
+ if (specset) {
+ if (nspec) {
+ spec = nspec;
+ if (verbose > 1)
+ printf(_("mount: going to mount %s by %s\n"), spec,
+ (specset==1) ? _("UUID") : _("label"));
+ } else if(!all)
+ die (EX_USAGE, _("mount: no such partition found"));
+ /* if -a then we may be rescued by a noauto option */
+ }
- rewind (F_mtab);
-
- while ((mnt = getmntent (F_mtab)))
- if (matching_type (mnt->mnt_type, types)
- && !streq (mnt->mnt_dir, "/")
- && !streq (mnt->mnt_dir, "root"))
- {
- spec_list = cons (xstrdup (mnt->mnt_fsname), spec_list);
- node_list = cons (xstrdup (mnt->mnt_dir), node_list);
+ if (type == NULL && !bind) {
+ if (strchr (spec, ':') != NULL) {
+ type = "nfs";
+ if (verbose)
+ printf(_("mount: no type was given - "
+ "I'll assume nfs because of the colon\n"));
}
+ }
- status = 0;
- while ((fstab = getfsent ()) != NULL)
- if (matching_type (fstab->mnt_type, types)
- && !streq (fstab->mnt_dir, "/")
- && !streq (fstab->mnt_dir, "root"))
- if (mounted (fstab->mnt_fsname, fstab->mnt_dir, spec_list, node_list))
- {
- if (verbose)
- printf("mount: %s already mounted on %s\n",
- fstab->mnt_fsname, fstab->mnt_dir);
- }
- else
- status |= mount_one (fstab->mnt_fsname, fstab->mnt_dir,
- fstab->mnt_type, fstab->mnt_opts,
- fstab->mnt_freq, fstab->mnt_passno);
+ /*
+ * Try to mount the file system. When the exit status is EX_BG,
+ * we will retry in the background. Otherwise, we're done.
+ */
+ status = try_mount_one (spec, node, type, opts, freq, pass, 0, 0);
+ if (status != EX_BG)
+ return status;
+
+ /*
+ * Retry in the background.
+ */
+ printf (_("mount: backgrounding \"%s\"\n"), spec);
+ fflush( stdout ); /* prevent duplicate output */
+ if (fork() > 0)
+ return 0; /* parent returns "success" */
+ spec = xstrdup(spec); /* arguments will be destroyed */
+ node = xstrdup(node); /* by set_proc_name() */
+ type = xstrdup(type);
+ opts = xstrdup(opts);
+ set_proc_name (spec); /* make a nice "ps" listing */
+ status2 = try_mount_one (spec, node, type, opts, freq, pass, 1, 0);
+ if (verbose && status2)
+ printf (_("mount: giving up \"%s\"\n"), spec);
+ exit (0); /* child stops here */
+}
- return status;
+/* Check if an fsname/dir pair was already in the old mtab. */
+static int
+mounted (char *spec, char *node) {
+ struct mntentchn *mc, *mc0;
+ char *nspec = NULL;
+
+ if (!strncmp(spec, "UUID=", 5))
+ nspec = get_spec_by_uuid(spec+5);
+ else if (!strncmp(spec, "LABEL=", 6))
+ nspec = get_spec_by_volume_label(spec+6);
+
+ if (nspec)
+ spec = nspec;
+
+ spec = canonicalize (spec);
+ node = canonicalize (node);
+
+ 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))
+ return 1;
+ return 0;
}
-/* Create mtab with a root entry. */
-static void
-create_mtab (void)
-{
- struct mntent *fstab;
- struct mntent mnt;
- int flags;
- char *extra_opts;
+/* 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)
- if ((F_mtab = setmntent (MOUNTED, "a+")) == NULL)
- die (1, "mount: can't open %s for writing: %s", MOUNTED, strerror (errno));
-
- /* Find the root entry by looking it up in fstab, which might be wrong.
- We could statfs "/" followed by a slew of stats on /dev/ but then
- we'd have to unparse the mount options as well.... */
- if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root")))
- {
- parse_opts (xstrdup (fstab->mnt_opts), &flags, &extra_opts);
- mnt = *fstab;
- mnt.mnt_fsname = canonicalize (fstab->mnt_fsname);
- mnt.mnt_dir = "/";
- mnt.mnt_opts = fix_opts_string (flags, extra_opts);
+static int
+mount_all (string_list types, char *options) {
+ 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 (matching_type (mc->m.mnt_type, types)
+ && !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);
+ } else {
+ 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;
+ }
+#ifdef HAVE_NFS
+ if (strcmp(mc->m.mnt_type, "nfs") == 0) {
+ g = xstrdup(mc->m.mnt_fsname);
+ colon = strchr(g, ':');
+ if (colon)
+ *colon = '\0';
+ }
+#endif
+ }
+ 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);
+ }
+ }
- if (addmntent (F_mtab, &mnt) == 1)
- die (1, "mount: error writing %s: %s", MOUNTED, strerror (errno));
- }
- if (fchmod (fileno (F_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
- die (1, "mount: error changing mode of %s: %s", MOUNTED, strerror (errno));
- endmntent (F_mtab);
+ /* 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;
}
extern char version[];
-static struct option longopts[] =
-{
- { "all", 0, 0, 'a' },
- { "fake", 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' },
- { "types", 1, 0, 't' },
- { NULL, 0, 0, 0 }
+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' },
+ { "types", 1, 0, 't' },
+ { "bind", 0, 0, 128 },
+ { "replace", 0, 0, 129 },
+ { "after", 0, 0, 130 },
+ { "before", 0, 0, 131 },
+ { "over", 0, 0, 132 },
+ { NULL, 0, 0, 0 }
};
-const char *usage_string = "\
-usage: mount [-hV]\n\
- mount -a [-nfrvw] [-t vfstypes]\n\
- mount [-nfrvw] [-o options] special | node\n\
- mount [-nfrvw] [-t vfstype] [-o options] special node\n\
-";
-
+/* 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, "%s", usage_string);
- exit (n);
+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 : 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"
+ "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"
+ "Union or stack mounts are specified using one of\n"
+ " --replace, --after, --before, --over\n"
+ "Other options: [-nfFrsvw] [-o options].\n"
+ "For many more details, say man 8 mount .\n"
+ ));
+ unlock_mtab();
+ exit (n);
}
int
-main (int argc, char *argv[])
-{
- int c;
- char *options = NULL;
- string_list types = NULL;
- struct mntent *fs;
- char *spec;
- int result = 0;
- struct stat statbuf;
-
- while ((c = getopt_long (argc, argv, "afhnrvVwt:o:", longopts, NULL)) != EOF)
- switch (c)
- {
- case 'a': /* mount everything in fstab */
- ++all;
- break;
- case 'f': /* fake (don't actually do mount(2) call) */
- ++fake;
- break;
- case 'h': /* help */
- usage (stdout, 0);
- break;
- case 'n': /* mount without writing in /etc/mtab */
- ++nomtab;
- break;
- case 'r': /* mount readonly */
- ++readonly;
- readwrite = 0;
- break;
- case 'v': /* be chatty */
- ++verbose;
- break;
- case 'V': /* version */
- printf ("%s\n", version);
- exit (0);
- case 'w': /* mount read/write */
- ++readwrite;
- readonly = 0;
- break;
- case 't': /* specify file system types */
- types = parse_list (optarg);
- break;
- case 'o': /* specify mount options */
- options = optarg;
- break;
- case 0:
- break;
- case '?':
- default:
- usage (stderr, 1);
- break;
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc == 0)
- {
- if (options)
- usage (stderr, 1);
- if (!all)
- return print_all (types);
- }
+main (int argc, char *argv[]) {
+ int c, result = 0, specseen;
+ char *options = NULL, *spec, *node;
+ char *volumelabel = NULL;
+ char *uuid = NULL;
+ string_list types = NULL;
+ struct mntentchn *mc;
+ int fd;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /* 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);
+
+#ifdef DO_PS_FIDDLING
+ initproctitle(argc, argv);
+#endif
- if (getuid () != geteuid ())
- {
- suid = 1;
- if (types || options || readwrite || nomtab || all || fake || argc != 1)
- die (2, "mount: only root can do that");
- }
+ while ((c = getopt_long (argc, argv, "afFhlL:no:rsU:vVwt:",
+ longopts, NULL)) != EOF) {
+ switch (c) {
+ case 'a': /* mount everything in fstab */
+ ++all;
+ break;
+ case 'f': /* fake: don't actually call mount(2) */
+ ++fake;
+ break;
+ case 'F':
+ ++optfork;
+ break;
+ case 'h': /* help */
+ usage (stdout, 0);
+ break;
+ case 'l':
+ list_with_volumelabel = 1;
+ break;
+ case 'L':
+ volumelabel = optarg;
+ break;
+ case 'n': /* do not write /etc/mtab */
+ ++nomtab;
+ break;
+ case 'o': /* specify mount options */
+ if (options)
+ options = xstrconcat3(options, ",", optarg);
+ else
+ options = xstrdup(optarg);
+ break;
+ case 'r': /* mount readonly */
+ readonly = 1;
+ readwrite = 0;
+ break;
+ case 's': /* allow sloppy mount options */
+ sloppy = 1;
+ break;
+ case 't': /* specify file system types */
+ types = parse_list (optarg);
+ break;
+ case 'U':
+ uuid = optarg;
+ break;
+ case 'v': /* be chatty - more so if repeated */
+ ++verbose;
+ break;
+ case 'V': /* version */
+ printf ("mount: %s\n", version);
+ exit (0);
+ case 'w': /* mount read/write */
+ readwrite = 1;
+ readonly = 0;
+ break;
+ case 0:
+ break;
+
+ case 128: /* bind */
+ ++bind;
+ break;
+ case 129: /* replace */
+ mounttype = MS_REPLACE;
+ break;
+ case 130: /* after */
+ mounttype = MS_AFTER;
+ break;
+ case 131: /* before */
+ mounttype = MS_BEFORE;
+ break;
+ case 132: /* over */
+ mounttype = MS_OVER;
+ break;
+
+ case '?':
+ default:
+ usage (stderr, EX_USAGE);
+ }
+ }
- if (!nomtab)
- {
- lock_mtab ();
- if (stat(MOUNTED, &statbuf) < 0)
- create_mtab ();
- open_mtab ("a+");
- }
- else if (stat(MOUNTED, &statbuf) >= 0)
- open_mtab ("r");
+ argc -= optind;
+ argv += optind;
+ specseen = (uuid || volumelabel) ? 1 : 0; /* yes, .. i know */
- switch (argc)
- {
- case 0:
- /* mount -a */
- result = mount_all (types);
- break;
+ if (argc+specseen == 0 && !all) {
+ if (options || mounttype || bind)
+ usage (stderr, EX_USAGE);
+ return print_all (types);
+ }
- case 1:
- /* mount [-nfrvw] [-o options] special | node */
- if (types != NULL)
- usage (stderr, 1);
- /* Try to find the other pathname in fstab. */
- spec = canonicalize (*argv);
- if (!(fs = getmntfile (spec))
- && !(fs = getfsspec (spec)) && !(fs = getfsfile (spec)))
- die (2, "mount: can't find %s in %s or %s",
- spec, MOUNTED, _PATH_FSTAB);
- /* Merge the fstab and command line options. */
- if (options == NULL)
- options = fs->mnt_opts;
- else
- {
- char *tmp = xmalloc(strlen(fs->mnt_opts) + strlen(options) + 2);
+ if (getuid () != geteuid ()) {
+ suid = 1;
+ if (types || options || readwrite || nomtab || all || fake ||
+ bind || mounttype || (argc + specseen) != 1)
+ die (EX_USAGE, _("mount: only root can do that"));
+ }
- sprintf (tmp, "%s,%s", fs->mnt_opts, options);
- options = tmp;
+ if (!nomtab && mtab_does_not_exist()) {
+ if (verbose > 1)
+ printf(_("mount: no %s found - creating it..\n"),
+ MOUNTED);
+ create_mtab ();
}
- result = mount_one (xstrdup (fs->mnt_fsname), xstrdup (fs->mnt_dir),
- xstrdup (fs->mnt_type), options,
- fs->mnt_freq, fs->mnt_passno);
- break;
- case 2:
- /* mount [-nfrvw] [-t vfstype] [-o options] special node */
- if (types == NULL)
- result = mount_one (argv[0], argv[1], NULL, options, 0, 0);
- else if (cdr (types) == NULL)
- result = mount_one (argv[0], argv[1], car (types), options, 0, 0);
- else
- usage (stderr, 2);
- break;
+ if (specseen) {
+ if (uuid)
+ spec = get_spec_by_uuid(uuid);
+ else
+ spec = get_spec_by_volume_label(volumelabel);
+ if (!spec)
+ die (EX_USAGE, _("mount: no such partition found"));
+ if (verbose)
+ printf(_("mount: mounting %s\n"), spec);
+ } else
+ spec = NULL; /* just for gcc */
+
+ switch (argc+specseen) {
+ case 0:
+ /* mount -a */
+ result = mount_all (types, options);
+ if (result == 0 && verbose)
+ error(_("not mounted anything"));
+ break;
+
+ case 1:
+ /* mount [-nfrvw] [-o options] special | node */
+ if (types != NULL)
+ usage (stderr, EX_USAGE);
+ if (specseen) {
+ /* We know the device. Where shall we mount it? */
+ mc = (uuid ? getfsuuidspec (uuid)
+ : getfsvolspec (volumelabel));
+ if (mc == NULL)
+ mc = getfsspec (spec);
+ if (mc == NULL)
+ die (EX_USAGE,
+ _("mount: cannot find %s in %s"),
+ spec, _PATH_FSTAB);
+ mc->m.mnt_fsname = spec;
+ } else {
+ /* Try to find the other pathname in fstab. */
+ spec = canonicalize (*argv);
+ if ((mc = getfsspec (spec)) == NULL &&
+ (mc = getfsfile (spec)) == NULL &&
+ /* Try noncanonical name in fstab
+ perhaps /dev/cdrom or /dos is a symlink */
+ (mc = getfsspec (*argv)) == NULL &&
+ (mc = getfsfile (*argv)) == NULL &&
+ /* Try mtab - maybe this was a remount */
+ (mc = getmntfile (spec)) == NULL)
+ die (EX_USAGE,
+ _("mount: can't find %s in %s or %s"),
+ spec, _PATH_FSTAB, MOUNTED);
+ /* Earlier mtab was tried first, but this would
+ sometimes try the wrong mount in case mtab had
+ the root device entry wrong. */
+ }
+
+ 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 [-nfrvw] [-t vfstype] [-o options] special node */
+ if (specseen) {
+ /* we have spec already */
+ node = argv[0];
+ } else {
+ spec = argv[0];
+ node = argv[1];
+ }
+ if (types == NULL)
+ result = mount_one (spec, node, NULL, NULL,
+ options, 0, 0);
+ else if (cdr (types) == NULL)
+ result = mount_one (spec, node, car (types), NULL,
+ options, 0, 0);
+ else
+ usage (stderr, EX_USAGE);
+ break;
- default:
- usage (stderr, 2);
- }
-
- if (!nomtab)
- {
- endmntent (F_mtab);
- unlock_mtab ();
- }
+ default:
+ usage (stderr, EX_USAGE);
+ }
- exit (result);
+ if (result == EX_SOMEOK)
+ result = 0;
+ exit (result);
}