xref: /illumos-gate/usr/src/uts/common/fs/zfs/dsl_prop.c (revision 92241e0b)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5ea8dc4b6Seschrock  * Common Development and Distribution License (the "License").
6ea8dc4b6Seschrock  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21fa9e4066Sahrens /*
225c0b6a79SRich Morris  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23fa9e4066Sahrens  * Use is subject to license terms.
24fa9e4066Sahrens  */
25fa9e4066Sahrens 
26*92241e0bSTom Erickson #include <sys/zfs_context.h>
27fa9e4066Sahrens #include <sys/dmu.h>
287f7322feSeschrock #include <sys/dmu_objset.h>
29fa9e4066Sahrens #include <sys/dmu_tx.h>
30fa9e4066Sahrens #include <sys/dsl_dataset.h>
31fa9e4066Sahrens #include <sys/dsl_dir.h>
32fa9e4066Sahrens #include <sys/dsl_prop.h>
331d452cf5Sahrens #include <sys/dsl_synctask.h>
34fa9e4066Sahrens #include <sys/spa.h>
35fa9e4066Sahrens #include <sys/zap.h>
36fa9e4066Sahrens #include <sys/fs/zfs.h>
37fa9e4066Sahrens 
38fa9e4066Sahrens #include "zfs_prop.h"
39fa9e4066Sahrens 
40*92241e0bSTom Erickson #define	ZPROP_INHERIT_SUFFIX "$inherit"
41*92241e0bSTom Erickson #define	ZPROP_RECVD_SUFFIX "$recvd"
42*92241e0bSTom Erickson 
43fa9e4066Sahrens static int
44*92241e0bSTom Erickson dodefault(const char *propname, int intsz, int numints, void *buf)
45fa9e4066Sahrens {
46fa9e4066Sahrens 	zfs_prop_t prop;
47fa9e4066Sahrens 
48da6c28aaSamw 	/*
49da6c28aaSamw 	 * The setonce properties are read-only, BUT they still
50da6c28aaSamw 	 * have a default value that can be used as the initial
51da6c28aaSamw 	 * value.
52da6c28aaSamw 	 */
53990b4856Slling 	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL ||
54da6c28aaSamw 	    (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop)))
55fa9e4066Sahrens 		return (ENOENT);
56fa9e4066Sahrens 
5791ebeef5Sahrens 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
58fa9e4066Sahrens 		if (intsz != 1)
59fa9e4066Sahrens 			return (EOVERFLOW);
60990b4856Slling 		(void) strncpy(buf, zfs_prop_default_string(prop),
61*92241e0bSTom Erickson 		    numints);
62fa9e4066Sahrens 	} else {
63*92241e0bSTom Erickson 		if (intsz != 8 || numints < 1)
64fa9e4066Sahrens 			return (EOVERFLOW);
65fa9e4066Sahrens 
66fa9e4066Sahrens 		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
67fa9e4066Sahrens 	}
68fa9e4066Sahrens 
69fa9e4066Sahrens 	return (0);
70fa9e4066Sahrens }
71fa9e4066Sahrens 
72bb0ade09Sahrens int
73bb0ade09Sahrens dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
74*92241e0bSTom Erickson     int intsz, int numints, void *buf, char *setpoint, boolean_t snapshot)
75fa9e4066Sahrens {
7699653d4eSeschrock 	int err = ENOENT;
77*92241e0bSTom Erickson 	dsl_dir_t *target = dd;
78bb0ade09Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
79e9dbad6fSeschrock 	zfs_prop_t prop;
80*92241e0bSTom Erickson 	boolean_t inheritable;
81*92241e0bSTom Erickson 	boolean_t inheriting = B_FALSE;
82*92241e0bSTom Erickson 	char *inheritstr;
83*92241e0bSTom Erickson 	char *recvdstr;
84fa9e4066Sahrens 
85bb0ade09Sahrens 	ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
86bb0ade09Sahrens 
87fa9e4066Sahrens 	if (setpoint)
88fa9e4066Sahrens 		setpoint[0] = '\0';
89fa9e4066Sahrens 
90e9dbad6fSeschrock 	prop = zfs_name_to_prop(propname);
91*92241e0bSTom Erickson 	inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
92*92241e0bSTom Erickson 	inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);
93*92241e0bSTom Erickson 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
94e9dbad6fSeschrock 
9599653d4eSeschrock 	/*
96*92241e0bSTom Erickson 	 * Note: dd may become NULL, therefore we shouldn't dereference it
97*92241e0bSTom Erickson 	 * after this loop.
9899653d4eSeschrock 	 */
9999653d4eSeschrock 	for (; dd != NULL; dd = dd->dd_parent) {
10099653d4eSeschrock 		ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
101*92241e0bSTom Erickson 
102*92241e0bSTom Erickson 		if (dd != target || snapshot) {
103*92241e0bSTom Erickson 			if (!inheritable)
104*92241e0bSTom Erickson 				break;
105*92241e0bSTom Erickson 			inheriting = B_TRUE;
106*92241e0bSTom Erickson 		}
107*92241e0bSTom Erickson 
108*92241e0bSTom Erickson 		/* Check for a local value. */
109*92241e0bSTom Erickson 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
110*92241e0bSTom Erickson 		    intsz, numints, buf);
111fa9e4066Sahrens 		if (err != ENOENT) {
112*92241e0bSTom Erickson 			if (setpoint != NULL && err == 0)
113fa9e4066Sahrens 				dsl_dir_name(dd, setpoint);
114fa9e4066Sahrens 			break;
115fa9e4066Sahrens 		}
116e9dbad6fSeschrock 
117e9dbad6fSeschrock 		/*
118*92241e0bSTom Erickson 		 * Skip the check for a received value if there is an explicit
119*92241e0bSTom Erickson 		 * inheritance entry.
120e9dbad6fSeschrock 		 */
121*92241e0bSTom Erickson 		err = zap_contains(mos, dd->dd_phys->dd_props_zapobj,
122*92241e0bSTom Erickson 		    inheritstr);
123*92241e0bSTom Erickson 		if (err != 0 && err != ENOENT)
124e9dbad6fSeschrock 			break;
125*92241e0bSTom Erickson 
126*92241e0bSTom Erickson 		if (err == ENOENT) {
127*92241e0bSTom Erickson 			/* Check for a received value. */
128*92241e0bSTom Erickson 			err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
129*92241e0bSTom Erickson 			    recvdstr, intsz, numints, buf);
130*92241e0bSTom Erickson 			if (err != ENOENT) {
131*92241e0bSTom Erickson 				if (setpoint != NULL && err == 0) {
132*92241e0bSTom Erickson 					if (inheriting) {
133*92241e0bSTom Erickson 						dsl_dir_name(dd, setpoint);
134*92241e0bSTom Erickson 					} else {
135*92241e0bSTom Erickson 						(void) strcpy(setpoint,
136*92241e0bSTom Erickson 						    ZPROP_SOURCE_VAL_RECVD);
137*92241e0bSTom Erickson 					}
138*92241e0bSTom Erickson 				}
139*92241e0bSTom Erickson 				break;
140*92241e0bSTom Erickson 			}
141*92241e0bSTom Erickson 		}
142*92241e0bSTom Erickson 
143*92241e0bSTom Erickson 		/*
144*92241e0bSTom Erickson 		 * If we found an explicit inheritance entry, err is zero even
145*92241e0bSTom Erickson 		 * though we haven't yet found the value, so reinitializing err
146*92241e0bSTom Erickson 		 * at the end of the loop (instead of at the beginning) ensures
147*92241e0bSTom Erickson 		 * that err has a valid post-loop value.
148*92241e0bSTom Erickson 		 */
149*92241e0bSTom Erickson 		err = ENOENT;
150fa9e4066Sahrens 	}
151*92241e0bSTom Erickson 
152fa9e4066Sahrens 	if (err == ENOENT)
153*92241e0bSTom Erickson 		err = dodefault(propname, intsz, numints, buf);
154*92241e0bSTom Erickson 
155*92241e0bSTom Erickson 	strfree(inheritstr);
156*92241e0bSTom Erickson 	strfree(recvdstr);
157fa9e4066Sahrens 
158fa9e4066Sahrens 	return (err);
159fa9e4066Sahrens }
160fa9e4066Sahrens 
161bb0ade09Sahrens int
162bb0ade09Sahrens dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
163*92241e0bSTom Erickson     int intsz, int numints, void *buf, char *setpoint)
164bb0ade09Sahrens {
165*92241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
166*92241e0bSTom Erickson 	boolean_t inheritable;
167*92241e0bSTom Erickson 	boolean_t snapshot;
168*92241e0bSTom Erickson 	uint64_t zapobj;
169*92241e0bSTom Erickson 
170bb0ade09Sahrens 	ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
171*92241e0bSTom Erickson 	inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
172*92241e0bSTom Erickson 	snapshot = (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds));
173*92241e0bSTom Erickson 	zapobj = (ds->ds_phys == NULL ? 0 : ds->ds_phys->ds_props_obj);
174*92241e0bSTom Erickson 
175*92241e0bSTom Erickson 	if (zapobj != 0) {
176*92241e0bSTom Erickson 		objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
177*92241e0bSTom Erickson 		int err;
178bb0ade09Sahrens 
179*92241e0bSTom Erickson 		ASSERT(snapshot);
180*92241e0bSTom Erickson 
181*92241e0bSTom Erickson 		/* Check for a local value. */
182*92241e0bSTom Erickson 		err = zap_lookup(mos, zapobj, propname, intsz, numints, buf);
183bb0ade09Sahrens 		if (err != ENOENT) {
184*92241e0bSTom Erickson 			if (setpoint != NULL && err == 0)
185bb0ade09Sahrens 				dsl_dataset_name(ds, setpoint);
186bb0ade09Sahrens 			return (err);
187bb0ade09Sahrens 		}
188*92241e0bSTom Erickson 
189*92241e0bSTom Erickson 		/*
190*92241e0bSTom Erickson 		 * Skip the check for a received value if there is an explicit
191*92241e0bSTom Erickson 		 * inheritance entry.
192*92241e0bSTom Erickson 		 */
193*92241e0bSTom Erickson 		if (inheritable) {
194*92241e0bSTom Erickson 			char *inheritstr = kmem_asprintf("%s%s", propname,
195*92241e0bSTom Erickson 			    ZPROP_INHERIT_SUFFIX);
196*92241e0bSTom Erickson 			err = zap_contains(mos, zapobj, inheritstr);
197*92241e0bSTom Erickson 			strfree(inheritstr);
198*92241e0bSTom Erickson 			if (err != 0 && err != ENOENT)
199*92241e0bSTom Erickson 				return (err);
200*92241e0bSTom Erickson 		}
201*92241e0bSTom Erickson 
202*92241e0bSTom Erickson 		if (err == ENOENT) {
203*92241e0bSTom Erickson 			/* Check for a received value. */
204*92241e0bSTom Erickson 			char *recvdstr = kmem_asprintf("%s%s", propname,
205*92241e0bSTom Erickson 			    ZPROP_RECVD_SUFFIX);
206*92241e0bSTom Erickson 			err = zap_lookup(mos, zapobj, recvdstr,
207*92241e0bSTom Erickson 			    intsz, numints, buf);
208*92241e0bSTom Erickson 			strfree(recvdstr);
209*92241e0bSTom Erickson 			if (err != ENOENT) {
210*92241e0bSTom Erickson 				if (setpoint != NULL && err == 0)
211*92241e0bSTom Erickson 					(void) strcpy(setpoint,
212*92241e0bSTom Erickson 					    ZPROP_SOURCE_VAL_RECVD);
213*92241e0bSTom Erickson 				return (err);
214*92241e0bSTom Erickson 			}
215*92241e0bSTom Erickson 		}
216bb0ade09Sahrens 	}
217bb0ade09Sahrens 
218bb0ade09Sahrens 	return (dsl_prop_get_dd(ds->ds_dir, propname,
219*92241e0bSTom Erickson 	    intsz, numints, buf, setpoint, snapshot));
220bb0ade09Sahrens }
221bb0ade09Sahrens 
222fa9e4066Sahrens /*
223fa9e4066Sahrens  * Register interest in the named property.  We'll call the callback
224fa9e4066Sahrens  * once to notify it of the current property value, and again each time
225fa9e4066Sahrens  * the property changes, until this callback is unregistered.
226fa9e4066Sahrens  *
227fa9e4066Sahrens  * Return 0 on success, errno if the prop is not an integer value.
228fa9e4066Sahrens  */
229fa9e4066Sahrens int
230fa9e4066Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname,
231fa9e4066Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
232fa9e4066Sahrens {
23399653d4eSeschrock 	dsl_dir_t *dd = ds->ds_dir;
234bb0ade09Sahrens 	dsl_pool_t *dp = dd->dd_pool;
235fa9e4066Sahrens 	uint64_t value;
236fa9e4066Sahrens 	dsl_prop_cb_record_t *cbr;
237fa9e4066Sahrens 	int err;
2381d452cf5Sahrens 	int need_rwlock;
239fa9e4066Sahrens 
240bb0ade09Sahrens 	need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
2411d452cf5Sahrens 	if (need_rwlock)
242bb0ade09Sahrens 		rw_enter(&dp->dp_config_rwlock, RW_READER);
243fa9e4066Sahrens 
244bb0ade09Sahrens 	err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
245fa9e4066Sahrens 	if (err != 0) {
24627345066Sck 		if (need_rwlock)
247bb0ade09Sahrens 			rw_exit(&dp->dp_config_rwlock);
248fa9e4066Sahrens 		return (err);
249fa9e4066Sahrens 	}
250fa9e4066Sahrens 
251fa9e4066Sahrens 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
25299653d4eSeschrock 	cbr->cbr_ds = ds;
253fa9e4066Sahrens 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
254fa9e4066Sahrens 	(void) strcpy((char *)cbr->cbr_propname, propname);
255fa9e4066Sahrens 	cbr->cbr_func = callback;
256fa9e4066Sahrens 	cbr->cbr_arg = cbarg;
257fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
258fa9e4066Sahrens 	list_insert_head(&dd->dd_prop_cbs, cbr);
259fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
260fa9e4066Sahrens 
261fa9e4066Sahrens 	cbr->cbr_func(cbr->cbr_arg, value);
262fa9e4066Sahrens 
263bb0ade09Sahrens 	VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object,
264ea8dc4b6Seschrock 	    NULL, cbr, &dd));
2651d452cf5Sahrens 	if (need_rwlock)
266bb0ade09Sahrens 		rw_exit(&dp->dp_config_rwlock);
267bb0ade09Sahrens 	/* Leave dir open until this callback is unregistered */
268fa9e4066Sahrens 	return (0);
269fa9e4066Sahrens }
270fa9e4066Sahrens 
271fa9e4066Sahrens int
272bb0ade09Sahrens dsl_prop_get(const char *dsname, const char *propname,
273fa9e4066Sahrens     int intsz, int numints, void *buf, char *setpoint)
274fa9e4066Sahrens {
275bb0ade09Sahrens 	dsl_dataset_t *ds;
276fa9e4066Sahrens 	int err;
277fa9e4066Sahrens 
278bb0ade09Sahrens 	err = dsl_dataset_hold(dsname, FTAG, &ds);
279ea8dc4b6Seschrock 	if (err)
280ea8dc4b6Seschrock 		return (err);
281fa9e4066Sahrens 
282bb0ade09Sahrens 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
283bb0ade09Sahrens 	err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
284bb0ade09Sahrens 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
285fa9e4066Sahrens 
286bb0ade09Sahrens 	dsl_dataset_rele(ds, FTAG);
287fa9e4066Sahrens 	return (err);
288fa9e4066Sahrens }
289fa9e4066Sahrens 
290fa9e4066Sahrens /*
291fa9e4066Sahrens  * Get the current property value.  It may have changed by the time this
292fa9e4066Sahrens  * function returns, so it is NOT safe to follow up with
293fa9e4066Sahrens  * dsl_prop_register() and assume that the value has not changed in
294fa9e4066Sahrens  * between.
295fa9e4066Sahrens  *
296fa9e4066Sahrens  * Return 0 on success, ENOENT if ddname is invalid.
297fa9e4066Sahrens  */
298fa9e4066Sahrens int
299fa9e4066Sahrens dsl_prop_get_integer(const char *ddname, const char *propname,
300fa9e4066Sahrens     uint64_t *valuep, char *setpoint)
301fa9e4066Sahrens {
302fa9e4066Sahrens 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
303fa9e4066Sahrens }
304fa9e4066Sahrens 
305*92241e0bSTom Erickson void
306*92241e0bSTom Erickson dsl_prop_setarg_init_uint64(dsl_prop_setarg_t *psa, const char *propname,
307*92241e0bSTom Erickson     zprop_source_t source, uint64_t *value)
308*92241e0bSTom Erickson {
309*92241e0bSTom Erickson 	psa->psa_name = propname;
310*92241e0bSTom Erickson 	psa->psa_source = source;
311*92241e0bSTom Erickson 	psa->psa_intsz = 8;
312*92241e0bSTom Erickson 	psa->psa_numints = 1;
313*92241e0bSTom Erickson 	psa->psa_value = value;
314*92241e0bSTom Erickson 
315*92241e0bSTom Erickson 	psa->psa_effective_value = -1ULL;
316*92241e0bSTom Erickson }
317*92241e0bSTom Erickson 
318*92241e0bSTom Erickson /*
319*92241e0bSTom Erickson  * Predict the effective value of the given special property if it were set with
320*92241e0bSTom Erickson  * the given value and source. This is not a general purpose function. It exists
321*92241e0bSTom Erickson  * only to handle the special requirements of the quota and reservation
322*92241e0bSTom Erickson  * properties. The fact that these properties are non-inheritable greatly
323*92241e0bSTom Erickson  * simplifies the prediction logic.
324*92241e0bSTom Erickson  *
325*92241e0bSTom Erickson  * Returns 0 on success, a positive error code on failure, or -1 if called with
326*92241e0bSTom Erickson  * a property not handled by this function.
327*92241e0bSTom Erickson  */
328*92241e0bSTom Erickson int
329*92241e0bSTom Erickson dsl_prop_predict_sync(dsl_dir_t *dd, dsl_prop_setarg_t *psa)
330*92241e0bSTom Erickson {
331*92241e0bSTom Erickson 	const char *propname = psa->psa_name;
332*92241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
333*92241e0bSTom Erickson 	zprop_source_t source = psa->psa_source;
334*92241e0bSTom Erickson 	objset_t *mos;
335*92241e0bSTom Erickson 	uint64_t zapobj;
336*92241e0bSTom Erickson 	uint64_t version;
337*92241e0bSTom Erickson 	char *recvdstr;
338*92241e0bSTom Erickson 	int err = 0;
339*92241e0bSTom Erickson 
340*92241e0bSTom Erickson 	switch (prop) {
341*92241e0bSTom Erickson 	case ZFS_PROP_QUOTA:
342*92241e0bSTom Erickson 	case ZFS_PROP_RESERVATION:
343*92241e0bSTom Erickson 	case ZFS_PROP_REFQUOTA:
344*92241e0bSTom Erickson 	case ZFS_PROP_REFRESERVATION:
345*92241e0bSTom Erickson 		break;
346*92241e0bSTom Erickson 	default:
347*92241e0bSTom Erickson 		return (-1);
348*92241e0bSTom Erickson 	}
349*92241e0bSTom Erickson 
350*92241e0bSTom Erickson 	mos = dd->dd_pool->dp_meta_objset;
351*92241e0bSTom Erickson 	zapobj = dd->dd_phys->dd_props_zapobj;
352*92241e0bSTom Erickson 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
353*92241e0bSTom Erickson 
354*92241e0bSTom Erickson 	version = spa_version(dd->dd_pool->dp_spa);
355*92241e0bSTom Erickson 	if (version < SPA_VERSION_RECVD_PROPS) {
356*92241e0bSTom Erickson 		if (source & ZPROP_SRC_NONE)
357*92241e0bSTom Erickson 			source = ZPROP_SRC_NONE;
358*92241e0bSTom Erickson 		else if (source & ZPROP_SRC_RECEIVED)
359*92241e0bSTom Erickson 			source = ZPROP_SRC_LOCAL;
360*92241e0bSTom Erickson 	}
361*92241e0bSTom Erickson 
362*92241e0bSTom Erickson 	switch (source) {
363*92241e0bSTom Erickson 	case ZPROP_SRC_NONE:
364*92241e0bSTom Erickson 		/* Revert to the received value, if any. */
365*92241e0bSTom Erickson 		err = zap_lookup(mos, zapobj, recvdstr, 8, 1,
366*92241e0bSTom Erickson 		    &psa->psa_effective_value);
367*92241e0bSTom Erickson 		if (err == ENOENT)
368*92241e0bSTom Erickson 			psa->psa_effective_value = 0;
369*92241e0bSTom Erickson 		break;
370*92241e0bSTom Erickson 	case ZPROP_SRC_LOCAL:
371*92241e0bSTom Erickson 		psa->psa_effective_value = *(uint64_t *)psa->psa_value;
372*92241e0bSTom Erickson 		break;
373*92241e0bSTom Erickson 	case ZPROP_SRC_RECEIVED:
374*92241e0bSTom Erickson 		/*
375*92241e0bSTom Erickson 		 * If there's no local setting, then the new received value will
376*92241e0bSTom Erickson 		 * be the effective value.
377*92241e0bSTom Erickson 		 */
378*92241e0bSTom Erickson 		err = zap_lookup(mos, zapobj, propname, 8, 1,
379*92241e0bSTom Erickson 		    &psa->psa_effective_value);
380*92241e0bSTom Erickson 		if (err == ENOENT)
381*92241e0bSTom Erickson 			psa->psa_effective_value = *(uint64_t *)psa->psa_value;
382*92241e0bSTom Erickson 		break;
383*92241e0bSTom Erickson 	case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):
384*92241e0bSTom Erickson 		/*
385*92241e0bSTom Erickson 		 * We're clearing the received value, so the local setting (if
386*92241e0bSTom Erickson 		 * it exists) remains the effective value.
387*92241e0bSTom Erickson 		 */
388*92241e0bSTom Erickson 		err = zap_lookup(mos, zapobj, propname, 8, 1,
389*92241e0bSTom Erickson 		    &psa->psa_effective_value);
390*92241e0bSTom Erickson 		if (err == ENOENT)
391*92241e0bSTom Erickson 			psa->psa_effective_value = 0;
392*92241e0bSTom Erickson 		break;
393*92241e0bSTom Erickson 	default:
394*92241e0bSTom Erickson 		cmn_err(CE_PANIC, "unexpected property source: %d", source);
395*92241e0bSTom Erickson 	}
396*92241e0bSTom Erickson 
397*92241e0bSTom Erickson 	strfree(recvdstr);
398*92241e0bSTom Erickson 
399*92241e0bSTom Erickson 	if (err == ENOENT)
400*92241e0bSTom Erickson 		return (0);
401*92241e0bSTom Erickson 
402*92241e0bSTom Erickson 	return (err);
403*92241e0bSTom Erickson }
404*92241e0bSTom Erickson 
405*92241e0bSTom Erickson #ifdef	ZFS_DEBUG
406*92241e0bSTom Erickson void
407*92241e0bSTom Erickson dsl_prop_check_prediction(dsl_dir_t *dd, dsl_prop_setarg_t *psa)
408*92241e0bSTom Erickson {
409*92241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(psa->psa_name);
410*92241e0bSTom Erickson 	uint64_t intval;
411*92241e0bSTom Erickson 	char setpoint[MAXNAMELEN];
412*92241e0bSTom Erickson 	uint64_t version = spa_version(dd->dd_pool->dp_spa);
413*92241e0bSTom Erickson 	int err;
414*92241e0bSTom Erickson 
415*92241e0bSTom Erickson 	if (version < SPA_VERSION_RECVD_PROPS) {
416*92241e0bSTom Erickson 		switch (prop) {
417*92241e0bSTom Erickson 		case ZFS_PROP_QUOTA:
418*92241e0bSTom Erickson 		case ZFS_PROP_RESERVATION:
419*92241e0bSTom Erickson 			return;
420*92241e0bSTom Erickson 		}
421*92241e0bSTom Erickson 	}
422*92241e0bSTom Erickson 
423*92241e0bSTom Erickson 	err = dsl_prop_get_dd(dd, psa->psa_name, 8, 1, &intval,
424*92241e0bSTom Erickson 	    setpoint, B_FALSE);
425*92241e0bSTom Erickson 	if (err == 0 && intval != psa->psa_effective_value) {
426*92241e0bSTom Erickson 		cmn_err(CE_PANIC, "%s property, source: %x, "
427*92241e0bSTom Erickson 		    "predicted effective value: %llu, "
428*92241e0bSTom Erickson 		    "actual effective value: %llu (setpoint: %s)",
429*92241e0bSTom Erickson 		    psa->psa_name, psa->psa_source,
430*92241e0bSTom Erickson 		    (unsigned long long)psa->psa_effective_value,
431*92241e0bSTom Erickson 		    (unsigned long long)intval, setpoint);
432*92241e0bSTom Erickson 	}
433*92241e0bSTom Erickson }
434*92241e0bSTom Erickson #endif
435*92241e0bSTom Erickson 
436fa9e4066Sahrens /*
437fa9e4066Sahrens  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
438fa9e4066Sahrens  * invalid, ENOMSG if no matching callback registered.
439fa9e4066Sahrens  */
440fa9e4066Sahrens int
441fa9e4066Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
442fa9e4066Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
443fa9e4066Sahrens {
44499653d4eSeschrock 	dsl_dir_t *dd = ds->ds_dir;
445fa9e4066Sahrens 	dsl_prop_cb_record_t *cbr;
446fa9e4066Sahrens 
447fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
448fa9e4066Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
449fa9e4066Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
45099653d4eSeschrock 		if (cbr->cbr_ds == ds &&
451fa9e4066Sahrens 		    cbr->cbr_func == callback &&
45299653d4eSeschrock 		    cbr->cbr_arg == cbarg &&
45399653d4eSeschrock 		    strcmp(cbr->cbr_propname, propname) == 0)
454fa9e4066Sahrens 			break;
455fa9e4066Sahrens 	}
456fa9e4066Sahrens 
457fa9e4066Sahrens 	if (cbr == NULL) {
458fa9e4066Sahrens 		mutex_exit(&dd->dd_lock);
459fa9e4066Sahrens 		return (ENOMSG);
460fa9e4066Sahrens 	}
461fa9e4066Sahrens 
462fa9e4066Sahrens 	list_remove(&dd->dd_prop_cbs, cbr);
463fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
464fa9e4066Sahrens 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
465fa9e4066Sahrens 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
466fa9e4066Sahrens 
467fa9e4066Sahrens 	/* Clean up from dsl_prop_register */
468fa9e4066Sahrens 	dsl_dir_close(dd, cbr);
469fa9e4066Sahrens 	return (0);
470fa9e4066Sahrens }
471fa9e4066Sahrens 
47299653d4eSeschrock /*
47399653d4eSeschrock  * Return the number of callbacks that are registered for this dataset.
47499653d4eSeschrock  */
47599653d4eSeschrock int
47699653d4eSeschrock dsl_prop_numcb(dsl_dataset_t *ds)
47799653d4eSeschrock {
47899653d4eSeschrock 	dsl_dir_t *dd = ds->ds_dir;
47999653d4eSeschrock 	dsl_prop_cb_record_t *cbr;
48099653d4eSeschrock 	int num = 0;
48199653d4eSeschrock 
48299653d4eSeschrock 	mutex_enter(&dd->dd_lock);
48399653d4eSeschrock 	for (cbr = list_head(&dd->dd_prop_cbs);
48499653d4eSeschrock 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
48599653d4eSeschrock 		if (cbr->cbr_ds == ds)
48699653d4eSeschrock 			num++;
48799653d4eSeschrock 	}
48899653d4eSeschrock 	mutex_exit(&dd->dd_lock);
48999653d4eSeschrock 
49099653d4eSeschrock 	return (num);
49199653d4eSeschrock }
49299653d4eSeschrock 
493fa9e4066Sahrens static void
494fa9e4066Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
495fa9e4066Sahrens     const char *propname, uint64_t value, int first)
496fa9e4066Sahrens {
497fa9e4066Sahrens 	dsl_dir_t *dd;
498fa9e4066Sahrens 	dsl_prop_cb_record_t *cbr;
499fa9e4066Sahrens 	objset_t *mos = dp->dp_meta_objset;
5001d452cf5Sahrens 	zap_cursor_t zc;
501d8d77200Sahrens 	zap_attribute_t *za;
502fa9e4066Sahrens 	int err;
503fa9e4066Sahrens 
504fa9e4066Sahrens 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
505ea8dc4b6Seschrock 	err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
506ea8dc4b6Seschrock 	if (err)
507ea8dc4b6Seschrock 		return;
508fa9e4066Sahrens 
509fa9e4066Sahrens 	if (!first) {
510fa9e4066Sahrens 		/*
511fa9e4066Sahrens 		 * If the prop is set here, then this change is not
512fa9e4066Sahrens 		 * being inherited here or below; stop the recursion.
513fa9e4066Sahrens 		 */
514*92241e0bSTom Erickson 		err = zap_contains(mos, dd->dd_phys->dd_props_zapobj, propname);
515fa9e4066Sahrens 		if (err == 0) {
516fa9e4066Sahrens 			dsl_dir_close(dd, FTAG);
517fa9e4066Sahrens 			return;
518fa9e4066Sahrens 		}
519fa9e4066Sahrens 		ASSERT3U(err, ==, ENOENT);
520fa9e4066Sahrens 	}
521fa9e4066Sahrens 
522fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
523bb0ade09Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs); cbr;
524bb0ade09Sahrens 	    cbr = list_next(&dd->dd_prop_cbs, cbr)) {
525bb0ade09Sahrens 		uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
526bb0ade09Sahrens 
527bb0ade09Sahrens 		if (strcmp(cbr->cbr_propname, propname) != 0)
528bb0ade09Sahrens 			continue;
529bb0ade09Sahrens 
530bb0ade09Sahrens 		/*
531bb0ade09Sahrens 		 * If the property is set on this ds, then it is not
532bb0ade09Sahrens 		 * inherited here; don't call the callback.
533bb0ade09Sahrens 		 */
534*92241e0bSTom Erickson 		if (propobj && 0 == zap_contains(mos, propobj, propname))
535bb0ade09Sahrens 			continue;
536bb0ade09Sahrens 
537bb0ade09Sahrens 		cbr->cbr_func(cbr->cbr_arg, value);
538fa9e4066Sahrens 	}
539fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
540fa9e4066Sahrens 
541d8d77200Sahrens 	za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
5421d452cf5Sahrens 	for (zap_cursor_init(&zc, mos,
5431d452cf5Sahrens 	    dd->dd_phys->dd_child_dir_zapobj);
544d8d77200Sahrens 	    zap_cursor_retrieve(&zc, za) == 0;
5451d452cf5Sahrens 	    zap_cursor_advance(&zc)) {
546d8d77200Sahrens 		dsl_prop_changed_notify(dp, za->za_first_integer,
5471d452cf5Sahrens 		    propname, value, FALSE);
548fa9e4066Sahrens 	}
549d8d77200Sahrens 	kmem_free(za, sizeof (zap_attribute_t));
5501d452cf5Sahrens 	zap_cursor_fini(&zc);
551fa9e4066Sahrens 	dsl_dir_close(dd, FTAG);
552fa9e4066Sahrens }
553fa9e4066Sahrens 
554*92241e0bSTom Erickson void
555ecd6cf80Smarks dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
556fa9e4066Sahrens {
557bb0ade09Sahrens 	dsl_dataset_t *ds = arg1;
558*92241e0bSTom Erickson 	dsl_prop_setarg_t *psa = arg2;
559bb0ade09Sahrens 	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
560*92241e0bSTom Erickson 	uint64_t zapobj, intval, dummy;
5611d452cf5Sahrens 	int isint;
562ecd6cf80Smarks 	char valbuf[32];
563*92241e0bSTom Erickson 	char *valstr = NULL;
564*92241e0bSTom Erickson 	char *inheritstr;
565*92241e0bSTom Erickson 	char *recvdstr;
566*92241e0bSTom Erickson 	char *tbuf = NULL;
567*92241e0bSTom Erickson 	int err;
568*92241e0bSTom Erickson 	uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa);
569*92241e0bSTom Erickson 	const char *propname = psa->psa_name;
570*92241e0bSTom Erickson 	zprop_source_t source = psa->psa_source;
571fa9e4066Sahrens 
572*92241e0bSTom Erickson 	isint = (dodefault(propname, 8, 1, &intval) == 0);
573fa9e4066Sahrens 
574*92241e0bSTom Erickson 	if (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds)) {
575*92241e0bSTom Erickson 		ASSERT(version >= SPA_VERSION_SNAP_PROPS);
576bb0ade09Sahrens 		if (ds->ds_phys->ds_props_obj == 0) {
577bb0ade09Sahrens 			dmu_buf_will_dirty(ds->ds_dbuf, tx);
578bb0ade09Sahrens 			ds->ds_phys->ds_props_obj =
579bb0ade09Sahrens 			    zap_create(mos,
580bb0ade09Sahrens 			    DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
581bb0ade09Sahrens 		}
582bb0ade09Sahrens 		zapobj = ds->ds_phys->ds_props_obj;
583bb0ade09Sahrens 	} else {
584bb0ade09Sahrens 		zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
585bb0ade09Sahrens 	}
586bb0ade09Sahrens 
587*92241e0bSTom Erickson 	if (version < SPA_VERSION_RECVD_PROPS) {
588*92241e0bSTom Erickson 		zfs_prop_t prop = zfs_name_to_prop(propname);
589*92241e0bSTom Erickson 		if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION)
590*92241e0bSTom Erickson 			return;
591*92241e0bSTom Erickson 
592*92241e0bSTom Erickson 		if (source & ZPROP_SRC_NONE)
593*92241e0bSTom Erickson 			source = ZPROP_SRC_NONE;
594*92241e0bSTom Erickson 		else if (source & ZPROP_SRC_RECEIVED)
595*92241e0bSTom Erickson 			source = ZPROP_SRC_LOCAL;
596*92241e0bSTom Erickson 	}
597*92241e0bSTom Erickson 
598*92241e0bSTom Erickson 	inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);
599*92241e0bSTom Erickson 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
600*92241e0bSTom Erickson 
601*92241e0bSTom Erickson 	switch (source) {
602*92241e0bSTom Erickson 	case ZPROP_SRC_NONE:
603*92241e0bSTom Erickson 		/*
604*92241e0bSTom Erickson 		 * revert to received value, if any (inherit -S)
605*92241e0bSTom Erickson 		 * - remove propname
606*92241e0bSTom Erickson 		 * - remove propname$inherit
607*92241e0bSTom Erickson 		 */
608*92241e0bSTom Erickson 		err = zap_remove(mos, zapobj, propname, tx);
609*92241e0bSTom Erickson 		ASSERT(err == 0 || err == ENOENT);
610*92241e0bSTom Erickson 		err = zap_remove(mos, zapobj, inheritstr, tx);
611*92241e0bSTom Erickson 		ASSERT(err == 0 || err == ENOENT);
612*92241e0bSTom Erickson 		break;
613*92241e0bSTom Erickson 	case ZPROP_SRC_LOCAL:
614*92241e0bSTom Erickson 		/*
615*92241e0bSTom Erickson 		 * remove propname$inherit
616*92241e0bSTom Erickson 		 * set propname -> value
617*92241e0bSTom Erickson 		 */
618*92241e0bSTom Erickson 		err = zap_remove(mos, zapobj, inheritstr, tx);
619*92241e0bSTom Erickson 		ASSERT(err == 0 || err == ENOENT);
620*92241e0bSTom Erickson 		VERIFY(0 == zap_update(mos, zapobj, propname,
621*92241e0bSTom Erickson 		    psa->psa_intsz, psa->psa_numints, psa->psa_value, tx));
622*92241e0bSTom Erickson 		break;
623*92241e0bSTom Erickson 	case ZPROP_SRC_INHERITED:
624*92241e0bSTom Erickson 		/*
625*92241e0bSTom Erickson 		 * explicitly inherit
626*92241e0bSTom Erickson 		 * - remove propname
627*92241e0bSTom Erickson 		 * - set propname$inherit
628*92241e0bSTom Erickson 		 */
629*92241e0bSTom Erickson 		err = zap_remove(mos, zapobj, propname, tx);
6301d452cf5Sahrens 		ASSERT(err == 0 || err == ENOENT);
631*92241e0bSTom Erickson 		if (version >= SPA_VERSION_RECVD_PROPS &&
632*92241e0bSTom Erickson 		    zap_contains(mos, zapobj, ZPROP_HAS_RECVD) == 0) {
633*92241e0bSTom Erickson 			dummy = 0;
634*92241e0bSTom Erickson 			err = zap_update(mos, zapobj, inheritstr,
635*92241e0bSTom Erickson 			    8, 1, &dummy, tx);
636*92241e0bSTom Erickson 			ASSERT(err == 0);
637fa9e4066Sahrens 		}
638*92241e0bSTom Erickson 		break;
639*92241e0bSTom Erickson 	case ZPROP_SRC_RECEIVED:
640*92241e0bSTom Erickson 		/*
641*92241e0bSTom Erickson 		 * set propname$recvd -> value
642*92241e0bSTom Erickson 		 */
643*92241e0bSTom Erickson 		err = zap_update(mos, zapobj, recvdstr,
644*92241e0bSTom Erickson 		    psa->psa_intsz, psa->psa_numints, psa->psa_value, tx);
645*92241e0bSTom Erickson 		ASSERT(err == 0);
646*92241e0bSTom Erickson 		break;
647*92241e0bSTom Erickson 	case (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED):
648*92241e0bSTom Erickson 		/*
649*92241e0bSTom Erickson 		 * clear local and received settings
650*92241e0bSTom Erickson 		 * - remove propname
651*92241e0bSTom Erickson 		 * - remove propname$inherit
652*92241e0bSTom Erickson 		 * - remove propname$recvd
653*92241e0bSTom Erickson 		 */
654*92241e0bSTom Erickson 		err = zap_remove(mos, zapobj, propname, tx);
655*92241e0bSTom Erickson 		ASSERT(err == 0 || err == ENOENT);
656*92241e0bSTom Erickson 		err = zap_remove(mos, zapobj, inheritstr, tx);
657*92241e0bSTom Erickson 		ASSERT(err == 0 || err == ENOENT);
658*92241e0bSTom Erickson 		/* FALLTHRU */
659*92241e0bSTom Erickson 	case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):
660*92241e0bSTom Erickson 		/*
661*92241e0bSTom Erickson 		 * remove propname$recvd
662*92241e0bSTom Erickson 		 */
663*92241e0bSTom Erickson 		err = zap_remove(mos, zapobj, recvdstr, tx);
664*92241e0bSTom Erickson 		ASSERT(err == 0 || err == ENOENT);
665*92241e0bSTom Erickson 		break;
666*92241e0bSTom Erickson 	default:
667*92241e0bSTom Erickson 		cmn_err(CE_PANIC, "unexpected property source: %d", source);
668fa9e4066Sahrens 	}
669fa9e4066Sahrens 
670*92241e0bSTom Erickson 	strfree(inheritstr);
671*92241e0bSTom Erickson 	strfree(recvdstr);
672*92241e0bSTom Erickson 
6731d452cf5Sahrens 	if (isint) {
674*92241e0bSTom Erickson 		VERIFY(0 == dsl_prop_get_ds(ds, propname, 8, 1, &intval, NULL));
675*92241e0bSTom Erickson 
676*92241e0bSTom Erickson 		if (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds)) {
677bb0ade09Sahrens 			dsl_prop_cb_record_t *cbr;
678bb0ade09Sahrens 			/*
679bb0ade09Sahrens 			 * It's a snapshot; nothing can inherit this
680bb0ade09Sahrens 			 * property, so just look for callbacks on this
681bb0ade09Sahrens 			 * ds here.
682bb0ade09Sahrens 			 */
683bb0ade09Sahrens 			mutex_enter(&ds->ds_dir->dd_lock);
684bb0ade09Sahrens 			for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
685bb0ade09Sahrens 			    cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
686bb0ade09Sahrens 				if (cbr->cbr_ds == ds &&
687*92241e0bSTom Erickson 				    strcmp(cbr->cbr_propname, propname) == 0)
688bb0ade09Sahrens 					cbr->cbr_func(cbr->cbr_arg, intval);
689bb0ade09Sahrens 			}
690bb0ade09Sahrens 			mutex_exit(&ds->ds_dir->dd_lock);
691bb0ade09Sahrens 		} else {
692bb0ade09Sahrens 			dsl_prop_changed_notify(ds->ds_dir->dd_pool,
693*92241e0bSTom Erickson 			    ds->ds_dir->dd_object, propname, intval, TRUE);
694bb0ade09Sahrens 		}
695*92241e0bSTom Erickson 
696ecd6cf80Smarks 		(void) snprintf(valbuf, sizeof (valbuf),
697ecd6cf80Smarks 		    "%lld", (longlong_t)intval);
698ecd6cf80Smarks 		valstr = valbuf;
699ecd6cf80Smarks 	} else {
700*92241e0bSTom Erickson 		if (source == ZPROP_SRC_LOCAL) {
701*92241e0bSTom Erickson 			valstr = (char *)psa->psa_value;
702*92241e0bSTom Erickson 		} else {
703*92241e0bSTom Erickson 			tbuf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP);
704*92241e0bSTom Erickson 			if (dsl_prop_get_ds(ds, propname, 1,
705*92241e0bSTom Erickson 			    ZAP_MAXVALUELEN, tbuf, NULL) == 0)
706*92241e0bSTom Erickson 				valstr = tbuf;
707*92241e0bSTom Erickson 		}
708ecd6cf80Smarks 	}
709*92241e0bSTom Erickson 
710*92241e0bSTom Erickson 	spa_history_internal_log((source == ZPROP_SRC_NONE ||
711*92241e0bSTom Erickson 	    source == ZPROP_SRC_INHERITED) ? LOG_DS_INHERIT :
712bb0ade09Sahrens 	    LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx, cr,
713*92241e0bSTom Erickson 	    "%s=%s dataset = %llu", propname,
714*92241e0bSTom Erickson 	    (valstr == NULL ? "" : valstr), ds->ds_object);
715*92241e0bSTom Erickson 
716*92241e0bSTom Erickson 	if (tbuf != NULL)
717*92241e0bSTom Erickson 		kmem_free(tbuf, ZAP_MAXVALUELEN);
718fa9e4066Sahrens }
719fa9e4066Sahrens 
720ea2f5b9eSMatthew Ahrens void
7215c0b6a79SRich Morris dsl_props_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
7225c0b6a79SRich Morris {
7235c0b6a79SRich Morris 	dsl_dataset_t *ds = arg1;
724*92241e0bSTom Erickson 	dsl_props_arg_t *pa = arg2;
725*92241e0bSTom Erickson 	nvlist_t *props = pa->pa_props;
726*92241e0bSTom Erickson 	dsl_prop_setarg_t psa;
7275c0b6a79SRich Morris 	nvpair_t *elem = NULL;
7285c0b6a79SRich Morris 
729*92241e0bSTom Erickson 	psa.psa_source = pa->pa_source;
7305c0b6a79SRich Morris 
731*92241e0bSTom Erickson 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
732*92241e0bSTom Erickson 		nvpair_t *pair = elem;
7335c0b6a79SRich Morris 
734*92241e0bSTom Erickson 		psa.psa_name = nvpair_name(pair);
735*92241e0bSTom Erickson 
736*92241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
737*92241e0bSTom Erickson 			/*
738*92241e0bSTom Erickson 			 * dsl_prop_get_all_impl() returns properties in this
739*92241e0bSTom Erickson 			 * format.
740*92241e0bSTom Erickson 			 */
741*92241e0bSTom Erickson 			nvlist_t *attrs;
742*92241e0bSTom Erickson 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
743*92241e0bSTom Erickson 			VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
744*92241e0bSTom Erickson 			    &pair) == 0);
745*92241e0bSTom Erickson 		}
746*92241e0bSTom Erickson 
747*92241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_STRING) {
748*92241e0bSTom Erickson 			VERIFY(nvpair_value_string(pair,
749*92241e0bSTom Erickson 			    (char **)&psa.psa_value) == 0);
750*92241e0bSTom Erickson 			psa.psa_intsz = 1;
751*92241e0bSTom Erickson 			psa.psa_numints = strlen(psa.psa_value) + 1;
7525c0b6a79SRich Morris 		} else {
7535c0b6a79SRich Morris 			uint64_t intval;
754*92241e0bSTom Erickson 			VERIFY(nvpair_value_uint64(pair, &intval) == 0);
755*92241e0bSTom Erickson 			psa.psa_intsz = sizeof (intval);
756*92241e0bSTom Erickson 			psa.psa_numints = 1;
757*92241e0bSTom Erickson 			psa.psa_value = &intval;
7585c0b6a79SRich Morris 		}
7595c0b6a79SRich Morris 		dsl_prop_set_sync(ds, &psa, cr, tx);
7605c0b6a79SRich Morris 	}
7615c0b6a79SRich Morris }
7625c0b6a79SRich Morris 
763a9799022Sck void
764842727c2SChris Kirby dsl_dir_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
765a9799022Sck     cred_t *cr, dmu_tx_t *tx)
766a9799022Sck {
767a9799022Sck 	objset_t *mos = dd->dd_pool->dp_meta_objset;
768a9799022Sck 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
769a9799022Sck 
770a9799022Sck 	ASSERT(dmu_tx_is_syncing(tx));
771a9799022Sck 
772a9799022Sck 	VERIFY(0 == zap_update(mos, zapobj, name, sizeof (val), 1, &val, tx));
773a9799022Sck 
774a9799022Sck 	dsl_prop_changed_notify(dd->dd_pool, dd->dd_object, name, val, TRUE);
775a9799022Sck 
776a9799022Sck 	spa_history_internal_log(LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr,
777a9799022Sck 	    "%s=%llu dataset = %llu", name, (u_longlong_t)val,
778a9799022Sck 	    dd->dd_phys->dd_head_dataset_obj);
779a9799022Sck }
780a9799022Sck 
781a2eea2e1Sahrens int
782*92241e0bSTom Erickson dsl_prop_set(const char *dsname, const char *propname, zprop_source_t source,
783fa9e4066Sahrens     int intsz, int numints, const void *buf)
784fa9e4066Sahrens {
785bb0ade09Sahrens 	dsl_dataset_t *ds;
786478ed9adSEric Taylor 	uint64_t version;
787fa9e4066Sahrens 	int err;
788*92241e0bSTom Erickson 	dsl_prop_setarg_t psa;
789fa9e4066Sahrens 
790455d5089Sahrens 	/*
791455d5089Sahrens 	 * We must do these checks before we get to the syncfunc, since
792455d5089Sahrens 	 * it can't fail.
793455d5089Sahrens 	 */
794455d5089Sahrens 	if (strlen(propname) >= ZAP_MAXNAMELEN)
795455d5089Sahrens 		return (ENAMETOOLONG);
796455d5089Sahrens 
797bb0ade09Sahrens 	err = dsl_dataset_hold(dsname, FTAG, &ds);
798ea8dc4b6Seschrock 	if (err)
799ea8dc4b6Seschrock 		return (err);
800bb0ade09Sahrens 
801478ed9adSEric Taylor 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
802478ed9adSEric Taylor 	if (intsz * numints >= (version < SPA_VERSION_STMF_PROP ?
803478ed9adSEric Taylor 	    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
804478ed9adSEric Taylor 		dsl_dataset_rele(ds, FTAG);
805478ed9adSEric Taylor 		return (E2BIG);
806478ed9adSEric Taylor 	}
807bb0ade09Sahrens 	if (dsl_dataset_is_snapshot(ds) &&
808478ed9adSEric Taylor 	    version < SPA_VERSION_SNAP_PROPS) {
809bb0ade09Sahrens 		dsl_dataset_rele(ds, FTAG);
810bb0ade09Sahrens 		return (ENOTSUP);
811bb0ade09Sahrens 	}
812bb0ade09Sahrens 
813*92241e0bSTom Erickson 	psa.psa_name = propname;
814*92241e0bSTom Erickson 	psa.psa_source = source;
815*92241e0bSTom Erickson 	psa.psa_intsz = intsz;
816*92241e0bSTom Erickson 	psa.psa_numints = numints;
817*92241e0bSTom Erickson 	psa.psa_value = buf;
818*92241e0bSTom Erickson 	psa.psa_effective_value = -1ULL;
819*92241e0bSTom Erickson 
820bb0ade09Sahrens 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
821bb0ade09Sahrens 	    NULL, dsl_prop_set_sync, ds, &psa, 2);
822bb0ade09Sahrens 
823bb0ade09Sahrens 	dsl_dataset_rele(ds, FTAG);
824fa9e4066Sahrens 	return (err);
825fa9e4066Sahrens }
8267f7322feSeschrock 
8275c0b6a79SRich Morris int
828*92241e0bSTom Erickson dsl_props_set(const char *dsname, zprop_source_t source, nvlist_t *props)
8295c0b6a79SRich Morris {
8305c0b6a79SRich Morris 	dsl_dataset_t *ds;
831478ed9adSEric Taylor 	uint64_t version;
8325c0b6a79SRich Morris 	nvpair_t *elem = NULL;
833*92241e0bSTom Erickson 	dsl_props_arg_t pa;
8345c0b6a79SRich Morris 	int err;
8355c0b6a79SRich Morris 
836478ed9adSEric Taylor 	if (err = dsl_dataset_hold(dsname, FTAG, &ds))
837478ed9adSEric Taylor 		return (err);
838ccba0801SRich Morris 	/*
839ccba0801SRich Morris 	 * Do these checks before the syncfunc, since it can't fail.
840ccba0801SRich Morris 	 */
841478ed9adSEric Taylor 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
842*92241e0bSTom Erickson 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
843478ed9adSEric Taylor 		if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
844478ed9adSEric Taylor 			dsl_dataset_rele(ds, FTAG);
845ccba0801SRich Morris 			return (ENAMETOOLONG);
846478ed9adSEric Taylor 		}
8475c0b6a79SRich Morris 		if (nvpair_type(elem) == DATA_TYPE_STRING) {
8485c0b6a79SRich Morris 			char *valstr;
8495c0b6a79SRich Morris 			VERIFY(nvpair_value_string(elem, &valstr) == 0);
850478ed9adSEric Taylor 			if (strlen(valstr) >= (version <
851478ed9adSEric Taylor 			    SPA_VERSION_STMF_PROP ?
852478ed9adSEric Taylor 			    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
853478ed9adSEric Taylor 				dsl_dataset_rele(ds, FTAG);
854ccba0801SRich Morris 				return (E2BIG);
855478ed9adSEric Taylor 			}
8565c0b6a79SRich Morris 		}
8575c0b6a79SRich Morris 	}
8585c0b6a79SRich Morris 
8595c0b6a79SRich Morris 	if (dsl_dataset_is_snapshot(ds) &&
860478ed9adSEric Taylor 	    version < SPA_VERSION_SNAP_PROPS) {
8615c0b6a79SRich Morris 		dsl_dataset_rele(ds, FTAG);
8625c0b6a79SRich Morris 		return (ENOTSUP);
8635c0b6a79SRich Morris 	}
8645c0b6a79SRich Morris 
865*92241e0bSTom Erickson 	pa.pa_props = props;
866*92241e0bSTom Erickson 	pa.pa_source = source;
867*92241e0bSTom Erickson 
8685c0b6a79SRich Morris 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
869*92241e0bSTom Erickson 	    NULL, dsl_props_set_sync, ds, &pa, 2);
8705c0b6a79SRich Morris 
8715c0b6a79SRich Morris 	dsl_dataset_rele(ds, FTAG);
8725c0b6a79SRich Morris 	return (err);
8735c0b6a79SRich Morris }
8745c0b6a79SRich Morris 
875*92241e0bSTom Erickson typedef enum dsl_prop_getflags {
876*92241e0bSTom Erickson 	DSL_PROP_GET_INHERITING = 0x1,	/* searching parent of target ds */
877*92241e0bSTom Erickson 	DSL_PROP_GET_SNAPSHOT = 0x2,	/* snapshot dataset */
878*92241e0bSTom Erickson 	DSL_PROP_GET_LOCAL = 0x4,	/* local properties */
879*92241e0bSTom Erickson 	DSL_PROP_GET_RECEIVED = 0x8	/* received properties */
880*92241e0bSTom Erickson } dsl_prop_getflags_t;
881*92241e0bSTom Erickson 
882*92241e0bSTom Erickson static int
883*92241e0bSTom Erickson dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj,
884*92241e0bSTom Erickson     const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv)
885*92241e0bSTom Erickson {
886*92241e0bSTom Erickson 	zap_cursor_t zc;
887*92241e0bSTom Erickson 	zap_attribute_t za;
888*92241e0bSTom Erickson 	int err = 0;
889*92241e0bSTom Erickson 
890*92241e0bSTom Erickson 	for (zap_cursor_init(&zc, mos, propobj);
891*92241e0bSTom Erickson 	    (err = zap_cursor_retrieve(&zc, &za)) == 0;
892*92241e0bSTom Erickson 	    zap_cursor_advance(&zc)) {
893*92241e0bSTom Erickson 		nvlist_t *propval;
894*92241e0bSTom Erickson 		zfs_prop_t prop;
895*92241e0bSTom Erickson 		char buf[ZAP_MAXNAMELEN];
896*92241e0bSTom Erickson 		char *valstr;
897*92241e0bSTom Erickson 		const char *suffix;
898*92241e0bSTom Erickson 		const char *propname;
899*92241e0bSTom Erickson 		const char *source;
900*92241e0bSTom Erickson 
901*92241e0bSTom Erickson 		suffix = strchr(za.za_name, '$');
902*92241e0bSTom Erickson 
903*92241e0bSTom Erickson 		if (suffix == NULL) {
904*92241e0bSTom Erickson 			/*
905*92241e0bSTom Erickson 			 * Skip local properties if we only want received
906*92241e0bSTom Erickson 			 * properties.
907*92241e0bSTom Erickson 			 */
908*92241e0bSTom Erickson 			if (flags & DSL_PROP_GET_RECEIVED)
909*92241e0bSTom Erickson 				continue;
910*92241e0bSTom Erickson 
911*92241e0bSTom Erickson 			propname = za.za_name;
912*92241e0bSTom Erickson 			source = setpoint;
913*92241e0bSTom Erickson 		} else if (strcmp(suffix, ZPROP_INHERIT_SUFFIX) == 0) {
914*92241e0bSTom Erickson 			/* Skip explicitly inherited entries. */
915*92241e0bSTom Erickson 			continue;
916*92241e0bSTom Erickson 		} else if (strcmp(suffix, ZPROP_RECVD_SUFFIX) == 0) {
917*92241e0bSTom Erickson 			if (flags & DSL_PROP_GET_LOCAL)
918*92241e0bSTom Erickson 				continue;
919*92241e0bSTom Erickson 
920*92241e0bSTom Erickson 			(void) strncpy(buf, za.za_name, (suffix - za.za_name));
921*92241e0bSTom Erickson 			buf[suffix - za.za_name] = '\0';
922*92241e0bSTom Erickson 			propname = buf;
923*92241e0bSTom Erickson 
924*92241e0bSTom Erickson 			if (!(flags & DSL_PROP_GET_RECEIVED)) {
925*92241e0bSTom Erickson 				/* Skip if locally overridden. */
926*92241e0bSTom Erickson 				err = zap_contains(mos, propobj, propname);
927*92241e0bSTom Erickson 				if (err == 0)
928*92241e0bSTom Erickson 					continue;
929*92241e0bSTom Erickson 				if (err != ENOENT)
930*92241e0bSTom Erickson 					break;
931*92241e0bSTom Erickson 
932*92241e0bSTom Erickson 				/* Skip if explicitly inherited. */
933*92241e0bSTom Erickson 				valstr = kmem_asprintf("%s%s", propname,
934*92241e0bSTom Erickson 				    ZPROP_INHERIT_SUFFIX);
935*92241e0bSTom Erickson 				err = zap_contains(mos, propobj, valstr);
936*92241e0bSTom Erickson 				strfree(valstr);
937*92241e0bSTom Erickson 				if (err == 0)
938*92241e0bSTom Erickson 					continue;
939*92241e0bSTom Erickson 				if (err != ENOENT)
940*92241e0bSTom Erickson 					break;
941*92241e0bSTom Erickson 			}
942*92241e0bSTom Erickson 
943*92241e0bSTom Erickson 			source = ((flags & DSL_PROP_GET_INHERITING) ?
944*92241e0bSTom Erickson 			    setpoint : ZPROP_SOURCE_VAL_RECVD);
945*92241e0bSTom Erickson 		} else {
946*92241e0bSTom Erickson 			/*
947*92241e0bSTom Erickson 			 * For backward compatibility, skip suffixes we don't
948*92241e0bSTom Erickson 			 * recognize.
949*92241e0bSTom Erickson 			 */
950*92241e0bSTom Erickson 			continue;
951*92241e0bSTom Erickson 		}
952*92241e0bSTom Erickson 
953*92241e0bSTom Erickson 		prop = zfs_name_to_prop(propname);
954*92241e0bSTom Erickson 
955*92241e0bSTom Erickson 		/* Skip non-inheritable properties. */
956*92241e0bSTom Erickson 		if ((flags & DSL_PROP_GET_INHERITING) && prop != ZPROP_INVAL &&
957*92241e0bSTom Erickson 		    !zfs_prop_inheritable(prop))
958*92241e0bSTom Erickson 			continue;
959*92241e0bSTom Erickson 
960*92241e0bSTom Erickson 		/* Skip properties not valid for this type. */
961*92241e0bSTom Erickson 		if ((flags & DSL_PROP_GET_SNAPSHOT) && prop != ZPROP_INVAL &&
962*92241e0bSTom Erickson 		    !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
963*92241e0bSTom Erickson 			continue;
964*92241e0bSTom Erickson 
965*92241e0bSTom Erickson 		/* Skip properties already defined. */
966*92241e0bSTom Erickson 		if (nvlist_exists(nv, propname))
967*92241e0bSTom Erickson 			continue;
968*92241e0bSTom Erickson 
969*92241e0bSTom Erickson 		VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
970*92241e0bSTom Erickson 		if (za.za_integer_length == 1) {
971*92241e0bSTom Erickson 			/*
972*92241e0bSTom Erickson 			 * String property
973*92241e0bSTom Erickson 			 */
974*92241e0bSTom Erickson 			char *tmp = kmem_alloc(za.za_num_integers,
975*92241e0bSTom Erickson 			    KM_SLEEP);
976*92241e0bSTom Erickson 			err = zap_lookup(mos, propobj,
977*92241e0bSTom Erickson 			    za.za_name, 1, za.za_num_integers, tmp);
978*92241e0bSTom Erickson 			if (err != 0) {
979*92241e0bSTom Erickson 				kmem_free(tmp, za.za_num_integers);
980*92241e0bSTom Erickson 				break;
981*92241e0bSTom Erickson 			}
982*92241e0bSTom Erickson 			VERIFY(nvlist_add_string(propval, ZPROP_VALUE,
983*92241e0bSTom Erickson 			    tmp) == 0);
984*92241e0bSTom Erickson 			kmem_free(tmp, za.za_num_integers);
985*92241e0bSTom Erickson 		} else {
986*92241e0bSTom Erickson 			/*
987*92241e0bSTom Erickson 			 * Integer property
988*92241e0bSTom Erickson 			 */
989*92241e0bSTom Erickson 			ASSERT(za.za_integer_length == 8);
990*92241e0bSTom Erickson 			(void) nvlist_add_uint64(propval, ZPROP_VALUE,
991*92241e0bSTom Erickson 			    za.za_first_integer);
992*92241e0bSTom Erickson 		}
993*92241e0bSTom Erickson 
994*92241e0bSTom Erickson 		VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0);
995*92241e0bSTom Erickson 		VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
996*92241e0bSTom Erickson 		nvlist_free(propval);
997*92241e0bSTom Erickson 	}
998*92241e0bSTom Erickson 	zap_cursor_fini(&zc);
999*92241e0bSTom Erickson 	if (err == ENOENT)
1000*92241e0bSTom Erickson 		err = 0;
1001*92241e0bSTom Erickson 	return (err);
1002*92241e0bSTom Erickson }
1003*92241e0bSTom Erickson 
10047f7322feSeschrock /*
10057f7322feSeschrock  * Iterate over all properties for this dataset and return them in an nvlist.
10067f7322feSeschrock  */
1007*92241e0bSTom Erickson static int
1008*92241e0bSTom Erickson dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp,
1009*92241e0bSTom Erickson     dsl_prop_getflags_t flags)
10107f7322feSeschrock {
101199653d4eSeschrock 	dsl_dir_t *dd = ds->ds_dir;
1012bb0ade09Sahrens 	dsl_pool_t *dp = dd->dd_pool;
1013bb0ade09Sahrens 	objset_t *mos = dp->dp_meta_objset;
1014*92241e0bSTom Erickson 	int err = 0;
1015*92241e0bSTom Erickson 	char setpoint[MAXNAMELEN];
10167f7322feSeschrock 
10177f7322feSeschrock 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
10187f7322feSeschrock 
1019*92241e0bSTom Erickson 	if (dsl_dataset_is_snapshot(ds))
1020*92241e0bSTom Erickson 		flags |= DSL_PROP_GET_SNAPSHOT;
10217f7322feSeschrock 
10227f7322feSeschrock 	rw_enter(&dp->dp_config_rwlock, RW_READER);
1023*92241e0bSTom Erickson 
1024*92241e0bSTom Erickson 	if (ds->ds_phys->ds_props_obj != 0) {
1025*92241e0bSTom Erickson 		ASSERT(flags & DSL_PROP_GET_SNAPSHOT);
1026*92241e0bSTom Erickson 		dsl_dataset_name(ds, setpoint);
1027*92241e0bSTom Erickson 		err = dsl_prop_get_all_impl(mos, ds->ds_phys->ds_props_obj,
1028*92241e0bSTom Erickson 		    setpoint, flags, *nvp);
1029*92241e0bSTom Erickson 		if (err)
1030*92241e0bSTom Erickson 			goto out;
1031*92241e0bSTom Erickson 	}
1032*92241e0bSTom Erickson 
1033*92241e0bSTom Erickson 	for (; dd != NULL; dd = dd->dd_parent) {
1034*92241e0bSTom Erickson 		if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) {
1035*92241e0bSTom Erickson 			if (flags & (DSL_PROP_GET_LOCAL |
1036*92241e0bSTom Erickson 			    DSL_PROP_GET_RECEIVED))
1037*92241e0bSTom Erickson 				break;
1038*92241e0bSTom Erickson 			flags |= DSL_PROP_GET_INHERITING;
1039bb0ade09Sahrens 		}
1040*92241e0bSTom Erickson 		dsl_dir_name(dd, setpoint);
1041*92241e0bSTom Erickson 		err = dsl_prop_get_all_impl(mos, dd->dd_phys->dd_props_zapobj,
1042*92241e0bSTom Erickson 		    setpoint, flags, *nvp);
1043*92241e0bSTom Erickson 		if (err)
1044*92241e0bSTom Erickson 			break;
1045*92241e0bSTom Erickson 	}
1046*92241e0bSTom Erickson out:
1047*92241e0bSTom Erickson 	rw_exit(&dp->dp_config_rwlock);
1048*92241e0bSTom Erickson 	return (err);
1049*92241e0bSTom Erickson }
1050a2eea2e1Sahrens 
1051*92241e0bSTom Erickson boolean_t
1052*92241e0bSTom Erickson dsl_prop_get_hasrecvd(objset_t *os)
1053*92241e0bSTom Erickson {
1054*92241e0bSTom Erickson 	dsl_dataset_t *ds = os->os_dsl_dataset;
1055*92241e0bSTom Erickson 	int rc;
1056*92241e0bSTom Erickson 	uint64_t dummy;
1057bb0ade09Sahrens 
1058*92241e0bSTom Erickson 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
1059*92241e0bSTom Erickson 	rc = dsl_prop_get_ds(ds, ZPROP_HAS_RECVD, 8, 1, &dummy, NULL);
1060*92241e0bSTom Erickson 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
1061*92241e0bSTom Erickson 	ASSERT(rc != 0 || spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS);
1062*92241e0bSTom Erickson 	return (rc == 0);
1063*92241e0bSTom Erickson }
1064e9dbad6fSeschrock 
1065*92241e0bSTom Erickson static void
1066*92241e0bSTom Erickson dsl_prop_set_hasrecvd_impl(objset_t *os, zprop_source_t source)
1067*92241e0bSTom Erickson {
1068*92241e0bSTom Erickson 	dsl_dataset_t *ds = os->os_dsl_dataset;
1069*92241e0bSTom Erickson 	uint64_t dummy = 0;
1070*92241e0bSTom Erickson 	dsl_prop_setarg_t psa;
1071da6c28aaSamw 
1072*92241e0bSTom Erickson 	if (spa_version(os->os_spa) < SPA_VERSION_RECVD_PROPS)
1073*92241e0bSTom Erickson 		return;
10747f7322feSeschrock 
1075*92241e0bSTom Erickson 	dsl_prop_setarg_init_uint64(&psa, ZPROP_HAS_RECVD, source, &dummy);
10767f7322feSeschrock 
1077*92241e0bSTom Erickson 	(void) dsl_sync_task_do(ds->ds_dir->dd_pool, NULL,
1078*92241e0bSTom Erickson 	    dsl_prop_set_sync, ds, &psa, 2);
1079*92241e0bSTom Erickson }
10807f7322feSeschrock 
1081*92241e0bSTom Erickson /*
1082*92241e0bSTom Erickson  * Call after successfully receiving properties to ensure that only the first
1083*92241e0bSTom Erickson  * receive on or after SPA_VERSION_RECVD_PROPS blows away local properties.
1084*92241e0bSTom Erickson  */
1085*92241e0bSTom Erickson void
1086*92241e0bSTom Erickson dsl_prop_set_hasrecvd(objset_t *os)
1087*92241e0bSTom Erickson {
1088*92241e0bSTom Erickson 	if (dsl_prop_get_hasrecvd(os)) {
1089*92241e0bSTom Erickson 		ASSERT(spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS);
1090*92241e0bSTom Erickson 		return;
10917f7322feSeschrock 	}
1092*92241e0bSTom Erickson 	dsl_prop_set_hasrecvd_impl(os, ZPROP_SRC_LOCAL);
1093*92241e0bSTom Erickson }
10947f7322feSeschrock 
1095*92241e0bSTom Erickson void
1096*92241e0bSTom Erickson dsl_prop_unset_hasrecvd(objset_t *os)
1097*92241e0bSTom Erickson {
1098*92241e0bSTom Erickson 	dsl_prop_set_hasrecvd_impl(os, ZPROP_SRC_NONE);
1099*92241e0bSTom Erickson }
1100*92241e0bSTom Erickson 
1101*92241e0bSTom Erickson int
1102*92241e0bSTom Erickson dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
1103*92241e0bSTom Erickson {
1104*92241e0bSTom Erickson 	return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, 0));
1105*92241e0bSTom Erickson }
1106*92241e0bSTom Erickson 
1107*92241e0bSTom Erickson int
1108*92241e0bSTom Erickson dsl_prop_get_received(objset_t *os, nvlist_t **nvp)
1109*92241e0bSTom Erickson {
1110*92241e0bSTom Erickson 	/*
1111*92241e0bSTom Erickson 	 * Received properties are not distinguishable from local properties
1112*92241e0bSTom Erickson 	 * until the dataset has received properties on or after
1113*92241e0bSTom Erickson 	 * SPA_VERSION_RECVD_PROPS.
1114*92241e0bSTom Erickson 	 */
1115*92241e0bSTom Erickson 	dsl_prop_getflags_t flags = (dsl_prop_get_hasrecvd(os) ?
1116*92241e0bSTom Erickson 	    DSL_PROP_GET_RECEIVED : DSL_PROP_GET_LOCAL);
1117*92241e0bSTom Erickson 	return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, flags));
11187f7322feSeschrock }
1119a2eea2e1Sahrens 
1120a2eea2e1Sahrens void
1121a2eea2e1Sahrens dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)
1122a2eea2e1Sahrens {
1123a2eea2e1Sahrens 	nvlist_t *propval;
1124*92241e0bSTom Erickson 	const char *propname = zfs_prop_to_name(prop);
1125*92241e0bSTom Erickson 	uint64_t default_value;
1126*92241e0bSTom Erickson 
1127*92241e0bSTom Erickson 	if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {
1128*92241e0bSTom Erickson 		VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
1129*92241e0bSTom Erickson 		return;
1130*92241e0bSTom Erickson 	}
1131a2eea2e1Sahrens 
1132a2eea2e1Sahrens 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1133990b4856Slling 	VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
1134*92241e0bSTom Erickson 	/* Indicate the default source if we can. */
1135*92241e0bSTom Erickson 	if (dodefault(propname, 8, 1, &default_value) == 0 &&
1136*92241e0bSTom Erickson 	    value == default_value) {
1137*92241e0bSTom Erickson 		VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, "") == 0);
1138*92241e0bSTom Erickson 	}
1139*92241e0bSTom Erickson 	VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
1140a2eea2e1Sahrens 	nvlist_free(propval);
1141a2eea2e1Sahrens }
1142a2eea2e1Sahrens 
1143a2eea2e1Sahrens void
1144a2eea2e1Sahrens dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)
1145a2eea2e1Sahrens {
1146a2eea2e1Sahrens 	nvlist_t *propval;
1147*92241e0bSTom Erickson 	const char *propname = zfs_prop_to_name(prop);
1148*92241e0bSTom Erickson 
1149*92241e0bSTom Erickson 	if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {
1150*92241e0bSTom Erickson 		VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
1151*92241e0bSTom Erickson 		return;
1152*92241e0bSTom Erickson 	}
1153a2eea2e1Sahrens 
1154a2eea2e1Sahrens 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1155990b4856Slling 	VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
1156*92241e0bSTom Erickson 	VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
1157a2eea2e1Sahrens 	nvlist_free(propval);
1158a2eea2e1Sahrens }
1159