summaryrefslogtreecommitdiff
path: root/mount/mount.c
diff options
context:
space:
mode:
Diffstat (limited to 'mount/mount.c')
-rw-r--r--mount/mount.c734
1 files changed, 734 insertions, 0 deletions
diff --git a/mount/mount.c b/mount/mount.c
new file mode 100644
index 00000000..b6ffbcd6
--- /dev/null
+++ b/mount/mount.c
@@ -0,0 +1,734 @@
+/*
+ * A mount(8) for Linux 0.99.
+ * mount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ *
+ * Thu Jul 14 07:32:40 1994: faith@cs.unc.edu added changed from Adam
+ * J. Richter (adam@adam.yggdrasil.com) so that /proc/filesystems is used
+ * if no -t option is given. I modified his patches so that, if
+ * /proc/filesystems is not available, the behavior of mount is the same as
+ * it was previously.
+ *
+ * Wed Sep 14 22:43:00 1994: Mitchum DSouza
+ * (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for mounting
+ * the "loop" device.
+ *
+ * Wed Sep 14 22:55:10 1994: Sander van Malssen (svm@kozmix.hacktic.nl)
+ * added support for remounting readonly file systems readonly.
+ *
+ * Wed Feb 8 09:23:18 1995: Mike Grupenhoff <kashmir@umiacs.UMD.EDU> added
+ * a probe of the superblock for the type before /proc/filesystems is
+ * checked.
+ *
+ * Wed Feb 8 12:27:00 1995: Andries.Brouwer@cwi.nl fixed up error messages.
+ *
+ */
+
+#include "sundries.h"
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/ext_fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/xia_fs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+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). */
+int readonly = 0;
+
+/* Nonzero for chatty (-v). */
+int verbose = 0;
+
+/* True for read/write (-w). */
+int readwrite = 0;
+
+/* True for all mount (-a). */
+int all = 0;
+
+/* True if ruid != euid. */
+int suid = 0;
+
+/* Map from -o and fstab option strings to the flag argument to mount(2). */
+struct opt_map
+{
+ const char *opt; /* option name */
+ int inv; /* true if flag value should be inverted */
+ int mask; /* flag mask value */
+};
+
+/* Custom mount options for our own purposes. */
+#define MS_NOAUTO 0x80000000
+#define MS_USER 0x40000000
+
+/* Options that we keep the mount system call from seeing. */
+#define MS_NOSYS (MS_NOAUTO|MS_USER)
+
+/* Options that we keep from appearing in the options field in the mtab. */
+#define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USER)
+
+/* 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 */
+ /* add new options here */
+#ifdef MS_NOSUB
+ { "sub", 1, MS_NOSUB }, /* allow submounts */
+ { "nosub", 0, MS_NOSUB }, /* don't allow submounts */
+#endif
+ { NULL, 0, 0 }
+};
+
+
+/* Report on a single mount. */
+static void
+print_one (const struct mntent *mnt)
+{
+ printf ("%s on %s", mnt->mnt_fsname, mnt->mnt_dir);
+ if ((mnt->mnt_type != NULL) && *mnt->mnt_type != '\0')
+ printf (" type %s", mnt->mnt_type);
+ if (mnt->mnt_opts != NULL)
+ printf (" (%s)", mnt->mnt_opts);
+ printf ("\n");
+}
+
+/* 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);
+
+ if (ferror (F_mtab))
+ die (1, "mount: error reading %s: %s", MOUNTED, strerror (errno));
+
+ exit (0);
+}
+
+
+/* Look for OPT in opt_map table and return mask value. If OPT isn't found,
+ tack it onto extra_opts. */
+static inline void
+parse_opt (const char *opt, int *mask, char *extra_opts)
+{
+ const struct opt_map *om;
+
+ for (om = opt_map; om->opt != NULL; om++)
+ if (streq (opt, om->opt))
+ {
+ if (om->inv)
+ *mask &= ~om->mask;
+ else
+ *mask |= om->mask;
+ if (om->mask == MS_USER)
+ *mask |= MS_SECURE;
+ return;
+ }
+ if (*extra_opts)
+ strcat(extra_opts, ",");
+ strcat(extra_opts, opt);
+}
+
+/* Take -o options list and compute 4th and 5th args to mount(2). flags
+ gets the standard options and extra_opts anything we don't recognize. */
+static void
+parse_opts (char *opts, int *flags, char **extra_opts)
+{
+ char *opt;
+
+ *flags = 0;
+ *extra_opts = NULL;
+
+ 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);
+ }
+
+ if (readonly)
+ *flags |= MS_RDONLY;
+ if (readwrite)
+ *flags &= ~MS_RDONLY;
+}
+
+/* Try to build a canonical options string. */
+static char *
+fix_opts_string (int flags, char *extra_opts)
+{
+ const struct opt_map *om;
+ 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)
+ 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;
+ 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;
+ }
+ return new_opts;
+}
+
+
+/*
+ char *fstype(const char *device);
+
+ probes the device and attempts to determine the type of filesystem
+ contained within.
+
+ Original routine by <jmorriso@bogomips.ww.ubc.ca>; made into a function
+ for mount(8) by Mike Grupenhoff <kashmir@umiacs.umd.edu>.
+
+ Currently supports: minix, ext, ext2, xia
+*/
+
+static char *
+fstype(const char *device)
+{
+ int fd;
+
+ /* MINIX */
+ struct minix_super_block ms;
+ /* extended fs */
+ struct ext_super_block es;
+ /* 2nd extended fs */
+ struct ext2_super_block e2s;
+ /* xia fs */
+ struct xiafs_super_block xfs;
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0) {
+ perror(device);
+ return 0;
+ }
+ lseek(fd, BLOCK_SIZE, SEEK_SET);
+ read(fd, (char *) &ms, sizeof(ms));
+ if (ms.s_magic == MINIX_SUPER_MAGIC || ms.s_magic == MINIX_SUPER_MAGIC2) {
+ close(fd);
+ return("minix");
+ }
+
+ lseek(fd, BLOCK_SIZE, SEEK_SET);
+ read(fd, (char *) &es, sizeof(es));
+ if (es.s_magic == EXT_SUPER_MAGIC) {
+ close(fd);
+ return("ext");
+ }
+
+ lseek(fd, BLOCK_SIZE, SEEK_SET);
+ read(fd, (char *) &e2s, sizeof(e2s));
+ if (e2s.s_magic == EXT2_SUPER_MAGIC || e2s.s_magic == EXT2_PRE_02B_MAGIC) {
+ close(fd);
+ return("ext2");
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ read(fd, (char *) &xfs, sizeof(xfs));
+ if (xfs.s_magic == _XIAFS_SUPER_MAGIC) {
+ close(fd);
+ return("xiafs");
+ }
+
+ close(fd);
+
+ return(0);
+
+}
+
+
+/* Mount a single file system. Return status,
+ so don't exit on non-fatal errors. */
+
+static int
+try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) {
+ FILE *procfs_file;
+ char line[100];
+ char fsname[50];
+
+ if (*type) return mount5 (spec, node, *type, flags & ~MS_NOSYS, mount_opts);
+ if (( procfs_file = fopen("/proc/filesystems", "r")) == NULL) {
+ /* If /proc/filesystems is not available,
+ preserve the old behavior of mount. */
+ return mount5 (spec,
+ node,
+ FSTYPE_DEFAULT,
+ flags & ~MS_NOSYS, mount_opts);
+ }
+ while (fgets(line, sizeof(line), procfs_file)) {
+ if (sscanf (line, "nodev %[^\n]\n", fsname) == 1) continue;
+ if (sscanf (line, " %[^ \n]\n", fsname) != 1) continue;
+ if (mount5 (spec, node, fsname, flags & ~MS_NOSYS, mount_opts) == 0) {
+ *type=xstrdup(fsname);
+ fclose(procfs_file);
+ return 0;
+ }
+ }
+ fclose(procfs_file);
+ return -1;
+}
+
+
+static int
+mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
+{
+ struct mntent mnt;
+ int mnt_err;
+ int flags;
+ char *extra_opts;
+ char *mount_opts;
+ int anti_recurse = 0;
+ int loop=0;
+
+ if (type == NULL)
+ {
+ if (strchr (spec, ':') != NULL)
+ type = "nfs";
+ }
+
+ parse_opts (xstrdup (opts), &flags, &extra_opts);
+
+ /* root may allow certain types of mounts by ordinary users */
+ if (suid && !(flags & MS_USER))
+ die (3, "mount: only root can mount %s on %s", spec, node);
+
+ /* quietly succeed for fstab entries that don't get mounted automatically */
+ if (all && (flags & MS_NOAUTO))
+ return 0;
+
+ mount_opts = extra_opts;
+
+ if (!fake && type && strncmp("lo@", type, 3)==0) {
+ extern int lomount (char *, char *, char *, char **,
+ int *, char **, char **);
+ char *dev=type+3;
+
+ loop=1;
+ if (lomount (spec, node, dev, &type,
+ &flags, &opts, &mount_opts) != 0)
+ return 1;
+ spec=dev;
+ mount_opts=NULL;
+ }
+
+ if (!fake && type && streq (type, "nfs"))
+#ifdef HAVE_NFS
+ if (nfsmount (spec, node, &flags, &extra_opts, &mount_opts) != 0)
+ return 1;
+#else
+ die (1, "mount: this version doesn't support the type `nfs'");
+#endif
+
+ if (!type && !(type = fstype(spec)))
+ return 1;
+
+ block_signals (SIG_BLOCK);
+
+ if (fake
+ || (try_mount5 (spec, node, &type, flags & ~MS_NOSYS, mount_opts)) == 0)
+ /* Mount succeeded, write mtab entry. */
+ {
+ if (!nomtab)
+ {
+ mnt.mnt_fsname = canonicalize (spec);
+ mnt.mnt_dir = canonicalize (node);
+ mnt.mnt_type = loop?"loop":type;
+ mnt.mnt_opts = fix_opts_string (flags & ~MS_NOMTAB,
+ loop?opts:extra_opts);
+ mnt.mnt_freq = freq;
+ mnt.mnt_passno = pass;
+
+ /* We get chatty now rather than after the update to mtab since the
+ mount succeeded, even if the write to /etc/mtab should fail. */
+ if (verbose)
+ print_one (&mnt);
+
+ if (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));
+ }
+
+ block_signals (SIG_UNBLOCK);
+ return 0;
+ }
+
+ 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. */
+ switch (mnt_err)
+ {
+ case EPERM:
+ if (geteuid() == 0)
+ error ("mount: mount point %s is not a directory", node);
+ else
+ error ("mount: must be superuser to use mount");
+ break;
+ case EBUSY:
+ error ("mount: %s already mounted or %s busy", spec, node);
+ break;
+ case ENOENT:
+ { struct stat statbuf;
+ if (stat (node, &statbuf))
+ error ("mount: mount point %s does not exist", node);
+ else if (stat (spec, &statbuf))
+ error ("mount: special device %s does not exist", spec);
+ else {
+ errno = mnt_err;
+ perror("mount");
+ }
+ break;
+ }
+ case ENOTDIR:
+ 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;
+ case EMFILE:
+ error ("mount table full"); break;
+ case EIO:
+ error ("mount: %s: can't read superblock", spec); break;
+ case ENODEV:
+ error ("mount: fs type %s not supported by kernel", type); break;
+ case ENOTBLK:
+ 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 EROFS: /* linux 1.1.38 and later */
+ if (anti_recurse)
+ {
+ error ("mount: block device %s is not permitted on its filesystem", spec);
+ break;
+ }
+ else
+ {
+ anti_recurse++;
+ if (opts)
+ {
+ opts = realloc(xstrdup(opts), strlen(opts)+3);
+ strcat(opts, ",ro");
+ }
+ else
+ opts = "ro";
+ error ("mount: block device %s is write-protected, mounting read-only", spec);
+ return mount_one (spec, node, type, opts, freq, pass);
+ }
+ break;
+ default:
+ error ("mount: %s", strerror (mnt_err)); break;
+ }
+ return 1;
+}
+
+/* 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);
+ }
+
+ status = 0;
+ while ((fstab = getfsent ()) != NULL)
+ if (matching_type (fstab->mnt_type, types)
+ && !streq (fstab->mnt_dir, "/")
+ && !streq (fstab->mnt_dir, "root"))
+ if (mounted (fstab->mnt_fsname, fstab->mnt_dir, spec_list, node_list))
+ {
+ if (verbose)
+ printf("mount: %s already mounted on %s\n",
+ fstab->mnt_fsname, fstab->mnt_dir);
+ }
+ else
+ status |= mount_one (fstab->mnt_fsname, fstab->mnt_dir,
+ fstab->mnt_type, fstab->mnt_opts,
+ fstab->mnt_freq, fstab->mnt_passno);
+
+ return status;
+}
+
+/* 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);
+
+ 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);
+}
+
+extern char version[];
+static struct option longopts[] =
+{
+ { "all", 0, 0, 'a' },
+ { "fake", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { "types", 1, 0, 't' },
+ { NULL, 0, 0, 0 }
+};
+
+const char *usage_string = "\
+usage: mount [-hV]\n\
+ mount -a [-nfrvw] [-t vfstypes]\n\
+ mount [-nfrvw] [-o options] special | node\n\
+ mount [-nfrvw] [-t vfstype] [-o options] special node\n\
+";
+
+static void
+usage (FILE *fp, int n)
+{
+ fprintf (fp, "%s", usage_string);
+ exit (n);
+}
+
+int
+main (int argc, char *argv[])
+{
+ int c;
+ char *options = NULL;
+ string_list types = NULL;
+ struct mntent *fs;
+ char *spec;
+ int result = 0;
+ struct stat statbuf;
+
+ while ((c = getopt_long (argc, argv, "afhnrvVwt:o:", longopts, NULL)) != EOF)
+ switch (c)
+ {
+ case 'a': /* mount everything in fstab */
+ ++all;
+ break;
+ case 'f': /* fake (don't actually do mount(2) call) */
+ ++fake;
+ break;
+ case 'h': /* help */
+ usage (stdout, 0);
+ break;
+ case 'n': /* mount without writing in /etc/mtab */
+ ++nomtab;
+ break;
+ case 'r': /* mount readonly */
+ ++readonly;
+ readwrite = 0;
+ break;
+ case 'v': /* be chatty */
+ ++verbose;
+ break;
+ case 'V': /* version */
+ printf ("%s\n", version);
+ exit (0);
+ case 'w': /* mount read/write */
+ ++readwrite;
+ readonly = 0;
+ break;
+ case 't': /* specify file system types */
+ types = parse_list (optarg);
+ break;
+ case 'o': /* specify mount options */
+ options = optarg;
+ break;
+ case 0:
+ break;
+ case '?':
+ default:
+ usage (stderr, 1);
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ {
+ if (options)
+ usage (stderr, 1);
+ if (!all)
+ return print_all (types);
+ }
+
+ 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");
+
+
+ switch (argc)
+ {
+ case 0:
+ /* mount -a */
+ result = mount_all (types);
+ break;
+
+ case 1:
+ /* mount [-nfrvw] [-o options] special | node */
+ if (types != NULL)
+ usage (stderr, 1);
+ /* Try to find the other pathname in fstab. */
+ spec = canonicalize (*argv);
+ if (!(fs = getmntfile (spec))
+ && !(fs = getfsspec (spec)) && !(fs = getfsfile (spec)))
+ die (2, "mount: can't find %s in %s or %s",
+ spec, MOUNTED, _PATH_FSTAB);
+ /* Merge the fstab and command line options. */
+ if (options == NULL)
+ options = fs->mnt_opts;
+ else
+ {
+ char *tmp = xmalloc(strlen(fs->mnt_opts) + strlen(options) + 2);
+
+ 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);
+ break;
+
+ case 2:
+ /* mount [-nfrvw] [-t vfstype] [-o options] special node */
+ if (types == NULL)
+ result = mount_one (argv[0], argv[1], NULL, options, 0, 0);
+ else if (cdr (types) == NULL)
+ result = mount_one (argv[0], argv[1], car (types), options, 0, 0);
+ else
+ usage (stderr, 2);
+ break;
+
+ default:
+ usage (stderr, 2);
+ }
+
+ if (!nomtab)
+ {
+ endmntent (F_mtab);
+ unlock_mtab ();
+ }
+
+ exit (result);
+}