summaryrefslogtreecommitdiffstats
path: root/mount/mount.c
diff options
context:
space:
mode:
Diffstat (limited to 'mount/mount.c')
-rw-r--r--mount/mount.c1023
1 files changed, 688 insertions, 335 deletions
diff --git a/mount/mount.c b/mount/mount.c
index 028848ac5..98093956c 100644
--- a/mount/mount.c
+++ b/mount/mount.c
@@ -22,42 +22,67 @@
* 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
*
+ * Fri Apr 5 01:13:33 1996: quinlan@bucknell.edu, fixed up iso9660 autodetect
+ *
+ * Since then, many changes - aeb.
*/
-#include "sundries.h"
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#if !defined(__GLIBC__)
+#define _LINUX_TYPES_H /* kludge to prevent inclusion */
+#endif /* __GLIBC */
+#define _LINUX_WAIT_H /* idem */
+#define _I386_BITOPS_H /* idem */
#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 <sys/stat.h>
-#include <unistd.h>
+#include <linux/iso_fs.h>
+
+#if defined(__GLIBC__)
+#define _SOCKETBITS_H
+#endif /* __GLIBC */
+#include "sundries.h"
+#include "fstab.h"
+#include "lomount.h"
+#include "loop.h"
#define PROC_FILESYSTEMS "/proc/filesystems"
#define SIZE(a) (sizeof(a)/sizeof(a[0]))
-int del_loop (const char *);
-
/* True for fake mount (-f). */
int fake = 0;
/* Don't write a entry in /etc/mtab (-n). */
int nomtab = 0;
-/* True for readonly (-r). */
+/* True for explicit readonly (-r). */
int readonly = 0;
/* Nonzero for chatty (-v). */
int verbose = 0;
-/* True for read/write (-w). */
+/* True for explicit read/write (-w). */
int readwrite = 0;
/* True for all mount (-a). */
int all = 0;
+/* True for fork() during all mount (-F). */
+int optfork = 0;
+
/* True if ruid != euid. */
int suid = 0;
@@ -65,16 +90,20 @@ int suid = 0;
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. */
+/* We can use the high-order 16 bits, since the mount call
+ has MS_MGC_VAL there. */
#define MS_NOAUTO 0x80000000
#define MS_USER 0x40000000
+#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_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)
@@ -82,72 +111,112 @@ struct opt_map
/* 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 */
+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 */
+ { "auto", 0, 1, MS_NOAUTO }, /* Can be mounted using -a */
+ { "noauto", 0, 0, MS_NOAUTO }, /* Can only be mounted explicitly */
+ { "user", 0, 0, MS_USER }, /* Allow ordinary user to mount */
+ { "nouser", 0, 1, MS_USER }, /* Forbid ordinary user to mount */
/* 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_SILENT
- { "quiet", 0, MS_SILENT }, /* be quiet */
- { "loud", 1, MS_SILENT }, /* print out messages. */
+#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
- { NULL, 0, 0 }
+ { "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
+ { NULL, 0, 0, 0 }
+};
+
+char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption;
+
+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 },
+ { NULL, 0, NULL }
};
+static void
+clear_string_opts(void) {
+ struct string_opt_map *m;
+
+ for (m = &string_opt_map[0]; m->tag; m++)
+ *(m->valptr) = NULL;
+}
+
+static int
+parse_string_opt(char *s) {
+ struct string_opt_map *m;
+ int lth;
+
+ for (m = &string_opt_map[0]; m->tag; m++) {
+ lth = strlen(m->tag);
+ if (!strncmp(s, m->tag, lth)) {
+ *(m->valptr) = xstrdup(s + lth);
+ return 1;
+ }
+ }
+ return 0;
+}
+
int mount_quiet=0;
/* Report on a single mount. */
static void
-print_one (const struct mntent *mnt)
-{
- if (mount_quiet) return;
- 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");
+print_one (const struct mntentchn *mc) {
+ if (mount_quiet)
+ return;
+ printf ("%s on %s", mc->mnt_fsname, mc->mnt_dir);
+ if (mc->mnt_type != NULL && *(mc->mnt_type) != '\0')
+ printf (" type %s", mc->mnt_type);
+ if (mc->mnt_opts != NULL)
+ printf (" (%s)", mc->mnt_opts);
+ printf ("\n");
}
/* Report on everything in mtab (of the specified types if any). */
static int
print_all (string_list types)
{
- struct mntent *mnt;
-
- open_mtab ("r");
-
- while ((mnt = getmntent (F_mtab)) != NULL)
- if (matching_type (mnt->mnt_type, types))
- print_one (mnt);
+ struct mntentchn *mc;
- if (ferror (F_mtab))
- die (1, "mount: error reading %s: %s", MOUNTED, strerror (errno));
-
- exit (0);
+ for (mc = mtab_head()->nxt; mc; mc = mc->nxt) {
+ if (matching_type (mc->mnt_type, types))
+ print_one (mc);
+ }
+ exit (0);
}
/* Look for OPT in opt_map table and return mask value. If OPT isn't found,
- tack it onto extra_opts. */
+ tack it onto extra_opts (which is non-NULL). */
static inline void
parse_opt (const char *opt, int *mask, char *extra_opts)
{
@@ -185,15 +254,16 @@ parse_opts (char *opts, int *flags, char **extra_opts)
*flags = 0;
*extra_opts = NULL;
+ clear_string_opts();
+
if (opts != NULL)
{
*extra_opts = xmalloc (strlen (opts) + 1);
**extra_opts = '\0';
- for (opt = strtok (opts, ",");
- opt != NULL;
- opt = strtok (NULL, ","))
- parse_opt (opt, flags, *extra_opts);
+ for (opt = strtok (opts, ","); opt; opt = strtok (NULL, ","))
+ if (!parse_string_opt (opt))
+ parse_opt (opt, flags, *extra_opts);
}
if (readonly)
@@ -207,30 +277,64 @@ static char *
fix_opts_string (int flags, char *extra_opts)
{
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);
+ }
return new_opts;
}
+/* Most file system types can be recognized by a `magic' number
+ in the superblock. Note that the order of the tests is
+ significant: by coincidence a filesystem can have the
+ magic numbers for several file system types simultaneously.
+ For example, the romfs magic lives in the 1st sector;
+ xiafs does not touch the 1st sector and has its magic in
+ the 2nd sector; ext2 does not touch the first two sectors. */
+
+/* ext definitions - required since 2.1.21 */
+#ifndef EXT_SUPER_MAGIC
+#define EXT_SUPER_MAGIC 0x137D
+struct ext_super_block {
+ unsigned long s_dummy[14];
+ unsigned short s_magic;
+};
+#endif
+
+/* xiafs definitions - required since they were removed from
+ the kernel since 2.1.21 */
+#ifndef _XIAFS_SUPER_MAGIC
+#define _XIAFS_SUPER_MAGIC 0x012FD16D
+struct xiafs_super_block {
+ u_char s_boot_segment[512]; /* 1st sector reserved for boot */
+ u_long s_dummy[15];
+ u_long s_magic; /* 15: magic number for xiafs */
+};
+#endif
+
+#ifndef EXT2_PRE_02B_MAGIC
+#define EXT2_PRE_02B_MAGIC 0xEF51
+#endif
+
+static inline unsigned short
+swapped(unsigned short a) {
+ return (a>>8) | (a<<8);
+}
/*
char *fstype(const char *device);
@@ -242,10 +346,13 @@ fix_opts_string (int flags, char *extra_opts)
for mount(8) by Mike Grupenhoff <kashmir@umiacs.umd.edu>.
Read the superblock only once - aeb
Added a test for iso9660 - aeb
+ Added a test for high sierra (iso9660) - quinlan@bucknell.edu
+ Corrected the test for xiafs - aeb
+ added romfs - aeb
- Currently supports: minix, ext, ext2, xiafs, iso9660
+ Currently supports: minix, ext, ext2, xiafs, iso9660, romfs
*/
-char *magic_known[] = { "minix", "ext", "ext2", "xiafs", "iso9660" };
+char *magic_known[] = { "minix", "ext", "ext2", "xiafs", "iso9660", "romfs" };
static int
tested(const char *device) {
@@ -261,65 +368,76 @@ static char *
fstype(const char *device)
{
int fd;
- char ifs_magic[8];
+ char *type = NULL;
union {
struct minix_super_block ms;
struct ext_super_block es;
struct ext2_super_block e2s;
- struct xiafs_super_block xfs;
} sb;
+ union {
+ struct xiafs_super_block xiasb;
+ char romfs_magic[8];
+ } xsb;
+ union {
+ struct iso_volume_descriptor iso;
+ struct hs_volume_descriptor hs;
+ } isosb;
struct stat statbuf;
/* opening and reading an arbitrary unknown path can have
undesired side effects - first check that `device' refers
to a block device */
- if (stat (device, &statbuf))
- error("mount: %s does not exist", device);
- if (!S_ISBLK(statbuf.st_mode))
- error("mount: %s is not a block device", device);
+ if (stat (device, &statbuf) || !S_ISBLK(statbuf.st_mode))
+ return 0;
fd = open(device, O_RDONLY);
- if (fd < 0
- || lseek(fd, BLOCK_SIZE, SEEK_SET) < 0
- || read(fd, (char *) &sb, sizeof(sb)) < 0) {
- perror(device);
- return 0;
- }
+ if (fd < 0)
+ return 0;
- if (sb.ms.s_magic == MINIX_SUPER_MAGIC
- || sb.ms.s_magic == MINIX_SUPER_MAGIC2) {
- close (fd);
- return("minix");
- }
+ if (lseek(fd, BLOCK_SIZE, SEEK_SET) != BLOCK_SIZE
+ || read(fd, (char *) &sb, sizeof(sb)) != sizeof(sb))
+ goto io_error;
- if (sb.es.s_magic == EXT_SUPER_MAGIC) {
- close (fd);
- return("ext");
- }
-
-#ifndef EXT2_PRE_02B_MAGIC
-#define EXT2_PRE_02B_MAGIC 0xEF51
-#endif
if (sb.e2s.s_magic == EXT2_SUPER_MAGIC
- || sb.e2s.s_magic == EXT2_PRE_02B_MAGIC) {
- close (fd);
- return("ext2");
+ || sb.e2s.s_magic == EXT2_PRE_02B_MAGIC
+ || sb.e2s.s_magic == swapped(EXT2_SUPER_MAGIC))
+ type = "ext2";
+
+ else if (sb.ms.s_magic == MINIX_SUPER_MAGIC
+ || sb.ms.s_magic == MINIX_SUPER_MAGIC2)
+ type = "minix";
+
+ else if (sb.es.s_magic == EXT_SUPER_MAGIC)
+ type = "ext";
+
+ if (!type) {
+ if (lseek(fd, 0, SEEK_SET) != 0
+ || read(fd, (char *) &xsb, sizeof(xsb)) != sizeof(xsb))
+ goto io_error;
+
+ if (xsb.xiasb.s_magic == _XIAFS_SUPER_MAGIC)
+ type = "xiafs";
+ else if(!strncmp(xsb.romfs_magic, "-rom1fs-", 8))
+ type = "romfs";
}
- if (sb.xfs.s_magic == _XIAFS_SUPER_MAGIC) {
- close (fd);
- return("xiafs");
- }
+ if (!type) {
+ if (lseek(fd, 0x8000, SEEK_SET) != 0x8000
+ || read(fd, (char *) &isosb, sizeof(isosb)) != sizeof(isosb))
+ goto io_error;
- if (lseek (fd, 0100000, SEEK_SET) != -1
- && read (fd, ifs_magic, 8) == 8
- && !strncmp(ifs_magic, "\001CD001\001", 8)) { /* ECMA 119 */
- close (fd);
- return("iso9660");
+ if(strncmp(isosb.iso.id, ISO_STANDARD_ID, sizeof(isosb.iso.id)) == 0
+ || strncmp(isosb.hs.id, HS_STANDARD_ID, sizeof(isosb.hs.id)) == 0)
+ type = "iso9660";
}
close (fd);
- return(0);
+ return(type);
+
+io_error:
+ perror(device);
+ close(fd);
+ return 0;
}
FILE *procfs;
@@ -363,20 +481,68 @@ is_in_proc(char *type) {
static int
already (char *spec, char *node) {
- struct mntent *me;
+ struct mntentchn *mc;
int ret = 1;
- if ((me = getmntfile(node)) != NULL)
+ if ((mc = getmntfile(node)) != NULL)
error ("mount: according to mtab, %s is already mounted on %s",
- me->mnt_fsname, node);
- else if ((me = getmntfile(spec)) != NULL)
+ mc->mnt_fsname, node);
+ else if ((mc = getmntfile(spec)) != NULL)
error ("mount: according to mtab, %s is mounted on %s",
- spec, me->mnt_dir);
+ spec, mc->mnt_dir);
else
ret = 0;
return ret;
}
+/* Create mtab with a root entry. */
+static void
+create_mtab (void) {
+ struct mntentchn *fstab;
+ struct mntent mnt;
+ int flags;
+ char *extra_opts;
+ FILE *fp;
+
+ lock_mtab();
+
+ if ((fp = setmntent (MOUNTED, "a+")) == NULL)
+ die (EX_FILEIO, "mount: can't open %s for writing: %s",
+ MOUNTED, strerror (errno));
+
+ /* Find the root entry by looking it up in fstab */
+ if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) {
+ parse_opts (xstrdup (fstab->mnt_opts), &flags, &extra_opts);
+ mnt.mnt_dir = "/";
+ mnt.mnt_fsname = canonicalize (fstab->mnt_fsname);
+ mnt.mnt_type = fstab->mnt_type;
+ mnt.mnt_opts = fix_opts_string (flags, extra_opts);
+ mnt.mnt_freq = mnt.mnt_passno = 0;
+
+ if (addmntent (fp, &mnt) == 1)
+ die (EX_FILEIO, "mount: error writing %s: %s",
+ MOUNTED, strerror (errno));
+ }
+ if (fchmod (fileno (fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
+ if (errno != EROFS)
+ die (EX_FILEIO, "mount: error changing mode of %s: %s",
+ MOUNTED, strerror (errno));
+ endmntent (fp);
+
+ unlock_mtab();
+}
+
+/* count successful mount system calls */
+static int mountcount = 0;
+
+static int
+mount5 (char *special, char *dir, char *type, int flags, void *data) {
+ int ret = mount (special, dir, type, 0xC0ED0000 | (flags), data);
+ if (ret == 0)
+ mountcount++;
+ return ret;
+}
+
/* Mount a single file system. Return status,
so don't exit on non-fatal errors. */
@@ -384,13 +550,11 @@ static int
try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) {
char *fsname;
- if (strcasecmp (*type, "auto") == 0)
+ if (*type && strcasecmp (*type, "auto") == 0)
*type = NULL;
- if (!*type) {
+ if (!*type && !(flags & MS_REMOUNT)) {
*type = fstype(spec);
- if (!*type && !procopen())
- *type = FSTYPE_DEFAULT;
if (verbose) {
printf ("mount: you didn't specify a filesystem type for %s\n",
spec);
@@ -402,9 +566,11 @@ try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) {
}
}
- if (*type)
+ if (*type || (flags & MS_REMOUNT))
return mount5 (spec, node, *type, flags & ~MS_NOSYS, mount_opts);
+ if (!procopen())
+ return -1;
while ((fsname = procnext()) != NULL) {
if (tested (fsname))
continue;
@@ -424,32 +590,52 @@ try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) {
return -1;
}
-
static int
-mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
+mount_one (char *spec0, char *node0, char *type0, char *opts0,
+ int freq, int pass)
{
+ struct mntentchn mcn;
struct mntent mnt;
int mnt_err;
int flags;
- char *extra_opts;
- char *mount_opts;
+ char *extra_opts; /* written in mtab */
+ char *mount_opts; /* actually used on system call */
static int added_ro = 0;
- int loop=0;
+ int loop, looptype, offset;
+ char *spec, *node, *type, *opts, *loopdev, *loopfile;
- if (type == NULL)
- {
- if (strchr (spec, ':') != NULL)
+ spec = xstrdup(spec0);
+ node = xstrdup(node0);
+ type = xstrdup(type0);
+ opts = xstrdup(opts0);
+
+/*
+ * There are two special cases: nfs mounts and loop mounts.
+ * In the case of nfs mounts spec is probably of the form machine:path.
+ * 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.
+ */
+
+ if (type == NULL) {
+ if (strchr (spec, ':') != NULL) {
type = "nfs";
- }
+ if (verbose)
+ printf("mount: no type was given - "
+ "I'll assume nfs because of the colon\n");
+ }
+ }
parse_opts (xstrdup (opts), &flags, &extra_opts);
/* root may allow certain types of mounts by ordinary users */
if (suid && !(flags & MS_USER)) {
if (already (spec, node))
- die (3, "mount failed");
+ die (EX_USAGE, "mount failed");
else
- die (3, "mount: only root can mount %s on %s", spec, node);
+ die (EX_USAGE, "mount: only root can mount %s on %s", spec, node);
}
/* quietly succeed for fstab entries that don't get mounted automatically */
@@ -458,69 +644,156 @@ 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;
+ /*
+ * A loop mount?
+ * Only test for explicitly specified loop here,
+ * and try implicit loop if the mount fails.
+ */
+ 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=1;
- if (lomount (spec, node, dev, &type,
- &flags, &opts, &mount_opts) != 0)
- return 1;
- spec=dev;
- mount_opts=NULL;
+ 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))
+ return EX_FAIL;
+ spec = loopdev;
+ if (loopro)
+ flags |= MS_RDONLY;
+ }
}
if (!fake && type && streq (type, "nfs"))
#ifdef HAVE_NFS
if (nfsmount (spec, node, &flags, &extra_opts, &mount_opts) != 0)
- return 1;
+ return EX_FAIL;
#else
- die (1, "mount: this version was compiled without support for the type `nfs'");
+ die (EX_SOFTWARE, "mount: this version was compiled "
+ "without support for the type `nfs'");
#endif
+ /*
+ * Call mount.TYPE for types that require a separate
+ * mount program. For the moment these types are ncp and smb.
+ */
+ if (type)
+#ifndef ALWAYS_STAT
+ if (streq (type, "smb") || streq (type, "ncp"))
+#else
+ if (strlen (type) < 100)
+#endif
+ {
+ struct stat statbuf;
+ char mountprog[120];
+
+ sprintf(mountprog, "/sbin/mount.%s", type);
+ if (stat(mountprog, &statbuf) == 0) {
+ if (fork() == 0) {
+ char *oo, *mountargs[10];
+ int i = 0;
+
+ setuid(getuid());
+ setgid(getgid());
+ oo = fix_opts_string (flags, extra_opts);
+ 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 (fork() != -1) {
+ int status;
+ wait(&status);
+ return status;
+ } else
+ error("cannot fork: %s", strerror(errno));
+ }
+ }
+
block_signals (SIG_BLOCK);
if (fake
|| (try_mount5 (spec, node, &type, flags & ~MS_NOSYS, mount_opts)) == 0)
- /* Mount succeeded, write mtab entry. */
+ /* Mount succeeded, report this (if verbose) and 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;
+ if (loop)
+ opt_loopdev = loopdev;
+
+ mcn.mnt_fsname = mnt.mnt_fsname = canonicalize (loop ? loopfile : spec);
+ mcn.mnt_dir = mnt.mnt_dir = canonicalize (node);
+ mcn.mnt_type = mnt.mnt_type = type ? type : "unknown";
+ mcn.mnt_opts = mnt.mnt_opts = fix_opts_string (flags & ~MS_NOMTAB, extra_opts);
+ mcn.nxt = 0;
+ 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);
+ /* 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 (&mcn);
+ if (!nomtab && mtab_is_writable()) {
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));
- }
+ update_mtab (mnt.mnt_dir, &mnt);
+ else {
+ FILE *fp = setmntent(MOUNTED, "a+");
+ if (fp == NULL)
+ error("mount: can't open %s: %s", MOUNTED,
+ strerror (errno));
+ else {
+ if ((addmntent (fp, &mnt)) == 1)
+ error("mount: error writing %s: %s", MOUNTED,
+ strerror (errno));
+ endmntent(fp);
+ }
+ }
+ }
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);
/* Mount failed, complain, but don't die. */
@@ -531,14 +804,22 @@ mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
switch (mnt_err)
{
case EPERM:
- if (geteuid() == 0)
- error ("mount: mount point %s is not a directory", node);
- else
+ if (geteuid() == 0) {
+ struct stat statbuf;
+ 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);
- already (spec, node);
+ if (flags & MS_REMOUNT) {
+ error ("mount: %s is busy", node);
+ } else {
+ error ("mount: %s already mounted or %s busy", spec, node);
+ already (spec, node);
+ }
break;
case ENOENT:
{ struct stat statbuf;
@@ -556,27 +837,78 @@ mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
break;
}
case ENOTDIR:
- error ("mount: mount point %s is not a directory", node); break;
+ error ("mount: mount point %s is not a directory", node);
+ break;
case EINVAL:
- error ("mount: wrong fs type or bad superblock on %s", spec); break;
+ { int fd, size;
+ struct stat statbuf;
+
+ if (flags & MS_REMOUNT) {
+ error ("mount: %s not mounted already, or bad option\n", 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)) >= 0) {
+ if(ioctl(fd, BLKGETSIZE, &size) == 0 && size <= 2)
+ error (" (aren't you trying to mount an extended partition,\n"
+ " instead of some logical partition inside?)\n");
+ close(fd);
+ }
+ }
+ break;
+ }
case EMFILE:
error ("mount table full"); break;
case EIO:
error ("mount: %s: can't read superblock", spec); break;
case ENODEV:
- if (is_in_proc(type))
+ if (is_in_proc(type) || !strcmp(type, "guess"))
error("mount: %s has wrong major or minor number", spec);
- else if (procfs)
+ else if (procfs) {
+ char *lowtype, *p;
+ int u;
+
error ("mount: fs type %s not supported by kernel", type);
- else
+
+ /* 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_proc(lowtype))
+ error ("mount: probably you meant %s", lowtype);
+ else if (!strncmp(lowtype, "iso", 3) && is_in_proc("iso9660"))
+ 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;
+ { struct stat statbuf;
+
+ 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 */
+ case EACCES: /* pre-linux 1.1.38, 1.1.41 and later */
case EROFS: /* linux 1.1.38 and later */
if (added_ro) {
error ("mount: block device %s is not permitted on its filesystem",
@@ -584,6 +916,10 @@ mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
break;
} else {
added_ro = 1;
+ if (loop) {
+ opts = opts0;
+ type = type0;
+ }
if (opts) {
opts = realloc(xstrdup(opts), strlen(opts)+4);
strcat(opts, ",ro");
@@ -591,104 +927,132 @@ mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
opts = "ro";
if (type && !strcmp(type, "guess"))
type = 0;
- error ("mount: block device %s is write-protected, "
- "mounting read-only", spec);
- return mount_one (spec, node, type, opts, freq, pass);
+ error ("mount: %s%s is write-protected, mounting read-only",
+ loop ? "" : "block device ", spec0);
+ return mount_one (spec0, node0, type, opts, freq, pass);
}
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)
-{
- 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;
-}
-
-/* Mount all filesystems of the specified types except swap and root. */
-static int
-mount_all (string_list types)
-{
- struct mntent *fstab;
- struct mntent *mnt;
- string_list spec_list = NULL;
- string_list node_list = NULL;
- int status;
-
- 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);
- }
+mounted (char *spec, char *node) {
+ struct mntentchn *mc;
- status = 0;
- if (!setfsent()) return 1;
- 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);
+ spec = canonicalize (spec);
+ node = canonicalize (node);
- return status;
+ for (mc = mtab_head()->nxt; mc; mc = mc->nxt)
+ if (streq (spec, mc->mnt_fsname) && streq (node, mc->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;
-
- 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);
+/* 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) ((m) & ~0xf)
- 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);
+static int
+mount_all (string_list types) {
+ struct mntentchn *mc, *mtmp;
+ int status = 0, m;
+ struct stat statbuf;
+ struct child {
+ pid_t pid;
+ dev_t major;
+ struct mntentchn *mec;
+ struct mntentchn *meclast;
+ struct child *nxt;
+ } childhead, *childtail, *cp;
+
+ /* build a chain of what we have to do, or maybe
+ several chains, one for each major */
+ childhead.nxt = 0;
+ childtail = &childhead;
+ for (mc = fstab_head()->nxt; mc; mc = mc->nxt) {
+ if (matching_type (mc->mnt_type, types)
+ && !streq (mc->mnt_dir, "/")
+ && !streq (mc->mnt_dir, "root")) {
+ if (mounted (mc->mnt_fsname, mc->mnt_dir)) {
+ if (verbose)
+ printf("mount: %s already mounted on %s\n",
+ mc->mnt_fsname, mc->mnt_dir);
+ } else {
+ mtmp = (struct mntentchn *) xmalloc(sizeof(*mtmp));
+ *mtmp = *mc;
+ mtmp->nxt = 0;
+ m = 0;
+ if (optfork && stat(mc->mnt_fsname, &statbuf) == 0
+ && S_ISBLK(statbuf.st_mode))
+ m = DISKMAJOR(statbuf.st_rdev);
+ if (m) {
+ for (cp = childhead.nxt; cp; cp = cp->nxt)
+ if (cp->major == m) {
+ cp->meclast->nxt = mtmp;
+ cp->meclast = mtmp;
+ goto fnd;
+ }
+ }
+ cp = (struct child *) xmalloc(sizeof *cp);
+ cp->nxt = 0;
+ cp->mec = cp->meclast = mtmp;
+ cp->major = m;
+ 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)
+ error("mount: cannot fork: %s", strerror (errno));
+ 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->mnt_fsname, mc->mnt_dir,
+ mc->mnt_type, mc->mnt_opts, 0, 0);
+ if (mountcount)
+ status |= EX_SOMEOK;
+ if (p == 0)
+ exit(status);
+ }
+ }
+
+ /* wait for children, if any */
+ while ((cp = childhead.nxt) != NULL) {
+ childhead.nxt = cp->nxt;
+ if (cp->pid) {
+ int ret;
+ keep_waiting:
+ if(waitpid(cp->pid, &ret, 0) == -1) {
+ if (errno == EINTR)
+ goto keep_waiting;
+ perror("waitpid");
+ } else if (WIFEXITED(ret))
+ status |= WEXITSTATUS(ret);
+ else
+ status |= EX_SYSERR;
+ }
+ }
+ if (mountcount)
+ status |= EX_SOMEOK;
+ return status;
}
extern char version[];
@@ -696,6 +1060,7 @@ 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' },
@@ -711,7 +1076,7 @@ static struct option longopts[] =
const char *usage_string = "\
usage: mount [-hV]\n\
- mount -a [-nfrvw] [-t vfstypes]\n\
+ mount -a [-nfFrvw] [-t vfstypes]\n\
mount [-nfrvw] [-o options] special | node\n\
mount [-nfrvw] [-t vfstype] [-o options] special node\n\
";
@@ -725,118 +1090,110 @@ usage (FILE *fp, int n)
}
int
-main (int argc, char *argv[])
-{
- int c;
- char *options = NULL;
+main (int argc, char *argv[]) {
+ int c, result = 0;
+ char *options = NULL, *spec;
string_list types = NULL;
- struct mntent *fs;
- char *spec;
- int result = 0;
- struct stat statbuf;
+ struct mntentchn *mc;
- while ((c = getopt_long (argc, argv, "afhnrvVwt:o:", longopts, NULL)) != EOF)
- switch (c)
- {
+ while ((c = getopt_long (argc, argv, "afFhno:rvVwt:", 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 'F':
+ ++optfork;
+ break;
case 'h': /* help */
usage (stdout, 0);
break;
case 'n': /* mount without writing in /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;
+ readonly = 1;
readwrite = 0;
break;
- case 'v': /* be chatty */
+ case 't': /* specify file system types */
+ types = parse_list (optarg);
+ break;
+ case 'v': /* be chatty - very chatty if repeated */
++verbose;
break;
case 'V': /* version */
printf ("mount: %s\n", version);
exit (0);
case 'w': /* mount read/write */
- ++readwrite;
+ readwrite = 1;
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;
- }
+ usage (stderr, EX_USAGE);
+ }
argc -= optind;
argv += optind;
- if (argc == 0)
- {
+ if (argc == 0) {
if (options)
- usage (stderr, 1);
+ usage (stderr, EX_USAGE);
if (!all)
return print_all (types);
- }
+ }
- if (getuid () != geteuid ())
- {
+ if (getuid () != geteuid ()) {
suid = 1;
if (types || options || readwrite || nomtab || all || fake || argc != 1)
- die (2, "mount: only root can do that");
- }
-
- if (!nomtab)
- {
- lock_mtab ();
- if (stat(MOUNTED, &statbuf) < 0)
- create_mtab ();
- open_mtab ("a+");
- }
- else if (stat(MOUNTED, &statbuf) >= 0)
- open_mtab ("r");
+ die (EX_USAGE, "mount: only root can do that");
+ }
+ if (!nomtab && mtab_does_not_exist()) {
+ if (verbose > 1)
+ printf("mount: no %s found - creating it..\n", MOUNTED);
+ create_mtab ();
+ }
- switch (argc)
- {
+ switch (argc) {
case 0:
/* mount -a */
result = mount_all (types);
+ if (result == 0 && verbose)
+ error("not mounted anything");
break;
case 1:
/* mount [-nfrvw] [-o options] special | node */
if (types != NULL)
- usage (stderr, 1);
+ usage (stderr, EX_USAGE);
+
/* 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);
+ if ((mc = getmntfile (spec)) == NULL &&
+ (mc = getfsspec (spec)) == NULL && (mc = getfsfile (spec)) == NULL)
+ die (EX_USAGE, "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;
+ options = mc->mnt_opts;
else
- {
- char *tmp = xmalloc(strlen(fs->mnt_opts) + strlen(options) + 2);
+ options = xstrconcat3(mc->mnt_opts, ",", options);
- sprintf (tmp, "%s,%s", fs->mnt_opts, options);
- options = tmp;
- }
- result = mount_one (xstrdup (fs->mnt_fsname), xstrdup (fs->mnt_dir),
- xstrdup (fs->mnt_type), options,
- fs->mnt_freq, fs->mnt_passno);
+ result = mount_one (xstrdup (mc->mnt_fsname), xstrdup (mc->mnt_dir),
+ xstrdup (mc->mnt_type), options, 0, 0);
break;
case 2:
@@ -846,18 +1203,14 @@ main (int argc, char *argv[])
else if (cdr (types) == NULL)
result = mount_one (argv[0], argv[1], car (types), options, 0, 0);
else
- usage (stderr, 2);
+ usage (stderr, EX_USAGE);
break;
default:
- usage (stderr, 2);
- }
-
- if (!nomtab)
- {
- endmntent (F_mtab);
- unlock_mtab ();
- }
+ usage (stderr, EX_USAGE);
+ }
+ if (result == EX_SOMEOK)
+ result = 0;
exit (result);
}