diff options
author | eschrock <none@none> | 2006-09-05 11:37:36 -0700 |
---|---|---|
committer | eschrock <none@none> | 2006-09-05 11:37:36 -0700 |
commit | e9dbad6f263d5570ed7ff5443ec5b958af8c24d7 (patch) | |
tree | 7bd6d85273a4462fc4699aa9ed69e33bd69e2613 /usr/src/cmd/zfs/zfs_main.c | |
parent | f724721be86f2d808bc08cd6be299acc214e3dc5 (diff) | |
download | illumos-joyent-e9dbad6f263d5570ed7ff5443ec5b958af8c24d7.tar.gz |
PSARC 2006/486 ZFS canmount property
PSARC 2006/497 ZFS create time properties
PSARC 2006/502 ZFS get all datasets
PSARC 2006/504 ZFS user properties
6269805 properties should be set via an nvlist.
6281585 user defined properties
6349494 'zfs list' output annoying for even moderately long dataset names
6366244 'canmount' option for container-like functionality
6367103 create-time properties
6416639 RFE: provide zfs get -a
6437808 ZFS module version should match on-disk version
6454551 'zfs create -b blocksize filesystem' should fail.
6457478 unrecognized character in error message with 'zpool create -R' command
6457865 missing device name in the error message of 'zpool clear' command
6458571 zfs_ioc_set_prop() doesn't validate input
6458614 zfs ACL #defines should use prefix
6458638 get_configs() accesses bogus memory
6458678 zvol functions should be moved out of zfs_ioctl.h
6458683 zfs_cmd_t could use more cleanup
6458691 common routines to manage zfs_cmd_t nvlists
6460398 zpool import cores on zfs_prop_get
6461029 zpool status -x noexisting-pool has incorrect error message.
6461223 index translations should live with property definitions
6461424 zpool_unmount_datasets() has some busted logic
6461427 zfs_realloc() would be useful
6461757 'zpool status' can report the wrong number of persistent errors
6461784 recursive zfs_snapshot() leaks memory
Diffstat (limited to 'usr/src/cmd/zfs/zfs_main.c')
-rw-r--r-- | usr/src/cmd/zfs/zfs_main.c | 706 |
1 files changed, 410 insertions, 296 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index ad9f6e8b07..eb5d4e4b75 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -26,6 +26,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include <assert.h> +#include <ctype.h> #include <errno.h> #include <libgen.h> #include <libintl.h> @@ -161,8 +162,10 @@ get_usage(zfs_help_t idx) case HELP_CLONE: return (gettext("\tclone <snapshot> <filesystem|volume>\n")); case HELP_CREATE: - return (gettext("\tcreate <filesystem>\n" - "\tcreate [-s] [-b blocksize] -V <size> <volume>\n")); + return (gettext("\tcreate [[-o property=value] ... ] " + "<filesystem>\n" + "\tcreate [-s] [-b blocksize] [[-o property=value] ...]\n" + "\t -V <size> <volume>\n")); case HELP_DESTROY: return (gettext("\tdestroy [-rRf] " "<filesystem|volume|snapshot>\n")); @@ -170,7 +173,7 @@ get_usage(zfs_help_t idx) return (gettext("\tget [-rHp] [-o field[,field]...] " "[-s source[,source]...]\n" "\t <all | property[,property]...> " - "<filesystem|volume|snapshot> ...\n")); + "[filesystem|volume|snapshot] ...\n")); case HELP_INHERIT: return (gettext("\tinherit [-r] <property> " "<filesystem|volume> ...\n")); @@ -302,6 +305,8 @@ usage(boolean_t requested) } (void) fprintf(fp, gettext("\nSizes are specified in bytes " "with standard units such as K, M, G, etc.\n")); + (void) fprintf(fp, gettext("\n\nUser-defined properties can " + "be specified by using a name containing a colon (:).\n")); } else { /* * TRANSLATION NOTE: @@ -312,6 +317,14 @@ usage(boolean_t requested) gettext("\nFor the property list, run: zfs set|get\n")); } + /* + * See comments at end of main(). + */ + if (getenv("ZFS_ABORT") != NULL) { + (void) printf("dumping core by request\n"); + abort(); + } + exit(requested ? 0 : 2); } @@ -357,7 +370,7 @@ zfs_do_clone(int argc, char **argv) return (1); /* pass to libzfs */ - ret = zfs_clone(zhp, argv[2]); + ret = zfs_clone(zhp, argv[2], NULL); /* create the mountpoint if necessary */ if (ret == 0) { @@ -375,8 +388,8 @@ zfs_do_clone(int argc, char **argv) } /* - * zfs create fs - * zfs create [-s] [-b blocksize] -V vol size + * zfs create [-o prop=value] ... fs + * zfs create [-s] [-b blocksize] [-o prop=value] ... -V vol size * * Create a new dataset. This command can be used to create filesystems * and volumes. Snapshot creation is handled by 'zfs snapshot'. @@ -390,22 +403,79 @@ static int zfs_do_create(int argc, char **argv) { zfs_type_t type = ZFS_TYPE_FILESYSTEM; - zfs_handle_t *zhp; - char *size = NULL; - char *blocksize = NULL; + zfs_handle_t *zhp = NULL; + uint64_t volsize; int c; boolean_t noreserve = B_FALSE; - int ret; + int ret = 1; + nvlist_t *props = NULL; + uint64_t intval; + char *propname; + char *propval, *strval; + + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { + (void) fprintf(stderr, gettext("internal error: " + "out of memory\n")); + return (1); + } /* check options */ - while ((c = getopt(argc, argv, ":V:b:s")) != -1) { + while ((c = getopt(argc, argv, ":V:b:so:")) != -1) { switch (c) { case 'V': type = ZFS_TYPE_VOLUME; - size = optarg; + if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { + (void) fprintf(stderr, gettext("bad volume " + "size '%s': %s\n"), optarg, + libzfs_error_description(g_zfs)); + goto error; + } + + if (nvlist_add_uint64(props, + zfs_prop_to_name(ZFS_PROP_VOLSIZE), + intval) != 0) { + (void) fprintf(stderr, gettext("internal " + "error: out of memory\n")); + goto error; + } + volsize = intval; break; case 'b': - blocksize = optarg; + if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { + (void) fprintf(stderr, gettext("bad volume " + "block size '%s': %s\n"), optarg, + libzfs_error_description(g_zfs)); + goto error; + } + + if (nvlist_add_uint64(props, + zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), + intval) != 0) { + (void) fprintf(stderr, gettext("internal " + "error: out of memory\n")); + goto error; + } + break; + case 'o': + propname = optarg; + if ((propval = strchr(propname, '=')) == NULL) { + (void) fprintf(stderr, gettext("missing " + "'=' for -o option\n")); + goto error; + } + *propval = '\0'; + propval++; + if (nvlist_lookup_string(props, propname, + &strval) == 0) { + (void) fprintf(stderr, gettext("property '%s' " + "specified multiple times\n"), propname); + goto error; + } + if (nvlist_add_string(props, propname, propval) != 0) { + (void) fprintf(stderr, gettext("internal " + "error: out of memory\n")); + goto error; + } break; case 's': noreserve = B_TRUE; @@ -413,19 +483,19 @@ zfs_do_create(int argc, char **argv) case ':': (void) fprintf(stderr, gettext("missing size " "argument\n")); - usage(B_FALSE); + goto badusage; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); - usage(B_FALSE); + goto badusage; } } if (noreserve && type != ZFS_TYPE_VOLUME) { (void) fprintf(stderr, gettext("'-s' can only be used when " "creating a volume\n")); - usage(B_FALSE); + goto badusage; } argc -= optind; @@ -435,33 +505,32 @@ zfs_do_create(int argc, char **argv) if (argc == 0) { (void) fprintf(stderr, gettext("missing %s argument\n"), zfs_type_to_name(type)); - usage(B_FALSE); + goto badusage; } if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); - usage(B_FALSE); + goto badusage; + } + + if (type == ZFS_TYPE_VOLUME && !noreserve && + nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION), + &strval) != 0) { + if (nvlist_add_uint64(props, + zfs_prop_to_name(ZFS_PROP_RESERVATION), + volsize) != 0) { + (void) fprintf(stderr, gettext("internal " + "error: out of memory\n")); + nvlist_free(props); + return (1); + } } /* pass to libzfs */ - if (zfs_create(g_zfs, argv[0], type, size, blocksize) != 0) - return (1); + if (zfs_create(g_zfs, argv[0], type, props) != 0) + goto error; if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) - return (1); - - /* - * Volume handling. By default, we try to create a reservation of equal - * size for the volume. If we can't do this, then destroy the dataset - * and report an error. - */ - if (type == ZFS_TYPE_VOLUME && !noreserve) { - if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) { - (void) fprintf(stderr, gettext("use '-s' to create a " - "volume without a matching reservation\n")); - (void) zfs_destroy(zhp); - return (1); - } - } + goto error; /* * Mount and/or share the new filesystem as appropriate. We provide a @@ -480,8 +549,15 @@ zfs_do_create(int argc, char **argv) ret = 0; } - zfs_close(zhp); +error: + if (zhp) + zfs_close(zhp); + nvlist_free(props); return (ret); +badusage: + nvlist_free(props); + usage(B_FALSE); + return (2); } /* @@ -765,11 +841,11 @@ zfs_do_destroy(int argc, char **argv) typedef struct get_cbdata { int cb_sources; int cb_columns[4]; - int cb_nprop; + int cb_colwidths[5]; boolean_t cb_scripted; boolean_t cb_literal; - boolean_t cb_isall; - zfs_prop_t cb_prop[ZFS_NPROP_ALL]; + boolean_t cb_first; + zfs_proplist_t *cb_proplist; } get_cbdata_t; #define GET_COL_NAME 1 @@ -778,15 +854,110 @@ typedef struct get_cbdata { #define GET_COL_SOURCE 4 /* + * Print the column headers for 'zfs get'. + */ +static void +print_get_headers(get_cbdata_t *cbp) +{ + zfs_proplist_t *pl = cbp->cb_proplist; + int i; + char *title; + size_t len; + + cbp->cb_first = B_FALSE; + if (cbp->cb_scripted) + return; + + /* + * Start with the length of the column headers. + */ + cbp->cb_colwidths[GET_COL_NAME] = strlen(gettext("NAME")); + cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(gettext("PROPERTY")); + cbp->cb_colwidths[GET_COL_VALUE] = strlen(gettext("VALUE")); + cbp->cb_colwidths[GET_COL_SOURCE] = strlen(gettext("SOURCE")); + + /* + * Go through and calculate the widths for each column. For the + * 'source' column, we kludge it up by taking the worst-case scenario of + * inheriting from the longest name. This is acceptable because in the + * majority of cases 'SOURCE' is the last column displayed, and we don't + * use the width anyway. Note that the 'VALUE' column can be oversized, + * if the name of the property is much longer the any values we find. + */ + for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { + /* + * 'PROPERTY' column + */ + if (pl->pl_prop != ZFS_PROP_INVAL) { + len = strlen(zfs_prop_to_name(pl->pl_prop)); + if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) + cbp->cb_colwidths[GET_COL_PROPERTY] = len; + } else { + len = strlen(pl->pl_user_prop); + if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) + cbp->cb_colwidths[GET_COL_PROPERTY] = len; + } + + /* + * 'VALUE' column + */ + if ((pl->pl_prop != ZFS_PROP_NAME || !pl->pl_all) && + pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE]) + cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width; + + /* + * 'NAME' and 'SOURCE' columns + */ + if (pl->pl_prop == ZFS_PROP_NAME && + pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) { + cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width; + cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width + + strlen(gettext("inherited from")); + } + } + + /* + * Now go through and print the headers. + */ + for (i = 0; i < 4; i++) { + switch (cbp->cb_columns[i]) { + case GET_COL_NAME: + title = gettext("NAME"); + break; + case GET_COL_PROPERTY: + title = gettext("PROPERTY"); + break; + case GET_COL_VALUE: + title = gettext("VALUE"); + break; + case GET_COL_SOURCE: + title = gettext("SOURCE"); + break; + default: + title = NULL; + } + + if (title != NULL) { + if (i == 3 || cbp->cb_columns[i + 1] == 0) + (void) printf("%s", title); + else + (void) printf("%-*s ", + cbp->cb_colwidths[cbp->cb_columns[i]], + title); + } + } + (void) printf("\n"); +} + +/* * Display a single line of output, according to the settings in the callback * structure. */ static void -print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, +print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, const char *propname, const char *value, zfs_source_t sourcetype, const char *source) { int i; - int width; const char *str; char buf[128]; @@ -796,25 +967,24 @@ print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, if ((sourcetype & cbp->cb_sources) == 0) return; + if (cbp->cb_first) + print_get_headers(cbp); + for (i = 0; i < 4; i++) { switch (cbp->cb_columns[i]) { case GET_COL_NAME: - width = 15; str = zfs_get_name(zhp); break; case GET_COL_PROPERTY: - width = 13; - str = zfs_prop_to_name(prop); + str = propname; break; case GET_COL_VALUE: - width = 25; str = value; break; case GET_COL_SOURCE: - width = 15; switch (sourcetype) { case ZFS_SRC_NONE: str = "-"; @@ -849,7 +1019,9 @@ print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, else if (cbp->cb_scripted) (void) printf("%s\t", str); else - (void) printf("%-*s ", width, str); + (void) printf("%-*s ", + cbp->cb_colwidths[cbp->cb_columns[i]], + str); } @@ -866,20 +1038,62 @@ get_callback(zfs_handle_t *zhp, void *data) zfs_source_t sourcetype; char source[ZFS_MAXNAMELEN]; get_cbdata_t *cbp = data; - int i; + nvlist_t *userprop = zfs_get_user_props(zhp); + zfs_proplist_t *pl = cbp->cb_proplist; + nvlist_t *propval; + char *strval; + char *sourceval; - for (i = 0; i < cbp->cb_nprop; i++) { - if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, - sizeof (buf), &sourcetype, source, sizeof (source), - cbp->cb_literal) != 0) { - if (cbp->cb_isall) - continue; - (void) strlcpy(buf, "-", sizeof (buf)); - sourcetype = ZFS_SRC_NONE; - } + for (; pl != NULL; pl = pl->pl_next) { + /* + * Skip the special fake placeholder. This will also skip over + * the name property when 'all' is specified. + */ + if (pl->pl_prop == ZFS_PROP_NAME && + pl == cbp->cb_proplist) + continue; + + if (pl->pl_prop != ZFS_PROP_INVAL) { + if (zfs_prop_get(zhp, pl->pl_prop, buf, + sizeof (buf), &sourcetype, source, + sizeof (source), + cbp->cb_literal) != 0) { + if (pl->pl_all) + continue; + sourcetype = ZFS_SRC_NONE; + (void) strlcpy(buf, "-", sizeof (buf)); + } - print_one_property(zhp, cbp, cbp->cb_prop[i], - buf, sourcetype, source); + print_one_property(zhp, cbp, + zfs_prop_to_name(pl->pl_prop), + buf, sourcetype, source); + } else { + if (nvlist_lookup_nvlist(userprop, + pl->pl_user_prop, &propval) != 0) { + if (pl->pl_all) + continue; + sourcetype = ZFS_SRC_NONE; + strval = "-"; + } else { + verify(nvlist_lookup_string(propval, + ZFS_PROP_VALUE, &strval) == 0); + verify(nvlist_lookup_string(propval, + ZFS_PROP_SOURCE, &sourceval) == 0); + + if (strcmp(sourceval, + zfs_get_name(zhp)) == 0) { + sourcetype = ZFS_SRC_LOCAL; + } else { + sourcetype = ZFS_SRC_INHERITED; + (void) strlcpy(source, + sourceval, sizeof (source)); + } + } + + print_one_property(zhp, cbp, + pl->pl_user_prop, strval, sourcetype, + source); + } } return (0); @@ -890,10 +1104,10 @@ zfs_do_get(int argc, char **argv) { get_cbdata_t cb = { 0 }; boolean_t recurse = B_FALSE; - int c; - char *value, *fields, *badopt; - int i; + int i, c; + char *value, *fields; int ret; + zfs_proplist_t fake_name = { 0 }; /* * Set up default columns and sources. @@ -1014,62 +1228,39 @@ zfs_do_get(int argc, char **argv) fields = argv[0]; - /* - * If the user specifies 'all', the behavior of 'zfs get' is slightly - * different, because we don't show properties which don't apply to the - * given dataset. - */ - if (strcmp(fields, "all") == 0) - cb.cb_isall = B_TRUE; - - if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL, - &cb.cb_nprop, &badopt)) != 0) { - if (ret == EINVAL) - (void) fprintf(stderr, gettext("invalid property " - "'%s'\n"), badopt); - else - (void) fprintf(stderr, gettext("too many properties " - "specified\n")); + if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) usage(B_FALSE); - } argc--; argv++; - /* check for at least one dataset name */ - if (argc < 1) { - (void) fprintf(stderr, gettext("missing dataset argument\n")); - usage(B_FALSE); - } - /* - * Print out any headers + * As part of zfs_expand_proplist(), we keep track of the maximum column + * width for each property. For the 'NAME' (and 'SOURCE') columns, we + * need to know the maximum name length. However, the user likely did + * not specify 'name' as one of the properties to fetch, so we need to + * make sure we always include at least this property for + * print_get_headers() to work properly. */ - if (!cb.cb_scripted) { - int i; - for (i = 0; i < 4; i++) { - switch (cb.cb_columns[i]) { - case GET_COL_NAME: - (void) printf("%-15s ", "NAME"); - break; - case GET_COL_PROPERTY: - (void) printf("%-13s ", "PROPERTY"); - break; - case GET_COL_VALUE: - (void) printf("%-25s ", "VALUE"); - break; - case GET_COL_SOURCE: - (void) printf("%s", "SOURCE"); - break; - } - } - (void) printf("\n"); + if (cb.cb_proplist != NULL) { + fake_name.pl_prop = ZFS_PROP_NAME; + fake_name.pl_width = strlen(gettext("NAME")); + fake_name.pl_next = cb.cb_proplist; + cb.cb_proplist = &fake_name; } + cb.cb_first = B_TRUE; + /* run for each object */ - return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, - get_callback, &cb)); + ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, + &cb.cb_proplist, get_callback, &cb); + + if (cb.cb_proplist == &fake_name) + zfs_free_proplist(fake_name.pl_next); + else + zfs_free_proplist(cb.cb_proplist); + return (ret); } /* @@ -1086,9 +1277,7 @@ zfs_do_get(int argc, char **argv) static int inherit_callback(zfs_handle_t *zhp, void *data) { - zfs_prop_t prop = (zfs_prop_t)data; - - return (zfs_prop_inherit(zhp, prop) != 0); + return (zfs_prop_inherit(zhp, data) != 0); } static int @@ -1127,33 +1316,35 @@ zfs_do_inherit(int argc, char **argv) } propname = argv[0]; + argc--; + argv++; - /* - * Get and validate the property before iterating over the datasets. We - * do this now so as to avoid printing out an error message for each and - * every dataset. - */ - if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { - (void) fprintf(stderr, gettext("invalid property '%s'\n"), + if ((prop = zfs_name_to_prop(propname)) != ZFS_PROP_INVAL) { + if (zfs_prop_readonly(prop)) { + (void) fprintf(stderr, gettext( + "%s property is read-only\n"), + propname); + return (1); + } + if (!zfs_prop_inheritable(prop)) { + (void) fprintf(stderr, gettext("'%s' property cannot " + "be inherited\n"), propname); + if (prop == ZFS_PROP_QUOTA || + prop == ZFS_PROP_RESERVATION) + (void) fprintf(stderr, gettext("use 'zfs set " + "%s=none' to clear\n"), propname); + return (1); + } + } else if (!zfs_prop_user(propname)) { + (void) fprintf(stderr, gettext( + "invalid property '%s'\n"), propname); usage(B_FALSE); } - if (zfs_prop_readonly(prop)) { - (void) fprintf(stderr, gettext("%s property is read-only\n"), - propname); - return (1); - } - if (!zfs_prop_inheritable(prop)) { - (void) fprintf(stderr, gettext("%s property cannot be " - "inherited\n"), propname); - (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " - "clear\n"), propname); - return (1); - } - return (zfs_for_each(argc - 1, argv + 1, recurse, - ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, - inherit_callback, (void *)prop)); + return (zfs_for_each(argc, argv, recurse, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, + inherit_callback, propname)); } /* @@ -1175,26 +1366,45 @@ zfs_do_inherit(int argc, char **argv) typedef struct list_cbdata { boolean_t cb_first; boolean_t cb_scripted; - zfs_prop_t cb_fields[ZFS_NPROP_ALL]; - int cb_fieldcount; + zfs_proplist_t *cb_proplist; } list_cbdata_t; /* * Given a list of columns to display, output appropriate headers for each one. */ static void -print_header(zfs_prop_t *fields, size_t count) +print_header(zfs_proplist_t *pl) { + char headerbuf[ZFS_MAXPROPLEN]; + const char *header; int i; + boolean_t first = B_TRUE; + boolean_t right_justify; - for (i = 0; i < count; i++) { - if (i != 0) + for (; pl != NULL; pl = pl->pl_next) { + if (!first) { (void) printf(" "); - if (i == count - 1) - (void) printf("%s", zfs_prop_column_name(fields[i])); - else /* LINTED - format specifier */ - (void) printf(zfs_prop_column_format(fields[i]), - zfs_prop_column_name(fields[i])); + } else { + first = B_FALSE; + } + + right_justify = B_FALSE; + if (pl->pl_prop != ZFS_PROP_INVAL) { + header = zfs_prop_column_name(pl->pl_prop); + right_justify = zfs_prop_align_right(pl->pl_prop); + } else { + for (i = 0; pl->pl_user_prop[i] != '\0'; i++) + headerbuf[i] = toupper(pl->pl_user_prop[i]); + headerbuf[i] = '\0'; + header = headerbuf; + } + + if (pl->pl_next == NULL && !right_justify) + (void) printf("%s", header); + else if (right_justify) + (void) printf("%*s", pl->pl_width, header); + else + (void) printf("%-*s", pl->pl_width, header); } (void) printf("\n"); @@ -1205,34 +1415,57 @@ print_header(zfs_prop_t *fields, size_t count) * to the described layout. */ static void -print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted) +print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted) { - int i; + boolean_t first = B_TRUE; char property[ZFS_MAXPROPLEN]; + nvlist_t *userprops = zfs_get_user_props(zhp); + nvlist_t *propval; + char *propstr; + boolean_t right_justify; + int width; - for (i = 0; i < count; i++) { - if (i != 0) { + for (; pl != NULL; pl = pl->pl_next) { + if (!first) { if (scripted) (void) printf("\t"); else (void) printf(" "); + } else { + first = B_FALSE; } - if (zfs_prop_get(zhp, fields[i], property, - sizeof (property), NULL, NULL, 0, B_FALSE) != 0) - (void) strlcpy(property, "-", sizeof (property)); + right_justify = B_FALSE; + if (pl->pl_prop != ZFS_PROP_INVAL) { + if (zfs_prop_get(zhp, pl->pl_prop, property, + sizeof (property), NULL, NULL, 0, B_FALSE) != 0) + propstr = "-"; + else + propstr = property; + + right_justify = zfs_prop_align_right(pl->pl_prop); + } else { + if (nvlist_lookup_nvlist(userprops, + pl->pl_user_prop, &propval) != 0) + propstr = "-"; + else + verify(nvlist_lookup_string(propval, + ZFS_PROP_VALUE, &propstr) == 0); + } + + width = pl->pl_width; /* * If this is being called in scripted mode, or if this is the * last column and it is left-justified, don't include a width * format specifier. */ - if (scripted || (i == count - 1 && - strchr(zfs_prop_column_format(fields[i]), '-') != NULL)) - (void) printf("%s", property); - else /* LINTED - format specifier */ - (void) printf(zfs_prop_column_format(fields[i]), - property); + if (scripted || (pl->pl_next == NULL && !right_justify)) + (void) printf("%s", propstr); + else if (right_justify) + (void) printf("%*s", width, propstr); + else + (void) printf("%-*s", width, propstr); } (void) printf("\n"); @@ -1248,12 +1481,11 @@ list_callback(zfs_handle_t *zhp, void *data) if (cbp->cb_first) { if (!cbp->cb_scripted) - print_header(cbp->cb_fields, cbp->cb_fieldcount); + print_header(cbp->cb_proplist); cbp->cb_first = B_FALSE; } - print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, - cbp->cb_scripted); + print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted); return (0); } @@ -1273,14 +1505,10 @@ zfs_do_list(int argc, char **argv) char *value; int ret; char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; - char *badopt; - int alloffset; zfs_sort_column_t *sortcol = NULL; /* check options */ while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { - zfs_prop_t prop; - switch (c) { case 'o': fields = optarg; @@ -1292,22 +1520,20 @@ zfs_do_list(int argc, char **argv) scripted = B_TRUE; break; case 's': - if ((prop = zfs_name_to_prop(optarg)) == - ZFS_PROP_INVAL) { + if (zfs_add_sort_column(&sortcol, optarg, + B_FALSE) != 0) { (void) fprintf(stderr, gettext("invalid property '%s'\n"), optarg); usage(B_FALSE); } - zfs_add_sort_column(&sortcol, prop, B_FALSE); break; case 'S': - if ((prop = zfs_name_to_prop(optarg)) == - ZFS_PROP_INVAL) { + if (zfs_add_sort_column(&sortcol, optarg, + B_TRUE) != 0) { (void) fprintf(stderr, gettext("invalid property '%s'\n"), optarg); usage(B_FALSE); } - zfs_add_sort_column(&sortcol, prop, B_TRUE); break; case 't': types = 0; @@ -1354,31 +1580,16 @@ zfs_do_list(int argc, char **argv) * normally include the name of the dataset. For 'zfs list', we always * want this property to be first. */ - if (strcmp(fields, "all") == 0) { - cb.cb_fields[0] = ZFS_PROP_NAME; - alloffset = 1; - } else { - alloffset = 0; - } - - if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset, - ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) { - if (ret == EINVAL) - (void) fprintf(stderr, gettext("invalid property " - "'%s'\n"), badopt); - else - (void) fprintf(stderr, gettext("too many properties " - "specified\n")); + if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) usage(B_FALSE); - } - cb.cb_fieldcount += alloffset; cb.cb_scripted = scripted; cb.cb_first = B_TRUE; - ret = zfs_for_each(argc, argv, recurse, types, sortcol, + ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist, list_callback, &cb); + zfs_free_proplist(cb.cb_proplist); zfs_free_sort_columns(sortcol); if (ret == 0 && cb.cb_first) @@ -1652,7 +1863,6 @@ out: typedef struct set_cbdata { char *cb_propname; char *cb_value; - zfs_prop_t cb_prop; } set_cbdata_t; static int @@ -1661,92 +1871,7 @@ set_callback(zfs_handle_t *zhp, void *data) set_cbdata_t *cbp = data; int ret = 1; - /* don't allow setting of properties for snapshots */ - if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { - (void) fprintf(stderr, gettext("cannot set %s property for " - "'%s': snapshot properties cannot be modified\n"), - cbp->cb_propname, zfs_get_name(zhp)); - return (1); - } - - /* - * If we're changing the volsize, make sure the value is appropriate, - * and set the reservation if this is a non-sparse volume. - */ - if (cbp->cb_prop == ZFS_PROP_VOLSIZE && - zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { - uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); - uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); - uint64_t reservation = zfs_prop_get_int(zhp, - ZFS_PROP_RESERVATION); - uint64_t blocksize = zfs_prop_get_int(zhp, - ZFS_PROP_VOLBLOCKSIZE); - uint64_t value; - - verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); - - if (value % blocksize != 0) { - char buf[64]; - - zfs_nicenum(blocksize, buf, sizeof (buf)); - (void) fprintf(stderr, gettext("cannot set %s for " - "'%s': must be a multiple of volume block size " - "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp), - buf); - return (1); - } - - if (value == 0) { - (void) fprintf(stderr, gettext("cannot set %s for " - "'%s': cannot be zero\n"), cbp->cb_propname, - zfs_get_name(zhp)); - return (1); - } - - if (volsize == reservation) { - if (value > volsize && (value - volsize) > avail) { - (void) fprintf(stderr, gettext("cannot set " - "%s property for '%s': volume size exceeds " - "amount of available space\n"), - cbp->cb_propname, zfs_get_name(zhp)); - return (1); - } - - if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, - cbp->cb_value) != 0) { - (void) fprintf(stderr, gettext("volsize and " - "reservation must remain equal\n")); - return (1); - } - } - } - - /* - * Do not allow the reservation to be set above the volume size. We do - * this here instead of inside libzfs because libzfs violates this rule - * internally. - */ - if (cbp->cb_prop == ZFS_PROP_RESERVATION && - zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { - uint64_t value; - uint64_t volsize; - - volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); - if (strcmp(cbp->cb_value, "none") == 0) - value = 0; - else - verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); - - if (value > volsize) { - (void) fprintf(stderr, gettext("cannot set %s " - "for '%s': size is greater than current " - "volume size\n"), cbp->cb_propname, - zfs_get_name(zhp)); - return (-1); - } - } - - if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) { + if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { switch (libzfs_errno(g_zfs)) { case EZFS_MOUNTFAILED: (void) fprintf(stderr, gettext("property may be set " @@ -1803,30 +1928,9 @@ zfs_do_set(int argc, char **argv) gettext("missing property in property=value argument\n")); usage(B_FALSE); } - if (*cb.cb_value == '\0') { - (void) fprintf(stderr, - gettext("missing value in property=value argument\n")); - usage(B_FALSE); - } - - /* get the property type */ - if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == - ZFS_PROP_INVAL) { - (void) fprintf(stderr, - gettext("invalid property '%s'\n"), cb.cb_propname); - usage(B_FALSE); - } - - /* - * Validate that the value is appropriate for this property. We do this - * once now so we don't generate multiple errors each time we try to - * apply it to a dataset. - */ - if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0) - return (1); return (zfs_for_each(argc - 2, argv + 2, B_FALSE, - ZFS_TYPE_ANY, NULL, set_callback, &cb)); + ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb)); } /* @@ -2125,7 +2229,7 @@ share_mount_callback(zfs_handle_t *zhp, void *data) share_mount_cbdata_t *cbp = data; const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; struct mnttab mnt; - uint64_t zoned; + uint64_t zoned, canmount; if (cbp->cb_options == NULL) mnt.mnt_mntopts = ""; @@ -2165,6 +2269,7 @@ share_mount_callback(zfs_handle_t *zhp, void *data) sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); + canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); if (cbp->cb_type == OP_SHARE) { if (strcmp(shareopts, "off") == 0) { @@ -2205,6 +2310,15 @@ share_mount_callback(zfs_handle_t *zhp, void *data) return (1); } + if (!canmount) { + if (!cbp->cb_explicit) + return (0); + + (void) fprintf(stderr, gettext("cannot %s '%s': 'canmount' " + "property is set to 'off'\n"), cmdname, zfs_get_name(zhp)); + return (1); + } + /* * At this point, we have verified that the mountpoint and/or shareopts * are appropriate for auto management. Determine if the filesystem is |