Skip to content

Commit

Permalink
Add support for zpool user properties
Browse files Browse the repository at this point in the history
`zpool set org.freebsd:comment="this is my pool" poolname`

Signed-off-by: Allan Jude <allan@klarasystems.com>
  • Loading branch information
allanjude committed Mar 7, 2021
1 parent e7a0635 commit 2cfa1e1
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 45 deletions.
15 changes: 14 additions & 1 deletion cmd/zpool/zpool_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5875,11 +5875,14 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
sizeof (property)) == 0) {
propstr = property;
} else if (zfs_prop_user(pl->pl_user_prop) &&
zpool_get_userprop(zhp, pl->pl_user_prop, property,
sizeof (property), NULL, cb->cb_literal) == 0) {
propstr = property;
} else {
propstr = "-";
}


/*
* 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
Expand Down Expand Up @@ -9738,6 +9741,16 @@ get_callback(zpool_handle_t *zhp, void *data)
continue;

if (pl->pl_prop == ZPROP_INVAL &&
zfs_prop_user(pl->pl_user_prop)) {
srctype = ZPROP_SRC_LOCAL;

if (zpool_get_userprop(zhp, pl->pl_user_prop, value,
sizeof (value), &srctype, cbp->cb_literal) != 0)
continue;

zprop_print_one_property(zpool_get_name(zhp), cbp,
pl->pl_user_prop, value, srctype, NULL, NULL);
} else if (pl->pl_prop == ZPROP_INVAL &&
(zpool_prop_feature(pl->pl_user_prop) ||
zpool_prop_unsupported(pl->pl_user_prop))) {
srctype = ZPROP_SRC_LOCAL;
Expand Down
2 changes: 2 additions & 0 deletions include/libzfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ const char *zpool_get_state_str(zpool_handle_t *);
extern int zpool_set_prop(zpool_handle_t *, const char *, const char *);
extern int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
size_t proplen, zprop_source_t *, boolean_t literal);
extern int zpool_get_userprop(zpool_handle_t *, const char *, char *,
size_t proplen, zprop_source_t *, boolean_t literal);
extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
zprop_source_t *);
extern int zpool_props_refresh(zpool_handle_t *);
Expand Down
92 changes: 89 additions & 3 deletions lib/libzfs/libzfs_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,37 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
return (0);
}

/*
* Get a zpool property value for 'propname' and return the value in
* a pre-allocated buffer.
*/
int
zpool_get_userprop(zpool_handle_t *zhp, const char *propname, char *buf,
size_t len, zprop_source_t *srctype, boolean_t literal)
{
nvlist_t *nv, *nvl;
uint64_t ival;
char *value;
zprop_source_t source = ZPROP_SRC_LOCAL;

nvl = zhp->zpool_props;
if (nvlist_lookup_nvlist(nvl, propname, &nv) == 0) {
if (nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0)
source = ival;
verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
} else {
source = ZPROP_SRC_DEFAULT;
value = "-";
}

if (srctype)
*srctype = source;

(void) strlcpy(buf, value, len);

return (0);
}

/*
* Check if the bootfs name has the same pool name as it is set to.
* Assuming bootfs is a valid dataset name.
Expand Down Expand Up @@ -525,6 +556,35 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
(void) no_memory(hdl);
goto error;
}
continue;
} else if (prop == ZPOOL_PROP_INVAL &&
zfs_prop_user(propname)) {
/*
* 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(retprops, propname,
strval) != 0) {
(void) no_memory(hdl);
goto error;
}

continue;
}

Expand Down Expand Up @@ -848,9 +908,30 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
features = zpool_get_features(zhp);

if ((*plp)->pl_all && firstexpand) {
/* Handle userprops in the all properties case */
if (zhp->zpool_props == NULL && zpool_props_refresh(zhp))
return (-1);

nvp = NULL;
while ((nvp = nvlist_next_nvpair(zhp->zpool_props, nvp)) !=
NULL) {
char *propname = nvpair_name(nvp);

if (!zfs_prop_user(propname))
continue;

entry = zfs_alloc(hdl, sizeof (zprop_list_t));
entry->pl_prop = ZPROP_INVAL;
entry->pl_user_prop = zfs_strdup(hdl, propname);
entry->pl_width = strlen(entry->pl_user_prop);
entry->pl_all = B_TRUE;

*last = entry;
last = &entry->pl_next;
}

