1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 5789Sahrens * Common Development and Distribution License, Version 1.0 only 6789Sahrens * (the "License"). You may not use this file except in compliance 7789Sahrens * with the License. 8789Sahrens * 9789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10789Sahrens * or http://www.opensolaris.org/os/licensing. 11789Sahrens * See the License for the specific language governing permissions 12789Sahrens * and limitations under the License. 13789Sahrens * 14789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 15789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16789Sahrens * If applicable, add the following below this CDDL HEADER, with the 17789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 18789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 19789Sahrens * 20789Sahrens * CDDL HEADER END 21789Sahrens */ 22789Sahrens /* 23789Sahrens * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28789Sahrens 29789Sahrens #include <sys/dmu.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); 53789Sahrens zfs_prop_default_string(prop, buf, 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 65789Sahrens dsl_prop_get_impl(dsl_pool_t *dp, uint64_t ddobj, const char *propname, 66789Sahrens int intsz, int numint, void *buf, char *setpoint) 67789Sahrens { 68789Sahrens int err = 0; 69789Sahrens objset_t *mos = dp->dp_meta_objset; 70789Sahrens 71789Sahrens if (setpoint) 72789Sahrens setpoint[0] = '\0'; 73789Sahrens 74789Sahrens ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock)); 75789Sahrens 76789Sahrens while (ddobj != 0) { 77789Sahrens dsl_dir_t *dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG); 78789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 79789Sahrens propname, intsz, numint, buf); 80789Sahrens if (err != ENOENT) { 81789Sahrens if (setpoint) 82789Sahrens dsl_dir_name(dd, setpoint); 83789Sahrens dsl_dir_close(dd, FTAG); 84789Sahrens break; 85789Sahrens } 86789Sahrens ASSERT3U(err, ==, ENOENT); 87789Sahrens ddobj = dd->dd_phys->dd_parent_obj; 88789Sahrens dsl_dir_close(dd, FTAG); 89789Sahrens } 90789Sahrens if (err == ENOENT) 91789Sahrens err = dodefault(propname, intsz, numint, buf); 92789Sahrens 93789Sahrens return (err); 94789Sahrens } 95789Sahrens 96789Sahrens /* 97789Sahrens * Register interest in the named property. We'll call the callback 98789Sahrens * once to notify it of the current property value, and again each time 99789Sahrens * the property changes, until this callback is unregistered. 100789Sahrens * 101789Sahrens * Return 0 on success, errno if the prop is not an integer value. 102789Sahrens */ 103789Sahrens int 104789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname, 105789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 106789Sahrens { 107789Sahrens dsl_dir_t *dd; 108789Sahrens uint64_t value; 109789Sahrens dsl_prop_cb_record_t *cbr; 110789Sahrens int err; 111789Sahrens 112789Sahrens dd = ds->ds_dir; 113789Sahrens 114789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 115789Sahrens 116789Sahrens err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, propname, 117789Sahrens 8, 1, &value, NULL); 118789Sahrens if (err == ENOENT) { 119789Sahrens err = 0; 120789Sahrens value = DSL_PROP_VALUE_UNDEFINED; 121789Sahrens } 122789Sahrens if (err != 0) { 123789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 124789Sahrens return (err); 125789Sahrens } 126789Sahrens 127789Sahrens cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 128789Sahrens cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 129789Sahrens (void) strcpy((char *)cbr->cbr_propname, propname); 130789Sahrens cbr->cbr_func = callback; 131789Sahrens cbr->cbr_arg = cbarg; 132789Sahrens mutex_enter(&dd->dd_lock); 133789Sahrens list_insert_head(&dd->dd_prop_cbs, cbr); 134789Sahrens mutex_exit(&dd->dd_lock); 135789Sahrens 136789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 137789Sahrens 138789Sahrens (void) dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, cbr); 139789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 140789Sahrens /* Leave dataset open until this callback is unregistered */ 141789Sahrens return (0); 142789Sahrens } 143789Sahrens 144789Sahrens int 145789Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname, 146789Sahrens int intsz, int numints, void *buf, char *setpoint) 147789Sahrens { 148789Sahrens int err; 149789Sahrens 150789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 151789Sahrens err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, 152789Sahrens propname, intsz, numints, buf, setpoint); 153789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 154789Sahrens 155789Sahrens return (err); 156789Sahrens } 157789Sahrens 158789Sahrens int 159789Sahrens dsl_prop_get(const char *ddname, const char *propname, 160789Sahrens int intsz, int numints, void *buf, char *setpoint) 161789Sahrens { 162789Sahrens dsl_dir_t *dd; 163789Sahrens const char *tail; 164789Sahrens int err; 165789Sahrens 166789Sahrens dd = dsl_dir_open(ddname, FTAG, &tail); 167789Sahrens if (dd == NULL) 168789Sahrens return (ENOENT); 169789Sahrens if (tail && tail[0] != '@') { 170789Sahrens dsl_dir_close(dd, FTAG); 171789Sahrens return (ENOENT); 172789Sahrens } 173789Sahrens 174789Sahrens err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 175789Sahrens 176789Sahrens dsl_dir_close(dd, FTAG); 177789Sahrens return (err); 178789Sahrens } 179789Sahrens 180789Sahrens /* 181789Sahrens * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if 182789Sahrens * valuelen not big enough. 183789Sahrens */ 184789Sahrens int 185789Sahrens dsl_prop_get_string(const char *ddname, const char *propname, 186789Sahrens char *value, int valuelen, char *setpoint) 187789Sahrens { 188789Sahrens return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint)); 189789Sahrens } 190789Sahrens 191789Sahrens /* 192789Sahrens * Get the current property value. It may have changed by the time this 193789Sahrens * function returns, so it is NOT safe to follow up with 194789Sahrens * dsl_prop_register() and assume that the value has not changed in 195789Sahrens * between. 196789Sahrens * 197789Sahrens * Return 0 on success, ENOENT if ddname is invalid. 198789Sahrens */ 199789Sahrens int 200789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname, 201789Sahrens uint64_t *valuep, char *setpoint) 202789Sahrens { 203789Sahrens return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 204789Sahrens } 205789Sahrens 206789Sahrens int 207789Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname, 208789Sahrens uint64_t *valuep, char *setpoint) 209789Sahrens { 210789Sahrens return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint)); 211789Sahrens } 212789Sahrens 213789Sahrens /* 214789Sahrens * Unregister this callback. Return 0 on success, ENOENT if ddname is 215789Sahrens * invalid, ENOMSG if no matching callback registered. 216789Sahrens */ 217789Sahrens int 218789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 219789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 220789Sahrens { 221789Sahrens dsl_dir_t *dd; 222789Sahrens dsl_prop_cb_record_t *cbr; 223789Sahrens 224789Sahrens dd = ds->ds_dir; 225789Sahrens 226789Sahrens mutex_enter(&dd->dd_lock); 227789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 228789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 229789Sahrens if (strcmp(cbr->cbr_propname, propname) == 0 && 230789Sahrens cbr->cbr_func == callback && 231789Sahrens cbr->cbr_arg == cbarg) 232789Sahrens break; 233789Sahrens } 234789Sahrens 235789Sahrens if (cbr == NULL) { 236789Sahrens mutex_exit(&dd->dd_lock); 237789Sahrens return (ENOMSG); 238789Sahrens } 239789Sahrens 240789Sahrens list_remove(&dd->dd_prop_cbs, cbr); 241789Sahrens mutex_exit(&dd->dd_lock); 242789Sahrens kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 243789Sahrens kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 244789Sahrens 245789Sahrens /* Clean up from dsl_prop_register */ 246789Sahrens dsl_dir_close(dd, cbr); 247789Sahrens return (0); 248789Sahrens } 249789Sahrens 250789Sahrens static void 251789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 252789Sahrens const char *propname, uint64_t value, int first) 253789Sahrens { 254789Sahrens dsl_dir_t *dd; 255789Sahrens dsl_prop_cb_record_t *cbr; 256789Sahrens objset_t *mos = dp->dp_meta_objset; 257789Sahrens int err; 258789Sahrens 259789Sahrens ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 260789Sahrens dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG); 261789Sahrens 262789Sahrens if (!first) { 263789Sahrens /* 264789Sahrens * If the prop is set here, then this change is not 265789Sahrens * being inherited here or below; stop the recursion. 266789Sahrens */ 267789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 268789Sahrens 8, 1, &value); 269789Sahrens if (err == 0) { 270789Sahrens dsl_dir_close(dd, FTAG); 271789Sahrens return; 272789Sahrens } 273789Sahrens ASSERT3U(err, ==, ENOENT); 274789Sahrens } 275789Sahrens 276789Sahrens mutex_enter(&dd->dd_lock); 277789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 278789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 279789Sahrens if (strcmp(cbr->cbr_propname, propname) == 0) { 280789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 281789Sahrens } 282789Sahrens } 283789Sahrens mutex_exit(&dd->dd_lock); 284789Sahrens 285789Sahrens if (dd->dd_phys->dd_child_dir_zapobj) { 286789Sahrens zap_cursor_t zc; 287789Sahrens zap_attribute_t za; 288789Sahrens 289789Sahrens for (zap_cursor_init(&zc, mos, 290789Sahrens dd->dd_phys->dd_child_dir_zapobj); 291789Sahrens zap_cursor_retrieve(&zc, &za) == 0; 292789Sahrens zap_cursor_advance(&zc)) { 293789Sahrens /* XXX recursion could blow stack; esp. za! */ 294789Sahrens dsl_prop_changed_notify(dp, za.za_first_integer, 295789Sahrens propname, value, FALSE); 296789Sahrens } 297*885Sahrens zap_cursor_fini(&zc); 298789Sahrens } 299789Sahrens dsl_dir_close(dd, FTAG); 300789Sahrens } 301789Sahrens 302789Sahrens struct prop_set_arg { 303789Sahrens const char *name; 304789Sahrens int intsz; 305789Sahrens int numints; 306789Sahrens const void *buf; 307789Sahrens }; 308789Sahrens 309789Sahrens static int 310789Sahrens dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx) 311789Sahrens { 312789Sahrens struct prop_set_arg *psa = arg; 313789Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 314789Sahrens uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 315789Sahrens uint64_t intval; 316789Sahrens int err, isint; 317789Sahrens 318789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER); 319789Sahrens 320789Sahrens isint = (dodefault(psa->name, 8, 1, &intval) == 0); 321789Sahrens 322789Sahrens if (psa->numints == 0) { 323789Sahrens err = zap_remove(mos, zapobj, psa->name, tx); 324789Sahrens if (err == ENOENT) /* that's fine. */ 325789Sahrens err = 0; 326789Sahrens if (err == 0 && isint) { 327789Sahrens err = dsl_prop_get_impl(dd->dd_pool, 328789Sahrens dd->dd_phys->dd_parent_obj, psa->name, 329789Sahrens 8, 1, &intval, NULL); 330789Sahrens } 331789Sahrens } else { 332789Sahrens err = zap_update(mos, zapobj, psa->name, 333789Sahrens psa->intsz, psa->numints, psa->buf, tx); 334789Sahrens if (isint) 335789Sahrens intval = *(uint64_t *)psa->buf; 336789Sahrens } 337789Sahrens 338789Sahrens if (err == 0 && isint) { 339789Sahrens dsl_prop_changed_notify(dd->dd_pool, 340789Sahrens dd->dd_object, psa->name, intval, TRUE); 341789Sahrens } 342789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 343789Sahrens 344789Sahrens return (err); 345789Sahrens } 346789Sahrens 347789Sahrens int 348789Sahrens dsl_prop_set(const char *ddname, const char *propname, 349789Sahrens int intsz, int numints, const void *buf) 350789Sahrens { 351789Sahrens dsl_dir_t *dd; 352789Sahrens int err; 353789Sahrens struct prop_set_arg psa; 354789Sahrens 355789Sahrens dd = dsl_dir_open(ddname, FTAG, NULL); 356789Sahrens if (dd == NULL) 357789Sahrens return (ENOENT); 358789Sahrens 359789Sahrens psa.name = propname; 360789Sahrens psa.intsz = intsz; 361789Sahrens psa.numints = numints; 362789Sahrens psa.buf = buf; 363789Sahrens err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 0); 364789Sahrens 365789Sahrens dsl_dir_close(dd, FTAG); 366789Sahrens 367789Sahrens return (err); 368789Sahrens } 369