diff options
Diffstat (limited to 'usr/src')
34 files changed, 2583 insertions, 1559 deletions
diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index 66f9a00daa..1963879b94 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -901,14 +901,6 @@ const struct ioc { "zfs_cmd_t" }, { (uint_t)ZFS_IOC_SET_PROP, "ZFS_IOC_SET_PROP", "zfs_cmd_t" }, - { (uint_t)ZFS_IOC_SET_QUOTA, "ZFS_IOC_SET_QUOTA", - "zfs_cmd_t" }, - { (uint_t)ZFS_IOC_SET_RESERVATION, "ZFS_IOC_SET_RESERVATION", - "zfs_cmd_t" }, - { (uint_t)ZFS_IOC_SET_VOLSIZE, "ZFS_IOC_SET_VOLSIZE", - "zfs_cmd_t" }, - { (uint_t)ZFS_IOC_SET_VOLBLOCKSIZE, "ZFS_IOC_SET_VOLBLOCKSIZE", - "zfs_cmd_t" }, { (uint_t)ZFS_IOC_CREATE_MINOR, "ZFS_IOC_CREATE_MINOR", "zfs_cmd_t" }, { (uint_t)ZFS_IOC_REMOVE_MINOR, "ZFS_IOC_REMOVE_MINOR", diff --git a/usr/src/cmd/zfs/Makefile b/usr/src/cmd/zfs/Makefile index 3a80b1c77e..9b80822041 100644 --- a/usr/src/cmd/zfs/Makefile +++ b/usr/src/cmd/zfs/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -39,7 +38,7 @@ LINKPROGS= mount umount ROOTETCFSTYPE= $(ROOTETC)/fs/$(FSTYPE) USRLIBFSTYPE= $(ROOTLIB)/fs/$(FSTYPE) -LDLIBS += -lzfs -luutil -lumem +LDLIBS += -lzfs -luutil -lumem -lnvpair C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zfs/zfs_iter.c b/usr/src/cmd/zfs/zfs_iter.c index 814a165393..c2d4260d8f 100644 --- a/usr/src/cmd/zfs/zfs_iter.c +++ b/usr/src/cmd/zfs/zfs_iter.c @@ -59,6 +59,7 @@ typedef struct callback_data { int cb_recurse; zfs_type_t cb_types; zfs_sort_column_t *cb_sortcol; + zfs_proplist_t **cb_proplist; } callback_data_t; uu_avl_pool_t *avl_pool; @@ -84,6 +85,11 @@ zfs_callback(zfs_handle_t *zhp, void *data) uu_avl_node_init(node, &node->zn_avlnode, avl_pool); if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, &idx) == NULL) { + if (cb->cb_proplist && + zfs_expand_proplist(zhp, cb->cb_proplist) != 0) { + free(node); + return (-1); + } uu_avl_insert(cb->cb_avl, node, idx); dontclose = 1; } else { @@ -105,17 +111,25 @@ zfs_callback(zfs_handle_t *zhp, void *data) return (0); } -void -zfs_add_sort_column(zfs_sort_column_t **sc, zfs_prop_t prop, +int +zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, boolean_t reverse) { zfs_sort_column_t *col; + zfs_prop_t prop; + + if ((prop = zfs_name_to_prop(name)) == ZFS_PROP_INVAL && + !zfs_prop_user(name)) + return (-1); col = safe_malloc(sizeof (zfs_sort_column_t)); col->sc_prop = prop; col->sc_reverse = reverse; - col->sc_next = NULL; + if (prop == ZFS_PROP_INVAL) { + col->sc_user_prop = safe_malloc(strlen(name) + 1); + (void) strcpy(col->sc_user_prop, name); + } if (*sc == NULL) { col->sc_last = col; @@ -124,6 +138,8 @@ zfs_add_sort_column(zfs_sort_column_t **sc, zfs_prop_t prop, (*sc)->sc_last->sc_next = col; (*sc)->sc_last = col; } + + return (0); } void @@ -133,6 +149,7 @@ zfs_free_sort_columns(zfs_sort_column_t *sc) while (sc != NULL) { col = sc->sc_next; + free(sc->sc_user_prop); free(sc); sc = col; } @@ -214,48 +231,72 @@ zfs_sort(const void *larg, const void *rarg, void *data) zfs_sort_column_t *psc; for (psc = sc; psc != NULL; psc = psc->sc_next) { - char lstr[ZFS_MAXPROPLEN], rstr[ZFS_MAXPROPLEN]; + char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; + char *lstr, *rstr; uint64_t lnum, rnum; - int lvalid, rvalid; + boolean_t lvalid, rvalid; int ret = 0; - if (zfs_prop_is_string(psc->sc_prop)) { - lvalid = zfs_prop_get(l, psc->sc_prop, lstr, - sizeof (lstr), NULL, NULL, 0, B_TRUE); - rvalid = zfs_prop_get(r, psc->sc_prop, rstr, - sizeof (rstr), NULL, NULL, 0, B_TRUE); - - if ((lvalid == -1) && (rvalid == -1)) - continue; - if (lvalid == -1) - return (1); - else if (rvalid == -1) - return (-1); - - ret = strcmp(lstr, rstr); + /* + * We group the checks below the generic code. If 'lstr' and + * 'rstr' are non-NULL, then we do a string based comparison. + * Otherwise, we compare 'lnum' and 'rnum'. + */ + lstr = rstr = NULL; + if (psc->sc_prop == ZFS_PROP_INVAL) { + nvlist_t *luser, *ruser; + nvlist_t *lval, *rval; + + luser = zfs_get_user_props(l); + ruser = zfs_get_user_props(r); + + lvalid = (nvlist_lookup_nvlist(luser, + psc->sc_user_prop, &lval) == 0); + rvalid = (nvlist_lookup_nvlist(ruser, + psc->sc_user_prop, &rval) == 0); + + if (lvalid) + verify(nvlist_lookup_string(lval, + ZFS_PROP_VALUE, &lstr) == 0); + if (rvalid) + verify(nvlist_lookup_string(rval, + ZFS_PROP_VALUE, &rstr) == 0); + + } else if (zfs_prop_is_string(psc->sc_prop)) { + lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, + sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); + rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, + sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); + + lstr = lbuf; + rstr = rbuf; } else { lvalid = zfs_prop_valid_for_type(psc->sc_prop, zfs_get_type(l)); rvalid = zfs_prop_valid_for_type(psc->sc_prop, zfs_get_type(r)); - if (!lvalid && !rvalid) - continue; - else if (!lvalid) - return (1); - else if (!rvalid) - return (-1); + if (lvalid) + (void) zfs_prop_get_numeric(l, psc->sc_prop, + &lnum, NULL, NULL, 0); + if (rvalid) + (void) zfs_prop_get_numeric(r, psc->sc_prop, + &rnum, NULL, NULL, 0); + } - (void) zfs_prop_get_numeric(l, psc->sc_prop, &lnum, - NULL, NULL, 0); - (void) zfs_prop_get_numeric(r, psc->sc_prop, &rnum, - NULL, NULL, 0); + if (!lvalid && !rvalid) + continue; + else if (!lvalid) + return (1); + else if (!rvalid) + return (-1); - if (lnum < rnum) - ret = -1; - else if (lnum > rnum) - ret = 1; - } + if (lstr) + ret = strcmp(lstr, rstr); + if (lnum < rnum) + ret = -1; + else if (lnum > rnum) + ret = 1; if (ret != 0) { if (psc->sc_reverse == B_TRUE) @@ -269,7 +310,8 @@ zfs_sort(const void *larg, const void *rarg, void *data) int zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, - zfs_sort_column_t *sortcol, zfs_iter_f callback, void *data) + zfs_sort_column_t *sortcol, zfs_proplist_t **proplist, zfs_iter_f callback, + void *data) { callback_data_t cb; int ret = 0; @@ -287,6 +329,7 @@ zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, cb.cb_sortcol = sortcol; cb.cb_recurse = recurse; + cb.cb_proplist = proplist; cb.cb_types = types; if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { (void) fprintf(stderr, diff --git a/usr/src/cmd/zfs/zfs_iter.h b/usr/src/cmd/zfs/zfs_iter.h index de734e8d4e..d5c735d604 100644 --- a/usr/src/cmd/zfs/zfs_iter.h +++ b/usr/src/cmd/zfs/zfs_iter.h @@ -36,12 +36,13 @@ typedef struct zfs_sort_column { struct zfs_sort_column *sc_next; struct zfs_sort_column *sc_last; zfs_prop_t sc_prop; + char *sc_user_prop; boolean_t sc_reverse; } zfs_sort_column_t; int zfs_for_each(int, char **, boolean_t, zfs_type_t, zfs_sort_column_t *, - zfs_iter_f, void *); -void zfs_add_sort_column(zfs_sort_column_t **, zfs_prop_t, boolean_t); + zfs_proplist_t **, zfs_iter_f, void *); +int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t); void zfs_free_sort_columns(zfs_sort_column_t *); #ifdef __cplusplus 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 diff --git a/usr/src/cmd/zoneadm/Makefile b/usr/src/cmd/zoneadm/Makefile index bf4e21d47e..cbe135d379 100644 --- a/usr/src/cmd/zoneadm/Makefile +++ b/usr/src/cmd/zoneadm/Makefile @@ -43,7 +43,7 @@ SRCS = $(OBJS:.o=.c) POFILE=zoneadm_all.po POFILES= $(OBJS:%.o=%.po) -LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs -luuid +LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs -luuid -lnvpair .KEEP_STATE: diff --git a/usr/src/cmd/zoneadm/zfs.c b/usr/src/cmd/zoneadm/zfs.c index 1fe567b218..dfb17534a9 100644 --- a/usr/src/cmd/zoneadm/zfs.c +++ b/usr/src/cmd/zoneadm/zfs.c @@ -407,14 +407,27 @@ clone_snap(char *snapshot_name, char *zonepath) int err; zfs_handle_t *zhp; zfs_handle_t *clone; + nvlist_t *props = NULL; if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL) return (Z_NO_ENTRY); (void) printf(gettext("Cloning snapshot %s\n"), snapshot_name); - err = zfs_clone(zhp, zonepath); + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_boolean_value(props, + zfs_prop_to_name(ZFS_PROP_SHARENFS), B_FALSE) != 0) { + nvlist_free(props); + (void) fprintf(stderr, gettext("could not create ZFS clone " + "%s: out of memory\n"), zonepath); + return (Z_ERR); + } + + err = zfs_clone(zhp, zonepath, props); zfs_close(zhp); + + nvlist_free(props); + if (err != 0) return (Z_ERR); @@ -431,20 +444,11 @@ clone_snap(char *snapshot_name, char *zonepath) "%s\n"), zfs_get_name(clone)); res = Z_ERR; - } else { - if (zfs_prop_set(clone, ZFS_PROP_SHARENFS, "off") != 0) { - /* we won't consider this a failure */ - (void) fprintf(stderr, gettext("could not turn off the " - "'sharenfs' property on ZFS clone %s\n"), - zfs_get_name(clone)); - } - - if (clean_out_clone() != Z_OK) { - (void) fprintf(stderr, gettext("could not remove the " - "software inventory from ZFS clone %s\n"), - zfs_get_name(clone)); - res = Z_ERR; - } + } else if (clean_out_clone() != Z_OK) { + (void) fprintf(stderr, gettext("could not remove the " + "software inventory from ZFS clone %s\n"), + zfs_get_name(clone)); + res = Z_ERR; } zfs_close(clone); @@ -700,25 +704,33 @@ create_zfs_zonepath(char *zonepath) { zfs_handle_t *zhp; char zfs_name[MAXPATHLEN]; + nvlist_t *props = NULL; if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK) return; - if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0 || + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_boolean_value(props, zfs_prop_to_name(ZFS_PROP_SHARENFS), + B_FALSE) != 0) { + nvlist_free(props); + (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: " + "out of memory\n"), zfs_name); + } + + if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 || (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_ANY)) == NULL) { (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: " "%s\n"), zfs_name, libzfs_error_description(g_zfs)); + nvlist_free(props); return; } + nvlist_free(props); + if (zfs_mount(zhp, NULL, 0) != 0) { (void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: " "%s\n"), zfs_name, libzfs_error_description(g_zfs)); (void) zfs_destroy(zhp); - } else if (zfs_prop_set(zhp, ZFS_PROP_SHARENFS, "off") != 0) { - (void) fprintf(stderr, gettext("file system %s successfully " - "created,\nbut could not turn off the 'sharenfs' " - "property\n"), zfs_name); } else { if (chmod(zonepath, S_IRWXU) != 0) { (void) fprintf(stderr, gettext("file system %s " @@ -849,7 +861,8 @@ move_zfs(char *zonepath, char *new_zonepath) if ((zhp = mount2zhandle(zonepath)) == NULL) return (Z_ERR); - if (zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, new_zonepath) == 0) { + if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), + new_zonepath) == 0) { /* * Clean up the old mount point. We ignore any failure since * the zone is already successfully mounted on the new path. diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 3bbaea3c15..88b79de7db 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -2605,7 +2605,8 @@ validate_datasets(zlog_t *zlogp) * first because we'll get EPERM if it is already set. */ if (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && - zfs_prop_set(zhp, ZFS_PROP_ZONED, "on") != 0) { + zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_ZONED), + "on") != 0) { zerror(zlogp, B_FALSE, "cannot set 'zoned' " "property for ZFS dataset '%s'\n", dstab.zone_dataset_name); diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 97e82dab46..bb175c9306 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -291,6 +291,14 @@ usage(boolean_t requested) } } + /* + * See comments at end of main(). + */ + if (getenv("ZFS_ABORT") != NULL) { + (void) printf("dumping core by request\n"); + abort(); + } + exit(requested ? 0 : 2); } @@ -572,7 +580,7 @@ zpool_do_create(int argc, char **argv) if (altroot != NULL && altroot[0] != '/') { (void) fprintf(stderr, gettext("invalid alternate root '%s': " - "must be an absolute path\n")); + "must be an absolute path\n"), altroot); nvlist_free(nvroot); return (1); } @@ -651,7 +659,8 @@ zpool_do_create(int argc, char **argv) if (pool != NULL) { if (mountpoint != NULL) verify(zfs_prop_set(pool, - ZFS_PROP_MOUNTPOINT, + zfs_prop_to_name( + ZFS_PROP_MOUNTPOINT), mountpoint) == 0); if (zfs_mount(pool, NULL, 0) == 0) ret = zfs_share(pool); @@ -2362,6 +2371,7 @@ zpool_do_scrub(int argc, char **argv) typedef struct status_cbdata { int cb_count; + boolean_t cb_allpools; boolean_t cb_verbose; boolean_t cb_explain; boolean_t cb_first; @@ -2685,8 +2695,15 @@ status_callback(zpool_handle_t *zhp, void *data) * If we were given 'zpool status -x', only report those pools with * problems. */ - if (reason == ZPOOL_STATUS_OK && cbp->cb_explain) + if (reason == ZPOOL_STATUS_OK && cbp->cb_explain) { + if (!cbp->cb_allpools) { + (void) printf(gettext("pool '%s' is healthy\n"), + zpool_get_name(zhp)); + if (cbp->cb_first) + cbp->cb_first = B_FALSE; + } return (0); + } if (cbp->cb_first) cbp->cb_first = B_FALSE; @@ -2852,8 +2869,8 @@ status_callback(zpool_handle_t *zhp, void *data) (void) printf(gettext("errors: No known data " "errors\n")); else if (!cbp->cb_verbose) - (void) printf(gettext("errors: %d data errors, " - "use '-v' for a list\n"), nerr); + (void) printf(gettext("errors: %llu data " + "errors, use '-v' for a list\n"), nerr); else print_error_log(zhp); } @@ -2901,20 +2918,15 @@ zpool_do_status(int argc, char **argv) cb.cb_first = B_TRUE; + if (argc == 0) + cb.cb_allpools = B_TRUE; + ret = for_each_pool(argc, argv, B_TRUE, status_callback, &cb); if (argc == 0 && cb.cb_count == 0) (void) printf(gettext("no pools available\n")); - else if (cb.cb_explain && cb.cb_first) { - if (argc == 0) { - (void) printf(gettext("all pools are healthy\n")); - } else { - int i; - for (i = 0; i < argc; i++) - (void) printf(gettext("pool '%s' is healthy\n"), - argv[i]); - } - } + else if (cb.cb_explain && cb.cb_first && cb.cb_allpools) + (void) printf(gettext("all pools are healthy\n")); return (ret); } diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c index fe9e11019f..8b2e7c3f93 100644 --- a/usr/src/common/zfs/zfs_prop.c +++ b/usr/src/common/zfs/zfs_prop.c @@ -77,86 +77,89 @@ typedef struct { int pd_types; const char *pd_values; const char *pd_colname; - const char *pd_colfmt; + boolean_t pd_rightalign; } prop_desc_t; static prop_desc_t zfs_prop_table[ZFS_NPROP_ALL] = { { "type", prop_type_string, 0, NULL, prop_readonly, - ZFS_TYPE_ANY, "filesystem | volume | snapshot", "TYPE", "%10s" }, + ZFS_TYPE_ANY, "filesystem | volume | snapshot", "TYPE", B_TRUE }, { "creation", prop_type_number, 0, NULL, prop_readonly, - ZFS_TYPE_ANY, "<date>", "CREATION", "%-20s" }, + ZFS_TYPE_ANY, "<date>", "CREATION", B_FALSE }, { "used", prop_type_number, 0, NULL, prop_readonly, - ZFS_TYPE_ANY, "<size>", "USED", "%5s" }, + ZFS_TYPE_ANY, "<size>", "USED", B_TRUE }, { "available", prop_type_number, 0, NULL, prop_readonly, - ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL", "%5s" }, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL", B_TRUE }, { "referenced", prop_type_number, 0, NULL, prop_readonly, ZFS_TYPE_ANY, - "<size>", "REFER", "%5s" }, + "<size>", "REFER", B_TRUE }, { "compressratio", prop_type_number, 0, NULL, prop_readonly, - ZFS_TYPE_ANY, "<1.00x or higher if compressed>", "RATIO", "%5s" }, + ZFS_TYPE_ANY, "<1.00x or higher if compressed>", "RATIO", B_TRUE }, { "mounted", prop_type_boolean, 0, NULL, prop_readonly, - ZFS_TYPE_FILESYSTEM, "yes | no | -", "MOUNTED", "%7s" }, + ZFS_TYPE_FILESYSTEM, "yes | no | -", "MOUNTED", B_TRUE }, { "origin", prop_type_string, 0, NULL, prop_readonly, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<snapshot>", "ORIGIN", - "%-20s" }, + B_FALSE }, { "quota", prop_type_number, 0, NULL, prop_default, - ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA", "%5s" }, + ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA", B_TRUE }, { "reservation", prop_type_number, 0, NULL, prop_default, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, - "<size> | none", "RESERV", "%6s" }, + "<size> | none", "RESERV", B_TRUE }, { "volsize", prop_type_number, 0, NULL, prop_default, - ZFS_TYPE_VOLUME, "<size>", "VOLSIZE", "%7s" }, + ZFS_TYPE_VOLUME, "<size>", "VOLSIZE", B_TRUE }, { "volblocksize", prop_type_number, 8192, NULL, prop_readonly, - ZFS_TYPE_VOLUME, "512 to 128k, power of 2", "VOLBLOCK", "%8s" }, + ZFS_TYPE_VOLUME, "512 to 128k, power of 2", "VOLBLOCK", B_TRUE }, { "recordsize", prop_type_number, SPA_MAXBLOCKSIZE, NULL, prop_inherit, ZFS_TYPE_FILESYSTEM, - "512 to 128k, power of 2", "RECSIZE", "%7s" }, + "512 to 128k, power of 2", "RECSIZE", B_TRUE }, { "mountpoint", prop_type_string, 0, "/", prop_inherit, ZFS_TYPE_FILESYSTEM, - "<path> | legacy | none", "MOUNTPOINT", "%-20s" }, + "<path> | legacy | none", "MOUNTPOINT", B_FALSE }, { "sharenfs", prop_type_string, 0, "off", prop_inherit, ZFS_TYPE_FILESYSTEM, - "on | off | share(1M) options", "SHARENFS", "%-15s" }, + "on | off | share(1M) options", "SHARENFS", B_FALSE }, { "checksum", prop_type_index, ZIO_CHECKSUM_DEFAULT, "on", prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, - "on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM", "%10s" }, + "on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM", B_TRUE }, { "compression", prop_type_index, ZIO_COMPRESS_DEFAULT, "off", prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, - "on | off | lzjb", "COMPRESS", "%8s" }, + "on | off | lzjb", "COMPRESS", B_TRUE }, { "atime", prop_type_boolean, 1, NULL, prop_inherit, ZFS_TYPE_FILESYSTEM, - "on | off", "ATIME", "%5s" }, + "on | off", "ATIME", B_TRUE }, { "devices", prop_type_boolean, 1, NULL, prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, - "on | off", "DEVICES", "%7s" }, + "on | off", "DEVICES", B_TRUE }, { "exec", prop_type_boolean, 1, NULL, prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, - "on | off", "EXEC", "%4s" }, + "on | off", "EXEC", B_TRUE }, { "setuid", prop_type_boolean, 1, NULL, prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "SETUID", - "%6s" }, + B_TRUE }, { "readonly", prop_type_boolean, 0, NULL, prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, - "on | off", "RDONLY", "%6s" }, + "on | off", "RDONLY", B_TRUE }, { "zoned", prop_type_boolean, 0, NULL, prop_inherit, ZFS_TYPE_FILESYSTEM, - "on | off", "ZONED", "%5s" }, + "on | off", "ZONED", B_TRUE }, { "snapdir", prop_type_index, ZFS_SNAPDIR_HIDDEN, "hidden", prop_inherit, ZFS_TYPE_FILESYSTEM, - "hidden | visible", "SNAPDIR", "%7s" }, - { "aclmode", prop_type_index, GROUPMASK, "groupmask", prop_inherit, + "hidden | visible", "SNAPDIR", B_TRUE }, + { "aclmode", prop_type_index, ZFS_ACL_GROUPMASK, "groupmask", + prop_inherit, ZFS_TYPE_FILESYSTEM, + "discard | groupmask | passthrough", "ACLMODE", B_TRUE }, + { "aclinherit", prop_type_index, ZFS_ACL_SECURE, "secure", + prop_inherit, ZFS_TYPE_FILESYSTEM, + "discard | noallow | secure | passthrough", "ACLINHERIT", B_TRUE }, + { "canmount", prop_type_boolean, 1, NULL, prop_default, ZFS_TYPE_FILESYSTEM, - "discard | groupmask | passthrough", "ACLMODE", "%11s" }, - { "aclinherit", prop_type_index, SECURE, "secure", prop_inherit, - ZFS_TYPE_FILESYSTEM, - "discard | noallow | secure | passthrough", "ACLINHERIT", "%11s" }, + "on | off", "CANMOUNT", B_TRUE }, { "createtxg", prop_type_number, 0, NULL, prop_readonly, ZFS_TYPE_ANY, NULL, NULL, NULL}, { "name", prop_type_string, 0, NULL, prop_readonly, ZFS_TYPE_ANY, - NULL, "NAME", "%-20s" }, + NULL, "NAME", B_FALSE }, }; zfs_proptype_t @@ -165,8 +168,8 @@ zfs_prop_get_type(zfs_prop_t prop) return (zfs_prop_table[prop].pd_proptype); } -static int -propname_match(const char *p, int prop, size_t len) +static boolean_t +propname_match(const char *p, zfs_prop_t prop, size_t len) { const char *propname = zfs_prop_table[prop].pd_name; #ifndef _KERNEL @@ -176,16 +179,16 @@ propname_match(const char *p, int prop, size_t len) #ifndef _KERNEL if (colname == NULL) - return (0); + return (B_FALSE); #endif if (len == strlen(propname) && strncmp(p, propname, len) == 0) - return (1); + return (B_TRUE); #ifndef _KERNEL if (len != strlen(colname)) - return (0); + return (B_FALSE); for (c = 0; c < len; c++) if (p[c] != tolower(colname[c])) @@ -193,7 +196,7 @@ propname_match(const char *p, int prop, size_t len) return (colname[c] == '\0'); #else - return (0); + return (B_FALSE); #endif } @@ -215,6 +218,42 @@ zfs_name_to_prop(const char *propname) } /* + * For user property names, we allow all lowercase alphanumeric characters, plus + * a few useful punctuation characters. + */ +static int +valid_char(char c) +{ + return ((c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '-' || c == '_' || c == '.' || c == ':'); +} + +/* + * Returns true if this is a valid user-defined property (one with a ':'). + */ +boolean_t +zfs_prop_user(const char *name) +{ + int i; + char c; + boolean_t foundsep = B_FALSE; + + for (i = 0; i < strlen(name); i++) { + c = name[i]; + if (!valid_char(c)) + return (B_FALSE); + if (c == ':') + foundsep = B_TRUE; + } + + if (!foundsep) + return (B_FALSE); + + return (B_TRUE); +} + +/* * Return the default value for the given property. */ const char * @@ -238,7 +277,6 @@ zfs_prop_readonly(zfs_prop_t prop) return (zfs_prop_table[prop].pd_attr == prop_readonly); } -#ifndef _KERNEL /* * Given a property ID, returns the corresponding name. */ @@ -257,6 +295,117 @@ zfs_prop_inheritable(zfs_prop_t prop) return (zfs_prop_table[prop].pd_attr == prop_inherit); } +#ifndef _KERNEL + +typedef struct zfs_index { + const char *name; + uint64_t index; +} zfs_index_t; + +static zfs_index_t checksum_table[] = { + { "on", ZIO_CHECKSUM_ON }, + { "off", ZIO_CHECKSUM_OFF }, + { "fletcher2", ZIO_CHECKSUM_FLETCHER_2 }, + { "fletcher4", ZIO_CHECKSUM_FLETCHER_4 }, + { "sha256", ZIO_CHECKSUM_SHA256 }, + { NULL } +}; + +static zfs_index_t compress_table[] = { + { "on", ZIO_COMPRESS_ON }, + { "off", ZIO_COMPRESS_OFF }, + { "lzjb", ZIO_COMPRESS_LZJB }, + { NULL } +}; + +static zfs_index_t snapdir_table[] = { + { "hidden", ZFS_SNAPDIR_HIDDEN }, + { "visible", ZFS_SNAPDIR_VISIBLE }, + { NULL } +}; + +static zfs_index_t acl_mode_table[] = { + { "discard", ZFS_ACL_DISCARD }, + { "groupmask", ZFS_ACL_GROUPMASK }, + { "passthrough", ZFS_ACL_PASSTHROUGH }, + { NULL } +}; + +static zfs_index_t acl_inherit_table[] = { + { "discard", ZFS_ACL_DISCARD }, + { "noallow", ZFS_ACL_NOALLOW }, + { "secure", ZFS_ACL_SECURE }, + { "passthrough", ZFS_ACL_PASSTHROUGH }, + { NULL } +}; + +static zfs_index_t * +zfs_prop_index_table(zfs_prop_t prop) +{ + switch (prop) { + case ZFS_PROP_CHECKSUM: + return (checksum_table); + break; + case ZFS_PROP_COMPRESSION: + return (compress_table); + break; + case ZFS_PROP_SNAPDIR: + return (snapdir_table); + break; + case ZFS_PROP_ACLMODE: + return (acl_mode_table); + break; + case ZFS_PROP_ACLINHERIT: + return (acl_inherit_table); + break; + default: + return (NULL); + } +} + + +/* + * Tables of index types, plus functions to convert between the user view + * (strings) and internal representation (uint64_t). + */ +int +zfs_prop_string_to_index(zfs_prop_t prop, const char *string, uint64_t *index) +{ + zfs_index_t *table; + int i; + + if ((table = zfs_prop_index_table(prop)) == NULL) + return (-1); + + for (i = 0; table[i].name != NULL; i++) { + if (strcmp(string, table[i].name) == 0) { + *index = table[i].index; + return (0); + } + } + + return (-1); +} + +int +zfs_prop_index_to_string(zfs_prop_t prop, uint64_t index, const char **string) +{ + zfs_index_t *table; + int i; + + if ((table = zfs_prop_index_table(prop)) == NULL) + return (-1); + + for (i = 0; table[i].name != NULL; i++) { + if (table[i].index == index) { + *string = table[i].name; + return (0); + } + } + + return (-1); +} + /* * Returns TRUE if the property applies to the given dataset types. */ @@ -299,110 +448,75 @@ zfs_prop_column_name(zfs_prop_t prop) } /* - * Returns the column formatting for the given property. Used only in - * 'zfs list -o', but centralized here with the other property information. + * Returns whether the given property should be displayed right-justified for + * 'zfs list'. */ -const char * -zfs_prop_column_format(zfs_prop_t prop) +boolean_t +zfs_prop_align_right(zfs_prop_t prop) { - return (zfs_prop_table[prop].pd_colfmt); + return (zfs_prop_table[prop].pd_rightalign); } /* - * Given a comma-separated list of fields, fills in the specified array with a - * list of properties. The keyword "all" can be used to specify all properties. - * The 'count' parameter returns the number of matching properties placed into - * the list. The 'props' parameter must point to an array of at least - * ZFS_NPROP_ALL elements. + * Determines the minimum width for the column, and indicates whether it's fixed + * or not. Only string columns are non-fixed. */ -int -zfs_get_proplist(char *fields, zfs_prop_t *props, int max, - int *count, char **badopt) +size_t +zfs_prop_width(zfs_prop_t prop, boolean_t *fixed) { + prop_desc_t *pd = &zfs_prop_table[prop]; + zfs_index_t *idx; + size_t ret; int i; - size_t len; - char *s, *p; - *count = 0; + *fixed = B_TRUE; /* - * Check for the special value "all", which indicates all properties - * should be displayed. + * Start with the width of the column name. */ - if (strcmp(fields, "all") == 0) { - if (max < ZFS_NPROP_VISIBLE) - return (EOVERFLOW); - - for (i = 0; i < ZFS_NPROP_VISIBLE; i++) - props[i] = i; - *count = ZFS_NPROP_VISIBLE; - return (0); - } + ret = strlen(pd->pd_colname); /* - * It would be nice to use getsubopt() here, but the inclusion of column - * aliases makes this more effort than it's worth. + * For fixed-width values, make sure the width is large enough to hold + * any possible value. */ - s = fields; - while (*s != '\0') { - if ((p = strchr(s, ',')) == NULL) { - len = strlen(s); - p = s + len; - } else { - len = p - s; - } - + switch (pd->pd_proptype) { + case prop_type_number: /* - * Check for empty options. + * The maximum length of a human-readable number is 5 characters + * ("20.4M", for example). */ - if (len == 0) { - *badopt = ""; - return (EINVAL); - } - + if (ret < 5) + ret = 5; /* - * Check all regular property names. + * 'creation' is handled specially because it's a number + * internally, but displayed as a date string. */ - for (i = 0; i < ZFS_NPROP_ALL; i++) { - if (propname_match(s, i, len)) - break; - } - + if (prop == ZFS_PROP_CREATION) + *fixed = B_FALSE; + break; + case prop_type_boolean: /* - * If no column is specified, then return failure, setting - * 'badopt' to point to the invalid option. + * The maximum length of a boolean value is 3 characters, for + * "off". */ - if (i == ZFS_NPROP_ALL) { - s[len] = '\0'; - *badopt = s; - return (EINVAL); + if (ret < 3) + ret = 3; + break; + case prop_type_index: + idx = zfs_prop_index_table(prop); + for (i = 0; idx[i].name != NULL; i++) { + if (strlen(idx[i].name) > ret) + ret = strlen(idx[i].name); } + break; - /* - * If the user specified too many options (by using the same one - * multiple times). return an error with 'badopt' set to NULL to - * indicate this condition. - */ - if (*count == max) - return (EOVERFLOW); - - props[*count] = i; - *count += 1; - - s = p; - if (*s == ',') - s++; - } - - /* - * If no fields were specified, return an error. - */ - if (*count == 0) { - *badopt = ""; - return (EINVAL); + case prop_type_string: + *fixed = B_FALSE; + break; } - return (0); + return (ret); } #endif diff --git a/usr/src/common/zfs/zfs_prop.h b/usr/src/common/zfs/zfs_prop.h index f95574acc9..2a89f0551f 100644 --- a/usr/src/common/zfs/zfs_prop.h +++ b/usr/src/common/zfs/zfs_prop.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -48,6 +47,9 @@ typedef enum { } zfs_proptype_t; zfs_proptype_t zfs_prop_get_type(zfs_prop_t); +int zfs_prop_string_to_index(zfs_prop_t, const char *, uint64_t *); +int zfs_prop_index_to_string(zfs_prop_t, uint64_t, const char **); +size_t zfs_prop_width(zfs_prop_t, boolean_t *); #ifdef __cplusplus } diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index 601bdb2f18..4459c70710 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -201,10 +201,6 @@ extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **); extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *); extern int zpool_get_errlog(zpool_handle_t *, nvlist_t ***, size_t *); -#define ZPOOL_ERR_DATASET "dataset" -#define ZPOOL_ERR_OBJECT "object" -#define ZPOOL_ERR_RANGE "range" - /* * Import and export functions */ @@ -246,25 +242,36 @@ typedef enum { * Property management functions. Some functions are shared with the kernel, * and are found in sys/fs/zfs.h. */ -const char *zfs_prop_to_name(zfs_prop_t); -int zfs_prop_set(zfs_handle_t *, zfs_prop_t, const char *); -int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, zfs_source_t *, - char *, size_t, boolean_t); -int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *, zfs_source_t *, - char *, size_t); -uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t); -int zfs_prop_validate(libzfs_handle_t *, zfs_prop_t, const char *, uint64_t *); -int zfs_prop_inheritable(zfs_prop_t); -int zfs_prop_inherit(zfs_handle_t *, zfs_prop_t); -const char *zfs_prop_values(zfs_prop_t); -int zfs_prop_valid_for_type(zfs_prop_t, int); -const char *zfs_prop_default_string(zfs_prop_t prop); -uint64_t zfs_prop_default_numeric(zfs_prop_t); -int zfs_prop_is_string(zfs_prop_t prop); -const char *zfs_prop_column_name(zfs_prop_t); -const char *zfs_prop_column_format(zfs_prop_t); -int zfs_get_proplist(char *fields, zfs_prop_t *proplist, int max, int *count, - char **badopt); +extern const char *zfs_prop_to_name(zfs_prop_t); +extern int zfs_prop_set(zfs_handle_t *, const char *, const char *); +extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, + zfs_source_t *, char *, size_t, boolean_t); +extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *, + zfs_source_t *, char *, size_t); +extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t); +extern const char *zfs_prop_get_string(zfs_handle_t *, zfs_prop_t); +extern int zfs_prop_inherit(zfs_handle_t *, const char *); +extern const char *zfs_prop_values(zfs_prop_t); +extern int zfs_prop_valid_for_type(zfs_prop_t, int); +extern const char *zfs_prop_default_string(zfs_prop_t prop); +extern uint64_t zfs_prop_default_numeric(zfs_prop_t); +extern int zfs_prop_is_string(zfs_prop_t prop); +extern const char *zfs_prop_column_name(zfs_prop_t); +extern boolean_t zfs_prop_align_right(zfs_prop_t); + +typedef struct zfs_proplist { + zfs_prop_t pl_prop; + char *pl_user_prop; + struct zfs_proplist *pl_next; + boolean_t pl_all; + size_t pl_width; + boolean_t pl_fixed; +} zfs_proplist_t; + +extern int zfs_get_proplist(libzfs_handle_t *, char *, zfs_proplist_t **); +extern void zfs_free_proplist(zfs_proplist_t *); +extern int zfs_expand_proplist(zfs_handle_t *, zfs_proplist_t **); +extern nvlist_t *zfs_get_user_props(zfs_handle_t *); #define ZFS_MOUNTPOINT_NONE "none" #define ZFS_MOUNTPOINT_LEGACY "legacy" @@ -283,10 +290,10 @@ extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *); * Functions to create and destroy datasets. */ extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t, - const char *, const char *); + nvlist_t *); extern int zfs_destroy(zfs_handle_t *); extern int zfs_destroy_snaps(zfs_handle_t *, char *); -extern int zfs_clone(zfs_handle_t *, const char *); +extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *); extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t); extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, int); extern int zfs_rename(zfs_handle_t *, const char *); @@ -331,7 +338,7 @@ extern int zfs_unshareall(zfs_handle_t *); * Utility function to convert a number to a human-readable form. */ extern void zfs_nicenum(uint64_t, char *, size_t); -extern int zfs_nicestrtonum(const char *, uint64_t *); +extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *); /* * Pool destroy special. Remove the device information without destroying diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c index 7b7b5cd9d4..6bc42d16e5 100644 --- a/usr/src/lib/libzfs/common/libzfs_changelist.c +++ b/usr/src/lib/libzfs/common/libzfs_changelist.c @@ -107,7 +107,7 @@ changelist_prefix(prop_changelist_t *clp) * If we have a volume and this was a rename, remove the * /dev/zvol links */ - if (cn->cn_handle->zfs_volblocksize && + if (ZFS_IS_VOLUME(cn->cn_handle) && clp->cl_realprop == ZFS_PROP_NAME) { if (zvol_remove_link(cn->cn_handle->zfs_hdl, cn->cn_handle->zfs_name) != 0) @@ -166,7 +166,7 @@ changelist_postfix(prop_changelist_t *clp) * If this is a volume and we're doing a rename, recreate the * /dev/zvol links. */ - if (cn->cn_handle->zfs_volblocksize && + if (ZFS_IS_VOLUME(cn->cn_handle) && clp->cl_realprop == ZFS_PROP_NAME) { if (zvol_create_link(cn->cn_handle->zfs_hdl, cn->cn_handle->zfs_name) != 0) @@ -358,7 +358,7 @@ change_one(zfs_handle_t *zhp, void *data) * rename, then always add it to the changelist. */ - if (!(zhp->zfs_volblocksize && clp->cl_realprop == ZFS_PROP_NAME) && + if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) && zfs_prop_get(zhp, clp->cl_prop, property, sizeof (property), &sourcetype, where, sizeof (where), B_FALSE) != 0) { @@ -508,6 +508,8 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) } else if (prop == ZFS_PROP_ZONED) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; clp->cl_allchildren = B_TRUE; + } else if (prop == ZFS_PROP_CANMOUNT) { + clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else { clp->cl_prop = prop; } diff --git a/usr/src/lib/libzfs/common/libzfs_config.c b/usr/src/lib/libzfs/common/libzfs_config.c index 593131cea6..d8fbd2ecb5 100644 --- a/usr/src/lib/libzfs/common/libzfs_config.c +++ b/usr/src/lib/libzfs/common/libzfs_config.c @@ -129,19 +129,9 @@ namespace_reload(libzfs_handle_t *hdl) return (no_memory(hdl)); } - /* - * Issue the ZFS_IOC_POOL_CONFIGS ioctl. - * This can fail for one of two reasons: - * - * EEXIST The generation counts match, nothing to do. - * ENOMEM The zc_config_dst buffer isn't large enough to - * hold the config; zc_config_dst_size will have - * been modified to tell us how much to allocate. - */ - zc.zc_config_dst_size = 1024; - if ((zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL) + if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) return (-1); + for (;;) { zc.zc_cookie = hdl->libzfs_ns_gen; if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CONFIGS, &zc) != 0) { @@ -150,18 +140,18 @@ namespace_reload(libzfs_handle_t *hdl) /* * The namespace hasn't changed. */ - free((void *)(uintptr_t)zc.zc_config_dst); + zcmd_free_nvlists(&zc); return (0); case ENOMEM: - free((void *)(uintptr_t)zc.zc_config_dst); - if ((zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_alloc(hdl, zc.zc_config_dst_size)) - == NULL) + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); return (-1); + } break; default: + zcmd_free_nvlists(&zc); return (zfs_standard_error(hdl, errno, dgettext(TEXT_DOMAIN, "failed to read " "pool configuration"))); @@ -172,13 +162,12 @@ namespace_reload(libzfs_handle_t *hdl) } } - if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, - zc.zc_config_dst_size, &config, 0) != 0) { - free((void *)(uintptr_t)zc.zc_config_dst); - return (no_memory(hdl)); + if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) { + zcmd_free_nvlists(&zc); + return (-1); } - free((void *)(uintptr_t)zc.zc_config_dst); + zcmd_free_nvlists(&zc); /* * Clear out any existing configuration information. @@ -256,6 +245,7 @@ zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing) zfs_cmd_t zc = { 0 }; int error; nvlist_t *config; + libzfs_handle_t *hdl = zhp->zpool_hdl; *missing = B_FALSE; (void) strcpy(zc.zc_name, zhp->zpool_name); @@ -263,9 +253,7 @@ zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing) if (zhp->zpool_config_size == 0) zhp->zpool_config_size = 1 << 16; - zc.zc_config_dst_size = zhp->zpool_config_size; - if ((zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_alloc(zhp->zpool_hdl, zc.zc_config_dst_size)) == NULL) + if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size) != 0) return (-1); for (;;) { @@ -279,13 +267,12 @@ zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing) } if (errno == ENOMEM) { - free((void *)(uintptr_t)zc.zc_config_dst); - if ((zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_alloc(zhp->zpool_hdl, - zc.zc_config_dst_size)) == NULL) + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); return (-1); + } } else { - free((void *)(uintptr_t)zc.zc_config_dst); + zcmd_free_nvlists(&zc); if (errno == ENOENT || errno == EINVAL) *missing = B_TRUE; zhp->zpool_state = POOL_STATE_UNAVAIL; @@ -293,14 +280,14 @@ zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing) } } - if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, - zc.zc_config_dst_size, &config, 0) != 0) { - free((void *)(uintptr_t)zc.zc_config_dst); - return (no_memory(zhp->zpool_hdl)); + if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) { + zcmd_free_nvlists(&zc); + return (-1); } - zhp->zpool_config_size = zc.zc_config_dst_size; - free((void *)(uintptr_t)zc.zc_config_dst); + zcmd_free_nvlists(&zc); + + zhp->zpool_config_size = zc.zc_nvlist_dst_size; if (set_pool_health(config) != 0) { nvlist_free(config); diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index c8908e2ce3..ffef8ad8ed 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -43,6 +43,7 @@ #include <sys/spa.h> #include <sys/zio.h> +#include <sys/zap.h> #include <libzfs.h> #include "zfs_namecheck.h" @@ -186,27 +187,57 @@ zfs_name_valid(const char *name, zfs_type_t type) } /* + * This function takes the raw DSL properties, and filters out the user-defined + * properties into a separate nvlist. + */ +static int +process_user_props(zfs_handle_t *zhp) +{ + libzfs_handle_t *hdl = zhp->zfs_hdl; + nvpair_t *elem; + nvlist_t *propval; + + nvlist_free(zhp->zfs_user_props); + + if (nvlist_alloc(&zhp->zfs_user_props, NV_UNIQUE_NAME, 0) != 0) + return (no_memory(hdl)); + + elem = NULL; + while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) { + if (!zfs_prop_user(nvpair_name(elem))) + continue; + + verify(nvpair_value_nvlist(elem, &propval) == 0); + if (nvlist_add_nvlist(zhp->zfs_user_props, + nvpair_name(elem), propval) != 0) + return (no_memory(hdl)); + } + + return (0); +} + +/* * Utility function to gather stats (objset and zpl) for the given object. */ static int get_stats(zfs_handle_t *zhp) { zfs_cmd_t zc = { 0 }; + libzfs_handle_t *hdl = zhp->zfs_hdl; (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - if ((zc.zc_config_src = (uint64_t)(uintptr_t)malloc(1024)) == NULL) + if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) return (-1); - zc.zc_config_src_size = 1024; while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) { if (errno == ENOMEM) { - free((void *)(uintptr_t)zc.zc_config_src); - if ((zc.zc_config_src = (uint64_t)(uintptr_t) - malloc(zc.zc_config_src_size)) == NULL) + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); return (-1); + } } else { - free((void *)(uintptr_t)zc.zc_config_src); + zcmd_free_nvlists(&zc); return (-1); } } @@ -214,23 +245,24 @@ get_stats(zfs_handle_t *zhp) bcopy(&zc.zc_objset_stats, &zhp->zfs_dmustats, sizeof (zc.zc_objset_stats)); - (void) strcpy(zhp->zfs_root, zc.zc_root); + (void) strlcpy(zhp->zfs_root, zc.zc_value, sizeof (zhp->zfs_root)); if (zhp->zfs_props) { nvlist_free(zhp->zfs_props); zhp->zfs_props = NULL; } - if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_src, - zc.zc_config_src_size, &zhp->zfs_props, 0) != 0) { - free((void *)(uintptr_t)zc.zc_config_src); + if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zfs_props) != 0) { + zcmd_free_nvlists(&zc); return (-1); } - zhp->zfs_volsize = zc.zc_volsize; - zhp->zfs_volblocksize = zc.zc_volblocksize; + zcmd_free_nvlists(&zc); - free((void *)(uintptr_t)zc.zc_config_src); + zhp->zfs_volstats = zc.zc_vol_stats; + + if (process_user_props(zhp) != 0) + return (-1); return (0); } @@ -373,64 +405,11 @@ zfs_close(zfs_handle_t *zhp) { if (zhp->zfs_mntopts) free(zhp->zfs_mntopts); - if (zhp->zfs_props) - nvlist_free(zhp->zfs_props); + nvlist_free(zhp->zfs_props); + nvlist_free(zhp->zfs_user_props); free(zhp); } -struct { - const char *name; - uint64_t value; -} checksum_table[] = { - { "on", ZIO_CHECKSUM_ON }, - { "off", ZIO_CHECKSUM_OFF }, - { "fletcher2", ZIO_CHECKSUM_FLETCHER_2 }, - { "fletcher4", ZIO_CHECKSUM_FLETCHER_4 }, - { "sha256", ZIO_CHECKSUM_SHA256 }, - { NULL } -}; - -struct { - const char *name; - uint64_t value; -} compress_table[] = { - { "on", ZIO_COMPRESS_ON }, - { "off", ZIO_COMPRESS_OFF }, - { "lzjb", ZIO_COMPRESS_LZJB }, - { NULL } -}; - -struct { - const char *name; - uint64_t value; -} snapdir_table[] = { - { "hidden", ZFS_SNAPDIR_HIDDEN }, - { "visible", ZFS_SNAPDIR_VISIBLE }, - { NULL } -}; - -struct { - const char *name; - uint64_t value; -} acl_mode_table[] = { - { "discard", DISCARD }, - { "groupmask", GROUPMASK }, - { "passthrough", PASSTHROUGH }, - { NULL } -}; - -struct { - const char *name; - uint64_t value; -} acl_inherit_table[] = { - { "discard", DISCARD }, - { "noallow", NOALLOW }, - { "secure", SECURE }, - { "passthrough", PASSTHROUGH }, - { NULL } -}; - - /* * Given a numeric suffix, convert the value into a number of bits that the * resulting value must be shifted. @@ -541,272 +520,496 @@ nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num) } int -zfs_nicestrtonum(const char *str, uint64_t *val) +zfs_nicestrtonum(libzfs_handle_t *hdl, const char *str, uint64_t *val) { - return (nicestrtonum(NULL, str, val)); + return (nicestrtonum(hdl, str, val)); } /* - * Given a property type and value, verify that the value is appropriate. Used - * by zfs_prop_set() and some libzfs consumers. + * The prop_parse_*() functions are designed to allow flexibility in callers + * when setting properties. At the DSL layer, all properties are either 64-bit + * numbers or strings. We want the user to be able to ignore this fact and + * specify properties as native values (boolean, for example) or as strings (to + * simplify command line utilities). This also handles converting index types + * (compression, checksum, etc) from strings to their on-disk index. */ -int -zfs_prop_validate(libzfs_handle_t *hdl, zfs_prop_t prop, const char *value, - uint64_t *intval) -{ - const char *propname = zfs_prop_to_name(prop); - uint64_t number; - char errbuf[1024]; - int i; - /* - * Check to see if this a read-only property. - */ - if (zfs_prop_readonly(prop)) - return (zfs_error(hdl, EZFS_PROPREADONLY, - dgettext(TEXT_DOMAIN, "cannot set %s property"), propname)); +static int +prop_parse_boolean(libzfs_handle_t *hdl, nvpair_t *elem, uint64_t *val) +{ + uint64_t ret; - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "bad %s value '%s'"), propname, value); + switch (nvpair_type(elem)) { + case DATA_TYPE_STRING: + { + char *value; + VERIFY(nvpair_value_string(elem, &value) == 0); - /* See if the property value is too long */ - if (strlen(value) >= ZFS_MAXPROPLEN) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "value is too long")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); - } + if (strcmp(value, "on") == 0) { + ret = 1; + } else if (strcmp(value, "off") == 0) { + ret = 0; + } else { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "property '%s' must be 'on' or 'off'"), + nvpair_name(elem)); + return (-1); + } + break; + } - /* Perform basic checking based on property type */ - switch (zfs_prop_get_type(prop)) { - case prop_type_boolean: - if (strcmp(value, "on") == 0) { - number = 1; - } else if (strcmp(value, "off") == 0) { - number = 0; - } else { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be 'on' or 'off'")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + case DATA_TYPE_UINT64: + { + VERIFY(nvpair_value_uint64(elem, &ret) == 0); + if (ret > 1) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be a boolean value"), + nvpair_name(elem)); + return (-1); + } + break; } - break; - case prop_type_number: - /* treat 'none' as 0 */ - if (strcmp(value, "none") == 0) { - number = 0; + case DATA_TYPE_BOOLEAN_VALUE: + { + boolean_t value; + VERIFY(nvpair_value_boolean_value(elem, &value) == 0); + ret = value; break; } - if (nicestrtonum(hdl, value, &number) != 0) - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + default: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be a boolean value"), + nvpair_name(elem)); + return (-1); + } - /* don't allow 0 for quota, use 'none' instead */ - if (prop == ZFS_PROP_QUOTA && number == 0 && - strcmp(value, "none") != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "use 'quota=none' to disable")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); - } + *val = ret; + return (0); +} - /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ - if (prop == ZFS_PROP_RECORDSIZE || - prop == ZFS_PROP_VOLBLOCKSIZE) { - if (number < SPA_MINBLOCKSIZE || - number > SPA_MAXBLOCKSIZE || !ISP2(number)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be power of 2 from %u to %uk"), - (uint_t)SPA_MINBLOCKSIZE, - (uint_t)SPA_MAXBLOCKSIZE >> 10); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); +static int +prop_parse_number(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop, + uint64_t *val) +{ + uint64_t ret; + boolean_t isnone = B_FALSE; + + switch (nvpair_type(elem)) { + case DATA_TYPE_STRING: + { + char *value; + (void) nvpair_value_string(elem, &value); + if (strcmp(value, "none") == 0) { + isnone = B_TRUE; + ret = 0; + } else if (nicestrtonum(hdl, value, &ret) != 0) { + return (-1); } + break; } + case DATA_TYPE_UINT64: + (void) nvpair_value_uint64(elem, &ret); break; - case prop_type_string: - case prop_type_index: + default: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be a number"), + nvpair_name(elem)); + return (-1); + } + + /* + * Quota special: force 'none' and don't allow 0. + */ + if (ret == 0 && !isnone && prop == ZFS_PROP_QUOTA) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "use 'none' to disable quota")); + return (-1); + } + + *val = ret; + return (0); +} + +static int +prop_parse_index(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop, + uint64_t *val) +{ + char *propname = nvpair_name(elem); + char *value; + + if (nvpair_type(elem) != DATA_TYPE_STRING) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be a string"), propname); + return (-1); + } + + (void) nvpair_value_string(elem, &value); + + if (zfs_prop_string_to_index(prop, value, val) != 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be one of '%s'"), propname, + zfs_prop_values(prop)); + return (-1); + } + + return (0); +} + +/* + * Given an nvlist of properties to set, validates that they are correct, and + * parses any numeric properties (index, boolean, etc) if they are specified as + * strings. + */ +static nvlist_t * +zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, + uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) +{ + nvpair_t *elem; + const char *propname; + zfs_prop_t prop; + uint64_t intval; + char *strval; + nvlist_t *ret; + + if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { + (void) no_memory(hdl); + return (NULL); + } + + if (type == ZFS_TYPE_SNAPSHOT) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "snaphot properties cannot be modified")); + (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); + goto error; + } + + elem = NULL; + while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { + propname = nvpair_name(elem); + /* - * The two writable string values, 'mountpoint' and - * 'checksum' need special consideration. The 'index' types are - * specified as strings by the user, but passed to the kernel as - * integers. + * Make sure this property is valid and applies to this type. */ - switch (prop) { - case ZFS_PROP_MOUNTPOINT: - if (strcmp(value, ZFS_MOUNTPOINT_NONE) == 0 || - strcmp(value, ZFS_MOUNTPOINT_LEGACY) == 0) - break; - - if (value[0] != '/') { + if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { + if (!zfs_prop_user(propname)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be an absolute path, 'none', or " - "'legacy'")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); - } - break; + "invalid property '%s'"), + propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } else { + /* + * If this is a user property, make sure it's a + * string, and that it's less than + * ZAP_MAXNAMELEN. + */ + if (nvpair_type(elem) != DATA_TYPE_STRING) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be a string"), + propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } - case ZFS_PROP_CHECKSUM: - for (i = 0; checksum_table[i].name != NULL; i++) { - if (strcmp(value, checksum_table[i].name) - == 0) { - number = checksum_table[i].value; - break; + if (strlen(nvpair_name(elem)) >= + ZAP_MAXNAMELEN) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "property name '%s' is too long"), + propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; } } - if (checksum_table[i].name == NULL) { + (void) nvpair_value_string(elem, &strval); + if (nvlist_add_string(ret, propname, strval) != 0) { + (void) no_memory(hdl); + goto error; + } + continue; + } + + /* + * Normalize the name, to get rid of shorthand abbrevations. + */ + propname = zfs_prop_to_name(prop); + + if (!zfs_prop_valid_for_type(prop, type)) { + zfs_error_aux(hdl, + dgettext(TEXT_DOMAIN, "'%s' does not " + "apply to datasets of this type"), propname); + (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); + goto error; + } + + if (zfs_prop_readonly(prop) && + (prop != ZFS_PROP_VOLBLOCKSIZE || zhp != NULL)) { + zfs_error_aux(hdl, + dgettext(TEXT_DOMAIN, "'%s' is readonly"), + propname); + (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); + goto error; + } + + /* + * Convert any properties to the internal DSL value types. + */ + strval = NULL; + switch (zfs_prop_get_type(prop)) { + case prop_type_boolean: + if (prop_parse_boolean(hdl, elem, &intval) != 0) { + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + break; + + case prop_type_string: + if (nvpair_type(elem) != DATA_TYPE_STRING) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be 'on', 'off', 'fletcher2', " - "'fletcher4', or 'sha256'")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + "'%s' must be a string"), + propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + (void) nvpair_value_string(elem, &strval); + if (strlen(strval) >= ZFS_MAXPROPLEN) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' is too long"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; } break; - case ZFS_PROP_COMPRESSION: - for (i = 0; compress_table[i].name != NULL; i++) { - if (strcmp(value, compress_table[i].name) - == 0) { - number = compress_table[i].value; - break; - } + case prop_type_number: + if (prop_parse_number(hdl, elem, prop, &intval) != 0) { + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; } + break; - if (compress_table[i].name == NULL) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be 'on', 'off', or 'lzjb'")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + case prop_type_index: + if (prop_parse_index(hdl, elem, prop, &intval) != 0) { + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; } break; - case ZFS_PROP_SNAPDIR: - for (i = 0; snapdir_table[i].name != NULL; i++) { - if (strcmp(value, snapdir_table[i].name) == 0) { - number = snapdir_table[i].value; - break; - } + default: + abort(); + } + + /* + * Add the result to our return set of properties. + */ + if (strval) { + if (nvlist_add_string(ret, propname, strval) != 0) { + (void) no_memory(hdl); + goto error; } + } else if (nvlist_add_uint64(ret, propname, intval) != 0) { + (void) no_memory(hdl); + goto error; + } - if (snapdir_table[i].name == NULL) { + /* + * Perform some additional checks for specific properties. + */ + switch (prop) { + case ZFS_PROP_RECORDSIZE: + case ZFS_PROP_VOLBLOCKSIZE: + /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ + if (intval < SPA_MINBLOCKSIZE || + intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be 'hidden' or 'visible'")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + "'%s' must be power of 2 from %u " + "to %uk"), propname, + (uint_t)SPA_MINBLOCKSIZE, + (uint_t)SPA_MAXBLOCKSIZE >> 10); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; } break; - case ZFS_PROP_ACLMODE: - for (i = 0; acl_mode_table[i].name != NULL; i++) { - if (strcmp(value, acl_mode_table[i].name) - == 0) { - number = acl_mode_table[i].value; - break; - } - } + case ZFS_PROP_MOUNTPOINT: + if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || + strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) + break; - if (acl_mode_table[i].name == NULL) { + if (strval[0] != '/') { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be 'disacard', 'groupmask', or " - "'passthrough'")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + "'%s' must be an absolute path, " + "'none', or 'legacy'"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; } break; + } - case ZFS_PROP_ACLINHERIT: - for (i = 0; acl_inherit_table[i].name != NULL; i++) { - if (strcmp(value, acl_inherit_table[i].name) - == 0) { - number = acl_inherit_table[i].value; - break; + /* + * For the mountpoint and sharenfs properties, check if it can + * be set in a global/non-global zone based on the zoned + * property value: + * + * global zone non-global zone + * ----------------------------------------------------- + * zoned=on mountpoint (no) mountpoint (yes) + * sharenfs (no) sharenfs (no) + * + * zoned=off mountpoint (yes) N/A + * sharenfs (yes) + */ + if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) { + if (zoned) { + if (getzoneid() == GLOBAL_ZONEID) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' cannot be set on " + "dataset in a non-global zone"), + propname); + (void) zfs_error(hdl, EZFS_ZONED, + errbuf); + goto error; + } else if (prop == ZFS_PROP_SHARENFS) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' cannot be set in " + "a non-global zone"), propname); + (void) zfs_error(hdl, EZFS_ZONED, + errbuf); + goto error; } + } else if (getzoneid() != GLOBAL_ZONEID) { + /* + * If zoned property is 'off', this must be in + * a globle zone. If not, something is wrong. + */ + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' cannot be set while dataset " + "'zoned' property is set"), propname); + (void) zfs_error(hdl, EZFS_ZONED, errbuf); + goto error; } + } - if (acl_inherit_table[i].name == NULL) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be 'discard, 'noallow', 'secure', " - "or 'passthrough'")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + /* + * For changes to existing volumes, we have some additional + * checks to enforce. + */ + if (type == ZFS_TYPE_VOLUME && zhp != NULL) { + uint64_t volsize = zfs_prop_get_int(zhp, + ZFS_PROP_VOLSIZE); + uint64_t blocksize = zfs_prop_get_int(zhp, + ZFS_PROP_VOLBLOCKSIZE); + char buf[64]; + + switch (prop) { + case ZFS_PROP_RESERVATION: + if (intval > volsize) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' is greater than current " + "volume size"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } + break; + + case ZFS_PROP_VOLSIZE: + if (intval % blocksize != 0) { + zfs_nicenum(blocksize, buf, + sizeof (buf)); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be a multiple of " + "volume block size (%s)"), + propname, buf); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } + + if (intval == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' cannot be zero"), + propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } } - break; + } + } - case ZFS_PROP_SHARENFS: - /* - * Nothing to do for 'sharenfs', this gets passed on to - * share(1M) verbatim. - */ - break; + /* + * If this is an existing volume, and someone is setting the volsize, + * make sure that it matches the reservation, or add it if necessary. + */ + if (zhp != NULL && type == ZFS_TYPE_VOLUME && + nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), + &intval) == 0) { + uint64_t old_volsize = zfs_prop_get_int(zhp, + ZFS_PROP_VOLSIZE); + uint64_t old_reservation = zfs_prop_get_int(zhp, + ZFS_PROP_RESERVATION); + uint64_t new_reservation; + + if (old_volsize == old_reservation && + nvlist_lookup_uint64(ret, + zfs_prop_to_name(ZFS_PROP_RESERVATION), + &new_reservation) != 0) { + if (nvlist_add_uint64(ret, + zfs_prop_to_name(ZFS_PROP_RESERVATION), + intval) != 0) { + (void) no_memory(hdl); + goto error; + } } } - if (intval != NULL) - *intval = number; + return (ret); - return (0); +error: + nvlist_free(ret); + return (NULL); } /* * Given a property name and value, set the property for the given dataset. */ int -zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) +zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) { - const char *propname = zfs_prop_to_name(prop); - uint64_t number; zfs_cmd_t zc = { 0 }; - int ret; - prop_changelist_t *cl; + int ret = -1; + prop_changelist_t *cl = NULL; char errbuf[1024]; libzfs_handle_t *hdl = zhp->zfs_hdl; - - if (zfs_prop_validate(zhp->zfs_hdl, prop, propval, &number) != 0) - return (-1); - + nvlist_t *nvl = NULL, *realprops; + zfs_prop_t prop; (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot set %s for '%s'"), propname, + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), zhp->zfs_name); - /* - * Check to see if the value applies to this type - */ - if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) - return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); - - /* - * For the mountpoint and sharenfs properties, check if it can be set - * in a global/non-global zone based on the zoned property value: - * - * global zone non-global zone - * ----------------------------------------------------- - * zoned=on mountpoint (no) mountpoint (yes) - * sharenfs (no) sharenfs (no) - * - * zoned=off mountpoint (yes) N/A - * sharenfs (yes) - */ - if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) { - if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { - if (getzoneid() == GLOBAL_ZONEID) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "dataset is used in a non-global zone")); - return (zfs_error(hdl, EZFS_ZONED, errbuf)); - } else if (prop == ZFS_PROP_SHARENFS) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "filesystems cannot be shared in a " - "non-global zone")); - return (zfs_error(hdl, EZFS_ZONED, errbuf)); - } - } else if (getzoneid() != GLOBAL_ZONEID) { - /* - * If zoned property is 'off', this must be in - * a globle zone. If not, something is wrong. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "dataset is used in a non-global zone, but " - "'zoned' property is not set")); - return (zfs_error(hdl, EZFS_ZONED, errbuf)); - } + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_string(nvl, propname, propval) != 0) { + (void) no_memory(hdl); + goto error; } + if ((realprops = zfs_validate_properties(hdl, zhp->zfs_type, nvl, + zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) + goto error; + nvlist_free(nvl); + nvl = realprops; + + prop = zfs_name_to_prop(propname); + if ((cl = changelist_gather(zhp, prop, 0)) == NULL) - return (-1); + goto error; if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, @@ -824,48 +1027,10 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) */ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - switch (prop) { - case ZFS_PROP_QUOTA: - zc.zc_cookie = number; - ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_QUOTA, &zc); - break; - case ZFS_PROP_RESERVATION: - zc.zc_cookie = number; - ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_RESERVATION, - &zc); - break; - case ZFS_PROP_MOUNTPOINT: - case ZFS_PROP_SHARENFS: - /* - * These properties are passed down as real strings. - */ - (void) strlcpy(zc.zc_prop_name, propname, - sizeof (zc.zc_prop_name)); - (void) strlcpy(zc.zc_prop_value, propval, - sizeof (zc.zc_prop_value)); - zc.zc_intsz = 1; - zc.zc_numints = strlen(propval) + 1; - ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc); - break; - case ZFS_PROP_VOLSIZE: - zc.zc_volsize = number; - ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLSIZE, &zc); - break; - case ZFS_PROP_VOLBLOCKSIZE: - zc.zc_volblocksize = number; - ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLBLOCKSIZE, - &zc); - break; - default: - (void) strlcpy(zc.zc_prop_name, propname, - sizeof (zc.zc_prop_name)); - /* LINTED - alignment */ - *(uint64_t *)zc.zc_prop_value = number; - zc.zc_intsz = 8; - zc.zc_numints = 1; - ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc); - break; - } + if (zcmd_write_src_nvlist(hdl, &zc, nvl, NULL) != 0) + goto error; + + ret = ioctl(hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc); if (ret != 0) { switch (errno) { @@ -900,7 +1065,7 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) if (prop == ZFS_PROP_VOLBLOCKSIZE) (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); else - return (zfs_standard_error(hdl, EBUSY, errbuf)); + (void) zfs_standard_error(hdl, EBUSY, errbuf); break; case EROFS: @@ -926,14 +1091,15 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) * Refresh the statistics so the new property value * is reflected. */ - if ((ret = changelist_postfix(cl)) != 0) - goto error; - - (void) get_stats(zhp); + if ((ret = changelist_postfix(cl)) == 0) + (void) get_stats(zhp); } error: - changelist_free(cl); + nvlist_free(nvl); + zcmd_free_nvlists(&zc); + if (cl) + changelist_free(cl); return (ret); } @@ -941,18 +1107,39 @@ error: * Given a property, inherit the value from the parent dataset. */ int -zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) +zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) { - const char *propname = zfs_prop_to_name(prop); zfs_cmd_t zc = { 0 }; int ret; prop_changelist_t *cl; libzfs_handle_t *hdl = zhp->zfs_hdl; char errbuf[1024]; + zfs_prop_t prop; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot inherit %s for '%s'"), propname, zhp->zfs_name); + if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { + /* + * For user properties, the amount of work we have to do is very + * small, so just do it here. + */ + if (!zfs_prop_user(propname)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid property")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + } + + (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); + + if (ioctl(zhp->zfs_hdl->libzfs_fd, + ZFS_IOC_SET_PROP, &zc) != 0) + return (zfs_standard_error(hdl, errno, errbuf)); + + return (0); + } + /* * Verify that this property is inheritable. */ @@ -969,7 +1156,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - (void) strlcpy(zc.zc_prop_name, propname, sizeof (zc.zc_prop_name)); + (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { @@ -995,8 +1182,6 @@ zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) if ((ret = changelist_prefix(cl)) != 0) goto error; - zc.zc_numints = 0; - if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc)) != 0) { return (zfs_standard_error(hdl, errno, errbuf)); @@ -1011,7 +1196,6 @@ zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) (void) get_stats(zhp); } - error: changelist_free(cl); return (ret); @@ -1240,11 +1424,11 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src, break; case ZFS_PROP_VOLSIZE: - *val = zhp->zfs_volsize; + *val = zhp->zfs_volstats.zv_volsize; break; case ZFS_PROP_VOLBLOCKSIZE: - *val = zhp->zfs_volblocksize; + *val = zhp->zfs_volstats.zv_volblocksize; break; case ZFS_PROP_USED: @@ -1259,6 +1443,10 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src, *val = (zhp->zfs_mntopts != NULL); break; + case ZFS_PROP_CANMOUNT: + *val = getprop_uint64(zhp, prop, source); + break; + default: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "cannot get non-numeric property")); @@ -1308,8 +1496,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, char *source = NULL; uint64_t val; char *str; - int i; const char *root; + const char *strval; /* * Check to see if this property applies to our object @@ -1327,6 +1515,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, case ZFS_PROP_ZONED: case ZFS_PROP_DEVICES: case ZFS_PROP_EXEC: + case ZFS_PROP_CANMOUNT: /* * Basic boolean values are built on top of * get_numeric_property(). @@ -1357,53 +1546,13 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, break; case ZFS_PROP_COMPRESSION: - val = getprop_uint64(zhp, prop, &source); - for (i = 0; compress_table[i].name != NULL; i++) { - if (compress_table[i].value == val) - break; - } - assert(compress_table[i].name != NULL); - (void) strlcpy(propbuf, compress_table[i].name, proplen); - break; - case ZFS_PROP_CHECKSUM: - val = getprop_uint64(zhp, prop, &source); - for (i = 0; checksum_table[i].name != NULL; i++) { - if (checksum_table[i].value == val) - break; - } - assert(checksum_table[i].name != NULL); - (void) strlcpy(propbuf, checksum_table[i].name, proplen); - break; - case ZFS_PROP_SNAPDIR: - val = getprop_uint64(zhp, prop, &source); - for (i = 0; snapdir_table[i].name != NULL; i++) { - if (snapdir_table[i].value == val) - break; - } - assert(snapdir_table[i].name != NULL); - (void) strlcpy(propbuf, snapdir_table[i].name, proplen); - break; - case ZFS_PROP_ACLMODE: - val = getprop_uint64(zhp, prop, &source); - for (i = 0; acl_mode_table[i].name != NULL; i++) { - if (acl_mode_table[i].value == val) - break; - } - assert(acl_mode_table[i].name != NULL); - (void) strlcpy(propbuf, acl_mode_table[i].name, proplen); - break; - case ZFS_PROP_ACLINHERIT: val = getprop_uint64(zhp, prop, &source); - for (i = 0; acl_inherit_table[i].name != NULL; i++) { - if (acl_inherit_table[i].value == val) - break; - } - assert(acl_inherit_table[i].name != NULL); - (void) strlcpy(propbuf, acl_inherit_table[i].name, proplen); + verify(zfs_prop_index_to_string(prop, val, &strval) == 0); + (void) strlcpy(propbuf, strval, proplen); break; case ZFS_PROP_CREATION: @@ -1737,10 +1886,12 @@ parent_name(const char *path, char *buf, size_t buflen) } /* - * Checks to make sure that the given path has a parent, and that it exists. + * Checks to make sure that the given path has a parent, and that it exists. We + * also fetch the 'zoned' property, which is used to validate property settings + * when creating new datasets. */ static int -check_parents(libzfs_handle_t *hdl, const char *path) +check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned) { zfs_cmd_t zc = { 0 }; char parent[ZFS_MAXNAMELEN]; @@ -1783,9 +1934,9 @@ check_parents(libzfs_handle_t *hdl, const char *path) } } + *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); /* we are in a non-global zone, but parent is in the global zone */ - if (getzoneid() != GLOBAL_ZONEID && - !zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { + if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { (void) zfs_standard_error(hdl, EPERM, errbuf); zfs_close(zhp); return (-1); @@ -1805,29 +1956,18 @@ check_parents(libzfs_handle_t *hdl, const char *path) } /* - * Create a new filesystem or volume. 'sizestr' and 'blocksizestr' are used - * only for volumes, and indicate the size and blocksize of the volume. + * Create a new filesystem or volume. */ int zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, - const char *sizestr, const char *blocksizestr) + nvlist_t *props) { zfs_cmd_t zc = { 0 }; int ret; uint64_t size = 0; uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); char errbuf[1024]; - - /* convert sizestr into integer size */ - if (sizestr != NULL && nicestrtonum(hdl, sizestr, &size) != 0) - return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN, - "bad volume size '%s'"), sizestr)); - - /* convert blocksizestr into integer blocksize */ - if (blocksizestr != NULL && nicestrtonum(hdl, blocksizestr, - &blocksize) != 0) - return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN, - "bad volume blocksize '%s'"), blocksizestr)); + uint64_t zoned; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); @@ -1837,7 +1977,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); /* validate parents exist */ - if (check_parents(hdl, path) != 0) + if (check_parents(hdl, path, &zoned) != 0) return (-1); /* @@ -1859,6 +1999,10 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, else zc.zc_objset_type = DMU_OST_ZFS; + if (props && (props = zfs_validate_properties(hdl, type, props, zoned, + NULL, errbuf)) == 0) + return (-1); + if (type == ZFS_TYPE_VOLUME) { /* * If we are creating a volume, the size and block size must @@ -1867,43 +2011,57 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, * volsize must be a multiple of the block size, and cannot be * zero. */ - if (size == 0) { + if (props == NULL || nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { + nvlist_free(props); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "cannot be zero")); - return (zfs_error(hdl, EZFS_BADPROP, - dgettext(TEXT_DOMAIN, "bad volume size '%s'"), - sizestr)); + "missing volume size")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + } + + if ((ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), + &blocksize)) != 0) { + if (ret == ENOENT) { + blocksize = zfs_prop_default_numeric( + ZFS_PROP_VOLBLOCKSIZE); + } else { + nvlist_free(props); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "missing volume block size")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); + } } - if (blocksize < SPA_MINBLOCKSIZE || - blocksize > SPA_MAXBLOCKSIZE || !ISP2(blocksize)) { + if (size == 0) { + nvlist_free(props); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be power of 2 from %u to %uk"), - (uint_t)SPA_MINBLOCKSIZE, - (uint_t)SPA_MAXBLOCKSIZE >> 10); - return (zfs_error(hdl, EZFS_BADPROP, - dgettext(TEXT_DOMAIN, - "bad volume block size '%s'"), blocksizestr)); + "volume size cannot be zero")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } if (size % blocksize != 0) { + nvlist_free(props); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be a multiple of volume block size")); - return (zfs_error(hdl, EZFS_BADPROP, - dgettext(TEXT_DOMAIN, "bad volume size '%s'"), - sizestr)); + "volume size must be a multiple of volume block " + "size")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } - - zc.zc_volsize = size; - zc.zc_volblocksize = blocksize; } + if (props && + zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0) + return (-1); + nvlist_free(props); + /* create the dataset */ ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); if (ret == 0 && type == ZFS_TYPE_VOLUME) ret = zvol_create_link(hdl, path); + zcmd_free_nvlists(&zc); + /* check for failure */ if (ret != 0) { char parent[ZFS_MAXNAMELEN]; @@ -1922,13 +2080,12 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, case EDOM: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must be power of 2 from %u to %uk"), + "volume block size must be power of 2 from " + "%u to %uk"), (uint_t)SPA_MINBLOCKSIZE, (uint_t)SPA_MAXBLOCKSIZE >> 10); - return (zfs_error(hdl, EZFS_BADPROP, - dgettext(TEXT_DOMAIN, "bad block size '%s'"), - blocksizestr ? blocksizestr : "<unknown>")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); #ifdef _ILP32 case EOVERFLOW: @@ -1960,11 +2117,7 @@ zfs_destroy(zfs_handle_t *zhp) (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - /* - * We use the check for 'zfs_volblocksize' instead of ZFS_TYPE_VOLUME - * so that we do the right thing for snapshots of volumes. - */ - if (zhp->zfs_volblocksize != 0) { + if (ZFS_IS_VOLUME(zhp)) { if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) return (-1); @@ -1997,9 +2150,9 @@ zfs_remove_link_cb(zfs_handle_t *zhp, void *arg) zfs_handle_t *szhp; char name[ZFS_MAXNAMELEN]; - (void) strcpy(name, zhp->zfs_name); - (void) strcat(name, "@"); - (void) strcat(name, dd->snapname); + (void) strlcpy(name, zhp->zfs_name, sizeof (name)); + (void) strlcat(name, "@", sizeof (name)); + (void) strlcat(name, dd->snapname, sizeof (name)); szhp = make_dataset_handle(zhp->zfs_hdl, name); if (szhp) { @@ -2039,7 +2192,7 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname) } (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - (void) strlcpy(zc.zc_prop_value, snapname, sizeof (zc.zc_prop_value)); + (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY_SNAPS, &zc); if (ret != 0) { @@ -2067,13 +2220,15 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname) * Clones the given dataset. The target must be of the same type as the source. */ int -zfs_clone(zfs_handle_t *zhp, const char *target) +zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) { zfs_cmd_t zc = { 0 }; char parent[ZFS_MAXNAMELEN]; int ret; char errbuf[1024]; libzfs_handle_t *hdl = zhp->zfs_hdl; + zfs_type_t type; + uint64_t zoned; assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); @@ -2085,21 +2240,39 @@ zfs_clone(zfs_handle_t *zhp, const char *target) return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); /* validate parents exist */ - if (check_parents(zhp->zfs_hdl, target) != 0) + if (check_parents(hdl, target, &zoned) != 0) return (-1); (void) parent_name(target, parent, sizeof (parent)); /* do the clone */ - if (zhp->zfs_volblocksize != 0) + if (ZFS_IS_VOLUME(zhp)) { zc.zc_objset_type = DMU_OST_ZVOL; - else + type = ZFS_TYPE_FILESYSTEM; + } else { zc.zc_objset_type = DMU_OST_ZFS; + type = ZFS_TYPE_VOLUME; + } + + if (props) { + if ((props = zfs_validate_properties(hdl, type, props, zoned, + zhp, errbuf)) == NULL) + return (-1); + + if (zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0) { + nvlist_free(props); + return (-1); + } + + nvlist_free(props); + } (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); - (void) strlcpy(zc.zc_filename, zhp->zfs_name, sizeof (zc.zc_filename)); + (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); + zcmd_free_nvlists(&zc); + if (ret != 0) { switch (errno) { @@ -2127,7 +2300,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target) return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf)); } - } else if (zhp->zfs_volblocksize != 0) { + } else if (ZFS_IS_VOLUME(zhp)) { ret = zvol_create_link(zhp->zfs_hdl, target); } @@ -2153,12 +2326,12 @@ promote_snap_cb(zfs_handle_t *zhp, void *data) return (0); /* Remove the device link if it's a zvol. */ - if (zhp->zfs_volblocksize != 0) + if (ZFS_IS_VOLUME(zhp)) (void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); /* Check for conflicting names */ - (void) strcpy(snapname, pd->cb_target); - (void) strcat(snapname, strchr(zhp->zfs_name, '@')); + (void) strlcpy(snapname, pd->cb_target, sizeof (snapname)); + (void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname)); szhp = make_dataset_handle(zhp->zfs_hdl, snapname); if (szhp != NULL) { zfs_close(szhp); @@ -2181,7 +2354,7 @@ promote_snap_done_cb(zfs_handle_t *zhp, void *data) return (0); /* Create the device link if it's a zvol. */ - if (zhp->zfs_volblocksize != 0) + if (ZFS_IS_VOLUME(zhp)) (void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); return (0); @@ -2211,7 +2384,7 @@ zfs_promote(zfs_handle_t *zhp) return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); } - (void) strcpy(parent, zhp->zfs_dmustats.dds_clone_of); + (void) strlcpy(parent, zhp->zfs_dmustats.dds_clone_of, sizeof (parent)); if (parent[0] == '\0') { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "not a cloned filesystem")); @@ -2240,8 +2413,8 @@ zfs_promote(zfs_handle_t *zhp) } /* issue the ioctl */ - (void) strlcpy(zc.zc_prop_value, zhp->zfs_dmustats.dds_clone_of, - sizeof (zc.zc_prop_value)); + (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_clone_of, + sizeof (zc.zc_value)); (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc); @@ -2278,13 +2451,14 @@ static int zfs_create_link_cb(zfs_handle_t *zhp, void *arg) { char *snapname = arg; + int ret; if (zhp->zfs_type == ZFS_TYPE_VOLUME) { char name[MAXPATHLEN]; - (void) strcpy(name, zhp->zfs_name); - (void) strcat(name, "@"); - (void) strcat(name, snapname); + (void) strlcpy(name, zhp->zfs_name, sizeof (name)); + (void) strlcat(name, "@", sizeof (name)); + (void) strlcat(name, snapname, sizeof (name)); (void) zvol_create_link(zhp->zfs_hdl, name); /* * NB: this is simply a best-effort. We don't want to @@ -2292,7 +2466,12 @@ zfs_create_link_cb(zfs_handle_t *zhp, void *arg) * the volumes. */ } - return (zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname)); + + ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname); + + zfs_close(zhp); + + return (ret); } /* @@ -2329,7 +2508,7 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive) } (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - (void) strlcpy(zc.zc_prop_value, delim+1, sizeof (zc.zc_prop_value)); + (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); zc.zc_cookie = recursive; ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT, &zc); @@ -2338,7 +2517,7 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive) * zc.zc_name. */ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_prop_value); + "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); if (ret == 0 && recursive) { (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, (char *)delim+1); @@ -2377,10 +2556,10 @@ zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from) /* do the ioctl() */ (void) strlcpy(zc.zc_name, zhp_to->zfs_name, sizeof (zc.zc_name)); if (zhp_from) { - (void) strlcpy(zc.zc_prop_value, zhp_from->zfs_name, + (void) strlcpy(zc.zc_value, zhp_from->zfs_name, sizeof (zc.zc_name)); } else { - zc.zc_prop_value[0] = '\0'; + zc.zc_value[0] = '\0'; } zc.zc_cookie = STDOUT_FILENO; @@ -2437,7 +2616,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, "cannot receive")); /* trim off snapname, if any */ - (void) strcpy(zc.zc_name, tosnap); + (void) strlcpy(zc.zc_name, tosnap, sizeof (zc.zc_name)); cp = strchr(zc.zc_name, '@'); if (cp) *cp = '\0'; @@ -2477,7 +2656,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, /* * Determine name of destination snapshot. */ - (void) strcpy(zc.zc_filename, tosnap); + (void) strlcpy(zc.zc_value, tosnap, sizeof (zc.zc_value)); if (isprefix) { if (strchr(tosnap, '@') != NULL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, @@ -2491,8 +2670,8 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, else cp++; - (void) strcat(zc.zc_filename, "/"); - (void) strcat(zc.zc_filename, cp); + (void) strcat(zc.zc_value, "/"); + (void) strcat(zc.zc_value, cp); } else if (strchr(tosnap, '@') == NULL) { /* * they specified just a filesystem; tack on the @@ -2501,7 +2680,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, cp = strchr(drr.drr_u.drr_begin.drr_toname, '@'); if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN) return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); - (void) strcat(zc.zc_filename, cp); + (void) strcat(zc.zc_value, cp); } if (drrb->drr_fromguid) { @@ -2509,7 +2688,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, /* incremental backup stream */ /* do the ioctl to the containing fs */ - (void) strcpy(zc.zc_name, zc.zc_filename); + (void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name)); cp = strchr(zc.zc_name, '@'); *cp = '\0'; @@ -2540,7 +2719,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, } else { /* full backup stream */ - (void) strcpy(zc.zc_name, zc.zc_filename); + (void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name)); /* make sure they aren't trying to receive into the root */ if (strchr(zc.zc_name, '/') == NULL) { @@ -2577,7 +2756,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, opname = dgettext(TEXT_DOMAIN, "create"); if (zfs_create(hdl, zc.zc_name, - ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0) { + ZFS_TYPE_FILESYSTEM, NULL) != 0) { if (errno == EEXIST) continue; goto ancestorerr; @@ -2623,16 +2802,14 @@ ancestorerr: *cp = '\0'; } - (void) strcpy(zc.zc_prop_value, tosnap); zc.zc_cookie = STDIN_FILENO; - zc.zc_intsz = isprefix; - zc.zc_numints = force; + zc.zc_guid = force; if (verbose) { (void) printf("%s %s stream of %s into %s\n", dryrun ? "would receive" : "receiving", drrb->drr_fromguid ? "incremental" : "full", drr.drr_u.drr_begin.drr_toname, - zc.zc_filename); + zc.zc_value); (void) fflush(stdout); } if (dryrun) @@ -2655,13 +2832,13 @@ ancestorerr: case EEXIST: if (drrb->drr_fromguid == 0) { /* it's the containing fs that exists */ - cp = strchr(zc.zc_filename, '@'); + cp = strchr(zc.zc_value, '@'); *cp = '\0'; } zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "destination already exists")); (void) zfs_error(hdl, EZFS_EXISTS, dgettext(TEXT_DOMAIN, - "cannot restore to %s"), zc.zc_filename); + "cannot restore to %s"), zc.zc_value); break; case EINVAL: (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); @@ -2683,12 +2860,12 @@ ancestorerr: * created). Also mount any children of the target filesystem * if we did an incremental receive. */ - cp = strchr(zc.zc_filename, '@'); + cp = strchr(zc.zc_value, '@'); if (cp && (ioctl_err == 0 || drrb->drr_fromguid)) { zfs_handle_t *h; *cp = '\0'; - h = zfs_open(hdl, zc.zc_filename, + h = zfs_open(hdl, zc.zc_value, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); *cp = '@'; if (h) { @@ -2696,7 +2873,7 @@ ancestorerr: err = zvol_create_link(hdl, h->zfs_name); if (err == 0 && ioctl_err == 0) err = zvol_create_link(hdl, - zc.zc_filename); + zc.zc_value); } else { if (drrb->drr_fromguid) { err = changelist_postfix(clp); @@ -2794,7 +2971,7 @@ do_rollback(zfs_handle_t *zhp) (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - if (zhp->zfs_volblocksize != 0) + if (ZFS_IS_VOLUME(zhp)) zc.zc_objset_type = DMU_OST_ZVOL; else zc.zc_objset_type = DMU_OST_ZFS; @@ -2973,8 +3150,10 @@ zfs_rename(zfs_handle_t *zhp, const char *target) } else { if (!zfs_validate_name(hdl, target, zhp->zfs_type)) return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); + uint64_t unused; + /* validate parents */ - if (check_parents(hdl, target) != 0) + if (check_parents(hdl, target, &unused) != 0) return (-1); (void) parent_name(target, parent, sizeof (parent)); @@ -3015,21 +3194,20 @@ zfs_rename(zfs_handle_t *zhp, const char *target) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "child dataset with inherited mountpoint is used " "in a non-global zone")); - ret = zfs_error(hdl, EZFS_ZONED, errbuf); + (void) zfs_error(hdl, EZFS_ZONED, errbuf); goto error; } if ((ret = changelist_prefix(cl)) != 0) goto error; - if (zhp->zfs_volblocksize != 0) + if (ZFS_IS_VOLUME(zhp)) zc.zc_objset_type = DMU_OST_ZVOL; else zc.zc_objset_type = DMU_OST_ZFS; (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - (void) strlcpy(zc.zc_prop_value, target, sizeof (zc.zc_prop_value)); - + (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) { (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); @@ -3128,3 +3306,259 @@ zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) return (0); } + +nvlist_t * +zfs_get_user_props(zfs_handle_t *zhp) +{ + return (zhp->zfs_user_props); +} + +/* + * Given a comma-separated list of properties, contruct a property list + * containing both user-defined and native properties. This function will + * return a NULL list if 'all' is specified, which can later be expanded on a + * per-dataset basis by zfs_expand_proplist(). + */ +int +zfs_get_proplist(libzfs_handle_t *hdl, char *fields, zfs_proplist_t **listp) +{ + int i; + size_t len; + char *s, *p; + char c; + zfs_prop_t prop; + zfs_proplist_t *entry; + zfs_proplist_t **last; + + *listp = NULL; + last = listp; + + /* + * If 'all' is specified, return a NULL list. + */ + if (strcmp(fields, "all") == 0) + return (0); + + /* + * If no fields were specified, return an error. + */ + if (fields[0] == '\0') { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "no properties specified")); + return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN, + "bad property list"))); + } + + /* + * It would be nice to use getsubopt() here, but the inclusion of column + * aliases makes this more effort than it's worth. + */ + s = fields; + while (*s != '\0') { + if ((p = strchr(s, ',')) == NULL) { + len = strlen(s); + p = s + len; + } else { + len = p - s; + } + + /* + * Check for empty options. + */ + if (len == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "empty property name")); + return (zfs_error(hdl, EZFS_BADPROP, + dgettext(TEXT_DOMAIN, "bad property list"))); + } + + /* + * Check all regular property names. + */ + c = s[len]; + s[len] = '\0'; + for (i = 0; i < ZFS_NPROP_ALL; i++) { + if ((prop = zfs_name_to_prop(s)) != ZFS_PROP_INVAL) + break; + } + + /* + * If no column is specified, and this isn't a user property, + * return failure. + */ + if (i == ZFS_NPROP_ALL && !zfs_prop_user(s)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid property '%s'"), s); + return (zfs_error(hdl, EZFS_BADPROP, + dgettext(TEXT_DOMAIN, "bad property list"))); + } + + if ((entry = zfs_alloc(hdl, sizeof (zfs_proplist_t))) == NULL) + return (-1); + + entry->pl_prop = prop; + if (prop == ZFS_PROP_INVAL) { + if ((entry->pl_user_prop = + zfs_strdup(hdl, s)) == NULL) { + free(entry); + return (-1); + } + entry->pl_width = strlen(s); + } else { + entry->pl_width = zfs_prop_width(prop, + &entry->pl_fixed); + } + + *last = entry; + last = &entry->pl_next; + + s = p; + if (c == ',') + s++; + } + + return (0); +} + +void +zfs_free_proplist(zfs_proplist_t *pl) +{ + zfs_proplist_t *next; + + while (pl != NULL) { + next = pl->pl_next; + free(pl->pl_user_prop); + free(pl); + pl = next; + } +} + +/* + * This function is used by 'zfs list' to determine the exact set of columns to + * display, and their maximum widths. This does two main things: + * + * - If this is a list of all properties, then expand the list to include + * all native properties, and set a flag so that for each dataset we look + * for new unique user properties and add them to the list. + * + * - For non fixed-width properties, keep track of the maximum width seen + * so that we can size the column appropriately. + */ +int +zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp) +{ + libzfs_handle_t *hdl = zhp->zfs_hdl; + zfs_prop_t prop; + zfs_proplist_t *entry; + zfs_proplist_t **last, **start; + nvlist_t *userprops, *propval; + nvpair_t *elem; + char *strval; + char buf[ZFS_MAXPROPLEN]; + + if (*plp == NULL) { + /* + * If this is the very first time we've been called for an 'all' + * specification, expand the list to include all native + * properties. + */ + last = plp; + for (prop = 0; prop < ZFS_NPROP_VISIBLE; prop++) { + if ((entry = zfs_alloc(hdl, + sizeof (zfs_proplist_t))) == NULL) + return (-1); + + entry->pl_prop = prop; + entry->pl_width = zfs_prop_width(prop, + &entry->pl_fixed); + entry->pl_all = B_TRUE; + + *last = entry; + last = &entry->pl_next; + } + + /* + * Add 'name' to the beginning of the list, which is handled + * specially. + */ + if ((entry = zfs_alloc(hdl, + sizeof (zfs_proplist_t))) == NULL) + return (-1); + + entry->pl_prop = ZFS_PROP_NAME; + entry->pl_width = zfs_prop_width(ZFS_PROP_NAME, + &entry->pl_fixed); + entry->pl_all = B_TRUE; + entry->pl_next = *plp; + *plp = entry; + } + + userprops = zfs_get_user_props(zhp); + + entry = *plp; + if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { + /* + * Go through and add any user properties as necessary. We + * start by incrementing our list pointer to the first + * non-native property. + */ + start = plp; + while (*start != NULL) { + if ((*start)->pl_prop == ZFS_PROP_INVAL) + break; + start = &(*start)->pl_next; + } + + elem = NULL; + while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { + /* + * See if we've already found this property in our list. + */ + for (last = start; *last != NULL; + last = &(*last)->pl_next) { + if (strcmp((*last)->pl_user_prop, + nvpair_name(elem)) == 0) + break; + } + + if (*last == NULL) { + if ((entry = zfs_alloc(hdl, + sizeof (zfs_proplist_t))) == NULL || + ((entry->pl_user_prop = zfs_strdup(hdl, + nvpair_name(elem)))) == NULL) { + free(entry); + return (-1); + } + + entry->pl_prop = ZFS_PROP_INVAL; + entry->pl_width = strlen(nvpair_name(elem)); + entry->pl_all = B_TRUE; + *last = entry; + } + } + } + + /* + * Now go through and check the width of any non-fixed columns + */ + for (entry = *plp; entry != NULL; entry = entry->pl_next) { + if (entry->pl_fixed) + continue; + + if (entry->pl_prop != ZFS_PROP_INVAL) { + if (zfs_prop_get(zhp, entry->pl_prop, + buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { + if (strlen(buf) > entry->pl_width) + entry->pl_width = strlen(buf); + } + } else if (nvlist_lookup_nvlist(userprops, + entry->pl_user_prop, &propval) == 0) { + verify(nvlist_lookup_string(propval, + ZFS_PROP_VALUE, &strval) == 0); + if (strlen(strval) > entry->pl_width) + entry->pl_width = strlen(strval); + } + } + + return (0); +} diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c index 8ea76e51a6..c283016df7 100644 --- a/usr/src/lib/libzfs/common/libzfs_graph.c +++ b/usr/src/lib/libzfs/common/libzfs_graph.c @@ -207,18 +207,15 @@ zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp, return (-1); if (zvp->zv_edgecount == zvp->zv_edgealloc) { - zfs_edge_t **newedges = zfs_alloc(hdl, zvp->zv_edgealloc * 2 * - sizeof (void *)); + void *ptr; - if (newedges == NULL) + if ((ptr = zfs_realloc(hdl, zvp->zv_edges, + zvp->zv_edgealloc * sizeof (void *), + zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL) return (-1); - bcopy(zvp->zv_edges, newedges, - zvp->zv_edgealloc * sizeof (void *)); - + zvp->zv_edges = ptr; zvp->zv_edgealloc *= 2; - free(zvp->zv_edges); - zvp->zv_edges = newedges; } zvp->zv_edges[zvp->zv_edgecount++] = zep; diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h index 3e0f737a4b..e4bc794cd8 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -60,14 +60,20 @@ struct zfs_handle { char zfs_name[ZFS_MAXNAMELEN]; zfs_type_t zfs_type; dmu_objset_stats_t zfs_dmustats; + zvol_stats_t zfs_volstats; nvlist_t *zfs_props; - uint64_t zfs_volsize; - uint64_t zfs_volblocksize; + nvlist_t *zfs_user_props; boolean_t zfs_mntcheck; char *zfs_mntopts; char zfs_root[MAXPATHLEN]; }; +/* + * This is different from checking zfs_type, because it will also catch + * snapshots of volumes. + */ +#define ZFS_IS_VOLUME(zhp) ((zhp)->zfs_volstats.zv_volblocksize != 0) + struct zpool_handle { libzfs_handle_t *zpool_hdl; char zpool_name[ZPOOL_MAXNAMELEN]; @@ -82,6 +88,7 @@ struct zpool_handle { int zfs_error(libzfs_handle_t *, int, const char *, ...); void zfs_error_aux(libzfs_handle_t *, const char *, ...); void *zfs_alloc(libzfs_handle_t *, size_t); +void *zfs_realloc(libzfs_handle_t *, void *, size_t, size_t); char *zfs_strdup(libzfs_handle_t *, const char *); int no_memory(libzfs_handle_t *); @@ -93,6 +100,12 @@ int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***, typedef struct prop_changelist prop_changelist_t; +int zcmd_alloc_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, size_t); +int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *, size_t *); +int zcmd_expand_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *); +int zcmd_read_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t **); +void zcmd_free_nvlists(zfs_cmd_t *); + int changelist_prefix(prop_changelist_t *); int changelist_postfix(prop_changelist_t *); void changelist_rename(prop_changelist_t *, const char *, const char *); diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c index 6b76b2922c..ea74079bee 100644 --- a/usr/src/lib/libzfs/common/libzfs_import.c +++ b/usr/src/lib/libzfs/common/libzfs_import.c @@ -382,7 +382,6 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) char *name; zfs_cmd_t zc = { 0 }; uint64_t version, guid; - char *packed; size_t len; int err; uint_t children = 0; @@ -575,50 +574,38 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) /* * Try to do the import in order to get vdev state. */ - if ((err = nvlist_size(config, &len, NV_ENCODE_NATIVE)) != 0) - goto nomem; - - if ((packed = zfs_alloc(hdl, len)) == NULL) - goto nomem; - - if ((err = nvlist_pack(config, &packed, &len, - NV_ENCODE_NATIVE, 0)) != 0) - goto nomem; + if (zcmd_write_src_nvlist(hdl, &zc, config, &len) != 0) + goto error; nvlist_free(config); config = NULL; - zc.zc_config_src_size = len; - zc.zc_config_src = (uint64_t)(uintptr_t)packed; - - zc.zc_config_dst_size = 2 * len; - if ((zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL) - goto nomem; + if (zcmd_alloc_dst_nvlist(hdl, &zc, len * 2) != 0) { + zcmd_free_nvlists(&zc); + goto error; + } while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT, &zc)) != 0 && errno == ENOMEM) { - free((void *)(uintptr_t)zc.zc_config_dst); - if ((zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL) - goto nomem; + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); + goto error; + } } - free(packed); - if (err) { (void) zpool_standard_error(hdl, errno, dgettext(TEXT_DOMAIN, "cannot discover pools")); - free((void *)(uintptr_t)zc.zc_config_dst); + zcmd_free_nvlists(&zc); goto error; } - if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, - zc.zc_config_dst_size, &config, 0) != 0) { - free((void *)(uintptr_t)zc.zc_config_dst); - goto nomem; + if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) { + zcmd_free_nvlists(&zc); + goto error; } - free((void *)(uintptr_t)zc.zc_config_dst); + + zcmd_free_nvlists(&zc); /* * Go through and update the paths for spares, now that we have @@ -640,6 +627,8 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) /* * Add this pool to the list of configs. */ + verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &name) == 0); if (nvlist_add_nvlist(ret, name, config) != 0) goto nomem; diff --git a/usr/src/lib/libzfs/common/libzfs_mount.c b/usr/src/lib/libzfs/common/libzfs_mount.c index 9a18777d4a..32c3191d46 100644 --- a/usr/src/lib/libzfs/common/libzfs_mount.c +++ b/usr/src/lib/libzfs/common/libzfs_mount.c @@ -151,6 +151,40 @@ zfs_is_mounted(zfs_handle_t *zhp, char **where) } /* + * Returns true if the given dataset is mountable, false otherwise. Returns the + * mountpoint in 'buf'. + */ +static boolean_t +zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, + zfs_source_t *source) +{ + char sourceloc[ZFS_MAXNAMELEN]; + zfs_source_t sourcetype; + + if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) + return (B_FALSE); + + verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, + &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); + + if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || + strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) + return (B_FALSE); + + if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) + return (B_FALSE); + + if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && + getzoneid() == GLOBAL_ZONEID) + return (B_FALSE); + + if (source) + *source = sourcetype; + + return (B_TRUE); +} + +/* * Mount the given filesystem. */ int @@ -166,22 +200,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) else (void) strlcpy(mntopts, options, sizeof (mntopts)); - /* ignore non-filesystems */ - if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, - sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) - return (0); - - /* return success if there is no mountpoint set */ - if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || - strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) - return (0); - - /* - * If the 'zoned' property is set, and we're in the global zone, simply - * return success. - */ - if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && - getzoneid() == GLOBAL_ZONEID) + if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); /* Create the directory if it doesn't already exist */ @@ -334,15 +353,7 @@ zfs_share(zfs_handle_t *zhp) FILE *fp; libzfs_handle_t *hdl = zhp->zfs_hdl; - /* ignore non-filesystems */ - if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) - return (0); - - /* return success if there is no mountpoint set */ - if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, - mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0 || - strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || - strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) + if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); /* return success if there are no share options */ @@ -352,14 +363,12 @@ zfs_share(zfs_handle_t *zhp) return (0); /* - * If the 'zoned' property is set, simply return success since: - * 1. in a global zone, a dataset should not be shared if it's - * managed in a local zone. - * 2. in a local zone, NFS server is not available. + * If the 'zoned' property is set, then zfs_is_mountable() will have + * already bailed out if we are in the global zone. But local + * zones cannot be NFS servers, so we ignore it for local zones as well. */ - if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { + if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) return (0); - } /* * Invoke the share(1M) command. We always do this, even if it's @@ -509,28 +518,19 @@ void remove_mountpoint(zfs_handle_t *zhp) { char mountpoint[ZFS_MAXPROPLEN]; - char source[ZFS_MAXNAMELEN]; - zfs_source_t sourcetype; - int zoneid = getzoneid(); + zfs_source_t source; - /* ignore non-filesystems */ - if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, - sizeof (mountpoint), &sourcetype, source, sizeof (source), - B_FALSE) != 0) + if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), + &source)) return; - if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 && - strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && - (sourcetype == ZFS_SRC_DEFAULT || - sourcetype == ZFS_SRC_INHERITED) && - (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) || - zoneid != GLOBAL_ZONEID)) { - + if (source == ZFS_SRC_DEFAULT || + source == ZFS_SRC_INHERITED) { /* * Try to remove the directory, silently ignoring any errors. * The filesystem may have since been removed or moved around, - * and this isn't really useful to the administrator in any - * way. + * and this error isn't really useful to the administrator in + * any way. */ (void) rmdir(mountpoint); } @@ -561,16 +561,15 @@ mount_cb(zfs_handle_t *zhp, void *data) } if (cbp->cb_alloc == cbp->cb_used) { - zfs_handle_t **datasets; + void *ptr; - if ((datasets = zfs_alloc(zhp->zfs_hdl, cbp->cb_alloc * 2 * - sizeof (void *))) == NULL) + if ((ptr = zfs_realloc(zhp->zfs_hdl, + cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), + cbp->cb_alloc * 2 * sizeof (void *))) == NULL) return (-1); + cbp->cb_datasets = ptr; - (void) memcpy(cbp->cb_datasets, datasets, - cbp->cb_alloc * sizeof (void *)); - free(cbp->cb_datasets); - cbp->cb_datasets = datasets; + cbp->cb_alloc *= 2; } cbp->cb_datasets[cbp->cb_used++] = zhp; @@ -706,23 +705,19 @@ zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force) alloc = 8; } else { - char **dest; + void *ptr; - if ((dest = zfs_alloc(hdl, + if ((ptr = zfs_realloc(hdl, mountpoints, + alloc * sizeof (void *), alloc * 2 * sizeof (void *))) == NULL) goto out; - (void) memcpy(dest, mountpoints, - alloc * sizeof (void *)); - free(mountpoints); - mountpoints = dest; + mountpoints = ptr; - if ((dest = zfs_alloc(hdl, + if ((ptr = zfs_realloc(hdl, datasets, + alloc * sizeof (void *), alloc * 2 * sizeof (void *))) == NULL) goto out; - (void) memcpy(dest, datasets, - alloc * sizeof (void *)); - free(datasets); - datasets = (zfs_handle_t **)dest; + datasets = ptr; alloc *= 2; } @@ -753,8 +748,7 @@ zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force) */ for (i = 0; i < used; i++) { if (is_shared(hdl, mountpoints[i]) && - unshare_one(hdl, datasets[i] ? datasets[i]->zfs_name : - mountpoints[i], mountpoints[i]) != 0) + unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) goto out; } @@ -765,7 +759,9 @@ zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force) for (i = 0; i < used; i++) { if (unmount_one(hdl, mountpoints[i], flags) != 0) goto out; + } + for (i = 0; i < used; i++) { if (datasets[i]) remove_mountpoint(datasets[i]); } diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index fd2d6cc175..35e865a151 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -344,10 +344,10 @@ zpool_get_root(zpool_handle_t *zhp, char *buf, size_t buflen) (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 || - zc.zc_root[0] == '\0') + zc.zc_value[0] == '\0') return (-1); - (void) strlcpy(buf, zc.zc_root, buflen); + (void) strlcpy(buf, zc.zc_value, buflen); return (0); } @@ -371,8 +371,6 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, const char *altroot) { zfs_cmd_t zc = { 0 }; - char *packed; - size_t len; char msg[1024]; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, @@ -385,27 +383,16 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, return (zfs_error(hdl, EZFS_BADPATH, dgettext(TEXT_DOMAIN, "bad alternate root '%s'"), altroot)); - if (nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) != 0) - return (no_memory(hdl)); - - if ((packed = zfs_alloc(hdl, len)) == NULL) + if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0) return (-1); - if (nvlist_pack(nvroot, &packed, &len, - NV_ENCODE_NATIVE, 0) != 0) { - free(packed); - return (no_memory(hdl)); - } - (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name)); - zc.zc_config_src = (uint64_t)(uintptr_t)packed; - zc.zc_config_src_size = len; if (altroot != NULL) - (void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root)); + (void) strlcpy(zc.zc_value, altroot, sizeof (zc.zc_value)); if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CREATE, &zc) != 0) { - free(packed); + zcmd_free_nvlists(&zc); switch (errno) { case EBUSY: @@ -447,17 +434,18 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, } } - free(packed); + zcmd_free_nvlists(&zc); /* * If this is an alternate root pool, then we automatically set the - * moutnpoint of the root dataset to be '/'. + * mountpoint of the root dataset to be '/'. */ if (altroot != NULL) { zfs_handle_t *zhp; verify((zhp = zfs_open(hdl, pool, ZFS_TYPE_ANY)) != NULL); - verify(zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, "/") == 0); + verify(zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), + "/") == 0); zfs_close(zhp); } @@ -519,9 +507,7 @@ zpool_destroy(zpool_handle_t *zhp) int zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) { - char *packed; - size_t len; - zfs_cmd_t zc; + zfs_cmd_t zc = { 0 }; int ret; libzfs_handle_t *hdl = zhp->zpool_hdl; char msg[1024]; @@ -539,16 +525,9 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) return (zfs_error(hdl, EZFS_BADVERSION, msg)); } - verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0); - - if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL) + if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0) return (-1); - - verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_config_src = (uint64_t)(uintptr_t)packed; - zc.zc_config_src_size = len; if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ADD, &zc) != 0) { switch (errno) { @@ -598,7 +577,7 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) ret = 0; } - free(packed); + zcmd_free_nvlists(&zc); return (ret); } @@ -635,9 +614,7 @@ int zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, const char *altroot) { - zfs_cmd_t zc; - char *packed; - size_t len; + zfs_cmd_t zc = { 0 }; char *thename; char *origname; int ret; @@ -663,23 +640,16 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name)); if (altroot != NULL) - (void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root)); + (void) strlcpy(zc.zc_value, altroot, sizeof (zc.zc_value)); else - zc.zc_root[0] = '\0'; + zc.zc_value[0] = '\0'; verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &zc.zc_guid) == 0); - verify(nvlist_size(config, &len, NV_ENCODE_NATIVE) == 0); - - if ((packed = zfs_alloc(hdl, len)) == NULL) + if (zcmd_write_src_nvlist(hdl, &zc, config, NULL) != 0) return (-1); - verify(nvlist_pack(config, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); - - zc.zc_config_src = (uint64_t)(uintptr_t)packed; - zc.zc_config_src_size = len; - ret = 0; if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_IMPORT, &zc) != 0) { char desc[1024]; @@ -722,7 +692,7 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, } } - free(packed); + zcmd_free_nvlists(&zc); return (ret); } @@ -979,9 +949,7 @@ zpool_vdev_attach(zpool_handle_t *zhp, { zfs_cmd_t zc = { 0 }; char msg[1024]; - char *packed; int ret; - size_t len; nvlist_t *tgt; boolean_t avail_spare; uint64_t val; @@ -1045,19 +1013,12 @@ zpool_vdev_attach(zpool_handle_t *zhp, return (zfs_error(hdl, EZFS_BADTARGET, msg)); } - verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0); - - if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL) + if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0) return (-1); - verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); - - zc.zc_config_src = (uint64_t)(uintptr_t)packed; - zc.zc_config_src_size = len; - ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ATTACH, &zc); - free(packed); + zcmd_free_nvlists(&zc); if (ret == 0) return (0); @@ -1224,7 +1185,7 @@ zpool_clear(zpool_handle_t *zhp, const char *path) if (path) (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot clear errors for %s"), - zc.zc_prop_value); + path); else (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot clear errors for %s"), @@ -1258,7 +1219,7 @@ do_zvol(zfs_handle_t *zhp, void *data) * We check for volblocksize intead of ZFS_TYPE_VOLUME so that we * correctly handle snapshots of volumes. */ - if (zhp->zfs_volblocksize != 0) { + if (ZFS_IS_VOLUME(zhp)) { if (linktype) ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); else @@ -1384,7 +1345,7 @@ set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path) zfs_cmd_t zc = { 0 }; (void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - (void) strncpy(zc.zc_prop_value, path, sizeof (zc.zc_prop_value)); + (void) strncpy(zc.zc_value, path, sizeof (zc.zc_value)); verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); @@ -1494,7 +1455,8 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) { zfs_cmd_t zc = { 0 }; uint64_t count; - zbookmark_t *zb; + zbookmark_t *zb = NULL; + libzfs_handle_t *hdl = zhp->zpool_hdl; int i, j; if (zhp->zpool_error_log != NULL) { @@ -1510,19 +1472,19 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) */ verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_ERRCOUNT, &count) == 0); - if ((zc.zc_config_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl, + if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl, count * sizeof (zbookmark_t))) == NULL) return (-1); - zc.zc_config_dst_size = count; + zc.zc_nvlist_dst_size = count; (void) strcpy(zc.zc_name, zhp->zpool_name); for (;;) { if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_ERROR_LOG, &zc) != 0) { - free((void *)(uintptr_t)zc.zc_config_dst); + free((void *)(uintptr_t)zc.zc_nvlist_dst); if (errno == ENOMEM) { - if ((zc.zc_config_dst = (uintptr_t) + if ((zc.zc_nvlist_dst = (uintptr_t) zfs_alloc(zhp->zpool_hdl, - zc.zc_config_dst_size)) == NULL) + zc.zc_nvlist_dst_size)) == NULL) return (-1); } else { return (-1); @@ -1535,13 +1497,14 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) /* * Sort the resulting bookmarks. This is a little confusing due to the * implementation of ZFS_IOC_ERROR_LOG. The bookmarks are copied last - * to first, and 'zc_config_dst_size' indicates the number of boomarks + * to first, and 'zc_nvlist_dst_size' indicates the number of boomarks * _not_ copied as part of the process. So we point the start of our * array appropriate and decrement the total number of elements. */ - zb = ((zbookmark_t *)(uintptr_t)zc.zc_config_dst) + - zc.zc_config_dst_size; - count -= zc.zc_config_dst_size; + zb = ((zbookmark_t *)(uintptr_t)zc.zc_nvlist_dst) + + zc.zc_nvlist_dst_size; + count -= zc.zc_nvlist_dst_size; + zc.zc_nvlist_dst = 0ULL; qsort(zb, count, sizeof (zbookmark_t), zbookmark_compare); @@ -1562,7 +1525,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) */ if (list == NULL) { *nelem = j; - free((void *)(uintptr_t)zc.zc_config_dst); + free((void *)(uintptr_t)zc.zc_nvlist_dst); return (0); } @@ -1573,7 +1536,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) */ if ((zhp->zpool_error_log = zfs_alloc(zhp->zpool_hdl, j * sizeof (nvlist_t *))) == NULL) { - free((void *)(uintptr_t)zc.zc_config_dst); + free((void *)(uintptr_t)zc.zc_nvlist_dst); return (-1); } @@ -1589,55 +1552,72 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) sizeof (zbookmark_t)) == 0) continue; - if (nvlist_alloc(&nv, NV_UNIQUE_NAME, - 0) != 0) + if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) goto nomem; - zhp->zpool_error_log[j] = nv; zc.zc_bookmark = zb[i]; - if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_BOOKMARK_NAME, - &zc) == 0) { - if (nvlist_add_string(nv, ZPOOL_ERR_DATASET, - zc.zc_prop_name) != 0 || - nvlist_add_string(nv, ZPOOL_ERR_OBJECT, - zc.zc_prop_value) != 0 || - nvlist_add_string(nv, ZPOOL_ERR_RANGE, - zc.zc_filename) != 0) - goto nomem; - } else { - (void) snprintf(buf, sizeof (buf), "%llx", - zb[i].zb_objset); - if (nvlist_add_string(nv, - ZPOOL_ERR_DATASET, buf) != 0) - goto nomem; - (void) snprintf(buf, sizeof (buf), "%llx", - zb[i].zb_object); - if (nvlist_add_string(nv, ZPOOL_ERR_OBJECT, - buf) != 0) - goto nomem; - (void) snprintf(buf, sizeof (buf), "lvl=%u blkid=%llu", - (int)zb[i].zb_level, (long long)zb[i].zb_blkid); - if (nvlist_add_string(nv, ZPOOL_ERR_RANGE, - buf) != 0) - goto nomem; + for (;;) { + if (ioctl(zhp->zpool_hdl->libzfs_fd, + ZFS_IOC_BOOKMARK_NAME, &zc) != 0) { + if (errno == ENOMEM) { + if (zcmd_expand_dst_nvlist(hdl, &zc) + != 0) { + zcmd_free_nvlists(&zc); + goto nomem; + } + + continue; + } else { + if (nvlist_alloc(&nv, NV_UNIQUE_NAME, + 0) != 0) + goto nomem; + + zhp->zpool_error_log[j] = nv; + (void) snprintf(buf, sizeof (buf), + "%llx", zb[i].zb_objset); + if (nvlist_add_string(nv, + ZPOOL_ERR_DATASET, buf) != 0) + goto nomem; + (void) snprintf(buf, sizeof (buf), + "%llx", zb[i].zb_object); + if (nvlist_add_string(nv, + ZPOOL_ERR_OBJECT, buf) != 0) + goto nomem; + (void) snprintf(buf, sizeof (buf), + "lvl=%u blkid=%llu", + (int)zb[i].zb_level, + (long long)zb[i].zb_blkid); + if (nvlist_add_string(nv, + ZPOOL_ERR_RANGE, buf) != 0) + goto nomem; + } + } else { + if (zcmd_read_dst_nvlist(hdl, &zc, + &zhp->zpool_error_log[j]) != 0) { + zcmd_free_nvlists(&zc); + goto nomem; + } + } + + break; } + zcmd_free_nvlists(&zc); + j++; } *list = zhp->zpool_error_log; *nelem = zhp->zpool_error_count; - - free((void *)(uintptr_t)zc.zc_config_dst); + free(zb); return (0); nomem: - free((void *)(uintptr_t)zc.zc_config_dst); - for (i = 0; i < zhp->zpool_error_count; i++) { - if (zhp->zpool_error_log[i]) - free(zhp->zpool_error_log[i]); - } + free(zb); + free((void *)(uintptr_t)zc.zc_nvlist_dst); + for (i = 0; i < zhp->zpool_error_count; i++) + nvlist_free(zhp->zpool_error_log[i]); free(zhp->zpool_error_log); zhp->zpool_error_log = NULL; return (no_memory(zhp->zpool_hdl)); diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index f2369efb5d..39fe569959 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -363,6 +363,24 @@ zfs_alloc(libzfs_handle_t *hdl, size_t size) } /* + * A safe form of realloc(), which also zeroes newly allocated space. + */ +void * +zfs_realloc(libzfs_handle_t *hdl, void *ptr, size_t oldsize, size_t newsize) +{ + void *ret; + + if ((ret = realloc(ptr, newsize)) == NULL) { + (void) no_memory(hdl); + free(ptr); + return (NULL); + } + + bzero((char *)ret + oldsize, (newsize - oldsize)); + return (ret); +} + +/* * A safe form of strdup() which will die if the allocation fails. */ char * @@ -475,3 +493,85 @@ zfs_get_handle(zfs_handle_t *zhp) { return (zhp->zfs_hdl); } + +/* + * Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from + * an ioctl(). + */ +int +zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len) +{ + if (len == 0) + len = 1024; + zc->zc_nvlist_dst_size = len; + if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t) + zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == NULL) + return (-1); + + return (0); +} + +/* + * Called when an ioctl() which returns an nvlist fails with ENOMEM. This will + * expand the nvlist to the size specified in 'zc_nvlist_dst_size', which was + * filled in by the kernel to indicate the actual required size. + */ +int +zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc) +{ + free((void *)(uintptr_t)zc->zc_nvlist_dst); + if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t) + zfs_alloc(hdl, zc->zc_nvlist_dst_size)) + == NULL) + return (-1); + + return (0); +} + +/* + * Called to free the destination nvlist stored in the command structure. This + * is only needed if the caller must abort abnormally. The various other + * zcmd_*() routines will free it on failure (or on success, for + * zcmd_read_nvlist). + */ +void +zcmd_free_nvlists(zfs_cmd_t *zc) +{ + free((void *)(uintptr_t)zc->zc_nvlist_src); + free((void *)(uintptr_t)zc->zc_nvlist_dst); +} + +int +zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl, + size_t *size) +{ + char *packed; + size_t len; + + verify(nvlist_size(nvl, &len, NV_ENCODE_NATIVE) == 0); + + if ((packed = zfs_alloc(hdl, len)) == NULL) + return (-1); + + verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); + + zc->zc_nvlist_src = (uint64_t)(uintptr_t)packed; + zc->zc_nvlist_src_size = len; + + if (size) + *size = len; + return (0); +} + +/* + * Unpacks an nvlist from the ZFS ioctl command structure. + */ +int +zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp) +{ + if (nvlist_unpack((void *)(uintptr_t)zc->zc_nvlist_dst, + zc->zc_nvlist_dst_size, nvlp, 0) != 0) + return (no_memory(hdl)); + + return (0); +} diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index 84ba2663c4..11474a8bee 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -38,8 +38,11 @@ SUNWprivate_1.1 { zfs_create; zfs_destroy; zfs_destroy_snaps; + zfs_expand_proplist; + zfs_free_proplist; zfs_get_handle; zfs_get_name; + zfs_get_user_props; zfs_get_proplist; zfs_get_type; zfs_is_mounted; @@ -56,7 +59,7 @@ SUNWprivate_1.1 { zfs_nicestrtonum; zfs_open; zfs_promote; - zfs_prop_column_format; + zfs_prop_align_right; zfs_prop_column_name; zfs_prop_default_numeric; zfs_prop_default_string; @@ -69,9 +72,10 @@ SUNWprivate_1.1 { zfs_prop_readonly; zfs_prop_set; zfs_prop_to_name; - zfs_prop_validate; + zfs_prop_user; zfs_prop_valid_for_type; zfs_prop_values; + zfs_prop_width; zfs_receive; zfs_refresh_properties; zfs_rename; diff --git a/usr/src/uts/common/fs/zfs/dmu.c b/usr/src/uts/common/fs/zfs/dmu.c index 8db58a884d..81536337ed 100644 --- a/usr/src/uts/common/fs/zfs/dmu.c +++ b/usr/src/uts/common/fs/zfs/dmu.c @@ -1796,17 +1796,16 @@ dmu_object_size_from_db(dmu_buf_t *db, uint32_t *blksize, u_longlong_t *nblk512) * human-readable format. */ int -spa_bookmark_name(spa_t *spa, zbookmark_t *zb, char *dsname, size_t dslen, - char *objname, size_t objlen, char *range, size_t rangelen) +spa_bookmark_name(spa_t *spa, zbookmark_t *zb, nvlist_t *nvl) { dsl_pool_t *dp; dsl_dataset_t *ds = NULL; objset_t *os = NULL; dnode_t *dn = NULL; int err, shift; - - if (dslen < MAXNAMELEN || objlen < 32 || rangelen < 64) - return (ENOSPC); + char dsname[MAXNAMELEN]; + char objname[32]; + char range[64]; dp = spa_get_dsl(spa); if (zb->zb_objset != 0) { @@ -1832,9 +1831,9 @@ spa_bookmark_name(spa_t *spa, zbookmark_t *zb, char *dsname, size_t dslen, if (zb->zb_object == DMU_META_DNODE_OBJECT) { - (void) strncpy(objname, "mdn", objlen); + (void) strncpy(objname, "mdn", sizeof (objname)); } else { - (void) snprintf(objname, objlen, "%lld", + (void) snprintf(objname, sizeof (objname), "%lld", (longlong_t)zb->zb_object); } @@ -1844,10 +1843,15 @@ spa_bookmark_name(spa_t *spa, zbookmark_t *zb, char *dsname, size_t dslen, shift = (dn->dn_datablkshift?dn->dn_datablkshift:SPA_MAXBLOCKSHIFT) + zb->zb_level * (dn->dn_indblkshift - SPA_BLKPTRSHIFT); - (void) snprintf(range, rangelen, "%llu-%llu", + (void) snprintf(range, sizeof (range), "%llu-%llu", (u_longlong_t)(zb->zb_blkid << shift), (u_longlong_t)((zb->zb_blkid+1) << shift)); + if ((err = nvlist_add_string(nvl, ZPOOL_ERR_DATASET, dsname)) != 0 || + (err = nvlist_add_string(nvl, ZPOOL_ERR_OBJECT, objname)) != 0 || + (err = nvlist_add_string(nvl, ZPOOL_ERR_RANGE, range)) != 0) + goto out; + out: if (dn) dnode_rele(dn, FTAG); diff --git a/usr/src/uts/common/fs/zfs/dsl_prop.c b/usr/src/uts/common/fs/zfs/dsl_prop.c index 0ad81ed920..5c615dad92 100644 --- a/usr/src/uts/common/fs/zfs/dsl_prop.c +++ b/usr/src/uts/common/fs/zfs/dsl_prop.c @@ -67,10 +67,13 @@ dsl_prop_get_impl(dsl_dir_t *dd, const char *propname, int intsz, int numint, void *buf, char *setpoint) { int err = ENOENT; + zfs_prop_t prop; if (setpoint) setpoint[0] = '\0'; + prop = zfs_name_to_prop(propname); + /* * Note: dd may be NULL, therefore we shouldn't dereference it * ouside this loop. @@ -85,6 +88,13 @@ dsl_prop_get_impl(dsl_dir_t *dd, const char *propname, dsl_dir_name(dd, setpoint); break; } + + /* + * Break out of this loop for non-inheritable properties. + */ + if (prop != ZFS_PROP_INVAL && + !zfs_prop_inheritable(prop)) + break; } if (err == ENOENT) err = dodefault(propname, intsz, numint, buf); @@ -403,7 +413,8 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp) zap_attribute_t za; char setpoint[MAXNAMELEN]; char *tmp; - nvlist_t *prop; + nvlist_t *propval; + zfs_prop_t prop; if (dsl_dataset_is_snapshot(ds)) { VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); @@ -422,10 +433,19 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp) for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); (err = zap_cursor_retrieve(&zc, &za)) == 0; zap_cursor_advance(&zc)) { - if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0) + /* + * Skip non-inheritable properties. + */ + if ((prop = zfs_name_to_prop(za.za_name)) != + ZFS_PROP_INVAL && !zfs_prop_inheritable(prop) && + dd != ds->ds_dir) + continue; + + if (nvlist_lookup_nvlist(*nvp, za.za_name, + &propval) == 0) continue; - VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME, + VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); if (za.za_integer_length == 1) { /* @@ -440,7 +460,7 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp) kmem_free(tmp, za.za_num_integers); break; } - VERIFY(nvlist_add_string(prop, + VERIFY(nvlist_add_string(propval, ZFS_PROP_VALUE, tmp) == 0); kmem_free(tmp, za.za_num_integers); } else { @@ -448,15 +468,15 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp) * Integer property */ ASSERT(za.za_integer_length == 8); - (void) nvlist_add_uint64(prop, ZFS_PROP_VALUE, - za.za_first_integer); + (void) nvlist_add_uint64(propval, + ZFS_PROP_VALUE, za.za_first_integer); } - VERIFY(nvlist_add_string(prop, + VERIFY(nvlist_add_string(propval, ZFS_PROP_SOURCE, setpoint) == 0); VERIFY(nvlist_add_nvlist(*nvp, za.za_name, - prop) == 0); - nvlist_free(prop); + propval) == 0); + nvlist_free(propval); } zap_cursor_fini(&zc); diff --git a/usr/src/uts/common/fs/zfs/sys/spa.h b/usr/src/uts/common/fs/zfs/sys/spa.h index 829c025af2..4f2eb50d1f 100644 --- a/usr/src/uts/common/fs/zfs/sys/spa.h +++ b/usr/src/uts/common/fs/zfs/sys/spa.h @@ -439,8 +439,8 @@ extern int spa_get_errlog(spa_t *spa, void *uaddr, size_t *count); extern void spa_errlog_rotate(spa_t *spa); extern void spa_errlog_drain(spa_t *spa); extern void spa_errlog_sync(spa_t *spa, uint64_t txg); -extern int spa_bookmark_name(spa_t *spa, struct zbookmark *zb, char *ds, - size_t dsname, char *obj, size_t objname, char *range, size_t rangelen); +extern int spa_bookmark_name(spa_t *spa, struct zbookmark *zb, + nvlist_t *nvl); extern void spa_get_errlists(spa_t *spa, avl_tree_t *last, avl_tree_t *scrub); /* Initialization and termination */ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h index 34057e83c9..e3a653c5fa 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h @@ -77,11 +77,11 @@ typedef struct zfs_acl { * whereas acl_inherit has secure instead of groupmask. */ -#define DISCARD 0 -#define NOALLOW 1 -#define GROUPMASK 2 -#define PASSTHROUGH 3 -#define SECURE 4 +#define ZFS_ACL_DISCARD 0 +#define ZFS_ACL_NOALLOW 1 +#define ZFS_ACL_GROUPMASK 2 +#define ZFS_ACL_PASSTHROUGH 3 +#define ZFS_ACL_SECURE 4 struct znode; diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h index a8f1f4ed89..b4f834e186 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h @@ -31,6 +31,7 @@ #include <sys/cred.h> #include <sys/dmu.h> #include <sys/zio.h> +#include <sys/zvol.h> #ifdef __cplusplus extern "C" { @@ -115,29 +116,29 @@ typedef struct zinject_record { typedef struct zfs_cmd { char zc_name[MAXPATHLEN]; - char zc_prop_name[MAXNAMELEN]; - char zc_prop_value[MAXPATHLEN]; - char zc_root[MAXPATHLEN]; - char zc_filename[MAXNAMELEN]; - uint32_t zc_intsz; - uint32_t zc_numints; + char zc_value[MAXPATHLEN]; uint64_t zc_guid; - uint64_t zc_config_src; /* really (char *) */ - uint64_t zc_config_src_size; - uint64_t zc_config_dst; /* really (char *) */ - uint64_t zc_config_dst_size; + uint64_t zc_nvlist_src; /* really (char *) */ + uint64_t zc_nvlist_src_size; + uint64_t zc_nvlist_dst; /* really (char *) */ + uint64_t zc_nvlist_dst_size; uint64_t zc_cookie; uint64_t zc_cred; uint64_t zc_dev; - uint64_t zc_volsize; - uint64_t zc_volblocksize; uint64_t zc_objset_type; dmu_objset_stats_t zc_objset_stats; + zvol_stats_t zc_vol_stats; struct drr_begin zc_begin_record; zinject_record_t zc_inject_record; zbookmark_t zc_bookmark; } zfs_cmd_t; +typedef struct zfs_create_data { + cred_t *zc_cred; + dev_t zc_dev; + nvlist_t *zc_props; +} zfs_create_data_t; + #define ZVOL_MAX_MINOR (1 << 16) #define ZFS_MIN_MINOR (ZVOL_MAX_MINOR + 1) @@ -145,30 +146,9 @@ typedef struct zfs_cmd { extern dev_info_t *zfs_dip; -extern int zfs_secpolicy_write(const char *dataset, const char *, cred_t *cr); +extern int zfs_secpolicy_write(const char *dataset, cred_t *cr); extern int zfs_busy(void); -extern int zvol_check_volsize(zfs_cmd_t *zc, uint64_t blocksize); -extern int zvol_check_volblocksize(zfs_cmd_t *zc); -extern int zvol_get_stats(zfs_cmd_t *zc, objset_t *os); -extern void zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx); -extern int zvol_create_minor(zfs_cmd_t *zc); -extern int zvol_remove_minor(zfs_cmd_t *zc); -extern int zvol_set_volsize(zfs_cmd_t *zc); -extern int zvol_set_volblocksize(zfs_cmd_t *zc); -extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr); -extern int zvol_close(dev_t dev, int flag, int otyp, cred_t *cr); -extern int zvol_strategy(buf_t *bp); -extern int zvol_read(dev_t dev, uio_t *uiop, cred_t *cr); -extern int zvol_write(dev_t dev, uio_t *uiop, cred_t *cr); -extern int zvol_aread(dev_t dev, struct aio_req *aio, cred_t *cr); -extern int zvol_awrite(dev_t dev, struct aio_req *aio, cred_t *cr); -extern int zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp); -extern int zvol_busy(void); -extern void zvol_init(void); -extern void zvol_fini(void); - #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/fs/zfs/sys/zvol.h b/usr/src/uts/common/fs/zfs/sys/zvol.h new file mode 100644 index 0000000000..9ddbfc2e21 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zvol.h @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_ZVOL_H +#define _SYS_ZVOL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/zfs_context.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zvol_stats { + uint64_t zv_volsize; + uint64_t zv_volblocksize; +} zvol_stats_t; + +#ifdef _KERNEL +extern int zvol_check_volsize(uint64_t volsize, uint64_t blocksize); +extern int zvol_check_volblocksize(uint64_t volblocksize); +extern int zvol_get_stats(objset_t *os, zvol_stats_t *zvs); +extern void zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx); +extern int zvol_create_minor(const char *, dev_t); +extern int zvol_remove_minor(const char *); +extern int zvol_set_volsize(const char *, dev_t, uint64_t); +extern int zvol_set_volblocksize(const char *, uint64_t); + +extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr); +extern int zvol_close(dev_t dev, int flag, int otyp, cred_t *cr); +extern int zvol_strategy(buf_t *bp); +extern int zvol_read(dev_t dev, uio_t *uiop, cred_t *cr); +extern int zvol_write(dev_t dev, uio_t *uiop, cred_t *cr); +extern int zvol_aread(dev_t dev, struct aio_req *aio, cred_t *cr); +extern int zvol_awrite(dev_t dev, struct aio_req *aio, cred_t *cr); +extern int zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, + int *rvalp); +extern int zvol_busy(void); +extern void zvol_init(void); +extern void zvol_fini(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ZVOL_H */ diff --git a/usr/src/uts/common/fs/zfs/zfs_acl.c b/usr/src/uts/common/fs/zfs/zfs_acl.c index 310ff9c917..430625b84f 100644 --- a/usr/src/uts/common/fs/zfs/zfs_acl.c +++ b/usr/src/uts/common/fs/zfs/zfs_acl.c @@ -788,7 +788,7 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp, } - if (zfsvfs->z_acl_mode == DISCARD) { + if (zfsvfs->z_acl_mode == ZFS_ACL_DISCARD) { zfs_ace_remove(aclp, i); continue; } @@ -820,7 +820,7 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp, * This is only applicable when the acl_mode * property == groupmask. */ - if (zfsvfs->z_acl_mode == GROUPMASK) { + if (zfsvfs->z_acl_mode == ZFS_ACL_GROUPMASK) { reuse_deny = zfs_reuse_deny(acep, i); @@ -902,7 +902,7 @@ zfs_acl_chmod_setattr(znode_t *zp, uint64_t mode, dmu_tx_t *tx) static void zfs_securemode_update(zfsvfs_t *zfsvfs, ace_t *acep) { - if ((zfsvfs->z_acl_inherit == SECURE) && + if ((zfsvfs->z_acl_inherit == ZFS_ACL_SECURE) && (acep->a_type == ALLOW)) acep->a_access_mask &= ~SECURE_CLEAR; } @@ -924,10 +924,10 @@ zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp) i = j = 0; pace_cnt = paclp->z_acl_count; pacep = paclp->z_acl; - if (zfsvfs->z_acl_inherit != DISCARD) { + if (zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) { for (i = 0; i != pace_cnt; i++) { - if (zfsvfs->z_acl_inherit == NOALLOW && + if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW && pacep[i].a_type == ALLOW) continue; @@ -941,12 +941,12 @@ zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp) } aclp = zfs_acl_alloc(ace_cnt + OGE_PAD); - if (ace_cnt && zfsvfs->z_acl_inherit != DISCARD) { + if (ace_cnt && zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) { acep = aclp->z_acl; pacep = paclp->z_acl; for (i = 0; i != pace_cnt; i++) { - if (zfsvfs->z_acl_inherit == NOALLOW && + if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW && pacep[i].a_type == ALLOW) continue; diff --git a/usr/src/uts/common/fs/zfs/zfs_ctldir.c b/usr/src/uts/common/fs/zfs/zfs_ctldir.c index e890530701..cb0807aa56 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c +++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c @@ -513,7 +513,7 @@ zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, err = zfsctl_snapshot_zname(sdvp, snm, MAXNAMELEN, from); if (err) return (err); - err = zfs_secpolicy_write(from, NULL, cr); + err = zfs_secpolicy_write(from, cr); if (err) return (err); @@ -558,7 +558,7 @@ zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) err = zfsctl_snapshot_zname(dvp, name, MAXNAMELEN, snapname); if (err) return (err); - err = zfs_secpolicy_write(snapname, NULL, cr); + err = zfs_secpolicy_write(snapname, cr); if (err) return (err); diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index 029138ff3a..e51c1587df 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -58,6 +58,7 @@ #include <sys/zfs_ctldir.h> #include "zfs_namecheck.h" +#include "zfs_prop.h" extern struct modlfs zfs_modlfs; @@ -68,7 +69,7 @@ ldi_ident_t zfs_li = NULL; dev_info_t *zfs_dip; typedef int zfs_ioc_func_t(zfs_cmd_t *); -typedef int zfs_secpolicy_func_t(const char *, const char *, cred_t *); +typedef int zfs_secpolicy_func_t(const char *, cred_t *); typedef struct zfs_ioc_vec { zfs_ioc_func_t *zvec_func; @@ -122,7 +123,7 @@ __dprintf(const char *file, const char *func, int line, const char *fmt, ...) */ /* ARGSUSED */ static int -zfs_secpolicy_none(const char *unused1, const char *unused2, cred_t *cr) +zfs_secpolicy_none(const char *unused1, cred_t *cr) { return (0); } @@ -133,7 +134,7 @@ zfs_secpolicy_none(const char *unused1, const char *unused2, cred_t *cr) */ /* ARGSUSED */ static int -zfs_secpolicy_read(const char *dataset, const char *unused, cred_t *cr) +zfs_secpolicy_read(const char *dataset, cred_t *cr) { if (INGLOBALZONE(curproc) || zone_dataset_visible(dataset, NULL)) @@ -184,9 +185,8 @@ zfs_dozonecheck(const char *dataset, cred_t *cr) * Policy for dataset write operations (create children, set properties, etc). * Requires SYS_MOUNT privilege, and must be writable in the local zone. */ -/* ARGSUSED */ int -zfs_secpolicy_write(const char *dataset, const char *unused, cred_t *cr) +zfs_secpolicy_write(const char *dataset, cred_t *cr) { int error; @@ -201,7 +201,7 @@ zfs_secpolicy_write(const char *dataset, const char *unused, cred_t *cr) * create, destroy, snapshot, clone, restore. */ static int -zfs_secpolicy_parent(const char *dataset, const char *unused, cred_t *cr) +zfs_secpolicy_parent(const char *dataset, cred_t *cr) { char parentname[MAXNAMELEN]; char *cp; @@ -221,65 +221,7 @@ zfs_secpolicy_parent(const char *dataset, const char *unused, cred_t *cr) } - return (zfs_secpolicy_write(parentname, unused, cr)); -} - -/* - * Policy for dataset write operations (create children, set properties, etc). - * Requires SYS_MOUNT privilege, and must be writable in the local zone. - */ -static int -zfs_secpolicy_setprop(const char *dataset, const char *prop, cred_t *cr) -{ - int error; - - if (error = zfs_dozonecheck(dataset, cr)) - return (error); - - if (strcmp(prop, "zoned") == 0) { - /* - * Disallow setting of 'zoned' from within a local zone. - */ - if (!INGLOBALZONE(curproc)) - return (EPERM); - } - - return (secpolicy_zfs(cr)); -} - -/* - * Security policy for setting the quota. This is the same as - * zfs_secpolicy_write, except that the local zone may not change the quota at - * the zone-property setpoint. - */ -/* ARGSUSED */ -static int -zfs_secpolicy_quota(const char *dataset, const char *unused, cred_t *cr) -{ - int error; - - if (error = zfs_dozonecheck(dataset, cr)) - return (error); - - if (!INGLOBALZONE(curproc)) { - uint64_t zoned; - char setpoint[MAXNAMELEN]; - int dslen; - /* - * Unprivileged users are allowed to modify the quota - * on things *under* (ie. contained by) the thing they - * own. - */ - if (dsl_prop_get_integer(dataset, "zoned", &zoned, setpoint)) - return (EPERM); - if (!zoned) /* this shouldn't happen */ - return (EPERM); - dslen = strlen(dataset); - if (dslen <= strlen(setpoint)) - return (EPERM); - } - - return (secpolicy_zfs(cr)); + return (zfs_secpolicy_write(parentname, cr)); } /* @@ -288,7 +230,7 @@ zfs_secpolicy_quota(const char *dataset, const char *unused, cred_t *cr) */ /* ARGSUSED */ static int -zfs_secpolicy_config(const char *unused, const char *unused2, cred_t *cr) +zfs_secpolicy_config(const char *unused, cred_t *cr) { if (secpolicy_sys_config(cr, B_FALSE) != 0) return (EPERM); @@ -301,7 +243,7 @@ zfs_secpolicy_config(const char *unused, const char *unused2, cred_t *cr) */ /* ARGSUSED */ static int -zfs_secpolicy_inject(const char *unused, const char *unused2, cred_t *cr) +zfs_secpolicy_inject(const char *unused, cred_t *cr) { return (secpolicy_zinject(cr)); } @@ -310,7 +252,7 @@ zfs_secpolicy_inject(const char *unused, const char *unused2, cred_t *cr) * Returns the nvlist as specified by the user in the zfs_cmd_t. */ static int -get_config(zfs_cmd_t *zc, nvlist_t **nvp) +get_nvlist(zfs_cmd_t *zc, nvlist_t **nvp) { char *packed; size_t size; @@ -318,16 +260,14 @@ get_config(zfs_cmd_t *zc, nvlist_t **nvp) nvlist_t *config = NULL; /* - * Read in and unpack the user-supplied nvlist. By this point, we know - * that the user has the SYS_CONFIG privilege, so allocating arbitrary - * sized regions of memory should not be a problem. + * Read in and unpack the user-supplied nvlist. */ - if ((size = zc->zc_config_src_size) == 0) + if ((size = zc->zc_nvlist_src_size) == 0) return (EINVAL); packed = kmem_alloc(size, KM_SLEEP); - if ((error = xcopyin((void *)(uintptr_t)zc->zc_config_src, packed, + if ((error = xcopyin((void *)(uintptr_t)zc->zc_nvlist_src, packed, size)) != 0) { kmem_free(packed, size); return (error); @@ -345,16 +285,39 @@ get_config(zfs_cmd_t *zc, nvlist_t **nvp) } static int +put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) +{ + char *packed = NULL; + size_t size; + int error; + + VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0); + + if (size > zc->zc_nvlist_dst_size) { + error = ENOMEM; + } else { + VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE, + KM_SLEEP) == 0); + error = xcopyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, + size); + kmem_free(packed, size); + } + + zc->zc_nvlist_dst_size = size; + return (error); +} + +static int zfs_ioc_pool_create(zfs_cmd_t *zc) { int error; nvlist_t *config; - if ((error = get_config(zc, &config)) != 0) + if ((error = get_nvlist(zc, &config)) != 0) return (error); - error = spa_create(zc->zc_name, config, zc->zc_root[0] == '\0' ? - NULL : zc->zc_root); + error = spa_create(zc->zc_name, config, zc->zc_value[0] == '\0' ? + NULL : zc->zc_value); nvlist_free(config); @@ -374,7 +337,7 @@ zfs_ioc_pool_import(zfs_cmd_t *zc) nvlist_t *config; uint64_t guid; - if ((error = get_config(zc, &config)) != 0) + if ((error = get_nvlist(zc, &config)) != 0) return (error); if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || @@ -382,7 +345,7 @@ zfs_ioc_pool_import(zfs_cmd_t *zc) error = EINVAL; else error = spa_import(zc->zc_name, config, - zc->zc_root[0] == '\0' ? NULL : zc->zc_root); + zc->zc_value[0] == '\0' ? NULL : zc->zc_value); nvlist_free(config); @@ -399,25 +362,13 @@ static int zfs_ioc_pool_configs(zfs_cmd_t *zc) { nvlist_t *configs; - char *packed = NULL; - size_t size = 0; int error; if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) return (EEXIST); - VERIFY(nvlist_pack(configs, &packed, &size, NV_ENCODE_NATIVE, - KM_SLEEP) == 0); + error = put_nvlist(zc, configs); - if (size > zc->zc_config_dst_size) - error = ENOMEM; - else - error = xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst, - size); - - zc->zc_config_dst_size = size; - - kmem_free(packed, size); nvlist_free(configs); return (error); @@ -427,27 +378,14 @@ static int zfs_ioc_pool_stats(zfs_cmd_t *zc) { nvlist_t *config; - char *packed = NULL; - size_t size = 0; int error; int ret = 0; - error = spa_get_stats(zc->zc_name, &config, zc->zc_root, - sizeof (zc->zc_root)); + error = spa_get_stats(zc->zc_name, &config, zc->zc_value, + sizeof (zc->zc_value)); if (config != NULL) { - VERIFY(nvlist_pack(config, &packed, &size, - NV_ENCODE_NATIVE, KM_SLEEP) == 0); - - if (size > zc->zc_config_dst_size) - ret = ENOMEM; - else if (xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst, - size)) - ret = EFAULT; - - zc->zc_config_dst_size = size; - - kmem_free(packed, size); + ret = put_nvlist(zc, config); nvlist_free(config); /* @@ -471,11 +409,9 @@ static int zfs_ioc_pool_tryimport(zfs_cmd_t *zc) { nvlist_t *tryconfig, *config; - char *packed = NULL; - size_t size = 0; int error; - if ((error = get_config(zc, &tryconfig)) != 0) + if ((error = get_nvlist(zc, &tryconfig)) != 0) return (error); config = spa_tryimport(tryconfig); @@ -485,18 +421,7 @@ zfs_ioc_pool_tryimport(zfs_cmd_t *zc) if (config == NULL) return (EINVAL); - VERIFY(nvlist_pack(config, &packed, &size, NV_ENCODE_NATIVE, - KM_SLEEP) == 0); - - if (size > zc->zc_config_dst_size) - error = ENOMEM; - else - error = xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst, - size); - - zc->zc_config_dst_size = size; - - kmem_free(packed, size); + error = put_nvlist(zc, config); nvlist_free(config); return (error); @@ -555,7 +480,7 @@ zfs_ioc_vdev_add(zfs_cmd_t *zc) if (error != 0) return (error); - if ((error = get_config(zc, &config)) == 0) { + if ((error = get_nvlist(zc, &config)) == 0) { error = spa_vdev_add(spa, config); nvlist_free(config); } @@ -619,7 +544,7 @@ zfs_ioc_vdev_attach(zfs_cmd_t *zc) if (error != 0) return (error); - if ((error = get_config(zc, &config)) == 0) { + if ((error = get_nvlist(zc, &config)) == 0) { error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); nvlist_free(config); } @@ -648,7 +573,7 @@ static int zfs_ioc_vdev_setpath(zfs_cmd_t *zc) { spa_t *spa; - char *path = zc->zc_prop_value; + char *path = zc->zc_value; uint64_t guid = zc->zc_guid; int error; @@ -668,8 +593,6 @@ zfs_ioc_objset_stats(zfs_cmd_t *zc) objset_t *os = NULL; int error; nvlist_t *nv; - size_t sz; - char *buf; retry: error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, @@ -691,27 +614,16 @@ retry: dmu_objset_stats(os, &zc->zc_objset_stats); - if (zc->zc_config_src != NULL && + if (zc->zc_nvlist_dst != NULL && (error = dsl_prop_get_all(os, &nv)) == 0) { - VERIFY(nvlist_size(nv, &sz, NV_ENCODE_NATIVE) == 0); - if (sz > zc->zc_config_src_size) { - zc->zc_config_src_size = sz; - error = ENOMEM; - } else { - buf = kmem_alloc(sz, KM_SLEEP); - VERIFY(nvlist_pack(nv, &buf, &sz, - NV_ENCODE_NATIVE, 0) == 0); - error = xcopyout(buf, - (void *)(uintptr_t)zc->zc_config_src, sz); - kmem_free(buf, sz); - } + error = put_nvlist(zc, nv); nvlist_free(nv); } if (!error && zc->zc_objset_stats.dds_type == DMU_OST_ZVOL) - error = zvol_get_stats(zc, os); + error = zvol_get_stats(os, &zc->zc_vol_stats); - spa_altroot(dmu_objset_spa(os), zc->zc_root, sizeof (zc->zc_root)); + spa_altroot(dmu_objset_spa(os), zc->zc_value, sizeof (zc->zc_value)); dmu_objset_close(os); return (error); @@ -818,46 +730,215 @@ retry: } static int -zfs_ioc_set_prop(zfs_cmd_t *zc) +zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl) { - return (dsl_prop_set(zc->zc_name, zc->zc_prop_name, - zc->zc_intsz, zc->zc_numints, zc->zc_prop_value)); -} + nvpair_t *elem; + int error; + const char *propname; + zfs_prop_t prop; + uint64_t intval; + char *strval; + + elem = NULL; + while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { + propname = nvpair_name(elem); + + if ((prop = zfs_name_to_prop(propname)) == + ZFS_PROP_INVAL) { + /* + * If this is a user-defined property, it must be a + * string, and there is no further validation to do. + */ + if (!zfs_prop_user(propname) || + nvpair_type(elem) != DATA_TYPE_STRING) + return (EINVAL); + + VERIFY(nvpair_value_string(elem, &strval) == 0); + error = dsl_prop_set(name, propname, 1, + strlen(strval) + 1, strval); + if (error == 0) + continue; + else + break; + } -static int -zfs_ioc_set_quota(zfs_cmd_t *zc) -{ - return (dsl_dir_set_quota(zc->zc_name, zc->zc_cookie)); -} + /* + * Check permissions for special properties. + */ + switch (prop) { + case ZFS_PROP_ZONED: + /* + * Disallow setting of 'zoned' from within a local zone. + */ + if (!INGLOBALZONE(curproc)) + return (EPERM); + break; -static int -zfs_ioc_set_reservation(zfs_cmd_t *zc) -{ - return (dsl_dir_set_reservation(zc->zc_name, zc->zc_cookie)); -} + case ZFS_PROP_QUOTA: + if (error = zfs_dozonecheck(name, cr)) + return (error); -static int -zfs_ioc_set_volsize(zfs_cmd_t *zc) -{ - return (zvol_set_volsize(zc)); + if (!INGLOBALZONE(curproc)) { + uint64_t zoned; + char setpoint[MAXNAMELEN]; + int dslen; + /* + * Unprivileged users are allowed to modify the + * quota on things *under* (ie. contained by) + * the thing they own. + */ + if (dsl_prop_get_integer(name, "zoned", &zoned, + setpoint)) + return (EPERM); + if (!zoned) /* this shouldn't happen */ + return (EPERM); + dslen = strlen(name); + if (dslen <= strlen(setpoint)) + return (EPERM); + } + } + + switch (prop) { + case ZFS_PROP_QUOTA: + if ((error = nvpair_value_uint64(elem, &intval)) != 0 || + (error = dsl_dir_set_quota(name, + intval)) != 0) + return (error); + break; + + case ZFS_PROP_RESERVATION: + if ((error = nvpair_value_uint64(elem, &intval)) != 0 || + (error = dsl_dir_set_reservation(name, + intval)) != 0) + return (error); + break; + + case ZFS_PROP_VOLSIZE: + if ((error = nvpair_value_uint64(elem, &intval)) != 0 || + (error = zvol_set_volsize(name, dev, + intval)) != 0) + return (error); + break; + + case ZFS_PROP_VOLBLOCKSIZE: + if ((error = nvpair_value_uint64(elem, &intval)) != 0 || + (error = zvol_set_volblocksize(name, + intval)) != 0) + return (error); + break; + + default: + if (nvpair_type(elem) == DATA_TYPE_STRING) { + if (zfs_prop_get_type(prop) != + prop_type_string) + return (EINVAL); + ASSERT(nvpair_value_string(elem, &strval) == 0); + error = dsl_prop_set(name, + nvpair_name(elem), 1, strlen(strval) + 1, + strval); + } else if (nvpair_type(elem) == DATA_TYPE_UINT64) { + ASSERT(nvpair_value_uint64(elem, &intval) == 0); + + switch (zfs_prop_get_type(prop)) { + case prop_type_number: + break; + case prop_type_boolean: + if (intval > 1) + error = EINVAL; + break; + case prop_type_string: + error = EINVAL; + break; + case prop_type_index: + switch (prop) { + case ZFS_PROP_CHECKSUM: + if (intval >= + ZIO_CHECKSUM_FUNCTIONS) + error = EINVAL; + break; + case ZFS_PROP_COMPRESSION: + if (intval >= + ZIO_COMPRESS_FUNCTIONS) + error = EINVAL; + break; + case ZFS_PROP_SNAPDIR: + if (intval > + ZFS_SNAPDIR_VISIBLE) + error = EINVAL; + break; + case ZFS_PROP_ACLMODE: + if (intval > + ZFS_ACL_PASSTHROUGH) + error = EINVAL; + break; + case ZFS_PROP_ACLINHERIT: + if (intval > ZFS_ACL_SECURE || + intval == ZFS_ACL_GROUPMASK) + error = EINVAL; + break; + default: + cmn_err(CE_PANIC, + "unknown index property"); + } + break; + default: + cmn_err(CE_PANIC, "unknown property " + "type"); + break; + } + + error = dsl_prop_set(name, propname, 8, 1, + &intval); + } else { + return (EINVAL); + } + break; + } + } + + return (0); } static int -zfs_ioc_set_volblocksize(zfs_cmd_t *zc) +zfs_ioc_set_prop(zfs_cmd_t *zc) { - return (zvol_set_volblocksize(zc)); + nvlist_t *nvl; + int error; + zfs_prop_t prop; + + /* + * If zc_value is set, then this is an attempt to inherit a value. + * Otherwise, zc_nvlist refers to a list of properties to set. + */ + if (zc->zc_value[0] != '\0') { + if (!zfs_prop_user(zc->zc_value) && + ((prop = zfs_name_to_prop(zc->zc_value)) == + ZFS_PROP_INVAL || + !zfs_prop_inheritable(prop))) + return (EINVAL); + + return (dsl_prop_set(zc->zc_name, zc->zc_value, 0, 0, NULL)); + } + + if ((error = get_nvlist(zc, &nvl)) != 0) + return (error); + + error = zfs_set_prop_nvlist(zc->zc_name, zc->zc_dev, + (cred_t *)(uintptr_t)zc->zc_cred, nvl); + nvlist_free(nvl); + return (error); } static int zfs_ioc_create_minor(zfs_cmd_t *zc) { - return (zvol_create_minor(zc)); + return (zvol_create_minor(zc->zc_name, zc->zc_dev)); } static int zfs_ioc_remove_minor(zfs_cmd_t *zc) { - return (zvol_remove_minor(zc)); + return (zvol_remove_minor(zc->zc_name)); } /* @@ -888,7 +969,7 @@ zfs_get_vfs(const char *resource) static void zfs_create_cb(objset_t *os, void *arg, dmu_tx_t *tx) { - zfs_cmd_t *zc = arg; + zfs_create_data_t *zc = arg; zfs_create_fs(os, (cred_t *)(uintptr_t)zc->zc_cred, tx); } @@ -897,6 +978,7 @@ zfs_ioc_create(zfs_cmd_t *zc) { objset_t *clone; int error = 0; + zfs_create_data_t cbdata = { 0 }; void (*cbfunc)(objset_t *os, void *arg, dmu_tx_t *tx); dmu_objset_type_t type = zc->zc_objset_type; @@ -916,47 +998,93 @@ zfs_ioc_create(zfs_cmd_t *zc) if (strchr(zc->zc_name, '@')) return (EINVAL); - if (zc->zc_filename[0] != '\0') { + if (zc->zc_nvlist_src != NULL && + (error = get_nvlist(zc, &cbdata.zc_props)) != 0) + return (error); + + cbdata.zc_cred = (cred_t *)(uintptr_t)zc->zc_cred; + cbdata.zc_dev = (dev_t)zc->zc_dev; + + if (zc->zc_value[0] != '\0') { /* * We're creating a clone of an existing snapshot. */ - zc->zc_filename[sizeof (zc->zc_filename) - 1] = '\0'; - if (dataset_namecheck(zc->zc_filename, NULL, NULL) != 0) + zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; + if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) { + nvlist_free(cbdata.zc_props); return (EINVAL); + } - error = dmu_objset_open(zc->zc_filename, type, + error = dmu_objset_open(zc->zc_value, type, DS_MODE_STANDARD | DS_MODE_READONLY, &clone); - if (error) + if (error) { + nvlist_free(cbdata.zc_props); return (error); + } error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL); dmu_objset_close(clone); } else { - if (cbfunc == NULL) + if (cbfunc == NULL) { + nvlist_free(cbdata.zc_props); return (EINVAL); - /* - * We're creating a new dataset. - */ - if (type == DMU_OST_ZVOL) { - - if ((error = zvol_check_volblocksize(zc)) != 0) - return (error); + } - if ((error = zvol_check_volsize(zc, - zc->zc_volblocksize)) != 0) + if (type == DMU_OST_ZVOL) { + uint64_t volsize, volblocksize; + + if (cbdata.zc_props == NULL || + nvlist_lookup_uint64(cbdata.zc_props, + zfs_prop_to_name(ZFS_PROP_VOLSIZE), + &volsize) != 0) { + nvlist_free(cbdata.zc_props); + return (EINVAL); + } + + if ((error = nvlist_lookup_uint64(cbdata.zc_props, + zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), + &volblocksize)) != 0 && error != ENOENT) { + nvlist_free(cbdata.zc_props); + return (EINVAL); + } + + if (error != 0) + volblocksize = zfs_prop_default_numeric( + ZFS_PROP_VOLBLOCKSIZE); + + if ((error = zvol_check_volblocksize( + volblocksize)) != 0 || + (error = zvol_check_volsize(volsize, + volblocksize)) != 0) { + nvlist_free(cbdata.zc_props); return (error); + } } - error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc, zc); + + error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc, + &cbdata); } + + /* + * It would be nice to do this atomically. + */ + if (error == 0) { + if ((error = zfs_set_prop_nvlist(zc->zc_name, + zc->zc_dev, (cred_t *)(uintptr_t)zc->zc_cred, + cbdata.zc_props)) != 0) + (void) dmu_objset_destroy(zc->zc_name); + } + + nvlist_free(cbdata.zc_props); return (error); } static int zfs_ioc_snapshot(zfs_cmd_t *zc) { - if (snapshot_namecheck(zc->zc_prop_value, NULL, NULL) != 0) + if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) return (EINVAL); return (dmu_objset_snapshot(zc->zc_name, - zc->zc_prop_value, zc->zc_cookie)); + zc->zc_value, zc->zc_cookie)); } static int @@ -1004,13 +1132,13 @@ zfs_ioc_destroy_snaps(zfs_cmd_t *zc) { int err; - if (snapshot_namecheck(zc->zc_prop_value, NULL, NULL) != 0) + if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) return (EINVAL); err = dmu_objset_find(zc->zc_name, - zfs_unmount_snap, zc->zc_prop_value, DS_FIND_CHILDREN); + zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN); if (err) return (err); - return (dmu_snapshots_destroy(zc->zc_name, zc->zc_prop_value)); + return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value)); } static int @@ -1034,8 +1162,8 @@ zfs_ioc_rollback(zfs_cmd_t *zc) static int zfs_ioc_rename(zfs_cmd_t *zc) { - zc->zc_prop_value[sizeof (zc->zc_prop_value) - 1] = '\0'; - if (dataset_namecheck(zc->zc_prop_value, NULL, NULL) != 0) + zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; + if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) return (EINVAL); if (strchr(zc->zc_name, '@') != NULL && @@ -1045,7 +1173,7 @@ zfs_ioc_rename(zfs_cmd_t *zc) return (err); } - return (dmu_objset_rename(zc->zc_name, zc->zc_prop_value)); + return (dmu_objset_rename(zc->zc_name, zc->zc_value)); } static int @@ -1058,8 +1186,8 @@ zfs_ioc_recvbackup(zfs_cmd_t *zc) fp = getf(fd); if (fp == NULL) return (EBADF); - error = dmu_recvbackup(zc->zc_filename, &zc->zc_begin_record, - &zc->zc_cookie, (boolean_t)zc->zc_numints, fp->f_vnode, + error = dmu_recvbackup(zc->zc_value, &zc->zc_begin_record, + &zc->zc_cookie, (boolean_t)zc->zc_guid, fp->f_vnode, fp->f_offset); releasef(fd); return (error); @@ -1078,8 +1206,8 @@ zfs_ioc_sendbackup(zfs_cmd_t *zc) if (error) return (error); - if (zc->zc_prop_value[0] != '\0') { - error = dmu_objset_open(zc->zc_prop_value, DMU_OST_ANY, + if (zc->zc_value[0] != '\0') { + error = dmu_objset_open(zc->zc_value, DMU_OST_ANY, DS_MODE_STANDARD | DS_MODE_READONLY, &fromsnap); if (error) { dmu_objset_close(tosnap); @@ -1143,17 +1271,17 @@ zfs_ioc_error_log(zfs_cmd_t *zc) { spa_t *spa; int error; - size_t count = (size_t)zc->zc_config_dst_size; + size_t count = (size_t)zc->zc_nvlist_dst_size; if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) return (error); - error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_config_dst, + error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, &count); if (error == 0) - zc->zc_config_dst_size = count; + zc->zc_nvlist_dst_size = count; else - zc->zc_config_dst_size = spa_get_errlog_size(spa); + zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); spa_close(spa, FTAG); @@ -1172,9 +1300,9 @@ zfs_ioc_clear(zfs_cmd_t *zc) spa_config_enter(spa, RW_WRITER, FTAG); - if (zc->zc_prop_value[0] == '\0') + if (zc->zc_guid == 0) { vd = NULL; - else if ((vd = spa_lookup_by_guid(spa, zc->zc_guid)) == NULL) { + } else if ((vd = spa_lookup_by_guid(spa, zc->zc_guid)) == NULL) { spa_config_exit(spa, FTAG); spa_close(spa, FTAG); return (ENODEV); @@ -1194,14 +1322,17 @@ zfs_ioc_bookmark_name(zfs_cmd_t *zc) { spa_t *spa; int error; + nvlist_t *nvl; if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) return (error); - error = spa_bookmark_name(spa, &zc->zc_bookmark, - zc->zc_prop_name, sizeof (zc->zc_prop_name), zc->zc_prop_value, - sizeof (zc->zc_prop_value), zc->zc_filename, - sizeof (zc->zc_filename)); + VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); + + error = spa_bookmark_name(spa, &zc->zc_bookmark, nvl); + if (error == 0) + error = put_nvlist(zc, nvl); + nvlist_free(nvl); spa_close(spa, FTAG); @@ -1217,10 +1348,10 @@ zfs_ioc_promote(zfs_cmd_t *zc) * We don't need to unmount *all* the origin fs's snapshots, but * it's easier. */ - cp = strchr(zc->zc_prop_value, '@'); + cp = strchr(zc->zc_value, '@'); if (cp) *cp = '\0'; - (void) dmu_objset_find(zc->zc_prop_value, + (void) dmu_objset_find(zc->zc_value, zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS); return (dsl_dataset_promote(zc->zc_name)); } @@ -1246,11 +1377,7 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { { zfs_ioc_objset_stats, zfs_secpolicy_read, dataset_name }, { zfs_ioc_dataset_list_next, zfs_secpolicy_read, dataset_name }, { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, dataset_name }, - { zfs_ioc_set_prop, zfs_secpolicy_setprop, dataset_name }, - { zfs_ioc_set_quota, zfs_secpolicy_quota, dataset_name }, - { zfs_ioc_set_reservation, zfs_secpolicy_write, dataset_name }, - { zfs_ioc_set_volsize, zfs_secpolicy_config, dataset_name }, - { zfs_ioc_set_volblocksize, zfs_secpolicy_config, dataset_name }, + { zfs_ioc_set_prop, zfs_secpolicy_write, dataset_name }, { zfs_ioc_create_minor, zfs_secpolicy_config, dataset_name }, { zfs_ioc_remove_minor, zfs_secpolicy_config, dataset_name }, { zfs_ioc_create, zfs_secpolicy_parent, dataset_name }, @@ -1292,8 +1419,7 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) if (error == 0) { zc->zc_cred = (uintptr_t)cr; zc->zc_dev = dev; - error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name, - zc->zc_prop_name, cr); + error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name, cr); } /* @@ -1421,7 +1547,8 @@ static struct dev_ops zfs_dev_ops = { }; static struct modldrv zfs_modldrv = { - &mod_driverops, "ZFS storage pool version 1", &zfs_dev_ops + &mod_driverops, "ZFS storage pool version " ZFS_VERSION_STRING, + &zfs_dev_ops }; static struct modlinkage modlinkage = { diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c index 5abaaa174f..04ada0c8d0 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c @@ -1237,5 +1237,5 @@ static vfsdef_t vfw = { }; struct modlfs zfs_modlfs = { - &mod_fsops, "ZFS filesystem version 1", &vfw + &mod_fsops, "ZFS filesystem version " ZFS_VERSION_STRING, &vfw }; diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c index 66a8327b51..0b83bd7bd0 100644 --- a/usr/src/uts/common/fs/zfs/zvol.c +++ b/usr/src/uts/common/fs/zfs/zvol.c @@ -116,27 +116,27 @@ zvol_size_changed(zvol_state_t *zv, dev_t dev) } int -zvol_check_volsize(zfs_cmd_t *zc, uint64_t blocksize) +zvol_check_volsize(uint64_t volsize, uint64_t blocksize) { - if (zc->zc_volsize == 0) + if (volsize == 0) return (EINVAL); - if (zc->zc_volsize % blocksize != 0) + if (volsize % blocksize != 0) return (EINVAL); #ifdef _ILP32 - if (zc->zc_volsize - 1 > SPEC_MAXOFFSET_T) + if (volsize - 1 > SPEC_MAXOFFSET_T) return (EOVERFLOW); #endif return (0); } int -zvol_check_volblocksize(zfs_cmd_t *zc) +zvol_check_volblocksize(uint64_t volblocksize) { - if (zc->zc_volblocksize < SPA_MINBLOCKSIZE || - zc->zc_volblocksize > SPA_MAXBLOCKSIZE || - !ISP2(zc->zc_volblocksize)) + if (volblocksize < SPA_MINBLOCKSIZE || + volblocksize > SPA_MAXBLOCKSIZE || + !ISP2(volblocksize)) return (EDOM); return (0); @@ -151,12 +151,12 @@ zvol_readonly_changed_cb(void *arg, uint64_t newval) } int -zvol_get_stats(zfs_cmd_t *zc, objset_t *os) +zvol_get_stats(objset_t *os, zvol_stats_t *zvs) { int error; dmu_object_info_t doi; - error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zc->zc_volsize); + error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zvs->zv_volsize); if (error) return (error); @@ -164,7 +164,7 @@ zvol_get_stats(zfs_cmd_t *zc, objset_t *os) error = dmu_object_info(os, ZVOL_OBJ, &doi); if (error == 0) - zc->zc_volblocksize = doi.doi_data_block_size; + zvs->zv_volblocksize = doi.doi_data_block_size; return (error); } @@ -187,7 +187,7 @@ zvol_minor_alloc(void) } static zvol_state_t * -zvol_minor_lookup(char *name) +zvol_minor_lookup(const char *name) { minor_t minor; zvol_state_t *zv; @@ -208,10 +208,26 @@ zvol_minor_lookup(char *name) void zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx) { - zfs_cmd_t *zc = arg; + zfs_create_data_t *zc = arg; int error; + uint64_t volblocksize, volsize; - error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, zc->zc_volblocksize, + VERIFY(nvlist_lookup_uint64(zc->zc_props, + zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) == 0); + if (nvlist_lookup_uint64(zc->zc_props, + zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), &volblocksize) != 0) + volblocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); + + /* + * These properites must be removed from the list so the generic + * property setting step won't apply to them. + */ + VERIFY(nvlist_remove_all(zc->zc_props, + zfs_prop_to_name(ZFS_PROP_VOLSIZE)) == 0); + (void) nvlist_remove_all(zc->zc_props, + zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE)); + + error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, volblocksize, DMU_OT_NONE, 0, tx); ASSERT(error == 0); @@ -219,7 +235,7 @@ zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx) DMU_OT_NONE, 0, tx); ASSERT(error == 0); - error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zc->zc_volsize, tx); + error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize, tx); ASSERT(error == 0); } @@ -284,10 +300,8 @@ zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = { * Create a minor node for the specified volume. */ int -zvol_create_minor(zfs_cmd_t *zc) +zvol_create_minor(const char *name, dev_t dev) { - char *name = zc->zc_name; - dev_t dev = zc->zc_dev; zvol_state_t *zv; objset_t *os; uint64_t volsize; @@ -377,7 +391,8 @@ zvol_create_minor(zfs_cmd_t *zc) return (EAGAIN); } - (void) ddi_prop_update_string(minor, zfs_dip, ZVOL_PROP_NAME, name); + (void) ddi_prop_update_string(minor, zfs_dip, ZVOL_PROP_NAME, + (char *)name); (void) sprintf(chrbuf, "%uc,raw", minor); @@ -431,14 +446,14 @@ zvol_create_minor(zfs_cmd_t *zc) * Remove minor node for the specified volume. */ int -zvol_remove_minor(zfs_cmd_t *zc) +zvol_remove_minor(const char *name) { zvol_state_t *zv; char namebuf[30]; mutex_enter(&zvol_state_lock); - if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) { + if ((zv = zvol_minor_lookup(name)) == NULL) { mutex_exit(&zvol_state_lock); return (ENXIO); } @@ -472,23 +487,23 @@ zvol_remove_minor(zfs_cmd_t *zc) } int -zvol_set_volsize(zfs_cmd_t *zc) +zvol_set_volsize(const char *name, dev_t dev, uint64_t volsize) { zvol_state_t *zv; - dev_t dev = zc->zc_dev; dmu_tx_t *tx; int error; dmu_object_info_t doi; mutex_enter(&zvol_state_lock); - if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) { + if ((zv = zvol_minor_lookup(name)) == NULL) { mutex_exit(&zvol_state_lock); return (ENXIO); } if ((error = dmu_object_info(zv->zv_objset, ZVOL_OBJ, &doi)) != 0 || - (error = zvol_check_volsize(zc, doi.doi_data_block_size)) != 0) { + (error = zvol_check_volsize(volsize, + doi.doi_data_block_size)) != 0) { mutex_exit(&zvol_state_lock); return (error); } @@ -500,7 +515,7 @@ zvol_set_volsize(zfs_cmd_t *zc) tx = dmu_tx_create(zv->zv_objset); dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL); - dmu_tx_hold_free(tx, ZVOL_OBJ, zc->zc_volsize, DMU_OBJECT_END); + dmu_tx_hold_free(tx, ZVOL_OBJ, volsize, DMU_OBJECT_END); error = dmu_tx_assign(tx, TXG_WAIT); if (error) { dmu_tx_abort(tx); @@ -509,16 +524,16 @@ zvol_set_volsize(zfs_cmd_t *zc) } error = zap_update(zv->zv_objset, ZVOL_ZAP_OBJ, "size", 8, 1, - &zc->zc_volsize, tx); + &volsize, tx); if (error == 0) { - error = dmu_free_range(zv->zv_objset, ZVOL_OBJ, zc->zc_volsize, + error = dmu_free_range(zv->zv_objset, ZVOL_OBJ, volsize, DMU_OBJECT_END, tx); } dmu_tx_commit(tx); if (error == 0) { - zv->zv_volsize = zc->zc_volsize; + zv->zv_volsize = volsize; zvol_size_changed(zv, dev); } @@ -528,7 +543,7 @@ zvol_set_volsize(zfs_cmd_t *zc) } int -zvol_set_volblocksize(zfs_cmd_t *zc) +zvol_set_volblocksize(const char *name, uint64_t volblocksize) { zvol_state_t *zv; dmu_tx_t *tx; @@ -536,7 +551,7 @@ zvol_set_volblocksize(zfs_cmd_t *zc) mutex_enter(&zvol_state_lock); - if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) { + if ((zv = zvol_minor_lookup(name)) == NULL) { mutex_exit(&zvol_state_lock); return (ENXIO); } @@ -553,7 +568,7 @@ zvol_set_volblocksize(zfs_cmd_t *zc) dmu_tx_abort(tx); } else { error = dmu_object_set_blocksize(zv->zv_objset, ZVOL_OBJ, - zc->zc_volblocksize, 0, tx); + volblocksize, 0, tx); if (error == ENOTSUP) error = EBUSY; dmu_tx_commit(tx); diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h index 2af99527fd..b882715516 100644 --- a/usr/src/uts/common/sys/fs/zfs.h +++ b/usr/src/uts/common/sys/fs/zfs.h @@ -84,6 +84,7 @@ typedef enum { ZFS_PROP_SNAPDIR, ZFS_PROP_ACLMODE, ZFS_PROP_ACLINHERIT, + ZFS_PROP_CANMOUNT, /* * The following properties are not exposed to the user, but are * accessible by libzfs clients. @@ -102,10 +103,12 @@ typedef enum { * The following functions are shared between libzfs and the kernel. */ zfs_prop_t zfs_name_to_prop(const char *); +boolean_t zfs_prop_user(const char *); int zfs_prop_readonly(zfs_prop_t); const char *zfs_prop_default_string(zfs_prop_t); +const char *zfs_prop_to_name(zfs_prop_t); uint64_t zfs_prop_default_numeric(zfs_prop_t); - +int zfs_prop_inheritable(zfs_prop_t); /* * On-disk version number. @@ -114,6 +117,7 @@ uint64_t zfs_prop_default_numeric(zfs_prop_t); #define ZFS_VERSION_2 2ULL #define ZFS_VERSION_3 3ULL #define ZFS_VERSION ZFS_VERSION_3 +#define ZFS_VERSION_STRING "3" /* * Symbolic names for the changes that caused a ZFS_VERSION switch. @@ -332,10 +336,6 @@ typedef enum zfs_ioc { ZFS_IOC_DATASET_LIST_NEXT, ZFS_IOC_SNAPSHOT_LIST_NEXT, ZFS_IOC_SET_PROP, - ZFS_IOC_SET_QUOTA, - ZFS_IOC_SET_RESERVATION, - ZFS_IOC_SET_VOLSIZE, - ZFS_IOC_SET_VOLBLOCKSIZE, ZFS_IOC_CREATE_MINOR, ZFS_IOC_REMOVE_MINOR, ZFS_IOC_CREATE, @@ -365,6 +365,13 @@ typedef enum { SPA_LOAD_TRYIMPORT /* tryimport in progress */ } spa_load_state_t; +/* + * Bookmark name values. + */ +#define ZPOOL_ERR_DATASET "dataset" +#define ZPOOL_ERR_OBJECT "object" +#define ZPOOL_ERR_RANGE "range" + #ifdef __cplusplus } #endif |