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