+/* vi: set sw=4 ts=4: */
/*
- 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.
+ * 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>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* todo:
+ * bb_getopt_ulflags();
+ */
+
+/* Design notes: There is no spec for mount. Remind me to write one.
+
+ mount_main() calls singlemount() which calls mount_it_now().
+
+ 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.
*/
-#include "internal.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
+#include "busybox.h"
#include <mntent.h>
-#include <sys/mount.h>
-#include <ctype.h>
-
-const char mount_usage[] = "mount\n"
-"\t\tmount [flags] special-device directory\n"
-"\n"
-"Flags:\n"
-"\t-a:\tMount all file systems in fstab.\n"
-"\t-f:\t\"Fake\" mount. Add entry to mount table but don't mount it.\n"
-"\t-n:\tDon't write a mount table entry.\n"
-"\t-o option:\tOne of many filesystem options, listed below.\n"
-"\t-r:\tMount the filesystem read-only.\n"
-"\t-t filesystem-type:\tSpecify the filesystem type.\n"
-"\t-w:\tMount for reading and writing (default).\n"
-"\n"
-"Options for use with the \"-o\" flag:\n"
-"\tasync / sync:\tWrites are asynchronous / synchronous.\n"
-"\tdev / nodev:\tAllow use of special device files / disallow them.\n"
-"\texec / noexec:\tAllow use of executable files / disallow them.\n"
-"\tsuid / nosuid:\tAllow set-user-id-root programs / disallow them.\n"
-"\tremount: Re-mount a currently-mounted filesystem, changing its flags.\n"
-"\tro / rw: Mount for read-only / read-write.\n"
-"\t"
-"There are EVEN MORE flags that are specific to each filesystem.\n"
-"You'll have to see the written documentation for those.\n";
-
-struct mount_options {
- const char * name;
- unsigned long and;
- unsigned long or;
-};
-static const struct mount_options mount_options[] = {
- { "async", ~MS_SYNCHRONOUS,0 },
- { "defaults", ~0, 0 },
- { "dev", ~MS_NODEV, 0 },
- { "exec", ~MS_NOEXEC, 0 },
- { "nodev", ~0, MS_NODEV },
- { "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 }
+// 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
+
+// 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 {
+ const char *name;
+ 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 void
-show_flags(unsigned long flags, char * buffer)
+/* Append mount options to string */
+static void append_mount_options(char **oldopts, char *newopts)
{
- const struct mount_options * f = mount_options;
- while ( f->name ) {
- if ( flags & f->and ) {
- int length = strlen(f->name);
- memcpy(buffer, f->name, length);
- buffer += length;
- *buffer++ = ',';
- *buffer = '\0';
- }
- f++;
+ 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 void
-one_option(
- char * option
-,unsigned long * flags
-,char * data)
+/* 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)
{
- const struct mount_options * f = mount_options;
+ int flags = MS_SILENT;
+
+ // 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;
+ }
+ }
+ // 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);
- while ( f->name != 0 ) {
- if ( strcasecmp(f->name, option) == 0 ) {
- *flags &= f->and;
- *flags |= f->or;
- return;
+ // Comma separated if it's not the first one
+ if (i) (*unrecognized)[i++] = ',';
+ strcpy((*unrecognized)+i, options);
}
- f++;
- }
- if ( *data ) {
- data += strlen(data);
- *data++ = ',';
+
+ // Advance to next option, or finish
+ if(comma) {
+ *comma = ',';
+ options = ++comma;
+ } else break;
}
- strcpy(data, option);
+
+ return flags;
}
-static void
-parse_mount_options(
- char * options
-,unsigned long * flags
-,char * data)
+// Return a list of all block device backed filesystems
+
+static llist_t *get_block_backed_filesystems(void)
{
- while ( *options ) {
- char * comma = strchr(options, ',');
- if ( comma )
- *comma = '\0';
- one_option(options, flags, data);
- if ( comma ) {
- *comma = ',';
- options = ++comma;
+ char *fs, *buf,
+ *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
+ llist_t *list = 0;
+ int i;
+ FILE *f;
+
+ for(i = 0; filesystems[i]; i++) {
+ if(!(f = fopen(filesystems[i], "r"))) continue;
+
+ 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));
}
- else
- break;
+ if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
}
+
+ return list;
}
-int
-mount_one(
- char * blockDevice
-,char * directory
-,char * filesystemType
-,unsigned long flags
-,char * string_flags
-,int noMtab
-,int fake)
+llist_t *fslist = 0;
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void delete_block_backed_filesystems(void)
{
- int error = 0;
- int status = 0;
-
- char buf[255];
-
- if (!fake) {
- if (*filesystemType == 'a') { //Will fail on real FS starting with 'a'
-
- FILE *f = fopen("/proc/filesystems", "r");
-
- if (f == NULL) return 1;
-
- while (fgets(buf, sizeof(buf), f) != NULL) {
- filesystemType = buf;
- if (*filesystemType == '\t') { // Not a nodev filesystem
-
- while (*filesystemType && *filesystemType != '\n') filesystemType++;
- *filesystemType = '\0';
-
- filesystemType = buf;
- filesystemType++; //hop past tab
-
- status = mount(blockDevice, directory, filesystemType,
- flags|MS_MGC_VAL ,string_flags);
- error = errno;
-
- if (status == 0) break;
- }
- }
- fclose(f);
- } else {
+ llist_free(fslist, free);
+}
+#else
+void delete_block_backed_filesystems(void);
+#endif
- status = mount( blockDevice, directory, filesystemType,
- flags|MS_MGC_VAL ,string_flags);
- error = errno;
- }
+#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.
+
+static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
+{
+ int rc;
+
+ if (fakeIt) { return 0; }
+
+ // Mount, with fallback to read-only if necessary.
+
+ 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;
}
- if ( status == 0 ) {
- char * s = &string_flags[strlen(string_flags)];
- FILE * mountTable;
- if ( s != string_flags ) {
- *s++ = ',';
- show_flags(flags, s);
- }
- if ( !noMtab && (mountTable = setmntent("/etc/mtab", "a+")) ) {
- int length = strlen(directory);
- struct mntent m;
+ // Abort entirely if permission denied.
- if ( length > 1 && directory[length - 1] == '/' )
- directory[length - 1] = '\0';
+ if (rc && errno == EPERM)
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
- if ( filesystemType == 0 ) {
- struct mntent * p
- = findMountPoint(blockDevice, "/proc/mounts");
+ /* If the mount was successful, and we're maintaining an old-style
+ * mtab file by hand, add the new entry to it now. */
- if ( p && p->mnt_type )
- filesystemType = p->mnt_type;
- }
- m.mnt_fsname = blockDevice;
- m.mnt_dir = directory;
- m.mnt_type = filesystemType ? filesystemType : "default";
-
- if (*string_flags) {
- m.mnt_opts = string_flags;
- } else {
- if ( (flags | MS_RDONLY) == flags )
- m.mnt_opts = "ro";
- else
- m.mnt_opts = "rw";
- }
+ 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;
+}
+
+
+// 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 rc = -1, vfsflags;
+ char *loopFile = 0, *filteropts = 0;
+ llist_t *fl = 0;
+ struct stat st;
+
+ vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
+
+ // Treat fstype "auto" as unspecified.
+
+ if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
+
+ // Might this be an NFS filesystem?
+
+ 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);
- m.mnt_freq = 0;
- m.mnt_passno = 0;
- addmntent(mountTable, &m);
- endmntent(mountTable);
- }
- return 0;
- } else {
- fprintf(stderr, "Mount %s", blockDevice);
- if ( filesystemType && *filesystemType )
- fprintf(stderr, " (type %s)", filesystemType);
-
- fprintf(
- stderr
- ," on %s: "
- ,directory);
-
- switch ( error ) {
- case EPERM:
- if (geteuid() == 0)
- fprintf(
- stderr
- ,"mount point %s is not a directory"
- ,blockDevice);
- else
- fprintf(
- stderr
- ,"must be superuser to use mount");
- break;
- case EBUSY:
- fprintf(
- stderr
- ,"%s already mounted or %s busy"
- ,blockDevice
- ,directory);
- break;
- case ENOENT:
- {
- struct stat statbuf;
- if ( stat(directory, &statbuf) != 0 )
- fprintf(
- stderr
- ,"directory %s does not exist"
- ,directory);
- else if ( stat(blockDevice, &statbuf) != 0 )
- fprintf(
- stderr
- ,"block device %s does not exist"
- ,blockDevice);
- else
- fprintf(
- stderr
- ,"%s is not mounted on %s, but the mount table says it is."
- ,blockDevice
- ,directory);
- break;
+ goto report_error;
}
- case ENOTDIR:
- fprintf(
- stderr
- ,"%s is not a directory"
- ,directory);
- break;
- case EINVAL:
- fprintf(
- stderr
- ,"wrong filesystem type, or bad superblock on %s"
- ,blockDevice);
- break;
- case EMFILE:
- fprintf(stderr, "mount table full");
- break;
- case EIO:
- fprintf(
- stderr
- ,"I/O error reading %s"
- ,blockDevice);
- break;
- case ENODEV:
- {
- FILE * f = fopen("/proc/filesystems", "r");
-
- fprintf(
- stderr
- ,"filesystem type %s not in kernel.\n"
- ,filesystemType);
- fprintf(stderr, "Do you need to load a module?\n");
- if ( f ) {
- char buf[100];
-
- fprintf(
- stderr
- ,"Here are the filesystem types the kernel"
- " can mount:\n");
- while ( fgets(buf, sizeof(buf), f) != 0 )
- fprintf(stderr, "\t%s", buf);
- fclose(f);
+ }
+
+ // 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;
}
- break;
+
+ // 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);
}
- case ENOTBLK:
- fprintf(
- stderr
- ,"%s is not a block device"
- ,blockDevice);
- break;
- case ENXIO:
- fprintf(
- stderr
- ,"%s is not a valid block device"
- ,blockDevice);
- break;
- default:
- fputs(strerror(errno), stderr);
+
+ 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;
}
- putc('\n', stderr);
- return -1;
}
+
+ 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);
+ }
+ }
+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(struct FileInfo * i, 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[1024];
- unsigned long flags = 0;
- char * filesystemType = "auto";
- int fake = 0;
- int noMtab = 0;
- int all = 0;
-
- *string_flags = '\0';
-
- if ( argc == 1 ) {
- FILE * mountTable;
- if ( (mountTable = setmntent("/etc/mtab", "r")) ) {
- struct mntent * m;
- while ( (m = getmntent(mountTable)) != 0 ) {
- printf(
- "%s on %s type %s (%s)\n"
- ,m->mnt_fsname
- ,m->mnt_dir
- ,m->mnt_type
- ,m->mnt_opts);
- }
- endmntent(mountTable);
+ 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];
+ }
+ 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 0;
}
- while ( argc >= 2 && argv[1][0] == '-' ) {
- switch ( argv[1][1] ) {
- case 'f':
- fake = 1;
- break;
- case 'n':
- noMtab = 1;
- break;
- case 'o':
- if ( argc < 3 ) {
- usage(mount_usage);
- return 1;
- }
- parse_mount_options(argv[2], &flags, string_flags);
- argc--;
- argv++;
- break;
- case 'r':
- flags |= MS_RDONLY;
- break;
- case 't':
- if ( argc < 3 ) {
- usage(mount_usage);
- return 1;
+ // 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);
}
- filesystemType = argv[2];
- argc--;
- argv++;
- break;
- case 'v':
- break;
- case 'w':
- flags &= ~MS_RDONLY;
- break;
- case 'a':
- all = 1;
- break;
- default:
- usage(mount_usage);
- return 1;
+ if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
+ return EXIT_SUCCESS;
}
- argc--;
- argv++;
}
- if (all == 1) {
- struct mntent *m;
- FILE *f = setmntent("/etc/fstab", "r");
+ // 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) {
- return 1;
- }
+ // If we have at least one argument, it's the storage location
+
+ 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);
- // FIXME: Combine read routine (make new function) with unmount_all to save space.
+ // Loop through entries until we find what we're looking for.
- while ((m = getmntent(f)) != NULL) {
- // If the file system isn't noauto, and isn't mounted on /, mount it
- if ((!strstr(m->mnt_opts, "noauto")) && (m->mnt_dir[1] != '\0')
- && !((m->mnt_type[0] == 's') && (m->mnt_type[1] == 'w'))
- && !((m->mnt_type[0] == 'n') && (m->mnt_type[1] == 'f'))) {
- mount_one(m->mnt_fsname, m->mnt_dir, m->mnt_type, flags, m->mnt_opts, noMtab, fake);
+ 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);
}
+ goto clean_up;
}
- endmntent(f);
- } else {
- if ( argc >= 3 ) {
- if ( mount_one( argv[1], argv[2], filesystemType, flags, string_flags, noMtab, fake) == 0 )
- return 0;
- else
- return 1;
+ /* 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 {
- usage(mount_usage);
- return 1;
+
+ // 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++;
+ }
}
}
- return 0;
+ if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
+
+clean_up:
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(storage_path);
+ free(cmdopts);
+ }
+
+ return rc;
}