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 /* 23*1356Seschrock * Copyright 2006 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> 30*1356Seschrock #include <sys/dmu_objset.h> 31789Sahrens #include <sys/dmu_tx.h> 32789Sahrens #include <sys/dsl_dataset.h> 33789Sahrens #include <sys/dsl_dir.h> 34789Sahrens #include <sys/dsl_prop.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); 54*1356Seschrock (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 66789Sahrens dsl_prop_get_impl(dsl_pool_t *dp, uint64_t ddobj, const char *propname, 67789Sahrens int intsz, int numint, void *buf, char *setpoint) 68789Sahrens { 69789Sahrens int err = 0; 70789Sahrens objset_t *mos = dp->dp_meta_objset; 71789Sahrens 72789Sahrens if (setpoint) 73789Sahrens setpoint[0] = '\0'; 74789Sahrens 75789Sahrens ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock)); 76789Sahrens 77789Sahrens while (ddobj != 0) { 78789Sahrens dsl_dir_t *dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG); 79789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 80789Sahrens propname, intsz, numint, buf); 81789Sahrens if (err != ENOENT) { 82789Sahrens if (setpoint) 83789Sahrens dsl_dir_name(dd, setpoint); 84789Sahrens dsl_dir_close(dd, FTAG); 85789Sahrens break; 86789Sahrens } 87789Sahrens ASSERT3U(err, ==, ENOENT); 88789Sahrens ddobj = dd->dd_phys->dd_parent_obj; 89789Sahrens dsl_dir_close(dd, FTAG); 90789Sahrens } 91789Sahrens if (err == ENOENT) 92789Sahrens err = dodefault(propname, intsz, numint, buf); 93789Sahrens 94789Sahrens return (err); 95789Sahrens } 96789Sahrens 97789Sahrens /* 98789Sahrens * Register interest in the named property. We'll call the callback 99789Sahrens * once to notify it of the current property value, and again each time 100789Sahrens * the property changes, until this callback is unregistered. 101789Sahrens * 102789Sahrens * Return 0 on success, errno if the prop is not an integer value. 103789Sahrens */ 104789Sahrens int 105789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname, 106789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 107789Sahrens { 108789Sahrens dsl_dir_t *dd; 109789Sahrens uint64_t value; 110789Sahrens dsl_prop_cb_record_t *cbr; 111789Sahrens int err; 112789Sahrens 113789Sahrens dd = ds->ds_dir; 114789Sahrens 115789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 116789Sahrens 117789Sahrens err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, propname, 118789Sahrens 8, 1, &value, NULL); 119789Sahrens if (err == ENOENT) { 120789Sahrens err = 0; 121789Sahrens value = DSL_PROP_VALUE_UNDEFINED; 122789Sahrens } 123789Sahrens if (err != 0) { 124789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 125789Sahrens return (err); 126789Sahrens } 127789Sahrens 128789Sahrens cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 129789Sahrens cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 130789Sahrens (void) strcpy((char *)cbr->cbr_propname, propname); 131789Sahrens cbr->cbr_func = callback; 132789Sahrens cbr->cbr_arg = cbarg; 133789Sahrens mutex_enter(&dd->dd_lock); 134789Sahrens list_insert_head(&dd->dd_prop_cbs, cbr); 135789Sahrens mutex_exit(&dd->dd_lock); 136789Sahrens 137789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 138789Sahrens 139789Sahrens (void) dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, cbr); 140789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 141789Sahrens /* Leave dataset open until this callback is unregistered */ 142789Sahrens return (0); 143789Sahrens } 144789Sahrens 145789Sahrens int 146789Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname, 147789Sahrens int intsz, int numints, void *buf, char *setpoint) 148789Sahrens { 149789Sahrens int err; 150789Sahrens 151789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 152789Sahrens err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, 153789Sahrens propname, intsz, numints, buf, setpoint); 154789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 155789Sahrens 156789Sahrens return (err); 157789Sahrens } 158789Sahrens 159789Sahrens int 160789Sahrens dsl_prop_get(const char *ddname, const char *propname, 161789Sahrens int intsz, int numints, void *buf, char *setpoint) 162789Sahrens { 163789Sahrens dsl_dir_t *dd; 164789Sahrens const char *tail; 165789Sahrens int err; 166789Sahrens 167789Sahrens dd = dsl_dir_open(ddname, FTAG, &tail); 168789Sahrens if (dd == NULL) 169789Sahrens return (ENOENT); 170789Sahrens if (tail && tail[0] != '@') { 171789Sahrens dsl_dir_close(dd, FTAG); 172789Sahrens return (ENOENT); 173789Sahrens } 174789Sahrens 175789Sahrens err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 176789Sahrens 177789Sahrens dsl_dir_close(dd, FTAG); 178789Sahrens return (err); 179789Sahrens } 180789Sahrens 181789Sahrens /* 182789Sahrens * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if 183789Sahrens * valuelen not big enough. 184789Sahrens */ 185789Sahrens int 186789Sahrens dsl_prop_get_string(const char *ddname, const char *propname, 187789Sahrens char *value, int valuelen, char *setpoint) 188789Sahrens { 189789Sahrens return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint)); 190789Sahrens } 191789Sahrens 192789Sahrens /* 193789Sahrens * Get the current property value. It may have changed by the time this 194789Sahrens * function returns, so it is NOT safe to follow up with 195789Sahrens * dsl_prop_register() and assume that the value has not changed in 196789Sahrens * between. 197789Sahrens * 198789Sahrens * Return 0 on success, ENOENT if ddname is invalid. 199789Sahrens */ 200789Sahrens int 201789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname, 202789Sahrens uint64_t *valuep, char *setpoint) 203789Sahrens { 204789Sahrens return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 205789Sahrens } 206789Sahrens 207789Sahrens int 208789Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname, 209789Sahrens uint64_t *valuep, char *setpoint) 210789Sahrens { 211789Sahrens return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint)); 212789Sahrens } 213789Sahrens 214789Sahrens /* 215789Sahrens * Unregister this callback. Return 0 on success, ENOENT if ddname is 216789Sahrens * invalid, ENOMSG if no matching callback registered. 217789Sahrens */ 218789Sahrens int 219789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 220789Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 221789Sahrens { 222789Sahrens dsl_dir_t *dd; 223789Sahrens dsl_prop_cb_record_t *cbr; 224789Sahrens 225789Sahrens dd = ds->ds_dir; 226789Sahrens 227789Sahrens mutex_enter(&dd->dd_lock); 228789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 229789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 230789Sahrens if (strcmp(cbr->cbr_propname, propname) == 0 && 231789Sahrens cbr->cbr_func == callback && 232789Sahrens cbr->cbr_arg == cbarg) 233789Sahrens break; 234789Sahrens } 235789Sahrens 236789Sahrens if (cbr == NULL) { 237789Sahrens mutex_exit(&dd->dd_lock); 238789Sahrens return (ENOMSG); 239789Sahrens } 240789Sahrens 241789Sahrens list_remove(&dd->dd_prop_cbs, cbr); 242789Sahrens mutex_exit(&dd->dd_lock); 243789Sahrens kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 244789Sahrens kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 245789Sahrens 246789Sahrens /* Clean up from dsl_prop_register */ 247789Sahrens dsl_dir_close(dd, cbr); 248789Sahrens return (0); 249789Sahrens } 250789Sahrens 251789Sahrens static void 252789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 253789Sahrens const char *propname, uint64_t value, int first) 254789Sahrens { 255789Sahrens dsl_dir_t *dd; 256789Sahrens dsl_prop_cb_record_t *cbr; 257789Sahrens objset_t *mos = dp->dp_meta_objset; 258789Sahrens int err; 259789Sahrens 260789Sahrens ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 261789Sahrens dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG); 262789Sahrens 263789Sahrens if (!first) { 264789Sahrens /* 265789Sahrens * If the prop is set here, then this change is not 266789Sahrens * being inherited here or below; stop the recursion. 267789Sahrens */ 268789Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 269789Sahrens 8, 1, &value); 270789Sahrens if (err == 0) { 271789Sahrens dsl_dir_close(dd, FTAG); 272789Sahrens return; 273789Sahrens } 274789Sahrens ASSERT3U(err, ==, ENOENT); 275789Sahrens } 276789Sahrens 277789Sahrens mutex_enter(&dd->dd_lock); 278789Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 279789Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 280789Sahrens if (strcmp(cbr->cbr_propname, propname) == 0) { 281789Sahrens cbr->cbr_func(cbr->cbr_arg, value); 282789Sahrens } 283789Sahrens } 284789Sahrens mutex_exit(&dd->dd_lock); 285789Sahrens 286789Sahrens if (dd->dd_phys->dd_child_dir_zapobj) { 287789Sahrens zap_cursor_t zc; 288789Sahrens zap_attribute_t za; 289789Sahrens 290789Sahrens for (zap_cursor_init(&zc, mos, 291789Sahrens dd->dd_phys->dd_child_dir_zapobj); 292789Sahrens zap_cursor_retrieve(&zc, &za) == 0; 293789Sahrens zap_cursor_advance(&zc)) { 294789Sahrens /* XXX recursion could blow stack; esp. za! */ 295789Sahrens dsl_prop_changed_notify(dp, za.za_first_integer, 296789Sahrens propname, value, FALSE); 297789Sahrens } 298885Sahrens zap_cursor_fini(&zc); 299789Sahrens } 300789Sahrens dsl_dir_close(dd, FTAG); 301789Sahrens } 302789Sahrens 303789Sahrens struct prop_set_arg { 304789Sahrens const char *name; 305789Sahrens int intsz; 306789Sahrens int numints; 307789Sahrens const void *buf; 308789Sahrens }; 309789Sahrens 310789Sahrens static int 311789Sahrens dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx) 312789Sahrens { 313789Sahrens struct prop_set_arg *psa = arg; 314789Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 315789Sahrens uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 316789Sahrens uint64_t intval; 317789Sahrens int err, isint; 318789Sahrens 319789Sahrens rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER); 320789Sahrens 321789Sahrens isint = (dodefault(psa->name, 8, 1, &intval) == 0); 322789Sahrens 323789Sahrens if (psa->numints == 0) { 324789Sahrens err = zap_remove(mos, zapobj, psa->name, tx); 325789Sahrens if (err == ENOENT) /* that's fine. */ 326789Sahrens err = 0; 327789Sahrens if (err == 0 && isint) { 328789Sahrens err = dsl_prop_get_impl(dd->dd_pool, 329789Sahrens dd->dd_phys->dd_parent_obj, psa->name, 330789Sahrens 8, 1, &intval, NULL); 331789Sahrens } 332789Sahrens } else { 333789Sahrens err = zap_update(mos, zapobj, psa->name, 334789Sahrens psa->intsz, psa->numints, psa->buf, tx); 335789Sahrens if (isint) 336789Sahrens intval = *(uint64_t *)psa->buf; 337789Sahrens } 338789Sahrens 339789Sahrens if (err == 0 && isint) { 340789Sahrens dsl_prop_changed_notify(dd->dd_pool, 341789Sahrens dd->dd_object, psa->name, intval, TRUE); 342789Sahrens } 343789Sahrens rw_exit(&dd->dd_pool->dp_config_rwlock); 344789Sahrens 345789Sahrens return (err); 346789Sahrens } 347789Sahrens 348789Sahrens int 349789Sahrens dsl_prop_set(const char *ddname, const char *propname, 350789Sahrens int intsz, int numints, const void *buf) 351789Sahrens { 352789Sahrens dsl_dir_t *dd; 353789Sahrens int err; 354789Sahrens struct prop_set_arg psa; 355789Sahrens 356789Sahrens dd = dsl_dir_open(ddname, FTAG, NULL); 357789Sahrens if (dd == NULL) 358789Sahrens return (ENOENT); 359789Sahrens 360789Sahrens psa.name = propname; 361789Sahrens psa.intsz = intsz; 362789Sahrens psa.numints = numints; 363789Sahrens psa.buf = buf; 364789Sahrens err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 0); 365789Sahrens 366789Sahrens dsl_dir_close(dd, FTAG); 367789Sahrens 368789Sahrens return (err); 369789Sahrens } 370*1356Seschrock 371*1356Seschrock /* 372*1356Seschrock * Iterate over all properties for this dataset and return them in an nvlist. 373*1356Seschrock */ 374*1356Seschrock int 375*1356Seschrock dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 376*1356Seschrock { 377*1356Seschrock dsl_dataset_t *ds = os->os->os_dsl_dataset; 378*1356Seschrock dsl_dir_t *dd, *parent; 379*1356Seschrock int err = 0; 380*1356Seschrock dsl_pool_t *dp; 381*1356Seschrock objset_t *mos; 382*1356Seschrock zap_cursor_t zc; 383*1356Seschrock zap_attribute_t za; 384*1356Seschrock char setpoint[MAXNAMELEN]; 385*1356Seschrock char *tmp; 386*1356Seschrock nvlist_t *prop; 387*1356Seschrock 388*1356Seschrock if (dsl_dataset_is_snapshot(ds)) { 389*1356Seschrock VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 390*1356Seschrock return (0); 391*1356Seschrock } 392*1356Seschrock 393*1356Seschrock dd = ds->ds_dir; 394*1356Seschrock 395*1356Seschrock VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 396*1356Seschrock 397*1356Seschrock dp = dd->dd_pool; 398*1356Seschrock mos = dp->dp_meta_objset; 399*1356Seschrock 400*1356Seschrock rw_enter(&dp->dp_config_rwlock, RW_READER); 401*1356Seschrock while (dd != NULL) { 402*1356Seschrock dsl_dir_name(dd, setpoint); 403*1356Seschrock 404*1356Seschrock for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); 405*1356Seschrock (err = zap_cursor_retrieve(&zc, &za)) == 0; 406*1356Seschrock zap_cursor_advance(&zc)) { 407*1356Seschrock if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0) 408*1356Seschrock continue; 409*1356Seschrock 410*1356Seschrock VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME, 411*1356Seschrock KM_SLEEP) == 0); 412*1356Seschrock if (za.za_integer_length == 1) { 413*1356Seschrock /* 414*1356Seschrock * String property 415*1356Seschrock */ 416*1356Seschrock 417*1356Seschrock tmp = kmem_alloc(za.za_num_integers, KM_SLEEP); 418*1356Seschrock err = zap_lookup(mos, 419*1356Seschrock dd->dd_phys->dd_props_zapobj, 420*1356Seschrock za.za_name, 1, za.za_num_integers, 421*1356Seschrock tmp); 422*1356Seschrock if (err != 0) { 423*1356Seschrock kmem_free(tmp, za.za_num_integers); 424*1356Seschrock break; 425*1356Seschrock } 426*1356Seschrock VERIFY(nvlist_add_string(prop, 427*1356Seschrock ZFS_PROP_VALUE, tmp) == 0); 428*1356Seschrock kmem_free(tmp, za.za_num_integers); 429*1356Seschrock } else { 430*1356Seschrock /* 431*1356Seschrock * Integer property 432*1356Seschrock */ 433*1356Seschrock ASSERT(za.za_integer_length == 8); 434*1356Seschrock (void) nvlist_add_uint64(prop, ZFS_PROP_VALUE, 435*1356Seschrock za.za_first_integer); 436*1356Seschrock } 437*1356Seschrock 438*1356Seschrock VERIFY(nvlist_add_string(prop, 439*1356Seschrock ZFS_PROP_SOURCE, setpoint) == 0); 440*1356Seschrock VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 441*1356Seschrock prop) == 0); 442*1356Seschrock nvlist_free(prop); 443*1356Seschrock } 444*1356Seschrock zap_cursor_fini(&zc); 445*1356Seschrock 446*1356Seschrock if (err != ENOENT) { 447*1356Seschrock if (dd != ds->ds_dir) 448*1356Seschrock dsl_dir_close(dd, FTAG); 449*1356Seschrock break; 450*1356Seschrock } else { 451*1356Seschrock err = 0; 452*1356Seschrock } 453*1356Seschrock 454*1356Seschrock /* 455*1356Seschrock * Continue to parent. 456*1356Seschrock */ 457*1356Seschrock if (dd->dd_phys->dd_parent_obj == 0) 458*1356Seschrock parent = NULL; 459*1356Seschrock else 460*1356Seschrock parent = dsl_dir_open_obj(dp, 461*1356Seschrock dd->dd_phys->dd_parent_obj, NULL, FTAG); 462*1356Seschrock if (dd != ds->ds_dir) 463*1356Seschrock dsl_dir_close(dd, FTAG); 464*1356Seschrock dd = parent; 465*1356Seschrock } 466*1356Seschrock rw_exit(&dp->dp_config_rwlock); 467*1356Seschrock 468*1356Seschrock return (err); 469*1356Seschrock } 470