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