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