xref: /freebsd/sys/contrib/openzfs/module/zcommon/zprop_common.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 /*
13  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
14  * Use is subject to license terms.
15  */
16 /*
17  * Copyright (c) 2012 by Delphix. All rights reserved.
18  */
19 
20 /*
21  * Common routines used by zfs and zpool property management.
22  */
23 
24 #include <sys/zio.h>
25 #include <sys/spa.h>
26 #include <sys/zfs_acl.h>
27 #include <sys/zfs_ioctl.h>
28 #include <sys/zfs_sysfs.h>
29 #include <sys/zfs_znode.h>
30 #include <sys/fs/zfs.h>
31 
32 #include "zfs_prop.h"
33 #include "zfs_deleg.h"
34 
35 #if !defined(_KERNEL)
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <sys/stat.h>
40 #endif
41 
42 static zprop_desc_t *
zprop_get_proptable(zfs_type_t type)43 zprop_get_proptable(zfs_type_t type)
44 {
45 	if (type == ZFS_TYPE_POOL)
46 		return (zpool_prop_get_table());
47 	else if (type == ZFS_TYPE_VDEV)
48 		return (vdev_prop_get_table());
49 	else
50 		return (zfs_prop_get_table());
51 }
52 
53 static int
zprop_get_numprops(zfs_type_t type)54 zprop_get_numprops(zfs_type_t type)
55 {
56 	if (type == ZFS_TYPE_POOL)
57 		return (ZPOOL_NUM_PROPS);
58 	else if (type == ZFS_TYPE_VDEV)
59 		return (VDEV_NUM_PROPS);
60 	else
61 		return (ZFS_NUM_PROPS);
62 }
63 
64 static boolean_t
zfs_mod_supported_prop(const char * name,zfs_type_t type,const struct zfs_mod_supported_features * sfeatures)65 zfs_mod_supported_prop(const char *name, zfs_type_t type,
66     const struct zfs_mod_supported_features *sfeatures)
67 {
68 /*
69  * The zfs module spa_feature_table[], whether in-kernel or in libzpool,
70  * always supports all the properties. libzfs needs to query the running
71  * module, via sysfs, to determine which properties are supported.
72  *
73  * The equivalent _can_ be done on FreeBSD by way of the sysctl
74  * tree, but this has not been done yet.
75  */
76 #if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD) || defined(__FreeBSD__)
77 	(void) name, (void) type, (void) sfeatures;
78 	return (B_TRUE);
79 #else
80 	return (zfs_mod_supported(type == ZFS_TYPE_POOL ?
81 	    ZFS_SYSFS_POOL_PROPERTIES : (type == ZFS_TYPE_VDEV ?
82 	    ZFS_SYSFS_VDEV_PROPERTIES : ZFS_SYSFS_DATASET_PROPERTIES),
83 	    name, sfeatures));
84 #endif
85 }
86 
87 void
zprop_register_impl(int prop,const char * name,zprop_type_t type,uint64_t numdefault,const char * strdefault,zprop_attr_t attr,int objset_types,const char * values,const char * colname,boolean_t rightalign,boolean_t visible,boolean_t flex,const zprop_index_t * idx_tbl,const struct zfs_mod_supported_features * sfeatures)88 zprop_register_impl(int prop, const char *name, zprop_type_t type,
89     uint64_t numdefault, const char *strdefault, zprop_attr_t attr,
90     int objset_types, const char *values, const char *colname,
91     boolean_t rightalign, boolean_t visible, boolean_t flex,
92     const zprop_index_t *idx_tbl,
93     const struct zfs_mod_supported_features *sfeatures)
94 {
95 	zprop_desc_t *prop_tbl = zprop_get_proptable(objset_types);
96 	zprop_desc_t *pd;
97 
98 	pd = &prop_tbl[prop];
99 
100 	ASSERT(pd->pd_name == NULL || pd->pd_name == name);
101 	ASSERT(name != NULL);
102 	ASSERT(colname != NULL);
103 
104 	pd->pd_name = name;
105 	pd->pd_propnum = prop;
106 	pd->pd_proptype = type;
107 	pd->pd_numdefault = numdefault;
108 	pd->pd_strdefault = strdefault;
109 	pd->pd_attr = attr;
110 	pd->pd_types = objset_types;
111 	pd->pd_values = values;
112 	pd->pd_colname = colname;
113 	pd->pd_rightalign = rightalign;
114 	pd->pd_visible = visible;
115 	pd->pd_zfs_mod_supported =
116 	    zfs_mod_supported_prop(name, objset_types, sfeatures);
117 	pd->pd_always_flex = flex;
118 	pd->pd_table = idx_tbl;
119 	pd->pd_table_size = 0;
120 	while (idx_tbl && (idx_tbl++)->pi_name != NULL)
121 		pd->pd_table_size++;
122 }
123 
124 void
zprop_register_string(int prop,const char * name,const char * def,zprop_attr_t attr,int objset_types,const char * values,const char * colname,const struct zfs_mod_supported_features * sfeatures)125 zprop_register_string(int prop, const char *name, const char *def,
126     zprop_attr_t attr, int objset_types, const char *values,
127     const char *colname, const struct zfs_mod_supported_features *sfeatures)
128 {
129 	zprop_register_impl(prop, name, PROP_TYPE_STRING, 0, def, attr,
130 	    objset_types, values, colname, B_FALSE, B_TRUE, B_TRUE, NULL,
131 	    sfeatures);
132 
133 }
134 
135 void
zprop_register_number(int prop,const char * name,uint64_t def,zprop_attr_t attr,int objset_types,const char * values,const char * colname,boolean_t flex,const struct zfs_mod_supported_features * sfeatures)136 zprop_register_number(int prop, const char *name, uint64_t def,
137     zprop_attr_t attr, int objset_types, const char *values,
138     const char *colname, boolean_t flex,
139     const struct zfs_mod_supported_features *sfeatures)
140 {
141 	zprop_register_impl(prop, name, PROP_TYPE_NUMBER, def, NULL, attr,
142 	    objset_types, values, colname, B_TRUE, B_TRUE, flex, NULL,
143 	    sfeatures);
144 }
145 
146 void
zprop_register_index(int prop,const char * name,uint64_t def,zprop_attr_t attr,int objset_types,const char * values,const char * colname,const zprop_index_t * idx_tbl,const struct zfs_mod_supported_features * sfeatures)147 zprop_register_index(int prop, const char *name, uint64_t def,
148     zprop_attr_t attr, int objset_types, const char *values,
149     const char *colname, const zprop_index_t *idx_tbl,
150     const struct zfs_mod_supported_features *sfeatures)
151 {
152 	zprop_register_impl(prop, name, PROP_TYPE_INDEX, def, NULL, attr,
153 	    objset_types, values, colname, B_FALSE, B_TRUE, B_TRUE, idx_tbl,
154 	    sfeatures);
155 }
156 
157 void
zprop_register_hidden(int prop,const char * name,zprop_type_t type,zprop_attr_t attr,int objset_types,const char * colname,boolean_t flex,const struct zfs_mod_supported_features * sfeatures)158 zprop_register_hidden(int prop, const char *name, zprop_type_t type,
159     zprop_attr_t attr, int objset_types, const char *colname, boolean_t flex,
160     const struct zfs_mod_supported_features *sfeatures)
161 {
162 	zprop_register_impl(prop, name, type, 0, NULL, attr,
163 	    objset_types, NULL, colname,
164 	    type == PROP_TYPE_NUMBER, B_FALSE, flex, NULL, sfeatures);
165 }
166 
167 
168 /*
169  * A comparison function we can use to order indexes into property tables.
170  */
171 static int
zprop_compare(const void * arg1,const void * arg2)172 zprop_compare(const void *arg1, const void *arg2)
173 {
174 	const zprop_desc_t *p1 = *((zprop_desc_t **)arg1);
175 	const zprop_desc_t *p2 = *((zprop_desc_t **)arg2);
176 	boolean_t p1ro, p2ro;
177 
178 	p1ro = (p1->pd_attr == PROP_READONLY);
179 	p2ro = (p2->pd_attr == PROP_READONLY);
180 
181 	if (p1ro == p2ro)
182 		return (strcmp(p1->pd_name, p2->pd_name));
183 
184 	return (p1ro ? -1 : 1);
185 }
186 
187 /*
188  * Iterate over all properties in the given property table, calling back
189  * into the specified function for each property. We will continue to
190  * iterate until we either reach the end or the callback function returns
191  * something other than ZPROP_CONT.
192  */
193 int
zprop_iter_common(zprop_func func,void * cb,boolean_t show_all,boolean_t ordered,zfs_type_t type)194 zprop_iter_common(zprop_func func, void *cb, boolean_t show_all,
195     boolean_t ordered, zfs_type_t type)
196 {
197 	int i, num_props, size, prop;
198 	zprop_desc_t *prop_tbl;
199 	zprop_desc_t **order;
200 
201 	prop_tbl = zprop_get_proptable(type);
202 	num_props = zprop_get_numprops(type);
203 	size = num_props * sizeof (zprop_desc_t *);
204 
205 #if defined(_KERNEL)
206 	order = kmem_alloc(size, KM_SLEEP);
207 #else
208 	if ((order = malloc(size)) == NULL)
209 		return (ZPROP_CONT);
210 #endif
211 
212 	for (int j = 0; j < num_props; j++)
213 		order[j] = &prop_tbl[j];
214 
215 	if (ordered) {
216 		qsort((void *)order, num_props, sizeof (zprop_desc_t *),
217 		    zprop_compare);
218 	}
219 
220 	prop = ZPROP_CONT;
221 	for (i = 0; i < num_props; i++) {
222 		if ((order[i]->pd_visible || show_all) &&
223 		    order[i]->pd_zfs_mod_supported &&
224 		    (func(order[i]->pd_propnum, cb) != ZPROP_CONT)) {
225 			prop = order[i]->pd_propnum;
226 			break;
227 		}
228 	}
229 
230 #if defined(_KERNEL)
231 	kmem_free(order, size);
232 #else
233 	free(order);
234 #endif
235 	return (prop);
236 }
237 
238 static boolean_t
propname_match(const char * p,size_t len,zprop_desc_t * prop_entry)239 propname_match(const char *p, size_t len, zprop_desc_t *prop_entry)
240 {
241 	const char *propname = prop_entry->pd_name;
242 #ifndef _KERNEL
243 	const char *colname = prop_entry->pd_colname;
244 	int c;
245 #endif
246 
247 	ASSERT(propname != NULL);
248 
249 	if (len == strlen(propname) &&
250 	    strncmp(p, propname, len) == 0)
251 		return (B_TRUE);
252 
253 #ifndef _KERNEL
254 	if (colname == NULL || len != strlen(colname))
255 		return (B_FALSE);
256 
257 	for (c = 0; c < len; c++)
258 		if (p[c] != tolower(colname[c]))
259 			break;
260 
261 	return (colname[c] == '\0');
262 #else
263 	return (B_FALSE);
264 #endif
265 }
266 
267 typedef struct name_to_prop_cb {
268 	const char *propname;
269 	zprop_desc_t *prop_tbl;
270 } name_to_prop_cb_t;
271 
272 static int
zprop_name_to_prop_cb(int prop,void * cb_data)273 zprop_name_to_prop_cb(int prop, void *cb_data)
274 {
275 	name_to_prop_cb_t *data = cb_data;
276 
277 	if (propname_match(data->propname, strlen(data->propname),
278 	    &data->prop_tbl[prop]))
279 		return (prop);
280 
281 	return (ZPROP_CONT);
282 }
283 
284 int
zprop_name_to_prop(const char * propname,zfs_type_t type)285 zprop_name_to_prop(const char *propname, zfs_type_t type)
286 {
287 	int prop;
288 	name_to_prop_cb_t cb_data;
289 
290 	cb_data.propname = propname;
291 	cb_data.prop_tbl = zprop_get_proptable(type);
292 
293 	prop = zprop_iter_common(zprop_name_to_prop_cb, &cb_data,
294 	    B_TRUE, B_FALSE, type);
295 
296 	return (prop == ZPROP_CONT ? ZPROP_INVAL : prop);
297 }
298 
299 int
zprop_string_to_index(int prop,const char * string,uint64_t * index,zfs_type_t type)300 zprop_string_to_index(int prop, const char *string, uint64_t *index,
301     zfs_type_t type)
302 {
303 	zprop_desc_t *prop_tbl;
304 	const zprop_index_t *idx_tbl;
305 	int i;
306 
307 	if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
308 		return (-1);
309 
310 	ASSERT(prop < zprop_get_numprops(type));
311 	prop_tbl = zprop_get_proptable(type);
312 	if ((idx_tbl = prop_tbl[prop].pd_table) == NULL)
313 		return (-1);
314 
315 	for (i = 0; idx_tbl[i].pi_name != NULL; i++) {
316 		if (strcmp(string, idx_tbl[i].pi_name) == 0) {
317 			*index = idx_tbl[i].pi_value;
318 			return (0);
319 		}
320 	}
321 
322 	return (-1);
323 }
324 
325 int
zprop_index_to_string(int prop,uint64_t index,const char ** string,zfs_type_t type)326 zprop_index_to_string(int prop, uint64_t index, const char **string,
327     zfs_type_t type)
328 {
329 	zprop_desc_t *prop_tbl;
330 	const zprop_index_t *idx_tbl;
331 	int i;
332 
333 	if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
334 		return (-1);
335 
336 	ASSERT(prop < zprop_get_numprops(type));
337 	prop_tbl = zprop_get_proptable(type);
338 	if ((idx_tbl = prop_tbl[prop].pd_table) == NULL)
339 		return (-1);
340 
341 	for (i = 0; idx_tbl[i].pi_name != NULL; i++) {
342 		if (idx_tbl[i].pi_value == index) {
343 			*string = idx_tbl[i].pi_name;
344 			return (0);
345 		}
346 	}
347 
348 	return (-1);
349 }
350 
351 /*
352  * Return a random valid property value.  Used by ztest.
353  */
354 uint64_t
zprop_random_value(int prop,uint64_t seed,zfs_type_t type)355 zprop_random_value(int prop, uint64_t seed, zfs_type_t type)
356 {
357 	zprop_desc_t *prop_tbl;
358 	const zprop_index_t *idx_tbl;
359 
360 	ASSERT((uint_t)prop < zprop_get_numprops(type));
361 	prop_tbl = zprop_get_proptable(type);
362 	idx_tbl = prop_tbl[prop].pd_table;
363 
364 	if (idx_tbl == NULL)
365 		return (seed);
366 
367 	return (idx_tbl[seed % prop_tbl[prop].pd_table_size].pi_value);
368 }
369 
370 const char *
zprop_values(int prop,zfs_type_t type)371 zprop_values(int prop, zfs_type_t type)
372 {
373 	zprop_desc_t *prop_tbl;
374 
375 	ASSERT(prop != ZPROP_INVAL && prop != ZPROP_CONT);
376 	ASSERT(prop < zprop_get_numprops(type));
377 
378 	prop_tbl = zprop_get_proptable(type);
379 
380 	return (prop_tbl[prop].pd_values);
381 }
382 
383 /*
384  * Returns TRUE if the property applies to any of the given dataset types.
385  *
386  * If headcheck is set, the check is being made against the head dataset
387  * type of a snapshot which requires to return B_TRUE when the property
388  * is only valid for snapshots.
389  */
390 boolean_t
zprop_valid_for_type(int prop,zfs_type_t type,boolean_t headcheck)391 zprop_valid_for_type(int prop, zfs_type_t type, boolean_t headcheck)
392 {
393 	zprop_desc_t *prop_tbl;
394 
395 	if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
396 		return (B_FALSE);
397 
398 	ASSERT(prop < zprop_get_numprops(type));
399 	prop_tbl = zprop_get_proptable(type);
400 	if (headcheck && prop_tbl[prop].pd_types == ZFS_TYPE_SNAPSHOT)
401 		return (B_TRUE);
402 	return ((prop_tbl[prop].pd_types & type) != 0);
403 }
404 
405 /*
406  * For user property names, we allow all lowercase alphanumeric characters, plus
407  * a few useful punctuation characters.
408  */
409 int
zprop_valid_char(char c)410 zprop_valid_char(char c)
411 {
412 	return ((c >= 'a' && c <= 'z') ||
413 	    (c >= '0' && c <= '9') ||
414 	    c == '-' || c == '_' || c == '.' || c == ':');
415 }
416 
417 #ifndef _KERNEL
418 
419 /*
420  * Determines the minimum width for the column, and indicates whether it's fixed
421  * or not.  Only string columns are non-fixed.
422  */
423 size_t
zprop_width(int prop,boolean_t * fixed,zfs_type_t type)424 zprop_width(int prop, boolean_t *fixed, zfs_type_t type)
425 {
426 	zprop_desc_t *prop_tbl, *pd;
427 	const zprop_index_t *idx;
428 	size_t ret;
429 	int i;
430 
431 	ASSERT(prop != ZPROP_INVAL && prop != ZPROP_CONT);
432 	ASSERT(prop < zprop_get_numprops(type));
433 
434 	prop_tbl = zprop_get_proptable(type);
435 	pd = &prop_tbl[prop];
436 
437 	if (type != ZFS_TYPE_POOL && type != ZFS_TYPE_VDEV)
438 		type = ZFS_TYPE_FILESYSTEM;
439 
440 	*fixed = !pd->pd_always_flex;
441 
442 	/*
443 	 * Start with the width of the column name.
444 	 */
445 	ret = strlen(pd->pd_colname);
446 
447 	/*
448 	 * For fixed-width values, make sure the width is large enough to hold
449 	 * any possible value.
450 	 */
451 	switch (pd->pd_proptype) {
452 	case PROP_TYPE_NUMBER:
453 		/*
454 		 * The maximum length of a human-readable number is 5 characters
455 		 * ("20.4M", for example).
456 		 */
457 		if (ret < 5)
458 			ret = 5;
459 		/*
460 		 * 'health' is handled specially because it's a number
461 		 * internally, but displayed as a fixed 8 character string.
462 		 */
463 		if (type == ZFS_TYPE_POOL && prop == ZPOOL_PROP_HEALTH)
464 			ret = 8;
465 		break;
466 
467 	case PROP_TYPE_INDEX:
468 		idx = prop_tbl[prop].pd_table;
469 		for (i = 0; idx[i].pi_name != NULL; i++) {
470 			if (strlen(idx[i].pi_name) > ret)
471 				ret = strlen(idx[i].pi_name);
472 		}
473 		break;
474 
475 	case PROP_TYPE_STRING:
476 		break;
477 	}
478 
479 	return (ret);
480 }
481 
482 #endif
483 
484 #if defined(_KERNEL)
485 /* Common routines to initialize property tables */
486 EXPORT_SYMBOL(zprop_register_impl);
487 EXPORT_SYMBOL(zprop_register_string);
488 EXPORT_SYMBOL(zprop_register_number);
489 EXPORT_SYMBOL(zprop_register_index);
490 EXPORT_SYMBOL(zprop_register_hidden);
491 
492 /* Common routines for zfs and zpool property management */
493 EXPORT_SYMBOL(zprop_iter_common);
494 EXPORT_SYMBOL(zprop_name_to_prop);
495 EXPORT_SYMBOL(zprop_string_to_index);
496 EXPORT_SYMBOL(zprop_index_to_string);
497 EXPORT_SYMBOL(zprop_random_value);
498 EXPORT_SYMBOL(zprop_values);
499 EXPORT_SYMBOL(zprop_valid_for_type);
500 EXPORT_SYMBOL(zprop_valid_char);
501 #endif
502