--- a/usr/src/lib/libzfs/common/libzfs_dataset.c Mon Sep 04 19:32:13 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c Tue Sep 05 11:37:36 2006 -0700
@@ -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 @@
}
/*
+ * 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 @@
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;
-
- free((void *)(uintptr_t)zc.zc_config_src);
+ zcmd_free_nvlists(&zc);
+
+ zhp->zfs_volstats = zc.zc_vol_stats;
+
+ if (process_user_props(zhp) != 0)
+ return (-1);
return (0);
}
@@ -373,64 +405,11 @@
{
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 @@
}
int
-zfs_nicestrtonum(const char *str, uint64_t *val)
+zfs_nicestrtonum(libzfs_handle_t *hdl, const char *str, uint64_t *val)
+{
+ return (nicestrtonum(hdl, str, val));
+}
+
+/*
+ * 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.
+ */
+
+static int
+prop_parse_boolean(libzfs_handle_t *hdl, nvpair_t *elem, uint64_t *val)
{
- return (nicestrtonum(NULL, str, val));
+ uint64_t ret;
+
+ switch (nvpair_type(elem)) {
+ case DATA_TYPE_STRING:
+ {
+ char *value;
+ VERIFY(nvpair_value_string(elem, &value) == 0);
+
+ 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;
+ }
+
+ 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;
+ }
+
+ case DATA_TYPE_BOOLEAN_VALUE:
+ {
+ boolean_t value;
+ VERIFY(nvpair_value_boolean_value(elem, &value) == 0);
+ ret = value;
+ break;
+ }
+
+ default:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be a boolean value"),
+ nvpair_name(elem));
+ return (-1);
+ }
+
+ *val = ret;
+ return (0);
+}
+
+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;
+
+ 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 a property type and value, verify that the value is appropriate. Used
- * by zfs_prop_set() and some libzfs consumers.
+ * 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.
*/
-int
-zfs_prop_validate(libzfs_handle_t *hdl, zfs_prop_t prop, const char *value,
- uint64_t *intval)
+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)
{
- 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));
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "bad %s value '%s'"), propname, value);
-
- /* 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));
+ 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;
}
- /* 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));
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
+ propname = nvpair_name(elem);
+
+ /*
+ * Make sure this property is valid and applies to this type.
+ */
+ if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
+ if (!zfs_prop_user(propname)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "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;
+ }
+
+ 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;
+ }
+ }
+
+ (void) nvpair_value_string(elem, &strval);
+ if (nvlist_add_string(ret, propname, strval) != 0) {
+ (void) no_memory(hdl);
+ goto error;
+ }
+ continue;
}
- break;
-
- case prop_type_number:
- /* treat 'none' as 0 */
- if (strcmp(value, "none") == 0) {
- number = 0;
+
+ /*
+ * 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;
- }
-
- if (nicestrtonum(hdl, value, &number) != 0)
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-
- /* 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));
- }
-
- /* 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)) {
+
+ case prop_type_string:
+ if (nvpair_type(elem) != DATA_TYPE_STRING) {
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));
+ "'%s' must be a string"),
+ propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
}
- }
-
- break;
-
- case prop_type_string:
- case prop_type_index:
- /*
- * 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.
- */
- switch (prop) {
- case ZFS_PROP_MOUNTPOINT:
- if (strcmp(value, ZFS_MOUNTPOINT_NONE) == 0 ||
- strcmp(value, ZFS_MOUNTPOINT_LEGACY) == 0)
- break;
-
- if (value[0] != '/') {
+ (void) nvpair_value_string(elem, &strval);
+ if (strlen(strval) >= ZFS_MAXPROPLEN) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "must be an absolute path, 'none', or "
- "'legacy'"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+ "'%s' is too long"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
+
+ case prop_type_number:
+ if (prop_parse_number(hdl, elem, prop, &intval) != 0) {
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
+
+ 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_CHECKSUM:
- for (i = 0; checksum_table[i].name != NULL; i++) {
- if (strcmp(value, checksum_table[i].name)
- == 0) {
- number = checksum_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;
}
-
- if (checksum_table[i].name == NULL) {
+ } else if (nvlist_add_uint64(ret, propname, intval) != 0) {
+ (void) no_memory(hdl);
+ goto error;
+ }
+
+ /*
+ * 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 'on', 'off', 'fletcher2', "
- "'fletcher4', or 'sha256'"));
- 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_COMPRESSION:
- for (i = 0; compress_table[i].name != NULL; i++) {
- if (strcmp(value, compress_table[i].name)
- == 0) {
- number = compress_table[i].value;
- break;
- }
- }
-
- if (compress_table[i].name == NULL) {
+ case ZFS_PROP_MOUNTPOINT:
+ if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
+ strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
+ break;
+
+ if (strval[0] != '/') {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "must be 'on', 'off', or 'lzjb'"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
- 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;
- }
- }
-
- if (snapdir_table[i].name == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "must be 'hidden' or 'visible'"));
- 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_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;
+ }
+
+ /*
+ * 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;
+ }
+ }
+
+ /*
+ * 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;
}
}
-
- if (acl_mode_table[i].name == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "must be 'disacard', 'groupmask', or "
- "'passthrough'"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
- 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;
- }
- }
-
- 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));
- }
- break;
-
- case ZFS_PROP_SHARENFS:
- /*
- * Nothing to do for 'sharenfs', this gets passed on to
- * share(1M) verbatim.
- */
- break;
}
}
- if (intval != NULL)
- *intval = number;
-
- return (0);
+ /*
+ * 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;
+ }
+ }
+ }
+
+ return (ret);
+
+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 @@
*/
(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 @@
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 @@
* 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 @@
* 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 @@
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 @@
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 @@
(void) get_stats(zhp);
}
-
error:
changelist_free(cl);
return (ret);
@@ -1240,11 +1424,11 @@
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 @@
*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 @@
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 @@
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 @@
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 @@
}
/*
- * 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 @@
}
}
+ *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 @@
}
/*
- * 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 @@
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 @@
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 @@
* 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 (blocksize < SPA_MINBLOCKSIZE ||
- blocksize > SPA_MAXBLOCKSIZE || !ISP2(blocksize)) {
+ 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 (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 @@
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 @@
(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_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 @@
}
(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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
/* 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 @@
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 @@
* 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 @@
}
(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 @@
* 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 @@
/* 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 @@
"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 @@
/*
* 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 @@
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 @@
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 @@
/* 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 @@
} 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 @@
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 @@
*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 @@
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 @@
* 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 @@
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 @@
(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 @@
} 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_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 @@
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);
+}