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