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