| 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 |  |