for (i = 0; i < SPA_FEATURES; i++) {
zprop_list_t *entry = zfs_alloc(hdl,
sizeof (zprop_list_t));
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
entry->pl_prop = ZPROP_INVAL;
entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
spa_feature_table[i].fi_uname);
Expand All @@ -867,7 +948,6 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
char *propname;
boolean_t found;
zprop_list_t *entry;

if (zfeature_is_supported(nvpair_name(nvp)))
continue;
Expand Down Expand Up @@ -913,6 +993,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
NULL, literal) == 0) {
if (strlen(buf) > entry->pl_width)
entry->pl_width = strlen(buf);
} else if (entry->pl_prop == ZPROP_INVAL &&
zfs_prop_user(entry->pl_user_prop) &&
zpool_get_userprop(zhp, entry->pl_user_prop, buf,
sizeof (buf), NULL, B_FALSE) == 0) {
if (strlen(buf) > entry->pl_width)
entry->pl_width = strlen(buf);
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/libzfs/libzfs_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1773,6 +1773,7 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
* dataset property,
*/
if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
!zfs_prop_user(propname) &&
!zpool_prop_feature(propname) &&
!zpool_prop_unsupported(propname)) ||
(type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&
Expand Down
128 changes: 87 additions & 41 deletions module/zfs/spa.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,22 @@ spa_prop_add_list(nvlist_t *nvl, zpool_prop_t prop, char *strval,
nvlist_free(propval);
}

/*
* Add a user property (source=src, propname=propval) to an nvlist.
*/
static void
spa_prop_add_user(nvlist_t *nvl, const char *propname, char *strval,
zprop_source_t src)
{
nvlist_t *propval;

VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
VERIFY(nvlist_add_uint64(propval, ZPROP_SOURCE, src) == 0);
VERIFY(nvlist_add_string(propval, ZPROP_VALUE, strval) == 0);
VERIFY(nvlist_add_nvlist(nvl, propname, propval) == 0);
nvlist_free(propval);
}

/*
* Get property values from the spa configuration.
*/
Expand Down Expand Up @@ -454,7 +470,8 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
zprop_source_t src = ZPROP_SRC_DEFAULT;
zpool_prop_t prop;

if ((prop = zpool_name_to_prop(za.za_name)) == ZPOOL_PROP_INVAL)
if ((prop = zpool_name_to_prop(za.za_name)) ==
ZPOOL_PROP_INVAL && !zfs_prop_user(za.za_name))
continue;

switch (za.za_integer_length) {
Expand Down Expand Up @@ -497,7 +514,13 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
kmem_free(strval, za.za_num_integers);
break;
}
spa_prop_add_list(*nvp, prop, strval, 0, src);
if (prop != ZPOOL_PROP_INVAL) {
spa_prop_add_list(*nvp, prop, strval, 0, src);
} else {
src = ZPROP_SRC_LOCAL;
spa_prop_add_user(*nvp, za.za_name, strval,
src);
}
kmem_free(strval, za.za_num_integers);
break;

Expand Down Expand Up @@ -539,36 +562,47 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)

switch (prop) {
case ZPOOL_PROP_INVAL:
if (!zpool_prop_feature(propname)) {
error = SET_ERROR(EINVAL);
break;
}

/*
* Sanitize the input.
*/
if (nvpair_type(elem) != DATA_TYPE_UINT64) {
error = SET_ERROR(EINVAL);
break;
}
if (zfs_prop_user(propname)) {
if (strlen(propname) >= ZAP_MAXNAMELEN) {
error = SET_ERROR(ENAMETOOLONG);
break;
}

if (nvpair_value_uint64(elem, &intval) != 0) {
error = SET_ERROR(EINVAL);
break;
}
if (strlen(fnvpair_value_string(elem)) >=
ZAP_MAXVALUELEN) {
error = SET_ERROR(E2BIG);
break;
}
} else if (zpool_prop_feature(propname)) {
if (nvpair_type(elem) != DATA_TYPE_UINT64) {
error = SET_ERROR(EINVAL);
break;
}

if (intval != 0) {
error = SET_ERROR(EINVAL);
break;
}
if (nvpair_value_uint64(elem, &intval) != 0) {
error = SET_ERROR(EINVAL);
break;
}

fname = strchr(propname, '@') + 1;
if (zfeature_lookup_name(fname, NULL) != 0) {
if (intval != 0) {
error = SET_ERROR(EINVAL);
break;
}

fname = strchr(propname, '@') + 1;
if (zfeature_lookup_name(fname, NULL) != 0) {
error = SET_ERROR(EINVAL);
break;
}

has_feature = B_TRUE;
} else {
error = SET_ERROR(EINVAL);
break;
}

has_feature = B_TRUE;
break;

case ZPOOL_PROP_VERSION:
Expand Down Expand Up @@ -775,6 +809,12 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp)
prop == ZPOOL_PROP_READONLY)
continue;

