Skip to content

Commit d04575e

Browse files
committed
Add support for zpool user properties
`zpool set org.freebsd:comment="this is my pool" poolname` Signed-off-by: Allan Jude <[email protected]>
1 parent ab15b1f commit d04575e

File tree

5 files changed

+193
-45
lines changed

5 files changed

+193
-45
lines changed

cmd/zpool/zpool_main.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5994,11 +5994,14 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
59945994
zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
59955995
sizeof (property)) == 0) {
59965996
propstr = property;
5997+
} else if (zfs_prop_user(pl->pl_user_prop) &&
5998+
zpool_get_userprop(zhp, pl->pl_user_prop, property,
5999+
sizeof (property), NULL, cb->cb_literal) == 0) {
6000+
propstr = property;
59976001
} else {
59986002
propstr = "-";
59996003
}
60006004

6001-
60026005
/*
60036006
* If this is being called in scripted mode, or if this is the
60046007
* last column and it is left-justified, don't include a width
@@ -9891,6 +9894,16 @@ get_callback(zpool_handle_t *zhp, void *data)
98919894
continue;
98929895

98939896
if (pl->pl_prop == ZPROP_INVAL &&
9897+
zfs_prop_user(pl->pl_user_prop)) {
9898+
srctype = ZPROP_SRC_LOCAL;
9899+
9900+
if (zpool_get_userprop(zhp, pl->pl_user_prop, value,
9901+
sizeof (value), &srctype, cbp->cb_literal) != 0)
9902+
continue;
9903+
9904+
zprop_print_one_property(zpool_get_name(zhp), cbp,
9905+
pl->pl_user_prop, value, srctype, NULL, NULL);
9906+
} else if (pl->pl_prop == ZPROP_INVAL &&
98949907
(zpool_prop_feature(pl->pl_user_prop) ||
98959908
zpool_prop_unsupported(pl->pl_user_prop))) {
98969909
srctype = ZPROP_SRC_LOCAL;

include/libzfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ _LIBZFS_H const char *zpool_get_state_str(zpool_handle_t *);
329329
_LIBZFS_H int zpool_set_prop(zpool_handle_t *, const char *, const char *);
330330
_LIBZFS_H int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
331331
size_t proplen, zprop_source_t *, boolean_t literal);
332+
_LIBZFS_H int zpool_get_userprop(zpool_handle_t *, const char *, char *,
333+
size_t proplen, zprop_source_t *, boolean_t literal);
332334
_LIBZFS_H uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
333335
zprop_source_t *);
334336
_LIBZFS_H int zpool_props_refresh(zpool_handle_t *);

lib/libzfs/libzfs_pool.c

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,37 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
429429
return (0);
430430
}
431431

432+
/*
433+
* Get a zpool property value for 'propname' and return the value in
434+
* a pre-allocated buffer.
435+
*/
436+
int
437+
zpool_get_userprop(zpool_handle_t *zhp, const char *propname, char *buf,
438+
size_t len, zprop_source_t *srctype, boolean_t literal)
439+
{
440+
nvlist_t *nv, *nvl;
441+
uint64_t ival;
442+
char *value;
443+
zprop_source_t source = ZPROP_SRC_LOCAL;
444+
445+
nvl = zhp->zpool_props;
446+
if (nvlist_lookup_nvlist(nvl, propname, &nv) == 0) {
447+
if (nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0)
448+
source = ival;
449+
verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
450+
} else {
451+
source = ZPROP_SRC_DEFAULT;
452+
value = "-";
453+
}
454+
455+
if (srctype)
456+
*srctype = source;
457+
458+
(void) strlcpy(buf, value, len);
459+
460+
return (0);
461+
}
462+
432463
/*
433464
* Check if the bootfs name has the same pool name as it is set to.
434465
* Assuming bootfs is a valid dataset name.
@@ -523,6 +554,35 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
523554
(void) no_memory(hdl);
524555
goto error;
525556
}
557+
continue;
558+
} else if (prop == ZPOOL_PROP_INVAL &&
559+
zfs_prop_user(propname)) {
560+
/*
561+
* This is a user property: make sure it's a
562+
* string, and that it's less than ZAP_MAXNAMELEN.
563+
*/
564+
if (nvpair_type(elem) != DATA_TYPE_STRING) {
565+
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
566+
"'%s' must be a string"), propname);
567+
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
568+
goto error;
569+
}
570+
571+
if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
572+
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
573+
"property name '%s' is too long"),
574+
propname);
575+
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
576+
goto error;
577+
}
578+
579+
(void) nvpair_value_string(elem, &strval);
580+
if (nvlist_add_string(retprops, propname,
581+
strval) != 0) {
582+
(void) no_memory(hdl);
583+
goto error;
584+
}
585+
526586
continue;
527587
}
528588

