xref: /onnv-gate/usr/src/uts/common/fs/zfs/dsl_prop.c (revision 12455:5752a4eb2071)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51544Seschrock  * Common Development and Distribution License (the "License").
61544Seschrock  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
2212296SLin.Ling@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
2511022STom.Erickson@Sun.COM #include <sys/zfs_context.h>
26789Sahrens #include <sys/dmu.h>
271356Seschrock #include <sys/dmu_objset.h>
28789Sahrens #include <sys/dmu_tx.h>
29789Sahrens #include <sys/dsl_dataset.h>
30789Sahrens #include <sys/dsl_dir.h>
31789Sahrens #include <sys/dsl_prop.h>
322199Sahrens #include <sys/dsl_synctask.h>
33789Sahrens #include <sys/spa.h>
34789Sahrens #include <sys/zap.h>
35789Sahrens #include <sys/fs/zfs.h>
36789Sahrens 
37789Sahrens #include "zfs_prop.h"
38789Sahrens 
3911022STom.Erickson@Sun.COM #define	ZPROP_INHERIT_SUFFIX "$inherit"
4011022STom.Erickson@Sun.COM #define	ZPROP_RECVD_SUFFIX "$recvd"
4111022STom.Erickson@Sun.COM 
42789Sahrens static int
dodefault(const char * propname,int intsz,int numints,void * buf)4311022STom.Erickson@Sun.COM dodefault(const char *propname, int intsz, int numints, void *buf)
44789Sahrens {
45789Sahrens 	zfs_prop_t prop;
46789Sahrens 
475331Samw 	/*
485331Samw 	 * The setonce properties are read-only, BUT they still
495331Samw 	 * have a default value that can be used as the initial
505331Samw 	 * value.
515331Samw 	 */
525094Slling 	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL ||
535331Samw 	    (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop)))
54789Sahrens 		return (ENOENT);
55789Sahrens 
564787Sahrens 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
57789Sahrens 		if (intsz != 1)
58789Sahrens 			return (EOVERFLOW);
595094Slling 		(void) strncpy(buf, zfs_prop_default_string(prop),
6011022STom.Erickson@Sun.COM 		    numints);
61789Sahrens 	} else {
6211022STom.Erickson@Sun.COM 		if (intsz != 8 || numints < 1)
63789Sahrens 			return (EOVERFLOW);
64789Sahrens 
65789Sahrens 		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
66789Sahrens 	}
67789Sahrens 
68789Sahrens 	return (0);
69789Sahrens }
70789Sahrens 
717265Sahrens int
dsl_prop_get_dd(dsl_dir_t * dd,const char * propname,int intsz,int numints,void * buf,char * setpoint,boolean_t snapshot)727265Sahrens dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
7311022STom.Erickson@Sun.COM     int intsz, int numints, void *buf, char *setpoint, boolean_t snapshot)
74789Sahrens {
752082Seschrock 	int err = ENOENT;
7611022STom.Erickson@Sun.COM 	dsl_dir_t *target = dd;
777265Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
782676Seschrock 	zfs_prop_t prop;
7911022STom.Erickson@Sun.COM 	boolean_t inheritable;
8011022STom.Erickson@Sun.COM 	boolean_t inheriting = B_FALSE;
8111022STom.Erickson@Sun.COM 	char *inheritstr;
8211022STom.Erickson@Sun.COM 	char *recvdstr;
83789Sahrens 
847265Sahrens 	ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
857265Sahrens 
86789Sahrens 	if (setpoint)
87789Sahrens 		setpoint[0] = '\0';
88789Sahrens 
892676Seschrock 	prop = zfs_name_to_prop(propname);
9011022STom.Erickson@Sun.COM 	inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
9111022STom.Erickson@Sun.COM 	inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);
9211022STom.Erickson@Sun.COM 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
932676Seschrock 
942082Seschrock 	/*
9511022STom.Erickson@Sun.COM 	 * Note: dd may become NULL, therefore we shouldn't dereference it
9611022STom.Erickson@Sun.COM 	 * after this loop.
972082Seschrock 	 */
982082Seschrock 	for (; dd != NULL; dd = dd->dd_parent) {
992082Seschrock 		ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
10011022STom.Erickson@Sun.COM 
10111022STom.Erickson@Sun.COM 		if (dd != target || snapshot) {
10211022STom.Erickson@Sun.COM 			if (!inheritable)
10311022STom.Erickson@Sun.COM 				break;
10411022STom.Erickson@Sun.COM 			inheriting = B_TRUE;
10511022STom.Erickson@Sun.COM 		}
10611022STom.Erickson@Sun.COM 
10711022STom.Erickson@Sun.COM 		/* Check for a local value. */
10811022STom.Erickson@Sun.COM 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
10911022STom.Erickson@Sun.COM 		    intsz, numints, buf);
110789Sahrens 		if (err != ENOENT) {
11111022STom.Erickson@Sun.COM 			if (setpoint != NULL && err == 0)
112789Sahrens 				dsl_dir_name(dd, setpoint);
113789Sahrens 			break;
114789Sahrens 		}
1152676Seschrock 
1162676Seschrock 		/*
11711022STom.Erickson@Sun.COM 		 * Skip the check for a received value if there is an explicit
11811022STom.Erickson@Sun.COM 		 * inheritance entry.
1192676Seschrock 		 */
12011022STom.Erickson@Sun.COM 		err = zap_contains(mos, dd->dd_phys->dd_props_zapobj,
12111022STom.Erickson@Sun.COM 		    inheritstr);
12211022STom.Erickson@Sun.COM 		if (err != 0 && err != ENOENT)
1232676Seschrock 			break;
12411022STom.Erickson@Sun.COM 
12511022STom.Erickson@Sun.COM 		if (err == ENOENT) {
12611022STom.Erickson@Sun.COM 			/* Check for a received value. */
12711022STom.Erickson@Sun.COM 			err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
12811022STom.Erickson@Sun.COM 			    recvdstr, intsz, numints, buf);
12911022STom.Erickson@Sun.COM 			if (err != ENOENT) {
13011022STom.Erickson@Sun.COM 				if (setpoint != NULL && err == 0) {
13111022STom.Erickson@Sun.COM 					if (inheriting) {
13211022STom.Erickson@Sun.COM 						dsl_dir_name(dd, setpoint);
13311022STom.Erickson@Sun.COM 					} else {
13411022STom.Erickson@Sun.COM 						(void) strcpy(setpoint,
13511022STom.Erickson@Sun.COM 						    ZPROP_SOURCE_VAL_RECVD);
13611022STom.Erickson@Sun.COM 					}
13711022STom.Erickson@Sun.COM 				}
13811022STom.Erickson@Sun.COM 				break;
13911022STom.Erickson@Sun.COM 			}
14011022STom.Erickson@Sun.COM 		}
14111022STom.Erickson@Sun.COM 
14211022STom.Erickson@Sun.COM 		/*
14311022STom.Erickson@Sun.COM 		 * If we found an explicit inheritance entry, err is zero even
14411022STom.Erickson@Sun.COM 		 * though we haven't yet found the value, so reinitializing err
14511022STom.Erickson@Sun.COM 		 * at the end of the loop (instead of at the beginning) ensures
14611022STom.Erickson@Sun.COM 		 * that err has a valid post-loop value.
14711022STom.Erickson@Sun.COM 		 */
14811022STom.Erickson@Sun.COM 		err = ENOENT;
149789Sahrens 	}
15011022STom.Erickson@Sun.COM 
151789Sahrens 	if (err == ENOENT)
15211022STom.Erickson@Sun.COM 		err = dodefault(propname, intsz, numints, buf);
15311022STom.Erickson@Sun.COM 
15411022STom.Erickson@Sun.COM 	strfree(inheritstr);
15511022STom.Erickson@Sun.COM 	strfree(recvdstr);
156789Sahrens 
157789Sahrens 	return (err);
158789Sahrens }
159789Sahrens 
1607265Sahrens int
dsl_prop_get_ds(dsl_dataset_t * ds,const char * propname,int intsz,int numints,void * buf,char * setpoint)1617265Sahrens dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
16211022STom.Erickson@Sun.COM     int intsz, int numints, void *buf, char *setpoint)
1637265Sahrens {
16411022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
16511022STom.Erickson@Sun.COM 	boolean_t inheritable;
16611022STom.Erickson@Sun.COM 	boolean_t snapshot;
16711022STom.Erickson@Sun.COM 	uint64_t zapobj;
1687265Sahrens 
16911022STom.Erickson@Sun.COM 	ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
17011022STom.Erickson@Sun.COM 	inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
17111022STom.Erickson@Sun.COM 	snapshot = (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds));
17211022STom.Erickson@Sun.COM 	zapobj = (ds->ds_phys == NULL ? 0 : ds->ds_phys->ds_props_obj);
17311022STom.Erickson@Sun.COM 
17411022STom.Erickson@Sun.COM 	if (zapobj != 0) {
17511022STom.Erickson@Sun.COM 		objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
17611022STom.Erickson@Sun.COM 		int err;
17711022STom.Erickson@Sun.COM 
17811022STom.Erickson@Sun.COM 		ASSERT(snapshot);
17911022STom.Erickson@Sun.COM 
18011022STom.Erickson@Sun.COM 		/* Check for a local value. */
18111022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, propname, intsz, numints, buf);
1827265Sahrens 		if (err != ENOENT) {
18311022STom.Erickson@Sun.COM 			if (setpoint != NULL && err == 0)
1847265Sahrens 				dsl_dataset_name(ds, setpoint);
1857265Sahrens 			return (err);
1867265Sahrens 		}
18711022STom.Erickson@Sun.COM 
18811022STom.Erickson@Sun.COM 		/*
18911022STom.Erickson@Sun.COM 		 * Skip the check for a received value if there is an explicit
19011022STom.Erickson@Sun.COM 		 * inheritance entry.
19111022STom.Erickson@Sun.COM 		 */
19211022STom.Erickson@Sun.COM 		if (inheritable) {
19311022STom.Erickson@Sun.COM 			char *inheritstr = kmem_asprintf("%s%s", propname,
19411022STom.Erickson@Sun.COM 			    ZPROP_INHERIT_SUFFIX);
19511022STom.Erickson@Sun.COM 			err = zap_contains(mos, zapobj, inheritstr);
19611022STom.Erickson@Sun.COM 			strfree(inheritstr);
19711022STom.Erickson@Sun.COM 			if (err != 0 && err != ENOENT)
19811022STom.Erickson@Sun.COM 				return (err);
19911022STom.Erickson@Sun.COM 		}
20011022STom.Erickson@Sun.COM 
20111022STom.Erickson@Sun.COM 		if (err == ENOENT) {
20211022STom.Erickson@Sun.COM 			/* Check for a received value. */
20311022STom.Erickson@Sun.COM 			char *recvdstr = kmem_asprintf("%s%s", propname,
20411022STom.Erickson@Sun.COM 			    ZPROP_RECVD_SUFFIX);
20511022STom.Erickson@Sun.COM 			err = zap_lookup(mos, zapobj, recvdstr,
20611022STom.Erickson@Sun.COM 			    intsz, numints, buf);
20711022STom.Erickson@Sun.COM 			strfree(recvdstr);
20811022STom.Erickson@Sun.COM 			if (err != ENOENT) {
20911022STom.Erickson@Sun.COM 				if (setpoint != NULL && err == 0)
21011022STom.Erickson@Sun.COM 					(void) strcpy(setpoint,
21111022STom.Erickson@Sun.COM 					    ZPROP_SOURCE_VAL_RECVD);
21211022STom.Erickson@Sun.COM 				return (err);
21311022STom.Erickson@Sun.COM 			}
21411022STom.Erickson@Sun.COM 		}
2157265Sahrens 	}
2167265Sahrens 
2177265Sahrens 	return (dsl_prop_get_dd(ds->ds_dir, propname,
21811022STom.Erickson@Sun.COM 	    intsz, numints, buf, setpoint, snapshot));
2197265Sahrens }
2207265Sahrens 
221789Sahrens /*
222789Sahrens  * Register interest in the named property.  We'll call the callback
223789Sahrens  * once to notify it of the current property value, and again each time
224789Sahrens  * the property changes, until this callback is unregistered.
225789Sahrens  *
226789Sahrens  * Return 0 on success, errno if the prop is not an integer value.
227789Sahrens  */
228789Sahrens int
dsl_prop_register(dsl_dataset_t * ds,const char * propname,dsl_prop_changed_cb_t * callback,void * cbarg)229789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname,
230789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
231789Sahrens {
2322082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
2337265Sahrens 	dsl_pool_t *dp = dd->dd_pool;
234789Sahrens 	uint64_t value;
235789Sahrens 	dsl_prop_cb_record_t *cbr;
236789Sahrens 	int err;
2372199Sahrens 	int need_rwlock;
238789Sahrens 
2397265Sahrens 	need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
2402199Sahrens 	if (need_rwlock)
2417265Sahrens 		rw_enter(&dp->dp_config_rwlock, RW_READER);
242789Sahrens 
2437265Sahrens 	err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
244789Sahrens 	if (err != 0) {
2455569Sck153898 		if (need_rwlock)
2467265Sahrens 			rw_exit(&dp->dp_config_rwlock);
247789Sahrens 		return (err);
248789Sahrens 	}
249789Sahrens 
250789Sahrens 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
2512082Seschrock 	cbr->cbr_ds = ds;
252789Sahrens 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
253789Sahrens 	(void) strcpy((char *)cbr->cbr_propname, propname);
254789Sahrens 	cbr->cbr_func = callback;
255789Sahrens 	cbr->cbr_arg = cbarg;
256789Sahrens 	mutex_enter(&dd->dd_lock);
257789Sahrens 	list_insert_head(&dd->dd_prop_cbs, cbr);
258789Sahrens 	mutex_exit(&dd->dd_lock);
259789Sahrens 
260789Sahrens 	cbr->cbr_func(cbr->cbr_arg, value);
261789Sahrens 
2622199Sahrens 	if (need_rwlock)
2637265Sahrens 		rw_exit(&dp->dp_config_rwlock);
264789Sahrens 	return (0);
265789Sahrens }
266789Sahrens 
267789Sahrens int
dsl_prop_get(const char * dsname,const char * propname,int intsz,int numints,void * buf,char * setpoint)2687265Sahrens dsl_prop_get(const char *dsname, const char *propname,
2694543Smarks     int intsz, int numints, void *buf, char *setpoint)
2704543Smarks {
2717265Sahrens 	dsl_dataset_t *ds;
272789Sahrens 	int err;
273789Sahrens 
2747265Sahrens 	err = dsl_dataset_hold(dsname, FTAG, &ds);
2751544Seschrock 	if (err)
2761544Seschrock 		return (err);
277789Sahrens 
2787265Sahrens 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
2797265Sahrens 	err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
2807265Sahrens 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
281789Sahrens 
2827265Sahrens 	dsl_dataset_rele(ds, FTAG);
283789Sahrens 	return (err);
284789Sahrens }
285789Sahrens 
286789Sahrens /*
287789Sahrens  * Get the current property value.  It may have changed by the time this
288789Sahrens  * function returns, so it is NOT safe to follow up with
289789Sahrens  * dsl_prop_register() and assume that the value has not changed in
290789Sahrens  * between.
291789Sahrens  *
292789Sahrens  * Return 0 on success, ENOENT if ddname is invalid.
293789Sahrens  */
294789Sahrens int
dsl_prop_get_integer(const char * ddname,const char * propname,uint64_t * valuep,char * setpoint)295789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname,
296789Sahrens     uint64_t *valuep, char *setpoint)
297789Sahrens {
298789Sahrens 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
299789Sahrens }
300789Sahrens 
30111022STom.Erickson@Sun.COM void
dsl_prop_setarg_init_uint64(dsl_prop_setarg_t * psa,const char * propname,zprop_source_t source,uint64_t * value)30211022STom.Erickson@Sun.COM dsl_prop_setarg_init_uint64(dsl_prop_setarg_t *psa, const char *propname,
30311022STom.Erickson@Sun.COM     zprop_source_t source, uint64_t *value)
30411022STom.Erickson@Sun.COM {
30511022STom.Erickson@Sun.COM 	psa->psa_name = propname;
30611022STom.Erickson@Sun.COM 	psa->psa_source = source;
30711022STom.Erickson@Sun.COM 	psa->psa_intsz = 8;
30811022STom.Erickson@Sun.COM 	psa->psa_numints = 1;
30911022STom.Erickson@Sun.COM 	psa->psa_value = value;
31011022STom.Erickson@Sun.COM 
31111022STom.Erickson@Sun.COM 	psa->psa_effective_value = -1ULL;
31211022STom.Erickson@Sun.COM }
31311022STom.Erickson@Sun.COM 
31411022STom.Erickson@Sun.COM /*
31511022STom.Erickson@Sun.COM  * Predict the effective value of the given special property if it were set with
31611022STom.Erickson@Sun.COM  * the given value and source. This is not a general purpose function. It exists
31711022STom.Erickson@Sun.COM  * only to handle the special requirements of the quota and reservation
31811022STom.Erickson@Sun.COM  * properties. The fact that these properties are non-inheritable greatly
31911022STom.Erickson@Sun.COM  * simplifies the prediction logic.
32011022STom.Erickson@Sun.COM  *
32111022STom.Erickson@Sun.COM  * Returns 0 on success, a positive error code on failure, or -1 if called with
32211022STom.Erickson@Sun.COM  * a property not handled by this function.
32311022STom.Erickson@Sun.COM  */
32411022STom.Erickson@Sun.COM int
dsl_prop_predict_sync(dsl_dir_t * dd,dsl_prop_setarg_t * psa)32511022STom.Erickson@Sun.COM dsl_prop_predict_sync(dsl_dir_t *dd, dsl_prop_setarg_t *psa)
32611022STom.Erickson@Sun.COM {
32711022STom.Erickson@Sun.COM 	const char *propname = psa->psa_name;
32811022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
32911022STom.Erickson@Sun.COM 	zprop_source_t source = psa->psa_source;
33011022STom.Erickson@Sun.COM 	objset_t *mos;
33111022STom.Erickson@Sun.COM 	uint64_t zapobj;
33211022STom.Erickson@Sun.COM 	uint64_t version;
33311022STom.Erickson@Sun.COM 	char *recvdstr;
33411022STom.Erickson@Sun.COM 	int err = 0;
33511022STom.Erickson@Sun.COM 
33611022STom.Erickson@Sun.COM 	switch (prop) {
33711022STom.Erickson@Sun.COM 	case ZFS_PROP_QUOTA:
33811022STom.Erickson@Sun.COM 	case ZFS_PROP_RESERVATION:
33911022STom.Erickson@Sun.COM 	case ZFS_PROP_REFQUOTA:
34011022STom.Erickson@Sun.COM 	case ZFS_PROP_REFRESERVATION:
34111022STom.Erickson@Sun.COM 		break;
34211022STom.Erickson@Sun.COM 	default:
34311022STom.Erickson@Sun.COM 		return (-1);
34411022STom.Erickson@Sun.COM 	}
34511022STom.Erickson@Sun.COM 
34611022STom.Erickson@Sun.COM 	mos = dd->dd_pool->dp_meta_objset;
34711022STom.Erickson@Sun.COM 	zapobj = dd->dd_phys->dd_props_zapobj;
34811022STom.Erickson@Sun.COM 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
34911022STom.Erickson@Sun.COM 
35011022STom.Erickson@Sun.COM 	version = spa_version(dd->dd_pool->dp_spa);
35111022STom.Erickson@Sun.COM 	if (version < SPA_VERSION_RECVD_PROPS) {
35211022STom.Erickson@Sun.COM 		if (source & ZPROP_SRC_NONE)
35311022STom.Erickson@Sun.COM 			source = ZPROP_SRC_NONE;
35411022STom.Erickson@Sun.COM 		else if (source & ZPROP_SRC_RECEIVED)
35511022STom.Erickson@Sun.COM 			source = ZPROP_SRC_LOCAL;
35611022STom.Erickson@Sun.COM 	}
35711022STom.Erickson@Sun.COM 
35811022STom.Erickson@Sun.COM 	switch (source) {
35911022STom.Erickson@Sun.COM 	case ZPROP_SRC_NONE:
36011022STom.Erickson@Sun.COM 		/* Revert to the received value, if any. */
36111022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, recvdstr, 8, 1,
36211022STom.Erickson@Sun.COM 		    &psa->psa_effective_value);
36311022STom.Erickson@Sun.COM 		if (err == ENOENT)
36411022STom.Erickson@Sun.COM 			psa->psa_effective_value = 0;
36511022STom.Erickson@Sun.COM 		break;
36611022STom.Erickson@Sun.COM 	case ZPROP_SRC_LOCAL:
36711022STom.Erickson@Sun.COM 		psa->psa_effective_value = *(uint64_t *)psa->psa_value;
36811022STom.Erickson@Sun.COM 		break;
36911022STom.Erickson@Sun.COM 	case ZPROP_SRC_RECEIVED:
37011022STom.Erickson@Sun.COM 		/*
37111022STom.Erickson@Sun.COM 		 * If there's no local setting, then the new received value will
37211022STom.Erickson@Sun.COM 		 * be the effective value.
37311022STom.Erickson@Sun.COM 		 */
37411022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, propname, 8, 1,
37511022STom.Erickson@Sun.COM 		    &psa->psa_effective_value);
37611022STom.Erickson@Sun.COM 		if (err == ENOENT)
37711022STom.Erickson@Sun.COM 			psa->psa_effective_value = *(uint64_t *)psa->psa_value;
37811022STom.Erickson@Sun.COM 		break;
37911022STom.Erickson@Sun.COM 	case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):
38011022STom.Erickson@Sun.COM 		/*
38111022STom.Erickson@Sun.COM 		 * We're clearing the received value, so the local setting (if
38211022STom.Erickson@Sun.COM 		 * it exists) remains the effective value.
38311022STom.Erickson@Sun.COM 		 */
38411022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, propname, 8, 1,
38511022STom.Erickson@Sun.COM 		    &psa->psa_effective_value);
38611022STom.Erickson@Sun.COM 		if (err == ENOENT)
38711022STom.Erickson@Sun.COM 			psa->psa_effective_value = 0;
38811022STom.Erickson@Sun.COM 		break;
38911022STom.Erickson@Sun.COM 	default:
39011022STom.Erickson@Sun.COM 		cmn_err(CE_PANIC, "unexpected property source: %d", source);
39111022STom.Erickson@Sun.COM 	}
39211022STom.Erickson@Sun.COM 
39311022STom.Erickson@Sun.COM 	strfree(recvdstr);
39411022STom.Erickson@Sun.COM 
39511022STom.Erickson@Sun.COM 	if (err == ENOENT)
39611022STom.Erickson@Sun.COM 		return (0);
39711022STom.Erickson@Sun.COM 
39811022STom.Erickson@Sun.COM 	return (err);
39911022STom.Erickson@Sun.COM }
40011022STom.Erickson@Sun.COM 
40111022STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
40211022STom.Erickson@Sun.COM void
dsl_prop_check_prediction(dsl_dir_t * dd,dsl_prop_setarg_t * psa)40311022STom.Erickson@Sun.COM dsl_prop_check_prediction(dsl_dir_t *dd, dsl_prop_setarg_t *psa)
40411022STom.Erickson@Sun.COM {
40511022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(psa->psa_name);
40611022STom.Erickson@Sun.COM 	uint64_t intval;
40711022STom.Erickson@Sun.COM 	char setpoint[MAXNAMELEN];
40811022STom.Erickson@Sun.COM 	uint64_t version = spa_version(dd->dd_pool->dp_spa);
40911022STom.Erickson@Sun.COM 	int err;
41011022STom.Erickson@Sun.COM 
41111022STom.Erickson@Sun.COM 	if (version < SPA_VERSION_RECVD_PROPS) {
41211022STom.Erickson@Sun.COM 		switch (prop) {
41311022STom.Erickson@Sun.COM 		case ZFS_PROP_QUOTA:
41411022STom.Erickson@Sun.COM 		case ZFS_PROP_RESERVATION:
41511022STom.Erickson@Sun.COM 			return;
41611022STom.Erickson@Sun.COM 		}
41711022STom.Erickson@Sun.COM 	}
41811022STom.Erickson@Sun.COM 
41911022STom.Erickson@Sun.COM 	err = dsl_prop_get_dd(dd, psa->psa_name, 8, 1, &intval,
42011022STom.Erickson@Sun.COM 	    setpoint, B_FALSE);
42111022STom.Erickson@Sun.COM 	if (err == 0 && intval != psa->psa_effective_value) {
42211022STom.Erickson@Sun.COM 		cmn_err(CE_PANIC, "%s property, source: %x, "
42311022STom.Erickson@Sun.COM 		    "predicted effective value: %llu, "
42411022STom.Erickson@Sun.COM 		    "actual effective value: %llu (setpoint: %s)",
42511022STom.Erickson@Sun.COM 		    psa->psa_name, psa->psa_source,
42611022STom.Erickson@Sun.COM 		    (unsigned long long)psa->psa_effective_value,
42711022STom.Erickson@Sun.COM 		    (unsigned long long)intval, setpoint);
42811022STom.Erickson@Sun.COM 	}
42911022STom.Erickson@Sun.COM }
43011022STom.Erickson@Sun.COM #endif
43111022STom.Erickson@Sun.COM 
432789Sahrens /*
433789Sahrens  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
434789Sahrens  * invalid, ENOMSG if no matching callback registered.
435789Sahrens  */
436789Sahrens int
dsl_prop_unregister(dsl_dataset_t * ds,const char * propname,dsl_prop_changed_cb_t * callback,void * cbarg)437789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
438789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
439789Sahrens {
4402082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
441789Sahrens 	dsl_prop_cb_record_t *cbr;
442789Sahrens 
443789Sahrens 	mutex_enter(&dd->dd_lock);
444789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
445789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
4462082Seschrock 		if (cbr->cbr_ds == ds &&
447789Sahrens 		    cbr->cbr_func == callback &&
4482082Seschrock 		    cbr->cbr_arg == cbarg &&
4492082Seschrock 		    strcmp(cbr->cbr_propname, propname) == 0)
450789Sahrens 			break;
451789Sahrens 	}
452789Sahrens 
453789Sahrens 	if (cbr == NULL) {
454789Sahrens 		mutex_exit(&dd->dd_lock);
455789Sahrens 		return (ENOMSG);
456789Sahrens 	}
457789Sahrens 
458789Sahrens 	list_remove(&dd->dd_prop_cbs, cbr);
459789Sahrens 	mutex_exit(&dd->dd_lock);
460789Sahrens 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
461789Sahrens 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
462789Sahrens 
463789Sahrens 	return (0);
464789Sahrens }
465789Sahrens 
4662082Seschrock /*
4672082Seschrock  * Return the number of callbacks that are registered for this dataset.
4682082Seschrock  */
4692082Seschrock int
dsl_prop_numcb(dsl_dataset_t * ds)4702082Seschrock dsl_prop_numcb(dsl_dataset_t *ds)
4712082Seschrock {
4722082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
4732082Seschrock 	dsl_prop_cb_record_t *cbr;
4742082Seschrock 	int num = 0;
4752082Seschrock 
4762082Seschrock 	mutex_enter(&dd->dd_lock);
4772082Seschrock 	for (cbr = list_head(&dd->dd_prop_cbs);
4782082Seschrock 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
4792082Seschrock 		if (cbr->cbr_ds == ds)
4802082Seschrock 			num++;
4812082Seschrock 	}
4822082Seschrock 	mutex_exit(&dd->dd_lock);
4832082Seschrock 
4842082Seschrock 	return (num);
4852082Seschrock }
4862082Seschrock 
487789Sahrens static void
dsl_prop_changed_notify(dsl_pool_t * dp,uint64_t ddobj,const char * propname,uint64_t value,int first)488789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
489789Sahrens     const char *propname, uint64_t value, int first)
490789Sahrens {
491789Sahrens 	dsl_dir_t *dd;
492789Sahrens 	dsl_prop_cb_record_t *cbr;
493789Sahrens 	objset_t *mos = dp->dp_meta_objset;
4942199Sahrens 	zap_cursor_t zc;
4956047Sahrens 	zap_attribute_t *za;
496789Sahrens 	int err;
497789Sahrens 
498789Sahrens 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
4991544Seschrock 	err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
5001544Seschrock 	if (err)
5011544Seschrock 		return;
502789Sahrens 
503789Sahrens 	if (!first) {
504789Sahrens 		/*
505789Sahrens 		 * If the prop is set here, then this change is not
506789Sahrens 		 * being inherited here or below; stop the recursion.
507789Sahrens 		 */
50811022STom.Erickson@Sun.COM 		err = zap_contains(mos, dd->dd_phys->dd_props_zapobj, propname);
509789Sahrens 		if (err == 0) {
510789Sahrens 			dsl_dir_close(dd, FTAG);
511789Sahrens 			return;
512789Sahrens 		}
513789Sahrens 		ASSERT3U(err, ==, ENOENT);
514789Sahrens 	}
515789Sahrens 
516789Sahrens 	mutex_enter(&dd->dd_lock);
5177265Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs); cbr;
5187265Sahrens 	    cbr = list_next(&dd->dd_prop_cbs, cbr)) {
5197265Sahrens 		uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
5207265Sahrens 
5217265Sahrens 		if (strcmp(cbr->cbr_propname, propname) != 0)
5227265Sahrens 			continue;
5237265Sahrens 
5247265Sahrens 		/*
5257265Sahrens 		 * If the property is set on this ds, then it is not
5267265Sahrens 		 * inherited here; don't call the callback.
5277265Sahrens 		 */
52811022STom.Erickson@Sun.COM 		if (propobj && 0 == zap_contains(mos, propobj, propname))
5297265Sahrens 			continue;
5307265Sahrens 
5317265Sahrens 		cbr->cbr_func(cbr->cbr_arg, value);
532789Sahrens 	}
533789Sahrens 	mutex_exit(&dd->dd_lock);
534789Sahrens 
5356047Sahrens 	za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
5362199Sahrens 	for (zap_cursor_init(&zc, mos,
5372199Sahrens 	    dd->dd_phys->dd_child_dir_zapobj);
5386047Sahrens 	    zap_cursor_retrieve(&zc, za) == 0;
5392199Sahrens 	    zap_cursor_advance(&zc)) {
5406047Sahrens 		dsl_prop_changed_notify(dp, za->za_first_integer,
5412199Sahrens 		    propname, value, FALSE);
542789Sahrens 	}
5436047Sahrens 	kmem_free(za, sizeof (zap_attribute_t));
5442199Sahrens 	zap_cursor_fini(&zc);
545789Sahrens 	dsl_dir_close(dd, FTAG);
546789Sahrens }
547789Sahrens 
54811022STom.Erickson@Sun.COM void
dsl_prop_set_sync(void * arg1,void * arg2,dmu_tx_t * tx)54912296SLin.Ling@Sun.COM dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
550789Sahrens {
5517265Sahrens 	dsl_dataset_t *ds = arg1;
55211022STom.Erickson@Sun.COM 	dsl_prop_setarg_t *psa = arg2;
5537265Sahrens 	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
55411022STom.Erickson@Sun.COM 	uint64_t zapobj, intval, dummy;
5552199Sahrens 	int isint;
5564543Smarks 	char valbuf[32];
55711022STom.Erickson@Sun.COM 	char *valstr = NULL;
55811022STom.Erickson@Sun.COM 	char *inheritstr;
55911022STom.Erickson@Sun.COM 	char *recvdstr;
56011022STom.Erickson@Sun.COM 	char *tbuf = NULL;
56111022STom.Erickson@Sun.COM 	int err;
56211022STom.Erickson@Sun.COM 	uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa);
56311022STom.Erickson@Sun.COM 	const char *propname = psa->psa_name;
56411022STom.Erickson@Sun.COM 	zprop_source_t source = psa->psa_source;
565789Sahrens 
56611022STom.Erickson@Sun.COM 	isint = (dodefault(propname, 8, 1, &intval) == 0);
56711022STom.Erickson@Sun.COM 
56811022STom.Erickson@Sun.COM 	if (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds)) {
56911022STom.Erickson@Sun.COM 		ASSERT(version >= SPA_VERSION_SNAP_PROPS);
5707265Sahrens 		if (ds->ds_phys->ds_props_obj == 0) {
5717265Sahrens 			dmu_buf_will_dirty(ds->ds_dbuf, tx);
5727265Sahrens 			ds->ds_phys->ds_props_obj =
5737265Sahrens 			    zap_create(mos,
5747265Sahrens 			    DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
5757265Sahrens 		}
5767265Sahrens 		zapobj = ds->ds_phys->ds_props_obj;
5777265Sahrens 	} else {
5787265Sahrens 		zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
5797265Sahrens 	}
5807265Sahrens 
58111022STom.Erickson@Sun.COM 	if (version < SPA_VERSION_RECVD_PROPS) {
58211022STom.Erickson@Sun.COM 		zfs_prop_t prop = zfs_name_to_prop(propname);
58311022STom.Erickson@Sun.COM 		if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION)
58411022STom.Erickson@Sun.COM 			return;
58511022STom.Erickson@Sun.COM 
58611022STom.Erickson@Sun.COM 		if (source & ZPROP_SRC_NONE)
58711022STom.Erickson@Sun.COM 			source = ZPROP_SRC_NONE;
58811022STom.Erickson@Sun.COM 		else if (source & ZPROP_SRC_RECEIVED)
58911022STom.Erickson@Sun.COM 			source = ZPROP_SRC_LOCAL;
590789Sahrens 	}
591789Sahrens 
59211022STom.Erickson@Sun.COM 	inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);
59311022STom.Erickson@Sun.COM 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
59411022STom.Erickson@Sun.COM 
59511022STom.Erickson@Sun.COM 	switch (source) {
59611022STom.Erickson@Sun.COM 	case ZPROP_SRC_NONE:
59711022STom.Erickson@Sun.COM 		/*
59811022STom.Erickson@Sun.COM 		 * revert to received value, if any (inherit -S)
59911022STom.Erickson@Sun.COM 		 * - remove propname
60011022STom.Erickson@Sun.COM 		 * - remove propname$inherit
60111022STom.Erickson@Sun.COM 		 */
60211022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, propname, tx);
60311022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
60411022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, inheritstr, tx);
60511022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
60611022STom.Erickson@Sun.COM 		break;
60711022STom.Erickson@Sun.COM 	case ZPROP_SRC_LOCAL:
60811022STom.Erickson@Sun.COM 		/*
60911022STom.Erickson@Sun.COM 		 * remove propname$inherit
61011022STom.Erickson@Sun.COM 		 * set propname -> value
61111022STom.Erickson@Sun.COM 		 */
61211022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, inheritstr, tx);
61311022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
61411022STom.Erickson@Sun.COM 		VERIFY(0 == zap_update(mos, zapobj, propname,
61511022STom.Erickson@Sun.COM 		    psa->psa_intsz, psa->psa_numints, psa->psa_value, tx));
61611022STom.Erickson@Sun.COM 		break;
61711022STom.Erickson@Sun.COM 	case ZPROP_SRC_INHERITED:
61811022STom.Erickson@Sun.COM 		/*
61911022STom.Erickson@Sun.COM 		 * explicitly inherit
62011022STom.Erickson@Sun.COM 		 * - remove propname
62111022STom.Erickson@Sun.COM 		 * - set propname$inherit
62211022STom.Erickson@Sun.COM 		 */
62311022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, propname, tx);
62411022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
62511022STom.Erickson@Sun.COM 		if (version >= SPA_VERSION_RECVD_PROPS &&
626*12455STom.Erickson@Sun.COM 		    dsl_prop_get_ds(ds, ZPROP_HAS_RECVD, 8, 1, &dummy,
627*12455STom.Erickson@Sun.COM 		    NULL) == 0) {
62811022STom.Erickson@Sun.COM 			dummy = 0;
62911022STom.Erickson@Sun.COM 			err = zap_update(mos, zapobj, inheritstr,
63011022STom.Erickson@Sun.COM 			    8, 1, &dummy, tx);
63111022STom.Erickson@Sun.COM 			ASSERT(err == 0);
63211022STom.Erickson@Sun.COM 		}
63311022STom.Erickson@Sun.COM 		break;
63411022STom.Erickson@Sun.COM 	case ZPROP_SRC_RECEIVED:
63511022STom.Erickson@Sun.COM 		/*
63611022STom.Erickson@Sun.COM 		 * set propname$recvd -> value
63711022STom.Erickson@Sun.COM 		 */
63811022STom.Erickson@Sun.COM 		err = zap_update(mos, zapobj, recvdstr,
63911022STom.Erickson@Sun.COM 		    psa->psa_intsz, psa->psa_numints, psa->psa_value, tx);
64011022STom.Erickson@Sun.COM 		ASSERT(err == 0);
64111022STom.Erickson@Sun.COM 		break;
64211022STom.Erickson@Sun.COM 	case (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED):
64311022STom.Erickson@Sun.COM 		/*
64411022STom.Erickson@Sun.COM 		 * clear local and received settings
64511022STom.Erickson@Sun.COM 		 * - remove propname
64611022STom.Erickson@Sun.COM 		 * - remove propname$inherit
64711022STom.Erickson@Sun.COM 		 * - remove propname$recvd
64811022STom.Erickson@Sun.COM 		 */
64911022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, propname, tx);
65011022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
65111022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, inheritstr, tx);
65211022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
65311022STom.Erickson@Sun.COM 		/* FALLTHRU */
65411022STom.Erickson@Sun.COM 	case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):
65511022STom.Erickson@Sun.COM 		/*
65611022STom.Erickson@Sun.COM 		 * remove propname$recvd
65711022STom.Erickson@Sun.COM 		 */
65811022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, recvdstr, tx);
65911022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
66011022STom.Erickson@Sun.COM 		break;
66111022STom.Erickson@Sun.COM 	default:
66211022STom.Erickson@Sun.COM 		cmn_err(CE_PANIC, "unexpected property source: %d", source);
66311022STom.Erickson@Sun.COM 	}
66411022STom.Erickson@Sun.COM 
66511022STom.Erickson@Sun.COM 	strfree(inheritstr);
66611022STom.Erickson@Sun.COM 	strfree(recvdstr);
66711022STom.Erickson@Sun.COM 
6682199Sahrens 	if (isint) {
66911022STom.Erickson@Sun.COM 		VERIFY(0 == dsl_prop_get_ds(ds, propname, 8, 1, &intval, NULL));
67011022STom.Erickson@Sun.COM 
67111022STom.Erickson@Sun.COM 		if (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds)) {
6727265Sahrens 			dsl_prop_cb_record_t *cbr;
6737265Sahrens 			/*
6747265Sahrens 			 * It's a snapshot; nothing can inherit this
6757265Sahrens 			 * property, so just look for callbacks on this
6767265Sahrens 			 * ds here.
6777265Sahrens 			 */
6787265Sahrens 			mutex_enter(&ds->ds_dir->dd_lock);
6797265Sahrens 			for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
6807265Sahrens 			    cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
6817265Sahrens 				if (cbr->cbr_ds == ds &&
68211022STom.Erickson@Sun.COM 				    strcmp(cbr->cbr_propname, propname) == 0)
6837265Sahrens 					cbr->cbr_func(cbr->cbr_arg, intval);
6847265Sahrens 			}
6857265Sahrens 			mutex_exit(&ds->ds_dir->dd_lock);
6867265Sahrens 		} else {
6877265Sahrens 			dsl_prop_changed_notify(ds->ds_dir->dd_pool,
68811022STom.Erickson@Sun.COM 			    ds->ds_dir->dd_object, propname, intval, TRUE);
6897265Sahrens 		}
69011022STom.Erickson@Sun.COM 
6914543Smarks 		(void) snprintf(valbuf, sizeof (valbuf),
6924543Smarks 		    "%lld", (longlong_t)intval);
6934543Smarks 		valstr = valbuf;
6944543Smarks 	} else {
69511022STom.Erickson@Sun.COM 		if (source == ZPROP_SRC_LOCAL) {
69611022STom.Erickson@Sun.COM 			valstr = (char *)psa->psa_value;
69711022STom.Erickson@Sun.COM 		} else {
69811022STom.Erickson@Sun.COM 			tbuf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP);
69911022STom.Erickson@Sun.COM 			if (dsl_prop_get_ds(ds, propname, 1,
70011022STom.Erickson@Sun.COM 			    ZAP_MAXVALUELEN, tbuf, NULL) == 0)
70111022STom.Erickson@Sun.COM 				valstr = tbuf;
70211022STom.Erickson@Sun.COM 		}
7034543Smarks 	}
70411022STom.Erickson@Sun.COM 
70512296SLin.Ling@Sun.COM 	spa_history_log_internal((source == ZPROP_SRC_NONE ||
70611022STom.Erickson@Sun.COM 	    source == ZPROP_SRC_INHERITED) ? LOG_DS_INHERIT :
70712296SLin.Ling@Sun.COM 	    LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx,
70811022STom.Erickson@Sun.COM 	    "%s=%s dataset = %llu", propname,
70911022STom.Erickson@Sun.COM 	    (valstr == NULL ? "" : valstr), ds->ds_object);
71011022STom.Erickson@Sun.COM 
71111022STom.Erickson@Sun.COM 	if (tbuf != NULL)
71211022STom.Erickson@Sun.COM 		kmem_free(tbuf, ZAP_MAXVALUELEN);
713789Sahrens }
714789Sahrens 
7159355SMatthew.Ahrens@Sun.COM void
dsl_props_set_sync(void * arg1,void * arg2,dmu_tx_t * tx)71612296SLin.Ling@Sun.COM dsl_props_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
7178697SRichard.Morris@Sun.COM {
7188697SRichard.Morris@Sun.COM 	dsl_dataset_t *ds = arg1;
71911022STom.Erickson@Sun.COM 	dsl_props_arg_t *pa = arg2;
72011022STom.Erickson@Sun.COM 	nvlist_t *props = pa->pa_props;
72111022STom.Erickson@Sun.COM 	dsl_prop_setarg_t psa;
7228697SRichard.Morris@Sun.COM 	nvpair_t *elem = NULL;
7238697SRichard.Morris@Sun.COM 
72411022STom.Erickson@Sun.COM 	psa.psa_source = pa->pa_source;
7258697SRichard.Morris@Sun.COM 
72611022STom.Erickson@Sun.COM 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
72711022STom.Erickson@Sun.COM 		nvpair_t *pair = elem;
72811022STom.Erickson@Sun.COM 
72911022STom.Erickson@Sun.COM 		psa.psa_name = nvpair_name(pair);
7308697SRichard.Morris@Sun.COM 
73111022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
73211022STom.Erickson@Sun.COM 			/*
73311022STom.Erickson@Sun.COM 			 * dsl_prop_get_all_impl() returns properties in this
73411022STom.Erickson@Sun.COM 			 * format.
73511022STom.Erickson@Sun.COM 			 */
73611022STom.Erickson@Sun.COM 			nvlist_t *attrs;
73711022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
73811022STom.Erickson@Sun.COM 			VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
73911022STom.Erickson@Sun.COM 			    &pair) == 0);
74011022STom.Erickson@Sun.COM 		}
74111022STom.Erickson@Sun.COM 
74211022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_STRING) {
74311022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_string(pair,
74411022STom.Erickson@Sun.COM 			    (char **)&psa.psa_value) == 0);
74511022STom.Erickson@Sun.COM 			psa.psa_intsz = 1;
74611022STom.Erickson@Sun.COM 			psa.psa_numints = strlen(psa.psa_value) + 1;
7478697SRichard.Morris@Sun.COM 		} else {
7488697SRichard.Morris@Sun.COM 			uint64_t intval;
74911022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_uint64(pair, &intval) == 0);
75011022STom.Erickson@Sun.COM 			psa.psa_intsz = sizeof (intval);
75111022STom.Erickson@Sun.COM 			psa.psa_numints = 1;
75211022STom.Erickson@Sun.COM 			psa.psa_value = &intval;
7538697SRichard.Morris@Sun.COM 		}
75412296SLin.Ling@Sun.COM 		dsl_prop_set_sync(ds, &psa, tx);
7558697SRichard.Morris@Sun.COM 	}
7568697SRichard.Morris@Sun.COM }
7578697SRichard.Morris@Sun.COM 
7585378Sck153898 void
dsl_dir_prop_set_uint64_sync(dsl_dir_t * dd,const char * name,uint64_t val,dmu_tx_t * tx)75910242Schris.kirby@sun.com dsl_dir_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
76012296SLin.Ling@Sun.COM     dmu_tx_t *tx)
7615378Sck153898 {
7625378Sck153898 	objset_t *mos = dd->dd_pool->dp_meta_objset;
7635378Sck153898 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
7645378Sck153898 
7655378Sck153898 	ASSERT(dmu_tx_is_syncing(tx));
7665378Sck153898 
7675378Sck153898 	VERIFY(0 == zap_update(mos, zapobj, name, sizeof (val), 1, &val, tx));
7685378Sck153898 
7695378Sck153898 	dsl_prop_changed_notify(dd->dd_pool, dd->dd_object, name, val, TRUE);
7705378Sck153898 
77112296SLin.Ling@Sun.COM 	spa_history_log_internal(LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx,
7725378Sck153898 	    "%s=%llu dataset = %llu", name, (u_longlong_t)val,
7735378Sck153898 	    dd->dd_phys->dd_head_dataset_obj);
7745378Sck153898 }
7755378Sck153898 
776789Sahrens int
dsl_prop_set(const char * dsname,const char * propname,zprop_source_t source,int intsz,int numints,const void * buf)77711022STom.Erickson@Sun.COM dsl_prop_set(const char *dsname, const char *propname, zprop_source_t source,
7782885Sahrens     int intsz, int numints, const void *buf)
7792885Sahrens {
7807265Sahrens 	dsl_dataset_t *ds;
7819643SEric.Taylor@Sun.COM 	uint64_t version;
7827265Sahrens 	int err;
78311022STom.Erickson@Sun.COM 	dsl_prop_setarg_t psa;
7842885Sahrens 
7852641Sahrens 	/*
7862641Sahrens 	 * We must do these checks before we get to the syncfunc, since
7872641Sahrens 	 * it can't fail.
7882641Sahrens 	 */
7892641Sahrens 	if (strlen(propname) >= ZAP_MAXNAMELEN)
7902641Sahrens 		return (ENAMETOOLONG);
7912641Sahrens 
7927265Sahrens 	err = dsl_dataset_hold(dsname, FTAG, &ds);
7931544Seschrock 	if (err)
7941544Seschrock 		return (err);
7957265Sahrens 
7969643SEric.Taylor@Sun.COM 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
7979643SEric.Taylor@Sun.COM 	if (intsz * numints >= (version < SPA_VERSION_STMF_PROP ?
7989643SEric.Taylor@Sun.COM 	    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
7999643SEric.Taylor@Sun.COM 		dsl_dataset_rele(ds, FTAG);
8009643SEric.Taylor@Sun.COM 		return (E2BIG);
8019643SEric.Taylor@Sun.COM 	}
8027265Sahrens 	if (dsl_dataset_is_snapshot(ds) &&
8039643SEric.Taylor@Sun.COM 	    version < SPA_VERSION_SNAP_PROPS) {
8047265Sahrens 		dsl_dataset_rele(ds, FTAG);
8057265Sahrens 		return (ENOTSUP);
8067265Sahrens 	}
8077265Sahrens 
80811022STom.Erickson@Sun.COM 	psa.psa_name = propname;
80911022STom.Erickson@Sun.COM 	psa.psa_source = source;
81011022STom.Erickson@Sun.COM 	psa.psa_intsz = intsz;
81111022STom.Erickson@Sun.COM 	psa.psa_numints = numints;
81211022STom.Erickson@Sun.COM 	psa.psa_value = buf;
81311022STom.Erickson@Sun.COM 	psa.psa_effective_value = -1ULL;
81411022STom.Erickson@Sun.COM 
8157265Sahrens 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
8167265Sahrens 	    NULL, dsl_prop_set_sync, ds, &psa, 2);
8177265Sahrens 
8187265Sahrens 	dsl_dataset_rele(ds, FTAG);
819789Sahrens 	return (err);
820789Sahrens }
8211356Seschrock 
8228697SRichard.Morris@Sun.COM int
dsl_props_set(const char * dsname,zprop_source_t source,nvlist_t * props)82311022STom.Erickson@Sun.COM dsl_props_set(const char *dsname, zprop_source_t source, nvlist_t *props)
8248697SRichard.Morris@Sun.COM {
8258697SRichard.Morris@Sun.COM 	dsl_dataset_t *ds;
8269643SEric.Taylor@Sun.COM 	uint64_t version;
8278697SRichard.Morris@Sun.COM 	nvpair_t *elem = NULL;
82811022STom.Erickson@Sun.COM 	dsl_props_arg_t pa;
8298697SRichard.Morris@Sun.COM 	int err;
8308697SRichard.Morris@Sun.COM 
8319643SEric.Taylor@Sun.COM 	if (err = dsl_dataset_hold(dsname, FTAG, &ds))
8329643SEric.Taylor@Sun.COM 		return (err);
8338924SRichard.Morris@Sun.COM 	/*
8348924SRichard.Morris@Sun.COM 	 * Do these checks before the syncfunc, since it can't fail.
8358924SRichard.Morris@Sun.COM 	 */
8369643SEric.Taylor@Sun.COM 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
83711022STom.Erickson@Sun.COM 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
8389643SEric.Taylor@Sun.COM 		if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
8399643SEric.Taylor@Sun.COM 			dsl_dataset_rele(ds, FTAG);
8408924SRichard.Morris@Sun.COM 			return (ENAMETOOLONG);
8419643SEric.Taylor@Sun.COM 		}
8428697SRichard.Morris@Sun.COM 		if (nvpair_type(elem) == DATA_TYPE_STRING) {
8438697SRichard.Morris@Sun.COM 			char *valstr;
8448697SRichard.Morris@Sun.COM 			VERIFY(nvpair_value_string(elem, &valstr) == 0);
8459643SEric.Taylor@Sun.COM 			if (strlen(valstr) >= (version <
8469643SEric.Taylor@Sun.COM 			    SPA_VERSION_STMF_PROP ?
8479643SEric.Taylor@Sun.COM 			    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
8489643SEric.Taylor@Sun.COM 				dsl_dataset_rele(ds, FTAG);
8498924SRichard.Morris@Sun.COM 				return (E2BIG);
8509643SEric.Taylor@Sun.COM 			}
8518697SRichard.Morris@Sun.COM 		}
8528697SRichard.Morris@Sun.COM 	}
8538697SRichard.Morris@Sun.COM 
8548697SRichard.Morris@Sun.COM 	if (dsl_dataset_is_snapshot(ds) &&
8559643SEric.Taylor@Sun.COM 	    version < SPA_VERSION_SNAP_PROPS) {
8568697SRichard.Morris@Sun.COM 		dsl_dataset_rele(ds, FTAG);
8578697SRichard.Morris@Sun.COM 		return (ENOTSUP);
8588697SRichard.Morris@Sun.COM 	}
8598697SRichard.Morris@Sun.COM 
86011022STom.Erickson@Sun.COM 	pa.pa_props = props;
86111022STom.Erickson@Sun.COM 	pa.pa_source = source;
86211022STom.Erickson@Sun.COM 
8638697SRichard.Morris@Sun.COM 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
86411022STom.Erickson@Sun.COM 	    NULL, dsl_props_set_sync, ds, &pa, 2);
8658697SRichard.Morris@Sun.COM 
8668697SRichard.Morris@Sun.COM 	dsl_dataset_rele(ds, FTAG);
8678697SRichard.Morris@Sun.COM 	return (err);
8688697SRichard.Morris@Sun.COM }
8698697SRichard.Morris@Sun.COM 
87011022STom.Erickson@Sun.COM typedef enum dsl_prop_getflags {
87111022STom.Erickson@Sun.COM 	DSL_PROP_GET_INHERITING = 0x1,	/* searching parent of target ds */
87211022STom.Erickson@Sun.COM 	DSL_PROP_GET_SNAPSHOT = 0x2,	/* snapshot dataset */
87311022STom.Erickson@Sun.COM 	DSL_PROP_GET_LOCAL = 0x4,	/* local properties */
87411022STom.Erickson@Sun.COM 	DSL_PROP_GET_RECEIVED = 0x8	/* received properties */
87511022STom.Erickson@Sun.COM } dsl_prop_getflags_t;
87611022STom.Erickson@Sun.COM 
87711022STom.Erickson@Sun.COM static int
dsl_prop_get_all_impl(objset_t * mos,uint64_t propobj,const char * setpoint,dsl_prop_getflags_t flags,nvlist_t * nv)87811022STom.Erickson@Sun.COM dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj,
87911022STom.Erickson@Sun.COM     const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv)
88011022STom.Erickson@Sun.COM {
88111022STom.Erickson@Sun.COM 	zap_cursor_t zc;
88211022STom.Erickson@Sun.COM 	zap_attribute_t za;
88311022STom.Erickson@Sun.COM 	int err = 0;
88411022STom.Erickson@Sun.COM 
88511022STom.Erickson@Sun.COM 	for (zap_cursor_init(&zc, mos, propobj);
88611022STom.Erickson@Sun.COM 	    (err = zap_cursor_retrieve(&zc, &za)) == 0;
88711022STom.Erickson@Sun.COM 	    zap_cursor_advance(&zc)) {
88811022STom.Erickson@Sun.COM 		nvlist_t *propval;
88911022STom.Erickson@Sun.COM 		zfs_prop_t prop;
89011022STom.Erickson@Sun.COM 		char buf[ZAP_MAXNAMELEN];
89111022STom.Erickson@Sun.COM 		char *valstr;
89211022STom.Erickson@Sun.COM 		const char *suffix;
89311022STom.Erickson@Sun.COM 		const char *propname;
89411022STom.Erickson@Sun.COM 		const char *source;
89511022STom.Erickson@Sun.COM 
89611022STom.Erickson@Sun.COM 		suffix = strchr(za.za_name, '$');
89711022STom.Erickson@Sun.COM 
89811022STom.Erickson@Sun.COM 		if (suffix == NULL) {
89911022STom.Erickson@Sun.COM 			/*
90011022STom.Erickson@Sun.COM 			 * Skip local properties if we only want received
90111022STom.Erickson@Sun.COM 			 * properties.
90211022STom.Erickson@Sun.COM 			 */
90311022STom.Erickson@Sun.COM 			if (flags & DSL_PROP_GET_RECEIVED)
90411022STom.Erickson@Sun.COM 				continue;
90511022STom.Erickson@Sun.COM 
90611022STom.Erickson@Sun.COM 			propname = za.za_name;
90711022STom.Erickson@Sun.COM 			source = setpoint;
90811022STom.Erickson@Sun.COM 		} else if (strcmp(suffix, ZPROP_INHERIT_SUFFIX) == 0) {
90911022STom.Erickson@Sun.COM 			/* Skip explicitly inherited entries. */
91011022STom.Erickson@Sun.COM 			continue;
91111022STom.Erickson@Sun.COM 		} else if (strcmp(suffix, ZPROP_RECVD_SUFFIX) == 0) {
91211022STom.Erickson@Sun.COM 			if (flags & DSL_PROP_GET_LOCAL)
91311022STom.Erickson@Sun.COM 				continue;
91411022STom.Erickson@Sun.COM 
91511022STom.Erickson@Sun.COM 			(void) strncpy(buf, za.za_name, (suffix - za.za_name));
91611022STom.Erickson@Sun.COM 			buf[suffix - za.za_name] = '\0';
91711022STom.Erickson@Sun.COM 			propname = buf;
91811022STom.Erickson@Sun.COM 
91911022STom.Erickson@Sun.COM 			if (!(flags & DSL_PROP_GET_RECEIVED)) {
92011022STom.Erickson@Sun.COM 				/* Skip if locally overridden. */
92111022STom.Erickson@Sun.COM 				err = zap_contains(mos, propobj, propname);
92211022STom.Erickson@Sun.COM 				if (err == 0)
92311022STom.Erickson@Sun.COM 					continue;
92411022STom.Erickson@Sun.COM 				if (err != ENOENT)
92511022STom.Erickson@Sun.COM 					break;
92611022STom.Erickson@Sun.COM 
92711022STom.Erickson@Sun.COM 				/* Skip if explicitly inherited. */
92811022STom.Erickson@Sun.COM 				valstr = kmem_asprintf("%s%s", propname,
92911022STom.Erickson@Sun.COM 				    ZPROP_INHERIT_SUFFIX);
93011022STom.Erickson@Sun.COM 				err = zap_contains(mos, propobj, valstr);
93111022STom.Erickson@Sun.COM 				strfree(valstr);
93211022STom.Erickson@Sun.COM 				if (err == 0)
93311022STom.Erickson@Sun.COM 					continue;
93411022STom.Erickson@Sun.COM 				if (err != ENOENT)
93511022STom.Erickson@Sun.COM 					break;
93611022STom.Erickson@Sun.COM 			}
93711022STom.Erickson@Sun.COM 
93811022STom.Erickson@Sun.COM 			source = ((flags & DSL_PROP_GET_INHERITING) ?
93911022STom.Erickson@Sun.COM 			    setpoint : ZPROP_SOURCE_VAL_RECVD);
94011022STom.Erickson@Sun.COM 		} else {
94111022STom.Erickson@Sun.COM 			/*
94211022STom.Erickson@Sun.COM 			 * For backward compatibility, skip suffixes we don't
94311022STom.Erickson@Sun.COM 			 * recognize.
94411022STom.Erickson@Sun.COM 			 */
94511022STom.Erickson@Sun.COM 			continue;
94611022STom.Erickson@Sun.COM 		}
94711022STom.Erickson@Sun.COM 
94811022STom.Erickson@Sun.COM 		prop = zfs_name_to_prop(propname);
94911022STom.Erickson@Sun.COM 
95011022STom.Erickson@Sun.COM 		/* Skip non-inheritable properties. */
95111022STom.Erickson@Sun.COM 		if ((flags & DSL_PROP_GET_INHERITING) && prop != ZPROP_INVAL &&
95211022STom.Erickson@Sun.COM 		    !zfs_prop_inheritable(prop))
95311022STom.Erickson@Sun.COM 			continue;
95411022STom.Erickson@Sun.COM 
95511022STom.Erickson@Sun.COM 		/* Skip properties not valid for this type. */
95611022STom.Erickson@Sun.COM 		if ((flags & DSL_PROP_GET_SNAPSHOT) && prop != ZPROP_INVAL &&
95711022STom.Erickson@Sun.COM 		    !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
95811022STom.Erickson@Sun.COM 			continue;
95911022STom.Erickson@Sun.COM 
96011022STom.Erickson@Sun.COM 		/* Skip properties already defined. */
96111022STom.Erickson@Sun.COM 		if (nvlist_exists(nv, propname))
96211022STom.Erickson@Sun.COM 			continue;
96311022STom.Erickson@Sun.COM 
96411022STom.Erickson@Sun.COM 		VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
96511022STom.Erickson@Sun.COM 		if (za.za_integer_length == 1) {
96611022STom.Erickson@Sun.COM 			/*
96711022STom.Erickson@Sun.COM 			 * String property
96811022STom.Erickson@Sun.COM 			 */
96911022STom.Erickson@Sun.COM 			char *tmp = kmem_alloc(za.za_num_integers,
97011022STom.Erickson@Sun.COM 			    KM_SLEEP);
97111022STom.Erickson@Sun.COM 			err = zap_lookup(mos, propobj,
97211022STom.Erickson@Sun.COM 			    za.za_name, 1, za.za_num_integers, tmp);
97311022STom.Erickson@Sun.COM 			if (err != 0) {
97411022STom.Erickson@Sun.COM 				kmem_free(tmp, za.za_num_integers);
97511022STom.Erickson@Sun.COM 				break;
97611022STom.Erickson@Sun.COM 			}
97711022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_string(propval, ZPROP_VALUE,
97811022STom.Erickson@Sun.COM 			    tmp) == 0);
97911022STom.Erickson@Sun.COM 			kmem_free(tmp, za.za_num_integers);
98011022STom.Erickson@Sun.COM 		} else {
98111022STom.Erickson@Sun.COM 			/*
98211022STom.Erickson@Sun.COM 			 * Integer property
98311022STom.Erickson@Sun.COM 			 */
98411022STom.Erickson@Sun.COM 			ASSERT(za.za_integer_length == 8);
98511022STom.Erickson@Sun.COM 			(void) nvlist_add_uint64(propval, ZPROP_VALUE,
98611022STom.Erickson@Sun.COM 			    za.za_first_integer);
98711022STom.Erickson@Sun.COM 		}
98811022STom.Erickson@Sun.COM 
98911022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0);
99011022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
99111022STom.Erickson@Sun.COM 		nvlist_free(propval);
99211022STom.Erickson@Sun.COM 	}
99311022STom.Erickson@Sun.COM 	zap_cursor_fini(&zc);
99411022STom.Erickson@Sun.COM 	if (err == ENOENT)
99511022STom.Erickson@Sun.COM 		err = 0;
99611022STom.Erickson@Sun.COM 	return (err);
99711022STom.Erickson@Sun.COM }
99811022STom.Erickson@Sun.COM 
9991356Seschrock /*
10001356Seschrock  * Iterate over all properties for this dataset and return them in an nvlist.
10011356Seschrock  */
100211022STom.Erickson@Sun.COM static int
dsl_prop_get_all_ds(dsl_dataset_t * ds,nvlist_t ** nvp,dsl_prop_getflags_t flags)100311022STom.Erickson@Sun.COM dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp,
100411022STom.Erickson@Sun.COM     dsl_prop_getflags_t flags)
10051356Seschrock {
10062082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
10077265Sahrens 	dsl_pool_t *dp = dd->dd_pool;
10087265Sahrens 	objset_t *mos = dp->dp_meta_objset;
100911022STom.Erickson@Sun.COM 	int err = 0;
101011022STom.Erickson@Sun.COM 	char setpoint[MAXNAMELEN];
10111356Seschrock 
10121356Seschrock 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
10131356Seschrock 
101411022STom.Erickson@Sun.COM 	if (dsl_dataset_is_snapshot(ds))
101511022STom.Erickson@Sun.COM 		flags |= DSL_PROP_GET_SNAPSHOT;
10161356Seschrock 
10171356Seschrock 	rw_enter(&dp->dp_config_rwlock, RW_READER);
10182885Sahrens 
101911022STom.Erickson@Sun.COM 	if (ds->ds_phys->ds_props_obj != 0) {
102011022STom.Erickson@Sun.COM 		ASSERT(flags & DSL_PROP_GET_SNAPSHOT);
102111022STom.Erickson@Sun.COM 		dsl_dataset_name(ds, setpoint);
102211022STom.Erickson@Sun.COM 		err = dsl_prop_get_all_impl(mos, ds->ds_phys->ds_props_obj,
102311022STom.Erickson@Sun.COM 		    setpoint, flags, *nvp);
102411022STom.Erickson@Sun.COM 		if (err)
102511022STom.Erickson@Sun.COM 			goto out;
102611022STom.Erickson@Sun.COM 	}
10271356Seschrock 
102811022STom.Erickson@Sun.COM 	for (; dd != NULL; dd = dd->dd_parent) {
102911022STom.Erickson@Sun.COM 		if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) {
103011022STom.Erickson@Sun.COM 			if (flags & (DSL_PROP_GET_LOCAL |
103111022STom.Erickson@Sun.COM 			    DSL_PROP_GET_RECEIVED))
103211022STom.Erickson@Sun.COM 				break;
103311022STom.Erickson@Sun.COM 			flags |= DSL_PROP_GET_INHERITING;
103411022STom.Erickson@Sun.COM 		}
103511022STom.Erickson@Sun.COM 		dsl_dir_name(dd, setpoint);
103611022STom.Erickson@Sun.COM 		err = dsl_prop_get_all_impl(mos, dd->dd_phys->dd_props_zapobj,
103711022STom.Erickson@Sun.COM 		    setpoint, flags, *nvp);
103811022STom.Erickson@Sun.COM 		if (err)
103911022STom.Erickson@Sun.COM 			break;
104011022STom.Erickson@Sun.COM 	}
104111022STom.Erickson@Sun.COM out:
104211022STom.Erickson@Sun.COM 	rw_exit(&dp->dp_config_rwlock);
104311022STom.Erickson@Sun.COM 	return (err);
104411022STom.Erickson@Sun.COM }
10457265Sahrens 
104611022STom.Erickson@Sun.COM boolean_t
dsl_prop_get_hasrecvd(objset_t * os)104711022STom.Erickson@Sun.COM dsl_prop_get_hasrecvd(objset_t *os)
104811022STom.Erickson@Sun.COM {
104911022STom.Erickson@Sun.COM 	dsl_dataset_t *ds = os->os_dsl_dataset;
105011022STom.Erickson@Sun.COM 	int rc;
105111022STom.Erickson@Sun.COM 	uint64_t dummy;
10521356Seschrock 
105311022STom.Erickson@Sun.COM 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
105411022STom.Erickson@Sun.COM 	rc = dsl_prop_get_ds(ds, ZPROP_HAS_RECVD, 8, 1, &dummy, NULL);
105511022STom.Erickson@Sun.COM 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
105611022STom.Erickson@Sun.COM 	ASSERT(rc != 0 || spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS);
105711022STom.Erickson@Sun.COM 	return (rc == 0);
105811022STom.Erickson@Sun.COM }
10592676Seschrock 
106011022STom.Erickson@Sun.COM static void
dsl_prop_set_hasrecvd_impl(objset_t * os,zprop_source_t source)106111022STom.Erickson@Sun.COM dsl_prop_set_hasrecvd_impl(objset_t *os, zprop_source_t source)
106211022STom.Erickson@Sun.COM {
106311022STom.Erickson@Sun.COM 	dsl_dataset_t *ds = os->os_dsl_dataset;
106411022STom.Erickson@Sun.COM 	uint64_t dummy = 0;
106511022STom.Erickson@Sun.COM 	dsl_prop_setarg_t psa;
106611022STom.Erickson@Sun.COM 
106711022STom.Erickson@Sun.COM 	if (spa_version(os->os_spa) < SPA_VERSION_RECVD_PROPS)
106811022STom.Erickson@Sun.COM 		return;
106911022STom.Erickson@Sun.COM 
107011022STom.Erickson@Sun.COM 	dsl_prop_setarg_init_uint64(&psa, ZPROP_HAS_RECVD, source, &dummy);
107111022STom.Erickson@Sun.COM 
107211022STom.Erickson@Sun.COM 	(void) dsl_sync_task_do(ds->ds_dir->dd_pool, NULL,
107311022STom.Erickson@Sun.COM 	    dsl_prop_set_sync, ds, &psa, 2);
107411022STom.Erickson@Sun.COM }
10751356Seschrock 
107611022STom.Erickson@Sun.COM /*
107711022STom.Erickson@Sun.COM  * Call after successfully receiving properties to ensure that only the first
107811022STom.Erickson@Sun.COM  * receive on or after SPA_VERSION_RECVD_PROPS blows away local properties.
107911022STom.Erickson@Sun.COM  */
108011022STom.Erickson@Sun.COM void
dsl_prop_set_hasrecvd(objset_t * os)108111022STom.Erickson@Sun.COM dsl_prop_set_hasrecvd(objset_t *os)
108211022STom.Erickson@Sun.COM {
108311022STom.Erickson@Sun.COM 	if (dsl_prop_get_hasrecvd(os)) {
108411022STom.Erickson@Sun.COM 		ASSERT(spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS);
108511022STom.Erickson@Sun.COM 		return;
108611022STom.Erickson@Sun.COM 	}
108711022STom.Erickson@Sun.COM 	dsl_prop_set_hasrecvd_impl(os, ZPROP_SRC_LOCAL);
108811022STom.Erickson@Sun.COM }
10891356Seschrock 
109011022STom.Erickson@Sun.COM void
dsl_prop_unset_hasrecvd(objset_t * os)109111022STom.Erickson@Sun.COM dsl_prop_unset_hasrecvd(objset_t *os)
109211022STom.Erickson@Sun.COM {
109311022STom.Erickson@Sun.COM 	dsl_prop_set_hasrecvd_impl(os, ZPROP_SRC_NONE);
109411022STom.Erickson@Sun.COM }
109511022STom.Erickson@Sun.COM 
109611022STom.Erickson@Sun.COM int
dsl_prop_get_all(objset_t * os,nvlist_t ** nvp)109711022STom.Erickson@Sun.COM dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
109811022STom.Erickson@Sun.COM {
109911022STom.Erickson@Sun.COM 	return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, 0));
110011022STom.Erickson@Sun.COM }
11011356Seschrock 
110211022STom.Erickson@Sun.COM int
dsl_prop_get_received(objset_t * os,nvlist_t ** nvp)110311022STom.Erickson@Sun.COM dsl_prop_get_received(objset_t *os, nvlist_t **nvp)
110411022STom.Erickson@Sun.COM {
110511022STom.Erickson@Sun.COM 	/*
110611022STom.Erickson@Sun.COM 	 * Received properties are not distinguishable from local properties
110711022STom.Erickson@Sun.COM 	 * until the dataset has received properties on or after
110811022STom.Erickson@Sun.COM 	 * SPA_VERSION_RECVD_PROPS.
110911022STom.Erickson@Sun.COM 	 */
111011022STom.Erickson@Sun.COM 	dsl_prop_getflags_t flags = (dsl_prop_get_hasrecvd(os) ?
111111022STom.Erickson@Sun.COM 	    DSL_PROP_GET_RECEIVED : DSL_PROP_GET_LOCAL);
111211022STom.Erickson@Sun.COM 	return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, flags));
11131356Seschrock }
11142885Sahrens 
11152885Sahrens void
dsl_prop_nvlist_add_uint64(nvlist_t * nv,zfs_prop_t prop,uint64_t value)11162885Sahrens dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)
11172885Sahrens {
11182885Sahrens 	nvlist_t *propval;
111911022STom.Erickson@Sun.COM 	const char *propname = zfs_prop_to_name(prop);
112011022STom.Erickson@Sun.COM 	uint64_t default_value;
112111022STom.Erickson@Sun.COM 
112211022STom.Erickson@Sun.COM 	if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {
112311022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
112411022STom.Erickson@Sun.COM 		return;
112511022STom.Erickson@Sun.COM 	}
11262885Sahrens 
11272885Sahrens 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11285094Slling 	VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
112911022STom.Erickson@Sun.COM 	/* Indicate the default source if we can. */
113011022STom.Erickson@Sun.COM 	if (dodefault(propname, 8, 1, &default_value) == 0 &&
113111022STom.Erickson@Sun.COM 	    value == default_value) {
113211022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, "") == 0);
113311022STom.Erickson@Sun.COM 	}
113411022STom.Erickson@Sun.COM 	VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
11352885Sahrens 	nvlist_free(propval);
11362885Sahrens }
11372885Sahrens 
11382885Sahrens void
dsl_prop_nvlist_add_string(nvlist_t * nv,zfs_prop_t prop,const char * value)11392885Sahrens dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)
11402885Sahrens {
11412885Sahrens 	nvlist_t *propval;
114211022STom.Erickson@Sun.COM 	const char *propname = zfs_prop_to_name(prop);
114311022STom.Erickson@Sun.COM 
114411022STom.Erickson@Sun.COM 	if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {
114511022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
114611022STom.Erickson@Sun.COM 		return;
114711022STom.Erickson@Sun.COM 	}
11482885Sahrens 
11492885Sahrens 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11505094Slling 	VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
115111022STom.Erickson@Sun.COM 	VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
11522885Sahrens 	nvlist_free(propval);
11532885Sahrens }
1154