Skip to content

Commit 0e73bf1

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 e7a0635 commit 0e73bf1

File tree

5 files changed

+192
-45
lines changed

5 files changed

+192
-45
lines changed

cmd/zpool/zpool_main.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5875,11 +5875,14 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
58755875
zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
58765876
sizeof (property)) == 0) {
58775877
propstr = property;
5878+
} else if (zfs_prop_user(pl->pl_user_prop) &&
5879+
zpool_get_userprop(zhp, pl->pl_user_prop, property,
5880+
sizeof (property), NULL, cb->cb_literal) == 0) {
5881+
propstr = property;
58785882
} else {
58795883
propstr = "-";
58805884
}
58815885

5882-
58835886
/*
58845887
* If this is being called in scripted mode, or if this is the
58855888
* last column and it is left-justified, don't include a width
@@ -9738,6 +9741,16 @@ get_callback(zpool_handle_t *zhp, void *data)
97389741
continue;
97399742

97409743
if (pl->pl_prop == ZPROP_INVAL &&
9744+
zfs_prop_user(pl->pl_user_prop)) {
9745+
srctype = ZPROP_SRC_LOCAL;
9746+
9747+
if (zpool_get_userprop(zhp, pl->pl_user_prop, value,
9748+
sizeof (value), &srctype, cbp->cb_literal) != 0)
9749+
continue;
9750+
9751+
zprop_print_one_property(zpool_get_name(zhp), cbp,
9752+
pl->pl_user_prop, value, srctype, NULL, NULL);
9753+
} else if (pl->pl_prop == ZPROP_INVAL &&
97419754
(zpool_prop_feature(pl->pl_user_prop) ||
97429755
zpool_prop_unsupported(pl->pl_user_prop))) {
97439756
srctype = ZPROP_SRC_LOCAL;

include/libzfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,8 @@ const char *zpool_get_state_str(zpool_handle_t *);
326326
extern int zpool_set_prop(zpool_handle_t *, const char *, const char *);
327327
extern int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
328328
size_t proplen, zprop_source_t *, boolean_t literal);
329+
extern int zpool_get_userprop(zpool_handle_t *, const char *, char *,
330+
size_t proplen, zprop_source_t *, boolean_t literal);
329331
extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
330332
zprop_source_t *);
331333
extern int zpool_props_refresh(zpool_handle_t *);

lib/libzfs/libzfs_pool.c

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

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

@@ -848,9 +907,30 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
848907
features = zpool_get_features(zhp);
849908

850909
if ((*plp)->pl_all && firstexpand) {
910+
/* Handle userprops in the all properties case */
911+
if (zhp->zpool_props == NULL && zpool_props_refresh(zhp))
912+
return (-1);
913+
914+
nvp = NULL;
915+
while ((nvp = nvlist_next_nvpair(zhp->zpool_props, nvp)) !=
916+
NULL) {
917+
char *propname = nvpair_name(nvp);
918+
919+
if (!zfs_prop_user(propname))
920+
continue;
921+
922+
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
923+
entry->pl_prop = ZPROP_INVAL;
924+
entry->pl_user_prop = zfs_strdup(hdl, propname);
925+
entry->pl_width = strlen(entry->pl_user_prop);
926+
entry->pl_all = B_TRUE;
927+
928+
*last = entry;
929+
last = &entry->pl_next;
930+
}
931+
851932
for (i = 0; i < SPA_FEATURES; i++) {
852-
zprop_list_t *entry = zfs_alloc(hdl,
853-
sizeof (zprop_list_t));
933+
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
854934
entry->pl_prop = ZPROP_INVAL;
855935
entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
856936
spa_feature_table[i].fi_uname);
@@ -867,7 +947,6 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
867947
nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
868948
char *propname;
869949
boolean_t found;
870-
zprop_list_t *entry;
871950

872951
if (zfeature_is_supported(nvpair_name(nvp)))
873952
continue;
@@ -913,6 +992,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
913992
NULL, literal) == 0) {
914993
if (strlen(buf) > entry->pl_width)
915994
entry->pl_width = strlen(buf);
995+
} else if (entry->pl_prop == ZPROP_INVAL &&
996+
zfs_prop_user(entry->pl_user_prop) &&
997+
zpool_get_userprop(zhp, entry->pl_user_prop, buf,
998+
sizeof (buf), NULL, B_FALSE) == 0) {
999+
if (strlen(buf) > entry->pl_width)
1000+
entry->pl_width = strlen(buf);
9161001
}
9171002
}
9181003