@@ -828,9 +888,30 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
828888
features = zpool_get_features(zhp);
829889

830890
if ((*plp)->pl_all && firstexpand) {
891+
/* Handle userprops in the all properties case */
892+
if (zhp->zpool_props == NULL && zpool_props_refresh(zhp))
893+
return (-1);
894+
895+
nvp = NULL;
896+
while ((nvp = nvlist_next_nvpair(zhp->zpool_props, nvp)) !=
897+
NULL) {
898+
char *propname = nvpair_name(nvp);
899+
900+
if (!zfs_prop_user(propname))
901+
continue;
902+
903+
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
904+
entry->pl_prop = ZPROP_INVAL;
905+
entry->pl_user_prop = zfs_strdup(hdl, propname);
906+
entry->pl_width = strlen(entry->pl_user_prop);
907+
entry->pl_all = B_TRUE;
908+
909+
*last = entry;
910+
last = &entry->pl_next;
911+
}
912+
831913
for (i = 0; i < SPA_FEATURES; i++) {
832-
zprop_list_t *entry = zfs_alloc(hdl,
833-
sizeof (zprop_list_t));
914+
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
834915
entry->pl_prop = ZPROP_INVAL;
835916
entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
836917
spa_feature_table[i].fi_uname);
@@ -847,7 +928,6 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
847928
nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
848929
char *propname;
849930
boolean_t found;
850-
zprop_list_t *entry;
851931

852932
if (zfeature_is_supported(nvpair_name(nvp)))
853933
continue;
@@ -893,6 +973,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
893973
NULL, literal) == 0) {
894974
if (strlen(buf) > entry->pl_width)
895975
entry->pl_width = strlen(buf);
976+
} else if (entry->pl_prop == ZPROP_INVAL &&
977+
zfs_prop_user(entry->pl_user_prop) &&
978+
zpool_get_userprop(zhp, entry->pl_user_prop, buf,
979+
sizeof (buf), NULL, B_FALSE) == 0) {
980+
if (strlen(buf) > entry->pl_width)
981+
entry->pl_width = strlen(buf);
896982
}
897983
}
898984

