Try to make a "type-punned pointer" warning go away for somebody on the
[oweals/busybox.git] / util-linux / mount.c
index f78786ebc012e73a288a110350f42fdd6d047939..f665a08758e2377809a04ba726448bba79c777f2 100644 (file)
  * Mini mount implementation for busybox
  *
  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * 3/21/1999   Charles P. Wright <cpwright@cpwright.com>
- *             searches through fstab when -a is passed
- *             will try mounting stuff with all fses when passed -t auto
- *
- * 1999-04-17  Dave Cinege...Rewrote -t auto. Fixed ro mtab.
- *
- * 1999-10-07  Erik Andersen <andersen@lineo.com>, <andersee@debian.org>.
- *              Rewrite of a lot of code. Removed mtab usage (I plan on
- *              putting it back as a compile-time option some time), 
- *              major adjustments to option parsing, and some serious 
- *              dieting all around.
- *
- * 1999-11-06  mtab suppport is back - andersee
- *
- * 2000-01-12   Ben Collins <bcollins@debian.org>, Borrowed utils-linux's
- *              mount to add loop support.
- *
- * 2000-04-30  Dave Cinege <dcinege@psychosis.com>
- *             Rewrote fstab while loop and lower mount section. Can now do
- *             single mounts from fstab. Can override fstab options for single
- *             mount. Common mount_one call for single mounts and 'all'. Fixed
- *             mtab updating and stale entries. Removed 'remount' default. 
- *     
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
-#include "busybox.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <mntent.h>
-#include <ctype.h>
-#if defined BB_FEATURE_USE_DEVPS_PATCH
-#include <linux/devmtab.h> /* For Erik's nifty devmtab device driver */
-#endif
+/* todo:
+ * bb_getopt_ulflags();
+ */
 
-enum {
-       MS_MGC_VAL = 0xc0ed0000, /* Magic number indicatng "new" flags */
-       MS_RDONLY = 1,      /* Mount read-only */
-       MS_NOSUID = 2,      /* Ignore suid and sgid bits */
-       MS_NODEV = 4,      /* Disallow access to device special files */
-       MS_NOEXEC = 8,      /* Disallow program execution */
-       MS_SYNCHRONOUS = 16,      /* Writes are synced at once */
-       MS_REMOUNT = 32,      /* Alter flags of a mounted FS */
-       MS_MANDLOCK = 64,      /* Allow mandatory locks on an FS */
-       S_QUOTA = 128,     /* Quota initialized for file/directory/symlink */
-       S_APPEND = 256,     /* Append-only file */
-       S_IMMUTABLE = 512,     /* Immutable file */
-       MS_NOATIME = 1024,    /* Do not update access times. */
-       MS_NODIRATIME = 2048,    /* Do not update directory access times */
-};
+/* Design notes: There is no spec for mount.  Remind me to write one.
 
+   mount_main() calls singlemount() which calls mount_it_now().
 
-#if defined BB_FEATURE_MOUNT_LOOP
-#include <fcntl.h>
-#include <sys/ioctl.h>
-static int use_loop = FALSE;
-#endif
+   mount_main() can loop through /etc/fstab for mount -a
+   singlemount() can loop through /etc/filesystems for fstype detection.
+   mount_it_now() does the actual mount.
+*/
 
-extern int mount (__const char *__special_file, __const char *__dir,
-                       __const char *__fstype, unsigned long int __rwflag,
-                       __const void *__data);
-extern int umount (__const char *__special_file);
-extern int umount2 (__const char *__special_file, int __flags);
+#include "busybox.h"
+#include <mntent.h>
 
+// These two aren't always defined in old headers
+#ifndef MS_BIND
+#define MS_BIND                4096
+#endif
+#ifndef MS_MOVE
+#define MS_MOVE                8192
+#endif
+#ifndef MS_SILENT
+#define MS_SILENT      32768
+#endif
 
-extern const char mtab_file[]; /* Defined in utility.c */
+// Not real flags, but we want to be able to check for this.
+#define MOUNT_NOAUTO    (1<<29)
+#define MOUNT_SWAP      (1<<30)
+/* Standard mount options (from -o options or --options), with corresponding
+ * flags */
 
