1eda14cbcSMatt Macy /* 2eda14cbcSMatt Macy * CDDL HEADER START 3eda14cbcSMatt Macy * 4eda14cbcSMatt Macy * The contents of this file are subject to the terms of the 5eda14cbcSMatt Macy * Common Development and Distribution License (the "License"). 6eda14cbcSMatt Macy * You may not use this file except in compliance with the License. 7eda14cbcSMatt Macy * 8eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 10eda14cbcSMatt Macy * See the License for the specific language governing permissions 11eda14cbcSMatt Macy * and limitations under the License. 12eda14cbcSMatt Macy * 13eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each 14eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the 16eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying 17eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner] 18eda14cbcSMatt Macy * 19eda14cbcSMatt Macy * CDDL HEADER END 20eda14cbcSMatt Macy */ 21eda14cbcSMatt Macy /* 22eda14cbcSMatt Macy * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23eda14cbcSMatt Macy * Copyright (c) 2012, 2017 by Delphix. All rights reserved. 24eda14cbcSMatt Macy * Copyright (c) 2013 Steven Hartland. All rights reserved. 25eda14cbcSMatt Macy */ 26eda14cbcSMatt Macy 27eda14cbcSMatt Macy #include <sys/zfs_context.h> 28eda14cbcSMatt Macy #include <sys/dsl_userhold.h> 29eda14cbcSMatt Macy #include <sys/dsl_dataset.h> 30eda14cbcSMatt Macy #include <sys/dsl_destroy.h> 31eda14cbcSMatt Macy #include <sys/dsl_synctask.h> 32eda14cbcSMatt Macy #include <sys/dmu_tx.h> 33eda14cbcSMatt Macy #include <sys/zfs_onexit.h> 34eda14cbcSMatt Macy #include <sys/dsl_pool.h> 35eda14cbcSMatt Macy #include <sys/dsl_dir.h> 36eda14cbcSMatt Macy #include <sys/zfs_ioctl.h> 37eda14cbcSMatt Macy #include <sys/zap.h> 38eda14cbcSMatt Macy 39eda14cbcSMatt Macy typedef struct dsl_dataset_user_hold_arg { 40eda14cbcSMatt Macy nvlist_t *dduha_holds; 41eda14cbcSMatt Macy nvlist_t *dduha_chkholds; 42eda14cbcSMatt Macy nvlist_t *dduha_errlist; 43eda14cbcSMatt Macy minor_t dduha_minor; 44eda14cbcSMatt Macy } dsl_dataset_user_hold_arg_t; 45eda14cbcSMatt Macy 46eda14cbcSMatt Macy /* 47eda14cbcSMatt Macy * If you add new checks here, you may need to add additional checks to the 48eda14cbcSMatt Macy * "temporary" case in snapshot_check() in dmu_objset.c. 49eda14cbcSMatt Macy */ 50eda14cbcSMatt Macy int 51eda14cbcSMatt Macy dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag, 52eda14cbcSMatt Macy boolean_t temphold, dmu_tx_t *tx) 53eda14cbcSMatt Macy { 54eda14cbcSMatt Macy dsl_pool_t *dp = dmu_tx_pool(tx); 55eda14cbcSMatt Macy objset_t *mos = dp->dp_meta_objset; 56eda14cbcSMatt Macy int error = 0; 57eda14cbcSMatt Macy 58eda14cbcSMatt Macy ASSERT(dsl_pool_config_held(dp)); 59eda14cbcSMatt Macy 60eda14cbcSMatt Macy if (strlen(htag) > MAXNAMELEN) 61eda14cbcSMatt Macy return (SET_ERROR(E2BIG)); 62eda14cbcSMatt Macy /* Tempholds have a more restricted length */ 63eda14cbcSMatt Macy if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN) 64eda14cbcSMatt Macy return (SET_ERROR(E2BIG)); 65eda14cbcSMatt Macy 66eda14cbcSMatt Macy /* tags must be unique (if ds already exists) */ 67eda14cbcSMatt Macy if (ds != NULL && dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { 68eda14cbcSMatt Macy uint64_t value; 69eda14cbcSMatt Macy 70eda14cbcSMatt Macy error = zap_lookup(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, 71eda14cbcSMatt Macy htag, 8, 1, &value); 72eda14cbcSMatt Macy if (error == 0) 73eda14cbcSMatt Macy error = SET_ERROR(EEXIST); 74eda14cbcSMatt Macy else if (error == ENOENT) 75eda14cbcSMatt Macy error = 0; 76eda14cbcSMatt Macy } 77eda14cbcSMatt Macy 78eda14cbcSMatt Macy return (error); 79eda14cbcSMatt Macy } 80eda14cbcSMatt Macy 81eda14cbcSMatt Macy static int 82eda14cbcSMatt Macy dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) 83eda14cbcSMatt Macy { 84eda14cbcSMatt Macy dsl_dataset_user_hold_arg_t *dduha = arg; 85eda14cbcSMatt Macy dsl_pool_t *dp = dmu_tx_pool(tx); 86eda14cbcSMatt Macy nvlist_t *tmp_holds; 87eda14cbcSMatt Macy 88eda14cbcSMatt Macy if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) 89eda14cbcSMatt Macy return (SET_ERROR(ENOTSUP)); 90eda14cbcSMatt Macy 91eda14cbcSMatt Macy if (!dmu_tx_is_syncing(tx)) 92eda14cbcSMatt Macy return (0); 93eda14cbcSMatt Macy 94eda14cbcSMatt Macy /* 95eda14cbcSMatt Macy * Ensure the list has no duplicates by copying name/values from 96eda14cbcSMatt Macy * non-unique dduha_holds to unique tmp_holds, and comparing counts. 97eda14cbcSMatt Macy */ 98eda14cbcSMatt Macy tmp_holds = fnvlist_alloc(); 99eda14cbcSMatt Macy for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); 100eda14cbcSMatt Macy pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { 101eda14cbcSMatt Macy size_t len = strlen(nvpair_name(pair)) + 102eda14cbcSMatt Macy strlen(fnvpair_value_string(pair)); 103eda14cbcSMatt Macy char *nameval = kmem_zalloc(len + 2, KM_SLEEP); 104eda14cbcSMatt Macy (void) strlcpy(nameval, nvpair_name(pair), len + 2); 105eda14cbcSMatt Macy (void) strlcat(nameval, "@", len + 2); 106eda14cbcSMatt Macy (void) strlcat(nameval, fnvpair_value_string(pair), len + 2); 107eda14cbcSMatt Macy fnvlist_add_string(tmp_holds, nameval, ""); 108eda14cbcSMatt Macy kmem_free(nameval, len + 2); 109eda14cbcSMatt Macy } 110eda14cbcSMatt Macy size_t tmp_count = fnvlist_num_pairs(tmp_holds); 111eda14cbcSMatt Macy fnvlist_free(tmp_holds); 112eda14cbcSMatt Macy if (tmp_count != fnvlist_num_pairs(dduha->dduha_holds)) 113eda14cbcSMatt Macy return (SET_ERROR(EEXIST)); 114eda14cbcSMatt Macy for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); 115eda14cbcSMatt Macy pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { 116eda14cbcSMatt Macy dsl_dataset_t *ds; 117eda14cbcSMatt Macy int error = 0; 1182a58b312SMartin Matuska const char *htag, *name; 119eda14cbcSMatt Macy 120eda14cbcSMatt Macy /* must be a snapshot */ 121eda14cbcSMatt Macy name = nvpair_name(pair); 122eda14cbcSMatt Macy if (strchr(name, '@') == NULL) 123eda14cbcSMatt Macy error = SET_ERROR(EINVAL); 124eda14cbcSMatt Macy 125eda14cbcSMatt Macy if (error == 0) 126eda14cbcSMatt Macy error = nvpair_value_string(pair, &htag); 127eda14cbcSMatt Macy 128eda14cbcSMatt Macy if (error == 0) 129eda14cbcSMatt Macy error = dsl_dataset_hold(dp, name, FTAG, &ds); 130eda14cbcSMatt Macy 131eda14cbcSMatt Macy if (error == 0) { 132eda14cbcSMatt Macy error = dsl_dataset_user_hold_check_one(ds, htag, 133eda14cbcSMatt Macy dduha->dduha_minor != 0, tx); 134eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 135eda14cbcSMatt Macy } 136eda14cbcSMatt Macy 137eda14cbcSMatt Macy if (error == 0) { 138eda14cbcSMatt Macy fnvlist_add_string(dduha->dduha_chkholds, name, htag); 139eda14cbcSMatt Macy } else { 140eda14cbcSMatt Macy /* 141eda14cbcSMatt Macy * We register ENOENT errors so they can be correctly 142eda14cbcSMatt Macy * reported if needed, such as when all holds fail. 143eda14cbcSMatt Macy */ 144eda14cbcSMatt Macy fnvlist_add_int32(dduha->dduha_errlist, name, error); 145eda14cbcSMatt Macy if (error != ENOENT) 146eda14cbcSMatt Macy return (error); 147eda14cbcSMatt Macy } 148eda14cbcSMatt Macy } 149eda14cbcSMatt Macy 150eda14cbcSMatt Macy return (0); 151eda14cbcSMatt Macy } 152eda14cbcSMatt Macy 153eda14cbcSMatt Macy 154eda14cbcSMatt Macy static void 155eda14cbcSMatt Macy dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds, 156eda14cbcSMatt Macy const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx) 157eda14cbcSMatt Macy { 158eda14cbcSMatt Macy dsl_pool_t *dp = ds->ds_dir->dd_pool; 159eda14cbcSMatt Macy objset_t *mos = dp->dp_meta_objset; 160eda14cbcSMatt Macy uint64_t zapobj; 161eda14cbcSMatt Macy 162eda14cbcSMatt Macy ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 163eda14cbcSMatt Macy 164eda14cbcSMatt Macy if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) { 165eda14cbcSMatt Macy /* 166eda14cbcSMatt Macy * This is the first user hold for this dataset. Create 167eda14cbcSMatt Macy * the userrefs zap object. 168eda14cbcSMatt Macy */ 169eda14cbcSMatt Macy dmu_buf_will_dirty(ds->ds_dbuf, tx); 170eda14cbcSMatt Macy zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj = 171eda14cbcSMatt Macy zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); 172eda14cbcSMatt Macy } else { 173eda14cbcSMatt Macy zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; 174eda14cbcSMatt Macy } 175eda14cbcSMatt Macy ds->ds_userrefs++; 176eda14cbcSMatt Macy 177eda14cbcSMatt Macy VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx)); 178eda14cbcSMatt Macy 179eda14cbcSMatt Macy if (minor != 0) { 180eda14cbcSMatt Macy char name[MAXNAMELEN]; 181eda14cbcSMatt Macy nvlist_t *tags; 182eda14cbcSMatt Macy 183eda14cbcSMatt Macy VERIFY0(dsl_pool_user_hold(dp, ds->ds_object, 184eda14cbcSMatt Macy htag, now, tx)); 185eda14cbcSMatt Macy (void) snprintf(name, sizeof (name), "%llx", 186eda14cbcSMatt Macy (u_longlong_t)ds->ds_object); 187eda14cbcSMatt Macy 188eda14cbcSMatt Macy if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) { 189eda14cbcSMatt Macy tags = fnvlist_alloc(); 190eda14cbcSMatt Macy fnvlist_add_boolean(tags, htag); 191eda14cbcSMatt Macy fnvlist_add_nvlist(tmpholds, name, tags); 192eda14cbcSMatt Macy fnvlist_free(tags); 193eda14cbcSMatt Macy } else { 194eda14cbcSMatt Macy fnvlist_add_boolean(tags, htag); 195eda14cbcSMatt Macy } 196eda14cbcSMatt Macy } 197eda14cbcSMatt Macy 198eda14cbcSMatt Macy spa_history_log_internal_ds(ds, "hold", tx, 199eda14cbcSMatt Macy "tag=%s temp=%d refs=%llu", 200eda14cbcSMatt Macy htag, minor != 0, (u_longlong_t)ds->ds_userrefs); 201eda14cbcSMatt Macy } 202eda14cbcSMatt Macy 203eda14cbcSMatt Macy typedef struct zfs_hold_cleanup_arg { 204eda14cbcSMatt Macy char zhca_spaname[ZFS_MAX_DATASET_NAME_LEN]; 205eda14cbcSMatt Macy uint64_t zhca_spa_load_guid; 206eda14cbcSMatt Macy nvlist_t *zhca_holds; 207eda14cbcSMatt Macy } zfs_hold_cleanup_arg_t; 208eda14cbcSMatt Macy 209eda14cbcSMatt Macy static void 210eda14cbcSMatt Macy dsl_dataset_user_release_onexit(void *arg) 211eda14cbcSMatt Macy { 212eda14cbcSMatt Macy zfs_hold_cleanup_arg_t *ca = arg; 213eda14cbcSMatt Macy spa_t *spa; 214eda14cbcSMatt Macy int error; 215eda14cbcSMatt Macy 216eda14cbcSMatt Macy error = spa_open(ca->zhca_spaname, &spa, FTAG); 217eda14cbcSMatt Macy if (error != 0) { 218eda14cbcSMatt Macy zfs_dbgmsg("couldn't release holds on pool=%s " 219eda14cbcSMatt Macy "because pool is no longer loaded", 220eda14cbcSMatt Macy ca->zhca_spaname); 221eda14cbcSMatt Macy return; 222eda14cbcSMatt Macy } 223eda14cbcSMatt Macy if (spa_load_guid(spa) != ca->zhca_spa_load_guid) { 224eda14cbcSMatt Macy zfs_dbgmsg("couldn't release holds on pool=%s " 225eda14cbcSMatt Macy "because pool is no longer loaded (guid doesn't match)", 226eda14cbcSMatt Macy ca->zhca_spaname); 227eda14cbcSMatt Macy spa_close(spa, FTAG); 228eda14cbcSMatt Macy return; 229eda14cbcSMatt Macy } 230eda14cbcSMatt Macy 231eda14cbcSMatt Macy (void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds); 232eda14cbcSMatt Macy fnvlist_free(ca->zhca_holds); 233eda14cbcSMatt Macy kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t)); 234eda14cbcSMatt Macy spa_close(spa, FTAG); 235eda14cbcSMatt Macy } 236eda14cbcSMatt Macy 237eda14cbcSMatt Macy static void 238eda14cbcSMatt Macy dsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor) 239eda14cbcSMatt Macy { 240eda14cbcSMatt Macy zfs_hold_cleanup_arg_t *ca; 241eda14cbcSMatt Macy 242eda14cbcSMatt Macy if (minor == 0 || nvlist_empty(holds)) { 243eda14cbcSMatt Macy fnvlist_free(holds); 244eda14cbcSMatt Macy return; 245eda14cbcSMatt Macy } 246eda14cbcSMatt Macy 247eda14cbcSMatt Macy ASSERT(spa != NULL); 248eda14cbcSMatt Macy ca = kmem_alloc(sizeof (*ca), KM_SLEEP); 249eda14cbcSMatt Macy 250eda14cbcSMatt Macy (void) strlcpy(ca->zhca_spaname, spa_name(spa), 251eda14cbcSMatt Macy sizeof (ca->zhca_spaname)); 252eda14cbcSMatt Macy ca->zhca_spa_load_guid = spa_load_guid(spa); 253eda14cbcSMatt Macy ca->zhca_holds = holds; 254eda14cbcSMatt Macy VERIFY0(zfs_onexit_add_cb(minor, 255eda14cbcSMatt Macy dsl_dataset_user_release_onexit, ca, NULL)); 256eda14cbcSMatt Macy } 257eda14cbcSMatt Macy 258eda14cbcSMatt Macy void 259eda14cbcSMatt Macy dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag, 260eda14cbcSMatt Macy minor_t minor, uint64_t now, dmu_tx_t *tx) 261eda14cbcSMatt Macy { 262eda14cbcSMatt Macy nvlist_t *tmpholds; 263eda14cbcSMatt Macy 264eda14cbcSMatt Macy if (minor != 0) 265eda14cbcSMatt Macy tmpholds = fnvlist_alloc(); 266eda14cbcSMatt Macy else 267eda14cbcSMatt Macy tmpholds = NULL; 268eda14cbcSMatt Macy dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx); 269eda14cbcSMatt Macy dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor); 270eda14cbcSMatt Macy } 271eda14cbcSMatt Macy 272eda14cbcSMatt Macy static void 273eda14cbcSMatt Macy dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx) 274eda14cbcSMatt Macy { 275eda14cbcSMatt Macy dsl_dataset_user_hold_arg_t *dduha = arg; 276eda14cbcSMatt Macy dsl_pool_t *dp = dmu_tx_pool(tx); 277eda14cbcSMatt Macy nvlist_t *tmpholds; 278eda14cbcSMatt Macy uint64_t now = gethrestime_sec(); 279eda14cbcSMatt Macy 280eda14cbcSMatt Macy if (dduha->dduha_minor != 0) 281eda14cbcSMatt Macy tmpholds = fnvlist_alloc(); 282eda14cbcSMatt Macy else 283eda14cbcSMatt Macy tmpholds = NULL; 284eda14cbcSMatt Macy for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL); 285eda14cbcSMatt Macy pair != NULL; 286eda14cbcSMatt Macy pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) { 287eda14cbcSMatt Macy dsl_dataset_t *ds; 288eda14cbcSMatt Macy 289eda14cbcSMatt Macy VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); 290eda14cbcSMatt Macy dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, 291eda14cbcSMatt Macy fnvpair_value_string(pair), dduha->dduha_minor, now, tx); 292eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 293eda14cbcSMatt Macy } 294eda14cbcSMatt Macy dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor); 295eda14cbcSMatt Macy } 296eda14cbcSMatt Macy 297eda14cbcSMatt Macy /* 298eda14cbcSMatt Macy * The full semantics of this function are described in the comment above 299eda14cbcSMatt Macy * lzc_hold(). 300eda14cbcSMatt Macy * 301eda14cbcSMatt Macy * To summarize: 302eda14cbcSMatt Macy * holds is nvl of snapname -> holdname 303eda14cbcSMatt Macy * errlist will be filled in with snapname -> error 304eda14cbcSMatt Macy * 305eda14cbcSMatt Macy * The snapshots must all be in the same pool. 306eda14cbcSMatt Macy * 307eda14cbcSMatt Macy * Holds for snapshots that don't exist will be skipped. 308eda14cbcSMatt Macy * 309eda14cbcSMatt Macy * If none of the snapshots for requested holds exist then ENOENT will be 310eda14cbcSMatt Macy * returned. 311eda14cbcSMatt Macy * 312eda14cbcSMatt Macy * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned 313eda14cbcSMatt Macy * up when the process exits. 314eda14cbcSMatt Macy * 315eda14cbcSMatt Macy * On success all the holds, for snapshots that existed, will be created and 0 316eda14cbcSMatt Macy * will be returned. 317eda14cbcSMatt Macy * 318eda14cbcSMatt Macy * On failure no holds will be created, the errlist will be filled in, 319eda14cbcSMatt Macy * and an errno will returned. 320eda14cbcSMatt Macy * 321eda14cbcSMatt Macy * In all cases the errlist will contain entries for holds where the snapshot 322eda14cbcSMatt Macy * didn't exist. 323eda14cbcSMatt Macy */ 324eda14cbcSMatt Macy int 325eda14cbcSMatt Macy dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist) 326eda14cbcSMatt Macy { 327eda14cbcSMatt Macy dsl_dataset_user_hold_arg_t dduha; 328eda14cbcSMatt Macy nvpair_t *pair; 329eda14cbcSMatt Macy int ret; 330eda14cbcSMatt Macy 331eda14cbcSMatt Macy pair = nvlist_next_nvpair(holds, NULL); 332eda14cbcSMatt Macy if (pair == NULL) 333eda14cbcSMatt Macy return (0); 334eda14cbcSMatt Macy 335eda14cbcSMatt Macy dduha.dduha_holds = holds; 336eda14cbcSMatt Macy /* chkholds can have non-unique name */ 337eda14cbcSMatt Macy VERIFY(0 == nvlist_alloc(&dduha.dduha_chkholds, 0, KM_SLEEP)); 338eda14cbcSMatt Macy dduha.dduha_errlist = errlist; 339eda14cbcSMatt Macy dduha.dduha_minor = cleanup_minor; 340eda14cbcSMatt Macy 341eda14cbcSMatt Macy ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check, 342eda14cbcSMatt Macy dsl_dataset_user_hold_sync, &dduha, 343eda14cbcSMatt Macy fnvlist_num_pairs(holds), ZFS_SPACE_CHECK_RESERVED); 344eda14cbcSMatt Macy fnvlist_free(dduha.dduha_chkholds); 345eda14cbcSMatt Macy 346eda14cbcSMatt Macy return (ret); 347eda14cbcSMatt Macy } 348eda14cbcSMatt Macy 349a0b956f5SMartin Matuska typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, const void *tag, 350eda14cbcSMatt Macy dsl_dataset_t **dsp); 351eda14cbcSMatt Macy 352eda14cbcSMatt Macy typedef struct dsl_dataset_user_release_arg { 353eda14cbcSMatt Macy dsl_holdfunc_t *ddura_holdfunc; 354eda14cbcSMatt Macy nvlist_t *ddura_holds; 355eda14cbcSMatt Macy nvlist_t *ddura_todelete; 356eda14cbcSMatt Macy nvlist_t *ddura_errlist; 357eda14cbcSMatt Macy nvlist_t *ddura_chkholds; 358eda14cbcSMatt Macy } dsl_dataset_user_release_arg_t; 359eda14cbcSMatt Macy 360eda14cbcSMatt Macy /* Place a dataset hold on the snapshot identified by passed dsobj string */ 361eda14cbcSMatt Macy static int 362a0b956f5SMartin Matuska dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, const void *tag, 363eda14cbcSMatt Macy dsl_dataset_t **dsp) 364eda14cbcSMatt Macy { 365eda14cbcSMatt Macy return (dsl_dataset_hold_obj(dp, zfs_strtonum(dsobj, NULL), tag, dsp)); 366eda14cbcSMatt Macy } 367eda14cbcSMatt Macy 368eda14cbcSMatt Macy static int 369eda14cbcSMatt Macy dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura, 370eda14cbcSMatt Macy dsl_dataset_t *ds, nvlist_t *holds, const char *snapname) 371eda14cbcSMatt Macy { 372eda14cbcSMatt Macy uint64_t zapobj; 373eda14cbcSMatt Macy nvlist_t *holds_found; 374eda14cbcSMatt Macy objset_t *mos; 375eda14cbcSMatt Macy int numholds; 376eda14cbcSMatt Macy 377eda14cbcSMatt Macy if (!ds->ds_is_snapshot) 378eda14cbcSMatt Macy return (SET_ERROR(EINVAL)); 379eda14cbcSMatt Macy 380eda14cbcSMatt Macy if (nvlist_empty(holds)) 381eda14cbcSMatt Macy return (0); 382eda14cbcSMatt Macy 383eda14cbcSMatt Macy numholds = 0; 384eda14cbcSMatt Macy mos = ds->ds_dir->dd_pool->dp_meta_objset; 385eda14cbcSMatt Macy zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; 386eda14cbcSMatt Macy VERIFY0(nvlist_alloc(&holds_found, NV_UNIQUE_NAME, KM_SLEEP)); 387eda14cbcSMatt Macy 388eda14cbcSMatt Macy for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 389eda14cbcSMatt Macy pair = nvlist_next_nvpair(holds, pair)) { 390eda14cbcSMatt Macy uint64_t tmp; 391eda14cbcSMatt Macy int error; 392eda14cbcSMatt Macy const char *holdname = nvpair_name(pair); 393eda14cbcSMatt Macy 394eda14cbcSMatt Macy if (zapobj != 0) 395eda14cbcSMatt Macy error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp); 396eda14cbcSMatt Macy else 397eda14cbcSMatt Macy error = SET_ERROR(ENOENT); 398eda14cbcSMatt Macy 399eda14cbcSMatt Macy /* 400eda14cbcSMatt Macy * Non-existent holds are put on the errlist, but don't 401eda14cbcSMatt Macy * cause an overall failure. 402eda14cbcSMatt Macy */ 403eda14cbcSMatt Macy if (error == ENOENT) { 404eda14cbcSMatt Macy if (ddura->ddura_errlist != NULL) { 405eda14cbcSMatt Macy char *errtag = kmem_asprintf("%s#%s", 406eda14cbcSMatt Macy snapname, holdname); 407eda14cbcSMatt Macy fnvlist_add_int32(ddura->ddura_errlist, errtag, 408eda14cbcSMatt Macy ENOENT); 409eda14cbcSMatt Macy kmem_strfree(errtag); 410eda14cbcSMatt Macy } 411eda14cbcSMatt Macy continue; 412eda14cbcSMatt Macy } 413eda14cbcSMatt Macy 414eda14cbcSMatt Macy if (error != 0) { 415eda14cbcSMatt Macy fnvlist_free(holds_found); 416eda14cbcSMatt Macy return (error); 417eda14cbcSMatt Macy } 418eda14cbcSMatt Macy 419eda14cbcSMatt Macy fnvlist_add_boolean(holds_found, holdname); 420eda14cbcSMatt Macy numholds++; 421eda14cbcSMatt Macy } 422eda14cbcSMatt Macy 423eda14cbcSMatt Macy if (DS_IS_DEFER_DESTROY(ds) && 424eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_num_children == 1 && 425eda14cbcSMatt Macy ds->ds_userrefs == numholds) { 426eda14cbcSMatt Macy /* we need to destroy the snapshot as well */ 427eda14cbcSMatt Macy if (dsl_dataset_long_held(ds)) { 428eda14cbcSMatt Macy fnvlist_free(holds_found); 429eda14cbcSMatt Macy return (SET_ERROR(EBUSY)); 430eda14cbcSMatt Macy } 431eda14cbcSMatt Macy fnvlist_add_boolean(ddura->ddura_todelete, snapname); 432eda14cbcSMatt Macy } 433eda14cbcSMatt Macy 434eda14cbcSMatt Macy if (numholds != 0) { 435eda14cbcSMatt Macy fnvlist_add_nvlist(ddura->ddura_chkholds, snapname, 436eda14cbcSMatt Macy holds_found); 437eda14cbcSMatt Macy } 438eda14cbcSMatt Macy fnvlist_free(holds_found); 439eda14cbcSMatt Macy 440eda14cbcSMatt Macy return (0); 441eda14cbcSMatt Macy } 442eda14cbcSMatt Macy 443eda14cbcSMatt Macy static int 444eda14cbcSMatt Macy dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) 445eda14cbcSMatt Macy { 446eda14cbcSMatt Macy dsl_dataset_user_release_arg_t *ddura; 447eda14cbcSMatt Macy dsl_holdfunc_t *holdfunc; 448eda14cbcSMatt Macy dsl_pool_t *dp; 449eda14cbcSMatt Macy 450eda14cbcSMatt Macy if (!dmu_tx_is_syncing(tx)) 451eda14cbcSMatt Macy return (0); 452eda14cbcSMatt Macy 453eda14cbcSMatt Macy dp = dmu_tx_pool(tx); 454eda14cbcSMatt Macy 455eda14cbcSMatt Macy ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 456eda14cbcSMatt Macy 457eda14cbcSMatt Macy ddura = arg; 458eda14cbcSMatt Macy holdfunc = ddura->ddura_holdfunc; 459eda14cbcSMatt Macy 460eda14cbcSMatt Macy for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); 461eda14cbcSMatt Macy pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { 462eda14cbcSMatt Macy int error; 463eda14cbcSMatt Macy dsl_dataset_t *ds; 464eda14cbcSMatt Macy nvlist_t *holds; 465eda14cbcSMatt Macy const char *snapname = nvpair_name(pair); 466eda14cbcSMatt Macy 467eda14cbcSMatt Macy error = nvpair_value_nvlist(pair, &holds); 468eda14cbcSMatt Macy if (error != 0) 469eda14cbcSMatt Macy error = (SET_ERROR(EINVAL)); 470eda14cbcSMatt Macy else 471eda14cbcSMatt Macy error = holdfunc(dp, snapname, FTAG, &ds); 472eda14cbcSMatt Macy if (error == 0) { 473eda14cbcSMatt Macy error = dsl_dataset_user_release_check_one(ddura, ds, 474eda14cbcSMatt Macy holds, snapname); 475eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 476eda14cbcSMatt Macy } 477eda14cbcSMatt Macy if (error != 0) { 478eda14cbcSMatt Macy if (ddura->ddura_errlist != NULL) { 479eda14cbcSMatt Macy fnvlist_add_int32(ddura->ddura_errlist, 480eda14cbcSMatt Macy snapname, error); 481eda14cbcSMatt Macy } 482eda14cbcSMatt Macy /* 483eda14cbcSMatt Macy * Non-existent snapshots are put on the errlist, 484eda14cbcSMatt Macy * but don't cause an overall failure. 485eda14cbcSMatt Macy */ 486eda14cbcSMatt Macy if (error != ENOENT) 487eda14cbcSMatt Macy return (error); 488eda14cbcSMatt Macy } 489eda14cbcSMatt Macy } 490eda14cbcSMatt Macy 491eda14cbcSMatt Macy return (0); 492eda14cbcSMatt Macy } 493eda14cbcSMatt Macy 494eda14cbcSMatt Macy static void 495eda14cbcSMatt Macy dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds, 496eda14cbcSMatt Macy dmu_tx_t *tx) 497eda14cbcSMatt Macy { 498eda14cbcSMatt Macy dsl_pool_t *dp = ds->ds_dir->dd_pool; 499eda14cbcSMatt Macy objset_t *mos = dp->dp_meta_objset; 500eda14cbcSMatt Macy 501eda14cbcSMatt Macy for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 502eda14cbcSMatt Macy pair = nvlist_next_nvpair(holds, pair)) { 503eda14cbcSMatt Macy int error; 504eda14cbcSMatt Macy const char *holdname = nvpair_name(pair); 505eda14cbcSMatt Macy 506eda14cbcSMatt Macy /* Remove temporary hold if one exists. */ 507eda14cbcSMatt Macy error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx); 508eda14cbcSMatt Macy VERIFY(error == 0 || error == ENOENT); 509eda14cbcSMatt Macy 510eda14cbcSMatt Macy VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, 511eda14cbcSMatt Macy holdname, tx)); 512eda14cbcSMatt Macy ds->ds_userrefs--; 513eda14cbcSMatt Macy 514eda14cbcSMatt Macy spa_history_log_internal_ds(ds, "release", tx, 515eda14cbcSMatt Macy "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs); 516eda14cbcSMatt Macy } 517eda14cbcSMatt Macy } 518eda14cbcSMatt Macy 519eda14cbcSMatt Macy static void 520eda14cbcSMatt Macy dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) 521eda14cbcSMatt Macy { 522eda14cbcSMatt Macy dsl_dataset_user_release_arg_t *ddura = arg; 523eda14cbcSMatt Macy dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc; 524eda14cbcSMatt Macy dsl_pool_t *dp = dmu_tx_pool(tx); 525eda14cbcSMatt Macy 526eda14cbcSMatt Macy ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 527eda14cbcSMatt Macy 528eda14cbcSMatt Macy for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL); 529eda14cbcSMatt Macy pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds, 530eda14cbcSMatt Macy pair)) { 531eda14cbcSMatt Macy dsl_dataset_t *ds; 532eda14cbcSMatt Macy const char *name = nvpair_name(pair); 533eda14cbcSMatt Macy 534eda14cbcSMatt Macy VERIFY0(holdfunc(dp, name, FTAG, &ds)); 535eda14cbcSMatt Macy 536eda14cbcSMatt Macy dsl_dataset_user_release_sync_one(ds, 537eda14cbcSMatt Macy fnvpair_value_nvlist(pair), tx); 538eda14cbcSMatt Macy if (nvlist_exists(ddura->ddura_todelete, name)) { 539eda14cbcSMatt Macy ASSERT(ds->ds_userrefs == 0 && 540eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_num_children == 1 && 541eda14cbcSMatt Macy DS_IS_DEFER_DESTROY(ds)); 542eda14cbcSMatt Macy dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); 543eda14cbcSMatt Macy } 544eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 545eda14cbcSMatt Macy } 546eda14cbcSMatt Macy } 547eda14cbcSMatt Macy 548eda14cbcSMatt Macy /* 549eda14cbcSMatt Macy * The full semantics of this function are described in the comment above 550eda14cbcSMatt Macy * lzc_release(). 551eda14cbcSMatt Macy * 552eda14cbcSMatt Macy * To summarize: 553eda14cbcSMatt Macy * Releases holds specified in the nvl holds. 554eda14cbcSMatt Macy * 555eda14cbcSMatt Macy * holds is nvl of snapname -> { holdname, ... } 556eda14cbcSMatt Macy * errlist will be filled in with snapname -> error 557eda14cbcSMatt Macy * 558eda14cbcSMatt Macy * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots, 559eda14cbcSMatt Macy * otherwise they should be the names of snapshots. 560eda14cbcSMatt Macy * 561eda14cbcSMatt Macy * As a release may cause snapshots to be destroyed this tries to ensure they 562eda14cbcSMatt Macy * aren't mounted. 563eda14cbcSMatt Macy * 564eda14cbcSMatt Macy * The release of non-existent holds are skipped. 565eda14cbcSMatt Macy * 566eda14cbcSMatt Macy * At least one hold must have been released for the this function to succeed 567eda14cbcSMatt Macy * and return 0. 568eda14cbcSMatt Macy */ 569eda14cbcSMatt Macy static int 570eda14cbcSMatt Macy dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist, 571eda14cbcSMatt Macy dsl_pool_t *tmpdp) 572eda14cbcSMatt Macy { 573eda14cbcSMatt Macy dsl_dataset_user_release_arg_t ddura; 574eda14cbcSMatt Macy nvpair_t *pair; 5752a58b312SMartin Matuska const char *pool; 576eda14cbcSMatt Macy int error; 577eda14cbcSMatt Macy 578eda14cbcSMatt Macy pair = nvlist_next_nvpair(holds, NULL); 579eda14cbcSMatt Macy if (pair == NULL) 580eda14cbcSMatt Macy return (0); 581eda14cbcSMatt Macy 582eda14cbcSMatt Macy /* 583eda14cbcSMatt Macy * The release may cause snapshots to be destroyed; make sure they 584eda14cbcSMatt Macy * are not mounted. 585eda14cbcSMatt Macy */ 586eda14cbcSMatt Macy if (tmpdp != NULL) { 587eda14cbcSMatt Macy /* Temporary holds are specified by dsobj string. */ 588eda14cbcSMatt Macy ddura.ddura_holdfunc = dsl_dataset_hold_obj_string; 589eda14cbcSMatt Macy pool = spa_name(tmpdp->dp_spa); 590eda14cbcSMatt Macy #ifdef _KERNEL 591eda14cbcSMatt Macy for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 592eda14cbcSMatt Macy pair = nvlist_next_nvpair(holds, pair)) { 593eda14cbcSMatt Macy dsl_dataset_t *ds; 594eda14cbcSMatt Macy 595eda14cbcSMatt Macy dsl_pool_config_enter(tmpdp, FTAG); 596eda14cbcSMatt Macy error = dsl_dataset_hold_obj_string(tmpdp, 597eda14cbcSMatt Macy nvpair_name(pair), FTAG, &ds); 598eda14cbcSMatt Macy if (error == 0) { 599eda14cbcSMatt Macy char name[ZFS_MAX_DATASET_NAME_LEN]; 600eda14cbcSMatt Macy dsl_dataset_name(ds, name); 601eda14cbcSMatt Macy dsl_pool_config_exit(tmpdp, FTAG); 602eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 603eda14cbcSMatt Macy (void) zfs_unmount_snap(name); 604eda14cbcSMatt Macy } else { 605eda14cbcSMatt Macy dsl_pool_config_exit(tmpdp, FTAG); 606eda14cbcSMatt Macy } 607eda14cbcSMatt Macy } 608eda14cbcSMatt Macy #endif 609eda14cbcSMatt Macy } else { 610eda14cbcSMatt Macy /* Non-temporary holds are specified by name. */ 611eda14cbcSMatt Macy ddura.ddura_holdfunc = dsl_dataset_hold; 612eda14cbcSMatt Macy pool = nvpair_name(pair); 613eda14cbcSMatt Macy #ifdef _KERNEL 614eda14cbcSMatt Macy for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 615eda14cbcSMatt Macy pair = nvlist_next_nvpair(holds, pair)) { 616eda14cbcSMatt Macy (void) zfs_unmount_snap(nvpair_name(pair)); 617eda14cbcSMatt Macy } 618eda14cbcSMatt Macy #endif 619eda14cbcSMatt Macy } 620eda14cbcSMatt Macy 621eda14cbcSMatt Macy ddura.ddura_holds = holds; 622eda14cbcSMatt Macy ddura.ddura_errlist = errlist; 623eda14cbcSMatt Macy VERIFY0(nvlist_alloc(&ddura.ddura_todelete, NV_UNIQUE_NAME, 624eda14cbcSMatt Macy KM_SLEEP)); 625eda14cbcSMatt Macy VERIFY0(nvlist_alloc(&ddura.ddura_chkholds, NV_UNIQUE_NAME, 626eda14cbcSMatt Macy KM_SLEEP)); 627eda14cbcSMatt Macy 628eda14cbcSMatt Macy error = dsl_sync_task(pool, dsl_dataset_user_release_check, 629eda14cbcSMatt Macy dsl_dataset_user_release_sync, &ddura, 0, 630eda14cbcSMatt Macy ZFS_SPACE_CHECK_EXTRA_RESERVED); 631eda14cbcSMatt Macy fnvlist_free(ddura.ddura_todelete); 632eda14cbcSMatt Macy fnvlist_free(ddura.ddura_chkholds); 633eda14cbcSMatt Macy 634eda14cbcSMatt Macy return (error); 635eda14cbcSMatt Macy } 636eda14cbcSMatt Macy 637eda14cbcSMatt Macy /* 638eda14cbcSMatt Macy * holds is nvl of snapname -> { holdname, ... } 639eda14cbcSMatt Macy * errlist will be filled in with snapname -> error 640eda14cbcSMatt Macy */ 641eda14cbcSMatt Macy int 642eda14cbcSMatt Macy dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist) 643eda14cbcSMatt Macy { 644eda14cbcSMatt Macy return (dsl_dataset_user_release_impl(holds, errlist, NULL)); 645eda14cbcSMatt Macy } 646eda14cbcSMatt Macy 647eda14cbcSMatt Macy /* 648eda14cbcSMatt Macy * holds is nvl of snapdsobj -> { holdname, ... } 649eda14cbcSMatt Macy */ 650eda14cbcSMatt Macy void 651eda14cbcSMatt Macy dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds) 652eda14cbcSMatt Macy { 653eda14cbcSMatt Macy ASSERT(dp != NULL); 654eda14cbcSMatt Macy (void) dsl_dataset_user_release_impl(holds, NULL, dp); 655eda14cbcSMatt Macy } 656eda14cbcSMatt Macy 657eda14cbcSMatt Macy int 658eda14cbcSMatt Macy dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) 659eda14cbcSMatt Macy { 660eda14cbcSMatt Macy dsl_pool_t *dp; 661eda14cbcSMatt Macy dsl_dataset_t *ds; 662eda14cbcSMatt Macy int err; 663eda14cbcSMatt Macy 664eda14cbcSMatt Macy err = dsl_pool_hold(dsname, FTAG, &dp); 665eda14cbcSMatt Macy if (err != 0) 666eda14cbcSMatt Macy return (err); 667eda14cbcSMatt Macy err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 668eda14cbcSMatt Macy if (err != 0) { 669eda14cbcSMatt Macy dsl_pool_rele(dp, FTAG); 670eda14cbcSMatt Macy return (err); 671eda14cbcSMatt Macy } 672eda14cbcSMatt Macy 673eda14cbcSMatt Macy if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { 674eda14cbcSMatt Macy zap_attribute_t *za; 675eda14cbcSMatt Macy zap_cursor_t zc; 676eda14cbcSMatt Macy 677*7a7741afSMartin Matuska za = zap_attribute_alloc(); 678eda14cbcSMatt Macy for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, 679eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_userrefs_obj); 680eda14cbcSMatt Macy zap_cursor_retrieve(&zc, za) == 0; 681eda14cbcSMatt Macy zap_cursor_advance(&zc)) { 682eda14cbcSMatt Macy fnvlist_add_uint64(nvl, za->za_name, 683eda14cbcSMatt Macy za->za_first_integer); 684eda14cbcSMatt Macy } 685eda14cbcSMatt Macy zap_cursor_fini(&zc); 686*7a7741afSMartin Matuska zap_attribute_free(za); 687eda14cbcSMatt Macy } 688eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 689eda14cbcSMatt Macy dsl_pool_rele(dp, FTAG); 690eda14cbcSMatt Macy return (0); 691eda14cbcSMatt Macy } 692