lib/libzfs/libzfs_util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,7 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
17521752
* dataset property,
17531753
*/
17541754
if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
1755+
!zfs_prop_user(propname) &&
17551756
!zpool_prop_feature(propname) &&
17561757
!zpool_prop_unsupported(propname)) ||
17571758
(type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&

module/zfs/spa.c

Lines changed: 87 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,22 @@ spa_prop_add_list(nvlist_t *nvl, zpool_prop_t prop, char *strval,
289289
nvlist_free(propval);
290290
}
291291

292+
/*
293+
* Add a user property (source=src, propname=propval) to an nvlist.
294+
*/
295+
static void
296+
spa_prop_add_user(nvlist_t *nvl, const char *propname, char *strval,
297+
zprop_source_t src)
298+
{
299+
nvlist_t *propval;
300+
301+
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
302+
VERIFY(nvlist_add_uint64(propval, ZPROP_SOURCE, src) == 0);
303+
VERIFY(nvlist_add_string(propval, ZPROP_VALUE, strval) == 0);
304+
VERIFY(nvlist_add_nvlist(nvl, propname, propval) == 0);
305+
nvlist_free(propval);
306+
}
307+
292308
/*
293309
* Get property values from the spa configuration.
294310
*/
@@ -458,7 +474,8 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
458474
zprop_source_t src = ZPROP_SRC_DEFAULT;
459475
zpool_prop_t prop;
460476

461-
if ((prop = zpool_name_to_prop(za.za_name)) == ZPOOL_PROP_INVAL)
477+
if ((prop = zpool_name_to_prop(za.za_name)) ==
478+
ZPOOL_PROP_INVAL && !zfs_prop_user(za.za_name))
462479
continue;
463480

464481
switch (za.za_integer_length) {
@@ -501,7 +518,13 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
501518
kmem_free(strval, za.za_num_integers);
502519
break;
503520
}
504-
spa_prop_add_list(*nvp, prop, strval, 0, src);
521+
if (prop != ZPOOL_PROP_INVAL) {
522+
spa_prop_add_list(*nvp, prop, strval, 0, src);
523+
} else {
524+
src = ZPROP_SRC_LOCAL;
525+
spa_prop_add_user(*nvp, za.za_name, strval,
526+
src);
527+
}
505528
kmem_free(strval, za.za_num_integers);
506529
break;
507530

@@ -543,36 +566,47 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
543566

544567
switch (prop) {
545568
case ZPOOL_PROP_INVAL:
546-
if (!zpool_prop_feature(propname)) {
547-
error = SET_ERROR(EINVAL);
548-
break;
549-
}
550-
551569
/*
552570
* Sanitize the input.
553571
*/
554-
if (nvpair_type(elem) != DATA_TYPE_UINT64) {
555-
error = SET_ERROR(EINVAL);
556-
break;
557-
}
572+
if (zfs_prop_user(propname)) {
573+
if (strlen(propname) >= ZAP_MAXNAMELEN) {
574+
error = SET_ERROR(ENAMETOOLONG);
575+
break;
576+
}
558577

559-
if (nvpair_value_uint64(elem, &intval) != 0) {
560-
error = SET_ERROR(EINVAL);
561-
break;
562-
}
578+
if (strlen(fnvpair_value_string(elem)) >=
579+
ZAP_MAXVALUELEN) {
580+
error = SET_ERROR(E2BIG);
581+
break;
582+
}
583+
} else if (zpool_prop_feature(propname)) {
584+
if (nvpair_type(elem) != DATA_TYPE_UINT64) {
585+
error = SET_ERROR(EINVAL);
586+
break;
587+
}
563588

564-
if (intval != 0) {
565-
error = SET_ERROR(EINVAL);
566-
break;
567-
}
589+
if (nvpair_value_uint64(elem, &intval) != 0) {
590+
error = SET_ERROR(EINVAL);
591+
break;
592+
}
568593

569-
fname = strchr(propname, '@') + 1;
570-
if (zfeature_lookup_name(fname, NULL) != 0) {
594+
if (intval != 0) {
595+
error = SET_ERROR(EINVAL);
596+
break;
597+
}
598+
599+
fname = strchr(propname, '@') + 1;
600+
if (zfeature_lookup_name(fname, NULL) != 0) {
601+
error = SET_ERROR(EINVAL);
602+
break;
603+
}
604+
605+
has_feature = B_TRUE;
606+
} else {
571607
error = SET_ERROR(EINVAL);
572608
break;
573609
}
574-
575-
has_feature = B_TRUE;
576610
break;
577611

578612
case ZPOOL_PROP_VERSION:
@@ -779,6 +813,12 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp)
779813
prop == ZPOOL_PROP_READONLY)
780814
continue;
781815

816+
if (prop == ZPOOL_PROP_INVAL &&
817+
zfs_prop_user(nvpair_name(elem))) {
818+
need_sync = B_TRUE;
819+
break;
820+
}
821+
782822
if (prop == ZPOOL_PROP_VERSION || prop == ZPOOL_PROP_INVAL) {
783823
uint64_t ver;
784824

@@ -8682,24 +8722,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
86828722
char *strval, *fname;
86838723
zpool_prop_t prop;
86848724
const char *propname;
8725+
const char *elemname = nvpair_name(elem);
86858726
zprop_type_t proptype;
86868727
spa_feature_t fid;
86878728

8688-
switch (prop = zpool_name_to_prop(nvpair_name(elem))) {
8689-
case ZPOOL_PROP_INVAL:
8690-
/*
8691-
* We checked this earlier in spa_prop_validate().
8692-
*/
8693-
ASSERT(zpool_prop_feature(nvpair_name(elem)));
8694-
8695-
fname = strchr(nvpair_name(elem), '@') + 1;
8696-
VERIFY0(zfeature_lookup_name(fname, &fid));
8697-
8698-
spa_feature_enable(spa, fid, tx);
8699-
spa_history_log_internal(spa, "set", tx,
8700-
"%s=enabled", nvpair_name(elem));
8701-
break;
8702-
8729+
switch (prop = zpool_name_to_prop(elemname)) {
87038730
case ZPOOL_PROP_VERSION:
87048731
intval = fnvpair_value_uint64(elem);
87058732
/*
@@ -8742,7 +8769,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87428769
spa_async_request(spa, SPA_ASYNC_CONFIG_UPDATE);
87438770
}
87448771
spa_history_log_internal(spa, "set", tx,
8745-
"%s=%s", nvpair_name(elem), strval);
8772+
"%s=%s", elemname, strval);
87468773
break;
87478774
case ZPOOL_PROP_COMPATIBILITY:
87488775
strval = fnvpair_value_string(elem);
@@ -8761,6 +8788,20 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87618788
"%s=%s", nvpair_name(elem), strval);
87628789
break;
87638790

8791+
case ZPOOL_PROP_INVAL:
8792+
if (zpool_prop_feature(elemname)) {
8793+
fname = strchr(elemname, '@') + 1;
8794+
VERIFY0(zfeature_lookup_name(fname, &fid));
8795+
8796+
spa_feature_enable(spa, fid, tx);
8797+
spa_history_log_internal(spa, "set", tx,
8798+
"%s=enabled", elemname);
8799+
break;
8800+
} else if (!zfs_prop_user(elemname)) {
8801+
ASSERT(zpool_prop_feature(elemname));
8802+
break;
8803+
}
8804+
/* FALLTHRU */
87648805
default:
87658806
/*
87668807
* Set pool property values in the poolprops mos object.
@@ -8775,6 +8816,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87758816
/* normalize the property name */
87768817
propname = zpool_prop_to_name(prop);
87778818
proptype = zpool_prop_get_type(prop);
8819+
if (prop == ZPOOL_PROP_INVAL &&
8820+
zfs_prop_user(elemname)) {
8821+
propname = elemname;
8822+
proptype = PROP_TYPE_STRING;
8823+
}
87788824

87798825
if (nvpair_type(elem) == DATA_TYPE_STRING) {
87808826
ASSERT(proptype == PROP_TYPE_STRING);
@@ -8783,7 +8829,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87838829
spa->spa_pool_props_object, propname,
87848830
1, strlen(strval) + 1, strval, tx));
87858831
spa_history_log_internal(spa, "set", tx,
8786-
"%s=%s", nvpair_name(elem), strval);
8832+
"%s=%s", elemname, strval);
87878833
} else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
87888834
intval = fnvpair_value_uint64(elem);
87898835

@@ -8796,7 +8842,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87968842
spa->spa_pool_props_object, propname,
87978843
8, 1, &intval, tx));
87988844
spa_history_log_internal(spa, "set", tx,
8799-
"%s=%lld", nvpair_name(elem),
8845+
"%s=%lld", elemname,
88008846
(longlong_t)intval);
88018847
} else {
88028848
ASSERT(0); /* not allowed */

0 commit comments

Comments
 (0)