aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/zfs
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2011-03-04 15:14:46 -0800
committerBrian Behlendorf <[email protected]>2011-03-09 15:26:48 -0800
commitd53368f6755ff67342f68e2e536c4157409fd047 (patch)
treebe909c931e465df4757a67f9329ab9e2b1dc6941 /cmd/zfs
parentadf2e8778e66e6a749cec981da00463b342bd563 (diff)
Fix mount helper
Several issues related to strange mount/umount behavior were reported and this commit should address most of them. The original idea was to put in place a zfs mount helper (mount.zfs). This helper is used to enforce 'legacy' mount behavior, and perform any extra mount argument processing (selinux, zfsutil, etc). This helper wasn't ready for the 0.6.0-rc1 release but with this change it's functional but needs to extensively tested. This change addresses the following open issues. Closes #101 Closes #107 Closes #113 Closes #115 Closes #119
Diffstat (limited to 'cmd/zfs')
-rw-r--r--cmd/zfs/zfs_main.c470
1 files changed, 45 insertions, 425 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index f21c36b81..32f85b1e0 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -3827,371 +3827,6 @@ zfs_do_python(int argc, char **argv)
return (-1);
}
-typedef struct option_map {
- const char *name;
- int mask;
-} option_map_t;
-
-static const option_map_t option_map[] = {
- /* Canonicalized filesystem independent options from mount(8) */
- { MNTOPT_NOAUTO, MS_COMMENT },
- { MNTOPT_DEFAULTS, MS_COMMENT },
- { MNTOPT_NODEVICES, MS_NODEV },
- { MNTOPT_DIRSYNC, MS_DIRSYNC },
- { MNTOPT_NOEXEC, MS_NOEXEC },
- { MNTOPT_GROUP, MS_GROUP },
- { MNTOPT_NETDEV, MS_COMMENT },
- { MNTOPT_NOFAIL, MS_COMMENT },
- { MNTOPT_NOSUID, MS_NOSUID },
- { MNTOPT_OWNER, MS_OWNER },
- { MNTOPT_REMOUNT, MS_REMOUNT },
- { MNTOPT_RO, MS_RDONLY },
- { MNTOPT_SYNC, MS_SYNCHRONOUS },
- { MNTOPT_USER, MS_USERS },
- { MNTOPT_USERS, MS_USERS },
-#ifdef MS_NOATIME
- { MNTOPT_NOATIME, MS_NOATIME },
-#endif
-#ifdef MS_NODIRATIME
- { MNTOPT_NODIRATIME, MS_NODIRATIME },
-#endif
-#ifdef MS_RELATIME
- { MNTOPT_RELATIME, MS_RELATIME },
-#endif
-#ifdef MS_STRICTATIME
- { MNTOPT_DFRATIME, MS_STRICTATIME },
-#endif
-#ifdef HAVE_SELINUX
- { MNTOPT_CONTEXT, MS_COMMENT },
- { MNTOPT_FSCONTEXT, MS_COMMENT },
- { MNTOPT_DEFCONTEXT, MS_COMMENT },
- { MNTOPT_ROOTCONTEXT, MS_COMMENT },
-#endif
-#ifdef MS_I_VERSION
- { MNTOPT_IVERSION, MS_I_VERSION },
-#endif
-#ifdef MS_MANDLOCK
- { MNTOPT_NBMAND, MS_MANDLOCK },
-#endif
- /* Valid options not found in mount(8) */
- { MNTOPT_BIND, MS_BIND },
-#ifdef MS_REC
- { MNTOPT_RBIND, MS_BIND|MS_REC },
-#endif
- { MNTOPT_COMMENT, MS_COMMENT },
- { MNTOPT_BOOTWAIT, MS_COMMENT },
- { MNTOPT_NOBOOTWAIT, MS_COMMENT },
- { MNTOPT_OPTIONAL, MS_COMMENT },
- { MNTOPT_SHOWTHROUGH, MS_COMMENT },
-#ifdef MS_NOSUB
- { MNTOPT_NOSUB, MS_NOSUB },
-#endif
-#ifdef MS_SILENT
- { MNTOPT_QUIET, MS_SILENT },
-#endif
- /* Custom zfs options */
- { MNTOPT_NOXATTR, MS_COMMENT },
- { NULL, 0 } };
-
-/*
- * Break the mount option in to a name/value pair. The name is
- * validated against the option map and mount flags set accordingly.
- */
-static int
-parse_option(char *mntopt, unsigned long *mntflags, int sloppy)
-{
- const option_map_t *opt;
- char *ptr, *name, *value = NULL;
- int rc = 0;
-
- name = strdup(mntopt);
- if (name == NULL)
- return (ENOMEM);
-
- for (ptr = name; ptr && *ptr; ptr++) {
- if (*ptr == '=') {
- *ptr = '\0';
- value = ptr+1;
- break;
- }
- }
-
- for (opt = option_map; opt->name != NULL; opt++) {
- if (strncmp(name, opt->name, strlen(name)) == 0) {
- *mntflags |= opt->mask;
-
- /* MS_USERS implies default user options */
- if (opt->mask & (MS_USERS))
- *mntflags |= (MS_NOEXEC|MS_NOSUID|MS_NODEV);
-
- /* MS_OWNER|MS_GROUP imply default owner options */
- if (opt->mask & (MS_OWNER | MS_GROUP))
- *mntflags |= (MS_NOSUID|MS_NODEV);
-
- rc = 0;
- goto out;
- }
- }
-
- if (!sloppy)
- rc = ENOENT;
-out:
- /* If required further process on the value may be done here */
- free(name);
- return (rc);
-}
-
-/*
- * Translate the mount option string in to MS_* mount flags for the
- * kernel vfs. When sloppy is non-zero unknown options will be ignored
- * otherwise they are considered fatal are copied in to badopt.
- */
-static int
-parse_options(char *mntopts, unsigned long *mntflags, int sloppy, char *badopt)
-{
- int rc = 0, quote = 0;
- char *ptr, *opt, *opts;
-
- opts = strdup(mntopts);
- if (opts == NULL)
- return (ENOMEM);
-
- *mntflags = 0;
- opt = NULL;
-
- /*
- * Scan through all mount options which must be comma delimited.
- * We must be careful to notice regions which are double quoted
- * and skip commas in these regions. Each option is then checked
- * to determine if it is a known option.
- */
- for (ptr = opts; ptr && *ptr; ptr++) {
- if (opt == NULL)
- opt = ptr;
-
- if (*ptr == '"')
- quote = !quote;
-
- if (quote)
- continue;
-
- if ((*ptr == ',') || (*ptr == '\0')) {
- *ptr = '\0';
- rc = parse_option(opt, mntflags, sloppy);
- if (rc) {
- strcpy(badopt, opt);
- goto out;
- }
-
- opt = NULL;
- }
- }
-out:
- free(opts);
- return (rc);
-}
-
-/*
- * Called when invoked as /sbin/mount.zfs, mount helper for mount(8).
- */
-static int
-manual_mount(int argc, char **argv)
-{
- zfs_handle_t *zhp;
- char legacy[ZFS_MAXPROPLEN];
- char mntopts[MNT_LINE_MAX] = { '\0' };
- char badopt[MNT_LINE_MAX] = { '\0' };
- char *dataset, *mntpoint;
- unsigned long mntflags;
- int sloppy = 0, fake = 0, verbose = 0;
- int rc, c;
-
- /* check options */
- while ((c = getopt(argc, argv, "sfnvo:h?")) != -1) {
- switch (c) {
- case 's':
- sloppy = 1;
- break;
- case 'f':
- fake = 1;
- break;
- case 'n':
- /* Ignored, handled by mount(8) */
- break;
- case 'v':
- verbose++;
- break;
- case 'o':
- (void) strlcpy(mntopts, optarg, sizeof (mntopts));
- break;
- case 'h':
- case '?':
- (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
- optopt);
- (void) fprintf(stderr, gettext("Usage: mount.zfs "
- "[-sfnv] [-o options] <dataset> <mountpoint>\n"));
- return (MOUNT_USAGE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check that we only have two arguments */
- if (argc != 2) {
- if (argc == 0)
- (void) fprintf(stderr, gettext("missing dataset "
- "argument\n"));
- else if (argc == 1)
- (void) fprintf(stderr,
- gettext("missing mountpoint argument\n"));
- else
- (void) fprintf(stderr, gettext("too many arguments\n"));
- (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
- return (MOUNT_USAGE);
- }
-
- dataset = argv[0];
- mntpoint = argv[1];
-
- /* try to open the dataset to access the mount point */
- if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) {
- (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
- "mounted, unable to open the dataset\n"), dataset);
- return (MOUNT_USAGE);
- }
-
- (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy,
- sizeof (legacy), NULL, NULL, 0, B_FALSE);
-
- zfs_close(zhp);
-
- /* check for legacy mountpoint or util mount option */
- if ((!strcmp(legacy, ZFS_MOUNTPOINT_LEGACY) == 0) &&
- (strstr(mntopts, MNTOPT_ZFSUTIL) == NULL)) {
- (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
- "mounted using 'mount -a -t zfs'\n"), dataset);
- (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
- "instead.\n"), mntpoint);
- (void) fprintf(stderr, gettext("If you must use 'mount -a -t "
- "zfs' or /etc/fstab, use 'zfs set mountpoint=legacy'.\n"));
- (void) fprintf(stderr, gettext("See zfs(8) for more "
- "information.\n"));
- return (MOUNT_USAGE);
- }
-
- /* validate mount options and set mntflags */
- rc = parse_options(mntopts, &mntflags, sloppy, badopt);
- if (rc) {
- switch (rc) {
- case ENOMEM:
- (void) fprintf(stderr, gettext("filesystem '%s' "
- "cannot be mounted due to a memory allocation "
- "failure\n"), dataset);
- return (MOUNT_SYSERR);
- case EINVAL:
- (void) fprintf(stderr, gettext("filesystem '%s' "
- "cannot be mounted of due to the invalid option "
- "'%s'\n"), dataset, badopt);
- (void) fprintf(stderr, gettext("Use the '-s' option "
- "to ignore the bad mount option.\n"));
- return (MOUNT_USAGE);
- default:
- (void) fprintf(stderr, gettext("filesystem '%s' "
- "cannot be mounted due to internal error %d\n"),
- dataset, rc);
- return (MOUNT_SOFTWARE);
- }
- }
-
- if (verbose > 2)
- printf("mount.zfs: dataset: \"%s\", mountpoint: \"%s\" "
- "mountflags: 0x%lx, mountopts: \"%s\"\n", dataset,
- mntpoint, mntflags, mntopts);
-
- /* load the zfs posix layer module (zpl) */
- if (libzfs_load_module("zpl")) {
- (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
- "mounted without the zpl kernel module\n"), dataset);
- (void) fprintf(stderr, gettext("Use 'dmesg' to determine why "
- "the module could not be loaded.\n"));
- return (MOUNT_SYSERR);
- }
-
- if (!fake) {
- rc = mount(dataset, mntpoint, MNTTYPE_ZFS, mntflags, mntopts);
- if (rc) {
- (void) fprintf(stderr, gettext("filesystem '%s' can"
- "not be mounted due to error %d\n"), dataset, rc);
- return (MOUNT_USAGE);
- }
- }
-
- return (MOUNT_SUCCESS);
-}
-
-#ifdef HAVE_UNMOUNT_HELPER
-/*
- * Called when invoked as /sbin/umount.zfs, mount helper for mount(8).
- * Unlike a manual mount, we allow unmounts of non-legacy filesystems,
- * as this is the dominant administrative interface.
- */
-static int
-manual_unmount(int argc, char **argv)
-{
- int verbose = 0, flags = 0;
- int c;
-
- /* check options */
- while ((c = getopt(argc, argv, "nlfvrh?")) != -1) {
- switch (c) {
- case 'n':
- /* Ignored, handled by mount(8) */
- break;
- case 'l':
- flags = MS_DETACH;
- break;
- case 'f':
- flags = MS_FORCE;
- break;
- case 'v':
- verbose++;
- break;
- case 'r':
- /* Remount read-only on umount failure, unsupported */
- (void) fprintf(stderr, gettext("Unsupported option "
- "'%c'\n"), optopt);
- return (MOUNT_USAGE);
- case 'h':
- case '?':
- (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
- optopt);
- (void) fprintf(stderr, gettext("Usage: umount.zfs "
- "[-nlfvr] <mountpoint>\n"));
- return (MOUNT_USAGE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check that we only have one argument */
- if (argc != 1) {
- if (argc == 0)
- (void) fprintf(stderr, gettext("missing mountpoint "
- "argument\n"));
- else
- (void) fprintf(stderr, gettext("too many arguments\n"));
-
- (void) fprintf(stderr, gettext("Usage: umount.zfs [-nlfvr] "
- "<mountpoint>\n"));
- return (MOUNT_USAGE);
- }
-
- return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
-}
-#endif /* HAVE_UNMOUNT_HELPER */
-
static int
find_command_idx(char *command, int *idx)
{
@@ -4289,7 +3924,6 @@ main(int argc, char **argv)
{
int ret;
int i = 0;
- char *progname;
char *cmdname;
(void) setlocale(LC_ALL, "");
@@ -4304,78 +3938,64 @@ main(int argc, char **argv)
}
/*
- * This command also doubles as the /etc/fs mount and unmount program.
- * Determine if we should take this behavior based on argv[0].
+ * Make sure the user has specified some command.
*/
- progname = basename(argv[0]);
- if (strcmp(progname, "mount.zfs") == 0) {
- ret = manual_mount(argc, argv);
-#ifdef HAVE_UNMOUNT_HELPER
- } else if (strcmp(progname, "umount.zfs") == 0) {
- ret = manual_unmount(argc, argv);
-#endif /* HAVE_UNMOUNT_HELPER */
- } else {
- /*
- * Make sure the user has specified some command.
- */
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing command\n"));
- usage(B_FALSE);
- }
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing command\n"));
+ usage(B_FALSE);
+ }
- cmdname = argv[1];
+ cmdname = argv[1];
- /*
- * The 'umount' command is an alias for 'unmount'
- */
- if (strcmp(cmdname, "umount") == 0)
- cmdname = "unmount";
+ /*
+ * The 'umount' command is an alias for 'unmount'
+ */
+ if (strcmp(cmdname, "umount") == 0)
+ cmdname = "unmount";
- /*
- * The 'recv' command is an alias for 'receive'
- */
- if (strcmp(cmdname, "recv") == 0)
- cmdname = "receive";
+ /*
+ * The 'recv' command is an alias for 'receive'
+ */
+ if (strcmp(cmdname, "recv") == 0)
+ cmdname = "receive";
- /*
- * Special case '-?'
- */
- if ((strcmp(cmdname, "-?") == 0) ||
- (strcmp(cmdname, "--help") == 0))
- usage(B_TRUE);
+ /*
+ * Special case '-?'
+ */
+ if ((strcmp(cmdname, "-?") == 0) ||
+ (strcmp(cmdname, "--help") == 0))
+ usage(B_TRUE);
- if ((g_zfs = libzfs_init()) == NULL)
- return (1);
+ if ((g_zfs = libzfs_init()) == NULL)
+ return (1);
- zpool_set_history_str("zfs", argc, argv, history_str);
- verify(zpool_stage_history(g_zfs, history_str) == 0);
+ zpool_set_history_str("zfs", argc, argv, history_str);
+ verify(zpool_stage_history(g_zfs, history_str) == 0);
- libzfs_print_on_error(g_zfs, B_TRUE);
+ libzfs_print_on_error(g_zfs, B_TRUE);
- /*
- * Run the appropriate command.
- */
- libzfs_mnttab_cache(g_zfs, B_TRUE);
- if (find_command_idx(cmdname, &i) == 0) {
- current_command = &command_table[i];
- ret = command_table[i].func(argc - 1, argv + 1);
- } else if (strchr(cmdname, '=') != NULL) {
- verify(find_command_idx("set", &i) == 0);
- current_command = &command_table[i];
- ret = command_table[i].func(argc, argv);
- } else {
- (void) fprintf(stderr, gettext("unrecognized "
- "command '%s'\n"), cmdname);
- usage(B_FALSE);
- ret = 1;
- }
- libzfs_mnttab_cache(g_zfs, B_FALSE);
+ /*
+ * Run the appropriate command.
+ */
+ libzfs_mnttab_cache(g_zfs, B_TRUE);
+ if (find_command_idx(cmdname, &i) == 0) {
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc - 1, argv + 1);
+ } else if (strchr(cmdname, '=') != NULL) {
+ verify(find_command_idx("set", &i) == 0);
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc, argv);
+ } else {
+ (void) fprintf(stderr, gettext("unrecognized "
+ "command '%s'\n"), cmdname);
+ usage(B_FALSE);
+ ret = 1;
}
+ libzfs_mnttab_cache(g_zfs, B_FALSE);
+ libzfs_fini(g_zfs);
(void) fclose(mnttab_file);
- libzfs_fini(g_zfs);
-
/*
* The 'ZFS_ABORT' environment variable causes us to dump core on exit
* for the purposes of running ::findleaks.