if (prop == ZPOOL_PROP_INVAL &&
zfs_prop_user(nvpair_name(elem))) {
need_sync = B_TRUE;
break;
}

if (prop == ZPOOL_PROP_VERSION || prop == ZPOOL_PROP_INVAL) {
uint64_t ver;

Expand Down Expand Up @@ -8626,24 +8666,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
char *strval, *fname;
zpool_prop_t prop;
const char *propname;
const char *elemname = nvpair_name(elem);
zprop_type_t proptype;
spa_feature_t fid;

switch (prop = zpool_name_to_prop(nvpair_name(elem))) {
case ZPOOL_PROP_INVAL:
/*
* We checked this earlier in spa_prop_validate().
*/
ASSERT(zpool_prop_feature(nvpair_name(elem)));

fname = strchr(nvpair_name(elem), '@') + 1;
VERIFY0(zfeature_lookup_name(fname, &fid));

spa_feature_enable(spa, fid, tx);
spa_history_log_internal(spa, "set", tx,
"%s=enabled", nvpair_name(elem));
break;

switch (prop = zpool_name_to_prop(elemname)) {
case ZPOOL_PROP_VERSION:
intval = fnvpair_value_uint64(elem);
/*
Expand Down Expand Up @@ -8682,7 +8709,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
if (tx->tx_txg != TXG_INITIAL)
vdev_config_dirty(spa->spa_root_vdev);
spa_history_log_internal(spa, "set", tx,
"%s=%s", nvpair_name(elem), strval);
"%s=%s", elemname, strval);
break;
case ZPOOL_PROP_COMPATIBILITY:
strval = fnvpair_value_string(elem);
Expand All @@ -8698,6 +8725,20 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
"%s=%s", nvpair_name(elem), strval);
break;

case ZPOOL_PROP_INVAL:
if (zpool_prop_feature(elemname)) {
fname = strchr(elemname, '@') + 1;
VERIFY0(zfeature_lookup_name(fname, &fid));

spa_feature_enable(spa, fid, tx);
spa_history_log_internal(spa, "set", tx,
"%s=enabled", elemname);
break;
} else if (!zfs_prop_user(elemname)) {
ASSERT(zpool_prop_feature(elemname));
break;
}
/* FALLTHRU */
default:
/*
* Set pool property values in the poolprops mos object.
Expand All @@ -8712,6 +8753,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
/* normalize the property name */
propname = zpool_prop_to_name(prop);
proptype = zpool_prop_get_type(prop);
if (prop == ZPOOL_PROP_INVAL &&
zfs_prop_user(elemname)) {
propname = elemname;
proptype = PROP_TYPE_STRING;
}

if (nvpair_type(elem) == DATA_TYPE_STRING) {
ASSERT(proptype == PROP_TYPE_STRING);
Expand All @@ -8720,7 +8766,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
spa->spa_pool_props_object, propname,
1, strlen(strval) + 1, strval, tx));
spa_history_log_internal(spa, "set", tx,
"%s=%s", nvpair_name(elem), strval);
"%s=%s", elemname, strval);
} else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
intval = fnvpair_value_uint64(elem);

Expand All @@ -8733,7 +8779,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
spa->spa_pool_props_object, propname,
8, 1, &intval, tx));
spa_history_log_internal(spa, "set", tx,
"%s=%lld", nvpair_name(elem),
"%s=%lld", elemname,
(longlong_t)intval);
} else {
ASSERT(0); /* not allowed */
Expand Down

0 comments on commit 2cfa1e1

Please sign in to comment.