1247580Smm/* 2247580Smm * CDDL HEADER START 3247580Smm * 4247580Smm * The contents of this file are subject to the terms of the 5247580Smm * Common Development and Distribution License (the "License"). 6247580Smm * You may not use this file except in compliance with the License. 7247580Smm * 8247580Smm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9247580Smm * or http://www.opensolaris.org/os/licensing. 10247580Smm * See the License for the specific language governing permissions 11247580Smm * and limitations under the License. 12247580Smm * 13247580Smm * When distributing Covered Code, include this CDDL HEADER in each 14247580Smm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15247580Smm * If applicable, add the following below this CDDL HEADER, with the 16247580Smm * fields enclosed by brackets "[]" replaced with your own identifying 17247580Smm * information: Portions Copyright [yyyy] [name of copyright owner] 18247580Smm * 19247580Smm * CDDL HEADER END 20247580Smm */ 21247580Smm/* 22247580Smm * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23249643Smm * Copyright (c) 2013 by Delphix. All rights reserved. 24252764Sdelphij * Copyright (c) 2013 Steven Hartland. All rights reserved. 25247580Smm */ 26247580Smm 27247580Smm#include <sys/zfs_context.h> 28247580Smm#include <sys/dsl_userhold.h> 29247580Smm#include <sys/dsl_dataset.h> 30247580Smm#include <sys/dsl_destroy.h> 31247580Smm#include <sys/dsl_synctask.h> 32247580Smm#include <sys/dmu_tx.h> 33247580Smm#include <sys/zfs_onexit.h> 34247580Smm#include <sys/dsl_pool.h> 35247580Smm#include <sys/dsl_dir.h> 36247580Smm#include <sys/zfs_ioctl.h> 37247580Smm#include <sys/zap.h> 38247580Smm 39247580Smmtypedef struct dsl_dataset_user_hold_arg { 40247580Smm nvlist_t *dduha_holds; 41252764Sdelphij nvlist_t *dduha_chkholds; 42247580Smm nvlist_t *dduha_errlist; 43247580Smm minor_t dduha_minor; 44247580Smm} dsl_dataset_user_hold_arg_t; 45247580Smm 46247580Smm/* 47247580Smm * If you add new checks here, you may need to add additional checks to the 48247580Smm * "temporary" case in snapshot_check() in dmu_objset.c. 49247580Smm */ 50247580Smmint 51247580Smmdsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag, 52247580Smm boolean_t temphold, dmu_tx_t *tx) 53247580Smm{ 54247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 55247580Smm objset_t *mos = dp->dp_meta_objset; 56247580Smm int error = 0; 57247580Smm 58252764Sdelphij ASSERT(dsl_pool_config_held(dp)); 59252764Sdelphij 60247580Smm if (strlen(htag) > MAXNAMELEN) 61252764Sdelphij return (SET_ERROR(E2BIG)); 62247580Smm /* Tempholds have a more restricted length */ 63247580Smm if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN) 64252764Sdelphij return (SET_ERROR(E2BIG)); 65247580Smm 66247580Smm /* tags must be unique (if ds already exists) */ 67252764Sdelphij if (ds != NULL && ds->ds_phys->ds_userrefs_obj != 0) { 68252764Sdelphij uint64_t value; 69252764Sdelphij 70252764Sdelphij error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj, 71252764Sdelphij htag, 8, 1, &value); 72252764Sdelphij if (error == 0) 73252764Sdelphij error = SET_ERROR(EEXIST); 74252764Sdelphij else if (error == ENOENT) 75252764Sdelphij error = 0; 76247580Smm } 77247580Smm 78247580Smm return (error); 79247580Smm} 80247580Smm 81247580Smmstatic int 82247580Smmdsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) 83247580Smm{ 84247580Smm dsl_dataset_user_hold_arg_t *dduha = arg; 85247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 86247580Smm 87247580Smm if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) 88249643Smm return (SET_ERROR(ENOTSUP)); 89247580Smm 90252764Sdelphij if (!dmu_tx_is_syncing(tx)) 91252764Sdelphij return (0); 92252764Sdelphij 93252764Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); 94252764Sdelphij pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { 95252764Sdelphij dsl_dataset_t *ds; 96247580Smm int error = 0; 97252764Sdelphij char *htag, *name; 98247580Smm 99247580Smm /* must be a snapshot */ 100252764Sdelphij name = nvpair_name(pair); 101252764Sdelphij if (strchr(name, '@') == NULL) 102249643Smm error = SET_ERROR(EINVAL); 103247580Smm 104247580Smm if (error == 0) 105247580Smm error = nvpair_value_string(pair, &htag); 106252764Sdelphij 107252764Sdelphij if (error == 0) 108252764Sdelphij error = dsl_dataset_hold(dp, name, FTAG, &ds); 109252764Sdelphij 110247580Smm if (error == 0) { 111247580Smm error = dsl_dataset_user_hold_check_one(ds, htag, 112247580Smm dduha->dduha_minor != 0, tx); 113247580Smm dsl_dataset_rele(ds, FTAG); 114247580Smm } 115247580Smm 116252764Sdelphij if (error == 0) { 117252764Sdelphij fnvlist_add_string(dduha->dduha_chkholds, name, htag); 118252764Sdelphij } else { 119252764Sdelphij /* 120252764Sdelphij * We register ENOENT errors so they can be correctly 121252764Sdelphij * reported if needed, such as when all holds fail. 122252764Sdelphij */ 123252764Sdelphij fnvlist_add_int32(dduha->dduha_errlist, name, error); 124252764Sdelphij if (error != ENOENT) 125252764Sdelphij return (error); 126247580Smm } 127247580Smm } 128252764Sdelphij 129252764Sdelphij return (0); 130247580Smm} 131247580Smm 132252764Sdelphij 133252764Sdelphijstatic void 134252764Sdelphijdsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds, 135252764Sdelphij const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx) 136247580Smm{ 137247580Smm dsl_pool_t *dp = ds->ds_dir->dd_pool; 138247580Smm objset_t *mos = dp->dp_meta_objset; 139247580Smm uint64_t zapobj; 140247580Smm 141252764Sdelphij ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 142252764Sdelphij 143247580Smm if (ds->ds_phys->ds_userrefs_obj == 0) { 144247580Smm /* 145247580Smm * This is the first user hold for this dataset. Create 146247580Smm * the userrefs zap object. 147247580Smm */ 148247580Smm dmu_buf_will_dirty(ds->ds_dbuf, tx); 149247580Smm zapobj = ds->ds_phys->ds_userrefs_obj = 150247580Smm zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); 151247580Smm } else { 152247580Smm zapobj = ds->ds_phys->ds_userrefs_obj; 153247580Smm } 154247580Smm ds->ds_userrefs++; 155247580Smm 156247580Smm VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx)); 157247580Smm 158247580Smm if (minor != 0) { 159252764Sdelphij char name[MAXNAMELEN]; 160252764Sdelphij nvlist_t *tags; 161252764Sdelphij 162247580Smm VERIFY0(dsl_pool_user_hold(dp, ds->ds_object, 163247580Smm htag, now, tx)); 164252764Sdelphij (void) snprintf(name, sizeof (name), "%llx", 165252764Sdelphij (u_longlong_t)ds->ds_object); 166252764Sdelphij 167252764Sdelphij if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) { 168252764Sdelphij tags = fnvlist_alloc(); 169252764Sdelphij fnvlist_add_boolean(tags, htag); 170252764Sdelphij fnvlist_add_nvlist(tmpholds, name, tags); 171252764Sdelphij fnvlist_free(tags); 172252764Sdelphij } else { 173252764Sdelphij fnvlist_add_boolean(tags, htag); 174252764Sdelphij } 175247580Smm } 176247580Smm 177247580Smm spa_history_log_internal_ds(ds, "hold", tx, 178247580Smm "tag=%s temp=%d refs=%llu", 179247580Smm htag, minor != 0, ds->ds_userrefs); 180247580Smm} 181247580Smm 182252764Sdelphijtypedef struct zfs_hold_cleanup_arg { 183252764Sdelphij char zhca_spaname[MAXNAMELEN]; 184252764Sdelphij uint64_t zhca_spa_load_guid; 185252764Sdelphij nvlist_t *zhca_holds; 186252764Sdelphij} zfs_hold_cleanup_arg_t; 187252764Sdelphij 188247580Smmstatic void 189252764Sdelphijdsl_dataset_user_release_onexit(void *arg) 190252764Sdelphij{ 191252764Sdelphij zfs_hold_cleanup_arg_t *ca = arg; 192252764Sdelphij spa_t *spa; 193252764Sdelphij int error; 194252764Sdelphij 195252764Sdelphij error = spa_open(ca->zhca_spaname, &spa, FTAG); 196252764Sdelphij if (error != 0) { 197252764Sdelphij zfs_dbgmsg("couldn't release holds on pool=%s " 198252764Sdelphij "because pool is no longer loaded", 199252764Sdelphij ca->zhca_spaname); 200252764Sdelphij return; 201252764Sdelphij } 202252764Sdelphij if (spa_load_guid(spa) != ca->zhca_spa_load_guid) { 203252764Sdelphij zfs_dbgmsg("couldn't release holds on pool=%s " 204252764Sdelphij "because pool is no longer loaded (guid doesn't match)", 205252764Sdelphij ca->zhca_spaname); 206252764Sdelphij spa_close(spa, FTAG); 207252764Sdelphij return; 208252764Sdelphij } 209252764Sdelphij 210252764Sdelphij (void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds); 211252764Sdelphij fnvlist_free(ca->zhca_holds); 212252764Sdelphij kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t)); 213252764Sdelphij spa_close(spa, FTAG); 214252764Sdelphij} 215252764Sdelphij 216252764Sdelphijstatic void 217252764Sdelphijdsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor) 218252764Sdelphij{ 219252764Sdelphij zfs_hold_cleanup_arg_t *ca; 220252764Sdelphij 221252764Sdelphij if (minor == 0 || nvlist_empty(holds)) { 222252764Sdelphij fnvlist_free(holds); 223252764Sdelphij return; 224252764Sdelphij } 225252764Sdelphij 226252764Sdelphij ASSERT(spa != NULL); 227252764Sdelphij ca = kmem_alloc(sizeof (*ca), KM_SLEEP); 228252764Sdelphij 229252764Sdelphij (void) strlcpy(ca->zhca_spaname, spa_name(spa), 230252764Sdelphij sizeof (ca->zhca_spaname)); 231252764Sdelphij ca->zhca_spa_load_guid = spa_load_guid(spa); 232252764Sdelphij ca->zhca_holds = holds; 233252764Sdelphij VERIFY0(zfs_onexit_add_cb(minor, 234252764Sdelphij dsl_dataset_user_release_onexit, ca, NULL)); 235252764Sdelphij} 236252764Sdelphij 237252764Sdelphijvoid 238252764Sdelphijdsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag, 239252764Sdelphij minor_t minor, uint64_t now, dmu_tx_t *tx) 240252764Sdelphij{ 241252764Sdelphij nvlist_t *tmpholds; 242252764Sdelphij 243252764Sdelphij if (minor != 0) 244252764Sdelphij tmpholds = fnvlist_alloc(); 245252764Sdelphij else 246252764Sdelphij tmpholds = NULL; 247252764Sdelphij dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx); 248252764Sdelphij dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor); 249252764Sdelphij} 250252764Sdelphij 251252764Sdelphijstatic void 252247580Smmdsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx) 253247580Smm{ 254247580Smm dsl_dataset_user_hold_arg_t *dduha = arg; 255247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 256252764Sdelphij nvlist_t *tmpholds; 257247580Smm uint64_t now = gethrestime_sec(); 258247580Smm 259252764Sdelphij if (dduha->dduha_minor != 0) 260252764Sdelphij tmpholds = fnvlist_alloc(); 261252764Sdelphij else 262252764Sdelphij tmpholds = NULL; 263252764Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL); 264252764Sdelphij pair != NULL; 265252764Sdelphij pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) { 266247580Smm dsl_dataset_t *ds; 267252764Sdelphij 268247580Smm VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); 269252764Sdelphij dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, 270252764Sdelphij fnvpair_value_string(pair), dduha->dduha_minor, now, tx); 271247580Smm dsl_dataset_rele(ds, FTAG); 272247580Smm } 273252764Sdelphij dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor); 274247580Smm} 275247580Smm 276247580Smm/* 277252764Sdelphij * The full semantics of this function are described in the comment above 278252764Sdelphij * lzc_hold(). 279252764Sdelphij * 280252764Sdelphij * To summarize: 281247580Smm * holds is nvl of snapname -> holdname 282247580Smm * errlist will be filled in with snapname -> error 283247580Smm * 284252764Sdelphij * The snaphosts must all be in the same pool. 285252764Sdelphij * 286252764Sdelphij * Holds for snapshots that don't exist will be skipped. 287252764Sdelphij * 288252764Sdelphij * If none of the snapshots for requested holds exist then ENOENT will be 289252764Sdelphij * returned. 290252764Sdelphij * 291252764Sdelphij * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned 292252764Sdelphij * up when the process exits. 293252764Sdelphij * 294252764Sdelphij * On success all the holds, for snapshots that existed, will be created and 0 295252764Sdelphij * will be returned. 296252764Sdelphij * 297252764Sdelphij * On failure no holds will be created, the errlist will be filled in, 298252764Sdelphij * and an errno will returned. 299252764Sdelphij * 300252764Sdelphij * In all cases the errlist will contain entries for holds where the snapshot 301252764Sdelphij * didn't exist. 302247580Smm */ 303247580Smmint 304247580Smmdsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist) 305247580Smm{ 306247580Smm dsl_dataset_user_hold_arg_t dduha; 307247580Smm nvpair_t *pair; 308252764Sdelphij int ret; 309247580Smm 310247580Smm pair = nvlist_next_nvpair(holds, NULL); 311247580Smm if (pair == NULL) 312247580Smm return (0); 313247580Smm 314247580Smm dduha.dduha_holds = holds; 315252764Sdelphij dduha.dduha_chkholds = fnvlist_alloc(); 316247580Smm dduha.dduha_errlist = errlist; 317247580Smm dduha.dduha_minor = cleanup_minor; 318247580Smm 319252764Sdelphij ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check, 320252764Sdelphij dsl_dataset_user_hold_sync, &dduha, fnvlist_num_pairs(holds)); 321252764Sdelphij fnvlist_free(dduha.dduha_chkholds); 322252764Sdelphij 323252764Sdelphij return (ret); 324247580Smm} 325247580Smm 326252764Sdelphijtypedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag, 327252764Sdelphij dsl_dataset_t **dsp); 328252764Sdelphij 329247580Smmtypedef struct dsl_dataset_user_release_arg { 330252764Sdelphij dsl_holdfunc_t *ddura_holdfunc; 331247580Smm nvlist_t *ddura_holds; 332247580Smm nvlist_t *ddura_todelete; 333247580Smm nvlist_t *ddura_errlist; 334252764Sdelphij nvlist_t *ddura_chkholds; 335247580Smm} dsl_dataset_user_release_arg_t; 336247580Smm 337252764Sdelphij/* Place a dataset hold on the snapshot identified by passed dsobj string */ 338247580Smmstatic int 339252764Sdelphijdsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag, 340252764Sdelphij dsl_dataset_t **dsp) 341247580Smm{ 342252764Sdelphij return (dsl_dataset_hold_obj(dp, strtonum(dsobj, NULL), tag, dsp)); 343252764Sdelphij} 344252764Sdelphij 345252764Sdelphijstatic int 346252764Sdelphijdsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura, 347252764Sdelphij dsl_dataset_t *ds, nvlist_t *holds, const char *snapname) 348252764Sdelphij{ 349247580Smm uint64_t zapobj; 350252764Sdelphij nvlist_t *holds_found; 351252764Sdelphij objset_t *mos; 352252764Sdelphij int numholds; 353247580Smm 354247580Smm if (!dsl_dataset_is_snapshot(ds)) 355249643Smm return (SET_ERROR(EINVAL)); 356247580Smm 357252764Sdelphij if (nvlist_empty(holds)) 358252764Sdelphij return (0); 359252764Sdelphij 360252764Sdelphij numholds = 0; 361252764Sdelphij mos = ds->ds_dir->dd_pool->dp_meta_objset; 362247580Smm zapobj = ds->ds_phys->ds_userrefs_obj; 363252764Sdelphij holds_found = fnvlist_alloc(); 364247580Smm 365252764Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 366247580Smm pair = nvlist_next_nvpair(holds, pair)) { 367247580Smm uint64_t tmp; 368252764Sdelphij int error; 369252764Sdelphij const char *holdname = nvpair_name(pair); 370252764Sdelphij 371252764Sdelphij if (zapobj != 0) 372252764Sdelphij error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp); 373252764Sdelphij else 374252764Sdelphij error = SET_ERROR(ENOENT); 375252764Sdelphij 376252764Sdelphij /* 377252764Sdelphij * Non-existent holds are put on the errlist, but don't 378252764Sdelphij * cause an overall failure. 379252764Sdelphij */ 380252764Sdelphij if (error == ENOENT) { 381252764Sdelphij if (ddura->ddura_errlist != NULL) { 382252764Sdelphij char *errtag = kmem_asprintf("%s#%s", 383252764Sdelphij snapname, holdname); 384252764Sdelphij fnvlist_add_int32(ddura->ddura_errlist, errtag, 385252764Sdelphij ENOENT); 386252764Sdelphij strfree(errtag); 387252764Sdelphij } 388252764Sdelphij continue; 389252764Sdelphij } 390252764Sdelphij 391252764Sdelphij if (error != 0) { 392252764Sdelphij fnvlist_free(holds_found); 393247580Smm return (error); 394252764Sdelphij } 395252764Sdelphij 396252764Sdelphij fnvlist_add_boolean(holds_found, holdname); 397247580Smm numholds++; 398247580Smm } 399247580Smm 400247580Smm if (DS_IS_DEFER_DESTROY(ds) && ds->ds_phys->ds_num_children == 1 && 401247580Smm ds->ds_userrefs == numholds) { 402247580Smm /* we need to destroy the snapshot as well */ 403252764Sdelphij if (dsl_dataset_long_held(ds)) { 404252764Sdelphij fnvlist_free(holds_found); 405249643Smm return (SET_ERROR(EBUSY)); 406252764Sdelphij } 407252764Sdelphij fnvlist_add_boolean(ddura->ddura_todelete, snapname); 408247580Smm } 409252764Sdelphij 410252764Sdelphij if (numholds != 0) { 411252764Sdelphij fnvlist_add_nvlist(ddura->ddura_chkholds, snapname, 412252764Sdelphij holds_found); 413252764Sdelphij } 414252764Sdelphij fnvlist_free(holds_found); 415252764Sdelphij 416247580Smm return (0); 417247580Smm} 418247580Smm 419247580Smmstatic int 420247580Smmdsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) 421247580Smm{ 422252764Sdelphij dsl_dataset_user_release_arg_t *ddura; 423252764Sdelphij dsl_holdfunc_t *holdfunc; 424252764Sdelphij dsl_pool_t *dp; 425247580Smm 426247580Smm if (!dmu_tx_is_syncing(tx)) 427247580Smm return (0); 428247580Smm 429252764Sdelphij dp = dmu_tx_pool(tx); 430252764Sdelphij 431252764Sdelphij ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 432252764Sdelphij 433252764Sdelphij ddura = arg; 434252764Sdelphij holdfunc = ddura->ddura_holdfunc; 435252764Sdelphij 436252764Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); 437252764Sdelphij pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { 438247580Smm int error; 439247580Smm dsl_dataset_t *ds; 440247580Smm nvlist_t *holds; 441252764Sdelphij const char *snapname = nvpair_name(pair); 442247580Smm 443247580Smm error = nvpair_value_nvlist(pair, &holds); 444247580Smm if (error != 0) 445252764Sdelphij error = (SET_ERROR(EINVAL)); 446252764Sdelphij else 447252764Sdelphij error = holdfunc(dp, snapname, FTAG, &ds); 448247580Smm if (error == 0) { 449252764Sdelphij error = dsl_dataset_user_release_check_one(ddura, ds, 450252764Sdelphij holds, snapname); 451247580Smm dsl_dataset_rele(ds, FTAG); 452247580Smm } 453247580Smm if (error != 0) { 454247580Smm if (ddura->ddura_errlist != NULL) { 455247580Smm fnvlist_add_int32(ddura->ddura_errlist, 456252764Sdelphij snapname, error); 457247580Smm } 458252764Sdelphij /* 459252764Sdelphij * Non-existent snapshots are put on the errlist, 460252764Sdelphij * but don't cause an overall failure. 461252764Sdelphij */ 462252764Sdelphij if (error != ENOENT) 463252764Sdelphij return (error); 464247580Smm } 465247580Smm } 466252764Sdelphij 467252764Sdelphij return (0); 468247580Smm} 469247580Smm 470247580Smmstatic void 471247580Smmdsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds, 472247580Smm dmu_tx_t *tx) 473247580Smm{ 474247580Smm dsl_pool_t *dp = ds->ds_dir->dd_pool; 475247580Smm objset_t *mos = dp->dp_meta_objset; 476247580Smm 477252764Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 478247580Smm pair = nvlist_next_nvpair(holds, pair)) { 479252764Sdelphij int error; 480252764Sdelphij const char *holdname = nvpair_name(pair); 481252764Sdelphij 482252764Sdelphij /* Remove temporary hold if one exists. */ 483252764Sdelphij error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx); 484247580Smm VERIFY(error == 0 || error == ENOENT); 485247580Smm 486252764Sdelphij VERIFY0(zap_remove(mos, ds->ds_phys->ds_userrefs_obj, holdname, 487252764Sdelphij tx)); 488252764Sdelphij ds->ds_userrefs--; 489252764Sdelphij 490247580Smm spa_history_log_internal_ds(ds, "release", tx, 491252764Sdelphij "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs); 492247580Smm } 493247580Smm} 494247580Smm 495247580Smmstatic void 496247580Smmdsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) 497247580Smm{ 498247580Smm dsl_dataset_user_release_arg_t *ddura = arg; 499252764Sdelphij dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc; 500247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 501247580Smm 502252764Sdelphij ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 503252764Sdelphij 504252764Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL); 505252764Sdelphij pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds, 506252764Sdelphij pair)) { 507247580Smm dsl_dataset_t *ds; 508252764Sdelphij const char *name = nvpair_name(pair); 509247580Smm 510252764Sdelphij VERIFY0(holdfunc(dp, name, FTAG, &ds)); 511252764Sdelphij 512247580Smm dsl_dataset_user_release_sync_one(ds, 513247580Smm fnvpair_value_nvlist(pair), tx); 514252764Sdelphij if (nvlist_exists(ddura->ddura_todelete, name)) { 515247580Smm ASSERT(ds->ds_userrefs == 0 && 516247580Smm ds->ds_phys->ds_num_children == 1 && 517247580Smm DS_IS_DEFER_DESTROY(ds)); 518247580Smm dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); 519247580Smm } 520247580Smm dsl_dataset_rele(ds, FTAG); 521247580Smm } 522247580Smm} 523247580Smm 524247580Smm/* 525252764Sdelphij * The full semantics of this function are described in the comment above 526252764Sdelphij * lzc_release(). 527252764Sdelphij * 528252764Sdelphij * To summarize: 529252764Sdelphij * Releases holds specified in the nvl holds. 530252764Sdelphij * 531247580Smm * holds is nvl of snapname -> { holdname, ... } 532247580Smm * errlist will be filled in with snapname -> error 533247580Smm * 534252764Sdelphij * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots, 535252764Sdelphij * otherwise they should be the names of shapshots. 536252764Sdelphij * 537252764Sdelphij * As a release may cause snapshots to be destroyed this trys to ensure they 538252764Sdelphij * aren't mounted. 539252764Sdelphij * 540252764Sdelphij * The release of non-existent holds are skipped. 541252764Sdelphij * 542252764Sdelphij * At least one hold must have been released for the this function to succeed 543252764Sdelphij * and return 0. 544247580Smm */ 545252764Sdelphijstatic int 546252764Sdelphijdsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist, 547252764Sdelphij dsl_pool_t *tmpdp) 548247580Smm{ 549247580Smm dsl_dataset_user_release_arg_t ddura; 550247580Smm nvpair_t *pair; 551252764Sdelphij char *pool; 552247580Smm int error; 553247580Smm 554247580Smm pair = nvlist_next_nvpair(holds, NULL); 555247580Smm if (pair == NULL) 556247580Smm return (0); 557247580Smm 558252764Sdelphij /* 559252764Sdelphij * The release may cause snapshots to be destroyed; make sure they 560252764Sdelphij * are not mounted. 561252764Sdelphij */ 562252764Sdelphij if (tmpdp != NULL) { 563252764Sdelphij /* Temporary holds are specified by dsobj string. */ 564252764Sdelphij ddura.ddura_holdfunc = dsl_dataset_hold_obj_string; 565252764Sdelphij pool = spa_name(tmpdp->dp_spa); 566252764Sdelphij#ifdef _KERNEL 567252764Sdelphij for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 568252764Sdelphij pair = nvlist_next_nvpair(holds, pair)) { 569252764Sdelphij dsl_dataset_t *ds; 570252764Sdelphij 571262175Savg dsl_pool_config_enter(tmpdp, FTAG); 572252764Sdelphij error = dsl_dataset_hold_obj_string(tmpdp, 573252764Sdelphij nvpair_name(pair), FTAG, &ds); 574252764Sdelphij if (error == 0) { 575252764Sdelphij char name[MAXNAMELEN]; 576252764Sdelphij dsl_dataset_name(ds, name); 577262175Savg dsl_pool_config_exit(tmpdp, FTAG); 578252764Sdelphij dsl_dataset_rele(ds, FTAG); 579252764Sdelphij (void) zfs_unmount_snap(name); 580262175Savg } else { 581262175Savg dsl_pool_config_exit(tmpdp, FTAG); 582252764Sdelphij } 583252764Sdelphij } 584252764Sdelphij#endif 585252764Sdelphij } else { 586252764Sdelphij /* Non-temporary holds are specified by name. */ 587252764Sdelphij ddura.ddura_holdfunc = dsl_dataset_hold; 588252764Sdelphij pool = nvpair_name(pair); 589252764Sdelphij#ifdef _KERNEL 590252764Sdelphij for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 591252764Sdelphij pair = nvlist_next_nvpair(holds, pair)) { 592252764Sdelphij (void) zfs_unmount_snap(nvpair_name(pair)); 593252764Sdelphij } 594252764Sdelphij#endif 595252764Sdelphij } 596252764Sdelphij 597247580Smm ddura.ddura_holds = holds; 598247580Smm ddura.ddura_errlist = errlist; 599247580Smm ddura.ddura_todelete = fnvlist_alloc(); 600252764Sdelphij ddura.ddura_chkholds = fnvlist_alloc(); 601247580Smm 602252764Sdelphij error = dsl_sync_task(pool, dsl_dataset_user_release_check, 603252764Sdelphij dsl_dataset_user_release_sync, &ddura, 604252764Sdelphij fnvlist_num_pairs(holds)); 605247580Smm fnvlist_free(ddura.ddura_todelete); 606252764Sdelphij fnvlist_free(ddura.ddura_chkholds); 607247580Smm 608247580Smm return (error); 609247580Smm} 610247580Smm 611252764Sdelphij/* 612252764Sdelphij * holds is nvl of snapname -> { holdname, ... } 613252764Sdelphij * errlist will be filled in with snapname -> error 614252764Sdelphij */ 615252764Sdelphijint 616252764Sdelphijdsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist) 617247580Smm{ 618252764Sdelphij return (dsl_dataset_user_release_impl(holds, errlist, NULL)); 619247580Smm} 620247580Smm 621247580Smm/* 622252764Sdelphij * holds is nvl of snapdsobj -> { holdname, ... } 623247580Smm */ 624247580Smmvoid 625252764Sdelphijdsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds) 626247580Smm{ 627252764Sdelphij ASSERT(dp != NULL); 628252764Sdelphij (void) dsl_dataset_user_release_impl(holds, NULL, dp); 629247580Smm} 630247580Smm 631247580Smmint 632247580Smmdsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) 633247580Smm{ 634247580Smm dsl_pool_t *dp; 635247580Smm dsl_dataset_t *ds; 636247580Smm int err; 637247580Smm 638247580Smm err = dsl_pool_hold(dsname, FTAG, &dp); 639247580Smm if (err != 0) 640247580Smm return (err); 641247580Smm err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 642247580Smm if (err != 0) { 643247580Smm dsl_pool_rele(dp, FTAG); 644247580Smm return (err); 645247580Smm } 646247580Smm 647247580Smm if (ds->ds_phys->ds_userrefs_obj != 0) { 648247580Smm zap_attribute_t *za; 649247580Smm zap_cursor_t zc; 650247580Smm 651247580Smm za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); 652247580Smm for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, 653247580Smm ds->ds_phys->ds_userrefs_obj); 654247580Smm zap_cursor_retrieve(&zc, za) == 0; 655247580Smm zap_cursor_advance(&zc)) { 656247580Smm fnvlist_add_uint64(nvl, za->za_name, 657247580Smm za->za_first_integer); 658247580Smm } 659247580Smm zap_cursor_fini(&zc); 660247580Smm kmem_free(za, sizeof (zap_attribute_t)); 661247580Smm } 662247580Smm dsl_dataset_rele(ds, FTAG); 663247580Smm dsl_pool_rele(dp, FTAG); 664247580Smm return (0); 665247580Smm} 666