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