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> 34789Sahrens #include <sys/spa.h> 35789Sahrens #include <sys/zio_checksum.h> /* for the default checksum value */ 36789Sahrens #include <sys/zap.h> 37789Sahrens #include <sys/fs/zfs.h> 38789Sahrens 39789Sahrens #include "zfs_prop.h" 40789Sahrens 41789Sahrens static int 42789Sahrens dodefault(const char *propname, int intsz, int numint, void *buf) 43789Sahrens { 44789Sahrens zfs_prop_t prop; 45789Sahrens 46789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL || 47789Sahrens zfs_prop_readonly(prop)) 48789Sahrens return (ENOENT); 49789Sahrens 50789Sahrens if (zfs_prop_get_type(prop) == prop_type_string) { 51789Sahrens if (intsz != 1) 52789Sahrens return (EOVERFLOW); 531356Seschrock (void) strncpy(buf, zfs_prop_default_string(prop), numint); 54789Sahrens } else { 55789Sahrens if (intsz != 8 || numint < 1) 56789Sahrens return (EOVERFLOW); 57789Sahrens 58789Sahrens *(uint64_t *)buf = zfs_prop_default_numeric(prop); 59789Sahrens } 60789Sahrens 61789Sahrens return (0); 62789Sahrens } 63789Sahrens 64789Sahrens static int 65*2082Seschrock dsl_prop_get_impl(dsl_dir_t *dd, const char *propname, 66789Sahrens int intsz, int numint, void *buf, char *setpoint) 67789Sahrens { 68*2082Seschrock int err = ENOENT; 69789Sahrens 70789Sahrens if (setpoint) 71789Sahrens setpoint[0] = '\0'; 72789Sahrens 73*2082Seschrock /* 74*2082Seschrock * Note: dd may be NULL, therefore we shouldn't dereference it 75*2082Seschrock * ouside this loop. 76*2082Seschrock */ 77*2082Seschrock for (; dd != NULL; dd = dd->dd_parent) { 78*2082Seschrock objset_t *mos = dd->dd_pool->dp_meta_objset; 79*2082Seschrock ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock)); 80789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 81789Sahrens propname, intsz, numint, buf); 82789Sahrens if (err != ENOENT) { 83789Sahrens if (setpoint) 84789Sahrens dsl_dir_name(dd, setpoint); 85789Sahrens break; 86789Sahrens } 87789Sahrens } 88789Sahrens if (err == ENOENT) 89789Sahrens err = dodefault(propname, intsz, numint, buf); 90789Sahrens 91789Sahrens return (err); 92789Sahrens } 93789Sahrens 94789Sahrens /* 95789Sahrens * Register interest in the named property. We'll call the callback 96789Sahrens * once to notify it of the current property value, and again each time 97789Sahrens * the property changes, until this callback is unregistered. 98789Sahrens * 99789Sahrens * Return 0 on success, errno if the prop is not an integer value. 100789Sahrens */ 101789Sahrens int 102789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname, 103789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 104789Sahrens { 105*2082Seschrock dsl_dir_t *dd = ds->ds_dir; 106789Sahrens uint64_t value; 107789Sahrens dsl_prop_cb_record_t *cbr; 108789Sahrens int err; 109789Sahrens 110789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 111789Sahrens 112*2082Seschrock err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL); 113789Sahrens if (err != 0) { 114789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 115789Sahrens return (err); 116789Sahrens } 117789Sahrens 118789Sahrens cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 119*2082Seschrock cbr->cbr_ds = ds; 120789Sahrens cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 121789Sahrens (void) strcpy((char *)cbr->cbr_propname, propname); 122789Sahrens cbr->cbr_func = callback; 123789Sahrens cbr->cbr_arg = cbarg; 124789Sahrens mutex_enter(&dd->dd_lock); 125789Sahrens list_insert_head(&dd->dd_prop_cbs, cbr); 126789Sahrens mutex_exit(&dd->dd_lock); 127789Sahrens 128789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 129789Sahrens 1301544Seschrock VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object, 1311544Seschrock NULL, cbr, &dd)); 132789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 133789Sahrens /* Leave dataset open until this callback is unregistered */ 134789Sahrens return (0); 135789Sahrens } 136789Sahrens 137789Sahrens int 138789Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname, 139789Sahrens int intsz, int numints, void *buf, char *setpoint) 140789Sahrens { 141789Sahrens int err; 142789Sahrens 143789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 144*2082Seschrock err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint); 145789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 146789Sahrens 147789Sahrens return (err); 148789Sahrens } 149789Sahrens 150789Sahrens int 151789Sahrens dsl_prop_get(const char *ddname, const char *propname, 152789Sahrens int intsz, int numints, void *buf, char *setpoint) 153789Sahrens { 154789Sahrens dsl_dir_t *dd; 155789Sahrens const char *tail; 156789Sahrens int err; 157789Sahrens 1581544Seschrock err = dsl_dir_open(ddname, FTAG, &dd, &tail); 1591544Seschrock if (err) 1601544Seschrock return (err); 161789Sahrens if (tail && tail[0] != '@') { 162789Sahrens dsl_dir_close(dd, FTAG); 163789Sahrens return (ENOENT); 164789Sahrens } 165789Sahrens 166789Sahrens err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 167789Sahrens 168789Sahrens dsl_dir_close(dd, FTAG); 169789Sahrens return (err); 170789Sahrens } 171789Sahrens 172789Sahrens /* 173789Sahrens * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if 174789Sahrens * valuelen not big enough. 175789Sahrens */ 176789Sahrens int 177789Sahrens dsl_prop_get_string(const char *ddname, const char *propname, 178789Sahrens char *value, int valuelen, char *setpoint) 179789Sahrens { 180789Sahrens return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint)); 181789Sahrens } 182789Sahrens 183789Sahrens /* 184789Sahrens * Get the current property value. It may have changed by the time this 185789Sahrens * function returns, so it is NOT safe to follow up with 186789Sahrens * dsl_prop_register() and assume that the value has not changed in 187789Sahrens * between. 188789Sahrens * 189789Sahrens * Return 0 on success, ENOENT if ddname is invalid. 190789Sahrens */ 191789Sahrens int 192789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname, 193789Sahrens uint64_t *valuep, char *setpoint) 194789Sahrens { 195789Sahrens return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 196789Sahrens } 197789Sahrens 198789Sahrens int 199789Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname, 200789Sahrens uint64_t *valuep, char *setpoint) 201789Sahrens { 202789Sahrens return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint)); 203789Sahrens } 204789Sahrens 205789Sahrens /* 206789Sahrens * Unregister this callback. Return 0 on success, ENOENT if ddname is 207789Sahrens * invalid, ENOMSG if no matching callback registered. 208789Sahrens */ 209789Sahrens int 210789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 211789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 212789Sahrens { 213*2082Seschrock dsl_dir_t *dd = ds->ds_dir; 214789Sahrens dsl_prop_cb_record_t *cbr; 215789Sahrens 216789Sahrens mutex_enter(&dd->dd_lock); 217789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 218789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 219*2082Seschrock if (cbr->cbr_ds == ds && 220789Sahrens cbr->cbr_func == callback && 221*2082Seschrock cbr->cbr_arg == cbarg && 222*2082Seschrock strcmp(cbr->cbr_propname, propname) == 0) 223789Sahrens break; 224789Sahrens } 225789Sahrens 226789Sahrens if (cbr == NULL) { 227789Sahrens mutex_exit(&dd->dd_lock); 228789Sahrens return (ENOMSG); 229789Sahrens } 230789Sahrens 231789Sahrens list_remove(&dd->dd_prop_cbs, cbr); 232789Sahrens mutex_exit(&dd->dd_lock); 233789Sahrens kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 234789Sahrens kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 235789Sahrens 236789Sahrens /* Clean up from dsl_prop_register */ 237789Sahrens dsl_dir_close(dd, cbr); 238789Sahrens return (0); 239789Sahrens } 240789Sahrens 241*2082Seschrock /* 242*2082Seschrock * Return the number of callbacks that are registered for this dataset. 243*2082Seschrock */ 244*2082Seschrock int 245*2082Seschrock dsl_prop_numcb(dsl_dataset_t *ds) 246*2082Seschrock { 247*2082Seschrock dsl_dir_t *dd = ds->ds_dir; 248*2082Seschrock dsl_prop_cb_record_t *cbr; 249*2082Seschrock int num = 0; 250*2082Seschrock 251*2082Seschrock mutex_enter(&dd->dd_lock); 252*2082Seschrock for (cbr = list_head(&dd->dd_prop_cbs); 253*2082Seschrock cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 254*2082Seschrock if (cbr->cbr_ds == ds) 255*2082Seschrock num++; 256*2082Seschrock } 257*2082Seschrock mutex_exit(&dd->dd_lock); 258*2082Seschrock 259*2082Seschrock return (num); 260*2082Seschrock } 261*2082Seschrock 262789Sahrens static void 263789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 264789Sahrens const char *propname, uint64_t value, int first) 265789Sahrens { 266789Sahrens dsl_dir_t *dd; 267789Sahrens dsl_prop_cb_record_t *cbr; 268789Sahrens objset_t *mos = dp->dp_meta_objset; 269789Sahrens int err; 270789Sahrens 271789Sahrens ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 2721544Seschrock err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd); 2731544Seschrock if (err) 2741544Seschrock return; 275789Sahrens 276789Sahrens if (!first) { 277789Sahrens /* 278789Sahrens * If the prop is set here, then this change is not 279789Sahrens * being inherited here or below; stop the recursion. 280789Sahrens */ 281789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 282789Sahrens 8, 1, &value); 283789Sahrens if (err == 0) { 284789Sahrens dsl_dir_close(dd, FTAG); 285789Sahrens return; 286789Sahrens } 287789Sahrens ASSERT3U(err, ==, ENOENT); 288789Sahrens } 289789Sahrens 290789Sahrens mutex_enter(&dd->dd_lock); 291789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 292789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 293789Sahrens if (strcmp(cbr->cbr_propname, propname) == 0) { 294789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 295789Sahrens } 296789Sahrens } 297789Sahrens mutex_exit(&dd->dd_lock); 298789Sahrens 299789Sahrens if (dd->dd_phys->dd_child_dir_zapobj) { 300789Sahrens zap_cursor_t zc; 301789Sahrens zap_attribute_t za; 302789Sahrens 303789Sahrens for (zap_cursor_init(&zc, mos, 304789Sahrens dd->dd_phys->dd_child_dir_zapobj); 305789Sahrens zap_cursor_retrieve(&zc, &za) == 0; 306789Sahrens zap_cursor_advance(&zc)) { 307789Sahrens /* XXX recursion could blow stack; esp. za! */ 308789Sahrens dsl_prop_changed_notify(dp, za.za_first_integer, 309789Sahrens propname, value, FALSE); 310789Sahrens } 311885Sahrens zap_cursor_fini(&zc); 312789Sahrens } 313789Sahrens dsl_dir_close(dd, FTAG); 314789Sahrens } 315789Sahrens 316789Sahrens struct prop_set_arg { 317789Sahrens const char *name; 318789Sahrens int intsz; 319789Sahrens int numints; 320789Sahrens const void *buf; 321789Sahrens }; 322789Sahrens 323789Sahrens static int 324789Sahrens dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx) 325789Sahrens { 326789Sahrens struct prop_set_arg *psa = arg; 327789Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 328789Sahrens uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 329789Sahrens uint64_t intval; 330789Sahrens int err, isint; 331789Sahrens 332789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER); 333789Sahrens 334789Sahrens isint = (dodefault(psa->name, 8, 1, &intval) == 0); 335789Sahrens 336789Sahrens if (psa->numints == 0) { 337789Sahrens err = zap_remove(mos, zapobj, psa->name, tx); 338789Sahrens if (err == ENOENT) /* that's fine. */ 339789Sahrens err = 0; 340789Sahrens if (err == 0 && isint) { 341*2082Seschrock err = dsl_prop_get_impl(dd->dd_parent, 342*2082Seschrock psa->name, 8, 1, &intval, NULL); 343789Sahrens } 344789Sahrens } else { 345789Sahrens err = zap_update(mos, zapobj, psa->name, 346789Sahrens psa->intsz, psa->numints, psa->buf, tx); 347789Sahrens if (isint) 348789Sahrens intval = *(uint64_t *)psa->buf; 349789Sahrens } 350789Sahrens 351789Sahrens if (err == 0 && isint) { 352789Sahrens dsl_prop_changed_notify(dd->dd_pool, 353789Sahrens dd->dd_object, psa->name, intval, TRUE); 354789Sahrens } 355789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 356789Sahrens 357789Sahrens return (err); 358789Sahrens } 359789Sahrens 360789Sahrens int 361789Sahrens dsl_prop_set(const char *ddname, const char *propname, 362789Sahrens int intsz, int numints, const void *buf) 363789Sahrens { 364789Sahrens dsl_dir_t *dd; 365789Sahrens int err; 366789Sahrens struct prop_set_arg psa; 367789Sahrens 3681544Seschrock err = dsl_dir_open(ddname, FTAG, &dd, NULL); 3691544Seschrock if (err) 3701544Seschrock return (err); 371789Sahrens 372789Sahrens psa.name = propname; 373789Sahrens psa.intsz = intsz; 374789Sahrens psa.numints = numints; 375789Sahrens psa.buf = buf; 3761544Seschrock err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 1<<20); 377789Sahrens 378789Sahrens dsl_dir_close(dd, FTAG); 379789Sahrens 380789Sahrens return (err); 381789Sahrens } 3821356Seschrock 3831356Seschrock /* 3841356Seschrock * Iterate over all properties for this dataset and return them in an nvlist. 3851356Seschrock */ 3861356Seschrock int 3871356Seschrock dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 3881356Seschrock { 3891356Seschrock dsl_dataset_t *ds = os->os->os_dsl_dataset; 390*2082Seschrock dsl_dir_t *dd = ds->ds_dir; 3911356Seschrock int err = 0; 3921356Seschrock dsl_pool_t *dp; 3931356Seschrock objset_t *mos; 3941356Seschrock zap_cursor_t zc; 3951356Seschrock zap_attribute_t za; 3961356Seschrock char setpoint[MAXNAMELEN]; 3971356Seschrock char *tmp; 3981356Seschrock nvlist_t *prop; 3991356Seschrock 4001356Seschrock if (dsl_dataset_is_snapshot(ds)) { 4011356Seschrock VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 4021356Seschrock return (0); 4031356Seschrock } 4041356Seschrock 4051356Seschrock VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 4061356Seschrock 4071356Seschrock dp = dd->dd_pool; 4081356Seschrock mos = dp->dp_meta_objset; 4091356Seschrock 4101356Seschrock rw_enter(&dp->dp_config_rwlock, RW_READER); 411*2082Seschrock for (; dd != NULL; dd = dd->dd_parent) { 4121356Seschrock dsl_dir_name(dd, setpoint); 4131356Seschrock 4141356Seschrock for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); 4151356Seschrock (err = zap_cursor_retrieve(&zc, &za)) == 0; 4161356Seschrock zap_cursor_advance(&zc)) { 4171356Seschrock if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0) 4181356Seschrock continue; 4191356Seschrock 4201356Seschrock VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME, 4211356Seschrock KM_SLEEP) == 0); 4221356Seschrock if (za.za_integer_length == 1) { 4231356Seschrock /* 4241356Seschrock * String property 4251356Seschrock */ 4261356Seschrock tmp = kmem_alloc(za.za_num_integers, KM_SLEEP); 4271356Seschrock err = zap_lookup(mos, 4281356Seschrock dd->dd_phys->dd_props_zapobj, 4291356Seschrock za.za_name, 1, za.za_num_integers, 4301356Seschrock tmp); 4311356Seschrock if (err != 0) { 4321356Seschrock kmem_free(tmp, za.za_num_integers); 4331356Seschrock break; 4341356Seschrock } 4351356Seschrock VERIFY(nvlist_add_string(prop, 4361356Seschrock ZFS_PROP_VALUE, tmp) == 0); 4371356Seschrock kmem_free(tmp, za.za_num_integers); 4381356Seschrock } else { 4391356Seschrock /* 4401356Seschrock * Integer property 4411356Seschrock */ 4421356Seschrock ASSERT(za.za_integer_length == 8); 4431356Seschrock (void) nvlist_add_uint64(prop, ZFS_PROP_VALUE, 4441356Seschrock za.za_first_integer); 4451356Seschrock } 4461356Seschrock 4471356Seschrock VERIFY(nvlist_add_string(prop, 4481356Seschrock ZFS_PROP_SOURCE, setpoint) == 0); 4491356Seschrock VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 4501356Seschrock prop) == 0); 4511356Seschrock nvlist_free(prop); 4521356Seschrock } 4531356Seschrock zap_cursor_fini(&zc); 4541356Seschrock 455*2082Seschrock if (err != ENOENT) 4561356Seschrock break; 457*2082Seschrock err = 0; 4581356Seschrock } 4591356Seschrock rw_exit(&dp->dp_config_rwlock); 4601356Seschrock 4611356Seschrock return (err); 4621356Seschrock } 463