diff options
Diffstat (limited to 'mount-deprecated')
-rw-r--r-- | mount-deprecated/.gitignore | 3 | ||||
-rw-r--r-- | mount-deprecated/Makemodule.am | 99 | ||||
-rw-r--r-- | mount-deprecated/devname.c | 14 | ||||
-rw-r--r-- | mount-deprecated/devname.h | 6 | ||||
-rw-r--r-- | mount-deprecated/fsprobe.c | 141 | ||||
-rw-r--r-- | mount-deprecated/fsprobe.h | 27 | ||||
-rw-r--r-- | mount-deprecated/fstab.c | 1170 | ||||
-rw-r--r-- | mount-deprecated/fstab.h | 44 | ||||
-rw-r--r-- | mount-deprecated/getusername.c | 14 | ||||
-rw-r--r-- | mount-deprecated/getusername.h | 1 | ||||
-rw-r--r-- | mount-deprecated/mount.8 | 2857 | ||||
-rw-r--r-- | mount-deprecated/mount.c | 2799 | ||||
-rw-r--r-- | mount-deprecated/mount_constants.h | 73 | ||||
-rw-r--r-- | mount-deprecated/mount_mntent.c | 165 | ||||
-rw-r--r-- | mount-deprecated/mount_mntent.h | 28 | ||||
-rw-r--r-- | mount-deprecated/sundries.c | 300 | ||||
-rw-r--r-- | mount-deprecated/sundries.h | 55 | ||||
-rw-r--r-- | mount-deprecated/umount.8 | 179 | ||||
-rw-r--r-- | mount-deprecated/umount.c | 878 |
19 files changed, 8853 insertions, 0 deletions
diff --git a/mount-deprecated/.gitignore b/mount-deprecated/.gitignore new file mode 100644 index 000000000..8be9aceb7 --- /dev/null +++ b/mount-deprecated/.gitignore @@ -0,0 +1,3 @@ +mount +mtab_lock_test +umount diff --git a/mount-deprecated/Makemodule.am b/mount-deprecated/Makemodule.am new file mode 100644 index 000000000..03fe51341 --- /dev/null +++ b/mount-deprecated/Makemodule.am @@ -0,0 +1,99 @@ +if BUILD_MOUNT + +bin_PROGRAMS += mount umount +dist_man_MANS += \ + mount-deprecated/mount.8 \ + mount-deprecated/umount.8 + +mount_common_sources = \ + mount-deprecated/sundries.c \ + mount-deprecated/sundries.h \ + mount-deprecated/fstab.h \ + mount-deprecated/mount_mntent.h \ + mount-deprecated/mount_constants.h \ + mount-deprecated/getusername.h \ + mount-deprecated/fsprobe.h \ + mount-deprecated/devname.c \ + mount-deprecated/devname.h \ + mount-deprecated/fstab.c \ + mount-deprecated/getusername.c \ + mount-deprecated/mount_mntent.c \ + mount-deprecated/fsprobe.c \ + \ + lib/canonicalize.c \ + lib/at.c \ + lib/blkdev.c \ + lib/env.c \ + lib/linux_version.c \ + lib/loopdev.c \ + lib/mangle.c \ + lib/strutils.c \ + lib/xgetpass.c \ + lib/sysfs.c + +mount_SOURCES = \ + mount-deprecated/mount.c \ + $(mount_common_sources) \ + lib/setproctitle.c +mount_CFLAGS = $(SUID_CFLAGS) $(AM_CFLAGS) -I$(ul_libblkid_incdir) +mount_LDFLAGS = $(SUID_LDFLAGS) $(AM_LDFLAGS) +mount_LDADD = libblkid.la + +umount_SOURCES = \ + mount-deprecated/umount.c \ + $(mount_common_sources) +umount_CFLAGS = $(SUID_CFLAGS) $(AM_CFLAGS) -I$(ul_libblkid_incdir) +umount_LDFLAGS = $(SUID_LDFLAGS) $(AM_LDFLAGS) +umount_LDADD = libblkid.la + + + +mount_static_LDADD = + +if HAVE_STATIC_MOUNT +bin_PROGRAMS += mount.static +mount_static_SOURCES = $(mount_SOURCES) +mount_static_CFLAGS = $(mount_CFLAGS) +mount_static_LDFLAGS = -all-static +mount_static_LDADD += libblkid.la +endif + +if HAVE_STATIC_UMOUNT +bin_PROGRAMS += umount.static +umount_static_SOURCES = $(umount_SOURCES) +umount_static_CFLAGS = $(umount_CFLAGS) +umount_static_LDFLAGS = -all-static +umount_static_LDADD = libblkid.la +endif + +if HAVE_SELINUX +mount_LDADD += $(SELINUX_LIBS) +mount_static_LDADD += $(SELINUX_LIBS_STATIC) +endif + +# hybrid mount -- old mount + new code from libmount for mtab management +# +if BUILD_LIBMOUNT_MOUNT +mount_LDADD += libmount.la +mount_CFLAGS += $(AM_CFLAGS) -I$(ul_libmount_incdir) +umount_LDADD += libmount.la +umount_CFLAGS += $(AM_CFLAGS) -I$(ul_libmount_incdir) +if HAVE_STATIC_MOUNT +mount_static_LDADD += libmount.la +mount_static_CFLAGS += -I$(ul_libmount_incdir) +endif +if HAVE_STATIC_UMOUNT +umount_static_LDADD += libmount.la +umount_static_CFLAGS += -I$(ul_libmount_incdir) +endif +endif + +if MAKEINSTALL_DO_SETUID +install-exec-hook-mount: + chmod 4755 $(DESTDIR)$(bindir)/mount + chmod 4755 $(DESTDIR)$(bindir)/umount + +INSTALL_EXEC_HOOKS += install-exec-hook-mount +endif + +endif # BUILD_MOUNT diff --git a/mount-deprecated/devname.c b/mount-deprecated/devname.c new file mode 100644 index 000000000..76dfe91ef --- /dev/null +++ b/mount-deprecated/devname.c @@ -0,0 +1,14 @@ +#include "fsprobe.h" + +#include "devname.h" +#include "sundries.h" /* for xstrdup */ + +const char * +spec_to_devname(const char *spec) +{ + if (!spec) + return NULL; + if (nocanonicalize || is_pseudo_fs(spec)) + return xstrdup(spec); + return fsprobe_get_devname_by_spec(spec); +} diff --git a/mount-deprecated/devname.h b/mount-deprecated/devname.h new file mode 100644 index 000000000..d6adeff7d --- /dev/null +++ b/mount-deprecated/devname.h @@ -0,0 +1,6 @@ +#ifndef MOUNT_DEVNAME_H +#define MOUNT_DEVNAME_H + +extern const char *spec_to_devname(const char *spec); + +#endif diff --git a/mount-deprecated/fsprobe.c b/mount-deprecated/fsprobe.c new file mode 100644 index 000000000..80c25669f --- /dev/null +++ b/mount-deprecated/fsprobe.c @@ -0,0 +1,141 @@ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <stdlib.h> + +#include <blkid.h> + +#include "blkdev.h" +#include "canonicalize.h" +#include "pathnames.h" +#include "fsprobe.h" + +/* + * THIS IS DEPRECATED -- use blkid_evaluate_* API rather than this extra layer + */ + + +static blkid_cache blcache; +static blkid_probe blprobe; + +void +fsprobe_init(void) +{ + blcache = NULL; + blprobe = NULL; +} + +void +fsprobe_exit(void) +{ + if (blprobe) + blkid_free_probe(blprobe); + if (blcache) + blkid_put_cache(blcache); +} + +/* + * Parses NAME=value, returns -1 on parse error, 0 success. The success is also + * when the 'spec' doesn't contain name=value pair (because the spec could be + * a devname too). In particular case the pointer 'name' is set to NULL. + */ +int +fsprobe_parse_spec(const char *spec, char **name, char **value) +{ + *name = NULL; + *value = NULL; + + if (strchr(spec, '=')) + return blkid_parse_tag_string(spec, name, value); + + return 0; +} + +char * +fsprobe_get_devname_by_spec(const char *spec) +{ + return blkid_evaluate_spec(spec, &blcache); +} + +int +fsprobe_known_fstype(const char *fstype) +{ + return blkid_known_fstype(fstype); +} + + +/* returns device LABEL, UUID, FSTYPE, ... by low-level + * probing interface + */ +static char * +fsprobe_get_value(const char *name, const char *devname, int *ambi) +{ + int fd, rc; + const char *data = NULL; + + if (!devname || !name) + return NULL; + fd = open(devname, O_RDONLY); + if (fd < 0) + return NULL; + if (!blprobe) + blprobe = blkid_new_probe(); + if (!blprobe) + goto done; + if (blkid_probe_set_device(blprobe, fd, 0, 0)) + goto done; + + blkid_probe_enable_superblocks(blprobe, 1); + + blkid_probe_set_superblocks_flags(blprobe, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | BLKID_SUBLKS_TYPE); + + rc = blkid_do_safeprobe(blprobe); + if (ambi) + *ambi = rc == -2 ? 1 : 0; /* ambivalent probing result */ + if (!rc) + blkid_probe_lookup_value(blprobe, name, &data, NULL); +done: + close(fd); + return data ? strdup((char *) data) : NULL; +} + +char * +fsprobe_get_label_by_devname(const char *devname) +{ + return fsprobe_get_value("LABEL", devname, NULL); +} + +char * +fsprobe_get_uuid_by_devname(const char *devname) +{ + return fsprobe_get_value("UUID", devname, NULL); +} + +char * +fsprobe_get_fstype_by_devname(const char *devname) +{ + return fsprobe_get_value("TYPE", devname, NULL); +} + +char * +fsprobe_get_fstype_by_devname_ambi(const char *devname, int *ambi) +{ + return fsprobe_get_value("TYPE", devname, ambi); +} + +char * +fsprobe_get_devname_by_uuid(const char *uuid) +{ + return blkid_evaluate_tag("UUID", uuid, &blcache); +} + +char * +fsprobe_get_devname_by_label(const char *label) +{ + return blkid_evaluate_tag("LABEL", label, &blcache); +} diff --git a/mount-deprecated/fsprobe.h b/mount-deprecated/fsprobe.h new file mode 100644 index 000000000..d9c1dc494 --- /dev/null +++ b/mount-deprecated/fsprobe.h @@ -0,0 +1,27 @@ +#ifndef FSPROBE_H +#define FSPROBE_H +/* + * THIS IS DEPRECATED -- use blkid_evaluate_* API rather than this extra layer + * + * This is the generic interface for filesystem guessing libraries. + * Implementations are provided by + */ +extern void fsprobe_init(void); +extern void fsprobe_exit(void); + +extern int fsprobe_parse_spec(const char *spec, char **name, char **value); + +/* all routines return newly allocated string */ +extern char *fsprobe_get_devname_by_uuid(const char *uuid); +extern char *fsprobe_get_devname_by_label(const char *label); +extern char *fsprobe_get_devname_by_spec(const char *spec); + +extern char *fsprobe_get_label_by_devname(const char *devname); +extern char *fsprobe_get_uuid_by_devname(const char *devname); +extern char *fsprobe_get_fstype_by_devname(const char *devname); +extern char *fsprobe_get_fstype_by_devname_ambi(const char *devname, int *ambi); + + +extern int fsprobe_known_fstype(const char *fstype); + +#endif /* FSPROBE_H */ diff --git a/mount-deprecated/fstab.c b/mount-deprecated/fstab.c new file mode 100644 index 000000000..90c8032fb --- /dev/null +++ b/mount-deprecated/fstab.c @@ -0,0 +1,1170 @@ +/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> + * - added Native Language Support + * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * - fixed strerr(errno) in gettext calls + */ + +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <time.h> +#include <mntent.h> + +#include "mount_mntent.h" +#include "fstab.h" +#include "sundries.h" +#include "fsprobe.h" +#include "pathnames.h" +#include "nls.h" +#include "usleep.h" +#include "strutils.h" + +#define streq(s, t) (strcmp ((s), (t)) == 0) + +/* Information about mtab. ------------------------------------*/ +static int have_mtab_info = 0; +static int var_mtab_does_not_exist = 0; +static int var_mtab_is_a_symlink = 0; + +static void +get_mtab_info(void) { + if (!have_mtab_info) { + struct stat mtab_stat; + + var_mtab_does_not_exist = 0; + var_mtab_is_a_symlink = 0; + + if (lstat(_PATH_MOUNTED, &mtab_stat)) + var_mtab_does_not_exist = 1; + else if (S_ISLNK(mtab_stat.st_mode)) + var_mtab_is_a_symlink = 1; + have_mtab_info = 1; + } +} + +void +reset_mtab_info(void) { + have_mtab_info = 0; +} + +int +mtab_does_not_exist(void) { + get_mtab_info(); + return var_mtab_does_not_exist; +} + +int +mtab_is_a_symlink(void) { + get_mtab_info(); + return var_mtab_is_a_symlink; +} + +int +mtab_is_writable() { + int fd; + + /* Should we write to /etc/mtab upon an update? + Probably not if it is a symlink to /proc/mounts, since that + would create a file /proc/mounts in case the proc filesystem + is not mounted. */ + if (mtab_is_a_symlink()) + return 0; + + fd = open(_PATH_MOUNTED, O_RDWR | O_CREAT, 0644); + if (fd >= 0) { + close(fd); + return 1; + } else + return 0; +} + +/* Contents of mtab and fstab ---------------------------------*/ + +struct mntentchn mounttable, fstab; +static int got_mtab = 0; +static int got_fstab = 0; + +static void read_mounttable(void), read_fstab(void); + +struct mntentchn * +mtab_head() { + if (!got_mtab) + read_mounttable(); + return &mounttable; +} + +struct mntentchn * +fstab_head() { + if (!got_fstab) + read_fstab(); + return &fstab; +} + +static void +my_free_mc(struct mntentchn *mc) { + if (mc) { + my_free(mc->m.mnt_fsname); + my_free(mc->m.mnt_dir); + my_free(mc->m.mnt_type); + my_free(mc->m.mnt_opts); + free(mc); + } +} + + +static void +discard_mntentchn(struct mntentchn *mc0) { + struct mntentchn *mc, *mc1; + + for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) { + mc1 = mc->nxt; + my_free_mc(mc); + } +} + +static void +read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) { + struct mntentchn *mc = mc0; + struct my_mntent *mnt; + + while ((mnt = my_getmntent(mfp)) != NULL) { + if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) { + mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc)); + mc->nxt->prev = mc; + mc = mc->nxt; + mc->m = *mnt; + mc->nxt = mc0; + } + } + mc0->prev = mc; + if (ferror(mfp->mntent_fp)) { + int errsv = errno; + error(_("warning: error reading %s: %s"), + fnam, strerror (errsv)); + mc0->nxt = mc0->prev = NULL; + } + my_endmntent(mfp); +} + +#ifdef HAVE_LIBMOUNT_MOUNT + +#define USE_UNSTABLE_LIBMOUNT_API +#include <libmount.h> /* libmount */ + +static void read_mounttable() +{ + struct mntentchn *mc0 = &mounttable, *mc = mc0; + struct libmnt_table *tb = mnt_new_table(); + struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD); + struct libmnt_fs *fs; + + got_mtab = 1; + mc->nxt = mc->prev = NULL; + + if (!tb || !itr) + goto err; + if (mnt_table_parse_mtab(tb, NULL)) + goto err; + + while(mnt_table_next_fs(tb, itr, &fs) == 0) { + const char *type = mnt_fs_get_fstype(fs); + struct my_mntent *mnt = NULL; + + if (type && strcmp(type, MNTTYPE_IGNORE) == 0) + continue; + if (mnt_fs_to_mntent(fs, (struct mntent **) &mnt)) + goto err; + mc->nxt = xmalloc(sizeof(*mc)); + mc->nxt->prev = mc; + mc = mc->nxt; + mc->m = *mnt; + mc->nxt = mc0; + } + + mc0->prev = mc; + return; +err: + error(_("warning: failed to read mtab")); + mnt_free_table(tb); + mnt_free_iter(itr); + mc->nxt = mc->prev = NULL; +} + +#else /* !HAVE_LIBMOUNT_MOUNT */ + +/* + * Read /etc/mtab. If that fails, try /proc/mounts. + * This produces a linked list. The list head mounttable is a dummy. + */ +static void +read_mounttable() { + mntFILE *mfp; + const char *fnam; + struct mntentchn *mc = &mounttable; + + got_mtab = 1; + mc->nxt = mc->prev = NULL; + + fnam = _PATH_MOUNTED; + mfp = my_setmntent (fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + int errsv = errno; + fnam = _PATH_PROC_MOUNTS; + mfp = my_setmntent (fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + error(_("warning: can't open %s: %s"), + _PATH_MOUNTED, strerror (errsv)); + return; + } + if (verbose) + printf (_("mount: could not open %s - " + "using %s instead\n"), + _PATH_MOUNTED, _PATH_PROC_MOUNTS); + } + read_mntentchn(mfp, fnam, mc); +} +#endif /* HAVE_LIBMOUNT_MOUNT */ + +static void +read_fstab() { + mntFILE *mfp = NULL; + const char *fnam; + struct mntentchn *mc = &fstab; + + got_fstab = 1; + mc->nxt = mc->prev = NULL; + + fnam = _PATH_MNTTAB; + mfp = my_setmntent (fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + int errsv = errno; + error(_("warning: can't open %s: %s"), + _PATH_MNTTAB, strerror (errsv)); + return; + } + read_mntentchn(mfp, fnam, mc); +} + + +/* Given the name NAME, try to find it in mtab. */ +struct mntentchn * +getmntfile (const char *name) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if (streq(mc->m.mnt_dir, name) || + streq(mc->m.mnt_fsname, name)) + return mc; + return NULL; +} + +/* + * Given the name NAME, and the place MCPREV we found it last time, + * try to find it in mtab. + */ +struct mntentchn * +getmntfilebackward (const char *name, struct mntentchn *mcprev) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + if (!mcprev) + mcprev = mc0; + + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_dir, name)) + return mc; + + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_fsname, name)) + return mc; + + return NULL; +} + +/* + * Given the directory name NAME, and the place MCPREV we found it last time, + * try to find more occurrences. + */ +struct mntentchn * +getmntdirbackward (const char *name, struct mntentchn *mcprev) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + if (!mcprev) + mcprev = mc0; + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_dir, name)) + return mc; + return NULL; +} + +/* + * Given the device name NAME, and the place MCPREV we found it last time, + * try to find more occurrences. + */ +struct mntentchn * +getmntdevbackward (const char *name, struct mntentchn *mcprev) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + if (!mcprev) + mcprev = mc0; + + /* canonical names in mtab */ + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) { + if (streq(mc->m.mnt_fsname, name)) + return mc; + } + + /* non-canonical names in mtab (this is BAD THING!) */ + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) { + char *cn = canonicalize(mc->m.mnt_fsname); + int res = cn ? streq(cn, name) : 0; + + free(cn); + if (res) + return mc; + } + + return NULL; +} + +/* + * Given the name NAME, check that it occurs precisely once as dir or dev. + */ +int +is_mounted_once(const char *name) { + struct mntentchn *mc, *mc0; + int ct = 0; + + mc0 = mtab_head(); + for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_dir, name) || + streq(mc->m.mnt_fsname, name)) + ct++; + return (ct == 1); +} + +/* Given the name FILE, try to find the option "loop=FILE" in mtab. */ +struct mntentchn * +getmntoptfile (const char *file) { + struct mntentchn *mc, *mc0; + const char *opts, *s; + int l; + + if (!file) + return NULL; + + l = strlen(file); + + mc0 = mtab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if ((opts = mc->m.mnt_opts) != NULL + && (s = strstr(opts, "loop=")) + && !strncmp(s+5, file, l) + && (s == opts || s[-1] == ',') + && (s[l+5] == 0 || s[l+5] == ',')) + return mc; + return NULL; +} + +/* compares "quoted" or 'quoted' with unquoted */ +static int +streq_quoted(const char *quoted, const char *unquoted) +{ + if (*quoted == '"' || *quoted == '\'') + return !strncmp(quoted + 1, unquoted, strlen(quoted) - 2); + + return streq(quoted, unquoted); +} + +static int +has_label(const char *device, const char *label) { + const char *devlabel; + int ret; + + devlabel = fsprobe_get_label_by_devname(device); + if (!devlabel) + return 0; + + ret = streq_quoted(label, devlabel); + my_free(devlabel); + return ret; +} + +static int +has_uuid(const char *device, const char *uuid){ + const char *devuuid; + int ret; + + devuuid = fsprobe_get_uuid_by_devname(device); + if (!devuuid) + return 0; + + ret = streq_quoted(uuid, devuuid); + my_free(devuuid); + return ret; +} + +/* Find the entry (DEV,DIR) in fstab -- spec and dir must be canonicalized! */ +struct mntentchn * +getfs_by_devdir (const char *dev, const char *dir) { + struct mntentchn *mc, *mc0; + + mc0 = fstab_head(); + + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + int ok = 1; + + /* dir */ + if (!streq(mc->m.mnt_dir, dir)) { + char *dr = canonicalize(mc->m.mnt_dir); + ok = streq(dr, dir); + my_free(dr); + } + + /* spec */ + if (ok && !streq(mc->m.mnt_fsname, dev)) { + const char *fs = mc->m.mnt_fsname; + + if (strncmp (fs, "LABEL=", 6) == 0) { + ok = has_label(dev, fs + 6); + } else if (strncmp (fs, "UUID=", 5) == 0) { + ok = has_uuid(dev, fs + 5); + } else { + fs = canonicalize_spec(mc->m.mnt_fsname); + ok = streq_except_trailing_slash(fs, dev); + my_free(fs); + } + } + if (ok) + return mc; + } + + return NULL; +} + +/* Find the dir DIR in fstab. */ +struct mntentchn * +getfs_by_dir (const char *dir) { + struct mntentchn *mc, *mc0; + char *cdir; + + mc0 = fstab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if (streq(mc->m.mnt_dir, dir)) + return mc; + + cdir = canonicalize(dir); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + int ok = streq(mc->m.mnt_dir, cdir); + if (!ok) { + char *dr = canonicalize(mc->m.mnt_dir); + ok = dr ? streq(dr, cdir) : 0; + free(dr); + } + if (ok) { + free(cdir); + return mc; + } + } + free(cdir); + return NULL; +} + +/* Find the device SPEC in fstab. */ +struct mntentchn * +getfs_by_spec (const char *spec) { + char *name = NULL, *value = NULL, *cspec; + struct mntentchn *mc = NULL; + + if (!spec) + return NULL; + + if (fsprobe_parse_spec(spec, &name, &value) != 0) + return NULL; /* parse error */ + + if (name) { + if (!strcmp(name,"LABEL")) + mc = getfs_by_label (value); + else if (!strcmp(name,"UUID")) + mc = getfs_by_uuid (value); + + free(name); + free(value); + return mc; + } + + cspec = canonicalize_spec(spec); + mc = getfs_by_devname(cspec); + free(cspec); + + if (!mc) + /* noncanonical name like /dev/cdrom */ + mc = getfs_by_devname(spec); + + return mc; +} + +/* Find the device in fstab. */ +struct mntentchn * +getfs_by_devname (const char *devname) { + struct mntentchn *mc, *mc0; + + mc0 = fstab_head(); + + /* canonical devname in fstab */ + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if (streq(mc->m.mnt_fsname, devname)) + return mc; + + /* noncanonical devname in fstab */ + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + char *fs; + + if (strncmp(mc->m.mnt_fsname, "LABEL=", 6) == 0 || + strncmp(mc->m.mnt_fsname, "UUID=", 5) == 0) + continue; + + fs = canonicalize_spec(mc->m.mnt_fsname); + if (streq(fs, devname)) { + free(fs); + return mc; + } + free(fs); + } + + return NULL; +} + + +/* Find the uuid UUID in fstab. */ +struct mntentchn * +getfs_by_uuid (const char *uuid) { + struct mntentchn *mc, *mc0; + + mc0 = fstab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if (strncmp (mc->m.mnt_fsname, "UUID=", 5) == 0 + && streq_quoted(mc->m.mnt_fsname + 5, uuid)) + return mc; + return NULL; +} + +/* Find the label LABEL in fstab. */ +struct mntentchn * +getfs_by_label (const char *label) { + struct mntentchn *mc, *mc0; + + mc0 = fstab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if (strncmp (mc->m.mnt_fsname, "LABEL=", 6) == 0 + && streq_quoted(mc->m.mnt_fsname + 6, label)) + return mc; + return NULL; +} + +/* Updating mtab ----------------------------------------------*/ + +/* Flag for already existing lock file. */ +static int we_created_lockfile = 0; +static int lockfile_fd = -1; + +/* Flag to indicate that signals have been set up. */ +static int signals_have_been_setup = 0; + +/* Ensure that the lock is released if we are interrupted. */ +#ifndef HAVE_STRSIGNAL_DECL +extern char *strsignal(int sig); /* not always in <string.h> */ +#endif + +static void +handler (int sig) { + die(EX_USER, "%s", strsignal(sig)); +} + +static void +setlkw_timeout (int sig __attribute__ ((__unused__))) { + /* nothing, fcntl will fail anyway */ +} + +/* Remove lock file. */ +void +unlock_mtab (void) { + if (we_created_lockfile) { + close(lockfile_fd); + lockfile_fd = -1; + unlink (_PATH_MOUNTED_LOCK); + we_created_lockfile = 0; + } +} + +/* Create the lock file. + The lock file will be removed if we catch a signal or when we exit. */ +/* The old code here used flock on a lock file /etc/mtab~ and deleted + this lock file afterwards. However, as rgooch remarks, that has a + race: a second mount may be waiting on the lock and proceed as + soon as the lock file is deleted by the first mount, and immediately + afterwards a third mount comes, creates a new /etc/mtab~, applies + flock to that, and also proceeds, so that the second and third mount + now both are scribbling in /etc/mtab. + The new code uses a link() instead of a creat(), where we proceed + only if it was us that created the lock, and hence we always have + to delete the lock afterwards. Now the use of flock() is in principle + superfluous, but avoids an arbitrary sleep(). */ + +/* Where does the link point to? Obvious choices are mtab and mtab~~. + HJLu points out that the latter leads to races. Right now we use + mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */ +#define MOUNTLOCK_LINKTARGET _PATH_MOUNTED_LOCK "%d" +#define MOUNTLOCK_LINKTARGET_LTH (sizeof(_PATH_MOUNTED_LOCK)+20) + +/* + * The original mount locking code has used sleep(1) between attempts and + * maximal number of attemps has been 5. + * + * There was very small number of attempts and extremely long waiting (1s) + * that is useless on machines with large number of concurret mount processes. + * + * Now we wait few thousand microseconds between attempts and we have global + * time limit (30s) rather than limit for number of attempts. The advantage + * is that this method also counts time which we spend in fcntl(F_SETLKW) and + * number of attempts is not so much restricted. + * + * -- kzak@redhat.com [2007-Mar-2007] + */ + +/* maximum seconds between first and last attempt */ +#define MOUNTLOCK_MAXTIME 30 + +/* sleep time (in microseconds, max=999999) between attempts */ +#define MOUNTLOCK_WAITTIME 5000 + +void +lock_mtab (void) { + int i; + struct timespec waittime; + struct timeval maxtime; + char linktargetfile[MOUNTLOCK_LINKTARGET_LTH]; + + if (!signals_have_been_setup) { + int sig = 0; + struct sigaction sa; + + sa.sa_handler = handler; + sa.sa_flags = 0; + sigfillset (&sa.sa_mask); + + while (sigismember (&sa.sa_mask, ++sig) != -1 + && sig != SIGCHLD) { + if (sig == SIGALRM) + sa.sa_handler = setlkw_timeout; + else + sa.sa_handler = handler; + sigaction (sig, &sa, (struct sigaction *) 0); + } + signals_have_been_setup = 1; + } + + sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ()); + + i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); + if (i < 0) { + int errsv = errno; + /* linktargetfile does not exist (as a file) + and we cannot create it. Read-only filesystem? + Too many files open in the system? + Filesystem full? */ + die (EX_FILEIO, _("can't create lock file %s: %s " + "(use -n flag to override)"), + linktargetfile, strerror (errsv)); + } + close(i); + + gettimeofday(&maxtime, NULL); + maxtime.tv_sec += MOUNTLOCK_MAXTIME; + + waittime.tv_sec = 0; + waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME); + + /* Repeat until it was us who made the link */ + while (!we_created_lockfile) { + struct timeval now; + struct flock flock; + int errsv, j; + + j = link(linktargetfile, _PATH_MOUNTED_LOCK); + errsv = errno; + + if (j == 0) + we_created_lockfile = 1; + + if (j < 0 && errsv != EEXIST) { + (void) unlink(linktargetfile); + die (EX_FILEIO, _("can't link lock file %s: %s " + "(use -n flag to override)"), + _PATH_MOUNTED_LOCK, strerror (errsv)); + } + + lockfile_fd = open (_PATH_MOUNTED_LOCK, O_WRONLY); + + if (lockfile_fd < 0) { + /* Strange... Maybe the file was just deleted? */ + int errsv = errno; + gettimeofday(&now, NULL); + if (errno == ENOENT && now.tv_sec < maxtime.tv_sec) { + we_created_lockfile = 0; + continue; + } + (void) unlink(linktargetfile); + die (EX_FILEIO, _("can't open lock file %s: %s " + "(use -n flag to override)"), + _PATH_MOUNTED_LOCK, strerror (errsv)); + } + + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + if (j == 0) { + /* We made the link. Now claim the lock. */ + if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) { + if (verbose) { + int errsv = errno; + printf(_("Can't lock lock file %s: %s\n"), + _PATH_MOUNTED_LOCK, strerror (errsv)); + } + /* proceed, since it was us who created the lockfile anyway */ + } + (void) unlink(linktargetfile); + } else { + /* Someone else made the link. Wait. */ + gettimeofday(&now, NULL); + if (now.tv_sec < maxtime.tv_sec) { + alarm(maxtime.tv_sec - now.tv_sec); + if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) { + int errsv = errno; + (void) unlink(linktargetfile); + die (EX_FILEIO, _("can't lock lock file %s: %s"), + _PATH_MOUNTED_LOCK, (errno == EINTR) ? + _("timed out") : strerror (errsv)); + } + alarm(0); + + nanosleep(&waittime, NULL); + } else { + (void) unlink(linktargetfile); + die (EX_FILEIO, _("Cannot create link %s\n" + "Perhaps there is a stale lock file?\n"), + _PATH_MOUNTED_LOCK); + } + close(lockfile_fd); + } + } +} + +/* returns whole option with name @optname from @src list */ +char * +get_option(const char *optname, const char *src, size_t *len) +{ + char *opt, *end; + size_t sz; + + if (!src) + return NULL; + + opt = strstr(src, optname); + if (!opt) + return NULL; + + end = strchr(opt, ','); + sz = end && end > opt ? (size_t) (end - opt) : strlen(opt); + + if (len) + *len = sz; + + if ((opt == src || *(opt - 1) == ',') && + (*(opt + sz) == '\0' || *(opt + sz) == ',')) + return opt; + + return NULL; +} + + /* If @list contains "user=peter" and @s is "user=", return "peter" */ +char * +get_option_value(const char *list, const char *s) +{ + const char *t; + size_t n = strlen(s); + + while (list && *list) { + if (strncmp(list, s, n) == 0) { + s = t = list + n; + while (*s && *s != ',') + s++; + return xstrndup(t, s-t); + } + while (*list && *list++ != ',') ; + } + return NULL; +} + +static int +cpy_option(const char *optname, char *dest, const char *src) +{ + char *opt; + size_t sz; + + opt = get_option(optname, src, &sz); + if (!opt) + /* the option doesn't exist */ + return 0; + + if (get_option(optname, dest, NULL)) + /* the options is already in dest */ + return 0; + + if (*dest) { + dest = dest + strlen(dest); + *dest++ = ','; /* separator */ + } + memcpy(dest, opt, sz); /* copy option */ + *(dest + sz) = '\0'; /* terminator */ + + return 1; +} + +/* Generates (and allocates) new options for remount + * + * We cannot blindly replace the old options, otherwise we will lost some + * internally generated stuff (e.g loop=). + */ +static char * +mk_remount_opts(const char *old, const char *instead) +{ + char *new; + size_t sz; + + if (old == NULL && instead == NULL) + return NULL; + if (!old) + return xstrdup(instead); + + /* max size of new options is: + * old + new + '\0' + separator (for each copied option) + */ + sz = strlen(old) + (instead ? strlen(instead) : 0) + 2; + new = xmalloc(sz); + if (instead && *instead) + strncpy(new, instead, sz); + else + *new = '\0'; + + cpy_option("loop=", new, old); + + return new; +} + +/* + * Update the mtab. + * Used by umount with null INSTEAD: remove the last DIR entry. + * Used by mount upon a remount: update option part, + * and complain if a wrong device or type was given. + * [Note that often a remount will be a rw remount of / + * where there was no entry before, and we'll have to believe + * the values given in INSTEAD.] + */ + +void +update_mtab (const char *dir, struct my_mntent *instead) { + mntFILE *mfp, *mftmp; + const char *fnam = _PATH_MOUNTED; + struct mntentchn mtabhead; /* dummy */ + struct mntentchn *mc, *mc0 = NULL, *absent = NULL; + struct stat sbuf; + int fd; + + if (mtab_does_not_exist() || !mtab_is_writable()) + return; + + lock_mtab(); + + /* having locked mtab, read it again */ + mc0 = mc = &mtabhead; + mc->nxt = mc->prev = NULL; + + mfp = my_setmntent(fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + int errsv = errno; + error (_("cannot open %s (%s) - mtab not updated"), + fnam, strerror (errsv)); + goto leave; + } + + read_mntentchn(mfp, fnam, mc); + + /* find last occurrence of dir */ + if (dir) { + for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_dir, dir)) + break; + } + if (dir && mc && mc != mc0) { + if (instead == NULL) { + /* An umount - remove entry */ + if (mc && mc != mc0) { + mc->prev->nxt = mc->nxt; + mc->nxt->prev = mc->prev; + my_free_mc(mc); + } + } else if (!strcmp(mc->m.mnt_dir, instead->mnt_dir)) { + /* A remount */ + char *opts = mk_remount_opts(mc->m.mnt_opts, + instead->mnt_opts); + my_free(mc->m.mnt_opts); + mc->m.mnt_opts = opts; + } else { + /* A move */ + my_free(mc->m.mnt_dir); + mc->m.mnt_dir = xstrdup(instead->mnt_dir); + } + } else if (instead) { + /* not found, add a new entry */ + absent = xmalloc(sizeof(*absent)); + absent->m.mnt_fsname = xstrdup(instead->mnt_fsname); + absent->m.mnt_dir = xstrdup(instead->mnt_dir); + absent->m.mnt_type = xstrdup(instead->mnt_type); + absent->m.mnt_opts = xstrdup(instead->mnt_opts); + absent->m.mnt_freq = instead->mnt_freq; + absent->m.mnt_passno = instead->mnt_passno; + absent->nxt = mc0; + if (mc0->prev != NULL) { + absent->prev = mc0->prev; + mc0->prev->nxt = absent; + } else { + absent->prev = mc0; + } + mc0->prev = absent; + if (mc0->nxt == NULL) + mc0->nxt = absent; + } + + /* write chain to mtemp */ + mftmp = my_setmntent (_PATH_MOUNTED_TMP, "w"); + if (mftmp == NULL || mftmp->mntent_fp == NULL) { + int errsv = errno; + error (_("cannot open %s (%s) - mtab not updated"), + _PATH_MOUNTED_TMP, strerror (errsv)); + goto leave; + } + + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + if (my_addmntent(mftmp, &(mc->m)) == 1) { + int errsv = errno; + error(_("error writing %s: %s"), + _PATH_MOUNTED_TMP, strerror (errsv)); + goto leave; + } + } + + discard_mntentchn(mc0); + mc0 = NULL; + + /* + * We have to be paranoid with write() to avoid incomplete + * /etc/mtab. Users are able to control writing by RLIMIT_FSIZE. + */ + if (fflush(mftmp->mntent_fp) != 0) { + int errsv = errno; + error (_("%s: cannot fflush changes: %s"), + _PATH_MOUNTED_TMP, strerror (errsv)); + goto leave; + } + + fd = fileno(mftmp->mntent_fp); + + /* + * It seems that better is incomplete and broken /etc/mtab that + * /etc/mtab that is writeable for non-root users. + * + * We always skip rename() when chown() and chmod() failed. + * -- kzak, 11-Oct-2007 + */ + + if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + int errsv = errno; + fprintf(stderr, _("error changing mode of %s: %s\n"), + _PATH_MOUNTED_TMP, strerror (errsv)); + goto leave; + } + + /* + * If mount is setuid and some non-root user mounts sth, + * then mtab.tmp might get the group of this user. Copy uid/gid + * from the present mtab before renaming. + */ + if (stat(_PATH_MOUNTED, &sbuf) == 0) { + if (fchown(fd, sbuf.st_uid, sbuf.st_gid) < 0) { + int errsv = errno; + fprintf (stderr, _("error changing owner of %s: %s\n"), + _PATH_MOUNTED_TMP, strerror(errsv)); + goto leave; + } + } + + my_endmntent (mftmp); + + /* rename mtemp to mtab */ + if (rename (_PATH_MOUNTED_TMP, _PATH_MOUNTED) < 0) { + int errsv = errno; + fprintf(stderr, _("can't rename %s to %s: %s\n"), + _PATH_MOUNTED_TMP, _PATH_MOUNTED, strerror(errsv)); + } + + leave: + if (mc0) + discard_mntentchn(mc0); + unlink(_PATH_MOUNTED_TMP); + unlock_mtab(); +} + + +#ifdef MAIN_TEST_MTABLOCK + +/* + * This is mtab locking code test for: + * + * - performance (how many concurrent processes) + * + * - lock reliability (is possible to see corrupted data if more + * concurrent processes modify a same file) + * + * The test is very simple -- it reads a number from locked file, increments the + * number and writes the number back to the file. + */ +/* dummy */ +char *fsprobe_get_label_by_devname(const char *spec __attribute__((__unused__))) +{ + return NULL; +} +char *fsprobe_get_uuid_by_devname(const char *spec __attribute__((__unused__))) +{ + return NULL; +} +int fsprobe_parse_spec(const char *spec __attribute__((__unused__)), + char **name __attribute__((__unused__)), + char **value __attribute__((__unused__))) +{ + return 0; +} +struct my_mntent *my_getmntent (mntFILE *mfp __attribute__((__unused__))) +{ + return NULL; +} +mntFILE *my_setmntent (const char *file __attribute__((__unused__)), + char *mode __attribute__((__unused__))) +{ + return NULL; +} +void my_endmntent (mntFILE *mfp __attribute__((__unused__))) +{ + /* nothing */ +} +int my_addmntent (mntFILE *mfp __attribute__((__unused__)), + struct my_mntent *mnt __attribute__((__unused__))) +{ + return 0; +} + +int +main(int argc, char **argv) +{ + time_t synctime; + char *filename; + int nloops, id, i; + pid_t pid = getpid(); + unsigned int usecs; + struct timeval tv; + struct stat st; + long last = 0; + + progname = argv[0]; + + if (argc < 3) + die(EXIT_FAILURE, + "usage: %s <id> <synctime> <file> <nloops>\n", + progname); + + id = atoi(argv[1]); + synctime = (time_t) atol(argv[2]); + filename = argv[3]; + nloops = atoi(argv[4]); + + if (stat(filename, &st) < -1) + die(EXIT_FAILURE, "%s: %m\n", filename); + + fprintf(stderr, "%05d (pid=%05d): START\n", id, pid); + + gettimeofday(&tv, NULL); + if (synctime && synctime - tv.tv_sec > 1) { + usecs = ((synctime - tv.tv_sec) * 1000000UL) - + (1000000UL - tv.tv_usec); + usleep(usecs); + } + + for (i = 0; i < nloops; i++) { + FILE *f; + long num; + char buf[256]; + + lock_mtab(); + + if (!(f = fopen(filename, "r"))) { + unlock_mtab(); + die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): " + "open for read failed\n", id, pid, i); + } + if (!fgets(buf, sizeof(buf), f)) { + unlock_mtab(); + die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): " + "read failed\n", id, pid, i); + } + fclose(f); + + num = atol(buf) + 1; + + if (!(f = fopen(filename, "w"))) { + unlock_mtab(); + die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): " + "open for write failed\n", id, pid, i); + } + fprintf(f, "%ld", num); + fclose(f); + + unlock_mtab(); + + gettimeofday(&tv, NULL); + + fprintf(stderr, "%010ld.%06ld %04d (pid=%05d, loop=%05d): " + "num=%09ld last=%09ld\n", + tv.tv_sec, tv.tv_usec, id, + pid, i, num, last); + last = num; + + /* The mount command usually finish after mtab update. We + * simulate this via short sleep -- it's also enough to make + * concurrent processes happy. + */ + usleep(50000); + } + + fprintf(stderr, "%05d (pid=%05d): DONE\n", id, pid); + + exit(EXIT_SUCCESS); +} +#endif diff --git a/mount-deprecated/fstab.h b/mount-deprecated/fstab.h new file mode 100644 index 000000000..bbef33b51 --- /dev/null +++ b/mount-deprecated/fstab.h @@ -0,0 +1,44 @@ +#ifndef MOUNT_FSTAB_H +#define MOUNT_FSTAB_H + +#include "mount_mntent.h" + +#ifdef HAVE_LIBMOUNT_MOUNT +#define USE_UNSTABLE_LIBMOUNT_API +#include <libmount.h> +#endif + +int mtab_is_writable(void); +int mtab_is_a_symlink(void); +int mtab_does_not_exist(void); +void reset_mtab_info(void); +int is_mounted_once(const char *name); + +struct mntentchn { + struct mntentchn *nxt, *prev; + struct my_mntent m; +}; + +struct mntentchn *mtab_head (void); +struct mntentchn *getmntfile (const char *name); +struct mntentchn *getmntfilebackward (const char *name, struct mntentchn *mcprev); +struct mntentchn *getmntoptfile (const char *file); +struct mntentchn *getmntdirbackward (const char *dir, struct mntentchn *mc); +struct mntentchn *getmntdevbackward (const char *dev, struct mntentchn *mc); + +struct mntentchn *fstab_head (void); +struct mntentchn *getfs_by_dir (const char *dir); +struct mntentchn *getfs_by_spec (const char *spec); +struct mntentchn *getfs_by_devname (const char *devname); +struct mntentchn *getfs_by_devdir (const char *dev, const char *dir); +struct mntentchn *getfs_by_uuid (const char *uuid); +struct mntentchn *getfs_by_label (const char *label); + +void lock_mtab (void); +void unlock_mtab (void); +void update_mtab (const char *special, struct my_mntent *with); + +char *get_option(const char *optname, const char *src, size_t *len); +char *get_option_value(const char *list, const char *s); + +#endif /* MOUNT_FSTAB_H */ diff --git a/mount-deprecated/getusername.c b/mount-deprecated/getusername.c new file mode 100644 index 000000000..9835768ca --- /dev/null +++ b/mount-deprecated/getusername.c @@ -0,0 +1,14 @@ +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#include "getusername.h" + +char * +getusername() { + char *user = 0; + struct passwd *pw = getpwuid(getuid()); + + if (pw) + user = pw->pw_name; + return user; +} diff --git a/mount-deprecated/getusername.h b/mount-deprecated/getusername.h new file mode 100644 index 000000000..808ac9b88 --- /dev/null +++ b/mount-deprecated/getusername.h @@ -0,0 +1 @@ +extern char *getusername(void); diff --git a/mount-deprecated/mount.8 b/mount-deprecated/mount.8 new file mode 100644 index 000000000..91dcc24f2 --- /dev/null +++ b/mount-deprecated/mount.8 @@ -0,0 +1,2857 @@ +.\" Copyright (c) 1996-2004 Andries Brouwer +.\" +.\" This page is somewhat derived from a page that was +.\" (c) 1980, 1989, 1991 The Regents of the University of California +.\" and had been heavily modified by Rik Faith and myself. +.\" (Probably no BSD text remains.) +.\" Fragments of text were written by Werner Almesberger, Remy Card, +.\" Stephen Tweedie and Eric Youngdale. +.\" +.\" This is free documentation; 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. +.\" +.\" The GNU General Public License's references to "object code" +.\" and "executables" are to be interpreted as the output of any +.\" document formatting or typesetting system, including +.\" intermediate and printed output. +.\" +.\" This manual 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., +.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" 960705, aeb: version for mount-2.7g +.\" 970114, aeb: xiafs and ext are dead; romfs is new +.\" 970623, aeb: -F option +.\" 970914, reg: -s option +.\" 981111, K.Garloff: /etc/filesystems +.\" 990111, aeb: documented /sbin/mount.smbfs +.\" 990730, Yann Droneaud <lch@multimania.com>: updated page +.\" 991214, Elrond <Elrond@Wunder-Nett.org>: added some docs on devpts +.\" 010714, Michael K. Johnson <johnsonm@redhat.com> added -O +.\" 010725, Nikita Danilov <NikitaDanilov@Yahoo.COM>: reiserfs options +.\" 011124, Karl Eichwalder <ke@gnu.franken.de>: tmpfs options +.\" +.TH MOUNT 8 "December 2004" "util-linux" "System Administration" +.SH NAME +mount \- mount a filesystem +.SH SYNOPSIS +.B mount +.RB [ \-lhV ] +.LP +.BI "mount \-a +.RB [ \-fFnrsvw ] +.RB [ \-t +.IR vfstype ] +.RB [ \-O +.IR optlist ] +.LP +.B mount +.RB [ \-fnrsvw ] +.RB [ \-o +.IR option [ \fB,\fPoption ]...] +.IR device | dir +.LP +.B mount +.RB [ \-fnrsvw ] +.RB [ \-t +.IB vfstype ] +.RB [ \-o +.IR options ] +.I device dir +.SH DESCRIPTION +All files accessible in a Unix system are arranged in one big +tree, the file hierarchy, rooted at +.BR / . +These files can be spread out over several devices. The +.B mount +command serves to attach the filesystem found on some device +to the big file tree. Conversely, the +.BR umount (8) +command will detach it again. + +The standard form of the +.B mount +command, is +.RS + +.br +.BI "mount \-t" " type device dir" +.br + +.RE +This tells the kernel to attach the filesystem found on +.I device +(which is of type +.IR type ) +at the directory +.IR dir . +The previous contents (if any) and owner and mode of +.I dir +become invisible, and as long as this filesystem remains mounted, +the pathname +.I dir +refers to the root of the filesystem on +.IR device . + +If only directory or device is given, for example: +.RS + +.br +.BI "mount /dir" +.br + +.RE +then mount looks for a mountpoint and if not found then for a device in the +/etc/fstab file. + +.B The listing and help. +.RS +Three forms of invocation do not actually mount anything: +.TP +.B "mount \-h" +prints a help message +.TP +.B "mount \-V" +prints a version string +.TP +.BR "mount " [ -l "] [" "-t \fItype\fP" ] +lists all mounted filesystems (of type +.IR type ). +The option \-l adds the labels in this listing. +See below. +.RE + +.B The device indication. +.RS +Most devices are indicated by a file name (of a block special device), like +.IR /dev/sda1 , +but there are other possibilities. For example, in the case of an NFS mount, +.I device +may look like +.IR knuth.cwi.nl:/dir . +It is possible to indicate a block special device using its +volume +.B LABEL +or +.B UUID +(see the \-L and \-U options below). + +The recommended setup is to use LABEL=<label> or UUID=<uuid> tags rather than +.B /dev/disk/by-{label,uuid} +udev symlinks in the /etc/fstab file. The tags are +more readable, robust and portable. The +.BR mount (8) +command internally uses udev +symlinks, so use the symlinks in /etc/fstab has no advantage over LABEL=/UUID=. +For more details see +.BR libblkid (3). + +Note that +.BR mount (8) +uses UUIDs as strings. The UUIDs from command line or +.BR fstab (5) +are not converted to internal binary representation. The string representation +of the UUID should be based on lower case characters. + +The +.I proc +filesystem is not associated with a special device, and when +mounting it, an arbitrary keyword, such as +.I proc +can be used instead of a device specification. +(The customary choice +.I none +is less fortunate: the error message `none busy' from +.B umount +can be confusing.) +.RE + +.B The /etc/fstab, /etc/mtab and /proc/mounts files. +.RS +The file +.I /etc/fstab +(see +.BR fstab (5)), +may contain lines describing what devices are usually +mounted where, using which options. +.LP +The command +.RS +.sp +.B mount \-a +.RB [ \-t +.IR type ] +.RB [ \-O +.IR optlist ] +.sp +.RE +(usually given in a bootscript) causes all filesystems mentioned in +.I fstab +(of the proper type and/or having or not having the proper options) +to be mounted as indicated, except for those whose line contains the +.B noauto +keyword. Adding the +.B \-F +option will make mount fork, so that the +filesystems are mounted simultaneously. +.LP +When mounting a filesystem mentioned in +.IR fstab +or +.IR mtab , +it suffices to give only the device, or only the mount point. + + +The programs +.B mount +and +.B umount +maintain a list of currently mounted filesystems in the file +.IR /etc/mtab . +If no arguments are given to +.BR mount , +this list is printed. + +The +.B mount +program does not read the +.I /etc/fstab +file if +.I device +(or LABEL/UUID) and +.I dir +are specified. For example: +.RS +.sp +.B "mount /dev/foo /dir" +.sp +.RE +If you want to override mount options from +.I /etc/fstab +you have to use: +.RS +.sp +.B "mount device|dir -o <options>" +.sp +.RE +and then the mount options from command line will be appended to +the list of options from +.IR /etc/fstab . +The usual behaviour is that the last option wins if there is more duplicated +options. + +When the +.I proc +filesystem is mounted (say at +.IR /proc ), +the files +.I /etc/mtab +and +.I /proc/mounts +have very similar contents. The former has somewhat +more information, such as the mount options used, +but is not necessarily up-to-date (cf. the +.B \-n +option below). It is possible to replace +.I /etc/mtab +by a symbolic link to +.IR /proc/mounts , +and especially when you have very large numbers of mounts +things will be much faster with that symlink, +but some information is lost that way, and in particular +using the "user" option will fail. +.RE + +.B The non-superuser mounts. +.RS +Normally, only the superuser can mount filesystems. +However, when +.I fstab +contains the +.B user +option on a line, anybody can mount the corresponding system. +.LP +Thus, given a line +.RS +.sp +.B "/dev/cdrom /cd iso9660 ro,user,noauto,unhide" +.sp +.RE +any user can mount the iso9660 filesystem found on his CDROM +using the command +.RS +.sp +.B "mount /dev/cdrom" +.sp +.RE +or +.RS +.sp +.B "mount /cd" +.sp +.RE +For more details, see +.BR fstab (5). +Only the user that mounted a filesystem can unmount it again. +If any user should be able to unmount, then use +.B users +instead of +.B user +in the +.I fstab +line. +The +.B owner +option is similar to the +.B user +option, with the restriction that the user must be the owner +of the special file. This may be useful e.g. for +.I /dev/fd +if a login script makes the console user owner of this device. +The +.B group +option is similar, with the restriction that the user must be +member of the group of the special file. +.RE + + +.B The bind mounts. +.RS +.\" In fact since 2.3.99. At first the syntax was mount -t bind. +Since Linux 2.4.0 it is possible to remount part of the +file hierarchy somewhere else. The call is +.RS +.br +.B mount --bind +.I olddir newdir +.RE +or shortoption +.RS +.br +.B mount -B +.I olddir newdir +.RE +or fstab entry is: +.RS +.br +.I /olddir +.I /newdir +.B none bind +.RE + +After this call the same contents is accessible in two places. +One can also remount a single file (on a single file). It's also +possible to use the bind mount to create a mountpoint from a regular +directory, for example: + +.RS +.br +.B mount --bind +.I foo foo +.RE + +The bind mount call attaches only (part of) a single filesystem, not possible +submounts. The entire file hierarchy including submounts is attached +a second place using + +.RS +.br +.B mount --rbind +.I olddir newdir +.RE + +or shortoption + +.RS +.br +.B mount -R +.I olddir newdir +.RE +.\" available since Linux 2.4.11. + +Note that the filesystem mount options will remain the same as those +on the original mount point, and cannot be changed by passing the -o +option along with --bind/--rbind. The mount options can be +changed by a separate remount command, for example: + +.RS +.br +.B mount --bind +.I olddir newdir +.br +.B mount -o remount,ro +.I newdir +.RE + +Note that behavior of the remount operation depends on the /etc/mtab file. The +first command stores the 'bind' flag to the /etc/mtab file and the second +command reads the flag from the file. If you have a system without the +/etc/mtab file or if you explicitly define source and target for the remount +command (then mount(8) does not read /etc/mtab), then you have to use bind flag +(or option) for the remount command too. For example: + +.RS +.br +.B mount --bind +.I olddir newdir +.br +.B mount -o remount,ro,bind +.I olddir newdir +.RE +.RE + +.B The move operation. +.RS +Since Linux 2.5.1 it is possible to atomically move a +.B mounted tree +to another place. The call is +.RS +.br +.B mount --move +.I olddir newdir +.RE +or shortoption +.RS +.br +.B mount -M +.I olddir newdir +.RE +This will cause the contents which previously appeared under olddir to be +accessed under newdir. The physical location of the files is not changed. +Note that the +.I olddir +has to be a mountpoint. +.RE + +.B The shared subtrees operations. +.RS +Since Linux 2.6.15 it is possible to mark a mount and its submounts as shared, +private, slave or unbindable. A shared mount provides ability to create mirrors +of that mount such that mounts and umounts within any of the mirrors propagate +to the other mirror. A slave mount receives propagation from its master, but +any not vice-versa. A private mount carries no propagation abilities. A +unbindable mount is a private mount which cannot be cloned through a bind +operation. Detailed semantics is documented in Documentation/filesystems/sharedsubtree.txt +file in the kernel source tree. + +.RS +.nf +.BI "mount --make-shared " mountpoint +.BI "mount --make-slave " mountpoint +.BI "mount --make-private " mountpoint +.BI "mount --make-unbindable " mountpoint +.fi +.RE + +The following commands allows one to recursively change the type of all the +mounts under a given mountpoint. + +.RS +.nf +.BI "mount --make-rshared " mountpoint +.BI "mount --make-rslave " mountpoint +.BI "mount --make-rprivate " mountpoint +.BI "mount --make-runbindable " mountpoint +.fi +.RE +.RE + +.SH COMMAND LINE OPTIONS +The full set of mount options used by an invocation of +.B mount +is determined by first extracting the +mount options for the filesystem from the +.I fstab +table, then applying any options specified by the +.B \-o +argument, and finally applying a +.BR \-r " or " \-w +option, when present. + +Command line options available for the +.B mount +command: +.IP "\fB\-V, \-\-version\fP" +Output version. +.IP "\fB\-h, \-\-help\fP" +Print a help message. +.IP "\fB\-v, \-\-verbose\fP" +Verbose mode. +.IP "\fB\-a, \-\-all\fP" +Mount all filesystems (of the given types) mentioned in +.IR fstab . +.IP "\fB\-F, \-\-fork\fP" +(Used in conjunction with +.BR \-a .) +Fork off a new incarnation of mount for each device. +This will do the mounts on different devices or different NFS servers +in parallel. +This has the advantage that it is faster; also NFS timeouts go in +parallel. A disadvantage is that the mounts are done in undefined order. +Thus, you cannot use this option if you want to mount both +.I /usr +and +.IR /usr/spool . +.IP "\fB\-f, \-\-fake\fP" +Causes everything to be done except for the actual system call; if it's not +obvious, this ``fakes'' mounting the filesystem. This option is useful in +conjunction with the +.B \-v +flag to determine what the +.B mount +command is trying to do. It can also be used to add entries for devices +that were mounted earlier with the -n option. The -f option checks for +existing record in /etc/mtab and fails when the record already +exists (with regular non-fake mount, this check is done by kernel). +.IP "\fB\-i, \-\-internal\-only\fP" +Don't call the /sbin/mount.<filesystem> helper even if it exists. +.IP "\fB\-l\fP" +Add the labels in the mount output. Mount must have +permission to read the disk device (e.g. be suid root) for this to work. +One can set such a label for ext2, ext3 or ext4 using the +.BR e2label (8) +utility, or for XFS using +.BR xfs_admin (8), +or for reiserfs using +.BR reiserfstune (8). +.IP "\fB\-n, \-\-no\-mtab\fP" +Mount without writing in +.IR /etc/mtab . +This is necessary for example when +.I /etc +is on a read-only filesystem. +.IP "\fB\-\-no\-canonicalize\fP" +Don't canonicalize paths. The mount command canonicalizes all paths +(from command line or fstab) and stores canonicalized paths to the +.IR /etc/mtab +file. This option can be used together with the +.B \-f +flag for already canonicalized absolut paths. +.IP "\fB\-p, \-\-pass\-fd \fInum\fP" +In case of a loop mount with encryption, read the passphrase from +file descriptor +.I num +instead of from the terminal. +.IP "\fB\-s\fP" +Tolerate sloppy mount options rather than failing. This will ignore +mount options not supported by a filesystem type. Not all filesystems +support this option. This option exists for support of the Linux +autofs\-based automounter. +.IP "\fB\-r, \-\-read\-only\fP" +Mount the filesystem read-only. A synonym is +.BR "\-o ro" . + +Note that, depending on the filesystem type, state and kernel behavior, the +system may still write to the device. For example, Ext3 or ext4 will replay its +journal if the filesystem is dirty. To prevent this kind of write access, you +may want to mount ext3 or ext4 filesystem with "ro,noload" mount options or +set the block device to read-only mode, see command +.BR blockdev (8). +.IP "\fB\-w, \-\-rw\fP" +Mount the filesystem read/write. This is the default. A synonym is +.BR "\-o rw" . +.IP "\fB\-L \fIlabel\fP" +Mount the partition that has the specified +.IR label . +.IP "\fB\-U \fIuuid\fP" +Mount the partition that has the specified +.IR uuid . +These two options require the file +.I /proc/partitions +(present since Linux 2.1.116) to exist. +.IP "\fB\-t, \-\-types \fIvfstype\fP" +The argument following the +.B \-t +is used to indicate the filesystem type. The filesystem types which are +currently supported include: +.IR adfs , +.IR affs , +.IR autofs , +.IR cifs , +.IR coda , +.IR coherent , +.IR cramfs , +.IR debugfs , +.IR devpts , +.IR efs , +.IR ext , +.IR ext2 , +.IR ext3 , +.IR ext4 , +.IR hfs , +.IR hfsplus , +.IR hpfs , +.IR iso9660 , +.IR jfs , +.IR minix , +.IR msdos , +.IR ncpfs , +.IR nfs , +.IR nfs4 , +.IR ntfs , +.IR proc , +.IR qnx4 , +.IR ramfs , +.IR reiserfs , +.IR romfs , +.IR squashfs , +.IR smbfs , +.IR sysv , +.IR tmpfs , +.IR ubifs , +.IR udf , +.IR ufs , +.IR umsdos , +.IR usbfs , +.IR vfat , +.IR xenix , +.IR xfs , +.IR xiafs . +Note that coherent, sysv and xenix are equivalent and that +.I xenix +and +.I coherent +will be removed at some point in the future \(em use +.I sysv +instead. Since kernel version 2.1.21 the types +.I ext +and +.I xiafs +do not exist anymore. Earlier, +.I usbfs +was known as +.IR usbdevfs . +Note, the real list of all supported filesystems depends on your +kernel. + +The programs +.B mount +and +.B umount +support filesystem subtypes. The subtype is defined by '.subtype' suffix. For +example 'fuse.sshfs'. It's recommended to use subtype notation rather than add +any prefix to the mount source (for example 'sshfs#example.com' is +depreacated). + +For most types all the +.B mount +program has to do is issue a simple +.IR mount (2) +system call, and no detailed knowledge of the filesystem type is required. +For a few types however (like nfs, nfs4, cifs, smbfs, ncpfs) ad hoc code is +necessary. The nfs, nfs4, cifs, smbfs, and ncpfs filesystems +have a separate mount program. In order to make it possible to +treat all types in a uniform way, mount will execute the program +.BI /sbin/mount. TYPE +(if that exists) when called with type +.IR TYPE . +Since various versions of the +.B smbmount +program have different calling conventions, +.B /sbin/mount.smbfs +may have to be a shell script that sets up the desired call. + +If no +.B \-t +option is given, or if the +.B auto +type is specified, mount will try to guess the desired type. +Mount uses the blkid library for guessing the filesystem +type; if that does not turn up anything that looks familiar, +mount will try to read the file +.IR /etc/filesystems , +or, if that does not exist, +.IR /proc/filesystems . +All of the filesystem types listed there will be tried, +except for those that are labeled "nodev" (e.g., +.IR devpts , +.I proc +and +.IR nfs ). +If +.I /etc/filesystems +ends in a line with a single * only, mount will read +.I /proc/filesystems +afterwards. + +The +.B auto +type may be useful for user-mounted floppies. +Creating a file +.I /etc/filesystems +can be useful to change the probe order (e.g., to try vfat before msdos +or ext3 before ext2) or if you use a kernel module autoloader. + +More than one type may be specified in a comma separated +list. The list of filesystem types can be prefixed with +.B no +to specify the filesystem types on which no action should be taken. +(This can be meaningful with the +.B \-a +option.) For example, the command: +.RS +.RS +.sp +.B "mount \-a \-t nomsdos,ext" +.sp +.RE +mounts all filesystems except those of type +.I msdos +and +.IR ext . +.RE +.IP "\fB\-O, \-\-test-opts \fIopts\fP" +Used in conjunction with +.BR \-a , +to limit the set of filesystems to which the +.B \-a +is applied. Like +.B \-t +in this regard except that it is useless except in the context of +.BR \-a . +For example, the command: +.RS +.RS +.sp +.B "mount \-a \-O no_netdev" +.sp +.RE +mounts all filesystems except those which have the option +.I _netdev +specified in the options field in the +.I /etc/fstab +file. + +It is different from +.B \-t +in that each option is matched exactly; a leading +.B no +at the beginning of one option does not negate the rest. + +The +.B \-t +and +.B \-O +options are cumulative in effect; that is, the command +.RS +.sp +.B "mount \-a \-t ext2 \-O _netdev" +.sp +.RE +mounts all ext2 filesystems with the _netdev option, not all filesystems +that are either ext2 or have the _netdev option specified. +.RE +.IP "\fB\-o, \-\-options \fIopts\fP" +Options are specified with a +.B \-o +flag followed by a comma separated string of options. For example: +.RS +.RS +.sp +.B "mount LABEL=mydisk \-o noatime,nouser" +.sp +.RE + +For more details, see +.B FILESYSTEM INDEPENDENT MOUNT OPTIONS +and +.B FILESYSTEM SPECIFIC MOUNT OPTIONS +sections. +.RE +.IP "\fB\-B, \-\-bind\fP" +Remount a subtree somewhere else (so that its contents are available +in both places). See above. +.IP "\fB\-R, \-\-rbind\fP" +Remount a subtree and all possible submounts somewhere else (so that its +contents are available in both places). See above. +.IP "\fB\-M, \-\-move\fP" +Move a subtree to some other place. See above. + +.SH FILESYSTEM INDEPENDENT MOUNT OPTIONS +Some of these options are only useful when they appear in the +.I /etc/fstab +file. + +Some of these options could be enabled or disabled by default +in the system kernel. To check the current setting see the options +in /proc/mounts. + +The following options apply to any filesystem that is being +mounted (but not every filesystem actually honors them - e.g., the +.B sync +option today has effect only for ext2, ext3, fat, vfat and ufs): + +.TP +.B async +All I/O to the filesystem should be done asynchronously. (See also the +.B sync +option.) +.TP +.B atime +Do not use noatime feature, then the inode access time is controlled by kernel +defaults. See also the description for +.B strictatime +and +.B relatime +mount options. +.TP +.B noatime +Do not update inode access times on this filesystem (e.g., for faster +access on the news spool to speed up news servers). +.TP +.B auto +Can be mounted with the +.B \-a +option. +.TP +.B noauto +Can only be mounted explicitly (i.e., the +.B \-a +option will not cause the filesystem to be mounted). +.TP +\fBcontext=\fP\fIcontext\fP, \fBfscontext=\fP\fIcontext\fP, \fBdefcontext=\fP\fIcontext\fP and \fBrootcontext=\fP\fIcontext\fP +The +.BR context= +option is useful when mounting filesystems that do not support +extended attributes, such as a floppy or hard disk formatted with VFAT, or +systems that are not normally running under SELinux, such as an ext3 formatted +disk from a non-SELinux workstation. You can also use +.BR context= +on filesystems you do not trust, such as a floppy. It also helps in compatibility with +xattr-supporting filesystems on earlier 2.4.<x> kernel versions. Even where +xattrs are supported, you can save time not having to label every file by +assigning the entire disk one security context. + +A commonly used option for removable media is +.BR context="system_u:object_r:removable_t" . + +Two other options are +.BR fscontext= +and +.BR defcontext= , +both of which are mutually exclusive of the context option. This means you +can use fscontext and defcontext with each other, but neither can be used with +context. + +The +.BR fscontext= +option works for all filesystems, regardless of their xattr +support. The fscontext option sets the overarching filesystem label to a +specific security context. This filesystem label is separate from the +individual labels on the files. It represents the entire filesystem for +certain kinds of permission checks, such as during mount or file creation. +Individual file labels are still obtained from the xattrs on the files +themselves. The context option actually sets the aggregate context that +fscontext provides, in addition to supplying the same label for individual +files. + +You can set the default security context for unlabeled files using +.BR defcontext= +option. This overrides the value set for unlabeled files in the policy and requires a +filesystem that supports xattr labeling. + +The +.BR rootcontext= +option allows you to explicitly label the root inode of a FS being mounted +before that FS or inode because visible to userspace. This was found to be +useful for things like stateless linux. + +Note that kernel rejects any remount request that includes the context +option even if unchanged from the current context. + +.B Warning that \fIcontext\fP value might contains comma +and in this case the value has to be properly quoted otherwise +.BR mount (8) +will interpret the comma as separator between mount options. Don't forget that +shell strips off quotes and +.BR "double quoting is required" , +for example: +.RS +.RS +.sp +mount -t tmpfs none /mnt \-o 'context="system_u:object_r:tmp_t:s0:c127,c456",noexec' +.sp +.RE + +For more details, see +.BR selinux (8) +.RE + +.TP +.B defaults +Use default options: +.BR rw ", " suid ", " dev ", " exec ", " auto ", " nouser ", and " async. +.TP +.B dev +Interpret character or block special devices on the filesystem. +.TP +.B nodev +Do not interpret character or block special devices on the file +system. +.TP +.B diratime +Update directory inode access times on this filesystem. This is the default. +.TP +.B nodiratime +Do not update directory inode access times on this filesystem. +.TP +.B dirsync +All directory updates within the filesystem should be done synchronously. +This affects the following system calls: creat, link, unlink, symlink, +mkdir, rmdir, mknod and rename. +.TP +.B exec +Permit execution of binaries. +.TP +.B noexec +Do not allow direct execution of any binaries on the mounted filesystem. +(Until recently it was possible to run binaries anyway using a command like +/lib/ld*.so /mnt/binary. This trick fails since Linux 2.4.25 / 2.6.0.) +.TP +.B group +Allow an ordinary (i.e., non-root) user to mount the filesystem if one +of his groups matches the group of the device. +This option implies the options +.BR nosuid " and " nodev +(unless overridden by subsequent options, as in the option line +.BR group,dev,suid ). +.TP +.B iversion +Every time the inode is modified, the i_version field will be incremented. +.TP +.B noiversion +Do not increment the i_version inode field. +.TP +.B mand +Allow mandatory locks on this filesystem. See +.BR fcntl (2). +.TP +.B nomand +Do not allow mandatory locks on this filesystem. +.TP +.B _netdev +The filesystem resides on a device that requires network access +(used to prevent the system from attempting to mount these filesystems +until the network has been enabled on the system). +.TP +.B nofail +Do not report errors for this device if it does not exist. +.TP +.B relatime +Update inode access times relative to modify or change time. Access +time is only updated if the previous access time was earlier than the +current modify or change time. (Similar to noatime, but doesn't break +mutt or other applications that need to know if a file has been read +since the last time it was modified.) + +Since Linux 2.6.30, the kernel defaults to the behavior provided by this +option (unless +.B noatime +was specified), and the +.B strictatime +option is required to obtain traditional semantics. In addition, since Linux +2.6.30, the file's last access time is always updated if it is more than 1 +day old. +.TP +.B norelatime +Do not use +.B relatime +feature. See also the +.B strictatime +mount option. +.TP +.B strictatime +Allows to explicitly requesting full atime updates. This makes it +possible for kernel to defaults to +.B relatime +or +.B noatime +but still allow userspace to override it. For more details about the default +system mount options see /proc/mounts. +.TP +.B nostrictatime +Use the kernel's default behaviour for inode access time updates. +.TP +.B suid +Allow set-user-identifier or set-group-identifier bits to take +effect. +.TP +.B nosuid +Do not allow set-user-identifier or set-group-identifier bits to take +effect. (This seems safe, but is in fact rather unsafe if you have +suidperl(1) installed.) +.TP +.B silent +Turn on the silent flag. +.TP +.B loud +Turn off the silent flag. +.TP +.B owner +Allow an ordinary (i.e., non-root) user to mount the filesystem if he +is the owner of the device. +This option implies the options +.BR nosuid " and " nodev +(unless overridden by subsequent options, as in the option line +.BR owner,dev,suid ). +.TP +.B remount +Attempt to remount an already-mounted filesystem. This is commonly +used to change the mount flags for a filesystem, especially to make a +readonly filesystem writable. It does not change device or mount point. + +The remount functionality follows the standard way how the mount command works +with options from fstab. It means the mount command doesn't read fstab (or +mtab) only when a +.IR device +and +.IR dir +are fully specified. + +.BR "mount -o remount,rw /dev/foo /dir" + +After this call all old mount options are replaced and arbitrary stuff from +fstab is ignored, except the loop= option which is internally generated and +maintained by the mount command. + +.BR "mount -o remount,rw /dir" + +After this call mount reads fstab (or mtab) and merges these options with +options from command line ( +.B -o +). +.TP +.B ro +Mount the filesystem read-only. +.TP +.B rw +Mount the filesystem read-write. +.TP +.B sync +All I/O to the filesystem should be done synchronously. In case of media with limited number of write cycles +(e.g. some flash drives) "sync" may cause life-cycle shortening. +.TP +.B user +Allow an ordinary user to mount the filesystem. +The name of the mounting user is written to mtab so that he can unmount +the filesystem again. +This option implies the options +.BR noexec ", " nosuid ", and " nodev +(unless overridden by subsequent options, as in the option line +.BR user,exec,dev,suid ). +.TP +.B nouser +Forbid an ordinary (i.e., non-root) user to mount the filesystem. +This is the default. +.TP +.B users +Allow every user to mount and unmount the filesystem. +This option implies the options +.BR noexec ", " nosuid ", and " nodev +(unless overridden by subsequent options, as in the option line +.BR users,exec,dev,suid ). + +.SH "FILESYSTEM SPECIFIC MOUNT OPTIONS" +The following options apply only to certain filesystems. +We sort them by filesystem. They all follow the +.B \-o +flag. + +What options are supported depends a bit on the running kernel. +More info may be found in the kernel source subdirectory +.IR Documentation/filesystems . + +.SH "Mount options for adfs" +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +Set the owner and group of the files in the filesystem (default: uid=gid=0). +.TP +\fBownmask=\fP\fIvalue\fP and \fBothmask=\fP\fIvalue\fP +Set the permission mask for ADFS 'owner' permissions and 'other' permissions, +respectively (default: 0700 and 0077, respectively). +See also +.IR /usr/src/linux/Documentation/filesystems/adfs.txt . +.SH "Mount options for affs" +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +Set the owner and group of the root of the filesystem (default: uid=gid=0, +but with option +.B uid +or +.B gid +without specified value, the uid and gid of the current process are taken). +.TP +\fBsetuid=\fP\fIvalue\fP and \fBsetgid=\fP\fIvalue\fP +Set the owner and group of all files. +.TP +.BI mode= value +Set the mode of all files to +.IR value " & 0777" +disregarding the original permissions. +Add search permission to directories that have read permission. +The value is given in octal. +.TP +.B protect +Do not allow any changes to the protection bits on the filesystem. +.TP +.B usemp +Set uid and gid of the root of the filesystem to the uid and gid +of the mount point upon the first sync or umount, and then +clear this option. Strange... +.TP +.B verbose +Print an informational message for each successful mount. +.TP +.BI prefix= string +Prefix used before volume name, when following a link. +.TP +.BI volume= string +Prefix (of length at most 30) used before '/' when following a symbolic link. +.TP +.BI reserved= value +(Default: 2.) Number of unused blocks at the start of the device. +.TP +.BI root= value +Give explicitly the location of the root block. +.TP +.BI bs= value +Give blocksize. Allowed values are 512, 1024, 2048, 4096. +.TP +.BR grpquota | noquota | quota | usrquota +These options are accepted but ignored. +(However, quota utilities may react to such strings in +.IR /etc/fstab .) + +.SH "Mount options for cifs" +See the options section of the +.BR mount.cifs (8) +man page (cifs-utils package must be installed). + +.SH "Mount options for coherent" +None. + +.SH "Mount options for debugfs" +The debugfs filesystem is a pseudo filesystem, traditionally mounted on +.IR /sys/kernel/debug . +.\" or just /debug +.\" present since 2.6.11 +There are no mount options. + +.SH "Mount options for devpts" +The devpts filesystem is a pseudo filesystem, traditionally mounted on +.IR /dev/pts . +In order to acquire a pseudo terminal, a process opens +.IR /dev/ptmx ; +the number of the pseudo terminal is then made available to the process +and the pseudo terminal slave can be accessed as +.IR /dev/pts/ <number>. +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +This sets the owner or the group of newly created PTYs to +the specified values. When nothing is specified, they will +be set to the UID and GID of the creating process. +For example, if there is a tty group with GID 5, then +.B gid=5 +will cause newly created PTYs to belong to the tty group. +.TP +.BI mode= value +Set the mode of newly created PTYs to the specified value. +The default is 0600. +A value of +.B mode=620 +and +.B gid=5 +makes "mesg y" the default on newly created PTYs. +.TP +\fBnewinstance +Create a private instance of devpts filesystem, such that +indices of ptys allocated in this new instance are +independent of indices created in other instances of devpts. + +All mounts of devpts without this +.B newinstance +option share the same set of pty indices (i.e legacy mode). +Each mount of devpts with the +.B newinstance +option has a private set of pty indices. + +This option is mainly used to support containers in the +linux kernel. It is implemented in linux kernel versions +starting with 2.6.29. Further, this mount option is valid +only if CONFIG_DEVPTS_MULTIPLE_INSTANCES is enabled in the +kernel configuration. + +To use this option effectively, +.IR /dev/ptmx +must be a symbolic link to +.IR pts/ptmx. +See +.IR Documentation/filesystems/devpts.txt +in the linux kernel source tree for details. +.TP +.BI ptmxmode= value + +Set the mode for the new +.IR ptmx +device node in the devpts filesystem. + +With the support for multiple instances of devpts (see +.B newinstance +option above), each instance has a private +.IR ptmx +node in the root of the devpts filesystem (typically +.IR /dev/pts/ptmx). + +For compatibility with older versions of the kernel, the +default mode of the new +.IR ptmx +node is 0000. +.BI ptmxmode= value +specifies a more useful mode for the +.IR ptmx +node and is highly recommended when the +.B newinstance +option is specified. + +This option is only implemented in linux kernel versions +starting with 2.6.29. Further this option is valid only if +CONFIG_DEVPTS_MULTIPLE_INSTANCES is enabled in the kernel +configuration. + +.SH "Mount options for ext" +None. +Note that the `ext' filesystem is obsolete. Don't use it. +Since Linux version 2.1.21 extfs is no longer part of the kernel source. + +.SH "Mount options for ext2" +The `ext2' filesystem is the standard Linux filesystem. +.\" Due to a kernel bug, it may be mounted with random mount options +.\" (fixed in Linux 2.0.4). +Since Linux 2.5.46, for most mount options the default +is determined by the filesystem superblock. Set them with +.BR tune2fs (8). +.TP +.BR acl | noacl +Support POSIX Access Control Lists (or not). +.\" requires CONFIG_EXT2_FS_POSIX_ACL +.TP +.BR bsddf | minixdf +Set the behaviour for the +.I statfs +system call. The +.B minixdf +behaviour is to return in the +.I f_blocks +field the total number of blocks of the filesystem, while the +.B bsddf +behaviour (which is the default) is to subtract the overhead blocks +used by the ext2 filesystem and not available for file storage. Thus +.nf + +% mount /k -o minixdf; df /k; umount /k +Filesystem 1024-blocks Used Available Capacity Mounted on +/dev/sda6 2630655 86954 2412169 3% /k +% mount /k -o bsddf; df /k; umount /k +Filesystem 1024-blocks Used Available Capacity Mounted on +/dev/sda6 2543714 13 2412169 0% /k + +.fi +(Note that this example shows that one can add command line options +to the options given in +.IR /etc/fstab .) + +.TP +.BR check=none " or " nocheck +No checking is done at mount time. This is the default. This is fast. +It is wise to invoke +.BR e2fsck (8) +every now and then, e.g. at boot time. The non-default behavior is unssuported +(check=normal and check=strict options have been removed). Note that these mount options +don't have to be supported if ext4 kernel driver is used for ext2 and ext3 filesystems. +.TP +.B debug +Print debugging info upon each (re)mount. +.TP +.BR errors= { continue | remount-ro | panic } +Define the behaviour when an error is encountered. +(Either ignore errors and just mark the filesystem erroneous and continue, +or remount the filesystem read-only, or panic and halt the system.) +The default is set in the filesystem superblock, and can be +changed using +.BR tune2fs (8). +.TP +.BR grpid | bsdgroups " and " nogrpid | sysvgroups +These options define what group id a newly created file gets. +When +.BR grpid +is set, it takes the group id of the directory in which it is created; +otherwise (the default) it takes the fsgid of the current process, unless +the directory has the setgid bit set, in which case it takes the gid +from the parent directory, and also gets the setgid bit set +if it is a directory itself. +.TP +.BR grpquota | noquota | quota | usrquota +These options are accepted but ignored. +.TP +.BR nouid32 +Disables 32-bit UIDs and GIDs. This is for interoperability with older +kernels which only store and expect 16-bit values. +.TP +.BR oldalloc " or " orlov +Use old allocator or Orlov allocator for new inodes. Orlov is default. +.TP +\fBresgid=\fP\fIn\fP and \fBresuid=\fP\fIn\fP +The ext2 filesystem reserves a certain percentage of the available +space (by default 5%, see +.BR mke2fs (8) +and +.BR tune2fs (8)). +These options determine who can use the reserved blocks. +(Roughly: whoever has the specified uid, or belongs to the specified group.) +.TP +.BI sb= n +Instead of block 1, use block +.I n +as superblock. This could be useful when the filesystem has been damaged. +(Earlier, copies of the superblock would be made every 8192 blocks: in +block 1, 8193, 16385, ... (and one got thousands of copies on +a big filesystem). Since version 1.08, +.B mke2fs +has a \-s (sparse superblock) option to reduce the number of backup +superblocks, and since version 1.15 this is the default. Note +that this may mean that ext2 filesystems created by a recent +.B mke2fs +cannot be mounted r/w under Linux 2.0.*.) +The block number here uses 1k units. Thus, if you want to use logical +block 32768 on a filesystem with 4k blocks, use "sb=131072". +.TP +.BR user_xattr | nouser_xattr +Support "user." extended attributes (or not). +.\" requires CONFIG_EXT2_FS_XATTR + + +.SH "Mount options for ext3" +The ext3 filesystem is a version of the ext2 filesystem which has been +enhanced with journalling. It supports the same options as ext2 as +well as the following additions: +.\" .TP +.\" .BR abort +.\" Mount the filesystem in abort mode, as if a fatal error has occurred. +.TP +.BR journal=update +Update the ext3 filesystem's journal to the current format. +.TP +.BR journal=inum +When a journal already exists, this option is ignored. Otherwise, it +specifies the number of the inode which will represent the ext3 filesystem's +journal file; ext3 will create a new journal, overwriting the old contents +of the file whose inode number is +.IR inum . +.TP +.BR journal_dev=devnum +When the external journal device's major/minor numbers +have changed, this option allows the user to specify +the new journal location. The journal device is +identified through its new major/minor numbers encoded +in devnum. +.TP +.BR norecovery / noload +Don't load the journal on mounting. Note that +if the filesystem was not unmounted cleanly, +skipping the journal replay will lead to the +filesystem containing inconsistencies that can +lead to any number of problems. +.TP +.BR data= { journal | ordered | writeback } +Specifies the journalling mode for file data. Metadata is always journaled. +To use modes other than +.B ordered +on the root filesystem, pass the mode to the kernel as boot parameter, e.g. +.IR rootflags=data=journal . +.RS +.TP +.B journal +All data is committed into the journal prior to being written into the +main filesystem. +.TP +.B ordered +This is the default mode. All data is forced directly out to the main file +system prior to its metadata being committed to the journal. +.TP +.B writeback +Data ordering is not preserved - data may be written into the main +filesystem after its metadata has been committed to the journal. +This is rumoured to be the highest-throughput option. It guarantees +internal filesystem integrity, however it can allow old data to appear +in files after a crash and journal recovery. +.RE +.TP +.BR barrier=0 " / " barrier=1 " +This enables/disables barriers. barrier=0 disables it, barrier=1 enables it. +Write barriers enforce proper on-disk ordering of journal commits, making +volatile disk write caches safe to use, at some performance penalty. The ext3 +filesystem does not enable write barriers by default. Be sure to enable +barriers unless your disks are battery-backed one way or another. Otherwise +you risk filesystem corruption in case of power failure. +.TP +.BI commit= nrsec +Sync all data and metadata every +.I nrsec +seconds. The default value is 5 seconds. Zero means default. +.TP +.BR user_xattr +Enable Extended User Attributes. See the +.BR attr (5) +manual page. +.TP +.BR acl +Enable POSIX Access Control Lists. See the +.BR acl (5) +manual page. + +.SH "Mount options for ext4" +The ext4 filesystem is an advanced level of the ext3 filesystem which +incorporates scalability and reliability enhancements for supporting large +filesystem. + +The options +.B journal_dev, noload, data, commit, orlov, oldalloc, [no]user_xattr +.B [no]acl, bsddf, minixdf, debug, errors, data_err, grpid, bsdgroups, nogrpid +.B sysvgroups, resgid, resuid, sb, quota, noquota, grpquota and usrquota +are backwardly compatible with ext3 or ext2. +.TP +.BR journal_checksum +Enable checksumming of the journal transactions. This will allow the recovery +code in e2fsck and the kernel to detect corruption in the kernel. It is a +compatible change and will be ignored by older kernels. +.TP +.BR journal_async_commit +Commit block can be written to disk without waiting for descriptor blocks. If +enabled older kernels cannot mount the device. +This will enable 'journal_checksum' internally. +.TP +.BR journal=update +Update the ext4 filesystem's journal to the current format. +.TP +.BR barrier=0 " / " barrier=1 " / " barrier " / " nobarrier +This enables/disables the use of write barriers in the jbd code. barrier=0 +disables, barrier=1 enables. This also requires an IO stack which can support +barriers, and if jbd gets an error on a barrier write, it will disable again +with a warning. Write barriers enforce proper on-disk ordering of journal +commits, making volatile disk write caches safe to use, at some performance +penalty. If your disks are battery-backed in one way or another, disabling +barriers may safely improve performance. The mount options "barrier" and +"nobarrier" can also be used to enable or disable barriers, for consistency +with other ext4 mount options. + +The ext4 filesystem enables write barriers by default. +.TP +.BI inode_readahead= n +This tuning parameter controls the maximum number of inode table blocks that +ext4's inode table readahead algorithm will pre-read into the buffer cache. +The default value is 32 blocks. +.TP +.BI stripe= n +Number of filesystem blocks that mballoc will try to use for allocation size +and alignment. For RAID5/6 systems this should be the number of data disks * +RAID chunk size in filesystem blocks. +.TP +.BR delalloc +Deferring block allocation until write-out time. +.TP +.BR nodelalloc +Disable delayed allocation. Blocks are allocated when data is copied from user +to page cache. +.TP +.BI max_batch_time= usec +Maximum amount of time ext4 should wait for additional filesystem operations to +be batch together with a synchronous write operation. Since a synchronous +write operation is going to force a commit and then a wait for the I/O +complete, it doesn't cost much, and can be a huge throughput win, we wait for a +small amount of time to see if any other transactions can piggyback on the +synchronous write. The algorithm used is designed to automatically tune for +the speed of the disk, by measuring the amount of time (on average) that it +takes to finish committing a transaction. Call this time the "commit time". +If the time that the transaction has been running is less than the commit time, +ext4 will try sleeping for the commit time to see if other operations will join +the transaction. The commit time is capped by the max_batch_time, which +defaults to 15000us (15ms). This optimization can be turned off entirely by +setting max_batch_time to 0. +.TP +.BI min_batch_time= usec +This parameter sets the commit time (as described above) to be at least +min_batch_time. It defaults to zero microseconds. Increasing this parameter +may improve the throughput of multi-threaded, synchronous workloads on very +fast disks, at the cost of increasing latency. +.TP +.BI journal_ioprio= prio +The I/O priority (from 0 to 7, where 0 is the highest priorty) which should be +used for I/O operations submitted by kjournald2 during a commit operation. +This defaults to 3, which is a slightly higher priority than the default I/O +priority. +.TP +.BR abort +Simulate the effects of calling ext4_abort() for +debugging purposes. This is normally used while +remounting a filesystem which is already mounted. +.TP +.BR auto_da_alloc | noauto_da_alloc +Many broken applications don't use fsync() when +replacing existing files via patterns such as + +fd = open("foo.new")/write(fd,..)/close(fd)/ rename("foo.new", "foo") + +or worse yet + +fd = open("foo", O_TRUNC)/write(fd,..)/close(fd). + +If auto_da_alloc is enabled, ext4 will detect the replace-via-rename and +replace-via-truncate patterns and force that any delayed allocation blocks are +allocated such that at the next journal commit, in the default data=ordered +mode, the data blocks of the new file are forced to disk before the rename() +operation is committed. This provides roughly the same level of guarantees as +ext3, and avoids the "zero-length" problem that can happen when a system +crashes before the delayed allocation blocks are forced to disk. +.TP +.BR discard / nodiscard +Controls whether ext4 should issue discard/TRIM commands to the underlying +block device when blocks are freed. This is useful for SSD devices and +sparse/thinly-provisioned LUNs, but it is off by default until sufficient +testing has been done. +.TP +.BR nouid32 +Disables 32-bit UIDs and GIDs. This is for +interoperability with older kernels which only +store and expect 16-bit values. +.TP +.BR resize +Allows to resize filesystem to the end of the last +existing block group, further resize has to be done +with resize2fs either online, or offline. It can be +used only with conjunction with remount. +.TP +.BR block_validity / noblock_validity +This options allows to enables/disables the in-kernel facility for tracking +filesystem metadata blocks within internal data structures. This allows multi- +block allocator and other routines to quickly locate extents which might +overlap with filesystem metadata blocks. This option is intended for debugging +purposes and since it negatively affects the performance, it is off by default. +.TP +.BR dioread_lock / dioread_nolock +Controls whether or not ext4 should use the DIO read locking. If the +dioread_nolock option is specified ext4 will allocate uninitialized extent +before buffer write and convert the extent to initialized after IO completes. +This approach allows ext4 code to avoid using inode mutex, which improves +scalability on high speed storages. However this does not work with data +journaling and dioread_nolock option will be ignored with kernel warning. +Note that dioread_nolock code path is only used for extent-based files. +Because of the restrictions this options comprises it is off by default +(e.g. dioread_lock). +.TP +.BR i_version +Enable 64-bit inode version support. This option is off by default. + +.SH "Mount options for fat" +(Note: +.I fat +is not a separate filesystem, but a common part of the +.IR msdos , +.I umsdos +and +.I vfat +filesystems.) +.TP +.BR blocksize= { 512 | 1024 | 2048 } +Set blocksize (default 512). This option is obsolete. +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +Set the owner and group of all files. +(Default: the uid and gid of the current process.) +.TP +.BI umask= value +Set the umask (the bitmask of the permissions that are +.B not +present). The default is the umask of the current process. +The value is given in octal. +.TP +.BI dmask= value +Set the umask applied to directories only. +The default is the umask of the current process. +The value is given in octal. +.\" Present since Linux 2.5.43. +.TP +.BI fmask= value +Set the umask applied to regular files only. +The default is the umask of the current process. +The value is given in octal. +.\" Present since Linux 2.5.43. +.TP +.BI allow_utime= value +This option controls the permission check of mtime/atime. +.RS +.TP +.B 20 +If current process is in group of file's group ID, you can change timestamp. +.TP +.B 2 +Other users can change timestamp. +.PP +The default is set from `dmask' option. (If the directory is writable, +.B utime(2) +is also allowed. I.e. ~dmask & 022) + +Normally +.B utime(2) +checks current process is owner of the file, or it has +CAP_FOWNER capability. But FAT filesystem doesn't have uid/gid on disk, so +normal check is too unflexible. With this option you can relax it. +.RE +.TP +.BI check= value +Three different levels of pickyness can be chosen: +.RS +.TP +.BR r [ elaxed ] +Upper and lower case are accepted and equivalent, long name parts are +truncated (e.g. +.I verylongname.foobar +becomes +.IR verylong.foo ), +leading and embedded spaces are accepted in each name part (name and extension). +.TP +.BR n [ ormal ] +Like "relaxed", but many special characters (*, ?, <, spaces, etc.) are +rejected. This is the default. +.TP +.BR s [ trict ] +Like "normal", but names may not contain long parts and special characters +that are sometimes used on Linux, but are not accepted by MS-DOS are +rejected. (+, =, spaces, etc.) +.RE +.TP +.BI codepage= value +Sets the codepage for converting to shortname characters on FAT +and VFAT filesystems. By default, codepage 437 is used. +.TP +.BR conv= {b [ inary ]| t [ ext ]| a [ uto ]} +The +.I fat +filesystem can perform CRLF<-->NL (MS-DOS text format to UNIX text +format) conversion in the kernel. The following conversion modes are +available: +.RS +.TP +.B binary +no translation is performed. This is the default. +.TP +.B text +CRLF<-->NL translation is performed on all files. +.TP +.B auto +CRLF<-->NL translation is performed on all files that don't have a +"well-known binary" extension. The list of known extensions can be found at +the beginning of +.I fs/fat/misc.c +(as of 2.0, the list is: exe, com, bin, app, sys, drv, ovl, ovr, obj, +lib, dll, pif, arc, zip, lha, lzh, zoo, tar, z, arj, tz, taz, tzp, tpz, +gz, tgz, deb, gif, bmp, tif, gl, jpg, pcx, tfm, vf, gf, pk, pxl, dvi). +.PP +Programs that do computed lseeks won't like in-kernel text conversion. +Several people have had their data ruined by this translation. Beware! + +For filesystems mounted in binary mode, a conversion tool +(fromdos/todos) is available. This option is obsolete. +.RE +.TP +.BI cvf_format= module +Forces the driver to use the CVF (Compressed Volume File) module +.RI cvf_ module +instead of auto-detection. If the kernel supports kmod, the +cvf_format=xxx option also controls on-demand CVF module loading. +This option is obsolete. +.TP +.BI cvf_option= option +Option passed to the CVF module. This option is obsolete. +.TP +.B debug +Turn on the +.I debug +flag. A version string and a list of filesystem parameters will be +printed (these data are also printed if the parameters appear to be +inconsistent). +.TP +.BR fat= {12 | 16 | 32 } +Specify a 12, 16 or 32 bit fat. This overrides +the automatic FAT type detection routine. Use with caution! +.TP +.BI iocharset= value +Character set to use for converting between 8 bit characters +and 16 bit Unicode characters. The default is iso8859-1. +Long filenames are stored on disk in Unicode format. +.TP +.BI tz=UTC +This option disables the conversion of timestamps +between local time (as used by Windows on FAT) and UTC +(which Linux uses internally). This is particularly +useful when mounting devices (like digital cameras) +that are set to UTC in order to avoid the pitfalls of +local time. +.TP +.B quiet +Turn on the +.I quiet +flag. Attempts to chown or chmod files do not return errors, +although they fail. Use with caution! +.TP +.B showexec +If set, the execute permission bits of the file will be allowed only if +the extension part of the name is .EXE, .COM, or .BAT. Not set by default. +.TP +.B sys_immutable +If set, ATTR_SYS attribute on FAT is handled as IMMUTABLE flag on Linux. +Not set by default. +.TP +.B flush +If set, the filesystem will try to flush to disk more early than normal. +Not set by default. +.TP +.B usefree +Use the "free clusters" value stored on FSINFO. It'll +be used to determine number of free clusters without +scanning disk. But it's not used by default, because +recent Windows don't update it correctly in some +case. If you are sure the "free clusters" on FSINFO is +correct, by this option you can avoid scanning disk. +.TP +.BR dots ", " nodots ", " dotsOK= [ yes | no ] +Various misguided attempts to force Unix or DOS conventions +onto a FAT filesystem. + +.SH "Mount options for hfs" +.TP +.BI creator= cccc ", type=" cccc +Set the creator/type values as shown by the MacOS finder +used for creating new files. Default values: '????'. +.TP +.BI uid= n ", gid=" n +Set the owner and group of all files. +(Default: the uid and gid of the current process.) +.TP +.BI dir_umask= n ", file_umask=" n ", umask=" n +Set the umask used for all directories, all regular files, or all +files and directories. Defaults to the umask of the current process. +.TP +.BI session= n +Select the CDROM session to mount. +Defaults to leaving that decision to the CDROM driver. +This option will fail with anything but a CDROM as underlying device. +.TP +.BI part= n +Select partition number n from the device. +Only makes sense for CDROMs. +Defaults to not parsing the partition table at all. +.TP +.B quiet +Don't complain about invalid mount options. + +.SH "Mount options for hpfs" +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +Set the owner and group of all files. (Default: the uid and gid +of the current process.) +.TP +.BI umask= value +Set the umask (the bitmask of the permissions that are +.B not +present). The default is the umask of the current process. +The value is given in octal. +.TP +.BR case= { lower | asis } +Convert all files names to lower case, or leave them. +(Default: +.BR case=lower .) +.TP +.BR conv= { binary | text | auto } +For +.BR conv=text , +delete some random CRs (in particular, all followed by NL) +when reading a file. +For +.BR conv=auto , +choose more or less at random between +.BR conv=binary " and " conv=text . +For +.BR conv=binary , +just read what is in the file. This is the default. +.TP +.B nocheck +Do not abort mounting when certain consistency checks fail. + +.SH "Mount options for iso9660" +ISO 9660 is a standard describing a filesystem structure to be used +on CD-ROMs. (This filesystem type is also seen on some DVDs. See also the +.I udf +filesystem.) + +Normal +.I iso9660 +filenames appear in a 8.3 format (i.e., DOS-like restrictions on filename +length), and in addition all characters are in upper case. Also there is +no field for file ownership, protection, number of links, provision for +block/character devices, etc. + +Rock Ridge is an extension to iso9660 that provides all of these UNIX-like +features. Basically there are extensions to each directory record that +supply all of the additional information, and when Rock Ridge is in use, +the filesystem is indistinguishable from a normal UNIX filesystem (except +that it is read-only, of course). +.TP +.B norock +Disable the use of Rock Ridge extensions, even if available. Cf.\& +.BR map . +.TP +.B nojoliet +Disable the use of Microsoft Joliet extensions, even if available. Cf.\& +.BR map . +.TP +.BR check= { r [ elaxed ]| s [ trict ]} +With +.BR check=relaxed , +a filename is first converted to lower case before doing the lookup. +This is probably only meaningful together with +.B norock +and +.BR map=normal . +(Default: +.BR check=strict .) +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +Give all files in the filesystem the indicated user or group id, +possibly overriding the information found in the Rock Ridge extensions. +(Default: +.BR uid=0,gid=0 .) +.TP +.BR map= { n [ ormal ]| o [ ff ]| a [ corn ]} +For non-Rock Ridge volumes, normal name translation maps upper +to lower case ASCII, drops a trailing `;1', and converts `;' to `.'. +With +.B map=off +no name translation is done. See +.BR norock . +(Default: +.BR map=normal .) +.B map=acorn +is like +.BR map=normal +but also apply Acorn extensions if present. +.TP +.BI mode= value +For non-Rock Ridge volumes, give all files the indicated mode. +(Default: read permission for everybody.) +Since Linux 2.1.37 one no longer needs to specify the mode in +decimal. (Octal is indicated by a leading 0.) +.TP +.B unhide +Also show hidden and associated files. +(If the ordinary files and the associated or hidden files have +the same filenames, this may make the ordinary files inaccessible.) +.TP +.BR block= { 512 | 1024 | 2048 } +Set the block size to the indicated value. +(Default: +.BR block=1024 .) +.TP +.BR conv= { a [ uto ]| b [ inary ]| m [ text ]| t [ ext ]} +(Default: +.BR conv=binary .) +Since Linux 1.3.54 this option has no effect anymore. +(And non-binary settings used to be very dangerous, +possibly leading to silent data corruption.) +.TP +.B cruft +If the high byte of the file length contains other garbage, +set this mount option to ignore the high order bits of the file length. +This implies that a file cannot be larger than 16MB. +.TP +.BI session= x +Select number of session on multisession CD. (Since 2.3.4.) +.TP +.BI sbsector= xxx +Session begins from sector xxx. (Since 2.3.4.) +.LP +The following options are the same as for vfat and specifying them only makes +sense when using discs encoded using Microsoft's Joliet extensions. +.TP +.BI iocharset= value +Character set to use for converting 16 bit Unicode characters on CD +to 8 bit characters. The default is iso8859-1. +.TP +.B utf8 +Convert 16 bit Unicode characters on CD to UTF-8. + +.SH "Mount options for jfs" +.TP +.BI iocharset= name +Character set to use for converting from Unicode to ASCII. The default is +to do no conversion. Use +.B iocharset=utf8 +for UTF8 translations. This requires CONFIG_NLS_UTF8 to be set in +the kernel +.I ".config" +file. +.TP +.BI resize= value +Resize the volume to +.I value +blocks. JFS only supports growing a volume, not shrinking it. This option +is only valid during a remount, when the volume is mounted read-write. The +.B resize +keyword with no value will grow the volume to the full size of the partition. +.TP +.B nointegrity +Do not write to the journal. The primary use of this option is to allow +for higher performance when restoring a volume from backup media. The +integrity of the volume is not guaranteed if the system abnormally abends. +.TP +.B integrity +Default. Commit metadata changes to the journal. Use this option to remount +a volume where the +.B nointegrity +option was previously specified in order to restore normal behavior. +.TP +.BR errors= { continue | remount-ro | panic } +Define the behaviour when an error is encountered. +(Either ignore errors and just mark the filesystem erroneous and continue, +or remount the filesystem read-only, or panic and halt the system.) +.TP +.BR noquota | quota | usrquota | grpquota +These options are accepted but ignored. + +.SH "Mount options for minix" +None. + +.SH "Mount options for msdos" +See mount options for fat. +If the +.I msdos +filesystem detects an inconsistency, it reports an error and sets the file +system read-only. The filesystem can be made writable again by remounting +it. + +.SH "Mount options for ncpfs" +Just like +.IR nfs ", the " ncpfs +implementation expects a binary argument (a +.IR "struct ncp_mount_data" ) +to the mount system call. This argument is constructed by +.BR ncpmount (8) +and the current version of +.B mount +(2.12) does not know anything about ncpfs. + +.SH "Mount options for nfs and nfs4" +See the options section of the +.BR nfs (5) +man page (nfs-utils package must be installed). + +The +.IR nfs " and " nfs4 +implementation expects a binary argument (a +.IR "struct nfs_mount_data" ) +to the mount system call. This argument is constructed by +.BR mount.nfs (8) +and the current version of +.B mount +(2.13) does not know anything about nfs and nfs4. + +.SH "Mount options for ntfs" +.TP +.BI iocharset= name +Character set to use when returning file names. +Unlike VFAT, NTFS suppresses names that contain +nonconvertible characters. Deprecated. +.\" since 2.5.11 +.TP +.BI nls= name +New name for the option earlier called +.IR iocharset . +.\" since 2.5.11 +.TP +.BR utf8 +Use UTF-8 for converting file names. +.TP +.BR uni_xlate= { 0 | 1 | 2 } +For 0 (or `no' or `false'), do not use escape sequences +for unknown Unicode characters. +For 1 (or `yes' or `true') or 2, use vfat-style 4-byte escape sequences +starting with ":". Here 2 give a little-endian encoding +and 1 a byteswapped bigendian encoding. +.TP +.B posix=[0|1] +If enabled (posix=1), the filesystem distinguishes between +upper and lower case. The 8.3 alias names are presented as +hard links instead of being suppressed. This option is obsolete. +.TP +\fBuid=\fP\fIvalue\fP, \fBgid=\fP\fIvalue\fP and \fBumask=\fP\fIvalue\fP +Set the file permission on the filesystem. +The umask value is given in octal. +By default, the files are owned by root and not readable by somebody else. + +.SH "Mount options for proc" +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +These options are recognized, but have no effect as far as I can see. + +.SH "Mount options for ramfs" +Ramfs is a memory based filesystem. Mount it and you have it. Unmount it +and it is gone. Present since Linux 2.3.99pre4. +There are no mount options. + +.SH "Mount options for reiserfs" +Reiserfs is a journaling filesystem. +.TP +.BR conv +Instructs version 3.6 reiserfs software to mount a version 3.5 filesystem, +using the 3.6 format for newly created objects. This filesystem will no +longer be compatible with reiserfs 3.5 tools. +.TP +.BR hash= { rupasov | tea | r5 | detect } +Choose which hash function reiserfs will use to find files within directories. +.RS +.TP +.B rupasov +A hash invented by Yury Yu. Rupasov. It is fast and preserves locality, +mapping lexicographically close file names to close hash values. +This option should not be used, as it causes a high probability of hash +collisions. +.TP +.B tea +A Davis-Meyer function implemented by Jeremy Fitzhardinge. +It uses hash permuting bits in the name. It gets high randomness +and, therefore, low probability of hash collisions at some CPU cost. +This may be used if EHASHCOLLISION errors are experienced with the r5 hash. +.TP +.B r5 +A modified version of the rupasov hash. It is used by default and is +the best choice unless the filesystem has huge directories and +unusual file-name patterns. +.TP +.B detect +Instructs +.IR mount +to detect which hash function is in use by examining +the filesystem being mounted, and to write this information into +the reiserfs superblock. This is only useful on the first mount of +an old format filesystem. +.RE +.TP +.BR hashed_relocation +Tunes the block allocator. This may provide performance improvements +in some situations. +.TP +.BR no_unhashed_relocation +Tunes the block allocator. This may provide performance improvements +in some situations. +.TP +.BR noborder +Disable the border allocator algorithm invented by Yury Yu. Rupasov. +This may provide performance improvements in some situations. +.TP +.BR nolog +Disable journalling. This will provide slight performance improvements in +some situations at the cost of losing reiserfs's fast recovery from crashes. +Even with this option turned on, reiserfs still performs all journalling +operations, save for actual writes into its journalling area. Implementation +of +.IR nolog +is a work in progress. +.TP +.BR notail +By default, reiserfs stores small files and `file tails' directly into its +tree. This confuses some utilities such as +.BR LILO (8). +This option is used to disable packing of files into the tree. +.TP +.BR replayonly +Replay the transactions which are in the journal, but do not actually +mount the filesystem. Mainly used by +.IR reiserfsck . +.TP +.BI resize= number +A remount option which permits online expansion of reiserfs partitions. +Instructs reiserfs to assume that the device has +.I number +blocks. +This option is designed for use with devices which are under logical +volume management (LVM). +There is a special +.I resizer +utility which can be obtained from +.IR ftp://ftp.namesys.com/pub/reiserfsprogs . +.TP +.BR user_xattr +Enable Extended User Attributes. See the +.BR attr (5) +manual page. +.TP +.BR acl +Enable POSIX Access Control Lists. See the +.BR acl (5) +manual page. +.TP +.BR barrier=none " / " barrier=flush " +This enables/disables the use of write barriers in the journaling code. +barrier=none disables it, barrier=flush enables it. Write barriers enforce +proper on-disk ordering of journal commits, making volatile disk write caches +safe to use, at some performance penalty. The reiserfs filesystem does not +enable write barriers by default. Be sure to enable barriers unless your disks +are battery-backed one way or another. Otherwise you risk filesystem +corruption in case of power failure. + +.SH "Mount options for romfs" +None. + +.SH "Mount options for squashfs" +None. + +.SH "Mount options for smbfs" +Just like +.IR nfs ", the " smbfs +implementation expects a binary argument (a +.IR "struct smb_mount_data" ) +to the mount system call. This argument is constructed by +.BR smbmount (8) +and the current version of +.B mount +(2.12) does not know anything about smbfs. + +.SH "Mount options for sysv" +None. + +.SH "Mount options for tmpfs" +.TP +.BI size= nbytes +Override default maximum size of the filesystem. +The size is given in bytes, and rounded up to entire pages. +The default is half of the memory. The size parameter also accepts a suffix % +to limit this tmpfs instance to that percentage of your physical RAM: +the default, when neither size nor nr_blocks is specified, is size=50% +.TP +.B nr_blocks= +The same as size, but in blocks of PAGE_CACHE_SIZE +.TP +.B nr_inodes= +The maximum number of inodes for this instance. The default +is half of the number of your physical RAM pages, or (on a +machine with highmem) the number of lowmem RAM pages, +whichever is the lower. +.PP +The tmpfs mount options for sizing ( +.BR size , +.BR nr_blocks , +and +.BR nr_inodes ) +accept a suffix +.BR k , +.B m +or +.B g +for Ki, Mi, Gi (binary kilo, mega and giga) and can be changed on remount. + +.TP +.B mode= +Set initial permissions of the root directory. +.TP +.B uid= +The user id. +.TP +.B gid= +The group id. +.TP +.B mpol=[default|prefer:Node|bind:NodeList|interleave|interleave:NodeList] +Set the NUMA memory allocation policy for all files in that +instance (if the kernel CONFIG_NUMA is enabled) - which can be adjusted on the +fly via 'mount -o remount ...' +.RS +.TP +.B default +prefers to allocate memory from the local node +.TP +.B prefer:Node +prefers to allocate memory from the given Node +.TP +.B bind:NodeList +allocates memory only from nodes in NodeList +.TP +.B interleave +prefers to allocate from each node in turn +.TP +.B interleave:NodeList +allocates from each node of NodeList in turn. +.PP +The NodeList format is a comma-separated list of decimal numbers and ranges, a +range being two hyphen-separated decimal numbers, the smallest and largest node +numbers in the range. For example, mpol=bind:0-3,5,7,9-15 + +Note that trying to mount a tmpfs with an mpol option will fail if the +running kernel does not support NUMA; and will fail if its nodelist +specifies a node which is not online. If your system relies on that +tmpfs being mounted, but from time to time runs a kernel built without +NUMA capability (perhaps a safe recovery kernel), or with fewer nodes +online, then it is advisable to omit the mpol option from automatic +mount options. It can be added later, when the tmpfs is already mounted +on MountPoint, by 'mount -o remount,mpol=Policy:NodeList MountPoint'. + +.SH "Mount options for ubifs" +UBIFS is a flash file system which works on top of UBI volumes. Note that +.B +atime +is not supported and is always turned off. +.TP +The device name may be specified as +.RS +.B ubiX_Y +UBI device number +.BR X , +volume number +.B Y +.TP +.B ubiY +UBI device number +.BR 0 , +volume number +.B Y +.TP +.B ubiX:NAME +UBI device number +.BR X , +volume with name +.B NAME +.TP +.B ubi:NAME +UBI device number +.BR 0 , +volume with name +.B NAME +.RE +Alternative +.B ! +separator may be used instead of +.BR : . +.TP +The following mount options are available: +.TP +.BR bulk_read +Enable bulk-read. VFS read-ahead is disabled because it slows down the file +system. Bulk-Read is an internal optimization. Some flashes may read faster if +the data are read at one go, rather than at several read requests. For +example, OneNAND can do "read-while-load" if it reads more than one NAND page. +.TP +.BR no_bulk_read +Do not bulk-read. This is the default. +.TP +.BR chk_data_crc +Check data CRC-32 checksums. This is the default. +.TP +.BR no_chk_data_crc. +Do not check data CRC-32 checksums. With this option, the filesystem does not +check CRC-32 checksum for data, but it does check it for the internal indexing +information. This option only affects reading, not writing. CRC-32 is always +calculated when writing the data. +.TP +.BR compr= { none | lzo | zlib } +Select the default compressor which is used when new files are written. It is +still possible to read compressed files if mounted with the +.B none +option. + +.SH "Mount options for udf" +udf is the "Universal Disk Format" filesystem defined by the Optical +Storage Technology Association, and is often used for DVD-ROM. +See also +.IR iso9660 . +.TP +.B gid= +Set the default group. +.TP +.B umask= +Set the default umask. +The value is given in octal. +.TP +.B uid= +Set the default user. +.TP +.B unhide +Show otherwise hidden files. +.TP +.B undelete +Show deleted files in lists. +.TP +.B nostrict +Unset strict conformance. +.\" .TP +.\" .B utf8 +.\" (unused). +.TP +.B iocharset +Set the NLS character set. +.TP +.B bs= +Set the block size. (May not work unless 2048.) +.TP +.B novrs +Skip volume sequence recognition. +.TP +.B session= +Set the CDROM session counting from 0. Default: last session. +.TP +.B anchor= +Override standard anchor location. Default: 256. +.TP +.B volume= +Override the VolumeDesc location. (unused) +.TP +.B partition= +Override the PartitionDesc location. (unused) +.TP +.B lastblock= +Set the last block of the filesystem. +.TP +.B fileset= +Override the fileset block location. (unused) +.TP +.B rootdir= +Override the root directory location. (unused) + +.SH "Mount options for ufs" +.TP +.BI ufstype= value +UFS is a filesystem widely used in different operating systems. +The problem are differences among implementations. Features of some +implementations are undocumented, so its hard to recognize the +type of ufs automatically. +That's why the user must specify the type of ufs by mount option. +Possible values are: +.RS +.TP +.B old +Old format of ufs, this is the default, read only. +(Don't forget to give the \-r option.) +.TP +.B 44bsd +For filesystems created by a BSD-like system (NetBSD,FreeBSD,OpenBSD). +.TP +.B ufs2 +Used in FreeBSD 5.x supported as read-write. +.TP +.B 5xbsd +Synonym for ufs2. +.TP +.B sun +For filesystems created by SunOS or Solaris on Sparc. +.TP +.B sunx86 +For filesystems created by Solaris on x86. +.TP +.B hp +For filesystems created by HP-UX, read-only. +.TP +.B nextstep +For filesystems created by NeXTStep (on NeXT station) (currently read only). +.TP +.B nextstep-cd +For NextStep CDROMs (block_size == 2048), read-only. +.TP +.B openstep +For filesystems created by OpenStep (currently read only). +The same filesystem type is also used by Mac OS X. +.RE + +.TP +.BI onerror= value +Set behaviour on error: +.RS +.TP +.B panic +If an error is encountered, cause a kernel panic. +.TP +.RB [ lock | umount | repair ] +These mount options don't do anything at present; +when an error is encountered only a console message is printed. +.RE + +.SH "Mount options for umsdos" +See mount options for msdos. +The +.B dotsOK +option is explicitly killed by +.IR umsdos . + +.SH "Mount options for vfat" +First of all, the mount options for +.I fat +are recognized. +The +.B dotsOK +option is explicitly killed by +.IR vfat . +Furthermore, there are +.TP +.B uni_xlate +Translate unhandled Unicode characters to special escaped sequences. +This lets you backup and restore filenames that are created with any +Unicode characters. Without this option, a '?' is used when no +translation is possible. The escape character is ':' because it is +otherwise illegal on the vfat filesystem. The escape sequence +that gets used, where u is the unicode character, +is: ':', (u & 0x3f), ((u>>6) & 0x3f), (u>>12). +.TP +.B posix +Allow two files with names that only differ in case. +This option is obsolete. +.TP +.B nonumtail +First try to make a short name without sequence number, +before trying +.IR name~num.ext . +.TP +.B utf8 +UTF8 is the filesystem safe 8-bit encoding of Unicode that is used by the +console. It can be enabled for the filesystem with this option or disabled +with utf8=0, utf8=no or utf8=false. If `uni_xlate' gets set, UTF8 gets +disabled. +.TP +.BR shortname= { lower | win95 | winnt | mixed } + +Defines the behaviour for creation and display of filenames which fit into +8.3 characters. If a long name for a file exists, it will always be +preferred display. There are four modes: +: +.RS +.TP +.I lower +Force the short name to lower case upon display; store a long name when +the short name is not all upper case. +.TP +.I win95 +Force the short name to upper case upon display; store a long name when +the short name is not all upper case. +.TP +.I winnt +Display the shortname as is; store a long name when the short name is +not all lower case or all upper case. +.TP +.I mixed +Display the short name as is; store a long name when the short name is not +all upper case. This mode is the default since Linux 2.6.32. +.RE + + +.SH "Mount options for usbfs" +.TP +\fBdevuid=\fP\fIuid\fP and \fBdevgid=\fP\fIgid\fP and \fBdevmode=\fP\fImode\fP +Set the owner and group and mode of the device files in the usbfs filesystem +(default: uid=gid=0, mode=0644). The mode is given in octal. +.TP +\fBbusuid=\fP\fIuid\fP and \fBbusgid=\fP\fIgid\fP and \fBbusmode=\fP\fImode\fP +Set the owner and group and mode of the bus directories in the usbfs +filesystem (default: uid=gid=0, mode=0555). The mode is given in octal. +.TP +\fBlistuid=\fP\fIuid\fP and \fBlistgid=\fP\fIgid\fP and \fBlistmode=\fP\fImode\fP +Set the owner and group and mode of the file +.I devices +(default: uid=gid=0, mode=0444). The mode is given in octal. + +.SH "Mount options for xenix" +None. + +.SH "Mount options for xfs" +.TP +.BI allocsize= size +Sets the buffered I/O end-of-file preallocation size when +doing delayed allocation writeout (default size is 64KiB). +Valid values for this option are page size (typically 4KiB) +through to 1GiB, inclusive, in power-of-2 increments. +.TP +.BR attr2 | noattr2 +The options enable/disable (default is enabled) an "opportunistic" +improvement to be made in the way inline extended attributes are +stored on-disk. +When the new form is used for the first time (by setting or +removing extended attributes) the on-disk superblock feature +bit field will be updated to reflect this format being in use. +.TP +.B barrier +Enables the use of block layer write barriers for writes into +the journal and unwritten extent conversion. This allows for +drive level write caching to be enabled, for devices that +support write barriers. +.TP +.B dmapi +Enable the DMAPI (Data Management API) event callouts. +Use with the +.B mtpt +option. +.TP +.BR grpid | bsdgroups " and " nogrpid | sysvgroups +These options define what group ID a newly created file gets. +When grpid is set, it takes the group ID of the directory in +which it is created; otherwise (the default) it takes the fsgid +of the current process, unless the directory has the setgid bit +set, in which case it takes the gid from the parent directory, +and also gets the setgid bit set if it is a directory itself. +.TP +.BI ihashsize= value +Sets the number of hash buckets available for hashing the +in-memory inodes of the specified mount point. If a value +of zero is used, the value selected by the default algorithm +will be displayed in +.IR /proc/mounts . +.TP +.BR ikeep | noikeep +When inode clusters are emptied of inodes, keep them around +on the disk (ikeep) - this is the traditional XFS behaviour +and is still the default for now. Using the noikeep option, +inode clusters are returned to the free space pool. +.TP +.B inode64 +Indicates that XFS is allowed to create inodes at any location +in the filesystem, including those which will result in inode +numbers occupying more than 32 bits of significance. This is +provided for backwards compatibility, but causes problems for +backup applications that cannot handle large inode numbers. +.TP +.BR largeio | nolargeio +If +.B nolargeio +is specified, the optimal I/O reported in +st_blksize by +.BR stat (2) +will be as small as possible to allow user +applications to avoid inefficient read/modify/write I/O. +If +.B largeio +is specified, a filesystem that has a +.B swidth +specified +will return the +.B swidth +value (in bytes) in st_blksize. If the +filesystem does not have a +.B swidth +specified but does specify +an +.B allocsize +then +.B allocsize +(in bytes) will be returned +instead. +If neither of these two options are specified, then filesystem +will behave as if +.B nolargeio +was specified. +.TP +.BI logbufs= value +Set the number of in-memory log buffers. Valid numbers range +from 2-8 inclusive. +The default value is 8 buffers for any recent kernel. +.TP +.BI logbsize= value +Set the size of each in-memory log buffer. +Size may be specified in bytes, or in kilobytes with a "k" suffix. +Valid sizes for version 1 and version 2 logs are 16384 (16k) and +32768 (32k). Valid sizes for version 2 logs also include +65536 (64k), 131072 (128k) and 262144 (256k). +The default value for any recent kernel is 32768. +.TP +\fBlogdev=\fP\fIdevice\fP and \fBrtdev=\fP\fIdevice\fP +Use an external log (metadata journal) and/or real-time device. +An XFS filesystem has up to three parts: a data section, a log section, +and a real-time section. +The real-time section is optional, and the log section can be separate +from the data section or contained within it. +Refer to +.BR xfs (5). +.TP +.BI mtpt= mountpoint +Use with the +.B dmapi +option. The value specified here will be +included in the DMAPI mount event, and should be the path of +the actual mountpoint that is used. +.TP +.B noalign +Data allocations will not be aligned at stripe unit boundaries. +.TP +.B noatime +Access timestamps are not updated when a file is read. +.TP +.B norecovery +The filesystem will be mounted without running log recovery. +If the filesystem was not cleanly unmounted, it is likely to +be inconsistent when mounted in +.B norecovery +mode. +Some files or directories may not be accessible because of this. +Filesystems mounted +.B norecovery +must be mounted read-only or the mount will fail. +.TP +.B nouuid +Don't check for double mounted filesystems using the filesystem uuid. +This is useful to mount LVM snapshot volumes. +.TP +.B osyncisosync +Make O_SYNC writes implement true O_SYNC. WITHOUT this option, +Linux XFS behaves as if an +.B osyncisdsync +option is used, +which will make writes to files opened with the O_SYNC flag set +behave as if the O_DSYNC flag had been used instead. +This can result in better performance without compromising +data safety. +However if this option is not in effect, timestamp updates from +O_SYNC writes can be lost if the system crashes. +If timestamp updates are critical, use the +.B osyncisosync +option. +.TP +.BR uquota | usrquota | uqnoenforce | quota +User disk quota accounting enabled, and limits (optionally) +enforced. Refer to +.BR xfs_quota (8) +for further details. +.TP +.BR gquota | grpquota | gqnoenforce +Group disk quota accounting enabled and limits (optionally) +enforced. Refer to +.BR xfs_quota (8) +for further details. +.TP +.BR pquota | prjquota | pqnoenforce +Project disk quota accounting enabled and limits (optionally) +enforced. Refer to +.BR xfs_quota (8) +for further details. +.TP +\fBsunit=\fP\fIvalue\fP and \fBswidth=\fP\fIvalue\fP +Used to specify the stripe unit and width for a RAID device or a stripe +volume. +.I value +must be specified in 512-byte block units. +If this option is not specified and the filesystem was made on a stripe +volume or the stripe width or unit were specified for the RAID device at +mkfs time, then the mount system call will restore the value from the +superblock. +For filesystems that are made directly on RAID devices, these options can be +used to override the information in the superblock if the underlying disk +layout changes after the filesystem has been created. +The +.B swidth +option is required if the +.B sunit +option has been specified, +and must be a multiple of the +.B sunit +value. +.TP +.B swalloc +Data allocations will be rounded up to stripe width boundaries +when the current end of file is being extended and the file +size is larger than the stripe width size. + +.SH "Mount options for xiafs" +None. Although nothing is wrong with xiafs, it is not used much, +and is not maintained. Probably one shouldn't use it. +Since Linux version 2.1.21 xiafs is no longer part of the kernel source. + +.SH "THE LOOP DEVICE" +One further possible type is a mount via the loop device. For example, +the command +.RS +.sp +.B "mount /tmp/disk.img /mnt -t vfat -o loop=/dev/loop" +.sp +.RE +will set up the loop device +.I /dev/loop3 +to correspond to the file +.IR /tmp/disk.img , +and then mount this device on +.IR /mnt . + +If no explicit loop device is mentioned +(but just an option `\fB\-o loop\fP' is given), then +.B mount +will try to find some unused loop device and use that, for example +.RS +.sp +.B "mount /tmp/disk.img /mnt -o loop" +.sp +.RE +The mount command +.B automatically +creates a loop device from a regular file if a filesystem type is +not specified or the filesystem is known for libblkid, for example: +.RS +.sp +.B "mount /tmp/disk.img /mnt" +.sp +.B "mount -t ext3 /tmp/disk.img /mnt" +.sp +.RE +This type of mount knows about four options, namely +.BR loop ", " offset ", " sizelimit " and " encryption , +that are really options to +.BR \%losetup (8). +(These options can be used in addition to those specific +to the filesystem type.) + +Since Linux 2.6.25 is supported auto-destruction of loop devices and +then any loop device allocated by +.B mount +will be freed by +.B umount +independently on +.IR /etc/mtab . + +You can also free a loop device by hand, using `losetup -d' or `umount -d`. + +.SH RETURN CODES +.B mount +has the following return codes (the bits can be ORed): +.TP +.BR 0 +success +.TP +.BR 1 +incorrect invocation or permissions +.TP +.BR 2 +system error (out of memory, cannot fork, no more loop devices) +.TP +.BR 4 +internal +.B mount +bug +.TP +.BR 8 +user interrupt +.TP +.BR 16 +problems writing or locking /etc/mtab +.TP +.BR 32 +mount failure +.TP +.BR 64 +some mount succeeded + +.SH NOTES +The syntax of external mount helpers is: + +.RS +.BI /sbin/mount. <suffix> +.I spec dir +.RB [ \-sfnv ] +.RB [ \-o +.IR options ] +.RB [ \-t +.IR type.subtype ] +.RE + +where the <type> is filesystem type and \-sfnvo options have same meaning like +standard mount options. The \-t option is used for filesystems with subtypes +support (for example /sbin/mount.fuse -t fuse.sshfs). + +.SH FILES +.TP 18n +.I /etc/fstab +filesystem table +.TP +.I /etc/mtab +table of mounted filesystems +.TP +.I /etc/mtab~ +lock file +.TP +.I /etc/mtab.tmp +temporary file +.TP +.I /etc/filesystems +a list of filesystem types to try + +.SH "SEE ALSO" +.BR mount (2), +.BR umount (2), +.BR fstab (5), +.BR umount (8), +.BR swapon (8), +.BR nfs (5), +.BR xfs (5), +.BR e2label (8), +.BR xfs_admin (8), +.BR mountd (8), +.BR nfsd (8), +.BR mke2fs (8), +.BR tune2fs (8), +.BR losetup (8) +.SH BUGS +It is possible for a corrupted filesystem to cause a crash. +.PP +Some Linux filesystems don't support +.B "\-o sync and \-o dirsync" +(the ext2, ext3, fat and vfat filesystems +.I do +support synchronous updates (a la BSD) when mounted with the +.B sync +option). +.PP +The +.B "\-o remount" +may not be able to change mount parameters (all +.IR ext2fs -specific +parameters, except +.BR sb , +are changeable with a remount, for example, but you can't change +.B gid +or +.B umask +for the +.IR fatfs ). +.PP +Mount by label or uuid will work only if your devices have the names listed in +.IR /proc/partitions . +In particular, it may well fail if the kernel was compiled with devfs +but devfs is not mounted. +.PP +It is possible that files +.IR /etc/mtab +and +.IR /proc/mounts +don't match. The first file is based only on the mount command options, but the +content of the second file also depends on the kernel and others settings (e.g. +remote NFS server. In particular case the mount command may reports unreliable +information about a NFS mount point and the /proc/mounts file usually contains +more reliable information.) +.PP +Checking files on NFS filesystem referenced by file descriptors (i.e. the +.BR fcntl +and +.BR ioctl +families of functions) may lead to inconsistent result due to the lack of +consistency check in kernel even if noac is used. +.SH HISTORY +A +.B mount +command existed in Version 5 AT&T UNIX. +.SH AVAILABILITY +The mount command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/mount-deprecated/mount.c b/mount-deprecated/mount.c new file mode 100644 index 000000000..2e9b2a4e3 --- /dev/null +++ b/mount-deprecated/mount.c @@ -0,0 +1,2799 @@ +/* + * A mount(8) for Linux. + * + * Modifications by many people. Distributed under GPL. + */ + +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <stdio.h> + +#include <pwd.h> +#include <grp.h> + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/mount.h> + +#include <mntent.h> + +#ifdef HAVE_LIBSELINUX +#include <selinux/selinux.h> +#include <selinux/context.h> +#endif + +#include "pathnames.h" +#include "fsprobe.h" +#include "devname.h" +#include "mount_constants.h" +#include "sundries.h" +#include "mount_mntent.h" +#include "fstab.h" +#include "loopdev.h" +#include "linux_version.h" +#include "getusername.h" +#include "env.h" +#include "nls.h" +#include "blkdev.h" +#include "strutils.h" +#include "closestream.h" +#include "xgetpass.h" + +#define DO_PS_FIDDLING + +#ifdef DO_PS_FIDDLING +#include "setproctitle.h" +#endif + +/* True for fake mount (-f). */ +static int fake = 0; + +/* True if we are allowed to call /sbin/mount.${FSTYPE} */ +static int external_allowed = 1; + +/* Don't write an entry in /etc/mtab (-n). */ +static int nomtab = 0; + +/* True for explicit readonly (-r). */ +static int readonly = 0; + +/* Nonzero for sloppy (-s). */ +static int sloppy = 0; + +/* True for explicit read/write (-w). */ +static int readwrite = 0; + +/* True for all mount (-a). */ +static int mount_all = 0; + +/* True for fork() during all mount (-F). */ +static int optfork = 0; + +/* Add volumelabel in a listing of mounted devices (-l). */ +static int list_with_volumelabel = 0; + +/* Nonzero for mount {bind|move|make-shared|make-private| + * make-unbindable|make-slave} + */ +static int mounttype = 0; + +/* True if (ruid != euid) or (0 != ruid), i.e. only "user" mounts permitted. */ +static int restricted = 1; + +/* Contains the fd to read the passphrase from, if any. */ +static int pfd = -1; + +#ifdef HAVE_LIBMOUNT_MOUNT +static struct libmnt_update *mtab_update; +static char *mtab_opts; +static unsigned long mtab_flags; + +static void prepare_mtab_entry(const char *spec, const char *node, + const char *type, const char *opts, unsigned long flags); +#endif + +/* mount(2) options */ +struct mountargs { + const char *spec; + const char *node; + const char *type; + int flags; + void *data; +}; + +/* Map from -o and fstab option strings to the flag argument to mount(2). */ +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 */ + int cmask; /* comments mask */ +}; + +/* Custom mount options for our own purposes. */ +/* Maybe these should now be freed for kernel use again */ +#define MS_NOAUTO 0x80000000 +#define MS_USERS 0x40000000 +#define MS_USER 0x20000000 +#define MS_OWNER 0x10000000 +#define MS_GROUP 0x08000000 +#define MS_COMMENT 0x02000000 +#define MS_LOOP 0x00010000 + +#define MS_COMMENT_NOFAIL (1 << 1) +#define MS_COMMENT_NETDEV (1 << 2) + +/* Options that we keep the mount system call from seeing. */ +#define MS_NOSYS (MS_NOAUTO|MS_USERS|MS_USER|MS_COMMENT|MS_LOOP) + +/* Options that we keep from appearing in the options field in the mtab. */ +#define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USERS|MS_USER) + +#define MS_PROPAGATION (MS_SHARED|MS_SLAVE|MS_UNBINDABLE|MS_PRIVATE) + +/* Options that we make ordinary users have by default. */ +#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV) + +/* Options that we make owner-mounted devices have by default */ +#define MS_OWNERSECURE (MS_NOSUID|MS_NODEV) + +static const struct opt_map opt_map[] = { + { "defaults", 0, 0, 0 }, /* default options */ + { "ro", 1, 0, MS_RDONLY }, /* read-only */ + { "rw", 1, 1, MS_RDONLY }, /* read-write */ + { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */ + { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */ + { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */ + { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */ + { "dev", 0, 1, MS_NODEV }, /* interpret device files */ + { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */ + { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */ + { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ + { "dirsync", 0, 0, MS_DIRSYNC}, /* synchronous directory modifications */ + { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */ + { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */ + { "rbind", 0, 0, MS_BIND|MS_REC }, /* Idem, plus mounted subtrees */ + { "auto", 0, 1, MS_NOAUTO }, /* Can be mounted using -a */ + { "noauto", 0, 0, MS_NOAUTO }, /* Can only be mounted explicitly */ + { "users", 0, 0, MS_USERS }, /* Allow ordinary user to mount */ + { "nousers", 0, 1, MS_USERS }, /* Forbid ordinary user to mount */ + { "user", 0, 0, MS_USER }, /* Allow ordinary user to mount */ + { "nouser", 0, 1, MS_USER }, /* Forbid ordinary user to mount */ + { "owner", 0, 0, MS_OWNER }, /* Let the owner of the device mount */ + { "noowner", 0, 1, MS_OWNER }, /* Device owner has no special privs */ + { "group", 0, 0, MS_GROUP }, /* Let the group of the device mount */ + { "nogroup", 0, 1, MS_GROUP }, /* Device group has no special privs */ + { "_netdev", 0, 0, MS_COMMENT, MS_COMMENT_NETDEV }, /* Device requires network */ + { "comment", 0, 0, MS_COMMENT}, /* fstab comment only (kudzu,_netdev)*/ + + /* add new options here */ +#ifdef MS_NOSUB + { "sub", 0, 1, MS_NOSUB }, /* allow submounts */ + { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */ +#endif +#ifdef MS_SILENT + { "silent", 0, 0, MS_SILENT }, /* be quiet */ + { "loud", 0, 1, MS_SILENT }, /* print out messages. */ +#endif +#ifdef MS_MANDLOCK + { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */ + { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */ +#endif + { "loop", 1, 0, MS_LOOP }, /* use a loop device */ +#ifdef MS_NOATIME + { "atime", 0, 1, MS_NOATIME }, /* Update access time */ + { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */ +#endif +#ifdef MS_I_VERSION + { "iversion", 0, 0, MS_I_VERSION }, /* Update inode I_version time */ + { "noiversion", 0, 1, MS_I_VERSION }, /* Don't update inode I_version time */ +#endif +#ifdef MS_NODIRATIME + { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */ + { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */ +#endif +#ifdef MS_RELATIME + { "relatime", 0, 0, MS_RELATIME }, /* Update access times relative to + mtime/ctime */ + { "norelatime", 0, 1, MS_RELATIME }, /* Update access time without regard + to mtime/ctime */ +#endif +#ifdef MS_STRICTATIME + { "strictatime", 0, 0, MS_STRICTATIME }, /* Strict atime semantics */ + { "nostrictatime", 0, 1, MS_STRICTATIME }, /* kernel default atime */ +#endif + { "nofail", 0, 0, MS_COMMENT, MS_COMMENT_NOFAIL }, /* Do not fail if ENOENT on dev */ + { NULL, 0, 0, 0 } +}; + +static int opt_nofail; +static int invuser_flags; +static int comment_flags; + +static const char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_sizelimit, + *opt_encryption, *opt_speed, *opt_comment, *opt_uhelper, *opt_helper; + +static int is_readonly(const char *node); +static int mounted (const char *spec0, const char *node0, struct mntentchn *fstab_mc); +static int check_special_mountprog(const char *spec, const char *node, + const char *type, int flags, char *extra_opts, int *status); + +static struct string_opt_map { + char *tag; + int skip; + const char **valptr; +} string_opt_map[] = { + { "loop=", 0, &opt_loopdev }, + { "vfs=", 1, &opt_vfstype }, + { "offset=", 0, &opt_offset }, + { "sizelimit=", 0, &opt_sizelimit }, + { "encryption=", 0, &opt_encryption }, + { "speed=", 0, &opt_speed }, + { "comment=", 1, &opt_comment }, + { "uhelper=", 0, &opt_uhelper }, + { "helper=", 0, &opt_helper }, + { 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 void +clear_flags_opts(void) { + invuser_flags = 0; + comment_flags = 0; + opt_nofail = 0; +} + +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; +} + +/* Report on a single mount. */ +static void +print_one (const struct my_mntent *me) { + + char *fsname = NULL; + + if (mount_quiet) + return; + + /* users assume backing file name rather than /dev/loopN in + * mount(8) output if the device has been initialized by mount(8). + */ + if (strncmp(me->mnt_fsname, "/dev/loop", 9) == 0 && + loopdev_is_autoclear(me->mnt_fsname)) + fsname = loopdev_get_backing_file(me->mnt_fsname); + + if (!fsname) + fsname = (char *) me->mnt_fsname; + + printf ("%s on %s", fsname, me->mnt_dir); + if (me->mnt_type != NULL && *(me->mnt_type) != '\0') + printf (" type %s", me->mnt_type); + if (me->mnt_opts != NULL) + printf (" (%s)", me->mnt_opts); + if (list_with_volumelabel && is_pseudo_fs(me->mnt_type) == 0) { + const char *devname = spec_to_devname(me->mnt_fsname); + + if (devname) { + const char *label; + + label = fsprobe_get_label_by_devname(devname); + my_free(devname); + + if (label) { + printf (" [%s]", label); + my_free(label); + } + } + } + printf ("\n"); +} + +/* Report on everything in mtab (of the specified types if any). */ +static int +print_all (char *types) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + if (matching_type (mc->m.mnt_type, types)) + print_one (&(mc->m)); + } + + if (!mtab_does_not_exist() && !mtab_is_a_symlink() && is_readonly(_PATH_MOUNTED)) + printf(_("\n" + "mount: warning: /etc/mtab is not writable (e.g. read-only filesystem).\n" + " It's possible that information reported by mount(8) is not\n" + " up to date. For actual information about system mount points\n" + " check the /proc/mounts file.\n\n")); + + exit (0); +} + +/* reallocates its first arg */ +static char * +append_opt(char *s, const char *opt, const char *val) +{ + if (!opt) + return s; + if (!s) { + if (!val) + return xstrdup(opt); /* opt */ + + return xstrconcat3(NULL, opt, val); /* opt=val */ + } + if (!val) + return xstrconcat3(s, ",", opt); /* s,opt */ + + return xstrconcat4(s, ",", opt, val); /* s,opt=val */ +} + +static char * +append_numopt(char *s, const char *opt, unsigned int num) +{ + char buf[32]; + + snprintf(buf, sizeof(buf), "%u", num); + return append_opt(s, opt, buf); +} + +#ifdef HAVE_LIBSELINUX +/* strip quotes from a "string" + * Warning: This function modify the "str" argument. + */ +static char * +strip_quotes(char *str) +{ + char *end = NULL; + + if (*str != '"') + return str; + + end = strrchr(str, '"'); + if (end == NULL || end == str) + die (EX_USAGE, _("mount: improperly quoted option string '%s'"), str); + + *end = '\0'; + return str+1; +} + +/* translates SELinux context from human to raw format and + * appends it to the mount extra options. + * + * returns -1 on error and 0 on success + */ +static int +append_context(const char *optname, char *optdata, char **extra_opts) +{ + security_context_t raw = NULL; + char *data = NULL; + + if (is_selinux_enabled() != 1) + /* ignore the option if we running without selinux */ + return 0; + + if (optdata==NULL || *optdata=='\0' || optname==NULL) + return -1; + + /* TODO: use strip_quotes() for all mount options? */ + data = *optdata =='"' ? strip_quotes(optdata) : optdata; + + if (selinux_trans_to_raw_context( + (security_context_t) data, &raw) == -1 || + raw == NULL) + return -1; + + if (verbose) + printf(_("mount: translated %s '%s' to '%s'\n"), + optname, data, (char *) raw); + + *extra_opts = append_opt(*extra_opts, optname, NULL); + *extra_opts = xstrconcat4(*extra_opts, "\"", (char *) raw, "\""); + + freecon(raw); + return 0; +} + +/* returns newly allocated string without *context= options */ +static char *remove_context_options(char *opts) +{ + char *begin = NULL, *end = NULL, *p; + int open_quote = 0, changed = 0; + + if (!opts) + return NULL; + + opts = xstrdup(opts); + + for (p = opts; p && *p; p++) { + if (!begin) + begin = p; /* begin of the option item */ + if (*p == '"') + open_quote ^= 1; /* reverse the status */ + if (open_quote) + continue; /* still in quoted block */ + if (*p == ',') + end = p; /* terminate the option item */ + else if (*(p + 1) == '\0') + end = p + 1; /* end of optstr */ + if (!begin || !end) + continue; + + if (strncmp(begin, "context=", 8) == 0 || + strncmp(begin, "fscontext=", 10) == 0 || + strncmp(begin, "defcontext=", 11) == 0 || + strncmp(begin, "rootcontext=", 12) == 0 || + strncmp(begin, "seclabel", 8) == 0) { + size_t sz; + + if ((begin == opts || *(begin - 1) == ',') && *end == ',') + end++; + sz = strlen(end); + + memmove(begin, end, sz + 1); + if (!*begin && *(begin - 1) == ',') + *(begin - 1) = '\0'; + + p = begin; + changed = 1; + } + begin = end = NULL; + } + + if (changed && verbose) + printf (_("mount: SELinux *context= options are ignore on remount.\n")); + + return opts; +} + +static int has_context_option(char *opts) +{ + if (get_option("context=", opts, NULL) || + get_option("fscontext=", opts, NULL) || + get_option("defcontext=", opts, NULL) || + get_option("rootcontext=", opts, NULL)) + return 1; + + return 0; +} + +#endif + +/* + * Look for OPT in opt_map table and return mask value. + * If OPT isn't found, tack it onto extra_opts (which is non-NULL). + * For the options uid= and gid= replace user or group name by its value. + */ +static inline void +parse_opt(char *opt, int *mask, int *inv_user, 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->inv && ((*mask & MS_USER) || (*mask & MS_USERS)) + && (om->mask & MS_SECURE)) + *inv_user |= om->mask; + if ((om->mask == MS_USER || om->mask == MS_USERS) + && !om->inv) + *mask |= MS_SECURE; + if ((om->mask == MS_OWNER || om->mask == MS_GROUP) + && !om->inv) + *mask |= MS_OWNERSECURE; +#ifdef MS_SILENT + if (om->mask == MS_SILENT && om->inv) { + mount_quiet = 1; + verbose = 0; + } +#endif + if (om->mask == MS_COMMENT) { + comment_flags |= om->cmask; + if (om->cmask == MS_COMMENT_NOFAIL) + opt_nofail = 1; + } + return; + } + + /* convert nonnumeric ids to numeric */ + if (!strncmp(opt, "uid=", 4) && !isdigit(opt[4])) { + struct passwd *pw = getpwnam(opt+4); + + if (pw) { + *extra_opts = append_numopt(*extra_opts, + "uid=", pw->pw_uid); + return; + } + } + if (!strncmp(opt, "gid=", 4) && !isdigit(opt[4])) { + struct group *gr = getgrnam(opt+4); + + if (gr) { + *extra_opts = append_numopt(*extra_opts, + "gid=", gr->gr_gid); + return; + } + } + +#ifdef HAVE_LIBSELINUX + if (strncmp(opt, "context=", 8) == 0 && *(opt+8)) { + if (append_context("context=", opt+8, extra_opts) == 0) + return; + } + if (strncmp(opt, "fscontext=", 10) == 0 && *(opt+10)) { + if (append_context("fscontext=", opt+10, extra_opts) == 0) + return; + } + if (strncmp(opt, "defcontext=", 11) == 0 && *(opt+11)) { + if (append_context("defcontext=", opt+11, extra_opts) == 0) + return; + } + if (strncmp(opt, "rootcontext=", 12) == 0 && *(opt+12)) { + if (append_context("rootcontext=", opt+12, extra_opts) == 0) + return; + } +#endif + *extra_opts = append_opt(*extra_opts, opt, NULL); +} + + +/* Take -o options list and compute 4th and 5th args to mount(2). flags + gets the standard options (indicated by bits) and extra_opts all the rest */ +static void +parse_opts (const char *options, int *flags, char **extra_opts) { + *flags = 0; + *extra_opts = NULL; + + clear_string_opts(); + clear_flags_opts(); + + if (options != NULL) { + char *opts = xstrdup(options); + int open_quote = 0; + char *opt, *p; + + for (p=opts, opt=NULL; p && *p; p++) { + if (!opt) + opt = p; /* begin of the option item */ + if (*p == '"') + open_quote ^= 1; /* reverse the status */ + if (open_quote) + continue; /* still in quoted block */ + if (*p == ',') + *p = '\0'; /* terminate the option item */ + /* end of option item or last item */ + if (*p == '\0' || *(p+1) == '\0') { + if (!parse_string_opt(opt)) + parse_opt(opt, flags, &invuser_flags, extra_opts); + opt = NULL; + } + } + free(opts); + } + + if (readonly) + *flags |= MS_RDONLY; + if (readwrite) + *flags &= ~MS_RDONLY; + + *flags |= mounttype; + + /* The propagation flags should not be used together with any + * other flags (except MS_REC and MS_SILENT) */ + if (*flags & MS_PROPAGATION) + *flags &= (MS_PROPAGATION | MS_REC | MS_SILENT); +} + +/* Try to build a canonical options string. */ +static char * +fix_opts_string (int flags, const char *extra_opts, + const char *user, int inv_user) +{ + const struct opt_map *om; + const struct string_opt_map *m; + char *new_opts; + + new_opts = append_opt(NULL, (flags & MS_RDONLY) ? "ro" : "rw", NULL); + for (om = opt_map; om->opt != NULL; om++) { + if (om->skip) + continue; + if (om->inv || !om->mask || (flags & om->mask) != om->mask) + continue; + if (om->mask == MS_COMMENT && !(comment_flags & om->cmask)) + continue; + new_opts = append_opt(new_opts, om->opt, NULL); + flags &= ~om->mask; + } + for (m = &string_opt_map[0]; m->tag; m++) { + if (!m->skip && *(m->valptr)) + new_opts = append_opt(new_opts, m->tag, *(m->valptr)); + } + if (extra_opts && *extra_opts) + new_opts = append_opt(new_opts, extra_opts, NULL); + + if (user) + new_opts = append_opt(new_opts, "user=", user); + + if (inv_user) { + for (om = opt_map; om->opt != NULL; om++) { + if (om->mask && om->inv + && (inv_user & om->mask) == om->mask) { + new_opts = append_opt(new_opts, om->opt, NULL); + inv_user &= ~om->mask; + } + } + } + + return new_opts; +} + +static int +already (const char *spec0, const char *node0) { + struct mntentchn *mc; + int ret = 1; + char *spec = canonicalize_spec(spec0); + char *node = canonicalize(node0); + + if ((mc = getmntfile(node)) != NULL) + error (_("mount: according to mtab, " + "%s is already mounted on %s"), + mc->m.mnt_fsname, node); + else if (spec && strcmp (spec, "none") && + (mc = getmntfile(spec)) != NULL) + error (_("mount: according to mtab, %s is mounted on %s"), + spec, mc->m.mnt_dir); + else + ret = 0; + + free(spec); + free(node); + + return ret; +} + +/* Create mtab with a root entry. */ +static void +create_mtab (void) { + struct mntentchn *fstab; + struct my_mntent mnt; + int flags; + mntFILE *mfp; + + lock_mtab(); + + mfp = my_setmntent (_PATH_MOUNTED, "a+"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + int errsv = errno; + die (EX_FILEIO, _("mount: can't open %s for writing: %s"), + _PATH_MOUNTED, strerror (errsv)); + } + + /* Find the root entry by looking it up in fstab */ + if ((fstab = getfs_by_dir ("/")) || (fstab = getfs_by_dir ("root"))) { + char *extra_opts; + parse_opts (fstab->m.mnt_opts, &flags, &extra_opts); + mnt.mnt_dir = "/"; + mnt.mnt_fsname = spec_to_devname(fstab->m.mnt_fsname); + mnt.mnt_type = fstab->m.mnt_type; + mnt.mnt_opts = fix_opts_string (flags, extra_opts, NULL, 0); + mnt.mnt_freq = mnt.mnt_passno = 0; + free(extra_opts); + + if (my_addmntent (mfp, &mnt) == 1) { + int errsv = errno; + die (EX_FILEIO, _("mount: error writing %s: %s"), + _PATH_MOUNTED, strerror (errsv)); + } + } + if (fchmod (fileno (mfp->mntent_fp), 0644) < 0) + if (errno != EROFS) { + int errsv = errno; + die (EX_FILEIO, + _("mount: error changing mode of %s: %s"), + _PATH_MOUNTED, strerror (errsv)); + } + my_endmntent (mfp); + + unlock_mtab(); + + reset_mtab_info(); +} + +/* count successful mount system calls */ +static int mountcount = 0; + +/* + * do_mount_syscall() + * Mount a single file system. Keep track of successes. + * returns: 0: OK, -1: error in errno + */ +static int +do_mount_syscall (struct mountargs *args) { + int flags = args->flags; + + if ((flags & MS_MGC_MSK) == 0) + flags |= MS_MGC_VAL; + + if (verbose > 2) + printf("mount: mount(2) syscall: source: \"%s\", target: \"%s\", " + "filesystemtype: \"%s\", mountflags: %d, data: %s\n", + args->spec, args->node, args->type, flags, (char *) args->data); + + return mount (args->spec, args->node, args->type, flags, args->data); +} + +/* + * do_mount() + * Mount a single file system, possibly invoking an external handler to + * do so. Keep track of successes. + * returns: 0: OK, -1: error in errno + */ +static int +do_mount (struct mountargs *args, int *special, int *status) { + int ret; + if (check_special_mountprog(args->spec, args->node, args->type, + args->flags, args->data, status)) { + *special = 1; + ret = 0; + } else { +#ifdef HAVE_LIBMOUNT_MOUNT + prepare_mtab_entry(args->spec, args->node, args->type, + mtab_opts, mtab_flags); +#endif + ret = do_mount_syscall(args); + } + if (ret == 0) + mountcount++; + return ret; +} + +/* + * check_special_mountprog() + * If there is a special mount program for this type, exec it. + * returns: 0: no exec was done, 1: exec was done, status has result + */ +static int +check_special_mountprog(const char *spec, const char *node, const char *type, int flags, + char *extra_opts, int *status) { + char search_path[] = FS_SEARCH_PATH; + char *path, mountprog[150]; + struct stat statbuf; + int res; + + if (!external_allowed) + return 0; + + if (type == NULL || strcmp(type, "none") == 0) + return 0; + + path = strtok(search_path, ":"); + while (path) { + int type_opt = 0; + + res = snprintf(mountprog, sizeof(mountprog), "%s/mount.%s", + path, type); + path = strtok(NULL, ":"); + if (res < 0 || (size_t) res >= sizeof(mountprog)) + continue; + + res = stat(mountprog, &statbuf); + if (res == -1 && errno == ENOENT && strchr(type, '.')) { + /* If type ends with ".subtype" try without it */ + *strrchr(mountprog, '.') = '\0'; + type_opt = 1; + res = stat(mountprog, &statbuf); + } + if (res) + continue; + + if (verbose) + fflush(stdout); + + switch (fork()) { + case 0: { /* child */ + char *oo, *mountargs[12]; + int i = 0; + + if (setgid(getgid()) < 0) + die(EX_FAIL, _("mount: cannot set group id: %m")); + + if (setuid(getuid()) < 0) + die(EX_FAIL, _("mount: cannot set user id: %m")); + + oo = fix_opts_string(flags, extra_opts, NULL, invuser_flags); + mountargs[i++] = mountprog; /* 1 */ + mountargs[i++] = (char *) spec; /* 2 */ + mountargs[i++] = (char *) node; /* 3 */ + if (sloppy && strncmp(type, "nfs", 3) == 0) + mountargs[i++] = "-s"; /* 4 */ + if (fake) + mountargs[i++] = "-f"; /* 5 */ + if (nomtab) + mountargs[i++] = "-n"; /* 6 */ + if (verbose) + mountargs[i++] = "-v"; /* 7 */ + if (oo && *oo) { + mountargs[i++] = "-o"; /* 8 */ + mountargs[i++] = oo; /* 9 */ + } + if (type_opt) { + mountargs[i++] = "-t"; /* 10 */ + mountargs[i++] = (char *) type; /* 11 */ + } + mountargs[i] = NULL; /* 12 */ + + if (verbose > 2) { + i = 0; + while (mountargs[i]) { + printf("mount: external mount: argv[%d] = \"%s\"\n", + i, mountargs[i]); + i++; + } + fflush(stdout); + } + + execv(mountprog, mountargs); + exit(1); /* exec failed */ + } + + default: { /* parent */ + int st; + wait(&st); + *status = (WIFEXITED(st) ? WEXITSTATUS(st) : EX_SYSERR); + return 1; + } + + case -1: { /* error */ + int errsv = errno; + error(_("mount: cannot fork: %s"), strerror(errsv)); + } + } + } + + return 0; +} + + +/* list of already tested filesystems by procfsloop_mount() */ +static struct tried { + struct tried *next; + char *type; +} *tried = NULL; + +static int +was_tested(const char *fstype) { + struct tried *t; + + for (t = tried; t; t = t->next) { + if (!strcmp(t->type, fstype)) + return 1; + } + return 0; +} + +static void +set_tested(const char *fstype) { + struct tried *t = xmalloc(sizeof(struct tried)); + + t->next = tried; + t->type = xstrdup(fstype); + tried = t; +} + +static void +free_tested(void) { + struct tried *t, *tt; + + t = tried; + while(t) { + free(t->type); + tt = t->next; + free(t); + t = tt; + } + tried = NULL; +} + +static char * +procfsnext(FILE *procfs) { + char line[100]; + char fsname[100]; + + while (fgets(line, sizeof(line), procfs)) { + if (sscanf (line, "nodev %[^#\n]\n", fsname) == 1) continue; + if (sscanf (line, " %[^# \n]\n", fsname) != 1) continue; + return xstrdup(fsname); + } + return 0; +} + +/* Only use /proc/filesystems here, this is meant to test what + the kernel knows about, so /etc/filesystems is irrelevant. + Return: 1: yes, 0: no, -1: cannot open procfs */ +static int +known_fstype_in_procfs(const char *type) +{ + FILE *procfs; + char *fsname; + int ret = -1; + + procfs = fopen(_PATH_PROC_FILESYSTEMS, "r"); + if (procfs) { + ret = 0; + while ((fsname = procfsnext(procfs)) != NULL) + if (!strcmp(fsname, type)) { + ret = 1; + break; + } + fclose(procfs); + procfs = NULL; + } + return ret; +} + +/* Try all types in FILESYSTEMS, except those in *types, + in case *types starts with "no" */ +/* return: 0: OK, -1: error in errno, 1: type not found */ +/* when 0 or -1 is returned, *types contains the type used */ +/* when 1 is returned, *types is NULL */ +static int +procfsloop_mount(int (*mount_fn)(struct mountargs *, int *, int *), + struct mountargs *args, + const char **types, + int *special, int *status) +{ + char *files[2] = { _PATH_FILESYSTEMS, _PATH_PROC_FILESYSTEMS }; + FILE *procfs; + char *fsname; + const char *notypes = NULL; + int no = 0; + int ret = 1; + int errsv = 0; + int i; + + if (*types && !strncmp(*types, "no", 2)) { + no = 1; + notypes = (*types) + 2; + } + *types = NULL; + + /* Use _PATH_PROC_FILESYSTEMS only when _PATH_FILESYSTEMS + * (/etc/filesystems) does not exist. In some cases trying a + * filesystem that the kernel knows about on the wrong data will crash + * the kernel; in such cases _PATH_FILESYSTEMS can be used to list the + * filesystems that we are allowed to try, and in the order they should + * be tried. End _PATH_FILESYSTEMS with a line containing a single '*' + * only, if _PATH_PROC_FILESYSTEMS should be tried afterwards. + */ + for (i=0; i<2; i++) { + procfs = fopen(files[i], "r"); + if (!procfs) + continue; + while ((fsname = procfsnext(procfs)) != NULL) { + if (!strcmp(fsname, "*")) { + fclose(procfs); + goto nexti; + } + if (was_tested (fsname)) + continue; + if (no && matching_type(fsname, notypes)) + continue; + set_tested (fsname); + args->type = fsname; + if (verbose) + printf(_("Trying %s\n"), fsname); + if ((*mount_fn) (args, special, status) == 0) { + *types = fsname; + ret = 0; + break; + } else if (errno != EINVAL && + known_fstype_in_procfs(fsname) == 1) { + *types = "guess"; + ret = -1; + errsv = errno; + break; + } + } + free_tested(); + fclose(procfs); + errno = errsv; + return ret; + nexti:; + } + return 1; +} + +static const char * +guess_fstype_by_devname(const char *devname, int *ambivalent) +{ + const char *type = fsprobe_get_fstype_by_devname_ambi(devname, ambivalent); + + if (verbose) { + printf (_("mount: you didn't specify a filesystem type for %s\n"), devname); + + if (!type) + printf (_(" I will try all types mentioned in %s or %s\n"), + _PATH_FILESYSTEMS, _PATH_PROC_FILESYSTEMS); + else if (!strcmp(type, MNTTYPE_SWAP)) + printf (_(" and it looks like this is swapspace\n")); + else + printf (_(" I will try type %s\n"), type); + } + return type; +} + +/* + * guess_fstype_and_mount() + * Mount a single file system. Guess the type when unknown. + * returns: 0: OK, -1: error in errno, 1: other error + * don't exit on non-fatal errors. + * on return types is filled with the type used. + */ +static int +guess_fstype_and_mount(const char *spec, const char *node, const char **types, + int flags, char *mount_opts, int *special, int *status) { + struct mountargs args = { spec, node, NULL, flags & ~MS_NOSYS, mount_opts }; + int ambivalent = 0; + + if (*types && strcasecmp (*types, "auto") == 0) + *types = NULL; + + if (!*types && !(flags & MS_REMOUNT)) { + *types = guess_fstype_by_devname(spec, &ambivalent); + if (*types) { + if (!strcmp(*types, MNTTYPE_SWAP)) { + error(_("%s looks like swapspace - not mounted"), spec); + *types = NULL; + return 1; + } else { + args.type = *types; + return do_mount (&args, special, status); + } + } else if (ambivalent) { + error(_("mount: %s: more filesystems detected. This should not happen,\n" + " use -t <type> to explicitly specify the filesystem type or\n" + " use wipefs(8) to clean up the device.\n"), spec); + return 1; + } + } + + /* Accept a comma-separated list of types, and try them one by one */ + /* A list like "nonfs,.." indicates types not to use */ + if (*types && strncmp(*types, "no", 2) && strchr(*types,',')) { + char *t = strdup(*types); + char *p; + + while((p = strchr(t,',')) != NULL) { + *p = 0; + args.type = *types = t; + if (do_mount (&args, special, status) == 0) + return 0; + t = p+1; + } + /* do last type below */ + *types = t; + } + + if (*types || (flags & MS_REMOUNT)) { + args.type = *types; + return do_mount (&args, special, status); + } + + return procfsloop_mount(do_mount, &args, types, special, status); +} + +/* + * restricted_check() + * Die if the user is not allowed to do this. + */ +static void +restricted_check(const char *spec, const char *node, int *flags, char **user) { + if (restricted) { + /* + * MS_OWNER: Allow owners to mount when fstab contains + * the owner option. Note that this should never be used + * in a high security environment, but may be useful to give + * people at the console the possibility of mounting a floppy. + * MS_GROUP: Allow members of device group to mount. (Martin Dickopp) + */ + if (*flags & (MS_OWNER | MS_GROUP)) { + struct stat sb; + + if (!strncmp(spec, "/dev/", 5) && stat(spec, &sb) == 0) { + + if (*flags & MS_OWNER) { + if (getuid() == sb.st_uid) + *flags |= MS_USER; + } + + if (*flags & MS_GROUP) { + if (getgid() == sb.st_gid) + *flags |= MS_USER; + else { + int n = getgroups(0, NULL); + + if (n > 0) { + gid_t *groups = xmalloc(n * sizeof(*groups)); + if (getgroups(n, groups) == n) { + int i; + for (i = 0; i < n; i++) { + if (groups[i] == sb.st_gid) { + *flags |= MS_USER; + break; + } + } + } + free(groups); + } + } + } + } + } + + /* James Kehl <mkehl@gil.com.au> came with a similar patch: + allow an arbitrary user to mount when he is the owner of + the mount-point and has write-access to the device. + This is even less secure. Let me skip it for the time being; + there should be an explicit fstab line allowing such things. */ + + if (!(*flags & (MS_USER | MS_USERS))) { + if (already (spec, node)) + die (EX_USAGE, _("mount failed")); + else + die (EX_USAGE, _("mount: only root can mount %s on %s"), spec, node); + } + if (*flags & MS_USER) + *user = getusername(); + } + + *flags &= ~(MS_OWNER | MS_GROUP); +} + +/* Check, if there already exists a mounted loop device on the mountpoint node + * with the same parameters. + */ +static int +is_mounted_same_loopfile(const char *node0, const char *loopfile, unsigned long long offset) +{ + struct mntentchn *mnt = NULL; + char *node; + int res = 0; + + node = canonicalize(node0); + + /* Search for mountpoint node in mtab, + * procceed if any of these has the loop option set or + * the device is a loop device + */ + mnt = getmntdirbackward(node, mnt); + if (!mnt) { + free(node); + return 0; + } + for(; mnt && res == 0; mnt = getmntdirbackward(node, mnt)) { + char *p; + + if (strncmp(mnt->m.mnt_fsname, "/dev/loop", 9) == 0) + res = loopdev_is_used((char *) mnt->m.mnt_fsname, + loopfile, offset, LOOPDEV_FL_OFFSET); + + else if (mnt->m.mnt_opts && + (p = strstr(mnt->m.mnt_opts, "loop="))) + { + char *dev = xstrdup(p+5); + if ((p = strchr(dev, ','))) + *p = '\0'; + res = loopdev_is_used(dev, + loopfile, offset, LOOPDEV_FL_OFFSET); + free(dev); + } + } + + free(node); + return res; +} + +static int +parse_offset(const char **opt, uintmax_t *val) +{ + char *tmp; + + if (strtosize(*opt, val)) + return -1; + + tmp = xmalloc(32); + snprintf(tmp, 32, "%jd", *val); + my_free(*opt); + *opt = tmp; + return 0; +} + +static int +loop_check(const char **spec, const char **type, int *flags, + int *loop, const char **loopdev, const char **loopfile, + const char *node) { + int looptype; + uintmax_t offset = 0, sizelimit = 0; + struct loopdev_cxt lc; + char *pwd = NULL; + int ret = EX_FAIL; + + /* + * In the case of a loop mount, either type is of the form lo@/dev/loop5 + * or the option "-o loop=/dev/loop5" or just "-o loop" is given, or + * mount just has to figure things out for itself from the fact that + * spec is not a block device. We do not test for a block device + * immediately: maybe later other types of mountable objects will occur. + */ + + *loopdev = opt_loopdev; + + looptype = (*type && strncmp("lo@", *type, 3) == 0); + if (looptype) { + if (*loopdev) + error(_("mount: loop device specified twice")); + *loopdev = *type + 3; + *type = opt_vfstype; + } else if (opt_vfstype) { + if (*type) + error(_("mount: type specified twice")); + else + *type = opt_vfstype; + } + + *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_sizelimit || opt_encryption); + *loopfile = *spec; + + /* Automatically create a loop device from a regular file if a filesystem + * is not specified or the filesystem is known for libblkid (these + * filesystems work with block devices only). + * + * Note that there is not a restriction (on kernel side) that prevents regular + * file as a mount(2) source argument. A filesystem that is able to mount + * regular files could be implemented. + */ + if (!*loop && !(*flags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) && + (!*type || strcmp(*type, "auto") == 0 || fsprobe_known_fstype(*type))) { + + struct stat st; + if (stat(*loopfile, &st) == 0) + *loop = S_ISREG(st.st_mode); + } + + if (*loop) { + *flags |= MS_LOOP; + if (fake) { + if (verbose) + printf(_("mount: skipping the setup of a loop device\n")); + } else { + int loop_opts = 0; + + /* since 2.6.37 we don't have to store backing filename to mtab + * because kernel provides the name in /sys + */ + if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) || + mtab_is_writable() == 0) { + + if (verbose) + printf(_("mount: enabling autoclear loopdev flag\n")); + loop_opts = LO_FLAGS_AUTOCLEAR; + } + + if (*flags & MS_RDONLY) + loop_opts |= LO_FLAGS_READ_ONLY; + + if (opt_offset && parse_offset(&opt_offset, &offset)) { + error(_("mount: invalid offset '%s' specified"), opt_offset); + return EX_FAIL; + } + if (opt_sizelimit && parse_offset(&opt_sizelimit, &sizelimit)) { + error(_("mount: invalid sizelimit '%s' specified"), opt_sizelimit); + return EX_FAIL; + } + + if (is_mounted_same_loopfile(node, *loopfile, offset)) { + error(_("mount: according to mtab %s is already mounted on %s as loop"), *loopfile, node); + return EX_FAIL; + } + + if (opt_encryption) { +#ifdef MCL_FUTURE + if (mlockall(MCL_CURRENT | MCL_FUTURE)) { + error(_("mount: couldn't lock into memory")); + return EX_FAIL; + } +#endif + pwd = xgetpass(pfd, _("Password: ")); + } + + loopcxt_init(&lc, 0); + /*loopcxt_enable_debug(&lc, 1);*/ + + if (*loopdev && **loopdev) + loopcxt_set_device(&lc, *loopdev); /* use loop=<devname> */ + + do { + int rc; + + if ((!*loopdev || !**loopdev) && loopcxt_find_unused(&lc) == 0) + *loopdev = loopcxt_strdup_device(&lc); + + if (!*loopdev) { + error(_("mount: failed to found free loop device")); + loopcxt_deinit(&lc); + goto err; /* no more loop devices */ + } + if (verbose) + printf(_("mount: going to use the loop device %s\n"), *loopdev); + + rc = loopcxt_set_backing_file(&lc, *loopfile); + + if (!rc && offset) + rc = loopcxt_set_offset(&lc, offset); + if (!rc && sizelimit) + rc = loopcxt_set_sizelimit(&lc, sizelimit); + if (!rc && opt_encryption && pwd) + loopcxt_set_encryption(&lc, opt_encryption, pwd); + if (!rc) + loopcxt_set_flags(&lc, loop_opts); + + if (rc) { + error(_("mount: %s: failed to set loopdev attributes"), *loopdev); + loopcxt_deinit(&lc); + goto err; + } + + /* setup the device */ + rc = loopcxt_setup_device(&lc); + if (!rc) + break; /* success */ + + if (rc != -EBUSY) { + error(_("mount: %s: failed setting up loop device: %m"), *loopfile); + if (!opt_loopdev) { + my_free(*loopdev); + *loopdev = NULL; + } + loopcxt_deinit(&lc); + goto err; + } + + if (!opt_loopdev) { + if (verbose) + printf(_("mount: stolen loop=%s ...trying again\n"), *loopdev); + my_free(*loopdev); + *loopdev = NULL; + continue; + } + error(_("mount: stolen loop=%s"), *loopdev); + loopcxt_deinit(&lc); + goto err; + + } while (!*loopdev); + + if (verbose > 1) + printf(_("mount: setup loop device successfully\n")); + *spec = *loopdev; + + if (loopcxt_is_readonly(&lc)) + *flags |= MS_RDONLY; + + if (loopcxt_is_autoclear(&lc)) + /* Prevent recording loop dev in mtab for cleanup on umount */ + *loop = 0; + + /* We have to keep the device open until mount(2), otherwise it will + * be auto-cleared by kernel (because LO_FLAGS_AUTOCLEAR) */ + loopcxt_set_fd(&lc, -1, 0); + loopcxt_deinit(&lc); + } + } + + ret = 0; +err: + if (pwd) { + char *p = pwd; + while (*p) + *p++ = '\0'; + free(pwd); + } + return ret; +} + + +#ifdef HAVE_LIBMOUNT_MOUNT +static void +verbose_mount_info(const char *spec, const char *node, const char *type, + const char *opts) +{ + struct my_mntent mnt; + + mnt.mnt_fsname = is_pseudo_fs(type) ? spec : canonicalize(spec); + mnt.mnt_dir = canonicalize (node); + mnt.mnt_type = type; + mnt.mnt_opts = opts; + + print_one (&mnt); + + if (spec != mnt.mnt_fsname) + my_free(mnt.mnt_fsname); + my_free(mnt.mnt_dir); +} + +static void +prepare_mtab_entry(const char *spec, const char *node, const char *type, + const char *opts, unsigned long flags) +{ + struct libmnt_fs *fs = mnt_new_fs(); + int rc = -1; + + if (!mtab_update) + mtab_update = mnt_new_update(); + + if (mtab_update && fs) { + const char *cn_spec = is_pseudo_fs(type) ? spec : canonicalize(spec); + const char *cn_node = canonicalize(node); + + mnt_fs_set_source(fs, cn_spec); + mnt_fs_set_target(fs, cn_node); + mnt_fs_set_fstype(fs, type); + mnt_fs_set_options(fs, opts); + + rc = mnt_update_set_fs(mtab_update, flags, NULL, fs); + + if (spec != cn_spec) + my_free(cn_spec); + my_free(cn_node); + } + + mnt_free_fs(fs); + + if (rc) { + mnt_free_update(mtab_update); + mtab_update = NULL; + } +} + +static void update_mtab_entry(int flags) +{ + unsigned long fl; + + if (!mtab_update) + return; + + fl = mnt_update_get_mflags(mtab_update); + + if ((flags & MS_RDONLY) != (fl & MS_RDONLY)) + mnt_update_force_rdonly(mtab_update, flags & MS_RDONLY); + + if (!nomtab) { + if (mtab_does_not_exist()) { + if (verbose > 1) + printf(_("mount: no %s found - creating it..\n"), + _PATH_MOUNTED); + create_mtab (); + } + + mnt_update_table(mtab_update, NULL); + } + + mnt_free_update(mtab_update); + mtab_update = NULL; +} + +#else /*!HAVE_LIBMOUNT_MOUNT */ +static void +update_mtab_entry(const char *spec, const char *node, const char *type, + const char *opts, int flags, int freq, int pass) { + struct my_mntent mnt; + + mnt.mnt_fsname = is_pseudo_fs(type) ? xstrdup(spec) : canonicalize(spec); + mnt.mnt_dir = canonicalize (node); + mnt.mnt_type = type; + mnt.mnt_opts = opts; + mnt.mnt_freq = freq; + mnt.mnt_passno = pass; + + /* We get chatty now rather than after the update to mtab since the + mount succeeded, even if the write to /etc/mtab should fail. */ + if (verbose) + print_one (&mnt); + + if (!nomtab && mtab_does_not_exist()) { + if (verbose > 1) + printf(_("mount: no %s found - creating it..\n"), + _PATH_MOUNTED); + create_mtab (); + + } + + if (!nomtab && mtab_is_writable()) { + if (flags & MS_REMOUNT) + update_mtab (mnt.mnt_dir, &mnt); + else if (flags & MS_MOVE) + update_mtab(mnt.mnt_fsname, &mnt); + else + update_mtab(NULL, &mnt); + } + my_free(mnt.mnt_fsname); + my_free(mnt.mnt_dir); +} +#endif /* !HAVE_LIBMOUNT_MOUNT */ + +static void +set_pfd(char *s) { + if (!isdigit(*s)) + die(EX_USAGE, + _("mount: argument to -p or --pass-fd must be a number")); + pfd = atoi(optarg); +} + +static void +cdrom_setspeed(const char *spec) { +#define CDROM_SELECT_SPEED 0x5322 /* Set the CD-ROM speed */ + if (opt_speed) { + int cdrom; + int speed = atoi(opt_speed); + + if ((cdrom = open(spec, O_RDONLY | O_NONBLOCK)) < 0) + die(EX_FAIL, + _("mount: cannot open %s for setting speed"), + spec); + if (ioctl(cdrom, CDROM_SELECT_SPEED, speed) < 0) + die(EX_FAIL, _("mount: cannot set speed: %m")); + close(cdrom); + } +} + +/* + * Check if @path is on read-only filesystem independently on file permissions. + */ +static int +is_readonly(const char *path) +{ + if (access(path, W_OK) == 0) + return 0; + if (errno == EROFS) + return 1; + if (errno != EACCES) + return 0; + +#ifdef HAVE_FUTIMENS + /* + * access(2) returns EACCES on read-only FS: + * + * - for set-uid application if one component of the path is not + * accessible for the current rUID. (Note that euidaccess(2) does not + * check for EROFS at all). + * + * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind) + */ + { + struct timespec times[2]; + + times[0].tv_nsec = UTIME_NOW; /* atime */ + times[1].tv_nsec = UTIME_OMIT; /* mtime */ + + if (utimensat(AT_FDCWD, path, times, 0) == -1) + return errno == EROFS; + } +#endif + return 0; +} + +/* + * try_mount_one() + * Try to mount one file system. + * + * returns: 0: OK, EX_SYSERR, EX_FAIL, return code from nfsmount, + * return status from wait + */ +static int +try_mount_one (const char *spec0, const char *node0, const char *types0, + const char *opts0, int freq, int pass, int ro) { + int res = 0, status = 0, special = 0; + int mnt5_res = 0; /* only for gcc */ + int mnt_err; + int flags; + char *extra_opts; /* written in mtab */ + char *mount_opts; /* actually used on system call */ + const char *opts, *spec, *node, *types; + char *user = 0; + int loop = 0; + const char *loopdev = 0, *loopfile = 0; + struct stat statbuf; + + /* copies for freeing on exit */ + const char *opts1, *spec1, *node1, *types1; + + if (verbose > 2) { + printf("mount: spec: \"%s\"\n", spec0); + printf("mount: node: \"%s\"\n", node0); + printf("mount: types: \"%s\"\n", types0); + printf("mount: opts: \"%s\"\n", opts0); + } + + spec = spec1 = xstrdup(spec0); + node = node1 = xstrdup(node0); + types = types1 = xstrdup(types0); + opts = opts1 = xstrdup(opts0); + + parse_opts (opts, &flags, &extra_opts); + mount_opts = xstrdup(extra_opts); + + /* quietly succeed for fstab entries that don't get mounted automatically */ + if (mount_all && (flags & MS_NOAUTO)) + goto out; + + restricted_check(spec, node, &flags, &user); + + /* The "mount -f" checks for for existing record in /etc/mtab (with + * regular non-fake mount this is usually done by kernel) + */ + if (!(flags & MS_REMOUNT) && fake && mounted (spec, node, NULL)) + die(EX_USAGE, _("mount: according to mtab, " + "%s is already mounted on %s\n"), + spec, node); + + if (opt_speed) + cdrom_setspeed(spec); + + if (!(flags & MS_REMOUNT)) { + /* + * Don't set up a (new) loop device if we only remount - this left + * stale assignments of files to loop devices. Nasty when used for + * encryption. + */ + res = loop_check(&spec, &types, &flags, &loop, &loopdev, &loopfile, node); + if (res) + goto out; + } + + if (loop) + opt_loopdev = loopdev; + + if (flags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) + types = "none"; + +#ifdef HAVE_LIBSELINUX + if (flags & MS_REMOUNT) { + /* + * Linux kernel does not accept any selinux context option on remount + */ + if (mount_opts) { + char *tmp = mount_opts; + mount_opts = remove_context_options(mount_opts); + my_free(tmp); + } + + } else if (types && strcmp(types, "tmpfs") == 0 && is_selinux_enabled() > 0 && + !has_context_option(mount_opts)) { + /* + * Add rootcontext= mount option for tmpfs + * https://bugzilla.redhat.com/show_bug.cgi?id=476964 + */ + security_context_t sc = NULL; + + if (getfilecon(node, &sc) > 0 && strcmp("unlabeled", sc)) + append_context("rootcontext=", (char *) sc, &mount_opts); + freecon(sc); + } +#endif + + /* + * Call mount.TYPE for types that require a separate mount program. + * For the moment these types are ncpfs and smbfs. Maybe also vxfs. + * All such special things must occur isolated in the types string. + */ + if (check_special_mountprog(spec, node, types, flags, mount_opts, &status)) { + res = status; + goto out; + } + +#ifdef HAVE_LIBMOUNT_MOUNT + mtab_opts = fix_opts_string(flags & ~MS_NOMTAB, extra_opts, user, 0); + mtab_flags = flags; + + if (fake) + prepare_mtab_entry(spec, node, types, mtab_opts, mtab_flags); +#endif + + block_signals (SIG_BLOCK); + + if (!fake) { + mnt5_res = guess_fstype_and_mount (spec, node, &types, flags & ~MS_NOSYS, + mount_opts, &special, &status); + + if (special) { + block_signals (SIG_UNBLOCK); + res = status; + goto out; + } + } + + /* Kernel allows to use MS_RDONLY for bind mounts, but the read-only request + * could be silently ignored. Check it to avoid 'ro' in mtab and 'rw' in + * /proc/mounts. + */ + if (!fake && mnt5_res == 0 && + (flags & MS_BIND) && (flags & MS_RDONLY) && !is_readonly(node)) { + + printf(_("mount: warning: %s seems to be mounted read-write.\n"), node); + flags &= ~MS_RDONLY; + } + + /* Kernel can silently add MS_RDONLY flag when mounting file system that + * does not have write support. Check this to avoid 'ro' in /proc/mounts + * and 'rw' in mtab. + */ + if (!fake && mnt5_res == 0 && + !(flags & (MS_RDONLY | MS_PROPAGATION | MS_MOVE)) && + is_readonly(node)) { + + printf(_("mount: warning: %s seems to be mounted read-only.\n"), node); + flags |= MS_RDONLY; + } + + if (fake || mnt5_res == 0) { + char *mo = fix_opts_string (flags & ~MS_NOMTAB, extra_opts, user, 0); + const char *tp = types ? types : "unknown"; + + /* Mount succeeded, report this (if verbose) and write mtab entry. */ +#ifdef HAVE_LIBMOUNT_MOUNT + update_mtab_entry(flags); + if (verbose) + verbose_mount_info(loop ? loopfile : spec, node, tp, mo); +#else + if (!(mounttype & MS_PROPAGATION)) + update_mtab_entry(loop ? loopfile : spec, + node, + tp, + mo, + flags, + freq, + pass); +#endif + block_signals (SIG_UNBLOCK); + free(mo); + + res = 0; + goto out; + } + + mnt_err = errno; + + if (loop) + loopdev_delete(spec); + + block_signals (SIG_UNBLOCK); + + /* Mount failed, complain, but don't die. */ + + if (types == 0) { + if (restricted) + error (_("mount: I could not determine the filesystem type, " + "and none was specified")); + else + error (_("mount: you must specify the filesystem type")); + } else if (mnt5_res != -1) { + /* should not happen */ + error (_("mount: mount failed")); + } else { + switch (mnt_err) { + case EPERM: + if (geteuid() == 0) { + 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: + if (flags & MS_REMOUNT) { + error (_("mount: %s is busy"), node); + } else if (!strcmp(types, "proc") && !strcmp(node, "/proc")) { + /* heuristic: if /proc/version exists, then probably proc is mounted */ + if (stat ("/proc/version", &statbuf)) /* proc mounted? */ + error (_("mount: %s is busy"), node); /* no */ + else if (!mount_all || verbose) /* yes, don't mention it */ + error (_("mount: proc already mounted")); + } else { + error (_("mount: %s already mounted or %s busy"), spec, node); + already (spec, node); + } + break; + case ENOENT: + if (lstat (node, &statbuf)) + error (_("mount: mount point %s does not exist"), node); + else if (stat (node, &statbuf)) + error (_("mount: mount point %s is a symbolic link to nowhere"), + node); + else if (stat (spec, &statbuf)) { + if (opt_nofail) + goto out; + error (_("mount: special device %s does not exist"), spec); + } else { + errno = mnt_err; + perror("mount"); + } + break; + case ENOTDIR: + if (stat (node, &statbuf) || ! S_ISDIR(statbuf.st_mode)) + error (_("mount: mount point %s is not a directory"), node); + else if (stat (spec, &statbuf) && errno == ENOTDIR) { + if (opt_nofail) + goto out; + error (_("mount: special device %s does not exist\n" + " (a path prefix is not a directory)\n"), spec); + } else { + errno = mnt_err; + perror("mount"); + } + break; + case EINVAL: + { int fd; + unsigned long long size = 0; + + if (flags & MS_REMOUNT) { + error (_("mount: %s not mounted or bad option"), node); + } else { + error (_("mount: wrong fs type, bad option, bad superblock on %s,\n" + " missing codepage or helper program, or other error"), + spec); + + if (stat(spec, &statbuf) < 0) { + if (errno == ENOENT) /* network FS? */ + error(_( + " (for several filesystems (e.g. nfs, cifs) you might\n" + " need a /sbin/mount.<type> helper program)")); + + } else if (S_ISBLK(statbuf.st_mode) + && (fd = open(spec, O_RDONLY | O_NONBLOCK)) >= 0) { + + if (blkdev_get_size(fd, &size) == 0) { + if (size == 0 && !loop) + error(_( + " (could this be the IDE device where you in fact use\n" + " ide-scsi so that sr0 or sda or so is needed?)")); + + if (size && size <= 2) + error(_( + " (aren't you trying to mount an extended partition,\n" + " instead of some logical partition inside?)")); + + close(fd); + } + } + error(_( + " In some cases useful info is found in syslog - try\n" + " dmesg | tail or so\n")); + } + break; + } + case EMFILE: + error (_("mount table full")); break; + case EIO: + error (_("mount: %s: can't read superblock"), spec); break; + case ENODEV: + { + int pfs = known_fstype_in_procfs(types); + + if (pfs == 1 || !strcmp(types, "guess")) + error(_("mount: %s: unknown device"), spec); + else if (pfs == 0) { + char *lowtype, *p; + int u; + + error (_("mount: unknown filesystem type '%s'"), types); + + /* maybe this loser asked for FAT or ISO9660 or isofs */ + lowtype = xstrdup(types); + u = 0; + for(p=lowtype; *p; p++) { + if(tolower(*p) != *p) { + *p = tolower(*p); + u++; + } + } + if (u && known_fstype_in_procfs(lowtype) == 1) + error (_("mount: probably you meant %s"), lowtype); + else if (!strncmp(lowtype, "iso", 3) && + known_fstype_in_procfs("iso9660") == 1) + error (_("mount: maybe you meant 'iso9660'?")); + else if (!strncmp(lowtype, "fat", 3) && + known_fstype_in_procfs("vfat") == 1) + error (_("mount: maybe you meant 'vfat'?")); + free(lowtype); + } else + error (_("mount: %s has wrong device number or fs type %s not supported"), + spec, types); + break; + } + case ENOTBLK: + if (opt_nofail) + goto out; + 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 `modprobe 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: + if (opt_nofail) + goto out; + error (_("mount: %s is not a valid block device"), spec); break; + case EACCES: /* pre-linux 1.1.38, 1.1.41 and later */ + case EROFS: /* linux 1.1.38 and later */ + { char *bd = (loop ? "" : _("block device ")); + if (ro || (flags & MS_RDONLY)) { + error (_("mount: cannot mount %s%s read-only"), + bd, spec); + break; + } else if (readwrite) { + error (_("mount: %s%s is write-protected but explicit `-w' flag given"), + bd, spec); + break; + } else if (flags & MS_REMOUNT) { + error (_("mount: cannot remount %s%s read-write, is write-protected"), + bd, spec); + break; + } else { + opts = opts0; + types = types0; + + if (opts) { + char *opts2 = append_opt(xstrdup(opts), "ro", NULL); + my_free(opts1); + opts = opts1 = opts2; + } else + opts = "ro"; + if (types && !strcmp(types, "guess")) + types = 0; + error (_("mount: %s%s is write-protected, mounting read-only"), + bd, spec0); + res = try_mount_one (spec0, node0, types, opts, freq, pass, 1); + goto out; + } + break; + } + case ENOMEDIUM: + error(_("mount: no medium found on %s"), spec); + break; + default: + error ("mount: %s", strerror (mnt_err)); break; + } + } + res = EX_FAIL; + + out: + +#if defined(HAVE_LIBSELINUX) && defined(HAVE_SECURITY_GET_INITIAL_CONTEXT) + if (res != EX_FAIL && verbose && is_selinux_enabled() > 0) { + security_context_t raw = NULL, def = NULL; + + if (getfilecon(node, &raw) > 0 && + security_get_initial_context("file", &def) == 0) { + + if (!selinux_file_context_cmp(raw, def)) + printf(_("mount: %s does not contain SELinux labels.\n" + " You just mounted an file system that supports labels which does not\n" + " contain labels, onto an SELinux box. It is likely that confined\n" + " applications will generate AVC messages and not be allowed access to\n" + " this file system. For more details see restorecon(8) and mount(8).\n"), + node); + } + freecon(raw); + freecon(def); + } +#endif + + my_free(mount_opts); + my_free(extra_opts); + my_free(spec1); + my_free(node1); + my_free(opts1); + my_free(types1); + + return res; +} + +static char * +subst_string(const char *s, const char *sub, int sublen, const char *repl) { + char *n; + + n = (char *) xmalloc(strlen(s)-sublen+strlen(repl)+1); + strncpy (n, s, sub-s); + strcpy (n + (sub-s), repl); + strcat (n, sub+sublen); + return n; +} + +static char * +usersubst(const char *opts) { + char *s, *w; + char id[40]; + + if (!opts) + return NULL; + + s = "uid=useruid"; + if (opts && (w = strstr(opts, s)) != NULL) { + sprintf(id, "uid=%u", getuid()); + opts = subst_string(opts, w, strlen(s), id); + } + s = "gid=usergid"; + if (opts && (w = strstr(opts, s)) != NULL) { + sprintf(id, "gid=%u", getgid()); + opts = subst_string(opts, w, strlen(s), id); + } + return xstrdup(opts); +} + +static int +is_existing_file (const char *s) { + struct stat statbuf; + + return (stat(s, &statbuf) == 0); +} + +/* + * Return 0 for success (either mounted sth or -a and NOAUTO was given) + */ +static int +mount_one (const char *spec, const char *node, const char *types, + const char *fstabopts, char *cmdlineopts, int freq, int pass) { + const char *nspec = NULL; + char *opts; + + /* Substitute values in opts, if required */ + opts = usersubst(fstabopts); + + /* Merge the fstab and command line options. */ + opts = append_opt(opts, cmdlineopts, NULL); + + if (types == NULL && !mounttype && !is_existing_file(spec)) { + if (strchr (spec, ':') != NULL) { + types = "nfs"; + if (verbose) + printf(_("mount: no type was given - " + "I'll assume nfs because of " + "the colon\n")); + } else if(!strncmp(spec, "//", 2)) { + types = "cifs"; + if (verbose) + printf(_("mount: no type was given - " + "I'll assume cifs because of " + "the // prefix\n")); + } + } + + /* Handle possible LABEL= and UUID= forms of spec */ + if (types == NULL || (strncmp(types, "9p", 2) && + strncmp(types, "nfs", 3) && + strncmp(types, "cifs", 4) && + strncmp(types, "smbfs", 5))) { + if (!is_pseudo_fs(types)) + nspec = spec_to_devname(spec); + if (nspec) + spec = nspec; + } + + return try_mount_one (spec, node, types, opts, freq, pass, 0); +} + +#ifdef HAVE_LIBMOUNT_MOUNT +static struct libmnt_table *minfo; /* parsed mountinfo file */ + +/* Check if an fsname/dir pair was already in the old mtab. */ +static int +mounted (const char *spec0, const char *node0, struct mntentchn *fstab_mc) { +#else +static int +mounted (const char *spec0, const char *node0, + struct mntentchn *fstab_mc __attribute__((__unused__))) { +#endif + struct mntentchn *mc, *mc0; + const char *spec, *node; + int ret = 0; + +#ifdef HAVE_LIBMOUNT_MOUNT + /* + * Use libmount to check for already mounted bind mounts on systems + * without mtab. + */ + if (fstab_mc && fstab_mc->m.mnt_opts && + mtab_is_a_symlink() && strstr(fstab_mc->m.mnt_opts, "bind")) { + + struct libmnt_fs *fs = mnt_new_fs(); + int rc = fs ? 0 : -1; + + if (!rc) + rc = mnt_fs_set_fstype(fs, fstab_mc->m.mnt_type); + if (!rc) + rc = mnt_fs_set_source(fs, fstab_mc->m.mnt_fsname); + if (!rc) + rc = mnt_fs_set_target(fs, fstab_mc->m.mnt_dir); + if (!rc) + rc = mnt_fs_set_options(fs, fstab_mc->m.mnt_opts); + if (!rc && !minfo) + minfo = mnt_new_table_from_file("/proc/self/mountinfo"); + if (!rc && minfo) + rc = mnt_table_is_fs_mounted(minfo, fs); + + mnt_free_fs(fs); + if (rc == 1) + return 1; + } +#endif + /* Handle possible UUID= and LABEL= in spec */ + spec = spec_to_devname(spec0); + if (!spec) + return ret; + + node = canonicalize(node0); + + + mc0 = mtab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) + if (streq (spec, mc->m.mnt_fsname) && + streq (node, mc->m.mnt_dir)) { + ret = 1; + break; + } + + my_free(spec); + my_free(node); + + return ret; +} + +/* returns 0 if not mounted, 1 if mounted and -1 in case of error */ +static int +is_fstab_entry_mounted(struct mntentchn *mc, int verbose) +{ + struct stat st; + + if (mounted(mc->m.mnt_fsname, mc->m.mnt_dir, mc)) + goto yes; + + /* extra care for loop devices */ + if ((mc->m.mnt_opts && strstr(mc->m.mnt_opts, "loop=")) || + (stat(mc->m.mnt_fsname, &st) == 0 && S_ISREG(st.st_mode))) { + + char *p = get_option_value(mc->m.mnt_opts, "offset="); + uintmax_t offset = 0; + + if (p && strtosize(p, &offset) != 0) { + if (verbose) + printf(_("mount: ignore %s " + "(unparsable offset= option)\n"), + mc->m.mnt_fsname); + return -1; + } + free(p); + if (is_mounted_same_loopfile(mc->m.mnt_dir, mc->m.mnt_fsname, offset)) + goto yes; + } + + return 0; +yes: + if (verbose) + printf(_("mount: %s already mounted on %s\n"), + mc->m.mnt_fsname, mc->m.mnt_dir); + return 1; +} + +/* avoid using stat() on things we are not going to mount anyway.. */ +static int +has_noauto (const char *opts) { + char *s; + + if (!opts) + return 0; + s = strstr(opts, "noauto"); + if (!s) + return 0; + return (s == opts || s[-1] == ',') && (s[6] == 0 || s[6] == ','); +} + +/* Mount all filesystems of the specified types except swap and root. */ +/* With the --fork option: fork and let different incarnations of + mount handle different filesystems. However, try to avoid several + simultaneous mounts on the same physical disk, since that is very slow. */ +#define DISKMAJOR(m) (((int) m) & ~0xf) + +static int +do_mount_all (char *types, char *options, char *test_opts) { + struct mntentchn *mc, *mc0, *mtmp; + int status = 0; + struct stat statbuf; + struct child { + pid_t pid; + char *group; + struct mntentchn *mec; + struct mntentchn *meclast; + struct child *nxt; + } childhead, *childtail, *cp; + char major[22]; + char *g, *colon; + + /* build a chain of what we have to do, or maybe + several chains, one for each major or NFS host */ + childhead.nxt = 0; + childtail = &childhead; + mc0 = fstab_head(); + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + if (has_noauto (mc->m.mnt_opts)) + continue; + if (matching_type (mc->m.mnt_type, types) + && matching_opts (mc->m.mnt_opts, test_opts) + && !streq (mc->m.mnt_dir, "/") + && !streq (mc->m.mnt_dir, "root") + && !is_fstab_entry_mounted(mc, verbose)) { + + mtmp = (struct mntentchn *) xmalloc(sizeof(*mtmp)); + *mtmp = *mc; + mtmp->nxt = 0; + g = NULL; + if (optfork) { + if (stat(mc->m.mnt_fsname, &statbuf) == 0 && + S_ISBLK(statbuf.st_mode)) { + sprintf(major, "#%x", + DISKMAJOR(statbuf.st_rdev)); + g = major; + } + if (strcmp(mc->m.mnt_type, "nfs") == 0) { + g = xstrdup(mc->m.mnt_fsname); + colon = strchr(g, ':'); + if (colon) + *colon = '\0'; + } + } + if (g) { + for (cp = childhead.nxt; cp; cp = cp->nxt) + if (cp->group && + strcmp(cp->group, g) == 0) { + cp->meclast->nxt = mtmp; + cp->meclast = mtmp; + goto fnd; + } + } + cp = (struct child *) xmalloc(sizeof *cp); + cp->nxt = 0; + cp->mec = cp->meclast = mtmp; + cp->group = xstrdup(g); + cp->pid = 0; + childtail->nxt = cp; + childtail = cp; + fnd:; + + } + } + + /* now do everything */ + for (cp = childhead.nxt; cp; cp = cp->nxt) { + pid_t p = -1; + if (optfork) { + p = fork(); + if (p == -1) { + int errsv = errno; + error(_("mount: cannot fork: %s"), + strerror (errsv)); + } + else if (p != 0) + cp->pid = p; + } + + /* if child, or not forked, do the mounting */ + if (p == 0 || p == -1) { + for (mc = cp->mec; mc; mc = mc->nxt) { + status |= mount_one (mc->m.mnt_fsname, + mc->m.mnt_dir, + mc->m.mnt_type, + mc->m.mnt_opts, + options, 0, 0); + } + if (mountcount) + status |= EX_SOMEOK; + if (p == 0) + exit(status); + } + } + + /* 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; +} + +static struct option longopts[] = { + { "all", 0, 0, 'a' }, + { "fake", 0, 0, 'f' }, + { "fork", 0, 0, 'F' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "read-only", 0, 0, 'r' }, + { "ro", 0, 0, 'r' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-write", 0, 0, 'w' }, + { "rw", 0, 0, 'w' }, + { "options", 1, 0, 'o' }, + { "test-opts", 1, 0, 'O' }, + { "pass-fd", 1, 0, 'p' }, + { "types", 1, 0, 't' }, + { "bind", 0, 0, 'B' }, + { "move", 0, 0, 'M' }, + { "guess-fstype", 1, 0, 134 }, + { "rbind", 0, 0, 'R' }, + { "make-shared", 0, 0, 136 }, + { "make-slave", 0, 0, 137 }, + { "make-private", 0, 0, 138 }, + { "make-unbindable", 0, 0, 139 }, + { "make-rshared", 0, 0, 140 }, + { "make-rslave", 0, 0, 141 }, + { "make-rprivate", 0, 0, 142 }, + { "make-runbindable", 0, 0, 143 }, + { "no-canonicalize", 0, 0, 144 }, + { "internal-only", 0, 0, 'i' }, + { NULL, 0, 0, 0 } +}; + +/* Keep the usage message at max 22 lines, each at most 70 chars long. + The user should not need a pager to read it. */ +static void +usage (FILE *fp, int n) { + fprintf(fp, _( + "Usage: mount -V : print version\n" + " mount -h : print this help\n" + " mount : list mounted filesystems\n" + " mount -l : idem, including volume labels\n" + "So far the informational part. Next the mounting.\n" + "The command is `mount [-t fstype] something somewhere'.\n" + "Details found in /etc/fstab may be omitted.\n" + " mount -a [-t|-O] ... : mount all stuff from /etc/fstab\n" + " mount device : mount device at the known place\n" + " mount directory : mount known device here\n" + " mount -t type dev dir : ordinary mount command\n" + "Note that one does not really mount a device, one mounts\n" + "a filesystem (of the given type) found on the device.\n" + "One can also mount an already visible directory tree elsewhere:\n" + " mount --bind olddir newdir\n" + "or move a subtree:\n" + " mount --move olddir newdir\n" + "One can change the type of mount containing the directory dir:\n" + " mount --make-shared dir\n" + " mount --make-slave dir\n" + " mount --make-private dir\n" + " mount --make-unbindable dir\n" + "One can change the type of all the mounts in a mount subtree\n" + "containing the directory dir:\n" + " mount --make-rshared dir\n" + " mount --make-rslave dir\n" + " mount --make-rprivate dir\n" + " mount --make-runbindable dir\n" + "A device can be given by name, say /dev/hda1 or /dev/cdrom,\n" + "or by label, using -L label or by uuid, using -U uuid .\n" + "Other options: [-nfFrsvw] [-o options] [-p passwdfd].\n" + "For many more details, say man 8 mount .\n" + )); + + unlock_mtab(); + exit (n); +} + +/* returns mount entry from fstab */ +static struct mntentchn * +getfs(const char *spec, const char *uuid, const char *label) +{ + struct mntentchn *mc = NULL; + const char *devname = NULL; + + if (!spec && !uuid && !label) + return NULL; + + /* + * A) 99% of all cases, the spec on cmdline matches + * with spec in fstab + */ + if (uuid) + mc = getfs_by_uuid(uuid); + else if (label) + mc = getfs_by_label(label); + else { + mc = getfs_by_dir(spec); + + if (!mc) + mc = getfs_by_spec(spec); + } + if (mc) + return mc; + + /* + * B) UUID or LABEL on cmdline, but devname in fstab + */ + if (uuid) + devname = fsprobe_get_devname_by_uuid(uuid); + else if (label) + devname = fsprobe_get_devname_by_label(label); + else + devname = spec_to_devname(spec); + + if (devname) + mc = getfs_by_devname(devname); + + /* + * C) mixed + */ + if (!mc && devname) { + const char *id = NULL; + + if (!label && (!spec || strncmp(spec, "LABEL=", 6))) { + id = fsprobe_get_label_by_devname(devname); + if (id) + mc = getfs_by_label(id); + } + if (!mc && !uuid && (!spec || strncmp(spec, "UUID=", 5))) { + id = fsprobe_get_uuid_by_devname(devname); + if (id) + mc = getfs_by_uuid(id); + } + my_free(id); + + if (mc) { + /* use real device name to avoid repetitional + * conversion from LABEL/UUID to devname + */ + my_free(mc->m.mnt_fsname); + mc->m.mnt_fsname = xstrdup(devname); + } + } + + /* + * D) remount -- try /etc/mtab + * Earlier mtab was tried first, but this would sometimes try the + * wrong mount in case mtab had the root device entry wrong. Try + * the last occurrence first, since that is the visible mount. + */ + if (!mc && (devname || spec)) + mc = getmntfilebackward (devname ? devname : spec, NULL); + + my_free(devname); + return mc; +} + + +static void +print_version(int rc) { + printf( "mount from %s (with " +#ifdef HAVE_LIBBLKID + "libblkid" +#else + "libvolume_id" +#endif +#ifdef HAVE_LIBSELINUX + " and selinux" +#endif + " support)\n", PACKAGE_STRING); + exit(rc); +} + +int +main(int argc, char *argv[]) { + int c, result = 0, specseen; + char *options = NULL, *test_opts = NULL, *node; + const char *spec = NULL; + char *label = NULL; + char *uuid = NULL; + char *types = NULL; + char *p; + struct mntentchn *mc; + int fd; + + sanitize_env(); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + progname = argv[0]; + if ((p = strrchr(progname, '/')) != NULL) + progname = p+1; + + umask(022); + + /* People report that a mount called from init without console + writes error messages to /etc/mtab + Let us try to avoid getting fd's 0,1,2 */ + while((fd = open("/dev/null", O_RDWR)) == 0 || fd == 1 || fd == 2) ; + if (fd > 2) + close(fd); + + fsprobe_init(); + +#ifdef DO_PS_FIDDLING + initproctitle(argc, argv); +#endif + + while ((c = getopt_long (argc, argv, "aBfFhilL:Mno:O:p:rRsU:vVwt:", + longopts, NULL)) != -1) { + switch (c) { + case 'a': /* mount everything in fstab */ + ++mount_all; + break; + case 'B': /* bind */ + mounttype = MS_BIND; + break; + case 'f': /* fake: don't actually call mount(2) */ + ++fake; + break; + case 'F': + ++optfork; + break; + case 'h': /* help */ + usage (stdout, 0); + break; + case 'i': + external_allowed = 0; + break; + case 'l': + list_with_volumelabel = 1; + break; + case 'L': + label = optarg; + break; + case 'M': /* move */ + mounttype = MS_MOVE; + break; + case 'n': /* do not write /etc/mtab */ + ++nomtab; + break; + case 'o': /* specify mount options */ + options = append_opt(options, optarg, NULL); + break; + case 'O': /* with -t: mount only if (not) opt */ + test_opts = append_opt(test_opts, optarg, NULL); + break; + case 'p': /* fd on which to read passwd */ + set_pfd(optarg); + break; + case 'r': /* mount readonly */ + readonly = 1; + readwrite = 0; + break; + case 'R': /* rbind */ + mounttype = (MS_BIND | MS_REC); + break; + case 's': /* allow sloppy mount options */ + sloppy = 1; + break; + case 't': /* specify file system types */ + types = optarg; + break; + case 'U': + uuid = optarg; + break; + case 'v': /* be chatty - more so if repeated */ + ++verbose; + break; + case 'V': /* version */ + print_version(EXIT_SUCCESS); + break; + case 'w': /* mount read/write */ + readwrite = 1; + readonly = 0; + break; + case 0: + break; + + case 134: + /* undocumented, may go away again: + call: mount --guess-fstype device + use only for testing purposes - + the guessing is not reliable at all */ + { + const char *fstype; + fstype = fsprobe_get_fstype_by_devname(optarg); + printf("%s\n", fstype ? fstype : "unknown"); + exit(fstype ? 0 : EX_FAIL); + } + + case 136: + mounttype = MS_SHARED; + break; + + case 137: + mounttype = MS_SLAVE; + break; + + case 138: + mounttype = MS_PRIVATE; + break; + + case 139: + mounttype = MS_UNBINDABLE; + break; + + case 140: + mounttype = (MS_SHARED | MS_REC); + break; + + case 141: + mounttype = (MS_SLAVE | MS_REC); + break; + + case 142: + mounttype = (MS_PRIVATE | MS_REC); + break; + + case 143: + mounttype = (MS_UNBINDABLE | MS_REC); + break; + case 144: + nocanonicalize = 1; + break; + case '?': + default: + usage (stderr, EX_USAGE); + } + } + + if (verbose > 2) { + printf("mount: fstab path: \"%s\"\n", _PATH_MNTTAB); + printf("mount: mtab path: \"%s\"\n", _PATH_MOUNTED); + printf("mount: lock path: \"%s\"\n", _PATH_MOUNTED_LOCK); + printf("mount: temp path: \"%s\"\n", _PATH_MOUNTED_TMP); + printf("mount: UID: %u\n", getuid()); + printf("mount: eUID: %u\n", geteuid()); + } + +#ifdef HAVE_LIBMOUNT_MOUNT + mnt_init_debug(0); +#endif + argc -= optind; + argv += optind; + + specseen = (uuid || label) ? 1 : 0; /* yes, .. i know */ + + if (argc+specseen == 0 && !mount_all) { + if (options || mounttype) + usage (stderr, EX_USAGE); + return print_all (types); + } + + { + const uid_t ruid = getuid(); + const uid_t euid = geteuid(); + + /* if we're really root and aren't running setuid */ + if (((uid_t)0 == ruid) && (ruid == euid)) { + restricted = 0; + } + + if (restricted && + (types || options || readwrite || nomtab || mount_all || + nocanonicalize || fake || mounttype || + (argc + specseen) != 1)) { + + if (ruid == 0 && euid != 0) + /* user is root, but setuid to non-root */ + die (EX_USAGE, _("mount: only root can do that " + "(effective UID is %u)"), euid); + + die (EX_USAGE, _("mount: only root can do that")); + } + } + + atexit(unlock_mtab); + + switch (argc+specseen) { + case 0: + /* mount -a */ + result = do_mount_all (types, options, test_opts); + if (result == 0 && verbose && !fake) + error(_("nothing was mounted")); + break; + + case 1: + /* mount [-nfrvw] [-o options] special | node + * mount -L label (or -U uuid) + * (/etc/fstab is necessary) + */ + if (types != NULL) + usage (stderr, EX_USAGE); + + if (uuid || label) + mc = getfs(NULL, uuid, label); + else + mc = getfs(*argv, NULL, NULL); + + if (!mc) { + if (uuid || label) + die (EX_USAGE, _("mount: no such partition found")); + + die (EX_USAGE, + _("mount: can't find %s in %s or %s"), + *argv, _PATH_MNTTAB, _PATH_MOUNTED); + } + + result = mount_one (xstrdup (mc->m.mnt_fsname), + xstrdup (mc->m.mnt_dir), + xstrdup (mc->m.mnt_type), + mc->m.mnt_opts, options, 0, 0); + break; + + case 2: + /* mount special node (/etc/fstab is not necessary) */ + if (specseen) { + /* mount -L label node (or -U uuid) */ + spec = uuid ? fsprobe_get_devname_by_uuid(uuid) : + fsprobe_get_devname_by_label(label); + node = argv[0]; + } else { + /* mount special node */ + spec = argv[0]; + node = argv[1]; + } + if (!spec) + die (EX_USAGE, _("mount: no such partition found")); + + result = mount_one (spec, node, types, NULL, options, 0, 0); + break; + + default: + usage (stderr, EX_USAGE); + } + + if (result == EX_SOMEOK) + result = 0; + + fsprobe_exit(); + + exit (result); +} diff --git a/mount-deprecated/mount_constants.h b/mount-deprecated/mount_constants.h new file mode 100644 index 000000000..ce51887dd --- /dev/null +++ b/mount-deprecated/mount_constants.h @@ -0,0 +1,73 @@ +#ifndef MS_RDONLY +#define MS_RDONLY 1 /* Mount read-only */ +#endif +#ifndef MS_NOSUID +#define MS_NOSUID 2 /* Ignore suid and sgid bits */ +#endif +#ifndef MS_NODEV +#define MS_NODEV 4 /* Disallow access to device special files */ +#endif +#ifndef MS_NOEXEC +#define MS_NOEXEC 8 /* Disallow program execution */ +#endif +#ifndef MS_SYNCHRONOUS +#define MS_SYNCHRONOUS 16 /* Writes are synced at once */ +#endif +#ifndef MS_REMOUNT +#define MS_REMOUNT 32 /* Alter flags of a mounted FS */ +#endif +#ifndef MS_MANDLOCK +#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */ +#endif +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 /* Directory modifications are synchronous */ +#endif +#ifndef MS_NOATIME +#define MS_NOATIME 0x400 /* 1024: Do not update access times. */ +#endif +#ifndef MS_NODIRATIME +#define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */ +#endif +#ifndef MS_BIND +#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */ +#endif +#ifndef MS_MOVE +#define MS_MOVE 0x2000 /* 8192: Atomically move tree */ +#endif +#ifndef MS_REC +#define MS_REC 0x4000 /* 16384: Recursive loopback */ +#endif +#ifndef MS_SILENT +#define MS_SILENT 0x8000 /* 32768 (was MS_VERBOSE) */ +#endif +#ifndef MS_RELATIME +#define MS_RELATIME 0x200000 /* 200000: Update access times relative + to mtime/ctime */ +#endif +#ifndef MS_UNBINDABLE +#define MS_UNBINDABLE (1<<17) /* 131072 unbindable*/ +#endif +#ifndef MS_PRIVATE +#define MS_PRIVATE (1<<18) /* 262144 Private*/ +#endif +#ifndef MS_SLAVE +#define MS_SLAVE (1<<19) /* 524288 Slave*/ +#endif +#ifndef MS_SHARED +#define MS_SHARED (1<<20) /* 1048576 Shared*/ +#endif +#ifndef MS_I_VERSION +#define MS_I_VERSION (1<<23) /* update inode I_version field */ +#endif +#ifndef MS_STRICTATIME +#define MS_STRICTATIME (1<<24) /* strict atime semantics */ +#endif +/* + * Magic mount flag number. Had to be or-ed to the flag values. + */ +#ifndef MS_MGC_VAL +#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */ +#endif +#ifndef MS_MGC_MSK +#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ +#endif diff --git a/mount-deprecated/mount_mntent.c b/mount-deprecated/mount_mntent.c new file mode 100644 index 000000000..88dab1025 --- /dev/null +++ b/mount-deprecated/mount_mntent.c @@ -0,0 +1,165 @@ +/* Private version of the libc *mntent() routines. */ +/* Note slightly different prototypes. */ + +/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> + * - added Native Language Support + */ + +#include <stdio.h> +#include <string.h> /* for index */ +#include <ctype.h> /* for isdigit */ +#include <sys/stat.h> /* for umask */ + +#include "mount_mntent.h" +#include "sundries.h" /* for xmalloc */ +#include "nls.h" +#include "mangle.h" +#include "closestream.h" + +static int +is_space_or_tab (char c) { + return (c == ' ' || c == '\t'); +} + +static char * +skip_spaces(char *s) { + while (is_space_or_tab(*s)) + s++; + return s; +} + +/* + * fstat'ing the file and allocating a buffer holding all of it + * may be a bad idea: if the file is /proc/mounts, the stat + * returns 0. + * (On the other hand, mangling and unmangling is meaningless + * for /proc/mounts.) + */ + +mntFILE * +my_setmntent (const char *file, char *mode) { + mntFILE *mfp = xmalloc(sizeof(*mfp)); + mode_t old_umask = umask(077); + + mfp->mntent_fp = fopen(file, mode); + umask(old_umask); + mfp->mntent_file = xstrdup(file); + mfp->mntent_errs = (mfp->mntent_fp == NULL); + mfp->mntent_softerrs = 0; + mfp->mntent_lineno = 0; + return mfp; +} + +void +my_endmntent(mntFILE * mfp) { + if (mfp) { + if (mfp->mntent_fp) + if (close_stream(mfp->mntent_fp)) + fprintf(stderr, _("write error")); + free(mfp->mntent_file); + free(mfp); + } +} + +int +my_addmntent (mntFILE *mfp, struct my_mntent *mnt) { + char *m1, *m2, *m3, *m4; + int res; + + if (fseek (mfp->mntent_fp, 0, SEEK_END)) + return 1; /* failure */ + + m1 = mangle(mnt->mnt_fsname); + m2 = mangle(mnt->mnt_dir); + m3 = mangle(mnt->mnt_type); + m4 = mnt->mnt_opts ? mangle(mnt->mnt_opts) : "rw"; + + res = fprintf (mfp->mntent_fp, "%s %s %s %s %d %d\n", + m1, m2, m3, m4, mnt->mnt_freq, mnt->mnt_passno); + + free(m1); + free(m2); + free(m3); + if (mnt->mnt_opts) + free(m4); + return (res < 0) ? 1 : 0; +} + +/* Read the next entry from the file fp. Stop reading at an incorrect entry. */ +struct my_mntent * +my_getmntent (mntFILE *mfp) { + static char buf[4096]; + static struct my_mntent me; + char *s; + + again: + if (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX) + return NULL; + + /* read the next non-blank non-comment line */ + do { + if (fgets (buf, sizeof(buf), mfp->mntent_fp) == NULL) + return NULL; + + mfp->mntent_lineno++; + s = strchr (buf, '\n'); + if (s == NULL) { + /* Missing final newline? Otherwise extremely */ + /* long line - assume file was corrupted */ + if (feof(mfp->mntent_fp)) { + fprintf(stderr, _("[mntent]: warning: no final " + "newline at the end of %s\n"), + mfp->mntent_file); + s = strchr (buf, 0); + } else { + mfp->mntent_errs = 1; + goto err; + } + } + *s = 0; + if (--s >= buf && *s == '\r') + *s = 0; + s = skip_spaces(buf); + } while (*s == '\0' || *s == '#'); + + me.mnt_fsname = unmangle(s, &s); + s = skip_spaces(s); + me.mnt_dir = unmangle(s, &s); + s = skip_spaces(s); + me.mnt_type = unmangle(s, &s); + s = skip_spaces(s); + me.mnt_opts = unmangle(s, &s); + s = skip_spaces(s); + + if (!me.mnt_fsname || !me.mnt_dir || !me.mnt_type) + goto err; + + if (isdigit(*s)) { + me.mnt_freq = atoi(s); + while(isdigit(*s)) s++; + } else + me.mnt_freq = 0; + if(*s && !is_space_or_tab(*s)) + goto err; + + s = skip_spaces(s); + if(isdigit(*s)) { + me.mnt_passno = atoi(s); + while(isdigit(*s)) s++; + } else + me.mnt_passno = 0; + if(*s && !is_space_or_tab(*s)) + goto err; + + /* allow more stuff, e.g. comments, on this line */ + + return &me; + + err: + mfp->mntent_softerrs++; + fprintf(stderr, _("[mntent]: line %d in %s is bad%s\n"), + mfp->mntent_lineno, mfp->mntent_file, + (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX) ? + _("; rest of file ignored") : ""); + goto again; +} diff --git a/mount-deprecated/mount_mntent.h b/mount-deprecated/mount_mntent.h new file mode 100644 index 000000000..3b7421535 --- /dev/null +++ b/mount-deprecated/mount_mntent.h @@ -0,0 +1,28 @@ +#ifndef MOUNT_MNTENT_H +#define MOUNT_MNTENT_H + +struct my_mntent { + const char *mnt_fsname; + const char *mnt_dir; + const char *mnt_type; + const char *mnt_opts; + int mnt_freq; + int mnt_passno; +}; + +#define ERR_MAX 5 + +typedef struct mntFILEstruct { + FILE *mntent_fp; + char *mntent_file; + int mntent_lineno; + int mntent_errs; + int mntent_softerrs; +} mntFILE; + +mntFILE *my_setmntent (const char *file, char *mode); +void my_endmntent (mntFILE *mfp); +int my_addmntent (mntFILE *mfp, struct my_mntent *mnt); +struct my_mntent *my_getmntent (mntFILE *mfp); + +#endif /* MOUNT_MNTENT_H */ diff --git a/mount-deprecated/sundries.c b/mount-deprecated/sundries.c new file mode 100644 index 000000000..9bff99753 --- /dev/null +++ b/mount-deprecated/sundries.c @@ -0,0 +1,300 @@ +/* + * Support functions. Exported functions are prototyped in sundries.h. + * + * added fcntl locking by Kjetil T. (kjetilho@math.uio.no) - aeb, 950927 + * + * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> + * - added Native Language Support + * + */ +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <mntent.h> /* for MNTTYPE_SWAP */ + +#include "canonicalize.h" + +#include "sundries.h" +#include "nls.h" + +int mount_quiet; +int verbose; +int nocanonicalize; +char *progname; + +char * +xstrndup (const char *s, int n) { + char *t; + + if (s == NULL) + die (EX_SOFTWARE, _("bug in xstrndup call")); + + t = xmalloc(n+1); + strncpy(t,s,n); + t[n] = 0; + + return t; +} + +/* reallocates its first arg - typical use: s = xstrconcat3(s,t,u); */ +char * +xstrconcat3 (char *s, const char *t, const char *u) { + size_t len = 0; + + len = (s ? strlen(s) : 0) + (t ? strlen(t) : 0) + (u ? strlen(u) : 0); + + if (!len) + return NULL; + if (!s) { + s = xmalloc(len + 1); + *s = '\0'; + } + else + s = xrealloc(s, len + 1); + if (t) + strcat(s, t); + if (u) + strcat(s, u); + return s; +} + +/* frees its first arg - typical use: s = xstrconcat4(s,t,u,v); */ +char * +xstrconcat4 (char *s, const char *t, const char *u, const char *v) { + size_t len = 0; + + len = (s ? strlen(s) : 0) + (t ? strlen(t) : 0) + + (u ? strlen(u) : 0) + (v ? strlen(v) : 0); + + if (!len) + return NULL; + if (!s) { + s = xmalloc(len + 1); + *s = '\0'; + } + else + s = xrealloc(s, len + 1); + if (t) + strcat(s, t); + if (u) + strcat(s, u); + if (v) + strcat(s, v); + return s; + + +} + +/* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock. */ +void +block_signals (int how) { + sigset_t sigs; + + sigfillset (&sigs); + sigdelset(&sigs, SIGTRAP); + sigdelset(&sigs, SIGSEGV); + sigprocmask (how, &sigs, (sigset_t *) 0); +} + + +/* Non-fatal error. Print message and return. */ +/* (print the message in a single printf, in an attempt + to avoid mixing output of several threads) */ +void +error (const char *fmt, ...) { + va_list args; + + if (mount_quiet) + return; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fputc('\n', stderr); +} + +/* Fatal error. Print message and exit. */ +void __attribute__ ((noreturn)) die(int err, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + + exit(err); +} + +/* True if fstypes match. Null *TYPES means match anything, + except that swap types always return false. */ +/* Accept nonfs,proc,devpts and nonfs,noproc,nodevpts + with the same meaning. */ +int +matching_type (const char *type, const char *types) { + int no; /* negated types list */ + int len; + const char *p; + + if (streq (type, MNTTYPE_SWAP)) + return 0; + if (types == NULL) + return 1; + + no = 0; + if (!strncmp(types, "no", 2)) { + no = 1; + types += 2; + } + + /* Does type occur in types, separated by commas? */ + len = strlen(type); + p = types; + while(1) { + if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) && + (p[len+2] == 0 || p[len+2] == ',')) + return 0; + if (strncmp(p, type, len) == 0 && + (p[len] == 0 || p[len] == ',')) + return !no; + p = strchr(p,','); + if (!p) + break; + p++; + } + return no; +} + +/* Returns 1 if needle found or noneedle not found in haystack + * Otherwise returns 0 + */ +static int +check_option(const char *haystack, const char *needle) { + const char *p, *r; + int len, needle_len, this_len; + int no; + + no = 0; + if (!strncmp(needle, "no", 2)) { + no = 1; + needle += 2; + } + needle_len = strlen(needle); + len = strlen(haystack); + + for (p = haystack; p < haystack+len; p++) { + r = strchr(p, ','); + if (r) { + this_len = r-p; + } else { + this_len = strlen(p); + } + if (this_len != needle_len) { + p += this_len; + continue; + } + if (strncmp(p, needle, this_len) == 0) + return !no; /* foo or nofoo was found */ + p += this_len; + } + + return no; /* foo or nofoo was not found */ +} + + +/* Returns 1 if each of the test_opts options agrees with the entire + * list of options. + * Returns 0 if any noopt is found in test_opts and opt is found in options. + * Returns 0 if any opt is found in test_opts but is not found in options. + * Unlike fs type matching, nonetdev,user and nonetdev,nouser have + * DIFFERENT meanings; each option is matched explicitly as specified. + */ +int +matching_opts (const char *options, const char *test_opts) { + const char *p, *r; + char *q; + int len, this_len; + + if (test_opts == NULL) + return 1; + if (options == NULL) + options = ""; + + len = strlen(test_opts); + q = alloca(len+1); + if (q == NULL) + die (EX_SYSERR, _("not enough memory")); + + for (p = test_opts; p < test_opts+len; p++) { + r = strchr(p, ','); + if (r) { + this_len = r-p; + } else { + this_len = strlen(p); + } + if (!this_len) continue; /* if two ',' appear in a row */ + strncpy(q, p, this_len); + q[this_len] = '\0'; + if (!check_option(options, q)) + return 0; /* any match failure means failure */ + p += this_len; + } + + /* no match failures in list means success */ + return 1; +} + +int +is_pseudo_fs(const char *type) +{ + if (type == NULL || *type == '/') + return 0; + if (streq(type, "none") || + streq(type, "proc") || + streq(type, "tmpfs") || + streq(type, "sysfs") || + streq(type, "usbfs") || + streq(type, "cgroup") || + streq(type, "cpuset") || + streq(type, "rpc_pipefs") || + streq(type, "devpts") || + streq(type, "securityfs") || + streq(type, "debugfs")) + return 1; + return 0; +} + +/* Make a canonical pathname from PATH. Returns a freshly malloced string. + It is up the *caller* to ensure that the PATH is sensible. i.e. + canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' + is not a legal pathname for ``/dev/fd0''. Anything we cannot parse + we return unmodified. */ +char * +canonicalize_spec (const char *path) +{ + char *res; + + if (!path) + return NULL; + if (nocanonicalize || is_pseudo_fs(path)) + return xstrdup(path); + + res = canonicalize_path(path); + if (!res) + die(EX_SYSERR, _("not enough memory")); + return res; +} + +char *canonicalize (const char *path) +{ + char *res; + + if (!path) + return NULL; + else if (nocanonicalize) + return xstrdup(path); + + res = canonicalize_path(path); + if (!res) + die(EX_SYSERR, _("not enough memory")); + return res; +} diff --git a/mount-deprecated/sundries.h b/mount-deprecated/sundries.h new file mode 100644 index 000000000..e58fa4975 --- /dev/null +++ b/mount-deprecated/sundries.h @@ -0,0 +1,55 @@ +/* + * sundries.h + * Support function prototypes. Functions are in sundries.c. + */ +#ifndef SUNDRIES_H +#define SUNDRIES_H + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> + +#define XALLOC_EXIT_CODE 2 /* same as EX_SYSERR, for backwards compatibility */ +#include "xalloc.h" + +/* global mount, umount, and losetup variables */ +extern int mount_quiet; +extern int verbose; +extern int nocanonicalize; +extern char *progname; + +#define streq(s, t) (strcmp ((s), (t)) == 0) +#define my_free(_p) free((void *) _p) + +void block_signals (int how); + +void error (const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 1, 2))); +void die(int err, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); + +int matching_type (const char *type, const char *types); +int matching_opts (const char *options, const char *test_opts); +char *xstrndup (const char *s, int n); +char *xstrconcat3 (char *, const char *, const char *); +char *xstrconcat4 (char *, const char *, const char *, const char *); + +int is_pseudo_fs(const char *type); + +char *canonicalize (const char *path); +char *canonicalize_spec (const char *path); + +/* exit status - bits below are ORed */ +#define EX_USAGE 1 /* incorrect invocation or permission */ +#define EX_SYSERR 2 /* out of memory, cannot fork, ... */ +#define EX_SOFTWARE 4 /* internal mount bug or wrong version */ +#define EX_USER 8 /* user interrupt */ +#define EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */ +#define EX_FAIL 32 /* mount failure */ +#define EX_SOMEOK 64 /* some mount succeeded */ + +#endif /* SUNDRIES_H */ diff --git a/mount-deprecated/umount.8 b/mount-deprecated/umount.8 new file mode 100644 index 000000000..1c7d59aa7 --- /dev/null +++ b/mount-deprecated/umount.8 @@ -0,0 +1,179 @@ +.\" Copyright (c) 1996 Andries Brouwer +.\" This page is somewhat derived from a page that was +.\" (c) 1980, 1989, 1991 The Regents of the University of California +.\" and had been heavily modified by Rik Faith and myself. +.\" +.\" This is free documentation; 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. +.\" +.\" The GNU General Public License's references to "object code" +.\" and "executables" are to be interpreted as the output of any +.\" document formatting or typesetting system, including +.\" intermediate and printed output. +.\" +.\" This manual 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., +.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.TH UMOUNT 8 "March 2010" "util-linux" "System Administration" +.SH NAME +umount \- unmount file systems +.SH SYNOPSIS +.B umount +.RB [ \-hV ] +.LP +.B umount \-a +.RB [ \-dflnrv ] +.RB [ \-t +.IR vfstype ] +.RB [ \-O +.IR options ] +.br +.B umount +.RB [ \-dflnrv ] +.RI { dir | device }... +.SH DESCRIPTION +The +.B umount +command detaches the file system(s) mentioned from the file hierarchy. +A file system is specified by giving the directory where it +has been mounted. Giving the special device on which the file system lives +may also work, but is obsolete, mainly because it will fail +in case this device was mounted on more than one directory. + +Note that a file system cannot be unmounted when it is `busy' - +for example, when there are open files on it, or when some process +has its working directory there, or when a swap file on it is in use. +The offending process could even be +.B umount +itself - it opens libc, and libc in its turn may open for example +locale files. +A lazy unmount avoids this problem. + +Options for the +.B umount +command: +.TP +.B \-V +Print version and exit. +.TP +.B \-h +Print help message and exit. +.TP +.B \-v +Verbose mode. +.TP +.B \-n +Unmount without writing in +.IR /etc/mtab . +.TP +.B \-r +In case unmounting fails, try to remount read-only. +.TP +.B \-d +In case the unmounted device was a loop device, also +free this loop device. +.TP +.B \-i +Don't call the /sbin/umount.<filesystem> helper even if it exists. By default /sbin/umount.<filesystem> helper is called if one exists. +.TP +.B \-a +All of the file systems described in +.I /etc/mtab +are unmounted. (With +.B umount +version 2.7 and later: the +.I proc +filesystem is not unmounted.) +.TP +.BI \-t " vfstype" +Indicate that the actions should only be taken on file systems of the +specified type. More than one type may be specified in a comma separated +list. The list of file system types can be prefixed with +.B no +to specify the file system types on which no action should be taken. +.TP +.BI \-O " options" +Indicate that the actions should only be taken on file systems with +the specified options in +.IR /etc/fstab . +More than one option type may be specified in a comma separated +list. Each option can be prefixed with +.B no +to specify options for which no action should be taken. +.TP +.B \-f +Force unmount (in case of an unreachable NFS system). +(Requires kernel 2.1.116 or later.) +.TP +.B \-l +Lazy unmount. Detach the filesystem from the filesystem hierarchy now, +and cleanup all references to the filesystem as soon as it is not busy +anymore. +(Requires kernel 2.4.11 or later.) +.IP "\fB\-\-no\-canonicalize\fP" +Don't canonicalize paths. For more details about this option see the +.B mount(8) +man page. +.IP "\fB\-\-fake\fP" +Causes everything to be done except for the actual system call; this +``fakes'' unmounting the filesystem. It can be used to remove +entries from +.I /etc/mtab +that were unmounted earlier with the -n option. + +.SH "THE LOOP DEVICE" +The +.B umount +command will free the loop device (if any) associated +with the mount, in case it finds the option `loop=...' in +.IR /etc/mtab , +or when the \-d option was given. +Any pending loop devices can be freed using `losetup -d', see +.BR losetup (8). + +.SH NOTES +The syntax of external umount helpers is: + +.br +.BI /sbin/umount. <suffix> +.RI { dir | device } +.RB [ \-nlfvr ] +.RB [ \-t +.IR type.subtype ] +.br + +where the <suffix> is filesystem type or a value from "uhelper=" or "helper=" +mtab option. The \-t option is used for filesystems with subtypes support +(for example /sbin/mount.fuse -t fuse.sshfs). + +The uhelper= (unprivileged umount helper) is possible to use when non-root user +wants to umount a mountpoint which is not defined in the /etc/fstab file (e.g +devices mounted by udisk). + +The helper= mount option redirects all umount requests to the +/sbin/umount.<helper> independently on UID. + +.SH FILES +.I /etc/mtab +table of mounted file systems + +.SH "SEE ALSO" +.BR umount (2), +.BR mount (8), +.BR losetup (8). + +.SH HISTORY +A +.B umount +command appeared in Version 6 AT&T UNIX. +.SH AVAILABILITY +The umount command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/mount-deprecated/umount.c b/mount-deprecated/umount.c new file mode 100644 index 000000000..d3375df83 --- /dev/null +++ b/mount-deprecated/umount.c @@ -0,0 +1,878 @@ +/* + * umount(8) for Linux 0.99 - jrs, 1993 + */ + +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/mount.h> + +#include "mount_constants.h" +#include "sundries.h" +#include "getusername.h" +#include "pathnames.h" +#include "loopdev.h" +#include "fstab.h" +#include "env.h" +#include "nls.h" +#include "strutils.h" +#include "closestream.h" + +#if defined(MNT_FORCE) +/* Interesting ... it seems libc knows about MNT_FORCE and presumably + about umount2 as well -- need not do anything */ +#else /* MNT_FORCE */ + +/* Does the present kernel source know about umount2? */ +#include <linux/unistd.h> +#ifdef __NR_umount2 + +static int umount2(const char *path, int flags); + +_syscall2(int, umount2, const char *, path, int, flags); + +#else /* __NR_umount2 */ + +static int +umount2(const char *path, int flags) { + fprintf(stderr, _("umount: compiled without support for -f\n")); + errno = ENOSYS; + return -1; +} +#endif /* __NR_umount2 */ + +#ifndef MNT_FORCE +# define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */ +#endif + +#endif /* MNT_FORCE */ + +#ifndef MNT_DETACH +# define MNT_DETACH 0x00000002 /* Just detach from the tree */ +#endif + +#ifndef UMOUNT_NOFOLLOW +# define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */ +#endif + +#ifndef UMOUNT_UNUSED +# define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */ +#endif + + +/* True if we are allowed to call /sbin/umount.${FSTYPE} */ +int external_allowed = 1; + +/* Nonzero for force umount (-f). There is kernel support since 2.1.116. */ +int force = 0; + +/* Nonzero for lazy umount (-l). There is kernel support since 2.4.11. */ +int lazy = 0; + +/* When umount fails, attempt a read-only remount (-r). */ +int remount = 0; + +/* Don't write a entry in /etc/mtab (-n). */ +int nomtab = 0; + +/* Call losetup -d for each unmounted loop device. */ +int delloop = 0; + +/* True if (ruid != euid) or (0 != ruid), i.e. only "user" umounts permitted. */ +int restricted = 1; + +/* Last error message */ +int complained_err = 0; +char *complained_dev = NULL; + +/* True for fake umount (--fake). */ +static int fake = 0; + +/* + * check_special_umountprog() + * If there is a special umount program for this type, exec it. + * returns: 0: no exec was done, 1: exec was done, status has result + */ +static int +check_special_umountprog(const char *node, + const char *type, int *status) { + char search_path[] = FS_SEARCH_PATH; + char *path, umountprog[150]; + struct stat statbuf; + int res; + + if (!external_allowed) + return 0; + + if (type == NULL || strcmp(type, "none") == 0) + return 0; + + path = strtok(search_path, ":"); + while (path) { + int type_opt = 0; + + res = snprintf(umountprog, sizeof(umountprog), "%s/umount.%s", + path, type); + path = strtok(NULL, ":"); + if (res < 0 || (size_t) res >= sizeof(umountprog)) + continue; + + res = stat(umountprog, &statbuf); + if (res == -1 && errno == ENOENT && strchr(type, '.')) { + /* If type ends with ".subtype" try without it */ + *strrchr(umountprog, '.') = '\0'; + type_opt = 1; + res = stat(umountprog, &statbuf); + } + if (res == 0) { + res = fork(); + if (res == 0) { + char *umountargs[10]; + int i = 0; + + if(setgid(getgid()) < 0) + die(EX_FAIL, _("umount: cannot set group id: %m")); + + if(setuid(getuid()) < 0) + die(EX_FAIL, _("umount: cannot set user id: %m")); + + umountargs[i++] = umountprog; + umountargs[i++] = xstrdup(node); + if (nomtab) + umountargs[i++] = "-n"; + if (lazy) + umountargs[i++] = "-l"; + if (force) + umountargs[i++] = "-f"; + if (verbose) + umountargs[i++] = "-v"; + if (remount) + umountargs[i++] = "-r"; + if (type_opt) { + umountargs[i++] = "-t"; + umountargs[i++] = (char *) type; + } + umountargs[i] = NULL; + execv(umountprog, umountargs); + exit(1); /* exec failed */ + } else if (res != -1) { + int st; + wait(&st); + *status = (WIFEXITED(st) ? WEXITSTATUS(st) + : EX_SYSERR); + return 1; + } else { + int errsv = errno; + error(_("umount: cannot fork: %s"), + strerror(errsv)); + } + } + } + return 0; +} + +/* complain about a failed umount */ +static void complain(int err, const char *dev) { + + if (complained_err == err && complained_dev && dev && + strcmp(dev, complained_dev) == 0) + return; + + complained_err = err; + free(complained_dev); + complained_dev = xstrdup(dev); + + switch (err) { + case ENXIO: + error (_("umount: %s: invalid block device"), dev); break; + case EINVAL: + error (_("umount: %s: not mounted"), dev); break; + case EIO: + error (_("umount: %s: can't write superblock"), dev); break; + case EBUSY: + /* Let us hope fstab has a line "proc /proc ..." + and not "none /proc ..."*/ + error (_("umount: %s: device is busy.\n" + " (In some cases useful info about processes that use\n" + " the device is found by lsof(8) or fuser(1))"), dev); + break; + case ENOENT: + error (_("umount: %s: not found"), dev); break; + case EPERM: + error (_("umount: %s: must be superuser to umount"), dev); break; + case EACCES: + error (_("umount: %s: block devices not permitted on fs"), dev); break; + default: + error (_("umount: %s: %s"), dev, strerror (err)); break; + } +} + +/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */ +static int umount_nofollow_support(void) +{ + int res = umount2("", UMOUNT_UNUSED); + if (res != -1 || errno != EINVAL) + return 0; + + res = umount2("", UMOUNT_NOFOLLOW); + if (res != -1 || errno != ENOENT) + return 0; + + return 1; +} + +static const char *chdir_to_parent(const char *node, char **resbuf) +{ + char *tmp, *res; + const char *parent; + char buf[65536]; + + *resbuf = xstrdup(node); + + tmp = strrchr(*resbuf, '/'); + if (!tmp) + die (2, _("umount: internal error: invalid abs path: %s"), node); + + if (tmp != *resbuf) { + *tmp = '\0'; + res = tmp + 1; + parent = *resbuf; + } else if (tmp[1] != '\0') { + res = tmp + 1; + parent = "/"; + } else { + res = "."; + parent = "/"; + } + + if (chdir(parent) == -1) + die (2, _("umount: failed to chdir to %s: %m"), parent); + + if (!getcwd(buf, sizeof(buf))) + die (2, _("umount: failed to obtain current directory: %m")); + + if (strcmp(buf, parent) != 0) + die (2, _("umount: mountpoint moved (%s -> %s)"), parent, buf); + + if (verbose) + printf(_("current directory moved to %s\n"), res); + + return res; +} + +/* Umount a single device. Return a status code, so don't exit + on a non-fatal error. We lock/unlock around each umount. */ +static int +umount_one (const char *spec, const char *node, const char *type, + struct mntentchn *mc) { + int umnt_err = 0; + int isroot; + int res = 0; + int status; + int extra_flags = 0; + const char *loopdev, *target = node; + char *targetbuf = NULL; + + /* Special case for root. As of 0.99pl10 we can (almost) unmount root; + the kernel will remount it readonly so that we can carry on running + afterwards. The readonly remount is illegal if any files are opened + for writing at the time, so we can't update mtab for an unmount of + root. As it is only really a remount, this doesn't matter too + much. [sct May 29, 1993] */ + isroot = (streq (node, "/") || streq (node, "root") + || streq (node, "rootfs")); + if (isroot) + nomtab++; + + /* + * Call umount.TYPE for types that require a separate umount program. + * All such special things must occur isolated in the types string. + */ + if (check_special_umountprog(node, type, &status)) + return status; + + block_signals(SIG_BLOCK); + + /* Skip the actual umounting for --fake */ + if (fake) + goto writemtab; + + if (restricted) { + if (umount_nofollow_support()) + extra_flags |= UMOUNT_NOFOLLOW; + + /* call umount(2) with relative path to avoid races */ + target = chdir_to_parent(node, &targetbuf); + } + + if (lazy) { + res = umount2 (target, MNT_DETACH | extra_flags); + if (res < 0) + umnt_err = errno; + goto writemtab; + } + + if (force) { /* only supported for NFS */ + res = umount2 (target, MNT_FORCE | extra_flags); + if (res == -1) { + int errsv = errno; + perror("umount2"); + errno = errsv; + if (errno == ENOSYS) { + if (verbose) + printf(_("no umount2, trying umount...\n")); + res = umount (target); + } + } + } else if (extra_flags) + res = umount2 (target, extra_flags); + else + res = umount (target); + + free(targetbuf); + + if (res < 0) + umnt_err = errno; + + if (res < 0 && remount && umnt_err == EBUSY) { + /* Umount failed - let us try a remount */ + res = mount(spec, node, NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (res == 0) { + fprintf(stderr, + _("umount: %s busy - remounted read-only\n"), + spec); + if (mc && !nomtab) { + /* update mtab if the entry is there */ + struct my_mntent remnt; + remnt.mnt_fsname = mc->m.mnt_fsname; + remnt.mnt_dir = mc->m.mnt_dir; + remnt.mnt_type = mc->m.mnt_type; + remnt.mnt_opts = "ro"; + remnt.mnt_freq = 0; + remnt.mnt_passno = 0; + update_mtab(node, &remnt); + } + block_signals(SIG_UNBLOCK); + return 0; + } else if (errno != EBUSY) { /* hmm ... */ + perror("remount"); + fprintf(stderr, + _("umount: could not remount %s read-only\n"), + spec); + } + } + + loopdev = 0; + if (res >= 0) { + /* Umount succeeded */ + if (verbose) + printf (_("%s has been unmounted\n"), spec); + + /* Free any loop devices that we allocated ourselves */ + if (mc) { + char *optl; + + /* old style mtab line? */ + if (streq(mc->m.mnt_type, "loop")) { + loopdev = spec; + goto gotloop; + } + + /* new style mtab line? */ + optl = mc->m.mnt_opts ? xstrdup(mc->m.mnt_opts) : ""; + for (optl = strtok (optl, ","); optl; + optl = strtok (NULL, ",")) { + if (!strncmp(optl, "loop=", 5)) { + loopdev = optl+5; + goto gotloop; + } + } + } else { + /* + * If option "-o loop=spec" occurs in mtab, + * note the mount point, and delete mtab line. + */ + if ((mc = getmntoptfile (spec)) != NULL) + node = mc->m.mnt_dir; + } + + /* Also free loop devices when -d flag is given */ + if (delloop) + loopdev = spec; + } + gotloop: + if (loopdev) { + struct loopdev_cxt lc; + + loopcxt_init(&lc, 0); + loopcxt_set_device(&lc, loopdev); + + if (loopcxt_get_backing_file(&lc) + && loopcxt_is_autoclear(&lc) == 0) + loopcxt_delete_device(&lc); + loopcxt_deinit(&lc); + } + + writemtab: + if (!nomtab && + (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) { +#ifdef HAVE_LIBMOUNT_MOUNT + struct libmnt_update *upd = mnt_new_update(); + + if (upd && !mnt_update_set_fs(upd, 0, node, NULL)) + mnt_update_table(upd, NULL); + + mnt_free_update(upd); +#else + update_mtab (node, NULL); +#endif + } + + block_signals(SIG_UNBLOCK); + + if (res >= 0) + return 0; + if (umnt_err) + complain(umnt_err, node); + return 1; +} + +/* + * umount_one_bw: unmount FILE that has last occurrence MC0 + * + * Why this loop? + * 1. People who boot a system with a bad fstab root entry + * will get an incorrect "/dev/foo on /" in mtab. + * If later /dev/foo is actually mounted elsewhere, + * it will occur twice in mtab. + * 2. With overmounting one can get the situation that + * the same filename is used as mount point twice. + * In both cases, it is best to try the last occurrence first. + */ +static int +umount_one_bw (const char *file, struct mntentchn *mc0) { + struct mntentchn *mc; + int res = 1; + + mc = mc0; + while (res && mc) { + res = umount_one(mc->m.mnt_fsname, mc->m.mnt_dir, + mc->m.mnt_type, mc); + mc = getmntdirbackward(file, mc); + } + mc = mc0; + while (res && mc) { + res = umount_one(mc->m.mnt_fsname, mc->m.mnt_dir, + mc->m.mnt_type, mc); + mc = getmntdevbackward(file, mc); + } + return res; +} + +/* Unmount all filesystems of type VFSTYPES found in mtab. Since we are + concurrently updating mtab after every successful umount, we have to + slurp in the entire file before we start. This isn't too bad, because + in any case it's important to umount mtab entries in reverse order + to mount, e.g. /usr/spool before /usr. */ +static int +umount_all (char *types, char *test_opts) { + struct mntentchn *mc, *hd; + int errors = 0; + + hd = mtab_head(); + if (!hd->prev) + die (2, _("umount: cannot find list of filesystems to unmount")); + for (mc = hd->prev; mc != hd; mc = mc->prev) { + if (matching_type (mc->m.mnt_type, types) + && matching_opts (mc->m.mnt_opts, test_opts)) { + errors |= umount_one (mc->m.mnt_fsname, mc->m.mnt_dir, + mc->m.mnt_type, mc); + } + } + + return errors; +} + +static struct option longopts[] = +{ + { "all", 0, 0, 'a' }, + { "force", 0, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "test-opts", 1, 0, 'O' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-only", 0, 0, 'r' }, + { "types", 1, 0, 't' }, + + { "no-canonicalize", 0, 0, 144 }, + { "fake", 0, 0, 145 }, + { NULL, 0, 0, 0 } +}; + +static void +usage (FILE *fp, int n) +{ + fprintf (fp, _("Usage: umount -h | -V\n" + " umount -a [-d] [-f] [-r] [-n] [-v] [-t vfstypes] [-O opts]\n" + " umount [-d] [-f] [-r] [-n] [-v] special | node...\n")); + exit (n); +} + +/* + * Look for an option in a comma-separated list + */ +static int +contains(const char *list, const char *s) { + int n = strlen(s); + + while (list && *list) { + if (strncmp(list, s, n) == 0 && + (list[n] == 0 || list[n] == ',')) + return 1; + while (*list && *list++ != ',') ; + } + return 0; +} + +/* check if @mc contains a loop device which is associated + * with the @file in fs + */ +static int +is_valid_loop(struct mntentchn *mc, struct mntentchn *fs) +{ + uintmax_t offset = 0; + char *p; + + /* check if it begins with /dev/loop */ + if (strncmp(mc->m.mnt_fsname, _PATH_DEV_LOOP, + sizeof(_PATH_DEV_LOOP) - 1)) + return 0; + + /* check for loop option in fstab */ + if (!contains(fs->m.mnt_opts, "loop")) + return 0; + + /* check for offset option in fstab */ + p = get_option_value(fs->m.mnt_opts, "offset="); + if (p && strtosize(p, &offset)) { + if (verbose > 1) + printf(_("failed to parse 'offset=%s' options\n"), p); + return 0; + } + + /* check association */ + if (loopdev_is_used((char *) mc->m.mnt_fsname, fs->m.mnt_fsname, + offset, LOOPDEV_FL_OFFSET) == 1) { + if (verbose > 1) + printf(_("device %s is associated with %s\n"), + mc->m.mnt_fsname, fs->m.mnt_fsname); + return 1; + } + + if (verbose > 1) + printf(_("device %s is not associated with %s\n"), + mc->m.mnt_fsname, fs->m.mnt_fsname); + return 0; +} + +/* + * umount helper call based on {u,p}helper= mount option + */ +static int check_helper_umountprog(const char *node, + const char *opts, const char *name, + int *status) +{ + char *helper; + + if (!external_allowed || !opts) + return 0; + + helper = get_option_value(opts, name); + if (helper) + return check_special_umountprog(node, helper, status); + + return 0; +} + +static int +umount_file (char *arg) { + struct mntentchn *mc, *fs; + const char *file, *options; + int fstab_has_user, fstab_has_users, fstab_has_owner, fstab_has_group; + int ok, status = 0; + struct stat statbuf; + char *loopdev = NULL; + + if (!*arg) { /* "" would be expanded to `pwd` */ + die(2, _("Cannot unmount \"\"\n")); + return 0; + } + + file = canonicalize(arg); /* mtab paths are canonicalized */ + +try_loopdev: + if (verbose > 1) + printf(_("Trying to unmount %s\n"), file); + + mc = getmntdirbackward(file, NULL); + if (!mc) { + mc = getmntdevbackward(file, NULL); + if (mc) { + struct mntentchn *mc1; + char *cn; + + mc1 = getmntdirbackward(mc->m.mnt_dir, NULL); + if (!mc1) + /* 'mc1' must exist, though not necessarily + equals to `mc'. Otherwise we go mad. */ + die(EX_SOFTWARE, + _("umount: confused when analyzing mtab")); + + cn = canonicalize(mc1->m.mnt_fsname); + if (cn && strcmp(file, cn)) { + /* Something was stacked over `file' on the + same mount point. */ + die(EX_FAIL, _("umount: cannot unmount %s -- %s is " + "mounted over it on the same point"), + file, mc1->m.mnt_fsname); + } + free(cn); + } + } + if (!mc && verbose) + printf(_("Could not find %s in mtab\n"), file); + + /* not found in mtab - check if it is associated with some loop device + * (only if it is a regular file) + */ + if (!mc && !loopdev && !stat(file, &statbuf) && S_ISREG(statbuf.st_mode)) { + int count = loopdev_count_by_backing_file(file, &loopdev); + + if (count == 1) { + if (verbose) + printf(_("%s is associated with %s\n"), + arg, loopdev); + file = loopdev; + goto try_loopdev; + + } else if (count > 1) + fprintf(stderr, _("umount: warning: %s is associated " + "with more than one loop device\n"), arg); + } + + if (mc) { + /* + * helper - umount helper (e.g. pam_mount) + */ + if (check_helper_umountprog(arg, mc->m.mnt_opts, + "helper=", &status)) + return status; + } + + if (restricted) { + char *mtab_user = NULL; + + if (!mc) + die(2, + _("umount: %s is not mounted (according to mtab)"), + file); + /* + * uhelper - unprivileged umount helper (e.g. HAL/udisks mounts) + */ + if (check_helper_umountprog(arg, mc->m.mnt_opts, + "uhelper=", &status)) + return status; + + /* The 2.4 kernel will generally refuse to mount the same + filesystem on the same mount point, but will accept NFS. + So, unmounting must be possible. */ + if (!is_mounted_once(file) && strcmp(mc->m.mnt_type,"nfs")) + die(2, + _("umount: it seems %s is mounted multiple times"), + file); + + /* If fstab contains the two lines + /dev/sda1 /mnt/zip auto user,noauto 0 0 + /dev/sda4 /mnt/zip auto user,noauto 0 0 + then "mount /dev/sda4" followed by "umount /mnt/zip" + used to fail. So, we must not look for file, but for + the pair (dev,file) in fstab. */ + fs = getfs_by_devdir(mc->m.mnt_fsname, mc->m.mnt_dir); + if (!fs) { + fs = getfs_by_dir(file); + if (!fs && !getfs_by_spec(file)) + die (2, + _("umount: %s is not in the fstab " + "(and you are not root)"), + file); + + /* spec could be a file which is loop mounted */ + if (!fs || !is_valid_loop(mc, fs)) + die (2, _("umount: %s mount disagrees with " + "the fstab"), file); + } + + /* + * User mounting and unmounting is allowed only + * if fstab contains one of the options `user', + * `users' or `owner' or `group'. + * + * The option `users' allows arbitrary users to mount + * and unmount - this may be a security risk. + * + * The options `user', `owner' and `group' only allow + * unmounting by the user that mounted (visible in mtab). + */ + + options = fs->m.mnt_opts; + if (!options) + options = ""; + fstab_has_user = contains(options, "user"); + fstab_has_users = contains(options, "users"); + fstab_has_owner = contains(options, "owner"); + fstab_has_group = contains(options, "group"); + ok = 0; + + if (fstab_has_users) + ok = 1; + + if (!ok && (fstab_has_user || fstab_has_owner || + fstab_has_group)) { + char *user = getusername(); + + options = mc->m.mnt_opts; + if (!options) + options = ""; + mtab_user = get_option_value(options, "user="); + + if (user && mtab_user && streq (user, mtab_user)) + ok = 1; + } + if (!ok) + die (2, _("umount: only %s can unmount %s from %s"), + mtab_user ? mtab_user : "root", + fs->m.mnt_fsname, fs->m.mnt_dir); + + } + + if (mc) + return umount_one_bw (file, mc); + else + return umount_one (arg, arg, arg, NULL); +} + +int +main (int argc, char *argv[]) { + int c; + int all = 0; + char *types = NULL, *test_opts = NULL, *p; + int result = 0; + + sanitize_env(); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + progname = argv[0]; + if ((p = strrchr(progname, '/')) != NULL) + progname = p+1; + + umask(022); + + while ((c = getopt_long (argc, argv, "adfhlnrit:O:vV", + longopts, NULL)) != -1) + switch (c) { + case 'a': /* umount everything */ + ++all; + break; + /* fall through? */ + case 'd': /* do losetup -d for unmounted loop devices */ + ++delloop; + break; + case 'f': /* force umount */ + ++force; + break; + case 'h': /* help */ + usage (stdout, 0); + break; + case 'l': /* lazy umount */ + ++lazy; + break; + case 'n': /* do not write in /etc/mtab */ + ++nomtab; + break; + case 'O': /* specify file system options */ + test_opts = optarg; + break; + case 'r': /* remount read-only if umount fails */ + ++remount; + break; + case 'v': /* make noise */ + ++verbose; + break; + case 'V': /* version */ + printf ("umount (%s)\n", PACKAGE_STRING); + exit (0); + case 't': /* specify file system type */ + types = optarg; + break; + case 'i': + external_allowed = 0; + break; + case 144: + nocanonicalize = 1; + break; + case 145: + fake = 1; + break; + case 0: + break; + case '?': + default: + usage (stderr, 1); + } + + { + const uid_t ruid = getuid(); + const uid_t euid = geteuid(); + + /* if we're really root and aren't running setuid */ + if (((uid_t)0 == ruid) && (ruid == euid)) { + restricted = 0; + } + } + + if (restricted && + (all || types || nomtab || force || remount || nocanonicalize || + fake)) { + die (2, _("umount: only root can do that")); + } + + argc -= optind; + argv += optind; + + atexit(unlock_mtab); + +#ifdef HAVE_LIBMOUNT_MOUNT + mnt_init_debug(0); +#endif + if (all) { + /* nodev stuff: sysfs, usbfs, oprofilefs, ... */ + if (types == NULL) + types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd"; + result = umount_all (types, test_opts); + } else if (argc < 1) { + usage (stderr, 2); + } else while (argc--) { + result += umount_file(*argv++); + } + exit (result); /* nonzero on at least one failure */ +} |