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