Skip to content

Commit 192c8d5

Browse files
allanjude0mp
andcommitted
Add support for zpool user properties
Usage: zpool set org.freebsd:comment="this is my pool" poolname Tests are based on zfs_set's user property tests. Also stop truncating property values at MAXNAMELEN, use ZFS_MAXPROPLEN. Sponsored-by: Klara Inc. Co-authored-by: Mateusz Piotrowski <[email protected]> Signed-off-by: Allan Jude <[email protected]> Signed-off-by: Mateusz Piotrowski <[email protected]>
1 parent 21c4b2a commit 192c8d5

File tree

10 files changed

+615
-48
lines changed

10 files changed

+615
-48
lines changed

cmd/zpool/zpool_main.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6069,11 +6069,14 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
60696069
zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
60706070
sizeof (property)) == 0) {
60716071
propstr = property;
6072+
} else if (zfs_prop_user(pl->pl_user_prop) &&
6073+
zpool_get_userprop(zhp, pl->pl_user_prop, property,
6074+
sizeof (property), NULL) == 0) {
6075+
propstr = property;
60726076
} else {
60736077
propstr = "-";
60746078
}
60756079

6076-
60776080
/*
60786081
* If this is being called in scripted mode, or if this is the
60796082
* last column and it is left-justified, don't include a width
@@ -10011,7 +10014,7 @@ static int
1001110014
get_callback(zpool_handle_t *zhp, void *data)
1001210015
{
1001310016
zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
10014-
char value[MAXNAMELEN];
10017+
char value[ZFS_MAXPROPLEN];
1001510018
zprop_source_t srctype;
1001610019
zprop_list_t *pl;
1001710020
int vid;
@@ -10047,6 +10050,17 @@ get_callback(zpool_handle_t *zhp, void *data)
1004710050
continue;
1004810051

1004910052
if (pl->pl_prop == ZPROP_INVAL &&
10053+
zfs_prop_user(pl->pl_user_prop)) {
10054+
srctype = ZPROP_SRC_LOCAL;
10055+
10056+
if (zpool_get_userprop(zhp, pl->pl_user_prop,
10057+
value, sizeof (value), &srctype) != 0)
10058+
continue;
10059+
10060+
zprop_print_one_property(zpool_get_name(zhp),
10061+
cbp, pl->pl_user_prop, value, srctype,
10062+
NULL, NULL);
10063+
} else if (pl->pl_prop == ZPROP_INVAL &&
1005010064
(zpool_prop_feature(pl->pl_user_prop) ||
1005110065
zpool_prop_unsupported(pl->pl_user_prop))) {
1005210066
srctype = ZPROP_SRC_LOCAL;

include/libzfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ _LIBZFS_H const char *zpool_get_state_str(zpool_handle_t *);
333333
_LIBZFS_H int zpool_set_prop(zpool_handle_t *, const char *, const char *);
334334
_LIBZFS_H int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
335335
size_t proplen, zprop_source_t *, boolean_t literal);
336+
_LIBZFS_H int zpool_get_userprop(zpool_handle_t *, const char *, char *,
337+
size_t proplen, zprop_source_t *);
336338
_LIBZFS_H uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
337339
zprop_source_t *);
338340
_LIBZFS_H int zpool_props_refresh(zpool_handle_t *);

lib/libzfs/libzfs_pool.c

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,37 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
426426
return (0);
427427
}
428428

429+
/*
430+
* Get a zpool property value for 'propname' and return the value in
431+
* a pre-allocated buffer.
432+
*/
433+
int
434+
zpool_get_userprop(zpool_handle_t *zhp, const char *propname, char *buf,
435+
size_t len, zprop_source_t *srctype)
436+
{
437+
nvlist_t *nv, *nvl;
438+
uint64_t ival;
439+
const char *value;
440+
zprop_source_t source = ZPROP_SRC_LOCAL;
441+
442+
nvl = zhp->zpool_props;
443+
if (nvlist_lookup_nvlist(nvl, propname, &nv) == 0) {
444+
if (nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0)
445+
source = ival;
446+
verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
447+
} else {
448+
source = ZPROP_SRC_DEFAULT;
449+
value = "-";
450+
}
451+
452+
if (srctype)
453+
*srctype = source;
454+
455+
(void) strlcpy(buf, value, len);
456+
457+
return (0);
458+
}
459+
429460
/*
430461
* Check if the bootfs name has the same pool name as it is set to.
431462
* Assuming bootfs is a valid dataset name.
@@ -549,6 +580,44 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
549580
(void) no_memory(hdl);
550581
goto error;
551582
}
583+
continue;
584+
} else if (prop == ZPOOL_PROP_INVAL &&
585+
zfs_prop_user(propname)) {
586+
/*
587+
* This is a user property: make sure it's a
588+
* string, and that it's less than ZAP_MAXNAMELEN.
589+
*/
590+
if (nvpair_type(elem) != DATA_TYPE_STRING) {
591+
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
592+
"'%s' must be a string"), propname);
593+
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
594+
goto error;
595+
}
596+
597+
if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
598+
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
599+
"property name '%s' is too long"),
600+
propname);
601+
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
602+
goto error;
603+
}
604+
605+
(void) nvpair_value_string(elem, &strval);
606+
607+
if (strlen(strval) >= ZFS_MAXPROPLEN) {
608+
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
609+
"property value '%s' is too long"),
610+
strval);
611+
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
612+
goto error;
613+
}
614+
615+
if (nvlist_add_string(retprops, propname,
616+
strval) != 0) {
617+
(void) no_memory(hdl);
618+
goto error;
619+
}
620+
552621
continue;
553622
}
554623

