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