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