1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5ea8dc4b6Seschrock * Common Development and Distribution License (the "License"). 6ea8dc4b6Seschrock * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21fa9e4066Sahrens /* 225c0b6a79SRich Morris * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23fa9e4066Sahrens * Use is subject to license terms. 24fa9e4066Sahrens */ 25fa9e4066Sahrens 26fa9e4066Sahrens #include <sys/dmu.h> 277f7322feSeschrock #include <sys/dmu_objset.h> 28fa9e4066Sahrens #include <sys/dmu_tx.h> 29fa9e4066Sahrens #include <sys/dsl_dataset.h> 30fa9e4066Sahrens #include <sys/dsl_dir.h> 31fa9e4066Sahrens #include <sys/dsl_prop.h> 321d452cf5Sahrens #include <sys/dsl_synctask.h> 33fa9e4066Sahrens #include <sys/spa.h> 34fa9e4066Sahrens #include <sys/zio_checksum.h> /* for the default checksum value */ 35fa9e4066Sahrens #include <sys/zap.h> 36fa9e4066Sahrens #include <sys/fs/zfs.h> 37fa9e4066Sahrens 38fa9e4066Sahrens #include "zfs_prop.h" 39fa9e4066Sahrens 40fa9e4066Sahrens static int 41fa9e4066Sahrens dodefault(const char *propname, int intsz, int numint, void *buf) 42fa9e4066Sahrens { 43fa9e4066Sahrens zfs_prop_t prop; 44fa9e4066Sahrens 45da6c28aaSamw /* 46da6c28aaSamw * The setonce properties are read-only, BUT they still 47da6c28aaSamw * have a default value that can be used as the initial 48da6c28aaSamw * value. 49da6c28aaSamw */ 50990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL || 51da6c28aaSamw (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop))) 52fa9e4066Sahrens return (ENOENT); 53fa9e4066Sahrens 5491ebeef5Sahrens if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) { 55fa9e4066Sahrens if (intsz != 1) 56fa9e4066Sahrens return (EOVERFLOW); 57990b4856Slling (void) strncpy(buf, zfs_prop_default_string(prop), 58990b4856Slling numint); 59fa9e4066Sahrens } else { 60fa9e4066Sahrens if (intsz != 8 || numint < 1) 61fa9e4066Sahrens return (EOVERFLOW); 62fa9e4066Sahrens 63fa9e4066Sahrens *(uint64_t *)buf = zfs_prop_default_numeric(prop); 64fa9e4066Sahrens } 65fa9e4066Sahrens 66fa9e4066Sahrens return (0); 67fa9e4066Sahrens } 68fa9e4066Sahrens 69bb0ade09Sahrens int 70bb0ade09Sahrens dsl_prop_get_dd(dsl_dir_t *dd, const char *propname, 71fa9e4066Sahrens int intsz, int numint, void *buf, char *setpoint) 72fa9e4066Sahrens { 7399653d4eSeschrock int err = ENOENT; 74bb0ade09Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 75e9dbad6fSeschrock zfs_prop_t prop; 76fa9e4066Sahrens 77bb0ade09Sahrens ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock)); 78bb0ade09Sahrens 79fa9e4066Sahrens if (setpoint) 80fa9e4066Sahrens setpoint[0] = '\0'; 81fa9e4066Sahrens 82e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 83e9dbad6fSeschrock 8499653d4eSeschrock /* 8599653d4eSeschrock * Note: dd may be NULL, therefore we shouldn't dereference it 8699653d4eSeschrock * ouside this loop. 8799653d4eSeschrock */ 8899653d4eSeschrock for (; dd != NULL; dd = dd->dd_parent) { 8999653d4eSeschrock ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock)); 90fa9e4066Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 91fa9e4066Sahrens propname, intsz, numint, buf); 92fa9e4066Sahrens if (err != ENOENT) { 93fa9e4066Sahrens if (setpoint) 94fa9e4066Sahrens dsl_dir_name(dd, setpoint); 95fa9e4066Sahrens break; 96fa9e4066Sahrens } 97e9dbad6fSeschrock 98e9dbad6fSeschrock /* 99e9dbad6fSeschrock * Break out of this loop for non-inheritable properties. 100e9dbad6fSeschrock */ 101da6c28aaSamw if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 102e9dbad6fSeschrock break; 103fa9e4066Sahrens } 104fa9e4066Sahrens if (err == ENOENT) 105fa9e4066Sahrens err = dodefault(propname, intsz, numint, buf); 106fa9e4066Sahrens 107fa9e4066Sahrens return (err); 108fa9e4066Sahrens } 109fa9e4066Sahrens 110bb0ade09Sahrens int 111bb0ade09Sahrens dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname, 112bb0ade09Sahrens int intsz, int numint, void *buf, char *setpoint) 113bb0ade09Sahrens { 114bb0ade09Sahrens ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock)); 115bb0ade09Sahrens 116bb0ade09Sahrens if (ds->ds_phys->ds_props_obj) { 117bb0ade09Sahrens int err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset, 118bb0ade09Sahrens ds->ds_phys->ds_props_obj, propname, intsz, numint, buf); 119bb0ade09Sahrens if (err != ENOENT) { 120bb0ade09Sahrens if (setpoint) 121bb0ade09Sahrens dsl_dataset_name(ds, setpoint); 122bb0ade09Sahrens return (err); 123bb0ade09Sahrens } 124bb0ade09Sahrens } 125bb0ade09Sahrens 126bb0ade09Sahrens return (dsl_prop_get_dd(ds->ds_dir, propname, 127bb0ade09Sahrens intsz, numint, buf, setpoint)); 128bb0ade09Sahrens } 129bb0ade09Sahrens 130fa9e4066Sahrens /* 131fa9e4066Sahrens * Register interest in the named property. We'll call the callback 132fa9e4066Sahrens * once to notify it of the current property value, and again each time 133fa9e4066Sahrens * the property changes, until this callback is unregistered. 134fa9e4066Sahrens * 135fa9e4066Sahrens * Return 0 on success, errno if the prop is not an integer value. 136fa9e4066Sahrens */ 137fa9e4066Sahrens int 138fa9e4066Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname, 139fa9e4066Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 140fa9e4066Sahrens { 14199653d4eSeschrock dsl_dir_t *dd = ds->ds_dir; 142bb0ade09Sahrens dsl_pool_t *dp = dd->dd_pool; 143fa9e4066Sahrens uint64_t value; 144fa9e4066Sahrens dsl_prop_cb_record_t *cbr; 145fa9e4066Sahrens int err; 1461d452cf5Sahrens int need_rwlock; 147fa9e4066Sahrens 148bb0ade09Sahrens need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock); 1491d452cf5Sahrens if (need_rwlock) 150bb0ade09Sahrens rw_enter(&dp->dp_config_rwlock, RW_READER); 151fa9e4066Sahrens 152bb0ade09Sahrens err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL); 153fa9e4066Sahrens if (err != 0) { 15427345066Sck if (need_rwlock) 155bb0ade09Sahrens rw_exit(&dp->dp_config_rwlock); 156fa9e4066Sahrens return (err); 157fa9e4066Sahrens } 158fa9e4066Sahrens 159fa9e4066Sahrens cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 16099653d4eSeschrock cbr->cbr_ds = ds; 161fa9e4066Sahrens cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 162fa9e4066Sahrens (void) strcpy((char *)cbr->cbr_propname, propname); 163fa9e4066Sahrens cbr->cbr_func = callback; 164fa9e4066Sahrens cbr->cbr_arg = cbarg; 165fa9e4066Sahrens mutex_enter(&dd->dd_lock); 166fa9e4066Sahrens list_insert_head(&dd->dd_prop_cbs, cbr); 167fa9e4066Sahrens mutex_exit(&dd->dd_lock); 168fa9e4066Sahrens 169fa9e4066Sahrens cbr->cbr_func(cbr->cbr_arg, value); 170fa9e4066Sahrens 171bb0ade09Sahrens VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object, 172ea8dc4b6Seschrock NULL, cbr, &dd)); 1731d452cf5Sahrens if (need_rwlock) 174bb0ade09Sahrens rw_exit(&dp->dp_config_rwlock); 175bb0ade09Sahrens /* Leave dir open until this callback is unregistered */ 176fa9e4066Sahrens return (0); 177fa9e4066Sahrens } 178fa9e4066Sahrens 179fa9e4066Sahrens int 180bb0ade09Sahrens dsl_prop_get(const char *dsname, const char *propname, 181fa9e4066Sahrens int intsz, int numints, void *buf, char *setpoint) 182fa9e4066Sahrens { 183bb0ade09Sahrens dsl_dataset_t *ds; 184fa9e4066Sahrens int err; 185fa9e4066Sahrens 186bb0ade09Sahrens err = dsl_dataset_hold(dsname, FTAG, &ds); 187ea8dc4b6Seschrock if (err) 188ea8dc4b6Seschrock return (err); 189fa9e4066Sahrens 190bb0ade09Sahrens rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER); 191bb0ade09Sahrens err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint); 192bb0ade09Sahrens rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock); 193fa9e4066Sahrens 194bb0ade09Sahrens dsl_dataset_rele(ds, FTAG); 195fa9e4066Sahrens return (err); 196fa9e4066Sahrens } 197fa9e4066Sahrens 198fa9e4066Sahrens /* 199fa9e4066Sahrens * Get the current property value. It may have changed by the time this 200fa9e4066Sahrens * function returns, so it is NOT safe to follow up with 201fa9e4066Sahrens * dsl_prop_register() and assume that the value has not changed in 202fa9e4066Sahrens * between. 203fa9e4066Sahrens * 204fa9e4066Sahrens * Return 0 on success, ENOENT if ddname is invalid. 205fa9e4066Sahrens */ 206fa9e4066Sahrens int 207fa9e4066Sahrens dsl_prop_get_integer(const char *ddname, const char *propname, 208fa9e4066Sahrens uint64_t *valuep, char *setpoint) 209fa9e4066Sahrens { 210fa9e4066Sahrens return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 211fa9e4066Sahrens } 212fa9e4066Sahrens 213fa9e4066Sahrens /* 214fa9e4066Sahrens * Unregister this callback. Return 0 on success, ENOENT if ddname is 215fa9e4066Sahrens * invalid, ENOMSG if no matching callback registered. 216fa9e4066Sahrens */ 217fa9e4066Sahrens int 218fa9e4066Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 219fa9e4066Sahrens dsl_prop_changed_cb_t *callback, void *cbarg) 220fa9e4066Sahrens { 22199653d4eSeschrock dsl_dir_t *dd = ds->ds_dir; 222fa9e4066Sahrens dsl_prop_cb_record_t *cbr; 223fa9e4066Sahrens 224fa9e4066Sahrens mutex_enter(&dd->dd_lock); 225fa9e4066Sahrens for (cbr = list_head(&dd->dd_prop_cbs); 226fa9e4066Sahrens cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 22799653d4eSeschrock if (cbr->cbr_ds == ds && 228fa9e4066Sahrens cbr->cbr_func == callback && 22999653d4eSeschrock cbr->cbr_arg == cbarg && 23099653d4eSeschrock strcmp(cbr->cbr_propname, propname) == 0) 231fa9e4066Sahrens break; 232fa9e4066Sahrens } 233fa9e4066Sahrens 234fa9e4066Sahrens if (cbr == NULL) { 235fa9e4066Sahrens mutex_exit(&dd->dd_lock); 236fa9e4066Sahrens return (ENOMSG); 237fa9e4066Sahrens } 238fa9e4066Sahrens 239fa9e4066Sahrens list_remove(&dd->dd_prop_cbs, cbr); 240fa9e4066Sahrens mutex_exit(&dd->dd_lock); 241fa9e4066Sahrens kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 242fa9e4066Sahrens kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 243fa9e4066Sahrens 244fa9e4066Sahrens /* Clean up from dsl_prop_register */ 245fa9e4066Sahrens dsl_dir_close(dd, cbr); 246fa9e4066Sahrens return (0); 247fa9e4066Sahrens } 248fa9e4066Sahrens 24999653d4eSeschrock /* 25099653d4eSeschrock * Return the number of callbacks that are registered for this dataset. 25199653d4eSeschrock */ 25299653d4eSeschrock int 25399653d4eSeschrock dsl_prop_numcb(dsl_dataset_t *ds) 25499653d4eSeschrock { 25599653d4eSeschrock dsl_dir_t *dd = ds->ds_dir; 25699653d4eSeschrock dsl_prop_cb_record_t *cbr; 25799653d4eSeschrock int num = 0; 25899653d4eSeschrock 25999653d4eSeschrock mutex_enter(&dd->dd_lock); 26099653d4eSeschrock for (cbr = list_head(&dd->dd_prop_cbs); 26199653d4eSeschrock cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 26299653d4eSeschrock if (cbr->cbr_ds == ds) 26399653d4eSeschrock num++; 26499653d4eSeschrock } 26599653d4eSeschrock mutex_exit(&dd->dd_lock); 26699653d4eSeschrock 26799653d4eSeschrock return (num); 26899653d4eSeschrock } 26999653d4eSeschrock 270fa9e4066Sahrens static void 271fa9e4066Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 272fa9e4066Sahrens const char *propname, uint64_t value, int first) 273fa9e4066Sahrens { 274fa9e4066Sahrens dsl_dir_t *dd; 275fa9e4066Sahrens dsl_prop_cb_record_t *cbr; 276fa9e4066Sahrens objset_t *mos = dp->dp_meta_objset; 2771d452cf5Sahrens zap_cursor_t zc; 278d8d77200Sahrens zap_attribute_t *za; 279fa9e4066Sahrens int err; 280bb0ade09Sahrens uint64_t dummyval; 281fa9e4066Sahrens 282fa9e4066Sahrens ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 283ea8dc4b6Seschrock err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd); 284ea8dc4b6Seschrock if (err) 285ea8dc4b6Seschrock return; 286fa9e4066Sahrens 287fa9e4066Sahrens if (!first) { 288fa9e4066Sahrens /* 289fa9e4066Sahrens * If the prop is set here, then this change is not 290fa9e4066Sahrens * being inherited here or below; stop the recursion. 291fa9e4066Sahrens */ 292fa9e4066Sahrens err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 293bb0ade09Sahrens 8, 1, &dummyval); 294fa9e4066Sahrens if (err == 0) { 295fa9e4066Sahrens dsl_dir_close(dd, FTAG); 296fa9e4066Sahrens return; 297fa9e4066Sahrens } 298fa9e4066Sahrens ASSERT3U(err, ==, ENOENT); 299fa9e4066Sahrens } 300fa9e4066Sahrens 301fa9e4066Sahrens mutex_enter(&dd->dd_lock); 302bb0ade09Sahrens for (cbr = list_head(&dd->dd_prop_cbs); cbr; 303bb0ade09Sahrens cbr = list_next(&dd->dd_prop_cbs, cbr)) { 304bb0ade09Sahrens uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj; 305bb0ade09Sahrens 306bb0ade09Sahrens if (strcmp(cbr->cbr_propname, propname) != 0) 307bb0ade09Sahrens continue; 308bb0ade09Sahrens 309bb0ade09Sahrens /* 310bb0ade09Sahrens * If the property is set on this ds, then it is not 311bb0ade09Sahrens * inherited here; don't call the callback. 312bb0ade09Sahrens */ 313bb0ade09Sahrens if (propobj && 0 == zap_lookup(mos, propobj, propname, 314bb0ade09Sahrens 8, 1, &dummyval)) 315bb0ade09Sahrens continue; 316bb0ade09Sahrens 317bb0ade09Sahrens cbr->cbr_func(cbr->cbr_arg, value); 318fa9e4066Sahrens } 319fa9e4066Sahrens mutex_exit(&dd->dd_lock); 320fa9e4066Sahrens 321d8d77200Sahrens za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); 3221d452cf5Sahrens for (zap_cursor_init(&zc, mos, 3231d452cf5Sahrens dd->dd_phys->dd_child_dir_zapobj); 324d8d77200Sahrens zap_cursor_retrieve(&zc, za) == 0; 3251d452cf5Sahrens zap_cursor_advance(&zc)) { 326d8d77200Sahrens dsl_prop_changed_notify(dp, za->za_first_integer, 3271d452cf5Sahrens propname, value, FALSE); 328fa9e4066Sahrens } 329d8d77200Sahrens kmem_free(za, sizeof (zap_attribute_t)); 3301d452cf5Sahrens zap_cursor_fini(&zc); 331fa9e4066Sahrens dsl_dir_close(dd, FTAG); 332fa9e4066Sahrens } 333fa9e4066Sahrens 334fa9e4066Sahrens struct prop_set_arg { 335fa9e4066Sahrens const char *name; 336fa9e4066Sahrens int intsz; 337fa9e4066Sahrens int numints; 338fa9e4066Sahrens const void *buf; 339fa9e4066Sahrens }; 340fa9e4066Sahrens 3411d452cf5Sahrens 3421d452cf5Sahrens static void 343ecd6cf80Smarks dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 344fa9e4066Sahrens { 345bb0ade09Sahrens dsl_dataset_t *ds = arg1; 3461d452cf5Sahrens struct prop_set_arg *psa = arg2; 347bb0ade09Sahrens objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 348bb0ade09Sahrens uint64_t zapobj, intval; 3491d452cf5Sahrens int isint; 350ecd6cf80Smarks char valbuf[32]; 351ecd6cf80Smarks char *valstr; 352fa9e4066Sahrens 353fa9e4066Sahrens isint = (dodefault(psa->name, 8, 1, &intval) == 0); 354fa9e4066Sahrens 355bb0ade09Sahrens if (dsl_dataset_is_snapshot(ds)) { 356bb0ade09Sahrens ASSERT(spa_version(ds->ds_dir->dd_pool->dp_spa) >= 357bb0ade09Sahrens SPA_VERSION_SNAP_PROPS); 358bb0ade09Sahrens if (ds->ds_phys->ds_props_obj == 0) { 359bb0ade09Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 360bb0ade09Sahrens ds->ds_phys->ds_props_obj = 361bb0ade09Sahrens zap_create(mos, 362bb0ade09Sahrens DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx); 363bb0ade09Sahrens } 364bb0ade09Sahrens zapobj = ds->ds_phys->ds_props_obj; 365bb0ade09Sahrens } else { 366bb0ade09Sahrens zapobj = ds->ds_dir->dd_phys->dd_props_zapobj; 367bb0ade09Sahrens } 368bb0ade09Sahrens 369fa9e4066Sahrens if (psa->numints == 0) { 3701d452cf5Sahrens int err = zap_remove(mos, zapobj, psa->name, tx); 3711d452cf5Sahrens ASSERT(err == 0 || err == ENOENT); 3721d452cf5Sahrens if (isint) { 373bb0ade09Sahrens VERIFY(0 == dsl_prop_get_ds(ds, 3741d452cf5Sahrens psa->name, 8, 1, &intval, NULL)); 375fa9e4066Sahrens } 376fa9e4066Sahrens } else { 3771d452cf5Sahrens VERIFY(0 == zap_update(mos, zapobj, psa->name, 3781d452cf5Sahrens psa->intsz, psa->numints, psa->buf, tx)); 379fa9e4066Sahrens if (isint) 380fa9e4066Sahrens intval = *(uint64_t *)psa->buf; 381fa9e4066Sahrens } 382fa9e4066Sahrens 3831d452cf5Sahrens if (isint) { 384bb0ade09Sahrens if (dsl_dataset_is_snapshot(ds)) { 385bb0ade09Sahrens dsl_prop_cb_record_t *cbr; 386bb0ade09Sahrens /* 387bb0ade09Sahrens * It's a snapshot; nothing can inherit this 388bb0ade09Sahrens * property, so just look for callbacks on this 389bb0ade09Sahrens * ds here. 390bb0ade09Sahrens */ 391bb0ade09Sahrens mutex_enter(&ds->ds_dir->dd_lock); 392bb0ade09Sahrens for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr; 393bb0ade09Sahrens cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) { 394bb0ade09Sahrens if (cbr->cbr_ds == ds && 395bb0ade09Sahrens strcmp(cbr->cbr_propname, psa->name) == 0) 396bb0ade09Sahrens cbr->cbr_func(cbr->cbr_arg, intval); 397bb0ade09Sahrens } 398bb0ade09Sahrens mutex_exit(&ds->ds_dir->dd_lock); 399bb0ade09Sahrens } else { 400bb0ade09Sahrens dsl_prop_changed_notify(ds->ds_dir->dd_pool, 401bb0ade09Sahrens ds->ds_dir->dd_object, psa->name, intval, TRUE); 402bb0ade09Sahrens } 403fa9e4066Sahrens } 404ecd6cf80Smarks if (isint) { 405ecd6cf80Smarks (void) snprintf(valbuf, sizeof (valbuf), 406ecd6cf80Smarks "%lld", (longlong_t)intval); 407ecd6cf80Smarks valstr = valbuf; 408ecd6cf80Smarks } else { 409ecd6cf80Smarks valstr = (char *)psa->buf; 410ecd6cf80Smarks } 411ecd6cf80Smarks spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT : 412bb0ade09Sahrens LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx, cr, 413bb0ade09Sahrens "%s=%s dataset = %llu", psa->name, valstr, ds->ds_object); 414fa9e4066Sahrens } 415fa9e4066Sahrens 416ea2f5b9eSMatthew Ahrens void 4175c0b6a79SRich Morris dsl_props_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 4185c0b6a79SRich Morris { 4195c0b6a79SRich Morris dsl_dataset_t *ds = arg1; 4205c0b6a79SRich Morris nvlist_t *nvl = arg2; 4215c0b6a79SRich Morris nvpair_t *elem = NULL; 4225c0b6a79SRich Morris 4235c0b6a79SRich Morris while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 4245c0b6a79SRich Morris struct prop_set_arg psa; 4255c0b6a79SRich Morris 4265c0b6a79SRich Morris psa.name = nvpair_name(elem); 4275c0b6a79SRich Morris 4285c0b6a79SRich Morris if (nvpair_type(elem) == DATA_TYPE_STRING) { 4295c0b6a79SRich Morris VERIFY(nvpair_value_string(elem, 4305c0b6a79SRich Morris (char **)&psa.buf) == 0); 4315c0b6a79SRich Morris psa.intsz = 1; 4325c0b6a79SRich Morris psa.numints = strlen(psa.buf) + 1; 4335c0b6a79SRich Morris } else { 4345c0b6a79SRich Morris uint64_t intval; 4355c0b6a79SRich Morris VERIFY(nvpair_value_uint64(elem, &intval) == 0); 4365c0b6a79SRich Morris psa.intsz = sizeof (intval); 4375c0b6a79SRich Morris psa.numints = 1; 4385c0b6a79SRich Morris psa.buf = &intval; 4395c0b6a79SRich Morris } 4405c0b6a79SRich Morris dsl_prop_set_sync(ds, &psa, cr, tx); 4415c0b6a79SRich Morris } 4425c0b6a79SRich Morris } 4435c0b6a79SRich Morris 444a9799022Sck void 445a9799022Sck dsl_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val, 446a9799022Sck cred_t *cr, dmu_tx_t *tx) 447a9799022Sck { 448a9799022Sck objset_t *mos = dd->dd_pool->dp_meta_objset; 449a9799022Sck uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 450a9799022Sck 451a9799022Sck ASSERT(dmu_tx_is_syncing(tx)); 452a9799022Sck 453a9799022Sck VERIFY(0 == zap_update(mos, zapobj, name, sizeof (val), 1, &val, tx)); 454a9799022Sck 455a9799022Sck dsl_prop_changed_notify(dd->dd_pool, dd->dd_object, name, val, TRUE); 456a9799022Sck 457a9799022Sck spa_history_internal_log(LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr, 458a9799022Sck "%s=%llu dataset = %llu", name, (u_longlong_t)val, 459a9799022Sck dd->dd_phys->dd_head_dataset_obj); 460a9799022Sck } 461a9799022Sck 462a2eea2e1Sahrens int 463bb0ade09Sahrens dsl_prop_set(const char *dsname, const char *propname, 464fa9e4066Sahrens int intsz, int numints, const void *buf) 465fa9e4066Sahrens { 466bb0ade09Sahrens dsl_dataset_t *ds; 467*478ed9adSEric Taylor uint64_t version; 468fa9e4066Sahrens int err; 469bb0ade09Sahrens struct prop_set_arg psa; 470fa9e4066Sahrens 471455d5089Sahrens /* 472455d5089Sahrens * We must do these checks before we get to the syncfunc, since 473455d5089Sahrens * it can't fail. 474455d5089Sahrens */ 475455d5089Sahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 476455d5089Sahrens return (ENAMETOOLONG); 477455d5089Sahrens 478bb0ade09Sahrens err = dsl_dataset_hold(dsname, FTAG, &ds); 479ea8dc4b6Seschrock if (err) 480ea8dc4b6Seschrock return (err); 481bb0ade09Sahrens 482*478ed9adSEric Taylor version = spa_version(ds->ds_dir->dd_pool->dp_spa); 483*478ed9adSEric Taylor if (intsz * numints >= (version < SPA_VERSION_STMF_PROP ? 484*478ed9adSEric Taylor ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) { 485*478ed9adSEric Taylor dsl_dataset_rele(ds, FTAG); 486*478ed9adSEric Taylor return (E2BIG); 487*478ed9adSEric Taylor } 488bb0ade09Sahrens if (dsl_dataset_is_snapshot(ds) && 489*478ed9adSEric Taylor version < SPA_VERSION_SNAP_PROPS) { 490bb0ade09Sahrens dsl_dataset_rele(ds, FTAG); 491bb0ade09Sahrens return (ENOTSUP); 492bb0ade09Sahrens } 493bb0ade09Sahrens 494bb0ade09Sahrens psa.name = propname; 495bb0ade09Sahrens psa.intsz = intsz; 496bb0ade09Sahrens psa.numints = numints; 497bb0ade09Sahrens psa.buf = buf; 498bb0ade09Sahrens err = dsl_sync_task_do(ds->ds_dir->dd_pool, 499bb0ade09Sahrens NULL, dsl_prop_set_sync, ds, &psa, 2); 500bb0ade09Sahrens 501bb0ade09Sahrens dsl_dataset_rele(ds, FTAG); 502fa9e4066Sahrens return (err); 503fa9e4066Sahrens } 5047f7322feSeschrock 5055c0b6a79SRich Morris int 5065c0b6a79SRich Morris dsl_props_set(const char *dsname, nvlist_t *nvl) 5075c0b6a79SRich Morris { 5085c0b6a79SRich Morris dsl_dataset_t *ds; 509*478ed9adSEric Taylor uint64_t version; 5105c0b6a79SRich Morris nvpair_t *elem = NULL; 5115c0b6a79SRich Morris int err; 5125c0b6a79SRich Morris 513*478ed9adSEric Taylor if (err = dsl_dataset_hold(dsname, FTAG, &ds)) 514*478ed9adSEric Taylor return (err); 515ccba0801SRich Morris /* 516ccba0801SRich Morris * Do these checks before the syncfunc, since it can't fail. 517ccba0801SRich Morris */ 518*478ed9adSEric Taylor version = spa_version(ds->ds_dir->dd_pool->dp_spa); 5195c0b6a79SRich Morris while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 520*478ed9adSEric Taylor if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 521*478ed9adSEric Taylor dsl_dataset_rele(ds, FTAG); 522ccba0801SRich Morris return (ENAMETOOLONG); 523*478ed9adSEric Taylor } 5245c0b6a79SRich Morris if (nvpair_type(elem) == DATA_TYPE_STRING) { 5255c0b6a79SRich Morris char *valstr; 5265c0b6a79SRich Morris VERIFY(nvpair_value_string(elem, &valstr) == 0); 527*478ed9adSEric Taylor if (strlen(valstr) >= (version < 528*478ed9adSEric Taylor SPA_VERSION_STMF_PROP ? 529*478ed9adSEric Taylor ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) { 530*478ed9adSEric Taylor dsl_dataset_rele(ds, FTAG); 531ccba0801SRich Morris return (E2BIG); 532*478ed9adSEric Taylor } 5335c0b6a79SRich Morris } 5345c0b6a79SRich Morris } 5355c0b6a79SRich Morris 5365c0b6a79SRich Morris if (dsl_dataset_is_snapshot(ds) && 537*478ed9adSEric Taylor version < SPA_VERSION_SNAP_PROPS) { 5385c0b6a79SRich Morris dsl_dataset_rele(ds, FTAG); 5395c0b6a79SRich Morris return (ENOTSUP); 5405c0b6a79SRich Morris } 5415c0b6a79SRich Morris 5425c0b6a79SRich Morris err = dsl_sync_task_do(ds->ds_dir->dd_pool, 5435c0b6a79SRich Morris NULL, dsl_props_set_sync, ds, nvl, 2); 5445c0b6a79SRich Morris 5455c0b6a79SRich Morris dsl_dataset_rele(ds, FTAG); 5465c0b6a79SRich Morris return (err); 5475c0b6a79SRich Morris } 5485c0b6a79SRich Morris 5497f7322feSeschrock /* 5507f7322feSeschrock * Iterate over all properties for this dataset and return them in an nvlist. 5517f7322feSeschrock */ 5527f7322feSeschrock int 553745cd3c5Smaybee dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local) 5547f7322feSeschrock { 5557f7322feSeschrock dsl_dataset_t *ds = os->os->os_dsl_dataset; 55699653d4eSeschrock dsl_dir_t *dd = ds->ds_dir; 557bb0ade09Sahrens boolean_t snapshot = dsl_dataset_is_snapshot(ds); 5587f7322feSeschrock int err = 0; 559bb0ade09Sahrens dsl_pool_t *dp = dd->dd_pool; 560bb0ade09Sahrens objset_t *mos = dp->dp_meta_objset; 561bb0ade09Sahrens uint64_t propobj = ds->ds_phys->ds_props_obj; 5627f7322feSeschrock 5637f7322feSeschrock VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 5647f7322feSeschrock 565bb0ade09Sahrens if (local && snapshot && !propobj) 566bb0ade09Sahrens return (0); 5677f7322feSeschrock 5687f7322feSeschrock rw_enter(&dp->dp_config_rwlock, RW_READER); 569bb0ade09Sahrens while (dd != NULL) { 570a2eea2e1Sahrens char setpoint[MAXNAMELEN]; 571a2eea2e1Sahrens zap_cursor_t zc; 572a2eea2e1Sahrens zap_attribute_t za; 573bb0ade09Sahrens dsl_dir_t *dd_next; 574bb0ade09Sahrens 575bb0ade09Sahrens if (propobj) { 576bb0ade09Sahrens dsl_dataset_name(ds, setpoint); 577bb0ade09Sahrens dd_next = dd; 578bb0ade09Sahrens } else { 579bb0ade09Sahrens dsl_dir_name(dd, setpoint); 580bb0ade09Sahrens propobj = dd->dd_phys->dd_props_zapobj; 581bb0ade09Sahrens dd_next = dd->dd_parent; 582bb0ade09Sahrens } 583a2eea2e1Sahrens 584bb0ade09Sahrens for (zap_cursor_init(&zc, mos, propobj); 5857f7322feSeschrock (err = zap_cursor_retrieve(&zc, &za)) == 0; 5867f7322feSeschrock zap_cursor_advance(&zc)) { 587a2eea2e1Sahrens nvlist_t *propval; 588bb0ade09Sahrens zfs_prop_t prop = zfs_name_to_prop(za.za_name); 589bb0ade09Sahrens 590bb0ade09Sahrens /* Skip non-inheritable properties. */ 591bb0ade09Sahrens if (prop != ZPROP_INVAL && 592bb0ade09Sahrens !zfs_prop_inheritable(prop) && 593bb0ade09Sahrens (dd != ds->ds_dir || (snapshot && dd != dd_next))) 594e9dbad6fSeschrock continue; 595e9dbad6fSeschrock 596bb0ade09Sahrens /* Skip properties not valid for this type. */ 597bb0ade09Sahrens if (snapshot && prop != ZPROP_INVAL && 598da6c28aaSamw !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT)) 599da6c28aaSamw continue; 600da6c28aaSamw 601bb0ade09Sahrens /* Skip properties already defined */ 602e9dbad6fSeschrock if (nvlist_lookup_nvlist(*nvp, za.za_name, 603e9dbad6fSeschrock &propval) == 0) 6047f7322feSeschrock continue; 6057f7322feSeschrock 606e9dbad6fSeschrock VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, 6077f7322feSeschrock KM_SLEEP) == 0); 6087f7322feSeschrock if (za.za_integer_length == 1) { 6097f7322feSeschrock /* 6107f7322feSeschrock * String property 6117f7322feSeschrock */ 612a2eea2e1Sahrens char *tmp = kmem_alloc(za.za_num_integers, 613a2eea2e1Sahrens KM_SLEEP); 614bb0ade09Sahrens err = zap_lookup(mos, propobj, 615bb0ade09Sahrens za.za_name, 1, za.za_num_integers, tmp); 6167f7322feSeschrock if (err != 0) { 6177f7322feSeschrock kmem_free(tmp, za.za_num_integers); 6187f7322feSeschrock break; 6197f7322feSeschrock } 620990b4856Slling VERIFY(nvlist_add_string(propval, ZPROP_VALUE, 621990b4856Slling tmp) == 0); 6227f7322feSeschrock kmem_free(tmp, za.za_num_integers); 6237f7322feSeschrock } else { 6247f7322feSeschrock /* 6257f7322feSeschrock * Integer property 6267f7322feSeschrock */ 6277f7322feSeschrock ASSERT(za.za_integer_length == 8); 628990b4856Slling (void) nvlist_add_uint64(propval, ZPROP_VALUE, 629990b4856Slling za.za_first_integer); 6307f7322feSeschrock } 6317f7322feSeschrock 632990b4856Slling VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, 633990b4856Slling setpoint) == 0); 6347f7322feSeschrock VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 635e9dbad6fSeschrock propval) == 0); 636e9dbad6fSeschrock nvlist_free(propval); 6377f7322feSeschrock } 6387f7322feSeschrock zap_cursor_fini(&zc); 6397f7322feSeschrock 64099653d4eSeschrock if (err != ENOENT) 6417f7322feSeschrock break; 64299653d4eSeschrock err = 0; 643745cd3c5Smaybee /* 644745cd3c5Smaybee * If we are just after the props that have been set 645745cd3c5Smaybee * locally, then we are done after the first iteration. 646745cd3c5Smaybee */ 647745cd3c5Smaybee if (local) 648745cd3c5Smaybee break; 649bb0ade09Sahrens dd = dd_next; 650bb0ade09Sahrens propobj = 0; 6517f7322feSeschrock } 6527f7322feSeschrock rw_exit(&dp->dp_config_rwlock); 6537f7322feSeschrock 6547f7322feSeschrock return (err); 6557f7322feSeschrock } 656a2eea2e1Sahrens 657a2eea2e1Sahrens void 658a2eea2e1Sahrens dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) 659a2eea2e1Sahrens { 660a2eea2e1Sahrens nvlist_t *propval; 661a2eea2e1Sahrens 662a2eea2e1Sahrens VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 663990b4856Slling VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); 664a2eea2e1Sahrens VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 665a2eea2e1Sahrens nvlist_free(propval); 666a2eea2e1Sahrens } 667a2eea2e1Sahrens 668a2eea2e1Sahrens void 669a2eea2e1Sahrens dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) 670a2eea2e1Sahrens { 671a2eea2e1Sahrens nvlist_t *propval; 672a2eea2e1Sahrens 673a2eea2e1Sahrens VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 674990b4856Slling VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); 675a2eea2e1Sahrens VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 676a2eea2e1Sahrens nvlist_free(propval); 677a2eea2e1Sahrens } 678