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