]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / cddl / contrib / opensolaris / uts / common / fs / zfs / dsl_prop.c
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
42 static int
43 dodefault(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
65 static int
66 dsl_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  */
112 int
113 dsl_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
152 int
153 dsl_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
165 int
166 dsl_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  */
195 int
196 dsl_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  */
206 int
207 dsl_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  */
241 int
242 dsl_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
259 static void
260 dsl_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
310 struct prop_set_arg {
311         const char *name;
312         int intsz;
313         int numints;
314         const void *buf;
315 };
316
317
318 static void
319 dsl_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
350 int
351 dsl_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
365 int
366 dsl_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  */
392 int
393 dsl_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
481 void
482 dsl_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
492 void
493 dsl_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 }