diff options
author | Brian Behlendorf <[email protected]> | 2011-03-04 15:14:46 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2011-03-09 15:26:48 -0800 |
commit | d53368f6755ff67342f68e2e536c4157409fd047 (patch) | |
tree | be909c931e465df4757a67f9329ab9e2b1dc6941 /cmd/zfs | |
parent | adf2e8778e66e6a749cec981da00463b342bd563 (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.c | 470 |
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. |