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