]> git.proxmox.com Git - mirror_zfs.git/blobdiff - cmd/zfs/zfs_main.c
Illumos 5745 - zfs set allows only one dataset property to be set at a time
[mirror_zfs.git] / cmd / zfs / zfs_main.c
index 50ac59fba74766034aa5def8ae29c9efafd75ae1..356bf9aae4f6af94d693c4ec552ab9f921d9873e 100644 (file)
@@ -258,12 +258,12 @@ get_usage(zfs_help_t idx)
        case HELP_ROLLBACK:
                return (gettext("\trollback [-rRf] <snapshot>\n"));
        case HELP_SEND:
-               return (gettext("\tsend [-DnPpRrve] [-[iI] snapshot] "
+               return (gettext("\tsend [-DnPpRvLe] [-[iI] snapshot] "
                    "<snapshot>\n"
-                   "\tsend [-e] [-i snapshot|bookmark] "
+                   "\tsend [-Le] [-i snapshot|bookmark] "
                    "<filesystem|volume|snapshot>\n"));
        case HELP_SET:
-               return (gettext("\tset <property=value> "
+               return (gettext("\tset <property=value> ... "
                    "<filesystem|volume|snapshot> ...\n"));
        case HELP_SHARE:
                return (gettext("\tshare <-a | filesystem>\n"));
@@ -478,15 +478,18 @@ usage(boolean_t requested)
        exit(requested ? 0 : 2);
 }
 
+/*
+ * Take a property=value argument string and add it to the given nvlist.
+ * Modifies the argument inplace.
+ */
 static int
-parseprop(nvlist_t *props)
+parseprop(nvlist_t *props, char *propname)
 {
-       char *propname = optarg;
        char *propval, *strval;
 
        if ((propval = strchr(propname, '=')) == NULL) {
                (void) fprintf(stderr, gettext("missing "
-                   "'=' for -o option\n"));
+                   "'=' for property=value argument\n"));
                return (-1);
        }
        *propval = '\0';
@@ -576,6 +579,51 @@ finish_progress(char *done)
        pt_header = NULL;
 }
 
+static int
+zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
+{
+       zfs_handle_t *zhp = NULL;
+       int ret = 0;
+
+       zhp = zfs_open(hdl, dataset, type);
+       if (zhp == NULL)
+               return (1);
+
+       /*
+        * Volumes may neither be mounted or shared.  Potentially in the
+        * future filesystems detected on these volumes could be mounted.
+        */
+       if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
+               zfs_close(zhp);
+               return (0);
+       }
+
+       /*
+        * Mount and/or share the new filesystem as appropriate.  We provide a
+        * verbose error message to let the user know that their filesystem was
+        * in fact created, even if we failed to mount or share it.
+        *
+        * If the user doesn't want the dataset automatically mounted, then
+        * skip the mount/share step
+        */
+       if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) &&
+           zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) {
+               if (zfs_mount(zhp, NULL, 0) != 0) {
+                       (void) fprintf(stderr, gettext("filesystem "
+                           "successfully created, but not mounted\n"));
+                       ret = 1;
+               } else if (zfs_share(zhp) != 0) {
+                       (void) fprintf(stderr, gettext("filesystem "
+                           "successfully created, but not shared\n"));
+                       ret = 1;
+               }
+       }
+
+       zfs_close(zhp);
+
+       return (ret);
+}
+
 /*
  * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
  *
@@ -602,7 +650,7 @@ zfs_do_clone(int argc, char **argv)
        while ((c = getopt(argc, argv, "o:p")) != -1) {
                switch (c) {
                case 'o':
-                       if (parseprop(props))
+                       if (parseprop(props, optarg) != 0)
                                return (1);
                        break;
                case 'p':
@@ -657,31 +705,12 @@ zfs_do_clone(int argc, char **argv)
 
        /* create the mountpoint if necessary */
        if (ret == 0) {
-               zfs_handle_t *clone;
-               int canmount = ZFS_CANMOUNT_OFF;
-
                if (log_history) {
                        (void) zpool_log_history(g_zfs, history_str);
                        log_history = B_FALSE;
                }
 
-               clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET);
-               if (clone != NULL) {
-                       /*
-                        * if the user doesn't want the dataset automatically
-                        * mounted, then skip the mount/share step.
-                        */
-                       if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT,
-                           zfs_get_type(clone), B_FALSE))
-                               canmount = zfs_prop_get_int(clone,
-                                   ZFS_PROP_CANMOUNT);
-
-                       if (zfs_get_type(clone) != ZFS_TYPE_VOLUME &&
-                           canmount == ZFS_CANMOUNT_ON)
-                               if ((ret = zfs_mount(clone, NULL, 0)) == 0)
-                                       ret = zfs_share(clone);
-                       zfs_close(clone);
-               }
+               ret = zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
        }
 
        zfs_close(zhp);