lib/libzfs/libzfs_util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,7 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
17731773
* dataset property,
17741774
*/
17751775
if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
1776+
!zfs_prop_user(propname) &&
17761777
!zpool_prop_feature(propname) &&
17771778
!zpool_prop_unsupported(propname)) ||
17781779
(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
@@ -285,6 +285,22 @@ spa_prop_add_list(nvlist_t *nvl, zpool_prop_t prop, char *strval,
285285
nvlist_free(propval);
286286
}
287287

288+
/*
289+
* Add a user property (source=src, propname=propval) to an nvlist.
290+
*/
291+
static void
292+
spa_prop_add_user(nvlist_t *nvl, const char *propname, char *strval,
293+
zprop_source_t src)
294+
{
295+
nvlist_t *propval;
296+
297+
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
298+
VERIFY(nvlist_add_uint64(propval, ZPROP_SOURCE, src) == 0);
299+
VERIFY(nvlist_add_string(propval, ZPROP_VALUE, strval) == 0);
300+
VERIFY(nvlist_add_nvlist(nvl, propname, propval) == 0);
301+
nvlist_free(propval);
302+
}
303+
288304
/*
289305
* Get property values from the spa configuration.
290306
*/
@@ -454,7 +470,8 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
454470
zprop_source_t src = ZPROP_SRC_DEFAULT;
455471
zpool_prop_t prop;
456472

457-
if ((prop = zpool_name_to_prop(za.za_name)) == ZPOOL_PROP_INVAL)
473+
if ((prop = zpool_name_to_prop(za.za_name)) == ZPOOL_PROP_INVAL
474+
&& !zfs_prop_user(za.za_name))
458475
continue;
459476

460477
switch (za.za_integer_length) {
@@ -497,7 +514,13 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
497514
kmem_free(strval, za.za_num_integers);
498515
break;
499516
}
500-
spa_prop_add_list(*nvp, prop, strval, 0, src);
517+
if (prop != ZPOOL_PROP_INVAL) {
518+
spa_prop_add_list(*nvp, prop, strval, 0, src);
519+
} else {
520+
src = ZPROP_SRC_LOCAL;
521+
spa_prop_add_user(*nvp, za.za_name, strval,
522+
src);
523+
}
501524
kmem_free(strval, za.za_num_integers);
502525
break;
503526

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

540563
switch (prop) {
541564
case ZPOOL_PROP_INVAL:
542-
if (!zpool_prop_feature(propname)) {
543-
error = SET_ERROR(EINVAL);
544-
break;
545-
}
546-
547565
/*
548566
* Sanitize the input.
549567
*/
550-
if (nvpair_type(elem) != DATA_TYPE_UINT64) {
551-
error = SET_ERROR(EINVAL);
552-
break;
553-
}
568+
if (zfs_prop_user(propname)) {
569+
if (strlen(propname) >= ZAP_MAXNAMELEN) {
570+
error = SET_ERROR(ENAMETOOLONG);
571+
break;
572+
}
554573

555-
if (nvpair_value_uint64(elem, &intval) != 0) {
556-
error = SET_ERROR(EINVAL);
557-
break;
558-
}
574+
if (strlen(fnvpair_value_string(elem)) >=
575+
ZAP_MAXVALUELEN) {
576+
error = SET_ERROR(E2BIG);
577+
break;
578+
}
579+
} else if (zpool_prop_feature(propname)) {
580+
if (nvpair_type(elem) != DATA_TYPE_UINT64) {
581+
error = SET_ERROR(EINVAL);
582+
break;
583+
}
559584

560-
if (intval != 0) {
561-
error = SET_ERROR(EINVAL);
562-
break;
563-
}
585+
if (nvpair_value_uint64(elem, &intval) != 0) {
586+
error = SET_ERROR(EINVAL);
587+
break;
588+
}
564589

565-
fname = strchr(propname, '@') + 1;
566-
if (zfeature_lookup_name(fname, NULL) != 0) {
590+
if (intval != 0) {
591+
error = SET_ERROR(EINVAL);
592+
break;
593+
}
594+
595+
fname = strchr(propname, '@') + 1;
596+
if (zfeature_lookup_name(fname, NULL) != 0) {
597+
error = SET_ERROR(EINVAL);
598+
break;
599+
}
600+
601+
has_feature = B_TRUE;
602+
} else {
567603
error = SET_ERROR(EINVAL);
568604
break;
569605
}
570-
571-
has_feature = B_TRUE;
572606
break;
573607

574608
case ZPOOL_PROP_VERSION:
@@ -775,6 +809,12 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp)
775809
prop == ZPOOL_PROP_READONLY)
776810
continue;
777811

812+
if (prop == ZPOOL_PROP_INVAL &&
813+
zfs_prop_user(nvpair_name(elem))) {
814+
need_sync = B_TRUE;
815+
break;
816+
}
817+
778818
if (prop == ZPOOL_PROP_VERSION || prop == ZPOOL_PROP_INVAL) {
779819
uint64_t ver;
780820

@@ -8626,24 +8666,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
86268666
char *strval, *fname;
86278667
zpool_prop_t prop;
86288668
const char *propname;
8669+
const char *elemname = nvpair_name(elem);
86298670
zprop_type_t proptype;
86308671
spa_feature_t fid;
86318672

8632-
switch (prop = zpool_name_to_prop(nvpair_name(elem))) {
8633-
case ZPOOL_PROP_INVAL:
8634-
/*
8635-
* We checked this earlier in spa_prop_validate().
8636-
*/
8637-
ASSERT(zpool_prop_feature(nvpair_name(elem)));
8638-
8639-
fname = strchr(nvpair_name(elem), '@') + 1;
8640-
VERIFY0(zfeature_lookup_name(fname, &fid));
8641-
8642-
spa_feature_enable(spa, fid, tx);
8643-
spa_history_log_internal(spa, "set", tx,
8644-
"%s=enabled", nvpair_name(elem));
8645-
break;
8646-
8673+
switch (prop = zpool_name_to_prop(elemname)) {
86478674
case ZPOOL_PROP_VERSION:
86488675
intval = fnvpair_value_uint64(elem);
86498676
/*
@@ -8682,7 +8709,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
86828709
if (tx->tx_txg != TXG_INITIAL)
86838710
vdev_config_dirty(spa->spa_root_vdev);
86848711
spa_history_log_internal(spa, "set", tx,
8685-
"%s=%s", nvpair_name(elem), strval);
8712+
"%s=%s", elemname, strval);
86868713
break;
86878714
case ZPOOL_PROP_COMPATIBILITY:
86888715
strval = fnvpair_value_string(elem);
@@ -8698,6 +8725,20 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
86988725
"%s=%s", nvpair_name(elem), strval);
86998726
break;
87008727

8728+
case ZPOOL_PROP_INVAL:
8729+
if (zpool_prop_feature(elemname)) {
8730+
fname = strchr(elemname, '@') + 1;
8731+
VERIFY0(zfeature_lookup_name(fname, &fid));
8732+
8733+
spa_feature_enable(spa, fid, tx);
8734+
spa_history_log_internal(spa, "set", tx,
8735+
"%s=enabled", elemname);
8736+
break;
8737+
} else if (!zfs_prop_user(elemname)) {
8738+
ASSERT(zpool_prop_feature(elemname));
8739+
break;
8740+
}
8741+
/* FALLTHRU */
87018742
default:
87028743
/*
87038744
* Set pool property values in the poolprops mos object.
@@ -8712,6 +8753,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87128753
/* normalize the property name */
87138754
propname = zpool_prop_to_name(prop);
87148755
proptype = zpool_prop_get_type(prop);
8756+
if (prop == ZPOOL_PROP_INVAL &&
8757+
zfs_prop_user(elemname)) {
8758+
propname = elemname;
8759+
proptype = PROP_TYPE_STRING;
8760+
}
87158761

87168762
if (nvpair_type(elem) == DATA_TYPE_STRING) {
87178763
ASSERT(proptype == PROP_TYPE_STRING);
@@ -8720,7 +8766,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87208766
spa->spa_pool_props_object, propname,
87218767
1, strlen(strval) + 1, strval, tx));
87228768
spa_history_log_internal(spa, "set", tx,
8723-
"%s=%s", nvpair_name(elem), strval);
8769+
"%s=%s", elemname, strval);
87248770
} else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
87258771
intval = fnvpair_value_uint64(elem);
87268772

@@ -8733,7 +8779,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
87338779
spa->spa_pool_props_object, propname,
87348780
8, 1, &intval, tx));
87358781
spa_history_log_internal(spa, "set", tx,
8736-
"%s=%lld", nvpair_name(elem),
8782+
"%s=%lld", elemname,
87378783
(longlong_t)intval);
87388784
} else {
87398785
ASSERT(0); /* not allowed */

0 commit comments

Comments
 (0)