@@ -855,9 +924,30 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
855924
features = zpool_get_features(zhp);
856925

857926
if ((*plp)->pl_all && firstexpand) {
927+
/* Handle userprops in the all properties case */
928+
if (zhp->zpool_props == NULL && zpool_props_refresh(zhp))
929+
return (-1);
930+
931+
nvp = NULL;
932+
while ((nvp = nvlist_next_nvpair(zhp->zpool_props, nvp)) !=
933+
NULL) {
934+
const char *propname = nvpair_name(nvp);
935+
936+
if (!zfs_prop_user(propname))
937+
continue;
938+
939+
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
940+
entry->pl_prop = ZPROP_USERPROP;
941+
entry->pl_user_prop = zfs_strdup(hdl, propname);
942+
entry->pl_width = strlen(entry->pl_user_prop);
943+
entry->pl_all = B_TRUE;
944+
945+
*last = entry;
946+
last = &entry->pl_next;
947+
}
948+
858949
for (i = 0; i < SPA_FEATURES; i++) {
859-
zprop_list_t *entry = zfs_alloc(hdl,
860-
sizeof (zprop_list_t));
950+
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
861951
entry->pl_prop = ZPROP_USERPROP;
862952
entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
863953
spa_feature_table[i].fi_uname);
@@ -874,7 +964,6 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
874964
nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
875965
char *propname;
876966
boolean_t found;
877-
zprop_list_t *entry;
878967

879968
if (zfeature_is_supported(nvpair_name(nvp)))
880969
continue;
@@ -920,6 +1009,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
9201009
NULL, literal) == 0) {
9211010
if (strlen(buf) > entry->pl_width)
9221011
entry->pl_width = strlen(buf);
1012+
} else if (entry->pl_prop == ZPROP_INVAL &&
1013+
zfs_prop_user(entry->pl_user_prop) &&
1014+
zpool_get_userprop(zhp, entry->pl_user_prop, buf,
1015+
sizeof (buf), NULL) == 0) {
1016+
if (strlen(buf) > entry->pl_width)
1017+
entry->pl_width = strlen(buf);
9231018
}
9241019
}
9251020

lib/libzfs/libzfs_util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,7 @@ addlist(libzfs_handle_t *hdl, const char *propname, zprop_list_t **listp,
17741774
* a user-defined property.
17751775
*/
17761776
if (prop == ZPROP_USERPROP && ((type == ZFS_TYPE_POOL &&
1777+
!zfs_prop_user(propname) &&
17771778
!zpool_prop_feature(propname) &&
17781779
!zpool_prop_unsupported(propname)) ||
17791780
((type == ZFS_TYPE_DATASET) && !zfs_prop_user(propname) &&

man/man7/zpoolprops.7

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
.\" Copyright 2017 Nexenta Systems, Inc.
2727
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
2828
.\" Copyright (c) 2021, Colm Buckley <[email protected]>
29+
.\" Copyright (c) 2023, Klara Inc.
2930
.\"
30-
.Dd May 27, 2021
31+
.Dd April 12, 2023
3132
.Dt ZPOOLPROPS 7
3233
.Os
3334
.
@@ -40,6 +41,12 @@ Each pool has several properties associated with it.
4041
Some properties are read-only statistics while others are configurable and
4142
change the behavior of the pool.
4243
.Pp
44+
User properties have no effect on ZFS behavior.
45+
Use them to annotate datasets in a way that is meaningful in your environment.
46+
For more information about user properties, see the
47+
.Sx User Properties
48+
section.
49+
.Pp
4350
The following are read-only properties:
4451
.Bl -tag -width "unsupported@guid"
4552
.It Sy allocated
@@ -431,3 +438,49 @@ backwards compatibility.
431438
Once feature flags are enabled on a pool this property will no longer have a
432439
value.
433440
.El
441+
.
442+
.Ss User Properties
443+
In addition to the standard native properties, ZFS supports arbitrary user
444+
properties.
445+
User properties have no effect on ZFS behavior, but applications or
446+
administrators can use them to annotate pools.
447+
.Pp
448+
User property names must contain a colon
449+
.Pq Qq Sy \&:
450+
character to distinguish them from native properties.
451+
They may contain lowercase letters, numbers, and the following punctuation
452+
characters: colon
453+
.Pq Qq Sy \&: ,
454+
dash
455+
.Pq Qq Sy - ,
456+
period
457+
.Pq Qq Sy \&. ,
458+
and underscore
459+
.Pq Qq Sy _ .
460+
The expected convention is that the property name is divided into two portions
461+
such as
462+
.Ar module : Ns Ar property ,
463+
but this namespace is not enforced by ZFS.
464+
User property names can be at most 256 characters, and cannot begin with a dash
465+
.Pq Qq Sy - .
466+
.Pp
467+
When making programmatic use of user properties, it is strongly suggested to use
468+
a reversed DNS domain name for the
469+
.Ar module
470+
component of property names to reduce the chance that two
471+
independently-developed packages use the same property name for different
472+
purposes.
473+
.Pp
474+
The values of user properties are arbitrary strings and
475+
are never validated.
476+
All of the commands that operate on properties
477+
.Po Nm zpool Cm list ,
478+
.Nm zpool Cm get ,
479+
.Nm zpool Cm set ,
480+
and so forth
481+
.Pc
482+
can be used to manipulate both native properties and user properties.
483+
Use
484+
.Nm zpool Cm set Ar name Ns =
485+
to clear a user property.
486+
Property values are limited to 8192 bytes.

0 commit comments

Comments
 (0)