1 /* 2 * CDDL HEADER START 3 * 4 * This file and its contents are supplied under the terms of the 5 * Common Development and Distribution License ("CDDL"), version 1.0. 6 * You may only use this file in accordance with the terms of version 7 * 1.0 of the CDDL. 8 * 9 * A full copy of the text of the CDDL should have accompanied this 10 * source. A copy of the CDDL is also available via the Internet at 11 * http://www.illumos.org/license/CDDL. 12 * 13 * CDDL HEADER END 14 */ 15 16 /* 17 * Copyright (c) 2013, 2014 by Delphix. All rights reserved. 18 * Copyright 2017 Nexenta Systems, Inc. 19 */ 20 21 #include <sys/zfs_context.h> 22 #include <sys/dsl_dataset.h> 23 #include <sys/dsl_dir.h> 24 #include <sys/dsl_prop.h> 25 #include <sys/dsl_synctask.h> 26 #include <sys/dmu_impl.h> 27 #include <sys/dmu_tx.h> 28 #include <sys/arc.h> 29 #include <sys/zap.h> 30 #include <sys/zfeature.h> 31 #include <sys/spa.h> 32 #include <sys/dsl_bookmark.h> 33 #include <zfs_namecheck.h> 34 35 static int 36 dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname, 37 dsl_dataset_t **dsp, void *tag, char **shortnamep) 38 { 39 char buf[ZFS_MAX_DATASET_NAME_LEN]; 40 char *hashp; 41 42 if (strlen(fullname) >= ZFS_MAX_DATASET_NAME_LEN) 43 return (SET_ERROR(ENAMETOOLONG)); 44 hashp = strchr(fullname, '#'); 45 if (hashp == NULL) 46 return (SET_ERROR(EINVAL)); 47 48 *shortnamep = hashp + 1; 49 if (zfs_component_namecheck(*shortnamep, NULL, NULL)) 50 return (SET_ERROR(EINVAL)); 51 (void) strlcpy(buf, fullname, hashp - fullname + 1); 52 return (dsl_dataset_hold(dp, buf, tag, dsp)); 53 } 54 55 /* 56 * Returns ESRCH if bookmark is not found. 57 */ 58 static int 59 dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname, 60 zfs_bookmark_phys_t *bmark_phys) 61 { 62 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 63 uint64_t bmark_zapobj = ds->ds_bookmarks; 64 matchtype_t mt = 0; 65 int err; 66 67 if (bmark_zapobj == 0) 68 return (SET_ERROR(ESRCH)); 69 70 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 71 mt = MT_NORMALIZE; 72 73 /* 74 * Zero out the bookmark in case the one stored on disk 75 * is in an older, shorter format. 76 */ 77 bzero(bmark_phys, sizeof (*bmark_phys)); 78 79 err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t), 80 sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt, 81 NULL, 0, NULL); 82 83 return (err == ENOENT ? ESRCH : err); 84 } 85 86 /* 87 * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark 88 * does not represents an earlier point in later_ds's timeline. 89 * 90 * Returns ENOENT if the dataset containing the bookmark does not exist. 91 * Returns ESRCH if the dataset exists but the bookmark was not found in it. 92 */ 93 int 94 dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname, 95 dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp) 96 { 97 char *shortname; 98 dsl_dataset_t *ds; 99 int error; 100 101 error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname); 102 if (error != 0) 103 return (error); 104 105 error = dsl_dataset_bmark_lookup(ds, shortname, bmp); 106 if (error == 0 && later_ds != NULL) { 107 if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg)) 108 error = SET_ERROR(EXDEV); 109 } 110 dsl_dataset_rele(ds, FTAG); 111 return (error); 112 } 113 114 typedef struct dsl_bookmark_create_arg { 115 nvlist_t *dbca_bmarks; 116 nvlist_t *dbca_errors; 117 } dsl_bookmark_create_arg_t; 118 119 static int 120 dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name, 121 dmu_tx_t *tx) 122 { 123 dsl_pool_t *dp = dmu_tx_pool(tx); 124 dsl_dataset_t *bmark_fs; 125 char *shortname; 126 int error; 127 zfs_bookmark_phys_t bmark_phys; 128 129 if (!snapds->ds_is_snapshot) 130 return (SET_ERROR(EINVAL)); 131 132 error = dsl_bookmark_hold_ds(dp, bookmark_name, 133 &bmark_fs, FTAG, &shortname); 134 if (error != 0) 135 return (error); 136 137 if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) { 138 dsl_dataset_rele(bmark_fs, FTAG); 139 return (SET_ERROR(EINVAL)); 140 } 141 142 error = dsl_dataset_bmark_lookup(bmark_fs, shortname, 143 &bmark_phys); 144 dsl_dataset_rele(bmark_fs, FTAG); 145 if (error == 0) 146 return (SET_ERROR(EEXIST)); 147 if (error == ESRCH) 148 return (0); 149 return (error); 150 } 151 152 static int 153 dsl_bookmark_create_check(void *arg, dmu_tx_t *tx) 154 { 155 dsl_bookmark_create_arg_t *dbca = arg; 156 dsl_pool_t *dp = dmu_tx_pool(tx); 157 int rv = 0; 158 159 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 160 return (SET_ERROR(ENOTSUP)); 161 162 for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); 163 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { 164 dsl_dataset_t *snapds; 165 int error; 166 167 /* note: validity of nvlist checked by ioctl layer */ 168 error = dsl_dataset_hold(dp, fnvpair_value_string(pair), 169 FTAG, &snapds); 170 if (error == 0) { 171 error = dsl_bookmark_create_check_impl(snapds, 172 nvpair_name(pair), tx); 173 dsl_dataset_rele(snapds, FTAG); 174 } 175 if (error != 0) { 176 fnvlist_add_int32(dbca->dbca_errors, 177 nvpair_name(pair), error); 178 rv = error; 179 } 180 } 181 182 return (rv); 183 } 184 185 static void 186 dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx) 187 { 188 dsl_bookmark_create_arg_t *dbca = arg; 189 dsl_pool_t *dp = dmu_tx_pool(tx); 190 objset_t *mos = dp->dp_meta_objset; 191 192 ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)); 193 194 for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); 195 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { 196 dsl_dataset_t *snapds, *bmark_fs; 197 zfs_bookmark_phys_t bmark_phys = { 0 }; 198 char *shortname; 199 uint32_t bmark_len = BOOKMARK_PHYS_SIZE_V1; 200 201 VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair), 202 FTAG, &snapds)); 203 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 204 &bmark_fs, FTAG, &shortname)); 205 if (bmark_fs->ds_bookmarks == 0) { 206 bmark_fs->ds_bookmarks = 207 zap_create_norm(mos, U8_TEXTPREP_TOUPPER, 208 DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx); 209 spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 210 211 dsl_dataset_zapify(bmark_fs, tx); 212 VERIFY0(zap_add(mos, bmark_fs->ds_object, 213 DS_FIELD_BOOKMARK_NAMES, 214 sizeof (bmark_fs->ds_bookmarks), 1, 215 &bmark_fs->ds_bookmarks, tx)); 216 } 217 218 bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid; 219 bmark_phys.zbm_creation_txg = 220 dsl_dataset_phys(snapds)->ds_creation_txg; 221 bmark_phys.zbm_creation_time = 222 dsl_dataset_phys(snapds)->ds_creation_time; 223 224 /* 225 * If the dataset is encrypted create a larger bookmark to 226 * accommodate the IVset guid. The IVset guid was added 227 * after the encryption feature to prevent a problem with 228 * raw sends. If we encounter an encrypted dataset without 229 * an IVset guid we fall back to a normal bookmark. 230 */ 231 if (snapds->ds_dir->dd_crypto_obj != 0 && 232 spa_feature_is_enabled(dp->dp_spa, 233 SPA_FEATURE_BOOKMARK_V2)) { 234 int err = zap_lookup(mos, snapds->ds_object, 235 DS_FIELD_IVSET_GUID, sizeof (uint64_t), 1, 236 &bmark_phys.zbm_ivset_guid); 237 if (err == 0) { 238 bmark_len = BOOKMARK_PHYS_SIZE_V2; 239 spa_feature_incr(dp->dp_spa, 240 SPA_FEATURE_BOOKMARK_V2, tx); 241 } 242 } 243 244 VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks, 245 shortname, sizeof (uint64_t), 246 bmark_len / sizeof (uint64_t), &bmark_phys, tx)); 247 248 spa_history_log_internal_ds(bmark_fs, "bookmark", tx, 249 "name=%s creation_txg=%llu target_snap=%llu", 250 shortname, 251 (longlong_t)bmark_phys.zbm_creation_txg, 252 (longlong_t)snapds->ds_object); 253 254 dsl_dataset_rele(bmark_fs, FTAG); 255 dsl_dataset_rele(snapds, FTAG); 256 } 257 } 258 259 /* 260 * The bookmarks must all be in the same pool. 261 */ 262 int 263 dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors) 264 { 265 nvpair_t *pair; 266 dsl_bookmark_create_arg_t dbca; 267 268 pair = nvlist_next_nvpair(bmarks, NULL); 269 if (pair == NULL) 270 return (0); 271 272 dbca.dbca_bmarks = bmarks; 273 dbca.dbca_errors = errors; 274 275 return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check, 276 dsl_bookmark_create_sync, &dbca, 277 fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL)); 278 } 279 280 int 281 dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl) 282 { 283 int err = 0; 284 zap_cursor_t zc; 285 zap_attribute_t attr; 286 dsl_pool_t *dp = ds->ds_dir->dd_pool; 287 288 uint64_t bmark_zapobj = ds->ds_bookmarks; 289 if (bmark_zapobj == 0) 290 return (0); 291 292 for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj); 293 zap_cursor_retrieve(&zc, &attr) == 0; 294 zap_cursor_advance(&zc)) { 295 char *bmark_name = attr.za_name; 296 zfs_bookmark_phys_t bmark_phys = { 0 }; 297 298 err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys); 299 ASSERT3U(err, !=, ENOENT); 300 if (err != 0) 301 break; 302 303 nvlist_t *out_props = fnvlist_alloc(); 304 if (nvlist_exists(props, 305 zfs_prop_to_name(ZFS_PROP_GUID))) { 306 dsl_prop_nvlist_add_uint64(out_props, 307 ZFS_PROP_GUID, bmark_phys.zbm_guid); 308 } 309 if (nvlist_exists(props, 310 zfs_prop_to_name(ZFS_PROP_CREATETXG))) { 311 dsl_prop_nvlist_add_uint64(out_props, 312 ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg); 313 } 314 if (nvlist_exists(props, 315 zfs_prop_to_name(ZFS_PROP_CREATION))) { 316 dsl_prop_nvlist_add_uint64(out_props, 317 ZFS_PROP_CREATION, bmark_phys.zbm_creation_time); 318 } 319 if (nvlist_exists(props, 320 zfs_prop_to_name(ZFS_PROP_IVSET_GUID))) { 321 dsl_prop_nvlist_add_uint64(out_props, 322 ZFS_PROP_IVSET_GUID, bmark_phys.zbm_ivset_guid); 323 } 324 325 fnvlist_add_nvlist(outnvl, bmark_name, out_props); 326 fnvlist_free(out_props); 327 } 328 zap_cursor_fini(&zc); 329 return (err); 330 } 331 332 /* 333 * Retrieve the bookmarks that exist in the specified dataset, and the 334 * requested properties of each bookmark. 335 * 336 * The "props" nvlist specifies which properties are requested. 337 * See lzc_get_bookmarks() for the list of valid properties. 338 */ 339 int 340 dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl) 341 { 342 dsl_pool_t *dp; 343 dsl_dataset_t *ds; 344 int err; 345 346 err = dsl_pool_hold(dsname, FTAG, &dp); 347 if (err != 0) 348 return (err); 349 err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 350 if (err != 0) { 351 dsl_pool_rele(dp, FTAG); 352 return (err); 353 } 354 355 err = dsl_get_bookmarks_impl(ds, props, outnvl); 356 357 dsl_dataset_rele(ds, FTAG); 358 dsl_pool_rele(dp, FTAG); 359 return (err); 360 } 361 362 typedef struct dsl_bookmark_destroy_arg { 363 nvlist_t *dbda_bmarks; 364 nvlist_t *dbda_success; 365 nvlist_t *dbda_errors; 366 } dsl_bookmark_destroy_arg_t; 367 368 static int 369 dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx) 370 { 371 int err; 372 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 373 uint64_t bmark_zapobj = ds->ds_bookmarks; 374 matchtype_t mt = 0; 375 uint64_t int_size, num_ints; 376 377 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 378 mt = MT_NORMALIZE; 379 380 err = zap_length(mos, bmark_zapobj, name, &int_size, &num_ints); 381 if (err != 0) 382 return (err); 383 384 ASSERT3U(int_size, ==, sizeof (uint64_t)); 385 386 if (num_ints * int_size > BOOKMARK_PHYS_SIZE_V1) { 387 spa_feature_decr(dmu_objset_spa(mos), 388 SPA_FEATURE_BOOKMARK_V2, tx); 389 } 390 391 return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); 392 } 393 394 static int 395 dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx) 396 { 397 dsl_bookmark_destroy_arg_t *dbda = arg; 398 dsl_pool_t *dp = dmu_tx_pool(tx); 399 int rv = 0; 400 401 ASSERT(nvlist_empty(dbda->dbda_success)); 402 ASSERT(nvlist_empty(dbda->dbda_errors)); 403 404 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 405 return (0); 406 407 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL); 408 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) { 409 const char *fullname = nvpair_name(pair); 410 dsl_dataset_t *ds; 411 zfs_bookmark_phys_t bm; 412 int error; 413 char *shortname; 414 415 error = dsl_bookmark_hold_ds(dp, fullname, &ds, 416 FTAG, &shortname); 417 if (error == ENOENT) { 418 /* ignore it; the bookmark is "already destroyed" */ 419 continue; 420 } 421 if (error == 0) { 422 error = dsl_dataset_bmark_lookup(ds, shortname, &bm); 423 dsl_dataset_rele(ds, FTAG); 424 if (error == ESRCH) { 425 /* 426 * ignore it; the bookmark is 427 * "already destroyed" 428 */ 429 continue; 430 } 431 } 432 if (error == 0) { 433 if (dmu_tx_is_syncing(tx)) { 434 fnvlist_add_boolean(dbda->dbda_success, 435 fullname); 436 } 437 } else { 438 fnvlist_add_int32(dbda->dbda_errors, fullname, error); 439 rv = error; 440 } 441 } 442 return (rv); 443 } 444 445 static void 446 dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx) 447 { 448 dsl_bookmark_destroy_arg_t *dbda = arg; 449 dsl_pool_t *dp = dmu_tx_pool(tx); 450 objset_t *mos = dp->dp_meta_objset; 451 452 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL); 453 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) { 454 dsl_dataset_t *ds; 455 char *shortname; 456 uint64_t zap_cnt; 457 458 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 459 &ds, FTAG, &shortname)); 460 VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx)); 461 462 /* 463 * If all of this dataset's bookmarks have been destroyed, 464 * free the zap object and decrement the feature's use count. 465 */ 466 VERIFY0(zap_count(mos, ds->ds_bookmarks, 467 &zap_cnt)); 468 if (zap_cnt == 0) { 469 dmu_buf_will_dirty(ds->ds_dbuf, tx); 470 VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); 471 ds->ds_bookmarks = 0; 472 spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 473 VERIFY0(zap_remove(mos, ds->ds_object, 474 DS_FIELD_BOOKMARK_NAMES, tx)); 475 } 476 477 spa_history_log_internal_ds(ds, "remove bookmark", tx, 478 "name=%s", shortname); 479 480 dsl_dataset_rele(ds, FTAG); 481 } 482 } 483 484 /* 485 * The bookmarks must all be in the same pool. 486 */ 487 int 488 dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors) 489 { 490 int rv; 491 dsl_bookmark_destroy_arg_t dbda; 492 nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); 493 if (pair == NULL) 494 return (0); 495 496 dbda.dbda_bmarks = bmarks; 497 dbda.dbda_errors = errors; 498 dbda.dbda_success = fnvlist_alloc(); 499 500 rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check, 501 dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks), 502 ZFS_SPACE_CHECK_RESERVED); 503 fnvlist_free(dbda.dbda_success); 504 return (rv); 505 } 506