xref: /illumos-gate/usr/src/uts/common/fs/zfs/dsl_prop.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
1*fa9e4066Sahrens /*
2*fa9e4066Sahrens  * CDDL HEADER START
3*fa9e4066Sahrens  *
4*fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5*fa9e4066Sahrens  * Common Development and Distribution License, Version 1.0 only
6*fa9e4066Sahrens  * (the "License").  You may not use this file except in compliance
7*fa9e4066Sahrens  * with the License.
8*fa9e4066Sahrens  *
9*fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
11*fa9e4066Sahrens  * See the License for the specific language governing permissions
12*fa9e4066Sahrens  * and limitations under the License.
13*fa9e4066Sahrens  *
14*fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15*fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17*fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18*fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19*fa9e4066Sahrens  *
20*fa9e4066Sahrens  * CDDL HEADER END
21*fa9e4066Sahrens  */
22*fa9e4066Sahrens /*
23*fa9e4066Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*fa9e4066Sahrens  * Use is subject to license terms.
25*fa9e4066Sahrens  */
26*fa9e4066Sahrens 
27*fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*fa9e4066Sahrens 
29*fa9e4066Sahrens #include <sys/dmu.h>
30*fa9e4066Sahrens #include <sys/dmu_tx.h>
31*fa9e4066Sahrens #include <sys/dsl_dataset.h>
32*fa9e4066Sahrens #include <sys/dsl_dir.h>
33*fa9e4066Sahrens #include <sys/dsl_prop.h>
34*fa9e4066Sahrens #include <sys/spa.h>
35*fa9e4066Sahrens #include <sys/zio_checksum.h> /* for the default checksum value */
36*fa9e4066Sahrens #include <sys/zap.h>
37*fa9e4066Sahrens #include <sys/fs/zfs.h>
38*fa9e4066Sahrens 
39*fa9e4066Sahrens #include "zfs_prop.h"
40*fa9e4066Sahrens 
41*fa9e4066Sahrens static int
42*fa9e4066Sahrens dodefault(const char *propname, int intsz, int numint, void *buf)
43*fa9e4066Sahrens {
44*fa9e4066Sahrens 	zfs_prop_t prop;
45*fa9e4066Sahrens 
46*fa9e4066Sahrens 	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL ||
47*fa9e4066Sahrens 	    zfs_prop_readonly(prop))
48*fa9e4066Sahrens 		return (ENOENT);
49*fa9e4066Sahrens 
50*fa9e4066Sahrens 	if (zfs_prop_get_type(prop) == prop_type_string) {
51*fa9e4066Sahrens 		if (intsz != 1)
52*fa9e4066Sahrens 			return (EOVERFLOW);
53*fa9e4066Sahrens 		zfs_prop_default_string(prop, buf, numint);
54*fa9e4066Sahrens 	} else {
55*fa9e4066Sahrens 		if (intsz != 8 || numint < 1)
56*fa9e4066Sahrens 			return (EOVERFLOW);
57*fa9e4066Sahrens 
58*fa9e4066Sahrens 		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
59*fa9e4066Sahrens 	}
60*fa9e4066Sahrens 
61*fa9e4066Sahrens 	return (0);
62*fa9e4066Sahrens }
63*fa9e4066Sahrens 
64*fa9e4066Sahrens static int
65*fa9e4066Sahrens dsl_prop_get_impl(dsl_pool_t *dp, uint64_t ddobj, const char *propname,
66*fa9e4066Sahrens     int intsz, int numint, void *buf, char *setpoint)
67*fa9e4066Sahrens {
68*fa9e4066Sahrens 	int err = 0;
69*fa9e4066Sahrens 	objset_t *mos = dp->dp_meta_objset;
70*fa9e4066Sahrens 
71*fa9e4066Sahrens 	if (setpoint)
72*fa9e4066Sahrens 		setpoint[0] = '\0';
73*fa9e4066Sahrens 
74*fa9e4066Sahrens 	ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock));
75*fa9e4066Sahrens 
76*fa9e4066Sahrens 	while (ddobj != 0) {
77*fa9e4066Sahrens 		dsl_dir_t *dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG);
78*fa9e4066Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
79*fa9e4066Sahrens 		    propname, intsz, numint, buf);
80*fa9e4066Sahrens 		if (err != ENOENT) {
81*fa9e4066Sahrens 			if (setpoint)
82*fa9e4066Sahrens 				dsl_dir_name(dd, setpoint);
83*fa9e4066Sahrens 			dsl_dir_close(dd, FTAG);
84*fa9e4066Sahrens 			break;
85*fa9e4066Sahrens 		}
86*fa9e4066Sahrens 		ASSERT3U(err, ==, ENOENT);
87*fa9e4066Sahrens 		ddobj = dd->dd_phys->dd_parent_obj;
88*fa9e4066Sahrens 		dsl_dir_close(dd, FTAG);
89*fa9e4066Sahrens 	}
90*fa9e4066Sahrens 	if (err == ENOENT)
91*fa9e4066Sahrens 		err = dodefault(propname, intsz, numint, buf);
92*fa9e4066Sahrens 
93*fa9e4066Sahrens 	return (err);
94*fa9e4066Sahrens }
95*fa9e4066Sahrens 
96*fa9e4066Sahrens /*
97*fa9e4066Sahrens  * Register interest in the named property.  We'll call the callback
98*fa9e4066Sahrens  * once to notify it of the current property value, and again each time
99*fa9e4066Sahrens  * the property changes, until this callback is unregistered.
100*fa9e4066Sahrens  *
101*fa9e4066Sahrens  * Return 0 on success, errno if the prop is not an integer value.
102*fa9e4066Sahrens  */
103*fa9e4066Sahrens int
104*fa9e4066Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname,
105*fa9e4066Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
106*fa9e4066Sahrens {
107*fa9e4066Sahrens 	dsl_dir_t *dd;
108*fa9e4066Sahrens 	uint64_t value;
109*fa9e4066Sahrens 	dsl_prop_cb_record_t *cbr;
110*fa9e4066Sahrens 	int err;
111*fa9e4066Sahrens 
112*fa9e4066Sahrens 	dd = ds->ds_dir;
113*fa9e4066Sahrens 
114*fa9e4066Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
115*fa9e4066Sahrens 
116*fa9e4066Sahrens 	err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, propname,
117*fa9e4066Sahrens 	    8, 1, &value, NULL);
118*fa9e4066Sahrens 	if (err == ENOENT) {
119*fa9e4066Sahrens 		err = 0;
120*fa9e4066Sahrens 		value = DSL_PROP_VALUE_UNDEFINED;
121*fa9e4066Sahrens 	}
122*fa9e4066Sahrens 	if (err != 0) {
123*fa9e4066Sahrens 		rw_exit(&dd->dd_pool->dp_config_rwlock);
124*fa9e4066Sahrens 		return (err);
125*fa9e4066Sahrens 	}
126*fa9e4066Sahrens 
127*fa9e4066Sahrens 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
128*fa9e4066Sahrens 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
129*fa9e4066Sahrens 	(void) strcpy((char *)cbr->cbr_propname, propname);
130*fa9e4066Sahrens 	cbr->cbr_func = callback;
131*fa9e4066Sahrens 	cbr->cbr_arg = cbarg;
132*fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
133*fa9e4066Sahrens 	list_insert_head(&dd->dd_prop_cbs, cbr);
134*fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
135*fa9e4066Sahrens 
136*fa9e4066Sahrens 	cbr->cbr_func(cbr->cbr_arg, value);
137*fa9e4066Sahrens 
138*fa9e4066Sahrens 	(void) dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, cbr);
139*fa9e4066Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
140*fa9e4066Sahrens 	/* Leave dataset open until this callback is unregistered */
141*fa9e4066Sahrens 	return (0);
142*fa9e4066Sahrens }
143*fa9e4066Sahrens 
144*fa9e4066Sahrens int
145*fa9e4066Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname,
146*fa9e4066Sahrens     int intsz, int numints, void *buf, char *setpoint)
147*fa9e4066Sahrens {
148*fa9e4066Sahrens 	int err;
149*fa9e4066Sahrens 
150*fa9e4066Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
151*fa9e4066Sahrens 	err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object,
152*fa9e4066Sahrens 	    propname, intsz, numints, buf, setpoint);
153*fa9e4066Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
154*fa9e4066Sahrens 
155*fa9e4066Sahrens 	return (err);
156*fa9e4066Sahrens }
157*fa9e4066Sahrens 
158*fa9e4066Sahrens int
159*fa9e4066Sahrens dsl_prop_get(const char *ddname, const char *propname,
160*fa9e4066Sahrens     int intsz, int numints, void *buf, char *setpoint)
161*fa9e4066Sahrens {
162*fa9e4066Sahrens 	dsl_dir_t *dd;
163*fa9e4066Sahrens 	const char *tail;
164*fa9e4066Sahrens 	int err;
165*fa9e4066Sahrens 
166*fa9e4066Sahrens 	dd = dsl_dir_open(ddname, FTAG, &tail);
167*fa9e4066Sahrens 	if (dd == NULL)
168*fa9e4066Sahrens 		return (ENOENT);
169*fa9e4066Sahrens 	if (tail && tail[0] != '@') {
170*fa9e4066Sahrens 		dsl_dir_close(dd, FTAG);
171*fa9e4066Sahrens 		return (ENOENT);
172*fa9e4066Sahrens 	}
173*fa9e4066Sahrens 
174*fa9e4066Sahrens 	err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint);
175*fa9e4066Sahrens 
176*fa9e4066Sahrens 	dsl_dir_close(dd, FTAG);
177*fa9e4066Sahrens 	return (err);
178*fa9e4066Sahrens }
179*fa9e4066Sahrens 
180*fa9e4066Sahrens /*
181*fa9e4066Sahrens  * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if
182*fa9e4066Sahrens  * valuelen not big enough.
183*fa9e4066Sahrens  */
184*fa9e4066Sahrens int
185*fa9e4066Sahrens dsl_prop_get_string(const char *ddname, const char *propname,
186*fa9e4066Sahrens     char *value, int valuelen, char *setpoint)
187*fa9e4066Sahrens {
188*fa9e4066Sahrens 	return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint));
189*fa9e4066Sahrens }
190*fa9e4066Sahrens 
191*fa9e4066Sahrens /*
192*fa9e4066Sahrens  * Get the current property value.  It may have changed by the time this
193*fa9e4066Sahrens  * function returns, so it is NOT safe to follow up with
194*fa9e4066Sahrens  * dsl_prop_register() and assume that the value has not changed in
195*fa9e4066Sahrens  * between.
196*fa9e4066Sahrens  *
197*fa9e4066Sahrens  * Return 0 on success, ENOENT if ddname is invalid.
198*fa9e4066Sahrens  */
199*fa9e4066Sahrens int
200*fa9e4066Sahrens dsl_prop_get_integer(const char *ddname, const char *propname,
201*fa9e4066Sahrens     uint64_t *valuep, char *setpoint)
202*fa9e4066Sahrens {
203*fa9e4066Sahrens 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
204*fa9e4066Sahrens }
205*fa9e4066Sahrens 
206*fa9e4066Sahrens int
207*fa9e4066Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname,
208*fa9e4066Sahrens     uint64_t *valuep, char *setpoint)
209*fa9e4066Sahrens {
210*fa9e4066Sahrens 	return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint));
211*fa9e4066Sahrens }
212*fa9e4066Sahrens 
213*fa9e4066Sahrens /*
214*fa9e4066Sahrens  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
215*fa9e4066Sahrens  * invalid, ENOMSG if no matching callback registered.
216*fa9e4066Sahrens  */
217*fa9e4066Sahrens int
218*fa9e4066Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
219*fa9e4066Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
220*fa9e4066Sahrens {
221*fa9e4066Sahrens 	dsl_dir_t *dd;
222*fa9e4066Sahrens 	dsl_prop_cb_record_t *cbr;
223*fa9e4066Sahrens 
224*fa9e4066Sahrens 	dd = ds->ds_dir;
225*fa9e4066Sahrens 
226*fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
227*fa9e4066Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
228*fa9e4066Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
229*fa9e4066Sahrens 		if (strcmp(cbr->cbr_propname, propname) == 0 &&
230*fa9e4066Sahrens 		    cbr->cbr_func == callback &&
231*fa9e4066Sahrens 		    cbr->cbr_arg == cbarg)
232*fa9e4066Sahrens 			break;
233*fa9e4066Sahrens 	}
234*fa9e4066Sahrens 
235*fa9e4066Sahrens 	if (cbr == NULL) {
236*fa9e4066Sahrens 		mutex_exit(&dd->dd_lock);
237*fa9e4066Sahrens 		return (ENOMSG);
238*fa9e4066Sahrens 	}
239*fa9e4066Sahrens 
240*fa9e4066Sahrens 	list_remove(&dd->dd_prop_cbs, cbr);
241*fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
242*fa9e4066Sahrens 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
243*fa9e4066Sahrens 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
244*fa9e4066Sahrens 
245*fa9e4066Sahrens 	/* Clean up from dsl_prop_register */
246*fa9e4066Sahrens 	dsl_dir_close(dd, cbr);
247*fa9e4066Sahrens 	return (0);
248*fa9e4066Sahrens }
249*fa9e4066Sahrens 
250*fa9e4066Sahrens static void
251*fa9e4066Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
252*fa9e4066Sahrens     const char *propname, uint64_t value, int first)
253*fa9e4066Sahrens {
254*fa9e4066Sahrens 	dsl_dir_t *dd;
255*fa9e4066Sahrens 	dsl_prop_cb_record_t *cbr;
256*fa9e4066Sahrens 	objset_t *mos = dp->dp_meta_objset;
257*fa9e4066Sahrens 	int err;
258*fa9e4066Sahrens 
259*fa9e4066Sahrens 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
260*fa9e4066Sahrens 	dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG);
261*fa9e4066Sahrens 
262*fa9e4066Sahrens 	if (!first) {
263*fa9e4066Sahrens 		/*
264*fa9e4066Sahrens 		 * If the prop is set here, then this change is not
265*fa9e4066Sahrens 		 * being inherited here or below; stop the recursion.
266*fa9e4066Sahrens 		 */
267*fa9e4066Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
268*fa9e4066Sahrens 		    8, 1, &value);
269*fa9e4066Sahrens 		if (err == 0) {
270*fa9e4066Sahrens 			dsl_dir_close(dd, FTAG);
271*fa9e4066Sahrens 			return;
272*fa9e4066Sahrens 		}
273*fa9e4066Sahrens 		ASSERT3U(err, ==, ENOENT);
274*fa9e4066Sahrens 	}
275*fa9e4066Sahrens 
276*fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
277*fa9e4066Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
278*fa9e4066Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
279*fa9e4066Sahrens 		if (strcmp(cbr->cbr_propname, propname) == 0) {
280*fa9e4066Sahrens 			cbr->cbr_func(cbr->cbr_arg, value);
281*fa9e4066Sahrens 		}
282*fa9e4066Sahrens 	}
283*fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
284*fa9e4066Sahrens 
285*fa9e4066Sahrens 	if (dd->dd_phys->dd_child_dir_zapobj) {
286*fa9e4066Sahrens 		zap_cursor_t zc;
287*fa9e4066Sahrens 		zap_attribute_t za;
288*fa9e4066Sahrens 
289*fa9e4066Sahrens 		for (zap_cursor_init(&zc, mos,
290*fa9e4066Sahrens 		    dd->dd_phys->dd_child_dir_zapobj);
291*fa9e4066Sahrens 		    zap_cursor_retrieve(&zc, &za) == 0;
292*fa9e4066Sahrens 		    zap_cursor_advance(&zc)) {
293*fa9e4066Sahrens 			/* XXX recursion could blow stack; esp. za! */
294*fa9e4066Sahrens 			dsl_prop_changed_notify(dp, za.za_first_integer,
295*fa9e4066Sahrens 			    propname, value, FALSE);
296*fa9e4066Sahrens 		}
297*fa9e4066Sahrens 	}
298*fa9e4066Sahrens 	dsl_dir_close(dd, FTAG);
299*fa9e4066Sahrens }
300*fa9e4066Sahrens 
301*fa9e4066Sahrens struct prop_set_arg {
302*fa9e4066Sahrens 	const char *name;
303*fa9e4066Sahrens 	int intsz;
304*fa9e4066Sahrens 	int numints;
305*fa9e4066Sahrens 	const void *buf;
306*fa9e4066Sahrens };
307*fa9e4066Sahrens 
308*fa9e4066Sahrens static int
309*fa9e4066Sahrens dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx)
310*fa9e4066Sahrens {
311*fa9e4066Sahrens 	struct prop_set_arg *psa = arg;
312*fa9e4066Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
313*fa9e4066Sahrens 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
314*fa9e4066Sahrens 	uint64_t intval;
315*fa9e4066Sahrens 	int err, isint;
316*fa9e4066Sahrens 
317*fa9e4066Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER);
318*fa9e4066Sahrens 
319*fa9e4066Sahrens 	isint = (dodefault(psa->name, 8, 1, &intval) == 0);
320*fa9e4066Sahrens 
321*fa9e4066Sahrens 	if (psa->numints == 0) {
322*fa9e4066Sahrens 		err = zap_remove(mos, zapobj, psa->name, tx);
323*fa9e4066Sahrens 		if (err == ENOENT) /* that's fine. */
324*fa9e4066Sahrens 			err = 0;
325*fa9e4066Sahrens 		if (err == 0 && isint) {
326*fa9e4066Sahrens 			err = dsl_prop_get_impl(dd->dd_pool,
327*fa9e4066Sahrens 			    dd->dd_phys->dd_parent_obj, psa->name,
328*fa9e4066Sahrens 			    8, 1, &intval, NULL);
329*fa9e4066Sahrens 		}
330*fa9e4066Sahrens 	} else {
331*fa9e4066Sahrens 		err = zap_update(mos, zapobj, psa->name,
332*fa9e4066Sahrens 		    psa->intsz, psa->numints, psa->buf, tx);
333*fa9e4066Sahrens 		if (isint)
334*fa9e4066Sahrens 			intval = *(uint64_t *)psa->buf;
335*fa9e4066Sahrens 	}
336*fa9e4066Sahrens 
337*fa9e4066Sahrens 	if (err == 0 && isint) {
338*fa9e4066Sahrens 		dsl_prop_changed_notify(dd->dd_pool,
339*fa9e4066Sahrens 		    dd->dd_object, psa->name, intval, TRUE);
340*fa9e4066Sahrens 	}
341*fa9e4066Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
342*fa9e4066Sahrens 
343*fa9e4066Sahrens 	return (err);
344*fa9e4066Sahrens }
345*fa9e4066Sahrens 
346*fa9e4066Sahrens int
347*fa9e4066Sahrens dsl_prop_set(const char *ddname, const char *propname,
348*fa9e4066Sahrens     int intsz, int numints, const void *buf)
349*fa9e4066Sahrens {
350*fa9e4066Sahrens 	dsl_dir_t *dd;
351*fa9e4066Sahrens 	int err;
352*fa9e4066Sahrens 	struct prop_set_arg psa;
353*fa9e4066Sahrens 
354*fa9e4066Sahrens 	dd = dsl_dir_open(ddname, FTAG, NULL);
355*fa9e4066Sahrens 	if (dd == NULL)
356*fa9e4066Sahrens 		return (ENOENT);
357*fa9e4066Sahrens 
358*fa9e4066Sahrens 	psa.name = propname;
359*fa9e4066Sahrens 	psa.intsz = intsz;
360*fa9e4066Sahrens 	psa.numints = numints;
361*fa9e4066Sahrens 	psa.buf = buf;
362*fa9e4066Sahrens 	err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 0);
363*fa9e4066Sahrens 
364*fa9e4066Sahrens 	dsl_dir_close(dd, FTAG);
365*fa9e4066Sahrens 
366*fa9e4066Sahrens 	return (err);
367*fa9e4066Sahrens }
368