@@ -716,7 +745,6 @@ static int
 zfs_do_create(int argc, char **argv)
 {
        zfs_type_t type = ZFS_TYPE_FILESYSTEM;
-       zfs_handle_t *zhp = NULL;
        uint64_t volsize = 0;
        int c;
        boolean_t noreserve = B_FALSE;
@@ -725,7 +753,6 @@ zfs_do_create(int argc, char **argv)
        int ret = 1;
        nvlist_t *props;
        uint64_t intval;
-       int canmount = ZFS_CANMOUNT_OFF;
 
        if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
                nomem();
@@ -765,7 +792,7 @@ zfs_do_create(int argc, char **argv)
                                nomem();
                        break;
                case 'o':
-                       if (parseprop(props))
+                       if (parseprop(props, optarg))
                                goto error;
                        break;
                case 's':
@@ -859,37 +886,8 @@ zfs_do_create(int argc, char **argv)
                log_history = B_FALSE;
        }
 
-       if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
-               goto error;
-
-       ret = 0;
-       /*
-        * if the user doesn't want the dataset automatically mounted,
-        * then skip the mount/share step
-        */
-       if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE))
-               canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
-
-       /*
-        * Mount and/or share the new filesystem as appropriate.  We provide a
-        * verbose error message to let the user know that their filesystem was
-        * in fact created, even if we failed to mount or share it.
-        */
-       if (canmount == ZFS_CANMOUNT_ON) {
-               if (zfs_mount(zhp, NULL, 0) != 0) {
-                       (void) fprintf(stderr, gettext("filesystem "
-                           "successfully created, but not mounted\n"));
-                       ret = 1;
-               } else if (zfs_share(zhp) != 0) {
-                       (void) fprintf(stderr, gettext("filesystem "
-                           "successfully created, but not shared\n"));
-                       ret = 1;
-               }
-       }
-
+       ret = zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);
 error:
-       if (zhp)
-               zfs_close(zhp);
        nvlist_free(props);
        return (ret);
 badusage:
@@ -3477,21 +3475,17 @@ out:
 }
 
 /*
- * zfs set property=value { fs | snap | vol } ...
+ * zfs set property=value ... { fs | snap | vol } ...
  *
- * Sets the given property for all datasets specified on the command line.
+ * Sets the given properties for all datasets specified on the command line.
  */
