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