13b2aab18SMatthew Ahrens /* 23b2aab18SMatthew Ahrens * CDDL HEADER START 33b2aab18SMatthew Ahrens * 43b2aab18SMatthew Ahrens * The contents of this file are subject to the terms of the 53b2aab18SMatthew Ahrens * Common Development and Distribution License (the "License"). 63b2aab18SMatthew Ahrens * You may not use this file except in compliance with the License. 73b2aab18SMatthew Ahrens * 83b2aab18SMatthew Ahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93b2aab18SMatthew Ahrens * or http://www.opensolaris.org/os/licensing. 103b2aab18SMatthew Ahrens * See the License for the specific language governing permissions 113b2aab18SMatthew Ahrens * and limitations under the License. 123b2aab18SMatthew Ahrens * 133b2aab18SMatthew Ahrens * When distributing Covered Code, include this CDDL HEADER in each 143b2aab18SMatthew Ahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153b2aab18SMatthew Ahrens * If applicable, add the following below this CDDL HEADER, with the 163b2aab18SMatthew Ahrens * fields enclosed by brackets "[]" replaced with your own identifying 173b2aab18SMatthew Ahrens * information: Portions Copyright [yyyy] [name of copyright owner] 183b2aab18SMatthew Ahrens * 193b2aab18SMatthew Ahrens * CDDL HEADER END 203b2aab18SMatthew Ahrens */ 213b2aab18SMatthew Ahrens /* 223b2aab18SMatthew Ahrens * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23*40a5c998SMatthew Ahrens * Copyright (c) 2012, 2015 by Delphix. All rights reserved. 24a7a845e4SSteven Hartland * Copyright (c) 2013 Steven Hartland. All rights reserved. 253b2aab18SMatthew Ahrens */ 263b2aab18SMatthew Ahrens 273b2aab18SMatthew Ahrens #include <sys/zfs_context.h> 283b2aab18SMatthew Ahrens #include <sys/dsl_userhold.h> 293b2aab18SMatthew Ahrens #include <sys/dsl_dataset.h> 303b2aab18SMatthew Ahrens #include <sys/dsl_destroy.h> 313b2aab18SMatthew Ahrens #include <sys/dsl_synctask.h> 323b2aab18SMatthew Ahrens #include <sys/dmu_tx.h> 333b2aab18SMatthew Ahrens #include <sys/zfs_onexit.h> 343b2aab18SMatthew Ahrens #include <sys/dsl_pool.h> 353b2aab18SMatthew Ahrens #include <sys/dsl_dir.h> 363b2aab18SMatthew Ahrens #include <sys/zfs_ioctl.h> 373b2aab18SMatthew Ahrens #include <sys/zap.h> 383b2aab18SMatthew Ahrens 393b2aab18SMatthew Ahrens typedef struct dsl_dataset_user_hold_arg { 403b2aab18SMatthew Ahrens nvlist_t *dduha_holds; 41a7a845e4SSteven Hartland nvlist_t *dduha_chkholds; 423b2aab18SMatthew Ahrens nvlist_t *dduha_errlist; 433b2aab18SMatthew Ahrens minor_t dduha_minor; 443b2aab18SMatthew Ahrens } dsl_dataset_user_hold_arg_t; 453b2aab18SMatthew Ahrens 463b2aab18SMatthew Ahrens /* 473b2aab18SMatthew Ahrens * If you add new checks here, you may need to add additional checks to the 483b2aab18SMatthew Ahrens * "temporary" case in snapshot_check() in dmu_objset.c. 493b2aab18SMatthew Ahrens */ 503b2aab18SMatthew Ahrens int 513b2aab18SMatthew Ahrens dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag, 523b2aab18SMatthew Ahrens boolean_t temphold, dmu_tx_t *tx) 533b2aab18SMatthew Ahrens { 543b2aab18SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 553b2aab18SMatthew Ahrens objset_t *mos = dp->dp_meta_objset; 563b2aab18SMatthew Ahrens int error = 0; 573b2aab18SMatthew Ahrens 58a7a845e4SSteven Hartland ASSERT(dsl_pool_config_held(dp)); 59a7a845e4SSteven Hartland 603b2aab18SMatthew Ahrens if (strlen(htag) > MAXNAMELEN) 61a7a845e4SSteven Hartland return (SET_ERROR(E2BIG)); 623b2aab18SMatthew Ahrens /* Tempholds have a more restricted length */ 633b2aab18SMatthew Ahrens if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN) 64a7a845e4SSteven Hartland return (SET_ERROR(E2BIG)); 653b2aab18SMatthew Ahrens 663b2aab18SMatthew Ahrens /* tags must be unique (if ds already exists) */ 67c1379625SJustin T. Gibbs if (ds != NULL && dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { 683b2aab18SMatthew Ahrens uint64_t value; 69a7a845e4SSteven Hartland 70c1379625SJustin T. Gibbs error = zap_lookup(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, 713b2aab18SMatthew Ahrens htag, 8, 1, &value); 723b2aab18SMatthew Ahrens if (error == 0) 73be6fd75aSMatthew Ahrens error = SET_ERROR(EEXIST); 743b2aab18SMatthew Ahrens else if (error == ENOENT) 753b2aab18SMatthew Ahrens error = 0; 763b2aab18SMatthew Ahrens } 773b2aab18SMatthew Ahrens 783b2aab18SMatthew Ahrens return (error); 793b2aab18SMatthew Ahrens } 803b2aab18SMatthew Ahrens 813b2aab18SMatthew Ahrens static int 823b2aab18SMatthew Ahrens dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) 833b2aab18SMatthew Ahrens { 843b2aab18SMatthew Ahrens dsl_dataset_user_hold_arg_t *dduha = arg; 853b2aab18SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 863b2aab18SMatthew Ahrens 873b2aab18SMatthew Ahrens if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) 88be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 893b2aab18SMatthew Ahrens 90a7a845e4SSteven Hartland if (!dmu_tx_is_syncing(tx)) 91a7a845e4SSteven Hartland return (0); 92a7a845e4SSteven Hartland 93a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); 94a7a845e4SSteven Hartland pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { 953b2aab18SMatthew Ahrens dsl_dataset_t *ds; 96a7a845e4SSteven Hartland int error = 0; 97a7a845e4SSteven Hartland char *htag, *name; 983b2aab18SMatthew Ahrens 993b2aab18SMatthew Ahrens /* must be a snapshot */ 100a7a845e4SSteven Hartland name = nvpair_name(pair); 101a7a845e4SSteven Hartland if (strchr(name, '@') == NULL) 102be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 1033b2aab18SMatthew Ahrens 1043b2aab18SMatthew Ahrens if (error == 0) 1053b2aab18SMatthew Ahrens error = nvpair_value_string(pair, &htag); 106a7a845e4SSteven Hartland 107a7a845e4SSteven Hartland if (error == 0) 108a7a845e4SSteven Hartland error = dsl_dataset_hold(dp, name, FTAG, &ds); 109a7a845e4SSteven Hartland 1103b2aab18SMatthew Ahrens if (error == 0) { 1113b2aab18SMatthew Ahrens error = dsl_dataset_user_hold_check_one(ds, htag, 1123b2aab18SMatthew Ahrens dduha->dduha_minor != 0, tx); 1133b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 1143b2aab18SMatthew Ahrens } 1153b2aab18SMatthew Ahrens 116a7a845e4SSteven Hartland if (error == 0) { 117a7a845e4SSteven Hartland fnvlist_add_string(dduha->dduha_chkholds, name, htag); 118a7a845e4SSteven Hartland } else { 119a7a845e4SSteven Hartland /* 120a7a845e4SSteven Hartland * We register ENOENT errors so they can be correctly 121a7a845e4SSteven Hartland * reported if needed, such as when all holds fail. 122a7a845e4SSteven Hartland */ 123a7a845e4SSteven Hartland fnvlist_add_int32(dduha->dduha_errlist, name, error); 124a7a845e4SSteven Hartland if (error != ENOENT) 125a7a845e4SSteven Hartland return (error); 1263b2aab18SMatthew Ahrens } 1273b2aab18SMatthew Ahrens } 1283b2aab18SMatthew Ahrens 129a7a845e4SSteven Hartland return (0); 130a7a845e4SSteven Hartland } 131a7a845e4SSteven Hartland 132a7a845e4SSteven Hartland 133a7a845e4SSteven Hartland static void 134a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds, 135a7a845e4SSteven Hartland const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx) 1363b2aab18SMatthew Ahrens { 1373b2aab18SMatthew Ahrens dsl_pool_t *dp = ds->ds_dir->dd_pool; 1383b2aab18SMatthew Ahrens objset_t *mos = dp->dp_meta_objset; 1393b2aab18SMatthew Ahrens uint64_t zapobj; 1403b2aab18SMatthew Ahrens 141a7a845e4SSteven Hartland ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 142a7a845e4SSteven Hartland 143c1379625SJustin T. Gibbs if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) { 1443b2aab18SMatthew Ahrens /* 1453b2aab18SMatthew Ahrens * This is the first user hold for this dataset. Create 1463b2aab18SMatthew Ahrens * the userrefs zap object. 1473b2aab18SMatthew Ahrens */ 1483b2aab18SMatthew Ahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 149c1379625SJustin T. Gibbs zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj = 1503b2aab18SMatthew Ahrens zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); 1513b2aab18SMatthew Ahrens } else { 152c1379625SJustin T. Gibbs zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; 1533b2aab18SMatthew Ahrens } 1543b2aab18SMatthew Ahrens ds->ds_userrefs++; 1553b2aab18SMatthew Ahrens 1563b2aab18SMatthew Ahrens VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx)); 1573b2aab18SMatthew Ahrens 1583b2aab18SMatthew Ahrens if (minor != 0) { 159a7a845e4SSteven Hartland char name[MAXNAMELEN]; 160a7a845e4SSteven Hartland nvlist_t *tags; 161a7a845e4SSteven Hartland 1623b2aab18SMatthew Ahrens VERIFY0(dsl_pool_user_hold(dp, ds->ds_object, 1633b2aab18SMatthew Ahrens htag, now, tx)); 164a7a845e4SSteven Hartland (void) snprintf(name, sizeof (name), "%llx", 165a7a845e4SSteven Hartland (u_longlong_t)ds->ds_object); 166a7a845e4SSteven Hartland 167a7a845e4SSteven Hartland if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) { 168a7a845e4SSteven Hartland tags = fnvlist_alloc(); 169a7a845e4SSteven Hartland fnvlist_add_boolean(tags, htag); 170a7a845e4SSteven Hartland fnvlist_add_nvlist(tmpholds, name, tags); 171a7a845e4SSteven Hartland fnvlist_free(tags); 172a7a845e4SSteven Hartland } else { 173a7a845e4SSteven Hartland fnvlist_add_boolean(tags, htag); 174a7a845e4SSteven Hartland } 1753b2aab18SMatthew Ahrens } 1763b2aab18SMatthew Ahrens 1773b2aab18SMatthew Ahrens spa_history_log_internal_ds(ds, "hold", tx, 1783b2aab18SMatthew Ahrens "tag=%s temp=%d refs=%llu", 1793b2aab18SMatthew Ahrens htag, minor != 0, ds->ds_userrefs); 1803b2aab18SMatthew Ahrens } 1813b2aab18SMatthew Ahrens 1823b2aab18SMatthew Ahrens typedef struct zfs_hold_cleanup_arg { 183*40a5c998SMatthew Ahrens char zhca_spaname[ZFS_MAX_DATASET_NAME_LEN]; 1843b2aab18SMatthew Ahrens uint64_t zhca_spa_load_guid; 185a7a845e4SSteven Hartland nvlist_t *zhca_holds; 1863b2aab18SMatthew Ahrens } zfs_hold_cleanup_arg_t; 1873b2aab18SMatthew Ahrens 1883b2aab18SMatthew Ahrens static void 1893b2aab18SMatthew Ahrens dsl_dataset_user_release_onexit(void *arg) 1903b2aab18SMatthew Ahrens { 1913b2aab18SMatthew Ahrens zfs_hold_cleanup_arg_t *ca = arg; 1923b2aab18SMatthew Ahrens spa_t *spa; 1933b2aab18SMatthew Ahrens int error; 1943b2aab18SMatthew Ahrens 1953b2aab18SMatthew Ahrens error = spa_open(ca->zhca_spaname, &spa, FTAG); 1963b2aab18SMatthew Ahrens if (error != 0) { 197a7a845e4SSteven Hartland zfs_dbgmsg("couldn't release holds on pool=%s " 1983b2aab18SMatthew Ahrens "because pool is no longer loaded", 199a7a845e4SSteven Hartland ca->zhca_spaname); 2003b2aab18SMatthew Ahrens return; 2013b2aab18SMatthew Ahrens } 2023b2aab18SMatthew Ahrens if (spa_load_guid(spa) != ca->zhca_spa_load_guid) { 203a7a845e4SSteven Hartland zfs_dbgmsg("couldn't release holds on pool=%s " 2043b2aab18SMatthew Ahrens "because pool is no longer loaded (guid doesn't match)", 205a7a845e4SSteven Hartland ca->zhca_spaname); 2063b2aab18SMatthew Ahrens spa_close(spa, FTAG); 2073b2aab18SMatthew Ahrens return; 2083b2aab18SMatthew Ahrens } 2093b2aab18SMatthew Ahrens 210a7a845e4SSteven Hartland (void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds); 211a7a845e4SSteven Hartland fnvlist_free(ca->zhca_holds); 2123b2aab18SMatthew Ahrens kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t)); 2133b2aab18SMatthew Ahrens spa_close(spa, FTAG); 2143b2aab18SMatthew Ahrens } 2153b2aab18SMatthew Ahrens 216a7a845e4SSteven Hartland static void 217a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor) 2183b2aab18SMatthew Ahrens { 219a7a845e4SSteven Hartland zfs_hold_cleanup_arg_t *ca; 220a7a845e4SSteven Hartland 221a7a845e4SSteven Hartland if (minor == 0 || nvlist_empty(holds)) { 222a7a845e4SSteven Hartland fnvlist_free(holds); 223a7a845e4SSteven Hartland return; 224a7a845e4SSteven Hartland } 225a7a845e4SSteven Hartland 226a7a845e4SSteven Hartland ASSERT(spa != NULL); 227a7a845e4SSteven Hartland ca = kmem_alloc(sizeof (*ca), KM_SLEEP); 228a7a845e4SSteven Hartland 2293b2aab18SMatthew Ahrens (void) strlcpy(ca->zhca_spaname, spa_name(spa), 2303b2aab18SMatthew Ahrens sizeof (ca->zhca_spaname)); 2313b2aab18SMatthew Ahrens ca->zhca_spa_load_guid = spa_load_guid(spa); 232a7a845e4SSteven Hartland ca->zhca_holds = holds; 2333b2aab18SMatthew Ahrens VERIFY0(zfs_onexit_add_cb(minor, 2343b2aab18SMatthew Ahrens dsl_dataset_user_release_onexit, ca, NULL)); 2353b2aab18SMatthew Ahrens } 2363b2aab18SMatthew Ahrens 237a7a845e4SSteven Hartland void 238a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag, 239a7a845e4SSteven Hartland minor_t minor, uint64_t now, dmu_tx_t *tx) 240a7a845e4SSteven Hartland { 241a7a845e4SSteven Hartland nvlist_t *tmpholds; 242a7a845e4SSteven Hartland 243a7a845e4SSteven Hartland if (minor != 0) 244a7a845e4SSteven Hartland tmpholds = fnvlist_alloc(); 245a7a845e4SSteven Hartland else 246a7a845e4SSteven Hartland tmpholds = NULL; 247a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx); 248a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor); 249a7a845e4SSteven Hartland } 250a7a845e4SSteven Hartland 251a7a845e4SSteven Hartland static void 252a7a845e4SSteven Hartland dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx) 253a7a845e4SSteven Hartland { 254a7a845e4SSteven Hartland dsl_dataset_user_hold_arg_t *dduha = arg; 255a7a845e4SSteven Hartland dsl_pool_t *dp = dmu_tx_pool(tx); 256a7a845e4SSteven Hartland nvlist_t *tmpholds; 257a7a845e4SSteven Hartland uint64_t now = gethrestime_sec(); 258a7a845e4SSteven Hartland 259a7a845e4SSteven Hartland if (dduha->dduha_minor != 0) 260a7a845e4SSteven Hartland tmpholds = fnvlist_alloc(); 261a7a845e4SSteven Hartland else 262a7a845e4SSteven Hartland tmpholds = NULL; 263a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL); 264a7a845e4SSteven Hartland pair != NULL; 265a7a845e4SSteven Hartland pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) { 266a7a845e4SSteven Hartland dsl_dataset_t *ds; 267a7a845e4SSteven Hartland 268a7a845e4SSteven Hartland VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); 269a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, 270a7a845e4SSteven Hartland fnvpair_value_string(pair), dduha->dduha_minor, now, tx); 271a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG); 272a7a845e4SSteven Hartland } 273a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor); 274a7a845e4SSteven Hartland } 275a7a845e4SSteven Hartland 276a7a845e4SSteven Hartland /* 277a7a845e4SSteven Hartland * The full semantics of this function are described in the comment above 278a7a845e4SSteven Hartland * lzc_hold(). 279a7a845e4SSteven Hartland * 280a7a845e4SSteven Hartland * To summarize: 281a7a845e4SSteven Hartland * holds is nvl of snapname -> holdname 282a7a845e4SSteven Hartland * errlist will be filled in with snapname -> error 283a7a845e4SSteven Hartland * 284a7a845e4SSteven Hartland * The snaphosts must all be in the same pool. 285a7a845e4SSteven Hartland * 286a7a845e4SSteven Hartland * Holds for snapshots that don't exist will be skipped. 287a7a845e4SSteven Hartland * 288a7a845e4SSteven Hartland * If none of the snapshots for requested holds exist then ENOENT will be 289a7a845e4SSteven Hartland * returned. 290a7a845e4SSteven Hartland * 291a7a845e4SSteven Hartland * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned 292a7a845e4SSteven Hartland * up when the process exits. 293a7a845e4SSteven Hartland * 294a7a845e4SSteven Hartland * On success all the holds, for snapshots that existed, will be created and 0 295a7a845e4SSteven Hartland * will be returned. 296a7a845e4SSteven Hartland * 297a7a845e4SSteven Hartland * On failure no holds will be created, the errlist will be filled in, 298a7a845e4SSteven Hartland * and an errno will returned. 299a7a845e4SSteven Hartland * 300a7a845e4SSteven Hartland * In all cases the errlist will contain entries for holds where the snapshot 301a7a845e4SSteven Hartland * didn't exist. 302a7a845e4SSteven Hartland */ 303a7a845e4SSteven Hartland int 304a7a845e4SSteven Hartland dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist) 305a7a845e4SSteven Hartland { 306a7a845e4SSteven Hartland dsl_dataset_user_hold_arg_t dduha; 307a7a845e4SSteven Hartland nvpair_t *pair; 308a7a845e4SSteven Hartland int ret; 309a7a845e4SSteven Hartland 310a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, NULL); 311a7a845e4SSteven Hartland if (pair == NULL) 312a7a845e4SSteven Hartland return (0); 313a7a845e4SSteven Hartland 314a7a845e4SSteven Hartland dduha.dduha_holds = holds; 315a7a845e4SSteven Hartland dduha.dduha_chkholds = fnvlist_alloc(); 316a7a845e4SSteven Hartland dduha.dduha_errlist = errlist; 317a7a845e4SSteven Hartland dduha.dduha_minor = cleanup_minor; 318a7a845e4SSteven Hartland 319a7a845e4SSteven Hartland ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check, 3207d46dc6cSMatthew Ahrens dsl_dataset_user_hold_sync, &dduha, 3217d46dc6cSMatthew Ahrens fnvlist_num_pairs(holds), ZFS_SPACE_CHECK_RESERVED); 322a7a845e4SSteven Hartland fnvlist_free(dduha.dduha_chkholds); 323a7a845e4SSteven Hartland 324a7a845e4SSteven Hartland return (ret); 325a7a845e4SSteven Hartland } 326a7a845e4SSteven Hartland 327a7a845e4SSteven Hartland typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag, 328a7a845e4SSteven Hartland dsl_dataset_t **dsp); 329a7a845e4SSteven Hartland 330a7a845e4SSteven Hartland typedef struct dsl_dataset_user_release_arg { 331a7a845e4SSteven Hartland dsl_holdfunc_t *ddura_holdfunc; 332a7a845e4SSteven Hartland nvlist_t *ddura_holds; 333a7a845e4SSteven Hartland nvlist_t *ddura_todelete; 334a7a845e4SSteven Hartland nvlist_t *ddura_errlist; 335a7a845e4SSteven Hartland nvlist_t *ddura_chkholds; 336a7a845e4SSteven Hartland } dsl_dataset_user_release_arg_t; 337a7a845e4SSteven Hartland 338a7a845e4SSteven Hartland /* Place a dataset hold on the snapshot identified by passed dsobj string */ 339a7a845e4SSteven Hartland static int 340a7a845e4SSteven Hartland dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag, 341a7a845e4SSteven Hartland dsl_dataset_t **dsp) 342a7a845e4SSteven Hartland { 343a7a845e4SSteven Hartland return (dsl_dataset_hold_obj(dp, strtonum(dsobj, NULL), tag, dsp)); 344a7a845e4SSteven Hartland } 345a7a845e4SSteven Hartland 346a7a845e4SSteven Hartland static int 347a7a845e4SSteven Hartland dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura, 348a7a845e4SSteven Hartland dsl_dataset_t *ds, nvlist_t *holds, const char *snapname) 349a7a845e4SSteven Hartland { 350a7a845e4SSteven Hartland uint64_t zapobj; 351a7a845e4SSteven Hartland nvlist_t *holds_found; 352a7a845e4SSteven Hartland objset_t *mos; 353a7a845e4SSteven Hartland int numholds; 354a7a845e4SSteven Hartland 355bc9014e6SJustin Gibbs if (!ds->ds_is_snapshot) 356a7a845e4SSteven Hartland return (SET_ERROR(EINVAL)); 357a7a845e4SSteven Hartland 358a7a845e4SSteven Hartland if (nvlist_empty(holds)) 359a7a845e4SSteven Hartland return (0); 360a7a845e4SSteven Hartland 361a7a845e4SSteven Hartland numholds = 0; 362a7a845e4SSteven Hartland mos = ds->ds_dir->dd_pool->dp_meta_objset; 363c1379625SJustin T. Gibbs zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; 364a7a845e4SSteven Hartland holds_found = fnvlist_alloc(); 365a7a845e4SSteven Hartland 366a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 367a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) { 368a7a845e4SSteven Hartland uint64_t tmp; 369a7a845e4SSteven Hartland int error; 370a7a845e4SSteven Hartland const char *holdname = nvpair_name(pair); 371a7a845e4SSteven Hartland 372a7a845e4SSteven Hartland if (zapobj != 0) 373a7a845e4SSteven Hartland error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp); 374a7a845e4SSteven Hartland else 375a7a845e4SSteven Hartland error = SET_ERROR(ENOENT); 376a7a845e4SSteven Hartland 377a7a845e4SSteven Hartland /* 378a7a845e4SSteven Hartland * Non-existent holds are put on the errlist, but don't 379a7a845e4SSteven Hartland * cause an overall failure. 380a7a845e4SSteven Hartland */ 381a7a845e4SSteven Hartland if (error == ENOENT) { 382a7a845e4SSteven Hartland if (ddura->ddura_errlist != NULL) { 383a7a845e4SSteven Hartland char *errtag = kmem_asprintf("%s#%s", 384a7a845e4SSteven Hartland snapname, holdname); 385a7a845e4SSteven Hartland fnvlist_add_int32(ddura->ddura_errlist, errtag, 386a7a845e4SSteven Hartland ENOENT); 387a7a845e4SSteven Hartland strfree(errtag); 388a7a845e4SSteven Hartland } 389a7a845e4SSteven Hartland continue; 390a7a845e4SSteven Hartland } 391a7a845e4SSteven Hartland 392a7a845e4SSteven Hartland if (error != 0) { 393a7a845e4SSteven Hartland fnvlist_free(holds_found); 394a7a845e4SSteven Hartland return (error); 395a7a845e4SSteven Hartland } 396a7a845e4SSteven Hartland 397a7a845e4SSteven Hartland fnvlist_add_boolean(holds_found, holdname); 398a7a845e4SSteven Hartland numholds++; 399a7a845e4SSteven Hartland } 400a7a845e4SSteven Hartland 401c1379625SJustin T. Gibbs if (DS_IS_DEFER_DESTROY(ds) && 402c1379625SJustin T. Gibbs dsl_dataset_phys(ds)->ds_num_children == 1 && 403a7a845e4SSteven Hartland ds->ds_userrefs == numholds) { 404a7a845e4SSteven Hartland /* we need to destroy the snapshot as well */ 405a7a845e4SSteven Hartland if (dsl_dataset_long_held(ds)) { 406a7a845e4SSteven Hartland fnvlist_free(holds_found); 407a7a845e4SSteven Hartland return (SET_ERROR(EBUSY)); 408a7a845e4SSteven Hartland } 409a7a845e4SSteven Hartland fnvlist_add_boolean(ddura->ddura_todelete, snapname); 410a7a845e4SSteven Hartland } 411a7a845e4SSteven Hartland 412a7a845e4SSteven Hartland if (numholds != 0) { 413a7a845e4SSteven Hartland fnvlist_add_nvlist(ddura->ddura_chkholds, snapname, 414a7a845e4SSteven Hartland holds_found); 415a7a845e4SSteven Hartland } 416a7a845e4SSteven Hartland fnvlist_free(holds_found); 417a7a845e4SSteven Hartland 418a7a845e4SSteven Hartland return (0); 419a7a845e4SSteven Hartland } 420a7a845e4SSteven Hartland 421a7a845e4SSteven Hartland static int 422a7a845e4SSteven Hartland dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) 423a7a845e4SSteven Hartland { 424a7a845e4SSteven Hartland dsl_dataset_user_release_arg_t *ddura; 425a7a845e4SSteven Hartland dsl_holdfunc_t *holdfunc; 426a7a845e4SSteven Hartland dsl_pool_t *dp; 427a7a845e4SSteven Hartland 428a7a845e4SSteven Hartland if (!dmu_tx_is_syncing(tx)) 429a7a845e4SSteven Hartland return (0); 430a7a845e4SSteven Hartland 431a7a845e4SSteven Hartland dp = dmu_tx_pool(tx); 432a7a845e4SSteven Hartland 433a7a845e4SSteven Hartland ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 434a7a845e4SSteven Hartland 435a7a845e4SSteven Hartland ddura = arg; 436a7a845e4SSteven Hartland holdfunc = ddura->ddura_holdfunc; 437a7a845e4SSteven Hartland 438a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); 439a7a845e4SSteven Hartland pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { 440a7a845e4SSteven Hartland int error; 441a7a845e4SSteven Hartland dsl_dataset_t *ds; 442a7a845e4SSteven Hartland nvlist_t *holds; 443a7a845e4SSteven Hartland const char *snapname = nvpair_name(pair); 444a7a845e4SSteven Hartland 445a7a845e4SSteven Hartland error = nvpair_value_nvlist(pair, &holds); 446a7a845e4SSteven Hartland if (error != 0) 447a7a845e4SSteven Hartland error = (SET_ERROR(EINVAL)); 448a7a845e4SSteven Hartland else 449a7a845e4SSteven Hartland error = holdfunc(dp, snapname, FTAG, &ds); 450a7a845e4SSteven Hartland if (error == 0) { 451a7a845e4SSteven Hartland error = dsl_dataset_user_release_check_one(ddura, ds, 452a7a845e4SSteven Hartland holds, snapname); 453a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG); 454a7a845e4SSteven Hartland } 455a7a845e4SSteven Hartland if (error != 0) { 456a7a845e4SSteven Hartland if (ddura->ddura_errlist != NULL) { 457a7a845e4SSteven Hartland fnvlist_add_int32(ddura->ddura_errlist, 458a7a845e4SSteven Hartland snapname, error); 459a7a845e4SSteven Hartland } 460a7a845e4SSteven Hartland /* 461a7a845e4SSteven Hartland * Non-existent snapshots are put on the errlist, 462a7a845e4SSteven Hartland * but don't cause an overall failure. 463a7a845e4SSteven Hartland */ 464a7a845e4SSteven Hartland if (error != ENOENT) 465a7a845e4SSteven Hartland return (error); 466a7a845e4SSteven Hartland } 467a7a845e4SSteven Hartland } 468a7a845e4SSteven Hartland 469a7a845e4SSteven Hartland return (0); 470a7a845e4SSteven Hartland } 471a7a845e4SSteven Hartland 472a7a845e4SSteven Hartland static void 473a7a845e4SSteven Hartland dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds, 474a7a845e4SSteven Hartland dmu_tx_t *tx) 475a7a845e4SSteven Hartland { 476a7a845e4SSteven Hartland dsl_pool_t *dp = ds->ds_dir->dd_pool; 477a7a845e4SSteven Hartland objset_t *mos = dp->dp_meta_objset; 478a7a845e4SSteven Hartland 479a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 480a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) { 481a7a845e4SSteven Hartland int error; 482a7a845e4SSteven Hartland const char *holdname = nvpair_name(pair); 483a7a845e4SSteven Hartland 484a7a845e4SSteven Hartland /* Remove temporary hold if one exists. */ 485a7a845e4SSteven Hartland error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx); 486a7a845e4SSteven Hartland VERIFY(error == 0 || error == ENOENT); 487a7a845e4SSteven Hartland 488c1379625SJustin T. Gibbs VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, 489c1379625SJustin T. Gibbs holdname, tx)); 490a7a845e4SSteven Hartland ds->ds_userrefs--; 491a7a845e4SSteven Hartland 492a7a845e4SSteven Hartland spa_history_log_internal_ds(ds, "release", tx, 493a7a845e4SSteven Hartland "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs); 494a7a845e4SSteven Hartland } 495a7a845e4SSteven Hartland } 496a7a845e4SSteven Hartland 497a7a845e4SSteven Hartland static void 498a7a845e4SSteven Hartland dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) 499a7a845e4SSteven Hartland { 500a7a845e4SSteven Hartland dsl_dataset_user_release_arg_t *ddura = arg; 501a7a845e4SSteven Hartland dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc; 502a7a845e4SSteven Hartland dsl_pool_t *dp = dmu_tx_pool(tx); 503a7a845e4SSteven Hartland 504a7a845e4SSteven Hartland ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 505a7a845e4SSteven Hartland 506a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL); 507a7a845e4SSteven Hartland pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds, 508a7a845e4SSteven Hartland pair)) { 509a7a845e4SSteven Hartland dsl_dataset_t *ds; 510a7a845e4SSteven Hartland const char *name = nvpair_name(pair); 511a7a845e4SSteven Hartland 512a7a845e4SSteven Hartland VERIFY0(holdfunc(dp, name, FTAG, &ds)); 513a7a845e4SSteven Hartland 514a7a845e4SSteven Hartland dsl_dataset_user_release_sync_one(ds, 515a7a845e4SSteven Hartland fnvpair_value_nvlist(pair), tx); 516a7a845e4SSteven Hartland if (nvlist_exists(ddura->ddura_todelete, name)) { 517a7a845e4SSteven Hartland ASSERT(ds->ds_userrefs == 0 && 518c1379625SJustin T. Gibbs dsl_dataset_phys(ds)->ds_num_children == 1 && 519a7a845e4SSteven Hartland DS_IS_DEFER_DESTROY(ds)); 520a7a845e4SSteven Hartland dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); 521a7a845e4SSteven Hartland } 522a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG); 523a7a845e4SSteven Hartland } 524a7a845e4SSteven Hartland } 525a7a845e4SSteven Hartland 526a7a845e4SSteven Hartland /* 527a7a845e4SSteven Hartland * The full semantics of this function are described in the comment above 528a7a845e4SSteven Hartland * lzc_release(). 529a7a845e4SSteven Hartland * 530a7a845e4SSteven Hartland * To summarize: 531a7a845e4SSteven Hartland * Releases holds specified in the nvl holds. 532a7a845e4SSteven Hartland * 533a7a845e4SSteven Hartland * holds is nvl of snapname -> { holdname, ... } 534a7a845e4SSteven Hartland * errlist will be filled in with snapname -> error 535a7a845e4SSteven Hartland * 536a7a845e4SSteven Hartland * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots, 537a7a845e4SSteven Hartland * otherwise they should be the names of shapshots. 538a7a845e4SSteven Hartland * 539a7a845e4SSteven Hartland * As a release may cause snapshots to be destroyed this trys to ensure they 540a7a845e4SSteven Hartland * aren't mounted. 541a7a845e4SSteven Hartland * 542a7a845e4SSteven Hartland * The release of non-existent holds are skipped. 543a7a845e4SSteven Hartland * 544a7a845e4SSteven Hartland * At least one hold must have been released for the this function to succeed 545a7a845e4SSteven Hartland * and return 0. 546a7a845e4SSteven Hartland */ 547a7a845e4SSteven Hartland static int 548a7a845e4SSteven Hartland dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist, 549a7a845e4SSteven Hartland dsl_pool_t *tmpdp) 550a7a845e4SSteven Hartland { 551a7a845e4SSteven Hartland dsl_dataset_user_release_arg_t ddura; 552a7a845e4SSteven Hartland nvpair_t *pair; 553a7a845e4SSteven Hartland char *pool; 554a7a845e4SSteven Hartland int error; 555a7a845e4SSteven Hartland 556a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, NULL); 557a7a845e4SSteven Hartland if (pair == NULL) 558a7a845e4SSteven Hartland return (0); 559a7a845e4SSteven Hartland 560a7a845e4SSteven Hartland /* 561a7a845e4SSteven Hartland * The release may cause snapshots to be destroyed; make sure they 562a7a845e4SSteven Hartland * are not mounted. 563a7a845e4SSteven Hartland */ 564a7a845e4SSteven Hartland if (tmpdp != NULL) { 565a7a845e4SSteven Hartland /* Temporary holds are specified by dsobj string. */ 566a7a845e4SSteven Hartland ddura.ddura_holdfunc = dsl_dataset_hold_obj_string; 567a7a845e4SSteven Hartland pool = spa_name(tmpdp->dp_spa); 568a7a845e4SSteven Hartland #ifdef _KERNEL 569a7a845e4SSteven Hartland for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 570a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) { 571a7a845e4SSteven Hartland dsl_dataset_t *ds; 572a7a845e4SSteven Hartland 573c50d56f6SSteven Hartland dsl_pool_config_enter(tmpdp, FTAG); 574a7a845e4SSteven Hartland error = dsl_dataset_hold_obj_string(tmpdp, 575a7a845e4SSteven Hartland nvpair_name(pair), FTAG, &ds); 576a7a845e4SSteven Hartland if (error == 0) { 577*40a5c998SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 578a7a845e4SSteven Hartland dsl_dataset_name(ds, name); 579c50d56f6SSteven Hartland dsl_pool_config_exit(tmpdp, FTAG); 580a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG); 581a7a845e4SSteven Hartland (void) zfs_unmount_snap(name); 582c50d56f6SSteven Hartland } else { 583a7a845e4SSteven Hartland dsl_pool_config_exit(tmpdp, FTAG); 584c50d56f6SSteven Hartland } 585c50d56f6SSteven Hartland } 586a7a845e4SSteven Hartland #endif 587a7a845e4SSteven Hartland } else { 588a7a845e4SSteven Hartland /* Non-temporary holds are specified by name. */ 589a7a845e4SSteven Hartland ddura.ddura_holdfunc = dsl_dataset_hold; 590a7a845e4SSteven Hartland pool = nvpair_name(pair); 591a7a845e4SSteven Hartland #ifdef _KERNEL 592a7a845e4SSteven Hartland for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 593a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) { 594a7a845e4SSteven Hartland (void) zfs_unmount_snap(nvpair_name(pair)); 595a7a845e4SSteven Hartland } 596a7a845e4SSteven Hartland #endif 597a7a845e4SSteven Hartland } 598a7a845e4SSteven Hartland 599a7a845e4SSteven Hartland ddura.ddura_holds = holds; 600a7a845e4SSteven Hartland ddura.ddura_errlist = errlist; 601a7a845e4SSteven Hartland ddura.ddura_todelete = fnvlist_alloc(); 602a7a845e4SSteven Hartland ddura.ddura_chkholds = fnvlist_alloc(); 603a7a845e4SSteven Hartland 604a7a845e4SSteven Hartland error = dsl_sync_task(pool, dsl_dataset_user_release_check, 6057d46dc6cSMatthew Ahrens dsl_dataset_user_release_sync, &ddura, 0, ZFS_SPACE_CHECK_NONE); 606a7a845e4SSteven Hartland fnvlist_free(ddura.ddura_todelete); 607a7a845e4SSteven Hartland fnvlist_free(ddura.ddura_chkholds); 608a7a845e4SSteven Hartland 609a7a845e4SSteven Hartland return (error); 610a7a845e4SSteven Hartland } 611a7a845e4SSteven Hartland 612a7a845e4SSteven Hartland /* 613a7a845e4SSteven Hartland * holds is nvl of snapname -> { holdname, ... } 614a7a845e4SSteven Hartland * errlist will be filled in with snapname -> error 615a7a845e4SSteven Hartland */ 616a7a845e4SSteven Hartland int 617a7a845e4SSteven Hartland dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist) 618a7a845e4SSteven Hartland { 619a7a845e4SSteven Hartland return (dsl_dataset_user_release_impl(holds, errlist, NULL)); 620a7a845e4SSteven Hartland } 621a7a845e4SSteven Hartland 622a7a845e4SSteven Hartland /* 623a7a845e4SSteven Hartland * holds is nvl of snapdsobj -> { holdname, ... } 624a7a845e4SSteven Hartland */ 625a7a845e4SSteven Hartland void 626a7a845e4SSteven Hartland dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds) 627a7a845e4SSteven Hartland { 628a7a845e4SSteven Hartland ASSERT(dp != NULL); 629a7a845e4SSteven Hartland (void) dsl_dataset_user_release_impl(holds, NULL, dp); 630a7a845e4SSteven Hartland } 631a7a845e4SSteven Hartland 6323b2aab18SMatthew Ahrens int 6333b2aab18SMatthew Ahrens dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) 6343b2aab18SMatthew Ahrens { 6353b2aab18SMatthew Ahrens dsl_pool_t *dp; 6363b2aab18SMatthew Ahrens dsl_dataset_t *ds; 6373b2aab18SMatthew Ahrens int err; 6383b2aab18SMatthew Ahrens 6393b2aab18SMatthew Ahrens err = dsl_pool_hold(dsname, FTAG, &dp); 6403b2aab18SMatthew Ahrens if (err != 0) 6413b2aab18SMatthew Ahrens return (err); 6423b2aab18SMatthew Ahrens err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 6433b2aab18SMatthew Ahrens if (err != 0) { 6443b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 6453b2aab18SMatthew Ahrens return (err); 6463b2aab18SMatthew Ahrens } 6473b2aab18SMatthew Ahrens 648c1379625SJustin T. Gibbs if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { 6493b2aab18SMatthew Ahrens zap_attribute_t *za; 6503b2aab18SMatthew Ahrens zap_cursor_t zc; 6513b2aab18SMatthew Ahrens 6523b2aab18SMatthew Ahrens za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); 6533b2aab18SMatthew Ahrens for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, 654c1379625SJustin T. Gibbs dsl_dataset_phys(ds)->ds_userrefs_obj); 6553b2aab18SMatthew Ahrens zap_cursor_retrieve(&zc, za) == 0; 6563b2aab18SMatthew Ahrens zap_cursor_advance(&zc)) { 6573b2aab18SMatthew Ahrens fnvlist_add_uint64(nvl, za->za_name, 6583b2aab18SMatthew Ahrens za->za_first_integer); 6593b2aab18SMatthew Ahrens } 6603b2aab18SMatthew Ahrens zap_cursor_fini(&zc); 6613b2aab18SMatthew Ahrens kmem_free(za, sizeof (zap_attribute_t)); 6623b2aab18SMatthew Ahrens } 6633b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 6643b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 6653b2aab18SMatthew Ahrens return (0); 6663b2aab18SMatthew Ahrens } 667