dsl_prop.c revision 168404
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/dmu.h>
29#include <sys/dmu_objset.h>
30#include <sys/dmu_tx.h>
31#include <sys/dsl_dataset.h>
32#include <sys/dsl_dir.h>
33#include <sys/dsl_prop.h>
34#include <sys/dsl_synctask.h>
35#include <sys/spa.h>
36#include <sys/zio_checksum.h> /* for the default checksum value */
37#include <sys/zap.h>
38#include <sys/fs/zfs.h>
39
40#include "zfs_prop.h"
41
42static int
43dodefault(const char *propname, int intsz, int numint, void *buf)
44{
45	zfs_prop_t prop;
46
47	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL ||
48	    zfs_prop_readonly(prop))
49		return (ENOENT);
50
51	if (zfs_prop_get_type(prop) == prop_type_string) {
52		if (intsz != 1)
53			return (EOVERFLOW);
54		(void) strncpy(buf, zfs_prop_default_string(prop), numint);
55	} else {
56		if (intsz != 8 || numint < 1)
57			return (EOVERFLOW);
58
59		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
60	}
61
62	return (0);
63}
64
65static int
66dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
67    int intsz, int numint, void *buf, char *setpoint)
68{
69	int err = ENOENT;
70	zfs_prop_t prop;
71
72	if (setpoint)
73		setpoint[0] = '\0';
74
75	prop = zfs_name_to_prop(propname);
76
77	/*
78	 * Note: dd may be NULL, therefore we shouldn't dereference it
79	 * ouside this loop.
80	 */
81	for (; dd != NULL; dd = dd->dd_parent) {
82		objset_t *mos = dd->dd_pool->dp_meta_objset;
83		ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
84		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
85		    propname, intsz, numint, buf);
86		if (err != ENOENT) {
87			if (setpoint)
88				dsl_dir_name(dd, setpoint);
89			break;
90		}
91
92		/*
93		 * Break out of this loop for non-inheritable properties.
94		 */
95		if (prop != ZFS_PROP_INVAL &&
96		    !zfs_prop_inheritable(prop))
97			break;
98	}
99	if (err == ENOENT)
100		err = dodefault(propname, intsz, numint, buf);
101
102	return (err);
103}
104
105/*
106 * Register interest in the named property.  We'll call the callback
107 * once to notify it of the current property value, and again each time
108 * the property changes, until this callback is unregistered.
109 *
110 * Return 0 on success, errno if the prop is not an integer value.
111 */
112int
113dsl_prop_register(dsl_dataset_t *ds, const char *propname,
114    dsl_prop_changed_cb_t *callback, void *cbarg)
115{
116	dsl_dir_t *dd = ds->ds_dir;
117	uint64_t value;
118	dsl_prop_cb_record_t *cbr;
119	int err;
120	int need_rwlock;
121
122	need_rwlock = !RW_WRITE_HELD(&dd->dd_pool->dp_config_rwlock);
123	if (need_rwlock)
124		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
125
126	err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL);
127	if (err != 0) {
128		rw_exit(&dd->dd_pool->dp_config_rwlock);
129		return (err);
130	}
131
132	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
133	cbr->cbr_ds = ds;
134	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
135	(void) strcpy((char *)cbr->cbr_propname, propname);
136	cbr->cbr_func = callback;
137	cbr->cbr_arg = cbarg;
138	mutex_enter(&dd->dd_lock);
139	list_insert_head(&dd->dd_prop_cbs, cbr);
140	mutex_exit(&dd->dd_lock);
141
142	cbr->cbr_func(cbr->cbr_arg, value);
143
144	VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object,
145	    NULL, cbr, &dd));
146	if (need_rwlock)
147		rw_exit(&dd->dd_pool->dp_config_rwlock);
148	/* Leave dataset open until this callback is unregistered */
149	return (0);
150}
151
152int
153dsl_prop_get_ds(dsl_dir_t *dd, const char *propname,
154    int intsz, int numints, void *buf, char *setpoint)
155{
156	int err;
157
158	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
159	err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint);
160	rw_exit(&dd->dd_pool->dp_config_rwlock);
161
162	return (err);
163}
164
165int
166dsl_prop_get(const char *ddname, const char *propname,
167    int intsz, int numints, void *buf, char *setpoint)
168{
169	dsl_dir_t *dd;
170	const char *tail;
171	int err;
172
173	err = dsl_dir_open(ddname, FTAG, &dd, &tail);
174	if (err)
175		return (err);
176	if (tail && tail[0] != '@') {
177		dsl_dir_close(dd, FTAG);
178		return (ENOENT);
179	}
180
181	err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint);
182
183	dsl_dir_close(dd, FTAG);
184	return (err);
185}
186
187/*
188 * Get the current property value.  It may have changed by the time this
189 * function returns, so it is NOT safe to follow up with
190 * dsl_prop_register() and assume that the value has not changed in
191 * between.
192 *
193 * Return 0 on success, ENOENT if ddname is invalid.
194 */
195int
196dsl_prop_get_integer(const char *ddname, const char *propname,
197    uint64_t *valuep, char *setpoint)
198{
199	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
200}
201
202/*
203 * Unregister this callback.  Return 0 on success, ENOENT if ddname is
204 * invalid, ENOMSG if no matching callback registered.
205 */
206int
207dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
208    dsl_prop_changed_cb_t *callback, void *cbarg)
209{
210	dsl_dir_t *dd = ds->ds_dir;
211	dsl_prop_cb_record_t *cbr;
212
213	mutex_enter(&dd->dd_lock);
214	for (cbr = list_head(&dd->dd_prop_cbs);
215	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
216		if (cbr->cbr_ds == ds &&
217		    cbr->cbr_func == callback &&
218		    cbr->cbr_arg == cbarg &&
219		    strcmp(cbr->cbr_propname, propname) == 0)
220			break;
221	}
222
223	if (cbr == NULL) {
224		mutex_exit(&dd->dd_lock);
225		return (ENOMSG);
226	}
227
228	list_remove(&dd->dd_prop_cbs, cbr);
229	mutex_exit(&dd->dd_lock);
230	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
231	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
232
233	/* Clean up from dsl_prop_register */
234	dsl_dir_close(dd, cbr);
235	return (0);
236}
237
238/*
239 * Return the number of callbacks that are registered for this dataset.
240 */
241int
242dsl_prop_numcb(dsl_dataset_t *ds)
243{
244	dsl_dir_t *dd = ds->ds_dir;
245	dsl_prop_cb_record_t *cbr;
246	int num = 0;
247
248	mutex_enter(&dd->dd_lock);
249	for (cbr = list_head(&dd->dd_prop_cbs);
250	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
251		if (cbr->cbr_ds == ds)
252			num++;
253	}
254	mutex_exit(&dd->dd_lock);
255
256	return (num);
257}
258
259static void
260dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
261    const char *propname, uint64_t value, int first)
262{
263	dsl_dir_t *dd;
264	dsl_prop_cb_record_t *cbr;
265	objset_t *mos = dp->dp_meta_objset;
266	zap_cursor_t zc;
267	zap_attribute_t za;
268	int err;
269
270	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
271	err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
272	if (err)
273		return;
274
275	if (!first) {
276		/*
277		 * If the prop is set here, then this change is not
278		 * being inherited here or below; stop the recursion.
279		 */
280		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
281		    8, 1, &value);
282		if (err == 0) {
283			dsl_dir_close(dd, FTAG);
284			return;
285		}
286		ASSERT3U(err, ==, ENOENT);
287	}
288
289	mutex_enter(&dd->dd_lock);
290	for (cbr = list_head(&dd->dd_prop_cbs);
291	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
292		if (strcmp(cbr->cbr_propname, propname) == 0) {
293			cbr->cbr_func(cbr->cbr_arg, value);
294		}
295	}
296	mutex_exit(&dd->dd_lock);
297
298	for (zap_cursor_init(&zc, mos,
299	    dd->dd_phys->dd_child_dir_zapobj);
300	    zap_cursor_retrieve(&zc, &za) == 0;
301	    zap_cursor_advance(&zc)) {
302		/* XXX recursion could blow stack; esp. za! */
303		dsl_prop_changed_notify(dp, za.za_first_integer,
304		    propname, value, FALSE);
305	}
306	zap_cursor_fini(&zc);
307	dsl_dir_close(dd, FTAG);
308}
309
310struct prop_set_arg {
311	const char *name;
312	int intsz;
313	int numints;
314	const void *buf;
315};
316
317
318static void
319dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
320{
321	dsl_dir_t *dd = arg1;
322	struct prop_set_arg *psa = arg2;
323	objset_t *mos = dd->dd_pool->dp_meta_objset;
324	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
325	uint64_t intval;
326	int isint;
327
328	isint = (dodefault(psa->name, 8, 1, &intval) == 0);
329
330	if (psa->numints == 0) {
331		int err = zap_remove(mos, zapobj, psa->name, tx);
332		ASSERT(err == 0 || err == ENOENT);
333		if (isint) {
334			VERIFY(0 == dsl_prop_get_impl(dd->dd_parent,
335			    psa->name, 8, 1, &intval, NULL));
336		}
337	} else {
338		VERIFY(0 == zap_update(mos, zapobj, psa->name,
339		    psa->intsz, psa->numints, psa->buf, tx));
340		if (isint)
341			intval = *(uint64_t *)psa->buf;
342	}
343
344	if (isint) {
345		dsl_prop_changed_notify(dd->dd_pool,
346		    dd->dd_object, psa->name, intval, TRUE);
347	}
348}
349
350int
351dsl_prop_set_dd(dsl_dir_t *dd, const char *propname,
352    int intsz, int numints, const void *buf)
353{
354	struct prop_set_arg psa;
355
356	psa.name = propname;
357	psa.intsz = intsz;
358	psa.numints = numints;
359	psa.buf = buf;
360
361	return (dsl_sync_task_do(dd->dd_pool,
362	    NULL, dsl_prop_set_sync, dd, &psa, 2));
363}
364
365int
366dsl_prop_set(const char *ddname, const char *propname,
367    int intsz, int numints, const void *buf)
368{
369	dsl_dir_t *dd;
370	int err;
371
372	/*
373	 * We must do these checks before we get to the syncfunc, since
374	 * it can't fail.
375	 */
376	if (strlen(propname) >= ZAP_MAXNAMELEN)
377		return (ENAMETOOLONG);
378	if (intsz * numints >= ZAP_MAXVALUELEN)
379		return (E2BIG);
380
381	err = dsl_dir_open(ddname, FTAG, &dd, NULL);
382	if (err)
383		return (err);
384	err = dsl_prop_set_dd(dd, propname, intsz, numints, buf);
385	dsl_dir_close(dd, FTAG);
386	return (err);
387}
388
389/*
390 * Iterate over all properties for this dataset and return them in an nvlist.
391 */
392int
393dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
394{
395	dsl_dataset_t *ds = os->os->os_dsl_dataset;
396	dsl_dir_t *dd = ds->ds_dir;
397	int err = 0;
398	dsl_pool_t *dp;
399	objset_t *mos;
400
401	if (dsl_dataset_is_snapshot(ds)) {
402		VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
403		return (0);
404	}
405
406	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
407
408	dp = dd->dd_pool;
409	mos = dp->dp_meta_objset;
410
411	rw_enter(&dp->dp_config_rwlock, RW_READER);
412	for (; dd != NULL; dd = dd->dd_parent) {
413		char setpoint[MAXNAMELEN];
414		zap_cursor_t zc;
415		zap_attribute_t za;
416
417		dsl_dir_name(dd, setpoint);
418
419		for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj);
420		    (err = zap_cursor_retrieve(&zc, &za)) == 0;
421		    zap_cursor_advance(&zc)) {
422			nvlist_t *propval;
423			zfs_prop_t prop;
424			/*
425			 * Skip non-inheritable properties.
426			 */
427			if ((prop = zfs_name_to_prop(za.za_name)) !=
428			    ZFS_PROP_INVAL && !zfs_prop_inheritable(prop) &&
429			    dd != ds->ds_dir)
430				continue;
431
432			if (nvlist_lookup_nvlist(*nvp, za.za_name,
433			    &propval) == 0)
434				continue;
435
436			VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME,
437			    KM_SLEEP) == 0);
438			if (za.za_integer_length == 1) {
439				/*
440				 * String property
441				 */
442				char *tmp = kmem_alloc(za.za_num_integers,
443				    KM_SLEEP);
444				err = zap_lookup(mos,
445				    dd->dd_phys->dd_props_zapobj,
446				    za.za_name, 1, za.za_num_integers,
447				    tmp);
448				if (err != 0) {
449					kmem_free(tmp, za.za_num_integers);
450					break;
451				}
452				VERIFY(nvlist_add_string(propval,
453				    ZFS_PROP_VALUE, tmp) == 0);
454				kmem_free(tmp, za.za_num_integers);
455			} else {
456				/*
457				 * Integer property
458				 */
459				ASSERT(za.za_integer_length == 8);
460				(void) nvlist_add_uint64(propval,
461				    ZFS_PROP_VALUE, za.za_first_integer);
462			}
463
464			VERIFY(nvlist_add_string(propval,
465			    ZFS_PROP_SOURCE, setpoint) == 0);
466			VERIFY(nvlist_add_nvlist(*nvp, za.za_name,
467			    propval) == 0);
468			nvlist_free(propval);
469		}
470		zap_cursor_fini(&zc);
471
472		if (err != ENOENT)
473			break;
474		err = 0;
475	}
476	rw_exit(&dp->dp_config_rwlock);
477
478	return (err);
479}
480
481void
482dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)
483{
484	nvlist_t *propval;
485
486	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
487	VERIFY(nvlist_add_uint64(propval, ZFS_PROP_VALUE, value) == 0);
488	VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
489	nvlist_free(propval);
490}
491
492void
493dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)
494{
495	nvlist_t *propval;
496
497	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
498	VERIFY(nvlist_add_string(propval, ZFS_PROP_VALUE, value) == 0);
499	VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
500	nvlist_free(propval);
501}
502