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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright (c) 2013 by Delphix. All rights reserved. 24 */ 25 26 #include <sys/zfs_context.h> 27 #include <sys/dsl_userhold.h> 28 #include <sys/dsl_dataset.h> 29 #include <sys/dsl_destroy.h> 30 #include <sys/dsl_synctask.h> 31 #include <sys/dmu_tx.h> 32 #include <sys/zfs_onexit.h> 33 #include <sys/dsl_pool.h> 34 #include <sys/dsl_dir.h> 35 #include <sys/zfs_ioctl.h> 36 #include <sys/zap.h> 37 38 typedef struct dsl_dataset_user_hold_arg { 39 nvlist_t *dduha_holds; 40 nvlist_t *dduha_errlist; 41 minor_t dduha_minor; 42 } dsl_dataset_user_hold_arg_t; 43 44 /* 45 * If you add new checks here, you may need to add additional checks to the 46 * "temporary" case in snapshot_check() in dmu_objset.c. 47 */ 48 int 49 dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag, 50 boolean_t temphold, dmu_tx_t *tx) 51 { 52 dsl_pool_t *dp = dmu_tx_pool(tx); 53 objset_t *mos = dp->dp_meta_objset; 54 int error = 0; 55 56 if (strlen(htag) > MAXNAMELEN) 57 return (E2BIG); 58 /* Tempholds have a more restricted length */ 59 if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN) 60 return (E2BIG); 61 62 /* tags must be unique (if ds already exists) */ 63 if (ds != NULL) { 64 mutex_enter(&ds->ds_lock); 65 if (ds->ds_phys->ds_userrefs_obj != 0) { 66 uint64_t value; 67 error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj, 68 htag, 8, 1, &value); 69 if (error == 0) 70 error = SET_ERROR(EEXIST); 71 else if (error == ENOENT) 72 error = 0; 73 } 74 mutex_exit(&ds->ds_lock); 75 } 76 77 return (error); 78 } 79 80 static int 81 dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) 82 { 83 dsl_dataset_user_hold_arg_t *dduha = arg; 84 dsl_pool_t *dp = dmu_tx_pool(tx); 85 nvpair_t *pair; 86 int rv = 0; 87 88 if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) 89 return (SET_ERROR(ENOTSUP)); 90 91 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL; 92 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { 93 int error = 0; 94 dsl_dataset_t *ds; 95 char *htag; 96 97 /* must be a snapshot */ 98 if (strchr(nvpair_name(pair), '@') == NULL) 99 error = SET_ERROR(EINVAL); 100 101 if (error == 0) 102 error = nvpair_value_string(pair, &htag); 103 if (error == 0) { 104 error = dsl_dataset_hold(dp, 105 nvpair_name(pair), FTAG, &ds); 106 } 107 if (error == 0) { 108 error = dsl_dataset_user_hold_check_one(ds, htag, 109 dduha->dduha_minor != 0, tx); 110 dsl_dataset_rele(ds, FTAG); 111 } 112 113 if (error != 0) { 114 rv = error; 115 fnvlist_add_int32(dduha->dduha_errlist, 116 nvpair_name(pair), error); 117 } 118 } 119 return (rv); 120 } 121 122 void 123 dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag, 124 minor_t minor, uint64_t now, dmu_tx_t *tx) 125 { 126 dsl_pool_t *dp = ds->ds_dir->dd_pool; 127 objset_t *mos = dp->dp_meta_objset; 128 uint64_t zapobj; 129 130 mutex_enter(&ds->ds_lock); 131 if (ds->ds_phys->ds_userrefs_obj == 0) { 132 /* 133 * This is the first user hold for this dataset. Create 134 * the userrefs zap object. 135 */ 136 dmu_buf_will_dirty(ds->ds_dbuf, tx); 137 zapobj = ds->ds_phys->ds_userrefs_obj = 138 zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); 139 } else { 140 zapobj = ds->ds_phys->ds_userrefs_obj; 141 } 142 ds->ds_userrefs++; 143 mutex_exit(&ds->ds_lock); 144 145 VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx)); 146 147 if (minor != 0) { 148 VERIFY0(dsl_pool_user_hold(dp, ds->ds_object, 149 htag, now, tx)); 150 } 151 152 spa_history_log_internal_ds(ds, "hold", tx, 153 "tag=%s temp=%d refs=%llu", 154 htag, minor != 0, ds->ds_userrefs); 155 } 156 157 static void 158 dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx) 159 { 160 dsl_dataset_user_hold_arg_t *dduha = arg; 161 dsl_pool_t *dp = dmu_tx_pool(tx); 162 nvpair_t *pair; 163 uint64_t now = gethrestime_sec(); 164 165 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL; 166 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { 167 dsl_dataset_t *ds; 168 169 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); 170 dsl_dataset_user_hold_sync_one(ds, fnvpair_value_string(pair), 171 dduha->dduha_minor, now, tx); 172 dsl_dataset_rele(ds, FTAG); 173 } 174 } 175 176 /* 177 * holds is nvl of snapname -> holdname 178 * errlist will be filled in with snapname -> error 179 * if cleanup_minor is not 0, the holds will be temporary, cleaned up 180 * when the process exits. 181 * 182 * if any fails, all will fail. 183 */ 184 int 185 dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist) 186 { 187 dsl_dataset_user_hold_arg_t dduha; 188 nvpair_t *pair; 189 int ret; 190 191 pair = nvlist_next_nvpair(holds, NULL); 192 if (pair == NULL) 193 return (0); 194 195 dduha.dduha_holds = holds; 196 dduha.dduha_errlist = errlist; 197 dduha.dduha_minor = cleanup_minor; 198 199 ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check, 200 dsl_dataset_user_hold_sync, &dduha, fnvlist_num_pairs(holds)); 201 if (ret == 0) 202 dsl_register_onexit_hold_cleanup(holds, cleanup_minor); 203 204 return (ret); 205 } 206 207 typedef struct dsl_dataset_user_release_arg { 208 nvlist_t *ddura_holds; 209 nvlist_t *ddura_todelete; 210 nvlist_t *ddura_errlist; 211 } dsl_dataset_user_release_arg_t; 212 213 static int 214 dsl_dataset_user_release_check_one(dsl_dataset_t *ds, 215 nvlist_t *holds, boolean_t *todelete) 216 { 217 uint64_t zapobj; 218 nvpair_t *pair; 219 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 220 int error; 221 int numholds = 0; 222 223 *todelete = B_FALSE; 224 225 if (!dsl_dataset_is_snapshot(ds)) 226 return (SET_ERROR(EINVAL)); 227 228 zapobj = ds->ds_phys->ds_userrefs_obj; 229 if (zapobj == 0) 230 return (SET_ERROR(ESRCH)); 231 232 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 233 pair = nvlist_next_nvpair(holds, pair)) { 234 /* Make sure the hold exists */ 235 uint64_t tmp; 236 error = zap_lookup(mos, zapobj, nvpair_name(pair), 8, 1, &tmp); 237 if (error == ENOENT) 238 error = SET_ERROR(ESRCH); 239 if (error != 0) 240 return (error); 241 numholds++; 242 } 243 244 if (DS_IS_DEFER_DESTROY(ds) && ds->ds_phys->ds_num_children == 1 && 245 ds->ds_userrefs == numholds) { 246 /* we need to destroy the snapshot as well */ 247 248 if (dsl_dataset_long_held(ds)) 249 return (SET_ERROR(EBUSY)); 250 *todelete = B_TRUE; 251 } 252 return (0); 253 } 254 255 static int 256 dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) 257 { 258 dsl_dataset_user_release_arg_t *ddura = arg; 259 dsl_pool_t *dp = dmu_tx_pool(tx); 260 nvpair_t *pair; 261 int rv = 0; 262 263 if (!dmu_tx_is_syncing(tx)) 264 return (0); 265 266 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL; 267 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { 268 const char *name = nvpair_name(pair); 269 int error; 270 dsl_dataset_t *ds; 271 nvlist_t *holds; 272 273 error = nvpair_value_nvlist(pair, &holds); 274 if (error != 0) 275 return (SET_ERROR(EINVAL)); 276 277 error = dsl_dataset_hold(dp, name, FTAG, &ds); 278 if (error == 0) { 279 boolean_t deleteme; 280 error = dsl_dataset_user_release_check_one(ds, 281 holds, &deleteme); 282 if (error == 0 && deleteme) { 283 fnvlist_add_boolean(ddura->ddura_todelete, 284 name); 285 } 286 dsl_dataset_rele(ds, FTAG); 287 } 288 if (error != 0) { 289 if (ddura->ddura_errlist != NULL) { 290 fnvlist_add_int32(ddura->ddura_errlist, 291 name, error); 292 } 293 rv = error; 294 } 295 } 296 return (rv); 297 } 298 299 static void 300 dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds, 301 dmu_tx_t *tx) 302 { 303 dsl_pool_t *dp = ds->ds_dir->dd_pool; 304 objset_t *mos = dp->dp_meta_objset; 305 uint64_t zapobj; 306 int error; 307 nvpair_t *pair; 308 309 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 310 pair = nvlist_next_nvpair(holds, pair)) { 311 ds->ds_userrefs--; 312 error = dsl_pool_user_release(dp, ds->ds_object, 313 nvpair_name(pair), tx); 314 VERIFY(error == 0 || error == ENOENT); 315 zapobj = ds->ds_phys->ds_userrefs_obj; 316 VERIFY0(zap_remove(mos, zapobj, nvpair_name(pair), tx)); 317 318 spa_history_log_internal_ds(ds, "release", tx, 319 "tag=%s refs=%lld", nvpair_name(pair), 320 (longlong_t)ds->ds_userrefs); 321 } 322 } 323 324 static void 325 dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) 326 { 327 dsl_dataset_user_release_arg_t *ddura = arg; 328 dsl_pool_t *dp = dmu_tx_pool(tx); 329 nvpair_t *pair; 330 331 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL; 332 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { 333 dsl_dataset_t *ds; 334 335 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); 336 dsl_dataset_user_release_sync_one(ds, 337 fnvpair_value_nvlist(pair), tx); 338 if (nvlist_exists(ddura->ddura_todelete, 339 nvpair_name(pair))) { 340 ASSERT(ds->ds_userrefs == 0 && 341 ds->ds_phys->ds_num_children == 1 && 342 DS_IS_DEFER_DESTROY(ds)); 343 dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); 344 } 345 dsl_dataset_rele(ds, FTAG); 346 } 347 } 348 349 /* 350 * holds is nvl of snapname -> { holdname, ... } 351 * errlist will be filled in with snapname -> error 352 * 353 * if any fails, all will fail. 354 */ 355 int 356 dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist) 357 { 358 dsl_dataset_user_release_arg_t ddura; 359 nvpair_t *pair, *pair2; 360 int error; 361 362 pair = nvlist_next_nvpair(holds, NULL); 363 if (pair == NULL) 364 return (0); 365 366 #ifdef _KERNEL 367 /* 368 * The release may cause the snapshot to be destroyed; make sure it 369 * is not mounted. 370 */ 371 for (pair2 = pair; pair2 != NULL; 372 pair2 = nvlist_next_nvpair(holds, pair2)) { 373 zfs_unmount_snap(nvpair_name(pair2)); 374 } 375 #endif 376 377 ddura.ddura_holds = holds; 378 ddura.ddura_errlist = errlist; 379 ddura.ddura_todelete = fnvlist_alloc(); 380 381 error = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_release_check, 382 dsl_dataset_user_release_sync, &ddura, fnvlist_num_pairs(holds)); 383 fnvlist_free(ddura.ddura_todelete); 384 return (error); 385 } 386 387 static void 388 dsl_dataset_user_release_onexit(void *arg) 389 { 390 nvlist_t *holds = arg; 391 392 (void) dsl_dataset_user_release(holds, NULL); 393 fnvlist_free(holds); 394 } 395 396 void 397 dsl_register_onexit_hold_cleanup(nvlist_t *holds, minor_t minor) 398 { 399 nvlist_t *ca; 400 nvpair_t *pair; 401 char *htag; 402 403 ca = fnvlist_alloc(); 404 /* 405 * Convert from hold format: nvl of snapname -> holdname 406 * to release format: nvl of snapname -> { holdname, ... } 407 */ 408 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 409 pair = nvlist_next_nvpair(holds, pair)) { 410 if (nvpair_value_string(pair, &htag) == 0) { 411 nvlist_t *tags; 412 413 tags = fnvlist_alloc(); 414 fnvlist_add_boolean(tags, htag); 415 fnvlist_add_nvlist(ca, nvpair_name(pair), tags); 416 fnvlist_free(tags); 417 } 418 } 419 VERIFY0(zfs_onexit_add_cb(minor, 420 dsl_dataset_user_release_onexit, ca, NULL)); 421 } 422 423 int 424 dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) 425 { 426 dsl_pool_t *dp; 427 dsl_dataset_t *ds; 428 int err; 429 430 err = dsl_pool_hold(dsname, FTAG, &dp); 431 if (err != 0) 432 return (err); 433 err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 434 if (err != 0) { 435 dsl_pool_rele(dp, FTAG); 436 return (err); 437 } 438 439 if (ds->ds_phys->ds_userrefs_obj != 0) { 440 zap_attribute_t *za; 441 zap_cursor_t zc; 442 443 za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); 444 for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, 445 ds->ds_phys->ds_userrefs_obj); 446 zap_cursor_retrieve(&zc, za) == 0; 447 zap_cursor_advance(&zc)) { 448 fnvlist_add_uint64(nvl, za->za_name, 449 za->za_first_integer); 450 } 451 zap_cursor_fini(&zc); 452 kmem_free(za, sizeof (zap_attribute_t)); 453 } 454 dsl_dataset_rele(ds, FTAG); 455 dsl_pool_rele(dp, FTAG); 456 return (0); 457 }