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