X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=cmd%2Fzfs%2Fzfs_main.c;h=86f0d5c39f1cbf9e1f4245abc90f3dd046ac3c77;hb=45066d1f20a582fc8229776503b1cdd554d7fde4;hp=9cb69c1bf21ea95266d84634bbb8504d2e2368e5;hpb=d4ed667343c3dac114642b9f6cb4f7baa3ff7334;p=zfs.git diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 9cb69c1..86f0d5c 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -546,6 +546,7 @@ finish_progress(char *done) free(pt_header); pt_header = NULL; } + /* * zfs clone [-p] [-o prop=value] ... * @@ -3137,11 +3138,11 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, shared_nfs = zfs_is_shared_nfs(zhp, NULL); shared_smb = zfs_is_shared_smb(zhp, NULL); - if (shared_nfs && shared_smb || - (shared_nfs && strcmp(shareopts, "on") == 0 && - strcmp(smbshareopts, "off") == 0) || - (shared_smb && strcmp(smbshareopts, "on") == 0 && - strcmp(shareopts, "off") == 0)) { + if ((shared_nfs && shared_smb) || + ((shared_nfs && strcmp(shareopts, "on") == 0) && + (strcmp(smbshareopts, "off") == 0)) || + ((shared_smb && strcmp(smbshareopts, "on") == 0) && + (strcmp(shareopts, "off") == 0))) { if (!explicit) return (0); @@ -3259,7 +3260,7 @@ share_mount(int op, int argc, char **argv) int flags = 0; /* check options */ - while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a")) + while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:" : "a")) != -1) { switch (c) { case 'a': @@ -3282,9 +3283,6 @@ share_mount(int op, int argc, char **argv) append_options(options, optarg); break; - case 'O': - flags |= MS_OVERLAY; - break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -3352,7 +3350,7 @@ share_mount(int op, int argc, char **argv) } /* - * When mount is given no arguments, go through /etc/mnttab and + * When mount is given no arguments, go through /etc/mtab and * display any active ZFS mounts. We hide any snapshots, since * they are controlled automatically. */ @@ -3430,7 +3428,7 @@ unshare_unmount_compare(const void *larg, const void *rarg, void *unused) /* * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an - * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, + * absolute path, find the entry /etc/mtab, verify that its a ZFS filesystem, * and unmount it appropriately. */ static int @@ -3444,7 +3442,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) ino_t path_inode; /* - * Search for the path in /etc/mnttab. Rather than looking for the + * Search for the path in /etc/mtab. Rather than looking for the * specific path, which can be fooled by non-standard paths (i.e. ".." * or "//"), we stat() the path and search for the corresponding * (major,minor) device pair. @@ -3471,7 +3469,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) "currently mounted\n"), cmdname, path); return (1); } - (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"), + (void) fprintf(stderr, gettext("warning: %s not in mtab\n"), path); if ((ret = umount2(path, flags)) != 0) (void) fprintf(stderr, gettext("%s: %s\n"), path, @@ -3513,8 +3511,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) strcmp(smbshare_prop, "off") == 0) { (void) fprintf(stderr, gettext("cannot unshare " "'%s': legacy share\n"), path); - (void) fprintf(stderr, gettext("use " - "unshare(1M) to unshare this filesystem\n")); + (void) fprintf(stderr, gettext("use exportfs(8) " + "or smbcontrol(1) to unshare this filesystem\n")); } else if (!zfs_is_shared(zhp)) { (void) fprintf(stderr, gettext("cannot unshare '%s': " "not currently shared\n"), path); @@ -3533,7 +3531,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) (void) fprintf(stderr, gettext("cannot unmount " "'%s': legacy mountpoint\n"), zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use umount(1M) " + (void) fprintf(stderr, gettext("use umount(8) " "to unmount this filesystem\n")); } else { ret = zfs_unmountall(zhp, flags); @@ -3583,8 +3581,8 @@ unshare_unmount(int op, int argc, char **argv) /* * We could make use of zfs_for_each() to walk all datasets in * the system, but this would be very inefficient, especially - * since we would have to linearly search /etc/mnttab for each - * one. Instead, do one pass through /etc/mnttab looking for + * since we would have to linearly search /etc/mtab for each + * one. Instead, do one pass through /etc/mtab looking for * zfs entries and call zfs_unmount() for each one. * * Things get a little tricky if the administrator has created @@ -3596,7 +3594,7 @@ unshare_unmount(int op, int argc, char **argv) */ struct mnttab entry; uu_avl_pool_t *pool; - uu_avl_t *tree; + uu_avl_t *tree = NULL; unshare_unmount_node_t *node; uu_avl_index_t idx; uu_avl_walk_t *walk; @@ -3829,44 +3827,210 @@ 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 } }; + /* - * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is - * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. + * 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; + + 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 mountpoint[ZFS_MAXPROPLEN]; + char legacy[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX] = { '\0' }; - int ret; - int c; - int flags = 0; - char *dataset, *path; + 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, ":mo:O")) != -1) { + while ((c = getopt(argc, argv, "sfnvo:h?")) != -1) { switch (c) { - case 'o': - (void) strlcpy(mntopts, optarg, sizeof (mntopts)); + case 's': + sloppy = 1; break; - case 'O': - flags |= MS_OVERLAY; + case 'f': + fake = 1; break; - case 'm': - flags |= MS_NOMNTTAB; + case 'n': + /* Ignored, handled by mount(8) */ break; - case ':': - (void) fprintf(stderr, gettext("missing argument for " - "'%c' option\n"), optopt); - usage(B_FALSE); + case 'v': + verbose++; break; + case 'o': + (void) strlcpy(mntopts, optarg, sizeof (mntopts)); + break; + case 'h': case '?': - (void) fprintf(stderr, gettext("invalid option '%c'\n"), + (void) fprintf(stderr, gettext("Invalid option '%c'\n"), optopt); - (void) fprintf(stderr, gettext("usage: mount [-o opts] " - "\n")); - return (2); + (void) fprintf(stderr, gettext("Usage: mount.zfs " + "[-sfnv] [-o options] \n")); + return (MOUNT_USAGE); } } @@ -3884,85 +4048,149 @@ manual_mount(int argc, char **argv) else (void) fprintf(stderr, gettext("too many arguments\n")); (void) fprintf(stderr, "usage: mount \n"); - return (2); + return (MOUNT_USAGE); } dataset = argv[0]; - path = argv[1]; + mntpoint = argv[1]; - /* try to open the dataset */ - if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) - return (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, mountpoint, - sizeof (mountpoint), NULL, NULL, 0, B_FALSE); + (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy, + sizeof (legacy), NULL, NULL, 0, B_FALSE); - /* check for legacy mountpoint and complain appropriately */ - ret = 0; - if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { - if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, - NULL, 0, mntopts, sizeof (mntopts)) != 0) { - (void) fprintf(stderr, gettext("mount failed: %s\n"), - strerror(errno)); - ret = 1; - } - } else { + 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 -F zfs'\n"), dataset); + "mounted using 'mount -a -t zfs'\n"), dataset); (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " - "instead.\n"), path); - (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " - "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); - (void) fprintf(stderr, gettext("See zfs(1M) for more " + "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")); - ret = 1; + 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); + } } - return (ret); + 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 /etc/fs/zfs/umount. Unlike a manual mount, we allow - * unmounts of non-legacy filesystems, as this is the dominant administrative - * interface. + * 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 flags = 0; + int verbose = 0, flags = 0; int c; /* check options */ - while ((c = getopt(argc, argv, "f")) != -1) { + 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"), + (void) fprintf(stderr, gettext("Invalid option '%c'\n"), optopt); - (void) fprintf(stderr, gettext("usage: unmount [-f] " - "\n")); - return (2); + (void) fprintf(stderr, gettext("Usage: umount.zfs " + "[-nlfvr] \n")); + return (MOUNT_USAGE); } } argc -= optind; argv += optind; - /* check arguments */ + /* check that we only have one argument */ if (argc != 1) { if (argc == 0) - (void) fprintf(stderr, gettext("missing path " + (void) fprintf(stderr, gettext("missing mountpoint " "argument\n")); else (void) fprintf(stderr, gettext("too many arguments\n")); - (void) fprintf(stderr, gettext("usage: unmount [-f] \n")); - return (2); + + (void) fprintf(stderr, gettext("Usage: umount.zfs [-nlfvr] " + "\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) @@ -4069,17 +4297,6 @@ main(int argc, char **argv) opterr = 0; - if ((g_zfs = libzfs_init()) == NULL) { - (void) fprintf(stderr, gettext("internal error: failed to " - "initialize ZFS library\n")); - return (1); - } - - 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); - if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { (void) fprintf(stderr, gettext("internal error: unable to " "open %s\n"), MNTTAB); @@ -4091,10 +4308,12 @@ main(int argc, char **argv) * Determine if we should take this behavior based on argv[0]. */ progname = basename(argv[0]); - if (strcmp(progname, "mount") == 0) { + if (strcmp(progname, "mount.zfs") == 0) { ret = manual_mount(argc, argv); - } else if (strcmp(progname, "umount") == 0) { +#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. @@ -4121,9 +4340,18 @@ main(int argc, char **argv) /* * Special case '-?' */ - if (strcmp(cmdname, "-?") == 0) + if ((strcmp(cmdname, "-?") == 0) || + (strcmp(cmdname, "--help") == 0)) usage(B_TRUE); + 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); + + libzfs_print_on_error(g_zfs, B_TRUE); + /* * Run the appropriate command. */