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