-struct mount_options {
+struct {
        const char *name;
-       unsigned long and;
-       unsigned long or;
+       long flags;
+} static const mount_options[] = {
+       // NOP flags.
+
+       {"loop", 0},
+       {"defaults", 0},
+       {"quiet", 0},
+
+       // vfs flags
+
+       {"ro", MS_RDONLY},
+       {"rw", ~MS_RDONLY},
+       {"nosuid", MS_NOSUID},
+       {"suid", ~MS_NOSUID},
+       {"dev", ~MS_NODEV},
+       {"nodev", MS_NODEV},
+       {"exec", ~MS_NOEXEC},
+       {"noexec", MS_NOEXEC},
+       {"sync", MS_SYNCHRONOUS},
+       {"async", ~MS_SYNCHRONOUS},
+       {"atime", ~MS_NOATIME},
+       {"noatime", MS_NOATIME},
+       {"diratime", ~MS_NODIRATIME},
+       {"nodiratime", MS_NODIRATIME},
+       {"loud", ~MS_SILENT},
+
+       // action flags
+
+       {"remount", MS_REMOUNT},
+       {"bind", MS_BIND},
+       {"move", MS_MOVE},
+       {"noauto",MOUNT_NOAUTO},
+       {"swap",MOUNT_SWAP}
 };
 
-static const struct mount_options mount_options[] = {
-       {"async", ~MS_SYNCHRONOUS, 0},
-       {"atime", ~0, ~MS_NOATIME},
-       {"defaults", ~0, 0},
-       {"dev", ~MS_NODEV, 0},
-       {"diratime", ~0, ~MS_NODIRATIME},
-       {"exec", ~MS_NOEXEC, 0},
-       {"noatime", ~0, MS_NOATIME},
-       {"nodev", ~0, MS_NODEV},
-       {"nodiratime", ~0, MS_NODIRATIME},
-       {"noexec", ~0, MS_NOEXEC},
-       {"nosuid", ~0, MS_NOSUID},
-       {"remount", ~0, MS_REMOUNT},
-       {"ro", ~0, MS_RDONLY},
-       {"rw", ~MS_RDONLY, 0},
-       {"suid", ~MS_NOSUID, 0},
-       {"sync", ~0, MS_SYNCHRONOUS},
-       {0, 0, 0}
-};
+/* Append mount options to string */
+static void append_mount_options(char **oldopts, char *newopts)
+{
+       if(*oldopts && **oldopts) {
+               char *temp=xasprintf("%s,%s",*oldopts,newopts);
+               free(*oldopts);
+               *oldopts=temp;
+       } else {
+               if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
+               *oldopts = xstrdup(newopts);
+       }
+}
 
-static int
-do_mount(char *specialfile, char *dir, char *filesystemtype,
-                long flags, void *string_flags, int useMtab, int fakeIt,
-                char *mtab_opts)
+/* Use the mount_options list to parse options into flags.
+ * Return list of unrecognized options in *strflags if strflags!=NULL */
+static int parse_mount_options(char *options, char **unrecognized)
 {
-       int status = 0;
-#if defined BB_FEATURE_MOUNT_LOOP
-       char *lofile = NULL;
-#endif
+       int flags = MS_SILENT;
 
-       if (fakeIt == FALSE)
-       {
-#if defined BB_FEATURE_MOUNT_LOOP
-               if (use_loop==TRUE) {
-                       int loro = flags & MS_RDONLY;
-                       char *lofile = specialfile;
-
-                       specialfile = find_unused_loop_device();
-                       if (specialfile == NULL) {
-                               error_msg_and_die("Could not find a spare loop device\n");
-                       }
-                       if (set_loop(specialfile, lofile, 0, &loro)) {
-                               error_msg_and_die("Could not setup loop device\n");
-                       }
-                       if (!(flags & MS_RDONLY) && loro) {     /* loop is ro, but wanted rw */
-                               error_msg("WARNING: loop device is read-only\n");
-                               flags &= ~MS_RDONLY;
+       // Loop through options
+       for (;;) {
+               int i;
+               char *comma = strchr(options, ',');
+
+               if (comma) *comma = 0;
+
+               // Find this option in mount_options
+               for (i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
+                       if (!strcasecmp(mount_options[i].name, options)) {
+                               long fl = mount_options[i].flags;
+                               if(fl < 0) flags &= fl;
+                               else flags |= fl;
+                               break;
                        }
                }
-#endif
-               status = mount(specialfile, dir, filesystemtype, flags, string_flags);
-               if (errno == EROFS) {
-                       error_msg("%s is write-protected, mounting read-only\n", specialfile);
-                       status = mount(specialfile, dir, filesystemtype, flags |= MS_RDONLY, string_flags);
+               // If unrecognized not NULL, append unrecognized mount options */
+               if (unrecognized
+                               && i == (sizeof(mount_options) / sizeof(*mount_options)))
+               {
+                       // Add it to strflags, to pass on to kernel
+                       i = *unrecognized ? strlen(*unrecognized) : 0;
+                       *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
+
+                       // Comma separated if it's not the first one
+                       if (i) (*unrecognized)[i++] = ',';
+                       strcpy((*unrecognized)+i, options);
                }
+
+               // Advance to next option, or finish
+               if(comma) {
+                       *comma = ',';
+                       options = ++comma;
+               } else break;
        }
 
+       return flags;
+}
+
+// Return a list of all block device backed filesystems
+
+static llist_t *get_block_backed_filesystems(void)
+{
+       char *fs, *buf,
+                *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
+       llist_t *list = 0;
+       int i;
+       FILE *f;
 
-       /* If the mount was sucessful, do anything needed, then return TRUE */
-       if (status == 0 || fakeIt==TRUE) {
+       for(i = 0; filesystems[i]; i++) {
+               if(!(f = fopen(filesystems[i], "r"))) continue;
 
-#if defined BB_MTAB
-               if (useMtab == TRUE) {
-                       erase_mtab(specialfile);        // Clean any stale entries
-                       write_mtab(specialfile, dir, filesystemtype, flags, mtab_opts);
+               for(fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
+                       free(buf))
+               {
+                       if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
+
+                       while(isspace(*fs)) fs++;
+                       if(*fs=='#' || *fs=='*') continue;
+                       if(!*fs) continue;
+
+                       llist_add_to_end(&list,xstrdup(fs));
                }
-#endif
-               return (TRUE);
+               if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
        }
 
-       /* Bummer.  mount failed.  Clean up */
-#if defined BB_FEATURE_MOUNT_LOOP
-       if (lofile != NULL) {
-               del_loop(specialfile);
-       }
-#endif
+       return list;
+}
 
-       if (errno == EPERM) {
-               error_msg_and_die("permission denied. Are you root?\n");
-       }
+llist_t *fslist = 0;
 
-       return (FALSE);
+#if ENABLE_FEATURE_CLEAN_UP
+static void delete_block_backed_filesystems(void)
+{
+       llist_free(fslist, free);
 }
+#else
+void delete_block_backed_filesystems(void);
+#endif
 
+#if ENABLE_FEATURE_MTAB_SUPPORT
+static int useMtab;
+static int fakeIt;
+#else
+#define useMtab 0
+#define fakeIt 0
+#endif
 
+// Perform actual mount of specific filesystem at specific location.
 
-/* Seperate standard mount options from the nonstandard string options */
-static void
-parse_mount_options(char *options, int *flags, char *strflags)
+static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
 {
-       while (options) {
-               int gotone = FALSE;
-               char *comma = strchr(options, ',');
-               const struct mount_options *f = mount_options;
-
-               if (comma)
-                       *comma = '\0';
+       int rc;
 
-               while (f->name != 0) {
-                       if (strcasecmp(f->name, options) == 0) {
+       if (fakeIt) { return 0; }
 
-                               *flags &= f->and;
-                               *flags |= f->or;
-                               gotone = TRUE;
-                               break;
-                       }
-                       f++;
-               }
-#if defined BB_FEATURE_MOUNT_LOOP
-               if (gotone == FALSE && !strcasecmp("loop", options)) {  /* loop device support */
-                       use_loop = TRUE;
-                       gotone = TRUE;
-               }
-#endif
-               if (*strflags && strflags != '\0' && gotone == FALSE) {
-                       char *temp = strflags;
+       // Mount, with fallback to read-only if necessary.
 
-                       temp += strlen(strflags);
-                       *temp++ = ',';
-                       *temp++ = '\0';
-               }
-               if (gotone == FALSE)
-                       strcat(strflags, options);
-               if (comma) {
-                       *comma = ',';
-                       options = ++comma;
-               } else {
+       for(;;) {
+               rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
+                               vfsflags, filteropts);
+               if(!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
                        break;
-               }
+               bb_error_msg("%s is write-protected, mounting read-only",
+                               mp->mnt_fsname);
+               vfsflags |= MS_RDONLY;
+       }
+
+       // Abort entirely if permission denied.
+
+       if (rc && errno == EPERM)
+               bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+       /* If the mount was successful, and we're maintaining an old-style
+        * mtab file by hand, add the new entry to it now. */
+
+       if(ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc) {
+               FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
+               int i;
+
+               if(!mountTable)
+                       bb_error_msg("No %s\n",bb_path_mtab_file);
+
+               // Add vfs string flags
+
+               for(i=0; mount_options[i].flags != MS_REMOUNT; i++)
+                       if (mount_options[i].flags > 0)
+                               append_mount_options(&(mp->mnt_opts),
+// Shut up about the darn const.  It's not important.  I don't care.
+                                               (char *)mount_options[i].name);
+
+               // Remove trailing / (if any) from directory we mounted on
+
+               i = strlen(mp->mnt_dir);
+               if(i>1 && mp->mnt_dir[i-1] == '/') mp->mnt_dir[i-1] = 0;
+
+               // Write and close.
+
+               if(!mp->mnt_type || !*mp->mnt_type) mp->mnt_type="--bind";
+               addmntent(mountTable, mp);
+               endmntent(mountTable);
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       if(strcmp(mp->mnt_type,"--bind")) mp->mnt_type = 0;
        }
+
+       return rc;
 }
 
-int
-mount_one(char *blockDevice, char *directory, char *filesystemType,
-                 unsigned long flags, char *string_flags, int useMtab, int fakeIt,
-                 char *mtab_opts, int whineOnErrors)
+
+// Mount one directory.  Handles NFS, loopback, autobind, and filesystem type
+// detection.  Returns 0 for success, nonzero for failure.
+
+static int singlemount(struct mntent *mp, int ignore_busy)
 {
-       int status = 0;
+       int rc = -1, vfsflags;
+       char *loopFile = 0, *filteropts = 0;
+       llist_t *fl = 0;
+       struct stat st;
 
-#if defined BB_FEATURE_USE_PROCFS
-       char buf[255];
-       if (strcmp(filesystemType, "auto") == 0) {
-               FILE *f = fopen("/proc/filesystems", "r");
+       vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
 
-               if (f == NULL)
-                       return (FALSE);
+       // Treat fstype "auto" as unspecified.
 
-               while (fgets(buf, sizeof(buf), f) != NULL) {
-                       filesystemType = buf;
-                       if (*filesystemType == '\t') {  // Not a nodev filesystem
+       if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
 
-                               // Add NULL termination to each line
-                               while (*filesystemType && *filesystemType != '\n')
-                                       filesystemType++;
-                               *filesystemType = '\0';
+       // Might this be an NFS filesystem?
 
-                               filesystemType = buf;
-                               filesystemType++;       // hop past tab
+       if (ENABLE_FEATURE_MOUNT_NFS &&
+               (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
+               strchr(mp->mnt_fsname, ':') != NULL)
+       {
+               if (nfsmount(mp->mnt_fsname, mp->mnt_dir, &vfsflags, &filteropts, 1)) {
+                       bb_perror_msg("nfsmount failed");
+                       goto report_error;
+               } else {
+                       // Strangely enough, nfsmount() doesn't actually mount() anything.
+                       mp->mnt_type = "nfs";
+                       rc = mount_it_now(mp, vfsflags, filteropts);
+                       if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
+                       
+                       goto report_error;
+               }
+       }
 
-                               status = do_mount(blockDevice, directory, filesystemType,
-                                                                 flags | MS_MGC_VAL, string_flags,
-                                                                 useMtab, fakeIt, mtab_opts);
-                               if (status == TRUE)
+       // Look at the file.  (Not found isn't a failure for remount, or for
+       // a synthetic filesystem like proc or sysfs.)
+
+       if (lstat(mp->mnt_fsname, &st));
+       else if (!(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
+               // Do we need to allocate a loopback device for it?
+
+               if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
+                       loopFile = bb_simplify_path(mp->mnt_fsname);
+                       mp->mnt_fsname = 0;
+                       switch(set_loop(&(mp->mnt_fsname), loopFile, 0)) {
+                               case 0:
+                               case 1:
                                        break;
+                               default:
+                                       bb_error_msg( errno == EPERM || errno == EACCES
+                                               ? bb_msg_perm_denied_are_you_root
+                                               : "Couldn't setup loop device");
+                                       return errno;
                        }
+
+               // Autodetect bind mounts
+
+               } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) vfsflags |= MS_BIND;
+       }
+
+       /* If we know the fstype (or don't need to), jump straight
+        * to the actual mount. */
+
+       if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
+               rc = mount_it_now(mp, vfsflags, filteropts);
+
+       // Loop through filesystem types until mount succeeds or we run out
+
+       else {
+
+               /* Initialize list of block backed filesystems.  This has to be
+                * done here so that during "mount -a", mounts after /proc shows up
+                * can autodetect. */
+
+               if (!fslist) {
+                       fslist = get_block_backed_filesystems();
+                       if (ENABLE_FEATURE_CLEAN_UP && fslist)
+                               atexit(delete_block_backed_filesystems);
                }
-               fclose(f);
-       } else
-#endif
-#if defined BB_FEATURE_USE_DEVPS_PATCH
-       if (strcmp(filesystemType, "auto") == 0) {
-               int fd, i, numfilesystems;
-               char device[] = "/dev/mtab";
-               struct k_fstype *fslist;
-
-               /* open device */ 
-               fd = open(device, O_RDONLY);
-               if (fd < 0)
-                       perror_msg_and_die("open failed for `%s'", device);
-
-               /* How many filesystems?  We need to know to allocate enough space */
-               numfilesystems = ioctl (fd, DEVMTAB_COUNT_FILESYSTEMS);
-               if (numfilesystems<0)
-                       perror_msg_and_die("\nDEVMTAB_COUNT_FILESYSTEMS");
-               fslist = (struct k_fstype *) xcalloc ( numfilesystems, sizeof(struct k_fstype));
-
-               /* Grab the list of available filesystems */
-               status = ioctl (fd, DEVMTAB_GET_FILESYSTEMS, fslist);
-               if (status<0)
-                       perror_msg_and_die("\nDEVMTAB_GET_FILESYSTEMS");
-
-               /* Walk the list trying to mount filesystems 
-                * that do not claim to be nodev filesystems */
-               for( i = 0 ; i < numfilesystems ; i++) {
-                       if (fslist[i].mnt_nodev)
-                               continue;
-                       status = do_mount(blockDevice, directory, fslist[i].mnt_type,
-                                                         flags | MS_MGC_VAL, string_flags,
-                                                         useMtab, fakeIt, mtab_opts);
-                       if (status == TRUE)
-                               break;
+
+               for (fl = fslist; fl; fl = fl->link) {
+                       mp->mnt_type = fl->data;
+
+                       if (!(rc = mount_it_now(mp,vfsflags, filteropts))) break;
+
+                       mp->mnt_type = 0;
                }
-               free( fslist);
-               close(fd);
-       } else
-#endif
-       {
-               status = do_mount(blockDevice, directory, filesystemType,
-                                                 flags | MS_MGC_VAL, string_flags, useMtab,
-                                                 fakeIt, mtab_opts);
        }
 
-       if (status == FALSE) {
-               if (whineOnErrors == TRUE) {
-                       perror_msg("Mounting %s on %s failed", blockDevice, directory);
+       if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
+
+       // If mount failed, clean up loop file (if any).
+
+       if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
+               del_loop(mp->mnt_fsname);
+               if (ENABLE_FEATURE_CLEAN_UP) {
+                       free(loopFile);
+                       free(mp->mnt_fsname);
                }
-               return (FALSE);
        }
-       return (TRUE);
+report_error:
+       if (rc && errno == EBUSY && ignore_busy) rc = 0;
+       if (rc < 0)
+               bb_perror_msg("Mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
+
+       return rc;
 }
 
-extern int mount_main(int argc, char **argv)
+// Parse options, if necessary parse fstab/mtab, and call singlemount for
+// each directory to be mounted.
+
+int mount_main(int argc, char **argv)
 {
-       char string_flags_buf[1024] = "";
-       char *string_flags = string_flags_buf;
-       char *extra_opts = string_flags_buf;
-       int flags = 0;
-       char *filesystemType = "auto";
-       char *device = NULL;
-       char *directory = NULL;
-       int all = FALSE;
-       int fakeIt = FALSE;
-       int useMtab = TRUE;
-       int i;
-       int rc = EXIT_FAILURE;
-       int fstabmount = FALSE; 
-
-#if defined BB_FEATURE_USE_DEVPS_PATCH
-       if (argc == 1) {
-               int fd, i, numfilesystems;
-               char device[] = "/dev/mtab";
-               struct k_mntent *mntentlist;
-
-               /* open device */ 
-               fd = open(device, O_RDONLY);
-               if (fd < 0)
-                       perror_msg_and_die("open failed for `%s'", device);
-
-               /* How many mounted filesystems?  We need to know to 
-                * allocate enough space for later... */
-               numfilesystems = ioctl (fd, DEVMTAB_COUNT_MOUNTS);
-               if (numfilesystems<0)
-                       perror_msg_and_die( "\nDEVMTAB_COUNT_MOUNTS");
-               mntentlist = (struct k_mntent *) xcalloc ( numfilesystems, sizeof(struct k_mntent));
-               
-               /* Grab the list of mounted filesystems */
-               if (ioctl (fd, DEVMTAB_GET_MOUNTS, mntentlist)<0)
-                       perror_msg_and_die( "\nDEVMTAB_GET_MOUNTS");
-
-               for( i = 0 ; i < numfilesystems ; i++) {
-                       printf( "%s %s %s %s %d %d\n", mntentlist[i].mnt_fsname,
-                                       mntentlist[i].mnt_dir, mntentlist[i].mnt_type, 
-                                       mntentlist[i].mnt_opts, mntentlist[i].mnt_freq, 
-                                       mntentlist[i].mnt_passno);
-               }
-#ifdef BB_FEATURE_CLEAN_UP
-               /* Don't bother to close files or free memory.  Exit 
-                * does that automagically, so we can save a few bytes */
-               free( mntentlist);
-               close(fd);
-#endif
-               return EXIT_SUCCESS;
+       char *cmdopts = xstrdup(""), *fstabname, *fstype=0, *storage_path=0;
+       FILE *fstab;
+       int i, opt, all = FALSE, rc = 0;
+       struct mntent mtpair[2], *mtcur = mtpair;
+
+       /* parse long options, like --bind and --move.  Note that -o option
+        * and --option are synonymous.  Yes, this means --remount,rw works. */
+
+       for (i = opt = 0; i < argc; i++) {
+               if (argv[i][0] == '-' && argv[i][1] == '-') {
+                       append_mount_options(&cmdopts,argv[i]+2);
+               } else argv[opt++] = argv[i];
        }
-#else
-       if (argc == 1) {
-               FILE *mountTable = setmntent(mtab_file, "r");
-
-               if (mountTable) {
-                       struct mntent *m;
-
-                       while ((m = getmntent(mountTable)) != 0) {
-                               char *blockDevice = m->mnt_fsname;
-                               if (strcmp(blockDevice, "/dev/root") == 0) {
-                                       find_real_root_device_name( blockDevice);
-                               }
-                               printf("%s on %s type %s (%s)\n", blockDevice, m->mnt_dir,
-                                          m->mnt_type, m->mnt_opts);
-                       }
-                       endmntent(mountTable);
-               } else {
-                       perror_msg_and_die("%s", mtab_file);
+       argc = opt;
+
+       // Parse remaining options
+
+       while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) {
+               switch (opt) {
+                       case 'o':
+                               append_mount_options(&cmdopts, optarg);
+                               break;
+                       case 't':
+                               fstype = optarg;
+                               break;
+                       case 'r':
+                               append_mount_options(&cmdopts, "ro");
+                               break;
+                       case 'w':
+                               append_mount_options(&cmdopts, "rw");
+                               break;
+                       case 'a':
+                               all = TRUE;
+                               break;
+                       case 'n':
+                               USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;)
+                               break;
+                       case 'f':
+                               USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;)
+                               break;
+                       case 'v':
+                               break;          // ignore -v
+                       default:
+                               bb_show_usage();
                }
-               return EXIT_SUCCESS;
        }
-#endif
 
-       /* Parse options */
-       i = --argc;
-       argv++;
-       while (i > 0 && **argv) {
-               if (**argv == '-') {
-                       char *opt = *argv;
-
-                       while (i > 0 && *++opt)
-                               switch (*opt) {
-                               case 'o':
-                                       if (--i == 0) {
-                                               goto goodbye;
-                                       }
-                                       parse_mount_options(*(++argv), &flags, string_flags);
-                                       break;
-                               case 'r':
-                                       flags |= MS_RDONLY;
-                                       break;
-                               case 't':
-                                       if (--i == 0) {
-                                               goto goodbye;
-                                       }
-                                       filesystemType = *(++argv);
-                                       break;
-                               case 'w':
-                                       flags &= ~MS_RDONLY;
-                                       break;
-                               case 'a':
-                                       all = TRUE;
-                                       break;
-                               case 'f':
-                                       fakeIt = TRUE;
-                                       break;
-#ifdef BB_MTAB
-                               case 'n':
-                                       useMtab = FALSE;
-                                       break;
-#endif
-                               case 'v':
-                                       break; /* ignore -v */
-                               case 'h':
-                               case '-':
-                                       goto goodbye;
-                               }
-               } else {
-                       if (device == NULL)
-                               device = *argv;
-                       else if (directory == NULL)
-                               directory = *argv;
-                       else {
-                               goto goodbye;
+       // Three or more non-option arguments?  Die with a usage message.
+
+       if (optind-argc>2) bb_show_usage();
+
+       // If we have no arguments, show currently mounted filesystems
+
+       if (optind == argc) {
+               if (!all) {
+                       FILE *mountTable = setmntent(bb_path_mtab_file, "r");
+
+                       if(!mountTable) bb_error_msg_and_die("No %s",bb_path_mtab_file);
+
+                       while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
+                                                               sizeof(bb_common_bufsiz1)))
+                       {
+                               // Don't show rootfs.
+                               if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
+
+                               if (!fstype || !strcmp(mtpair->mnt_type, fstype))
+                                       printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
+                                                       mtpair->mnt_dir, mtpair->mnt_type,
+                                                       mtpair->mnt_opts);
                        }
+                       if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
+                       return EXIT_SUCCESS;
                }
-               i--;
-               argv++;
        }
 
-       if (all == TRUE || directory == NULL) {
-               struct mntent *m;
-               FILE *f = setmntent("/etc/fstab", "r");
-               fstabmount = TRUE;
+       // When we have two arguments, the second is the directory and we can
+       // skip looking at fstab entirely.  We can always abspath() the directory
+       // argument when we get it.
+
+       if (optind+2 == argc) {
+               mtpair->mnt_fsname = argv[optind];
+               mtpair->mnt_dir = argv[optind+1];
+               mtpair->mnt_type = fstype;
+               mtpair->mnt_opts = cmdopts;
+               rc = singlemount(mtpair, 0);
+               goto clean_up;
+       }
 
-               if (f == NULL)
-                       perror_msg_and_die( "\nCannot read /etc/fstab");
+       // If we have at least one argument, it's the storage location
 
-               while ((m = getmntent(f)) != NULL) {
-                       if (all == FALSE && directory == NULL && (
-                               (strcmp(device, m->mnt_fsname) != 0) &&
-                               (strcmp(device, m->mnt_dir) != 0) ) ) {
-                               continue;
-                       }
-                       
-                       if (all == TRUE && (                            // If we're mounting 'all'
-                               (strstr(m->mnt_opts, "noauto")) ||      // and the file system isn't noauto,
-                               (strstr(m->mnt_type, "swap")) ||        // and isn't swap or nfs, then mount it
-                               (strstr(m->mnt_type, "nfs")) ) ) {
-                               continue;
-                       }
-                       
-                       if (all == TRUE || flags == 0) {        // Allow single mount to override fstab flags
-                               flags = 0;
-                               *string_flags = '\0';
-                               parse_mount_options(m->mnt_opts, &flags, string_flags);
+       if (optind < argc) storage_path = bb_simplify_path(argv[optind]);
+
+       // Open either fstab or mtab
+
+       if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
+               fstabname = (char *)bb_path_mtab_file;  // Again with the evil const.
+       else fstabname="/etc/fstab";
+
+       if (!(fstab=setmntent(fstabname,"r")))
+               bb_perror_msg_and_die("Cannot read %s",fstabname);
+
+       // Loop through entries until we find what we're looking for.
+
+       memset(mtpair,0,sizeof(mtpair));
+       for (;;) {
+               struct mntent *mtnext = mtpair + (mtcur==mtpair ? 1 : 0);
+
+               // Get next fstab entry
+
+               if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
+                                       + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
+                               sizeof(bb_common_bufsiz1)/2))
+               {
+                       // Were we looking for something specific?
+
+                       if (optind != argc) {
+
+                               // If we didn't find anything, complain.
+
+                               if (!mtnext->mnt_fsname)
+                                       bb_error_msg_and_die("Can't find %s in %s",
+                                               argv[optind], fstabname);
+
+                               // Mount the last thing we found.
+
+                               mtcur = mtnext;
+                               mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+                               append_mount_options(&(mtcur->mnt_opts),cmdopts);
+                               rc = singlemount(mtcur, 0);
+                               free(mtcur->mnt_opts);
                        }
-                       
-                       device = strdup(m->mnt_fsname);
-                       directory = strdup(m->mnt_dir);
-                       filesystemType = strdup(m->mnt_type);
-singlemount:                   
-                       rc = EXIT_SUCCESS;
-#ifdef BB_NFSMOUNT
-                       if (strchr(device, ':') != NULL)
-                               filesystemType = "nfs";
-                       if (strcmp(filesystemType, "nfs") == 0) {
-                               rc = nfsmount (device, directory, &flags,
-                                       &extra_opts, &string_flags, 1);
-                               if ( rc != 0) {
-                                       perror_msg_and_die("nfsmount failed");  
-                                       rc = EXIT_FAILURE;
-                               }
+                       goto clean_up;
+               }
+
+               /* If we're trying to mount something specific and this isn't it,
+                * skip it.  Note we must match both the exact text in fstab (ala
+                * "proc") or a full path from root */
+
+               if (optind != argc) {
+
+                       // Is this what we're looking for?
+
+                       if(strcmp(argv[optind],mtcur->mnt_fsname) &&
+                          strcmp(storage_path,mtcur->mnt_fsname) &&
+                          strcmp(argv[optind],mtcur->mnt_dir) &&
+                          strcmp(storage_path,mtcur->mnt_dir)) continue;
+
+                       // Remember this entry.  Something later may have overmounted
+                       // it, and we want the _last_ match.
+
+                       mtcur = mtnext;
+
+               // If we're mounting all.
+
+               } else {
+
+                       // Do we need to match a filesystem type?
+                       if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
+
+                       // Skip noauto and swap anyway.
+
+                       if (parse_mount_options(mtcur->mnt_opts,0)
+                               & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
+
+                       // Mount this thing.
+
+                       if (singlemount(mtcur, 1)) {
+                               /* Count number of failed mounts */
+                               rc++;
                        }
-#endif
-                       if (!mount_one(device, directory, filesystemType, flags,
-                                       string_flags, useMtab, fakeIt, extra_opts, TRUE))
-                               rc = EXIT_FAILURE;
-                               
-                       if (all == FALSE)
-                               break;
                }
-               if (fstabmount == TRUE)
-                       endmntent(f);
-                       
-               if (all == FALSE && fstabmount == TRUE && directory == NULL)
-                       fprintf(stderr, "Can't find %s in /etc/fstab\n", device);
-       
-               exit(rc);
        }
-       
-       goto singlemount;
-       
-goodbye:
-       usage(mount_usage);
+       if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
+
+clean_up:
+
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(storage_path);
+               free(cmdopts);
+       }
+
+       return rc;
 }