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 /*
  23  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  24  * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
  25  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  26  * Copyright (c) 2014, Nexenta Systems, Inc. All rights reserved.
  27  * Copyright (c) 2014 Integros [integros.com]
  28  */
  29 
  30 #ifdef _KERNEL
  31 #include <sys/systm.h>
  32 #else
  33 #include <errno.h>
  34 #include <string.h>
  35 #endif
  36 #include <sys/debug.h>
  37 #include <sys/fs/zfs.h>
  38 #include <sys/inttypes.h>
  39 #include <sys/types.h>
  40 #include "zfeature_common.h"
  41 
  42 /*
  43  * Set to disable all feature checks while opening pools, allowing pools with
  44  * unsupported features to be opened. Set for testing only.
  45  */
  46 boolean_t zfeature_checks_disable = B_FALSE;
  47 
  48 zfeature_info_t spa_feature_table[SPA_FEATURES];
  49 
  50 /*
  51  * Valid characters for feature guids. This list is mainly for aesthetic
  52  * purposes and could be expanded in the future. There are different allowed
  53  * characters in the guids reverse dns portion (before the colon) and its
  54  * short name (after the colon).
  55  */
  56 static int
  57 valid_char(char c, boolean_t after_colon)
  58 {
  59         return ((c >= 'a' && c <= 'z') ||
  60             (c >= '0' && c <= '9') ||
  61             (after_colon && c == '_') ||
  62             (!after_colon && (c == '.' || c == '-')));
  63 }
  64 
  65 /*
  66  * Every feature guid must contain exactly one colon which separates a reverse
  67  * dns organization name from the feature's "short" name (e.g.
  68  * "com.company:feature_name").
  69  */
  70 boolean_t
  71 zfeature_is_valid_guid(const char *name)
  72 {
  73         int i;
  74         boolean_t has_colon = B_FALSE;
  75 
  76         i = 0;
  77         while (name[i] != '\0') {
  78                 char c = name[i++];
  79                 if (c == ':') {
  80                         if (has_colon)
  81                                 return (B_FALSE);
  82                         has_colon = B_TRUE;
  83                         continue;
  84                 }
  85                 if (!valid_char(c, has_colon))
  86                         return (B_FALSE);
  87         }
  88 
  89         return (has_colon);
  90 }
  91 
  92 boolean_t
  93 zfeature_is_supported(const char *guid)
  94 {
  95         if (zfeature_checks_disable)
  96                 return (B_TRUE);
  97 
  98         for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
  99                 zfeature_info_t *feature = &spa_feature_table[i];
 100                 if (strcmp(guid, feature->fi_guid) == 0)
 101                         return (B_TRUE);
 102         }
 103         return (B_FALSE);
 104 }
 105 
 106 int
 107 zfeature_lookup_name(const char *name, spa_feature_t *res)
 108 {
 109         for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
 110                 zfeature_info_t *feature = &spa_feature_table[i];
 111                 if (strcmp(name, feature->fi_uname) == 0) {
 112                         if (res != NULL)
 113                                 *res = i;
 114                         return (0);
 115                 }
 116         }
 117 
 118         return (ENOENT);
 119 }
 120 
 121 boolean_t
 122 zfeature_depends_on(spa_feature_t fid, spa_feature_t check)
 123 {
 124         zfeature_info_t *feature = &spa_feature_table[fid];
 125 
 126         for (int i = 0; feature->fi_depends[i] != SPA_FEATURE_NONE; i++) {
 127                 if (feature->fi_depends[i] == check)
 128                         return (B_TRUE);
 129         }
 130         return (B_FALSE);
 131 }
 132 
 133 static boolean_t
 134 deps_contains_feature(const spa_feature_t *deps, const spa_feature_t feature)
 135 {
 136         for (int i = 0; deps[i] != SPA_FEATURE_NONE; i++)
 137                 if (deps[i] == feature)
 138                         return (B_TRUE);
 139 
 140         return (B_FALSE);
 141 }
 142 
 143 static void
 144 zfeature_register(spa_feature_t fid, const char *guid, const char *name,
 145     const char *desc, zfeature_flags_t flags, const spa_feature_t *deps)
 146 {
 147         zfeature_info_t *feature = &spa_feature_table[fid];
 148         static spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
 149 
 150         ASSERT(name != NULL);
 151         ASSERT(desc != NULL);
 152         ASSERT((flags & ZFEATURE_FLAG_READONLY_COMPAT) == 0 ||
 153             (flags & ZFEATURE_FLAG_MOS) == 0);
 154         ASSERT3U(fid, <, SPA_FEATURES);
 155         ASSERT(zfeature_is_valid_guid(guid));
 156 
 157         if (deps == NULL)
 158                 deps = nodeps;
 159 
 160         VERIFY(((flags & ZFEATURE_FLAG_PER_DATASET) == 0) ||
 161             (deps_contains_feature(deps, SPA_FEATURE_EXTENSIBLE_DATASET)));
 162 
 163         feature->fi_feature = fid;
 164         feature->fi_guid = guid;
 165         feature->fi_uname = name;
 166         feature->fi_desc = desc;
 167         feature->fi_flags = flags;
 168         feature->fi_depends = deps;
 169 }
 170 
 171 void
 172 zpool_feature_init(void)
 173 {
 174         zfeature_register(SPA_FEATURE_ASYNC_DESTROY,
 175             "com.delphix:async_destroy", "async_destroy",
 176             "Destroy filesystems asynchronously.",
 177             ZFEATURE_FLAG_READONLY_COMPAT, NULL);
 178 
 179         zfeature_register(SPA_FEATURE_EMPTY_BPOBJ,
 180             "com.delphix:empty_bpobj", "empty_bpobj",
 181             "Snapshots use less space.",
 182             ZFEATURE_FLAG_READONLY_COMPAT, NULL);
 183 
 184         zfeature_register(SPA_FEATURE_LZ4_COMPRESS,
 185             "org.illumos:lz4_compress", "lz4_compress",
 186             "LZ4 compression algorithm support.",
 187             ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, NULL);
 188 
 189         zfeature_register(SPA_FEATURE_MULTI_VDEV_CRASH_DUMP,
 190             "com.joyent:multi_vdev_crash_dump", "multi_vdev_crash_dump",
 191             "Crash dumps to multiple vdev pools.",
 192             0, NULL);
 193 
 194         zfeature_register(SPA_FEATURE_SPACEMAP_HISTOGRAM,
 195             "com.delphix:spacemap_histogram", "spacemap_histogram",
 196             "Spacemaps maintain space histograms.",
 197             ZFEATURE_FLAG_READONLY_COMPAT, NULL);
 198 
 199         zfeature_register(SPA_FEATURE_ENABLED_TXG,
 200             "com.delphix:enabled_txg", "enabled_txg",
 201             "Record txg at which a feature is enabled",
 202             ZFEATURE_FLAG_READONLY_COMPAT, NULL);
 203 
 204         static spa_feature_t hole_birth_deps[] = {
 205                 SPA_FEATURE_ENABLED_TXG,
 206                 SPA_FEATURE_NONE
 207         };
 208         zfeature_register(SPA_FEATURE_HOLE_BIRTH,
 209             "com.delphix:hole_birth", "hole_birth",
 210             "Retain hole birth txg for more precise zfs send",
 211             ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
 212             hole_birth_deps);
 213 
 214         zfeature_register(SPA_FEATURE_EXTENSIBLE_DATASET,
 215             "com.delphix:extensible_dataset", "extensible_dataset",
 216             "Enhanced dataset functionality, used by other features.",
 217             0, NULL);
 218 
 219         static const spa_feature_t bookmarks_deps[] = {
 220                 SPA_FEATURE_EXTENSIBLE_DATASET,
 221                 SPA_FEATURE_NONE
 222         };
 223         zfeature_register(SPA_FEATURE_BOOKMARKS,
 224             "com.delphix:bookmarks", "bookmarks",
 225             "\"zfs bookmark\" command",
 226             ZFEATURE_FLAG_READONLY_COMPAT, bookmarks_deps);
 227 
 228         static const spa_feature_t filesystem_limits_deps[] = {
 229                 SPA_FEATURE_EXTENSIBLE_DATASET,
 230                 SPA_FEATURE_NONE
 231         };
 232         zfeature_register(SPA_FEATURE_FS_SS_LIMIT,
 233             "com.joyent:filesystem_limits", "filesystem_limits",
 234             "Filesystem and snapshot limits.",
 235             ZFEATURE_FLAG_READONLY_COMPAT, filesystem_limits_deps);
 236 
 237         zfeature_register(SPA_FEATURE_EMBEDDED_DATA,
 238             "com.delphix:embedded_data", "embedded_data",
 239             "Blocks which compress very well use even less space.",
 240             ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
 241             NULL);
 242 
 243         static const spa_feature_t large_blocks_deps[] = {
 244                 SPA_FEATURE_EXTENSIBLE_DATASET,
 245                 SPA_FEATURE_NONE
 246         };
 247         zfeature_register(SPA_FEATURE_LARGE_BLOCKS,
 248             "org.open-zfs:large_blocks", "large_blocks",
 249             "Support for blocks larger than 128KB.",
 250             ZFEATURE_FLAG_PER_DATASET, large_blocks_deps);
 251 
 252         static const spa_feature_t sha512_deps[] = {
 253                 SPA_FEATURE_EXTENSIBLE_DATASET,
 254                 SPA_FEATURE_NONE
 255         };
 256         zfeature_register(SPA_FEATURE_SHA512,
 257             "org.illumos:sha512", "sha512",
 258             "SHA-512/256 hash algorithm.",
 259             ZFEATURE_FLAG_PER_DATASET, sha512_deps);
 260 
 261         static const spa_feature_t skein_deps[] = {
 262                 SPA_FEATURE_EXTENSIBLE_DATASET,
 263                 SPA_FEATURE_NONE
 264         };
 265         zfeature_register(SPA_FEATURE_SKEIN,
 266             "org.illumos:skein", "skein",
 267             "Skein hash algorithm.",
 268             ZFEATURE_FLAG_PER_DATASET, skein_deps);
 269 
 270         static const spa_feature_t edonr_deps[] = {
 271                 SPA_FEATURE_EXTENSIBLE_DATASET,
 272                 SPA_FEATURE_NONE
 273         };
 274         zfeature_register(SPA_FEATURE_EDONR,
 275             "org.illumos:edonr", "edonr",
 276             "Edon-R hash algorithm.",
 277             ZFEATURE_FLAG_PER_DATASET, edonr_deps);
 278 
 279         zfeature_register(SPA_FEATURE_DEVICE_REMOVAL,
 280             "com.delphix:device_removal", "device_removal",
 281             "Top-level vdevs can be removed, reducing logical pool size.",
 282             ZFEATURE_FLAG_MOS, NULL);
 283 
 284         static const spa_feature_t obsolete_counts_deps[] = {
 285                 SPA_FEATURE_EXTENSIBLE_DATASET,
 286                 SPA_FEATURE_DEVICE_REMOVAL,
 287                 SPA_FEATURE_NONE
 288         };
 289         zfeature_register(SPA_FEATURE_OBSOLETE_COUNTS,
 290             "com.delphix:obsolete_counts", "obsolete_counts",
 291             "Reduce memory used by removed devices when their blocks are "
 292             "freed or remapped.",
 293             ZFEATURE_FLAG_READONLY_COMPAT, obsolete_counts_deps);
 294 }