]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - mount/mount.c
Imported from util-linux-2.10s tarball.
[thirdparty/util-linux.git] / mount / mount.c
index b6ffbcd67fadd05cb817c634ed3ac16c3c43c2fd..39ccd3e634cc1fa7dc7339d5653fef14d4cc04b6 100644 (file)
@@ -2,12 +2,6 @@
  * 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))
@@ -342,393 +750,794 @@ mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
 
   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);
 }