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 /* 221356Seschrock * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <sys/dmu.h> 291356Seschrock #include <sys/dmu_objset.h> 30789Sahrens #include <sys/dmu_tx.h> 31789Sahrens #include <sys/dsl_dataset.h> 32789Sahrens #include <sys/dsl_dir.h> 33789Sahrens #include <sys/dsl_prop.h> 342199Sahrens #include <sys/dsl_synctask.h> 35789Sahrens #include <sys/spa.h> 36789Sahrens #include <sys/zio_checksum.h> /* for the default checksum value */ 37789Sahrens #include <sys/zap.h> 38789Sahrens #include <sys/fs/zfs.h> 39789Sahrens 40789Sahrens #include "zfs_prop.h" 41789Sahrens 42789Sahrens static int 43789Sahrens dodefault(const char *propname, int intsz, int numint, void *buf) 44789Sahrens { 45789Sahrens zfs_prop_t prop; 46789Sahrens 47789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL || 48789Sahrens zfs_prop_readonly(prop)) 49789Sahrens return (ENOENT); 50789Sahrens 51789Sahrens if (zfs_prop_get_type(prop) == prop_type_string) { 52789Sahrens if (intsz != 1) 53789Sahrens return (EOVERFLOW); 541356Seschrock (void) strncpy(buf, zfs_prop_default_string(prop), numint); 55789Sahrens } else { 56789Sahrens if (intsz != 8 || numint < 1) 57789Sahrens return (EOVERFLOW); 58789Sahrens 59789Sahrens *(uint64_t *)buf = zfs_prop_default_numeric(prop); 60789Sahrens } 61789Sahrens 62789Sahrens return (0); 63789Sahrens } 64789Sahrens 65789Sahrens static int 662082Seschrock dsl_prop_get_impl(dsl_dir_t *dd, const char *propname, 67789Sahrens int intsz, int numint, void *buf, char *setpoint) 68789Sahrens { 692082Seschrock int err = ENOENT; 70789Sahrens 71789Sahrens if (setpoint) 72789Sahrens setpoint[0] = '\0'; 73789Sahrens 742082Seschrock /* 752082Seschrock * Note: dd may be NULL, therefore we shouldn't dereference it 762082Seschrock * ouside this loop. 772082Seschrock */ 782082Seschrock for (; dd != NULL; dd = dd->dd_parent) { 792082Seschrock objset_t *mos = dd->dd_pool->dp_meta_objset; 802082Seschrock ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock)); 81789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 82789Sahrens propname, intsz, numint, buf); 83789Sahrens if (err != ENOENT) { 84789Sahrens if (setpoint) 85789Sahrens dsl_dir_name(dd, setpoint); 86789Sahrens break; 87789Sahrens } 88789Sahrens } 89789Sahrens if (err == ENOENT) 90789Sahrens err = dodefault(propname, intsz, numint, buf); 91789Sahrens 92789Sahrens return (err); 93789Sahrens } 94789Sahrens 95789Sahrens /* 96789Sahrens * Register interest in the named property. We'll call the callback 97789Sahrens * once to notify it of the current property value, and again each time 98789Sahrens * the property changes, until this callback is unregistered. 99789Sahrens * 100789Sahrens * Return 0 on success, errno if the prop is not an integer value. 101789Sahrens */ 102789Sahrens int 103789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname, 104789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 105789Sahrens { 1062082Seschrock dsl_dir_t *dd = ds->ds_dir; 107789Sahrens uint64_t value; 108789Sahrens dsl_prop_cb_record_t *cbr; 109789Sahrens int err; 1102199Sahrens int need_rwlock; 111789Sahrens 1122199Sahrens need_rwlock = !RW_WRITE_HELD(&dd->dd_pool->dp_config_rwlock); 1132199Sahrens if (need_rwlock) 1142199Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 115789Sahrens 1162082Seschrock err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL); 117789Sahrens if (err != 0) { 118789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 119789Sahrens return (err); 120789Sahrens } 121789Sahrens 122789Sahrens cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 1232082Seschrock cbr->cbr_ds = ds; 124789Sahrens cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 125789Sahrens (void) strcpy((char *)cbr->cbr_propname, propname); 126789Sahrens cbr->cbr_func = callback; 127789Sahrens cbr->cbr_arg = cbarg; 128789Sahrens mutex_enter(&dd->dd_lock); 129789Sahrens list_insert_head(&dd->dd_prop_cbs, cbr); 130789Sahrens mutex_exit(&dd->dd_lock); 131789Sahrens 132789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 133789Sahrens 1341544Seschrock VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object, 1351544Seschrock NULL, cbr, &dd)); 1362199Sahrens if (need_rwlock) 1372199Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 138789Sahrens /* Leave dataset open until this callback is unregistered */ 139789Sahrens return (0); 140789Sahrens } 141789Sahrens 142789Sahrens int 143789Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname, 144789Sahrens int intsz, int numints, void *buf, char *setpoint) 145789Sahrens { 146789Sahrens int err; 147789Sahrens 148789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 1492082Seschrock err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint); 150789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 151789Sahrens 152789Sahrens return (err); 153789Sahrens } 154789Sahrens 155789Sahrens int 156789Sahrens dsl_prop_get(const char *ddname, const char *propname, 157789Sahrens int intsz, int numints, void *buf, char *setpoint) 158789Sahrens { 159789Sahrens dsl_dir_t *dd; 160789Sahrens const char *tail; 161789Sahrens int err; 162789Sahrens 1631544Seschrock err = dsl_dir_open(ddname, FTAG, &dd, &tail); 1641544Seschrock if (err) 1651544Seschrock return (err); 166789Sahrens if (tail && tail[0] != '@') { 167789Sahrens dsl_dir_close(dd, FTAG); 168789Sahrens return (ENOENT); 169789Sahrens } 170789Sahrens 171789Sahrens err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 172789Sahrens 173789Sahrens dsl_dir_close(dd, FTAG); 174789Sahrens return (err); 175789Sahrens } 176789Sahrens 177789Sahrens /* 178789Sahrens * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if 179789Sahrens * valuelen not big enough. 180789Sahrens */ 181789Sahrens int 182789Sahrens dsl_prop_get_string(const char *ddname, const char *propname, 183789Sahrens char *value, int valuelen, char *setpoint) 184789Sahrens { 185789Sahrens return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint)); 186789Sahrens } 187789Sahrens 188789Sahrens /* 189789Sahrens * Get the current property value. It may have changed by the time this 190789Sahrens * function returns, so it is NOT safe to follow up with 191789Sahrens * dsl_prop_register() and assume that the value has not changed in 192789Sahrens * between. 193789Sahrens * 194789Sahrens * Return 0 on success, ENOENT if ddname is invalid. 195789Sahrens */ 196789Sahrens int 197789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname, 198789Sahrens uint64_t *valuep, char *setpoint) 199789Sahrens { 200789Sahrens return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 201789Sahrens } 202789Sahrens 203789Sahrens int 204789Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname, 205789Sahrens uint64_t *valuep, char *setpoint) 206789Sahrens { 207789Sahrens return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint)); 208789Sahrens } 209789Sahrens 210789Sahrens /* 211789Sahrens * Unregister this callback. Return 0 on success, ENOENT if ddname is 212789Sahrens * invalid, ENOMSG if no matching callback registered. 213789Sahrens */ 214789Sahrens int 215789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 216789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 217789Sahrens { 2182082Seschrock dsl_dir_t *dd = ds->ds_dir; 219789Sahrens dsl_prop_cb_record_t *cbr; 220789Sahrens 221789Sahrens mutex_enter(&dd->dd_lock); 222789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 223789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 2242082Seschrock if (cbr->cbr_ds == ds && 225789Sahrens cbr->cbr_func == callback && 2262082Seschrock cbr->cbr_arg == cbarg && 2272082Seschrock strcmp(cbr->cbr_propname, propname) == 0) 228789Sahrens break; 229789Sahrens } 230789Sahrens 231789Sahrens if (cbr == NULL) { 232789Sahrens mutex_exit(&dd->dd_lock); 233789Sahrens return (ENOMSG); 234789Sahrens } 235789Sahrens 236789Sahrens list_remove(&dd->dd_prop_cbs, cbr); 237789Sahrens mutex_exit(&dd->dd_lock); 238789Sahrens kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 239789Sahrens kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 240789Sahrens 241789Sahrens /* Clean up from dsl_prop_register */ 242789Sahrens dsl_dir_close(dd, cbr); 243789Sahrens return (0); 244789Sahrens } 245789Sahrens 2462082Seschrock /* 2472082Seschrock * Return the number of callbacks that are registered for this dataset. 2482082Seschrock */ 2492082Seschrock int 2502082Seschrock dsl_prop_numcb(dsl_dataset_t *ds) 2512082Seschrock { 2522082Seschrock dsl_dir_t *dd = ds->ds_dir; 2532082Seschrock dsl_prop_cb_record_t *cbr; 2542082Seschrock int num = 0; 2552082Seschrock 2562082Seschrock mutex_enter(&dd->dd_lock); 2572082Seschrock for (cbr = list_head(&dd->dd_prop_cbs); 2582082Seschrock cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 2592082Seschrock if (cbr->cbr_ds == ds) 2602082Seschrock num++; 2612082Seschrock } 2622082Seschrock mutex_exit(&dd->dd_lock); 2632082Seschrock 2642082Seschrock return (num); 2652082Seschrock } 2662082Seschrock 267789Sahrens static void 268789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 269789Sahrens const char *propname, uint64_t value, int first) 270789Sahrens { 271789Sahrens dsl_dir_t *dd; 272789Sahrens dsl_prop_cb_record_t *cbr; 273789Sahrens objset_t *mos = dp->dp_meta_objset; 2742199Sahrens zap_cursor_t zc; 2752199Sahrens zap_attribute_t za; 276789Sahrens int err; 277789Sahrens 278789Sahrens ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 2791544Seschrock err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd); 2801544Seschrock if (err) 2811544Seschrock return; 282789Sahrens 283789Sahrens if (!first) { 284789Sahrens /* 285789Sahrens * If the prop is set here, then this change is not 286789Sahrens * being inherited here or below; stop the recursion. 287789Sahrens */ 288789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 289789Sahrens 8, 1, &value); 290789Sahrens if (err == 0) { 291789Sahrens dsl_dir_close(dd, FTAG); 292789Sahrens return; 293789Sahrens } 294789Sahrens ASSERT3U(err, ==, ENOENT); 295789Sahrens } 296789Sahrens 297789Sahrens mutex_enter(&dd->dd_lock); 298789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 299789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 300789Sahrens if (strcmp(cbr->cbr_propname, propname) == 0) { 301789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 302789Sahrens } 303789Sahrens } 304789Sahrens mutex_exit(&dd->dd_lock); 305789Sahrens 3062199Sahrens for (zap_cursor_init(&zc, mos, 3072199Sahrens dd->dd_phys->dd_child_dir_zapobj); 3082199Sahrens zap_cursor_retrieve(&zc, &za) == 0; 3092199Sahrens zap_cursor_advance(&zc)) { 3102199Sahrens /* XXX recursion could blow stack; esp. za! */ 3112199Sahrens dsl_prop_changed_notify(dp, za.za_first_integer, 3122199Sahrens propname, value, FALSE); 313789Sahrens } 3142199Sahrens zap_cursor_fini(&zc); 315789Sahrens dsl_dir_close(dd, FTAG); 316789Sahrens } 317789Sahrens 318789Sahrens struct prop_set_arg { 319789Sahrens const char *name; 320789Sahrens int intsz; 321789Sahrens int numints; 322789Sahrens const void *buf; 323789Sahrens }; 324789Sahrens 3252199Sahrens 3262199Sahrens static void 3272199Sahrens dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx) 328789Sahrens { 3292199Sahrens dsl_dir_t *dd = arg1; 3302199Sahrens struct prop_set_arg *psa = arg2; 331789Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 332789Sahrens uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 333789Sahrens uint64_t intval; 3342199Sahrens int isint; 335789Sahrens 336789Sahrens isint = (dodefault(psa->name, 8, 1, &intval) == 0); 337789Sahrens 338789Sahrens if (psa->numints == 0) { 3392199Sahrens int err = zap_remove(mos, zapobj, psa->name, tx); 3402199Sahrens ASSERT(err == 0 || err == ENOENT); 3412199Sahrens if (isint) { 3422199Sahrens VERIFY(0 == dsl_prop_get_impl(dd->dd_parent, 3432199Sahrens psa->name, 8, 1, &intval, NULL)); 344789Sahrens } 345789Sahrens } else { 3462199Sahrens VERIFY(0 == zap_update(mos, zapobj, psa->name, 3472199Sahrens psa->intsz, psa->numints, psa->buf, tx)); 348789Sahrens if (isint) 349789Sahrens intval = *(uint64_t *)psa->buf; 350789Sahrens } 351789Sahrens 3522199Sahrens if (isint) { 353789Sahrens dsl_prop_changed_notify(dd->dd_pool, 354789Sahrens dd->dd_object, psa->name, intval, TRUE); 355789Sahrens } 356789Sahrens } 357789Sahrens 358789Sahrens int 359789Sahrens dsl_prop_set(const char *ddname, const char *propname, 360789Sahrens int intsz, int numints, const void *buf) 361789Sahrens { 362789Sahrens dsl_dir_t *dd; 363789Sahrens int err; 364789Sahrens struct prop_set_arg psa; 365789Sahrens 366*2641Sahrens /* 367*2641Sahrens * We must do these checks before we get to the syncfunc, since 368*2641Sahrens * it can't fail. 369*2641Sahrens */ 370*2641Sahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 371*2641Sahrens return (ENAMETOOLONG); 372*2641Sahrens if (intsz * numints >= ZAP_MAXVALUELEN) 373*2641Sahrens return (E2BIG); 374*2641Sahrens 3751544Seschrock err = dsl_dir_open(ddname, FTAG, &dd, NULL); 3761544Seschrock if (err) 3771544Seschrock return (err); 378789Sahrens 379789Sahrens psa.name = propname; 380789Sahrens psa.intsz = intsz; 381789Sahrens psa.numints = numints; 382789Sahrens psa.buf = buf; 3832199Sahrens err = dsl_sync_task_do(dd->dd_pool, 3842199Sahrens NULL, dsl_prop_set_sync, dd, &psa, 2); 385789Sahrens 386789Sahrens dsl_dir_close(dd, FTAG); 387789Sahrens 388789Sahrens return (err); 389789Sahrens } 3901356Seschrock 3911356Seschrock /* 3921356Seschrock * Iterate over all properties for this dataset and return them in an nvlist. 3931356Seschrock */ 3941356Seschrock int 3951356Seschrock dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 3961356Seschrock { 3971356Seschrock dsl_dataset_t *ds = os->os->os_dsl_dataset; 3982082Seschrock dsl_dir_t *dd = ds->ds_dir; 3991356Seschrock int err = 0; 4001356Seschrock dsl_pool_t *dp; 4011356Seschrock objset_t *mos; 4021356Seschrock zap_cursor_t zc; 4031356Seschrock zap_attribute_t za; 4041356Seschrock char setpoint[MAXNAMELEN]; 4051356Seschrock char *tmp; 4061356Seschrock nvlist_t *prop; 4071356Seschrock 4081356Seschrock if (dsl_dataset_is_snapshot(ds)) { 4091356Seschrock VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 4101356Seschrock return (0); 4111356Seschrock } 4121356Seschrock 4131356Seschrock VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 4141356Seschrock 4151356Seschrock dp = dd->dd_pool; 4161356Seschrock mos = dp->dp_meta_objset; 4171356Seschrock 4181356Seschrock rw_enter(&dp->dp_config_rwlock, RW_READER); 4192082Seschrock for (; dd != NULL; dd = dd->dd_parent) { 4201356Seschrock dsl_dir_name(dd, setpoint); 4211356Seschrock 4221356Seschrock for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); 4231356Seschrock (err = zap_cursor_retrieve(&zc, &za)) == 0; 4241356Seschrock zap_cursor_advance(&zc)) { 4251356Seschrock if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0) 4261356Seschrock continue; 4271356Seschrock 4281356Seschrock VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME, 4291356Seschrock KM_SLEEP) == 0); 4301356Seschrock if (za.za_integer_length == 1) { 4311356Seschrock /* 4321356Seschrock * String property 4331356Seschrock */ 4341356Seschrock tmp = kmem_alloc(za.za_num_integers, KM_SLEEP); 4351356Seschrock err = zap_lookup(mos, 4361356Seschrock dd->dd_phys->dd_props_zapobj, 4371356Seschrock za.za_name, 1, za.za_num_integers, 4381356Seschrock tmp); 4391356Seschrock if (err != 0) { 4401356Seschrock kmem_free(tmp, za.za_num_integers); 4411356Seschrock break; 4421356Seschrock } 4431356Seschrock VERIFY(nvlist_add_string(prop, 4441356Seschrock ZFS_PROP_VALUE, tmp) == 0); 4451356Seschrock kmem_free(tmp, za.za_num_integers); 4461356Seschrock } else { 4471356Seschrock /* 4481356Seschrock * Integer property 4491356Seschrock */ 4501356Seschrock ASSERT(za.za_integer_length == 8); 4511356Seschrock (void) nvlist_add_uint64(prop, ZFS_PROP_VALUE, 4521356Seschrock za.za_first_integer); 4531356Seschrock } 4541356Seschrock 4551356Seschrock VERIFY(nvlist_add_string(prop, 4561356Seschrock ZFS_PROP_SOURCE, setpoint) == 0); 4571356Seschrock VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 4581356Seschrock prop) == 0); 4591356Seschrock nvlist_free(prop); 4601356Seschrock } 4611356Seschrock zap_cursor_fini(&zc); 4621356Seschrock 4632082Seschrock if (err != ENOENT) 4641356Seschrock break; 4652082Seschrock err = 0; 4661356Seschrock } 4671356Seschrock rw_exit(&dp->dp_config_rwlock); 4681356Seschrock 4691356Seschrock return (err); 4701356Seschrock } 471