]>
Commit | Line | Data |
---|---|---|
34dc7c2f BB |
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 | |
1d3ba0bf | 9 | * or https://opensource.org/licenses/CDDL-1.0. |
34dc7c2f BB |
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 | /* | |
428870ff | 22 | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. |
eca7b760 | 23 | * Copyright (c) 2012, 2015 by Delphix. All rights reserved. |
b1118acb | 24 | * Copyright (c) 2013 Martin Matuska. All rights reserved. |
e2ef1cbf | 25 | * Copyright 2019 Joyent, Inc. |
5405be03 | 26 | * Copyright (c) 2022 Hewlett Packard Enterprise Development LP. |
34dc7c2f BB |
27 | */ |
28 | ||
428870ff | 29 | #include <sys/zfs_context.h> |
34dc7c2f BB |
30 | #include <sys/dmu.h> |
31 | #include <sys/dmu_objset.h> | |
32 | #include <sys/dmu_tx.h> | |
33 | #include <sys/dsl_dataset.h> | |
34 | #include <sys/dsl_dir.h> | |
35 | #include <sys/dsl_prop.h> | |
36 | #include <sys/dsl_synctask.h> | |
37 | #include <sys/spa.h> | |
34dc7c2f BB |
38 | #include <sys/zap.h> |
39 | #include <sys/fs/zfs.h> | |
40 | ||
41 | #include "zfs_prop.h" | |
42 | ||
428870ff BB |
43 | #define ZPROP_INHERIT_SUFFIX "$inherit" |
44 | #define ZPROP_RECVD_SUFFIX "$recvd" | |
5405be03 | 45 | #define ZPROP_IUV_SUFFIX "$iuv" |
428870ff | 46 | |
34dc7c2f | 47 | static int |
887d1e60 | 48 | dodefault(zfs_prop_t prop, int intsz, int numints, void *buf) |
34dc7c2f | 49 | { |
34dc7c2f BB |
50 | /* |
51 | * The setonce properties are read-only, BUT they still | |
52 | * have a default value that can be used as the initial | |
53 | * value. | |
54 | */ | |
887d1e60 | 55 | if (prop == ZPROP_INVAL || |
34dc7c2f | 56 | (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop))) |
2e528b49 | 57 | return (SET_ERROR(ENOENT)); |
34dc7c2f BB |
58 | |
59 | if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) { | |
60 | if (intsz != 1) | |
2e528b49 | 61 | return (SET_ERROR(EOVERFLOW)); |
7584fbe8 | 62 | (void) strlcpy(buf, zfs_prop_default_string(prop), |
428870ff | 63 | numints); |
34dc7c2f | 64 | } else { |
428870ff | 65 | if (intsz != 8 || numints < 1) |
2e528b49 | 66 | return (SET_ERROR(EOVERFLOW)); |
34dc7c2f BB |
67 | |
68 | *(uint64_t *)buf = zfs_prop_default_numeric(prop); | |
69 | } | |
70 | ||
71 | return (0); | |
72 | } | |
73 | ||
5405be03 A |
74 | static int |
75 | dsl_prop_known_index(zfs_prop_t prop, uint64_t value) | |
76 | { | |
77 | const char *str = NULL; | |
109731cd DS |
78 | if (prop != ZPROP_CONT && prop != ZPROP_INVAL && |
79 | zfs_prop_get_type(prop) == PROP_TYPE_INDEX) | |
5405be03 A |
80 | return (!zfs_prop_index_to_string(prop, value, &str)); |
81 | ||
82 | return (-1); | |
83 | } | |
84 | ||
b128c09f BB |
85 | int |
86 | dsl_prop_get_dd(dsl_dir_t *dd, const char *propname, | |
428870ff | 87 | int intsz, int numints, void *buf, char *setpoint, boolean_t snapshot) |
34dc7c2f | 88 | { |
28caa74b | 89 | int err; |
428870ff | 90 | dsl_dir_t *target = dd; |
b128c09f | 91 | objset_t *mos = dd->dd_pool->dp_meta_objset; |
34dc7c2f | 92 | zfs_prop_t prop; |
428870ff BB |
93 | boolean_t inheritable; |
94 | boolean_t inheriting = B_FALSE; | |
95 | char *inheritstr; | |
96 | char *recvdstr; | |
5405be03 | 97 | char *iuvstr; |
34dc7c2f | 98 | |
13fe0198 | 99 | ASSERT(dsl_pool_config_held(dd->dd_pool)); |
b128c09f | 100 | |
34dc7c2f BB |
101 | if (setpoint) |
102 | setpoint[0] = '\0'; | |
103 | ||
104 | prop = zfs_name_to_prop(propname); | |
4ff7a8fa | 105 | inheritable = (prop == ZPROP_USERPROP || zfs_prop_inheritable(prop)); |
428870ff BB |
106 | inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX); |
107 | recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); | |
5405be03 | 108 | iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX); |
34dc7c2f BB |
109 | |
110 | /* | |
428870ff BB |
111 | * Note: dd may become NULL, therefore we shouldn't dereference it |
112 | * after this loop. | |
34dc7c2f BB |
113 | */ |
114 | for (; dd != NULL; dd = dd->dd_parent) { | |
428870ff | 115 | if (dd != target || snapshot) { |
28caa74b MM |
116 | if (!inheritable) { |
117 | err = SET_ERROR(ENOENT); | |
428870ff | 118 | break; |
28caa74b | 119 | } |
428870ff BB |
120 | inheriting = B_TRUE; |
121 | } | |
122 | ||
5405be03 A |
123 | /* Check for a iuv value. */ |
124 | err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, | |
125 | iuvstr, intsz, numints, buf); | |
ebabb93e | 126 | if (err == 0 && dsl_prop_known_index(prop, |
5405be03 A |
127 | *(uint64_t *)buf) != 1) |
128 | err = ENOENT; | |
129 | if (err != ENOENT) { | |
130 | if (setpoint != NULL && err == 0) | |
131 | dsl_dir_name(dd, setpoint); | |
132 | break; | |
133 | } | |
134 | ||
428870ff | 135 | /* Check for a local value. */ |
d683ddbb JG |
136 | err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, |
137 | propname, intsz, numints, buf); | |
34dc7c2f | 138 | if (err != ENOENT) { |
428870ff | 139 | if (setpoint != NULL && err == 0) |
34dc7c2f BB |
140 | dsl_dir_name(dd, setpoint); |
141 | break; | |
142 | } | |
143 | ||
144 | /* | |
428870ff BB |
145 | * Skip the check for a received value if there is an explicit |
146 | * inheritance entry. | |
34dc7c2f | 147 | */ |
d683ddbb | 148 | err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj, |
428870ff BB |
149 | inheritstr); |
150 | if (err != 0 && err != ENOENT) | |
34dc7c2f | 151 | break; |
428870ff BB |
152 | |
153 | if (err == ENOENT) { | |
154 | /* Check for a received value. */ | |
d683ddbb | 155 | err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, |
428870ff BB |
156 | recvdstr, intsz, numints, buf); |
157 | if (err != ENOENT) { | |
158 | if (setpoint != NULL && err == 0) { | |
159 | if (inheriting) { | |
160 | dsl_dir_name(dd, setpoint); | |
161 | } else { | |
c9e319fa JL |
162 | (void) strlcpy(setpoint, |
163 | ZPROP_SOURCE_VAL_RECVD, | |
164 | MAXNAMELEN); | |
428870ff BB |
165 | } |
166 | } | |
167 | break; | |
168 | } | |
169 | } | |
170 | ||
171 | /* | |
172 | * If we found an explicit inheritance entry, err is zero even | |
173 | * though we haven't yet found the value, so reinitializing err | |
174 | * at the end of the loop (instead of at the beginning) ensures | |
175 | * that err has a valid post-loop value. | |
176 | */ | |
2e528b49 | 177 | err = SET_ERROR(ENOENT); |
34dc7c2f | 178 | } |
428870ff | 179 | |
34dc7c2f | 180 | if (err == ENOENT) |
887d1e60 | 181 | err = dodefault(prop, intsz, numints, buf); |
428870ff | 182 | |
e4f5fa12 MM |
183 | kmem_strfree(inheritstr); |
184 | kmem_strfree(recvdstr); | |
5405be03 | 185 | kmem_strfree(iuvstr); |
34dc7c2f BB |
186 | |
187 | return (err); | |
188 | } | |
189 | ||
b128c09f BB |
190 | int |
191 | dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname, | |
428870ff | 192 | int intsz, int numints, void *buf, char *setpoint) |
b128c09f | 193 | { |
428870ff BB |
194 | zfs_prop_t prop = zfs_name_to_prop(propname); |
195 | boolean_t inheritable; | |
428870ff BB |
196 | uint64_t zapobj; |
197 | ||
13fe0198 | 198 | ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); |
4ff7a8fa | 199 | inheritable = (prop == ZPROP_USERPROP || zfs_prop_inheritable(prop)); |
d683ddbb | 200 | zapobj = dsl_dataset_phys(ds)->ds_props_obj; |
428870ff BB |
201 | |
202 | if (zapobj != 0) { | |
203 | objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; | |
204 | int err; | |
b128c09f | 205 | |
0c66c32d | 206 | ASSERT(ds->ds_is_snapshot); |
428870ff BB |
207 | |
208 | /* Check for a local value. */ | |
209 | err = zap_lookup(mos, zapobj, propname, intsz, numints, buf); | |
b128c09f | 210 | if (err != ENOENT) { |
428870ff | 211 | if (setpoint != NULL && err == 0) |
b128c09f BB |
212 | dsl_dataset_name(ds, setpoint); |
213 | return (err); | |
214 | } | |
428870ff BB |
215 | |
216 | /* | |
217 | * Skip the check for a received value if there is an explicit | |
218 | * inheritance entry. | |
219 | */ | |
220 | if (inheritable) { | |
221 | char *inheritstr = kmem_asprintf("%s%s", propname, | |
222 | ZPROP_INHERIT_SUFFIX); | |
223 | err = zap_contains(mos, zapobj, inheritstr); | |
e4f5fa12 | 224 | kmem_strfree(inheritstr); |
428870ff BB |
225 | if (err != 0 && err != ENOENT) |
226 | return (err); | |
227 | } | |
228 | ||
229 | if (err == ENOENT) { | |
230 | /* Check for a received value. */ | |
231 | char *recvdstr = kmem_asprintf("%s%s", propname, | |
232 | ZPROP_RECVD_SUFFIX); | |
233 | err = zap_lookup(mos, zapobj, recvdstr, | |
234 | intsz, numints, buf); | |
e4f5fa12 | 235 | kmem_strfree(recvdstr); |
428870ff BB |
236 | if (err != ENOENT) { |
237 | if (setpoint != NULL && err == 0) | |
c9e319fa JL |
238 | (void) strlcpy(setpoint, |
239 | ZPROP_SOURCE_VAL_RECVD, | |
240 | MAXNAMELEN); | |
428870ff BB |
241 | return (err); |
242 | } | |
243 | } | |
b128c09f BB |
244 | } |
245 | ||
246 | return (dsl_prop_get_dd(ds->ds_dir, propname, | |
0c66c32d | 247 | intsz, numints, buf, setpoint, ds->ds_is_snapshot)); |
b128c09f BB |
248 | } |
249 | ||
0eb21616 JG |
250 | static dsl_prop_record_t * |
251 | dsl_prop_record_find(dsl_dir_t *dd, const char *propname) | |
252 | { | |
253 | dsl_prop_record_t *pr = NULL; | |
254 | ||
255 | ASSERT(MUTEX_HELD(&dd->dd_lock)); | |
256 | ||
257 | for (pr = list_head(&dd->dd_props); | |
258 | pr != NULL; pr = list_next(&dd->dd_props, pr)) { | |
259 | if (strcmp(pr->pr_propname, propname) == 0) | |
260 | break; | |
261 | } | |
262 | ||
263 | return (pr); | |
264 | } | |
265 | ||
266 | static dsl_prop_record_t * | |
267 | dsl_prop_record_create(dsl_dir_t *dd, const char *propname) | |
268 | { | |
269 | dsl_prop_record_t *pr; | |
270 | ||
271 | ASSERT(MUTEX_HELD(&dd->dd_lock)); | |
272 | ||
273 | pr = kmem_alloc(sizeof (dsl_prop_record_t), KM_SLEEP); | |
274 | pr->pr_propname = spa_strdup(propname); | |
275 | list_create(&pr->pr_cbs, sizeof (dsl_prop_cb_record_t), | |
276 | offsetof(dsl_prop_cb_record_t, cbr_pr_node)); | |
277 | list_insert_head(&dd->dd_props, pr); | |
278 | ||
279 | return (pr); | |
280 | } | |
281 | ||
282 | void | |
283 | dsl_prop_init(dsl_dir_t *dd) | |
284 | { | |
285 | list_create(&dd->dd_props, sizeof (dsl_prop_record_t), | |
286 | offsetof(dsl_prop_record_t, pr_node)); | |
287 | } | |
288 | ||
289 | void | |
290 | dsl_prop_fini(dsl_dir_t *dd) | |
291 | { | |
292 | dsl_prop_record_t *pr; | |
293 | ||
294 | while ((pr = list_remove_head(&dd->dd_props)) != NULL) { | |
295 | list_destroy(&pr->pr_cbs); | |
20aa7a4e | 296 | spa_strfree((char *)pr->pr_propname); |
0eb21616 JG |
297 | kmem_free(pr, sizeof (dsl_prop_record_t)); |
298 | } | |
299 | list_destroy(&dd->dd_props); | |
300 | } | |
301 | ||
34dc7c2f BB |
302 | /* |
303 | * Register interest in the named property. We'll call the callback | |
304 | * once to notify it of the current property value, and again each time | |
305 | * the property changes, until this callback is unregistered. | |
306 | * | |
307 | * Return 0 on success, errno if the prop is not an integer value. | |
308 | */ | |
309 | int | |
310 | dsl_prop_register(dsl_dataset_t *ds, const char *propname, | |
311 | dsl_prop_changed_cb_t *callback, void *cbarg) | |
312 | { | |
313 | dsl_dir_t *dd = ds->ds_dir; | |
314 | uint64_t value; | |
0eb21616 | 315 | dsl_prop_record_t *pr; |
34dc7c2f BB |
316 | dsl_prop_cb_record_t *cbr; |
317 | int err; | |
2a8ba608 | 318 | dsl_pool_t *dp __maybe_unused = dd->dd_pool; |
34dc7c2f | 319 | |
13fe0198 | 320 | ASSERT(dsl_pool_config_held(dp)); |
34dc7c2f | 321 | |
13fe0198 MA |
322 | err = dsl_prop_get_int_ds(ds, propname, &value); |
323 | if (err != 0) | |
34dc7c2f | 324 | return (err); |
34dc7c2f | 325 | |
79c76d5b | 326 | cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); |
34dc7c2f | 327 | cbr->cbr_ds = ds; |
34dc7c2f BB |
328 | cbr->cbr_func = callback; |
329 | cbr->cbr_arg = cbarg; | |
0eb21616 | 330 | |
34dc7c2f | 331 | mutex_enter(&dd->dd_lock); |
0eb21616 JG |
332 | pr = dsl_prop_record_find(dd, propname); |
333 | if (pr == NULL) | |
334 | pr = dsl_prop_record_create(dd, propname); | |
335 | cbr->cbr_pr = pr; | |
336 | list_insert_head(&pr->pr_cbs, cbr); | |
337 | list_insert_head(&ds->ds_prop_cbs, cbr); | |
34dc7c2f BB |
338 | mutex_exit(&dd->dd_lock); |
339 | ||
340 | cbr->cbr_func(cbr->cbr_arg, value); | |
34dc7c2f BB |
341 | return (0); |
342 | } | |
343 | ||
344 | int | |
b128c09f | 345 | dsl_prop_get(const char *dsname, const char *propname, |
34dc7c2f BB |
346 | int intsz, int numints, void *buf, char *setpoint) |
347 | { | |
13fe0198 MA |
348 | objset_t *os; |
349 | int error; | |
34dc7c2f | 350 | |
13fe0198 MA |
351 | error = dmu_objset_hold(dsname, FTAG, &os); |
352 | if (error != 0) | |
353 | return (error); | |
34dc7c2f | 354 | |
13fe0198 MA |
355 | error = dsl_prop_get_ds(dmu_objset_ds(os), propname, |
356 | intsz, numints, buf, setpoint); | |
34dc7c2f | 357 | |
13fe0198 MA |
358 | dmu_objset_rele(os, FTAG); |
359 | return (error); | |
34dc7c2f BB |
360 | } |
361 | ||
362 | /* | |
363 | * Get the current property value. It may have changed by the time this | |
364 | * function returns, so it is NOT safe to follow up with | |
365 | * dsl_prop_register() and assume that the value has not changed in | |
366 | * between. | |
367 | * | |
368 | * Return 0 on success, ENOENT if ddname is invalid. | |
369 | */ | |
370 | int | |
371 | dsl_prop_get_integer(const char *ddname, const char *propname, | |
372 | uint64_t *valuep, char *setpoint) | |
373 | { | |
374 | return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); | |
375 | } | |
376 | ||
13fe0198 MA |
377 | int |
378 | dsl_prop_get_int_ds(dsl_dataset_t *ds, const char *propname, | |
379 | uint64_t *valuep) | |
428870ff | 380 | { |
13fe0198 | 381 | return (dsl_prop_get_ds(ds, propname, 8, 1, valuep, NULL)); |
428870ff BB |
382 | } |
383 | ||
384 | /* | |
385 | * Predict the effective value of the given special property if it were set with | |
386 | * the given value and source. This is not a general purpose function. It exists | |
387 | * only to handle the special requirements of the quota and reservation | |
388 | * properties. The fact that these properties are non-inheritable greatly | |
389 | * simplifies the prediction logic. | |
390 | * | |
391 | * Returns 0 on success, a positive error code on failure, or -1 if called with | |
392 | * a property not handled by this function. | |
393 | */ | |
394 | int | |
13fe0198 MA |
395 | dsl_prop_predict(dsl_dir_t *dd, const char *propname, |
396 | zprop_source_t source, uint64_t value, uint64_t *newvalp) | |
428870ff | 397 | { |
428870ff | 398 | zfs_prop_t prop = zfs_name_to_prop(propname); |
428870ff BB |
399 | objset_t *mos; |
400 | uint64_t zapobj; | |
401 | uint64_t version; | |
402 | char *recvdstr; | |
403 | int err = 0; | |
404 | ||
405 | switch (prop) { | |
406 | case ZFS_PROP_QUOTA: | |
407 | case ZFS_PROP_RESERVATION: | |
408 | case ZFS_PROP_REFQUOTA: | |
409 | case ZFS_PROP_REFRESERVATION: | |
410 | break; | |
411 | default: | |
412 | return (-1); | |
413 | } | |
414 | ||
415 | mos = dd->dd_pool->dp_meta_objset; | |
d683ddbb | 416 | zapobj = dsl_dir_phys(dd)->dd_props_zapobj; |
428870ff BB |
417 | recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); |
418 | ||
419 | version = spa_version(dd->dd_pool->dp_spa); | |
420 | if (version < SPA_VERSION_RECVD_PROPS) { | |
421 | if (source & ZPROP_SRC_NONE) | |
422 | source = ZPROP_SRC_NONE; | |
423 | else if (source & ZPROP_SRC_RECEIVED) | |
424 | source = ZPROP_SRC_LOCAL; | |
425 | } | |
426 | ||
a31a70bb | 427 | switch ((int)source) { |
428870ff BB |
428 | case ZPROP_SRC_NONE: |
429 | /* Revert to the received value, if any. */ | |
13fe0198 | 430 | err = zap_lookup(mos, zapobj, recvdstr, 8, 1, newvalp); |
428870ff | 431 | if (err == ENOENT) |
13fe0198 | 432 | *newvalp = 0; |
428870ff BB |
433 | break; |
434 | case ZPROP_SRC_LOCAL: | |
13fe0198 | 435 | *newvalp = value; |
428870ff BB |
436 | break; |
437 | case ZPROP_SRC_RECEIVED: | |
438 | /* | |
439 | * If there's no local setting, then the new received value will | |
440 | * be the effective value. | |
441 | */ | |
13fe0198 | 442 | err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp); |
428870ff | 443 | if (err == ENOENT) |
13fe0198 | 444 | *newvalp = value; |
428870ff BB |
445 | break; |
446 | case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED): | |
447 | /* | |
448 | * We're clearing the received value, so the local setting (if | |
449 | * it exists) remains the effective value. | |
450 | */ | |
13fe0198 | 451 | err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp); |
428870ff | 452 | if (err == ENOENT) |
13fe0198 | 453 | *newvalp = 0; |
428870ff BB |
454 | break; |
455 | default: | |
13fe0198 | 456 | panic("unexpected property source: %d", source); |
428870ff BB |
457 | } |
458 | ||
e4f5fa12 | 459 | kmem_strfree(recvdstr); |
428870ff BB |
460 | |
461 | if (err == ENOENT) | |
462 | return (0); | |
463 | ||
464 | return (err); | |
465 | } | |
466 | ||
34dc7c2f BB |
467 | /* |
468 | * Unregister this callback. Return 0 on success, ENOENT if ddname is | |
d3cc8b15 | 469 | * invalid, or ENOMSG if no matching callback registered. |
0eb21616 JG |
470 | * |
471 | * NOTE: This function is no longer used internally but has been preserved | |
472 | * to prevent breaking external consumers (Lustre, etc). | |
34dc7c2f BB |
473 | */ |
474 | int | |
475 | dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, | |
476 | dsl_prop_changed_cb_t *callback, void *cbarg) | |
477 | { | |
478 | dsl_dir_t *dd = ds->ds_dir; | |
479 | dsl_prop_cb_record_t *cbr; | |
480 | ||
481 | mutex_enter(&dd->dd_lock); | |
0eb21616 JG |
482 | for (cbr = list_head(&ds->ds_prop_cbs); |
483 | cbr; cbr = list_next(&ds->ds_prop_cbs, cbr)) { | |
34dc7c2f BB |
484 | if (cbr->cbr_ds == ds && |
485 | cbr->cbr_func == callback && | |
486 | cbr->cbr_arg == cbarg && | |
0eb21616 | 487 | strcmp(cbr->cbr_pr->pr_propname, propname) == 0) |
34dc7c2f BB |
488 | break; |
489 | } | |
490 | ||
491 | if (cbr == NULL) { | |
492 | mutex_exit(&dd->dd_lock); | |
2e528b49 | 493 | return (SET_ERROR(ENOMSG)); |
34dc7c2f BB |
494 | } |
495 | ||
0eb21616 JG |
496 | list_remove(&ds->ds_prop_cbs, cbr); |
497 | list_remove(&cbr->cbr_pr->pr_cbs, cbr); | |
34dc7c2f | 498 | mutex_exit(&dd->dd_lock); |
34dc7c2f BB |
499 | kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); |
500 | ||
34dc7c2f BB |
501 | return (0); |
502 | } | |
503 | ||
0eb21616 JG |
504 | /* |
505 | * Unregister all callbacks that are registered with the | |
506 | * given callback argument. | |
507 | */ | |
508 | void | |
509 | dsl_prop_unregister_all(dsl_dataset_t *ds, void *cbarg) | |
34dc7c2f | 510 | { |
0eb21616 JG |
511 | dsl_prop_cb_record_t *cbr, *next_cbr; |
512 | ||
34dc7c2f | 513 | dsl_dir_t *dd = ds->ds_dir; |
34dc7c2f BB |
514 | |
515 | mutex_enter(&dd->dd_lock); | |
0eb21616 JG |
516 | next_cbr = list_head(&ds->ds_prop_cbs); |
517 | while (next_cbr != NULL) { | |
518 | cbr = next_cbr; | |
519 | next_cbr = list_next(&ds->ds_prop_cbs, cbr); | |
520 | if (cbr->cbr_arg == cbarg) { | |
521 | list_remove(&ds->ds_prop_cbs, cbr); | |
522 | list_remove(&cbr->cbr_pr->pr_cbs, cbr); | |
523 | kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); | |
13fe0198 | 524 | } |
34dc7c2f BB |
525 | } |
526 | mutex_exit(&dd->dd_lock); | |
0eb21616 JG |
527 | } |
528 | ||
529 | boolean_t | |
530 | dsl_prop_hascb(dsl_dataset_t *ds) | |
531 | { | |
532 | return (!list_is_empty(&ds->ds_prop_cbs)); | |
13fe0198 | 533 | } |
34dc7c2f | 534 | |
13fe0198 MA |
535 | static int |
536 | dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) | |
537 | { | |
14e4e3cb | 538 | (void) arg; |
13fe0198 | 539 | dsl_dir_t *dd = ds->ds_dir; |
0eb21616 | 540 | dsl_prop_record_t *pr; |
13fe0198 MA |
541 | dsl_prop_cb_record_t *cbr; |
542 | ||
543 | mutex_enter(&dd->dd_lock); | |
0eb21616 JG |
544 | for (pr = list_head(&dd->dd_props); |
545 | pr; pr = list_next(&dd->dd_props, pr)) { | |
546 | for (cbr = list_head(&pr->pr_cbs); cbr; | |
547 | cbr = list_next(&pr->pr_cbs, cbr)) { | |
548 | uint64_t value; | |
13fe0198 | 549 | |
0eb21616 JG |
550 | /* |
551 | * Callback entries do not have holds on their | |
552 | * datasets so that datasets with registered | |
553 | * callbacks are still eligible for eviction. | |
554 | * Unlike operations to update properties on a | |
555 | * single dataset, we are performing a recursive | |
556 | * descent of related head datasets. The caller | |
557 | * of this function only has a dataset hold on | |
558 | * the passed in head dataset, not the snapshots | |
559 | * associated with this dataset. Without a hold, | |
560 | * the dataset pointer within callback records | |
561 | * for snapshots can be invalidated by eviction | |
562 | * at any time. | |
563 | * | |
564 | * Use dsl_dataset_try_add_ref() to verify | |
565 | * that the dataset for a snapshot has not | |
566 | * begun eviction processing and to prevent | |
567 | * eviction from occurring for the duration of | |
568 | * the callback. If the hold attempt fails, | |
569 | * this object is already being evicted and the | |
570 | * callback can be safely ignored. | |
571 | */ | |
572 | if (ds != cbr->cbr_ds && | |
573 | !dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) | |
574 | continue; | |
6ebebace | 575 | |
0eb21616 JG |
576 | if (dsl_prop_get_ds(cbr->cbr_ds, |
577 | cbr->cbr_pr->pr_propname, sizeof (value), 1, | |
578 | &value, NULL) == 0) | |
579 | cbr->cbr_func(cbr->cbr_arg, value); | |
6ebebace | 580 | |
0eb21616 JG |
581 | if (ds != cbr->cbr_ds) |
582 | dsl_dataset_rele(cbr->cbr_ds, FTAG); | |
583 | } | |
13fe0198 MA |
584 | } |
585 | mutex_exit(&dd->dd_lock); | |
586 | ||
587 | return (0); | |
588 | } | |
589 | ||
590 | /* | |
591 | * Update all property values for ddobj & its descendants. This is used | |
592 | * when renaming the dir. | |
593 | */ | |
594 | void | |
595 | dsl_prop_notify_all(dsl_dir_t *dd) | |
596 | { | |
597 | dsl_pool_t *dp = dd->dd_pool; | |
598 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); | |
599 | (void) dmu_objset_find_dp(dp, dd->dd_object, dsl_prop_notify_all_cb, | |
600 | NULL, DS_FIND_CHILDREN); | |
34dc7c2f BB |
601 | } |
602 | ||
603 | static void | |
604 | dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, | |
605 | const char *propname, uint64_t value, int first) | |
606 | { | |
607 | dsl_dir_t *dd; | |
0eb21616 | 608 | dsl_prop_record_t *pr; |
34dc7c2f BB |
609 | dsl_prop_cb_record_t *cbr; |
610 | objset_t *mos = dp->dp_meta_objset; | |
611 | zap_cursor_t zc; | |
612 | zap_attribute_t *za; | |
613 | int err; | |
614 | ||
13fe0198 MA |
615 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); |
616 | err = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd); | |
34dc7c2f BB |
617 | if (err) |
618 | return; | |
619 | ||
620 | if (!first) { | |
621 | /* | |
622 | * If the prop is set here, then this change is not | |
623 | * being inherited here or below; stop the recursion. | |
624 | */ | |
d683ddbb JG |
625 | err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj, |
626 | propname); | |
34dc7c2f | 627 | if (err == 0) { |
13fe0198 | 628 | dsl_dir_rele(dd, FTAG); |
34dc7c2f BB |
629 | return; |
630 | } | |
631 | ASSERT3U(err, ==, ENOENT); | |
632 | } | |
633 | ||
634 | mutex_enter(&dd->dd_lock); | |
0eb21616 JG |
635 | pr = dsl_prop_record_find(dd, propname); |
636 | if (pr != NULL) { | |
637 | for (cbr = list_head(&pr->pr_cbs); cbr; | |
638 | cbr = list_next(&pr->pr_cbs, cbr)) { | |
639 | uint64_t propobj; | |
b128c09f | 640 | |
0eb21616 JG |
641 | /* |
642 | * cbr->cbr_ds may be invalidated due to eviction, | |
643 | * requiring the use of dsl_dataset_try_add_ref(). | |
644 | * See comment block in dsl_prop_notify_all_cb() | |
645 | * for details. | |
646 | */ | |
647 | if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) | |
648 | continue; | |
b128c09f | 649 | |
0eb21616 | 650 | propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj; |
6ebebace | 651 | |
0eb21616 JG |
652 | /* |
653 | * If the property is not set on this ds, then it is | |
654 | * inherited here; call the callback. | |
655 | */ | |
656 | if (propobj == 0 || | |
657 | zap_contains(mos, propobj, propname) != 0) | |
658 | cbr->cbr_func(cbr->cbr_arg, value); | |
b128c09f | 659 | |
0eb21616 JG |
660 | dsl_dataset_rele(cbr->cbr_ds, FTAG); |
661 | } | |
34dc7c2f BB |
662 | } |
663 | mutex_exit(&dd->dd_lock); | |
664 | ||
79c76d5b | 665 | za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); |
34dc7c2f | 666 | for (zap_cursor_init(&zc, mos, |
d683ddbb | 667 | dsl_dir_phys(dd)->dd_child_dir_zapobj); |
34dc7c2f BB |
668 | zap_cursor_retrieve(&zc, za) == 0; |
669 | zap_cursor_advance(&zc)) { | |
670 | dsl_prop_changed_notify(dp, za->za_first_integer, | |
671 | propname, value, FALSE); | |
672 | } | |
673 | kmem_free(za, sizeof (zap_attribute_t)); | |
674 | zap_cursor_fini(&zc); | |
13fe0198 | 675 | dsl_dir_rele(dd, FTAG); |
34dc7c2f BB |
676 | } |
677 | ||
5405be03 A |
678 | |
679 | /* | |
680 | * For newer values in zfs index type properties, we add a new key | |
681 | * propname$iuv (iuv = Ignore Unknown Values) to the properties zap object | |
682 | * to store the new property value and store the default value in the | |
683 | * existing prop key. So that the propname$iuv key is ignored by the older zfs | |
684 | * versions and the default property value from the existing prop key is | |
685 | * used. | |
686 | */ | |
687 | static void | |
688 | dsl_prop_set_iuv(objset_t *mos, uint64_t zapobj, const char *propname, | |
689 | int intsz, int numints, const void *value, dmu_tx_t *tx) | |
690 | { | |
691 | char *iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX); | |
692 | boolean_t iuv = B_FALSE; | |
693 | zfs_prop_t prop = zfs_name_to_prop(propname); | |
694 | ||
695 | switch (prop) { | |
696 | case ZFS_PROP_REDUNDANT_METADATA: | |
697 | if (*(uint64_t *)value == ZFS_REDUNDANT_METADATA_SOME || | |
698 | *(uint64_t *)value == ZFS_REDUNDANT_METADATA_NONE) | |
699 | iuv = B_TRUE; | |
700 | break; | |
701 | default: | |
702 | break; | |
703 | } | |
704 | ||
705 | if (iuv) { | |
706 | VERIFY0(zap_update(mos, zapobj, iuvstr, intsz, numints, | |
707 | value, tx)); | |
708 | uint64_t val = zfs_prop_default_numeric(prop); | |
709 | VERIFY0(zap_update(mos, zapobj, propname, intsz, numints, | |
710 | &val, tx)); | |
711 | } else { | |
712 | zap_remove(mos, zapobj, iuvstr, tx); | |
713 | } | |
714 | kmem_strfree(iuvstr); | |
715 | } | |
716 | ||
428870ff | 717 | void |
13fe0198 MA |
718 | dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname, |
719 | zprop_source_t source, int intsz, int numints, const void *value, | |
720 | dmu_tx_t *tx) | |
34dc7c2f | 721 | { |
b128c09f | 722 | objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; |
c317c8c8 | 723 | uint64_t zapobj, intval, dummy, count; |
34dc7c2f BB |
724 | int isint; |
725 | char valbuf[32]; | |
13fe0198 | 726 | const char *valstr = NULL; |
428870ff BB |
727 | char *inheritstr; |
728 | char *recvdstr; | |
5405be03 | 729 | char *iuvstr; |
428870ff BB |
730 | char *tbuf = NULL; |
731 | int err; | |
732 | uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa); | |
34dc7c2f | 733 | |
887d1e60 | 734 | isint = (dodefault(zfs_name_to_prop(propname), 8, 1, &intval) == 0); |
34dc7c2f | 735 | |
0c66c32d | 736 | if (ds->ds_is_snapshot) { |
428870ff | 737 | ASSERT(version >= SPA_VERSION_SNAP_PROPS); |
c317c8c8 TC |
738 | if (dsl_dataset_phys(ds)->ds_props_obj == 0 && |
739 | (source & ZPROP_SRC_NONE) == 0) { | |
b128c09f | 740 | dmu_buf_will_dirty(ds->ds_dbuf, tx); |
d683ddbb | 741 | dsl_dataset_phys(ds)->ds_props_obj = |
b128c09f BB |
742 | zap_create(mos, |
743 | DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx); | |
744 | } | |
d683ddbb | 745 | zapobj = dsl_dataset_phys(ds)->ds_props_obj; |
b128c09f | 746 | } else { |
d683ddbb | 747 | zapobj = dsl_dir_phys(ds->ds_dir)->dd_props_zapobj; |
b128c09f BB |
748 | } |
749 | ||
c317c8c8 TC |
750 | /* If we are removing objects from a non-existent ZAP just return */ |
751 | if (zapobj == 0) | |
752 | return; | |
753 | ||
428870ff | 754 | if (version < SPA_VERSION_RECVD_PROPS) { |
428870ff BB |
755 | if (source & ZPROP_SRC_NONE) |
756 | source = ZPROP_SRC_NONE; | |
757 | else if (source & ZPROP_SRC_RECEIVED) | |
758 | source = ZPROP_SRC_LOCAL; | |
759 | } | |
760 | ||
761 | inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX); | |
762 | recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); | |
5405be03 | 763 | iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX); |
428870ff | 764 | |
a31a70bb | 765 | switch ((int)source) { |
428870ff BB |
766 | case ZPROP_SRC_NONE: |
767 | /* | |
768 | * revert to received value, if any (inherit -S) | |
769 | * - remove propname | |
770 | * - remove propname$inherit | |
771 | */ | |
772 | err = zap_remove(mos, zapobj, propname, tx); | |
773 | ASSERT(err == 0 || err == ENOENT); | |
774 | err = zap_remove(mos, zapobj, inheritstr, tx); | |
34dc7c2f | 775 | ASSERT(err == 0 || err == ENOENT); |
428870ff BB |
776 | break; |
777 | case ZPROP_SRC_LOCAL: | |
778 | /* | |
779 | * remove propname$inherit | |
780 | * set propname -> value | |
5405be03 | 781 | * set propname$iuv -> new property value |
428870ff BB |
782 | */ |
783 | err = zap_remove(mos, zapobj, inheritstr, tx); | |
784 | ASSERT(err == 0 || err == ENOENT); | |
13fe0198 MA |
785 | VERIFY0(zap_update(mos, zapobj, propname, |
786 | intsz, numints, value, tx)); | |
5405be03 A |
787 | (void) dsl_prop_set_iuv(mos, zapobj, propname, intsz, |
788 | numints, value, tx); | |
428870ff BB |
789 | break; |
790 | case ZPROP_SRC_INHERITED: | |
791 | /* | |
792 | * explicitly inherit | |
793 | * - remove propname | |
794 | * - set propname$inherit | |
795 | */ | |
796 | err = zap_remove(mos, zapobj, propname, tx); | |
797 | ASSERT(err == 0 || err == ENOENT); | |
5405be03 A |
798 | err = zap_remove(mos, zapobj, iuvstr, tx); |
799 | ASSERT(err == 0 || err == ENOENT); | |
428870ff | 800 | if (version >= SPA_VERSION_RECVD_PROPS && |
13fe0198 | 801 | dsl_prop_get_int_ds(ds, ZPROP_HAS_RECVD, &dummy) == 0) { |
428870ff | 802 | dummy = 0; |
13fe0198 MA |
803 | VERIFY0(zap_update(mos, zapobj, inheritstr, |
804 | 8, 1, &dummy, tx)); | |
34dc7c2f | 805 | } |
428870ff BB |
806 | break; |
807 | case ZPROP_SRC_RECEIVED: | |
808 | /* | |
809 | * set propname$recvd -> value | |
810 | */ | |
811 | err = zap_update(mos, zapobj, recvdstr, | |
13fe0198 | 812 | intsz, numints, value, tx); |
428870ff BB |
813 | ASSERT(err == 0); |
814 | break; | |
815 | case (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED): | |
816 | /* | |
817 | * clear local and received settings | |
818 | * - remove propname | |
819 | * - remove propname$inherit | |
820 | * - remove propname$recvd | |
821 | */ | |
822 | err = zap_remove(mos, zapobj, propname, tx); | |
823 | ASSERT(err == 0 || err == ENOENT); | |
824 | err = zap_remove(mos, zapobj, inheritstr, tx); | |
825 | ASSERT(err == 0 || err == ENOENT); | |
9a70e97f | 826 | zfs_fallthrough; |
428870ff BB |
827 | case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED): |
828 | /* | |
829 | * remove propname$recvd | |
830 | */ | |
831 | err = zap_remove(mos, zapobj, recvdstr, tx); | |
832 | ASSERT(err == 0 || err == ENOENT); | |
833 | break; | |
834 | default: | |
835 | cmn_err(CE_PANIC, "unexpected property source: %d", source); | |
34dc7c2f BB |
836 | } |
837 | ||
e4f5fa12 MM |
838 | kmem_strfree(inheritstr); |
839 | kmem_strfree(recvdstr); | |
5405be03 | 840 | kmem_strfree(iuvstr); |
428870ff | 841 | |
c317c8c8 TC |
842 | /* |
843 | * If we are left with an empty snap zap we can destroy it. | |
844 | * This will prevent unnecessary calls to zap_lookup() in | |
845 | * the "zfs list" and "zfs get" code paths. | |
846 | */ | |
847 | if (ds->ds_is_snapshot && | |
848 | zap_count(mos, zapobj, &count) == 0 && count == 0) { | |
849 | dmu_buf_will_dirty(ds->ds_dbuf, tx); | |
850 | dsl_dataset_phys(ds)->ds_props_obj = 0; | |
851 | zap_destroy(mos, zapobj, tx); | |
852 | } | |
853 | ||
34dc7c2f | 854 | if (isint) { |
13fe0198 | 855 | VERIFY0(dsl_prop_get_int_ds(ds, propname, &intval)); |
428870ff | 856 | |
0c66c32d | 857 | if (ds->ds_is_snapshot) { |
b128c09f BB |
858 | dsl_prop_cb_record_t *cbr; |
859 | /* | |
860 | * It's a snapshot; nothing can inherit this | |
861 | * property, so just look for callbacks on this | |
862 | * ds here. | |
863 | */ | |
864 | mutex_enter(&ds->ds_dir->dd_lock); | |
0eb21616 JG |
865 | for (cbr = list_head(&ds->ds_prop_cbs); cbr; |
866 | cbr = list_next(&ds->ds_prop_cbs, cbr)) { | |
867 | if (strcmp(cbr->cbr_pr->pr_propname, | |
868 | propname) == 0) | |
b128c09f BB |
869 | cbr->cbr_func(cbr->cbr_arg, intval); |
870 | } | |
871 | mutex_exit(&ds->ds_dir->dd_lock); | |
872 | } else { | |
873 | dsl_prop_changed_notify(ds->ds_dir->dd_pool, | |
428870ff | 874 | ds->ds_dir->dd_object, propname, intval, TRUE); |
b128c09f | 875 | } |
428870ff | 876 | |
34dc7c2f BB |
877 | (void) snprintf(valbuf, sizeof (valbuf), |
878 | "%lld", (longlong_t)intval); | |
879 | valstr = valbuf; | |
880 | } else { | |
428870ff | 881 | if (source == ZPROP_SRC_LOCAL) { |
13fe0198 | 882 | valstr = value; |
428870ff | 883 | } else { |
79c76d5b | 884 | tbuf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); |
428870ff BB |
885 | if (dsl_prop_get_ds(ds, propname, 1, |
886 | ZAP_MAXVALUELEN, tbuf, NULL) == 0) | |
887 | valstr = tbuf; | |
888 | } | |
34dc7c2f | 889 | } |
428870ff | 890 | |
6f1ffb06 MA |
891 | spa_history_log_internal_ds(ds, (source == ZPROP_SRC_NONE || |
892 | source == ZPROP_SRC_INHERITED) ? "inherit" : "set", tx, | |
893 | "%s=%s", propname, (valstr == NULL ? "" : valstr)); | |
428870ff BB |
894 | |
895 | if (tbuf != NULL) | |
896 | kmem_free(tbuf, ZAP_MAXVALUELEN); | |
34dc7c2f BB |
897 | } |
898 | ||
13fe0198 MA |
899 | int |
900 | dsl_prop_set_int(const char *dsname, const char *propname, | |
901 | zprop_source_t source, uint64_t value) | |
9babb374 | 902 | { |
13fe0198 MA |
903 | nvlist_t *nvl = fnvlist_alloc(); |
904 | int error; | |
428870ff | 905 | |
13fe0198 MA |
906 | fnvlist_add_uint64(nvl, propname, value); |
907 | error = dsl_props_set(dsname, source, nvl); | |
908 | fnvlist_free(nvl); | |
909 | return (error); | |
9babb374 BB |
910 | } |
911 | ||
34dc7c2f | 912 | int |
13fe0198 MA |
913 | dsl_prop_set_string(const char *dsname, const char *propname, |
914 | zprop_source_t source, const char *value) | |
34dc7c2f | 915 | { |
13fe0198 MA |
916 | nvlist_t *nvl = fnvlist_alloc(); |
917 | int error; | |
b128c09f | 918 | |
13fe0198 MA |
919 | fnvlist_add_string(nvl, propname, value); |
920 | error = dsl_props_set(dsname, source, nvl); | |
921 | fnvlist_free(nvl); | |
922 | return (error); | |
923 | } | |
428870ff | 924 | |
13fe0198 MA |
925 | int |
926 | dsl_prop_inherit(const char *dsname, const char *propname, | |
927 | zprop_source_t source) | |
928 | { | |
929 | nvlist_t *nvl = fnvlist_alloc(); | |
930 | int error; | |
b128c09f | 931 | |
13fe0198 MA |
932 | fnvlist_add_boolean(nvl, propname); |
933 | error = dsl_props_set(dsname, source, nvl); | |
934 | fnvlist_free(nvl); | |
935 | return (error); | |
34dc7c2f BB |
936 | } |
937 | ||
e2ef1cbf | 938 | int |
13fe0198 | 939 | dsl_props_set_check(void *arg, dmu_tx_t *tx) |
9babb374 | 940 | { |
13fe0198 MA |
941 | dsl_props_set_arg_t *dpsa = arg; |
942 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
9babb374 BB |
943 | dsl_dataset_t *ds; |
944 | uint64_t version; | |
945 | nvpair_t *elem = NULL; | |
946 | int err; | |
947 | ||
13fe0198 MA |
948 | err = dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds); |
949 | if (err != 0) | |
9babb374 | 950 | return (err); |
13fe0198 | 951 | |
9babb374 | 952 | version = spa_version(ds->ds_dir->dd_pool->dp_spa); |
13fe0198 | 953 | while ((elem = nvlist_next_nvpair(dpsa->dpsa_props, elem)) != NULL) { |
9babb374 BB |
954 | if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { |
955 | dsl_dataset_rele(ds, FTAG); | |
2e528b49 | 956 | return (SET_ERROR(ENAMETOOLONG)); |
9babb374 BB |
957 | } |
958 | if (nvpair_type(elem) == DATA_TYPE_STRING) { | |
d1807f16 | 959 | const char *valstr = fnvpair_value_string(elem); |
9babb374 BB |
960 | if (strlen(valstr) >= (version < |
961 | SPA_VERSION_STMF_PROP ? | |
962 | ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) { | |
963 | dsl_dataset_rele(ds, FTAG); | |
ecb2b7dc | 964 | return (SET_ERROR(E2BIG)); |
9babb374 BB |
965 | } |
966 | } | |
967 | } | |
968 | ||
0c66c32d | 969 | if (ds->ds_is_snapshot && version < SPA_VERSION_SNAP_PROPS) { |
9babb374 | 970 | dsl_dataset_rele(ds, FTAG); |
2e528b49 | 971 | return (SET_ERROR(ENOTSUP)); |
9babb374 | 972 | } |
13fe0198 MA |
973 | dsl_dataset_rele(ds, FTAG); |
974 | return (0); | |
975 | } | |
976 | ||
977 | void | |
978 | dsl_props_set_sync_impl(dsl_dataset_t *ds, zprop_source_t source, | |
979 | nvlist_t *props, dmu_tx_t *tx) | |
980 | { | |
981 | nvpair_t *elem = NULL; | |
982 | ||
983 | while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { | |
984 | nvpair_t *pair = elem; | |
5f1346c2 | 985 | const char *name = nvpair_name(pair); |
9babb374 | 986 | |
13fe0198 MA |
987 | if (nvpair_type(pair) == DATA_TYPE_NVLIST) { |
988 | /* | |
5f1346c2 | 989 | * This usually happens when we reuse the nvlist_t data |
990 | * returned by the counterpart dsl_prop_get_all_impl(). | |
991 | * For instance we do this to restore the original | |
992 | * received properties when an error occurs in the | |
993 | * zfs_ioc_recv() codepath. | |
13fe0198 MA |
994 | */ |
995 | nvlist_t *attrs = fnvpair_value_nvlist(pair); | |
996 | pair = fnvlist_lookup_nvpair(attrs, ZPROP_VALUE); | |
997 | } | |
998 | ||
999 | if (nvpair_type(pair) == DATA_TYPE_STRING) { | |
1000 | const char *value = fnvpair_value_string(pair); | |
5f1346c2 | 1001 | dsl_prop_set_sync_impl(ds, name, |
13fe0198 MA |
1002 | source, 1, strlen(value) + 1, value, tx); |
1003 | } else if (nvpair_type(pair) == DATA_TYPE_UINT64) { | |
1004 | uint64_t intval = fnvpair_value_uint64(pair); | |
5f1346c2 | 1005 | dsl_prop_set_sync_impl(ds, name, |
13fe0198 MA |
1006 | source, sizeof (intval), 1, &intval, tx); |
1007 | } else if (nvpair_type(pair) == DATA_TYPE_BOOLEAN) { | |
5f1346c2 | 1008 | dsl_prop_set_sync_impl(ds, name, |
13fe0198 MA |
1009 | source, 0, 0, NULL, tx); |
1010 | } else { | |
1011 | panic("invalid nvpair type"); | |
1012 | } | |
1013 | } | |
1014 | } | |
428870ff | 1015 | |
e2ef1cbf | 1016 | void |
13fe0198 MA |
1017 | dsl_props_set_sync(void *arg, dmu_tx_t *tx) |
1018 | { | |
1019 | dsl_props_set_arg_t *dpsa = arg; | |
1020 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
1021 | dsl_dataset_t *ds; | |
9babb374 | 1022 | |
13fe0198 MA |
1023 | VERIFY0(dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds)); |
1024 | dsl_props_set_sync_impl(ds, dpsa->dpsa_source, dpsa->dpsa_props, tx); | |
9babb374 | 1025 | dsl_dataset_rele(ds, FTAG); |
13fe0198 MA |
1026 | } |
1027 | ||
1028 | /* | |
1029 | * All-or-nothing; if any prop can't be set, nothing will be modified. | |
1030 | */ | |
1031 | int | |
1032 | dsl_props_set(const char *dsname, zprop_source_t source, nvlist_t *props) | |
1033 | { | |
1034 | dsl_props_set_arg_t dpsa; | |
1035 | int nblks = 0; | |
1036 | ||
1037 | dpsa.dpsa_dsname = dsname; | |
1038 | dpsa.dpsa_source = source; | |
1039 | dpsa.dpsa_props = props; | |
1040 | ||
1041 | /* | |
1042 | * If the source includes NONE, then we will only be removing entries | |
1043 | * from the ZAP object. In that case don't check for ENOSPC. | |
1044 | */ | |
1045 | if ((source & ZPROP_SRC_NONE) == 0) | |
1046 | nblks = 2 * fnvlist_num_pairs(props); | |
1047 | ||
1048 | return (dsl_sync_task(dsname, dsl_props_set_check, dsl_props_set_sync, | |
3d45fdd6 | 1049 | &dpsa, nblks, ZFS_SPACE_CHECK_RESERVED)); |
9babb374 BB |
1050 | } |
1051 | ||
428870ff BB |
1052 | typedef enum dsl_prop_getflags { |
1053 | DSL_PROP_GET_INHERITING = 0x1, /* searching parent of target ds */ | |
1054 | DSL_PROP_GET_SNAPSHOT = 0x2, /* snapshot dataset */ | |
1055 | DSL_PROP_GET_LOCAL = 0x4, /* local properties */ | |
b5256303 | 1056 | DSL_PROP_GET_RECEIVED = 0x8, /* received properties */ |
428870ff BB |
1057 | } dsl_prop_getflags_t; |
1058 | ||
1059 | static int | |
1060 | dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, | |
1061 | const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv) | |
1062 | { | |
1063 | zap_cursor_t zc; | |
1064 | zap_attribute_t za; | |
1065 | int err = 0; | |
1066 | ||
1067 | for (zap_cursor_init(&zc, mos, propobj); | |
1068 | (err = zap_cursor_retrieve(&zc, &za)) == 0; | |
1069 | zap_cursor_advance(&zc)) { | |
1070 | nvlist_t *propval; | |
1071 | zfs_prop_t prop; | |
1072 | char buf[ZAP_MAXNAMELEN]; | |
1073 | char *valstr; | |
1074 | const char *suffix; | |
1075 | const char *propname; | |
1076 | const char *source; | |
1077 | ||
1078 | suffix = strchr(za.za_name, '$'); | |
1079 | ||
1080 | if (suffix == NULL) { | |
1081 | /* | |
1082 | * Skip local properties if we only want received | |
1083 | * properties. | |
1084 | */ | |
1085 | if (flags & DSL_PROP_GET_RECEIVED) | |
1086 | continue; | |
1087 | ||
1088 | propname = za.za_name; | |
1089 | source = setpoint; | |
5405be03 A |
1090 | |
1091 | /* Skip if iuv entries are preset. */ | |
1092 | valstr = kmem_asprintf("%s%s", propname, | |
1093 | ZPROP_IUV_SUFFIX); | |
1094 | err = zap_contains(mos, propobj, valstr); | |
1095 | kmem_strfree(valstr); | |
1096 | if (err == 0) | |
1097 | continue; | |
428870ff BB |
1098 | } else if (strcmp(suffix, ZPROP_INHERIT_SUFFIX) == 0) { |
1099 | /* Skip explicitly inherited entries. */ | |
1100 | continue; | |
1101 | } else if (strcmp(suffix, ZPROP_RECVD_SUFFIX) == 0) { | |
1102 | if (flags & DSL_PROP_GET_LOCAL) | |
1103 | continue; | |
1104 | ||
7584fbe8 RY |
1105 | (void) strlcpy(buf, za.za_name, |
1106 | MIN(sizeof (buf), suffix - za.za_name + 1)); | |
428870ff BB |
1107 | propname = buf; |
1108 | ||
1109 | if (!(flags & DSL_PROP_GET_RECEIVED)) { | |
1110 | /* Skip if locally overridden. */ | |
1111 | err = zap_contains(mos, propobj, propname); | |
1112 | if (err == 0) | |
1113 | continue; | |
1114 | if (err != ENOENT) | |
1115 | break; | |
1116 | ||
1117 | /* Skip if explicitly inherited. */ | |
1118 | valstr = kmem_asprintf("%s%s", propname, | |
1119 | ZPROP_INHERIT_SUFFIX); | |
1120 | err = zap_contains(mos, propobj, valstr); | |
e4f5fa12 | 1121 | kmem_strfree(valstr); |
428870ff BB |
1122 | if (err == 0) |
1123 | continue; | |
1124 | if (err != ENOENT) | |
1125 | break; | |
1126 | } | |
1127 | ||
1128 | source = ((flags & DSL_PROP_GET_INHERITING) ? | |
1129 | setpoint : ZPROP_SOURCE_VAL_RECVD); | |
5405be03 A |
1130 | } else if (strcmp(suffix, ZPROP_IUV_SUFFIX) == 0) { |
1131 | (void) strlcpy(buf, za.za_name, | |
1132 | MIN(sizeof (buf), suffix - za.za_name + 1)); | |
1133 | propname = buf; | |
1134 | source = setpoint; | |
1135 | prop = zfs_name_to_prop(propname); | |
1136 | ||
1137 | if (dsl_prop_known_index(prop, | |
1138 | za.za_first_integer) != 1) | |
1139 | continue; | |
428870ff BB |
1140 | } else { |
1141 | /* | |
1142 | * For backward compatibility, skip suffixes we don't | |
1143 | * recognize. | |
1144 | */ | |
1145 | continue; | |
1146 | } | |
1147 | ||
1148 | prop = zfs_name_to_prop(propname); | |
1149 | ||
1150 | /* Skip non-inheritable properties. */ | |
4ff7a8fa AJ |
1151 | if ((flags & DSL_PROP_GET_INHERITING) && |
1152 | prop != ZPROP_USERPROP && !zfs_prop_inheritable(prop)) | |
428870ff BB |
1153 | continue; |
1154 | ||
1155 | /* Skip properties not valid for this type. */ | |
4ff7a8fa | 1156 | if ((flags & DSL_PROP_GET_SNAPSHOT) && prop != ZPROP_USERPROP && |
962d5242 | 1157 | !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT, B_FALSE)) |
428870ff BB |
1158 | continue; |
1159 | ||
1160 | /* Skip properties already defined. */ | |
1161 | if (nvlist_exists(nv, propname)) | |
1162 | continue; | |
1163 | ||
1164 | VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); | |
1165 | if (za.za_integer_length == 1) { | |
1166 | /* | |
1167 | * String property | |
1168 | */ | |
1169 | char *tmp = kmem_alloc(za.za_num_integers, | |
1170 | KM_SLEEP); | |
1171 | err = zap_lookup(mos, propobj, | |
1172 | za.za_name, 1, za.za_num_integers, tmp); | |
1173 | if (err != 0) { | |
1174 | kmem_free(tmp, za.za_num_integers); | |
1175 | break; | |
1176 | } | |
1177 | VERIFY(nvlist_add_string(propval, ZPROP_VALUE, | |
1178 | tmp) == 0); | |
1179 | kmem_free(tmp, za.za_num_integers); | |
1180 | } else { | |
1181 | /* | |
1182 | * Integer property | |
1183 | */ | |
1184 | ASSERT(za.za_integer_length == 8); | |
1185 | (void) nvlist_add_uint64(propval, ZPROP_VALUE, | |
1186 | za.za_first_integer); | |
1187 | } | |
1188 | ||
1189 | VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0); | |
1190 | VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); | |
1191 | nvlist_free(propval); | |
1192 | } | |
1193 | zap_cursor_fini(&zc); | |
1194 | if (err == ENOENT) | |
1195 | err = 0; | |
1196 | return (err); | |
1197 | } | |
1198 | ||
34dc7c2f BB |
1199 | /* |
1200 | * Iterate over all properties for this dataset and return them in an nvlist. | |
1201 | */ | |
428870ff BB |
1202 | static int |
1203 | dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp, | |
1204 | dsl_prop_getflags_t flags) | |
34dc7c2f | 1205 | { |
34dc7c2f | 1206 | dsl_dir_t *dd = ds->ds_dir; |
b128c09f BB |
1207 | dsl_pool_t *dp = dd->dd_pool; |
1208 | objset_t *mos = dp->dp_meta_objset; | |
428870ff | 1209 | int err = 0; |
eca7b760 | 1210 | char setpoint[ZFS_MAX_DATASET_NAME_LEN]; |
34dc7c2f BB |
1211 | |
1212 | VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); | |
1213 | ||
0c66c32d | 1214 | if (ds->ds_is_snapshot) |
428870ff | 1215 | flags |= DSL_PROP_GET_SNAPSHOT; |
34dc7c2f | 1216 | |
13fe0198 | 1217 | ASSERT(dsl_pool_config_held(dp)); |
428870ff | 1218 | |
d683ddbb | 1219 | if (dsl_dataset_phys(ds)->ds_props_obj != 0) { |
428870ff BB |
1220 | ASSERT(flags & DSL_PROP_GET_SNAPSHOT); |
1221 | dsl_dataset_name(ds, setpoint); | |
d683ddbb JG |
1222 | err = dsl_prop_get_all_impl(mos, |
1223 | dsl_dataset_phys(ds)->ds_props_obj, setpoint, flags, *nvp); | |
428870ff BB |
1224 | if (err) |
1225 | goto out; | |
1226 | } | |
1227 | ||
1228 | for (; dd != NULL; dd = dd->dd_parent) { | |
1229 | if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) { | |
1230 | if (flags & (DSL_PROP_GET_LOCAL | | |
1231 | DSL_PROP_GET_RECEIVED)) | |
1232 | break; | |
1233 | flags |= DSL_PROP_GET_INHERITING; | |
b128c09f | 1234 | } |
428870ff | 1235 | dsl_dir_name(dd, setpoint); |
d683ddbb JG |
1236 | err = dsl_prop_get_all_impl(mos, |
1237 | dsl_dir_phys(dd)->dd_props_zapobj, setpoint, flags, *nvp); | |
428870ff BB |
1238 | if (err) |
1239 | break; | |
1240 | } | |
b5256303 | 1241 | |
428870ff | 1242 | out: |
6d421005 B |
1243 | if (err) { |
1244 | nvlist_free(*nvp); | |
1245 | *nvp = NULL; | |
1246 | } | |
428870ff BB |
1247 | return (err); |
1248 | } | |
34dc7c2f | 1249 | |
428870ff | 1250 | boolean_t |
13fe0198 | 1251 | dsl_prop_get_hasrecvd(const char *dsname) |
428870ff | 1252 | { |
428870ff | 1253 | uint64_t dummy; |
b128c09f | 1254 | |
13fe0198 MA |
1255 | return (0 == |
1256 | dsl_prop_get_integer(dsname, ZPROP_HAS_RECVD, &dummy, NULL)); | |
428870ff | 1257 | } |
34dc7c2f | 1258 | |
13fe0198 MA |
1259 | static int |
1260 | dsl_prop_set_hasrecvd_impl(const char *dsname, zprop_source_t source) | |
428870ff | 1261 | { |
13fe0198 MA |
1262 | uint64_t version; |
1263 | spa_t *spa; | |
1264 | int error = 0; | |
34dc7c2f | 1265 | |
13fe0198 MA |
1266 | VERIFY0(spa_open(dsname, &spa, FTAG)); |
1267 | version = spa_version(spa); | |
1268 | spa_close(spa, FTAG); | |
34dc7c2f | 1269 | |
13fe0198 MA |
1270 | if (version >= SPA_VERSION_RECVD_PROPS) |
1271 | error = dsl_prop_set_int(dsname, ZPROP_HAS_RECVD, source, 0); | |
1272 | return (error); | |
428870ff | 1273 | } |
34dc7c2f | 1274 | |
428870ff BB |
1275 | /* |
1276 | * Call after successfully receiving properties to ensure that only the first | |
1277 | * receive on or after SPA_VERSION_RECVD_PROPS blows away local properties. | |
1278 | */ | |
13fe0198 MA |
1279 | int |
1280 | dsl_prop_set_hasrecvd(const char *dsname) | |
428870ff | 1281 | { |
13fe0198 MA |
1282 | int error = 0; |
1283 | if (!dsl_prop_get_hasrecvd(dsname)) | |
1284 | error = dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_LOCAL); | |
1285 | return (error); | |
428870ff | 1286 | } |
34dc7c2f | 1287 | |
428870ff | 1288 | void |
13fe0198 | 1289 | dsl_prop_unset_hasrecvd(const char *dsname) |
428870ff | 1290 | { |
13fe0198 | 1291 | VERIFY0(dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_NONE)); |
428870ff BB |
1292 | } |
1293 | ||
1294 | int | |
1295 | dsl_prop_get_all(objset_t *os, nvlist_t **nvp) | |
1296 | { | |
1297 | return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, 0)); | |
1298 | } | |
1299 | ||
1300 | int | |
13fe0198 | 1301 | dsl_prop_get_received(const char *dsname, nvlist_t **nvp) |
428870ff | 1302 | { |
13fe0198 MA |
1303 | objset_t *os; |
1304 | int error; | |
1305 | ||
428870ff BB |
1306 | /* |
1307 | * Received properties are not distinguishable from local properties | |
1308 | * until the dataset has received properties on or after | |
1309 | * SPA_VERSION_RECVD_PROPS. | |
1310 | */ | |
13fe0198 | 1311 | dsl_prop_getflags_t flags = (dsl_prop_get_hasrecvd(dsname) ? |
428870ff | 1312 | DSL_PROP_GET_RECEIVED : DSL_PROP_GET_LOCAL); |
13fe0198 MA |
1313 | |
1314 | error = dmu_objset_hold(dsname, FTAG, &os); | |
1315 | if (error != 0) | |
1316 | return (error); | |
1317 | error = dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, flags); | |
1318 | dmu_objset_rele(os, FTAG); | |
1319 | return (error); | |
34dc7c2f BB |
1320 | } |
1321 | ||
1322 | void | |
1323 | dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) | |
1324 | { | |
1325 | nvlist_t *propval; | |
428870ff BB |
1326 | const char *propname = zfs_prop_to_name(prop); |
1327 | uint64_t default_value; | |
1328 | ||
1329 | if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) { | |
1330 | VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); | |
1331 | return; | |
1332 | } | |
34dc7c2f BB |
1333 | |
1334 | VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); | |
1335 | VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); | |
428870ff | 1336 | /* Indicate the default source if we can. */ |
887d1e60 | 1337 | if (dodefault(prop, 8, 1, &default_value) == 0 && |
428870ff BB |
1338 | value == default_value) { |
1339 | VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, "") == 0); | |
1340 | } | |
1341 | VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); | |
34dc7c2f BB |
1342 | nvlist_free(propval); |
1343 | } | |
1344 | ||
1345 | void | |
1346 | dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) | |
1347 | { | |
1348 | nvlist_t *propval; | |
428870ff BB |
1349 | const char *propname = zfs_prop_to_name(prop); |
1350 | ||
1351 | if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) { | |
1352 | VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); | |
1353 | return; | |
1354 | } | |
34dc7c2f BB |
1355 | |
1356 | VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); | |
1357 | VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); | |
428870ff | 1358 | VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); |
34dc7c2f BB |
1359 | nvlist_free(propval); |
1360 | } | |
c28b2279 | 1361 | |
93ce2b4c | 1362 | #if defined(_KERNEL) |
f0fd83be BB |
1363 | EXPORT_SYMBOL(dsl_prop_register); |
1364 | EXPORT_SYMBOL(dsl_prop_unregister); | |
0eb21616 | 1365 | EXPORT_SYMBOL(dsl_prop_unregister_all); |
f0fd83be BB |
1366 | EXPORT_SYMBOL(dsl_prop_get); |
1367 | EXPORT_SYMBOL(dsl_prop_get_integer); | |
c28b2279 | 1368 | EXPORT_SYMBOL(dsl_prop_get_all); |
f0fd83be BB |
1369 | EXPORT_SYMBOL(dsl_prop_get_received); |
1370 | EXPORT_SYMBOL(dsl_prop_get_ds); | |
36342b13 | 1371 | EXPORT_SYMBOL(dsl_prop_get_int_ds); |
f0fd83be | 1372 | EXPORT_SYMBOL(dsl_prop_get_dd); |
36342b13 BB |
1373 | EXPORT_SYMBOL(dsl_props_set); |
1374 | EXPORT_SYMBOL(dsl_prop_set_int); | |
1375 | EXPORT_SYMBOL(dsl_prop_set_string); | |
1376 | EXPORT_SYMBOL(dsl_prop_inherit); | |
1377 | EXPORT_SYMBOL(dsl_prop_predict); | |
c28b2279 | 1378 | EXPORT_SYMBOL(dsl_prop_nvlist_add_uint64); |
f0fd83be | 1379 | EXPORT_SYMBOL(dsl_prop_nvlist_add_string); |
c28b2279 | 1380 | #endif |