-typedef struct set_cbdata {
-       char            *cb_propname;
-       char            *cb_value;
-} set_cbdata_t;
 
 static int
 set_callback(zfs_handle_t *zhp, void *data)
 {
-       set_cbdata_t *cbp = data;
+       nvlist_t *props = data;
 
-       if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) {
+       if (zfs_prop_set_list(zhp, props) != 0) {
                switch (libzfs_errno(g_zfs)) {
                case EZFS_MOUNTFAILED:
                        (void) fprintf(stderr, gettext("property may be set "
@@ -3510,8 +3504,10 @@ set_callback(zfs_handle_t *zhp, void *data)
 static int
 zfs_do_set(int argc, char **argv)
 {
-       set_cbdata_t cb;
+       nvlist_t *props = NULL;
+       int ds_start = -1; /* argv idx of first dataset arg */
        int ret = 0;
+       int i;
 
        /* check for options */
        if (argc > 1 && argv[1][0] == '-') {
@@ -3522,36 +3518,51 @@ zfs_do_set(int argc, char **argv)
 
        /* check number of arguments */
        if (argc < 2) {
-               (void) fprintf(stderr, gettext("missing property=value "
-                   "argument\n"));
+               (void) fprintf(stderr, gettext("missing arguments\n"));
                usage(B_FALSE);
        }
        if (argc < 3) {
-               (void) fprintf(stderr, gettext("missing dataset name\n"));
+               if (strchr(argv[1], '=') == NULL) {
+                       (void) fprintf(stderr, gettext("missing property=value "
+                           "argument(s)\n"));
+               } else {
+                       (void) fprintf(stderr, gettext("missing dataset "
+                           "name(s)\n"));
+               }
                usage(B_FALSE);
        }
 
-       /* validate property=value argument */
-       cb.cb_propname = argv[1];
-       if (((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) ||
-           (cb.cb_value[1] == '\0')) {
-               (void) fprintf(stderr, gettext("missing value in "
-                   "property=value argument\n"));
+       /* validate argument order:  prop=val args followed by dataset args */
+       for (i = 1; i < argc; i++) {
+               if (strchr(argv[i], '=') != NULL) {
+                       if (ds_start > 0) {
+                               /* out-of-order prop=val argument */
+                               (void) fprintf(stderr, gettext("invalid "
+                                   "argument order\n"));
+                               usage(B_FALSE);
+                       }
+               } else if (ds_start < 0) {
+                       ds_start = i;
+               }
+       }
+       if (ds_start < 0) {
+               (void) fprintf(stderr, gettext("missing dataset name(s)\n"));
                usage(B_FALSE);
        }
 
-       *cb.cb_value = '\0';
-       cb.cb_value++;
-
-       if (*cb.cb_propname == '\0') {
-               (void) fprintf(stderr,
-                   gettext("missing property in property=value argument\n"));
-               usage(B_FALSE);
+       /* Populate a list of property settings */
+       if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
+               nomem();
+       for (i = 1; i < ds_start; i++) {
+               if ((ret = parseprop(props, argv[i])) != 0)
+                       goto error;
        }
 
-       ret = zfs_for_each(argc - 2, argv + 2, 0,
-           ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
+       ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,
+           ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props);
 
+error:
+       nvlist_free(props);
        return (ret);
 }
 
@@ -3611,7 +3622,7 @@ zfs_do_snapshot(int argc, char **argv)
        while ((c = getopt(argc, argv, "ro:")) != -1) {
                switch (c) {
                case 'o':
-                       if (parseprop(props))
+                       if (parseprop(props, optarg))
                                return (1);
                        break;
                case 'r':
@@ -3683,7 +3694,7 @@ zfs_do_send(int argc, char **argv)
        boolean_t extraverbose = B_FALSE;
 
        /* check options */
-       while ((c = getopt(argc, argv, ":i:I:RDpvnPe")) != -1) {
+       while ((c = getopt(argc, argv, ":i:I:RDpvnPLe")) != -1) {
                switch (c) {
                case 'i':
                        if (fromname)
@@ -3718,6 +3729,9 @@ zfs_do_send(int argc, char **argv)
                case 'n':
                        flags.dryrun = B_TRUE;
                        break;
+               case 'L':
+                       flags.largeblock = B_TRUE;
+                       break;
                case 'e':
                        flags.embed_data = B_TRUE;
                        break;
@@ -3774,6 +3788,8 @@ zfs_do_send(int argc, char **argv)
                if (zhp == NULL)
                        return (1);
 
+               if (flags.largeblock)
+                       lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
                if (flags.embed_data)
                        lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
 
@@ -6703,8 +6719,10 @@ main(int argc, char **argv)
            (strcmp(cmdname, "--help") == 0))
                usage(B_TRUE);
 
-       if ((g_zfs = libzfs_init()) == NULL)
+       if ((g_zfs = libzfs_init()) == NULL) {
+               (void) fprintf(stderr, "%s", libzfs_error_init(errno));
                return (1);
+       }
 
        mnttab_file = g_zfs->libzfs_mnttab;