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