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 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 = 0; 343 344 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 345 mt = MT_NORMALIZE; 346 347 return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); 348 } 349 350 static int 351 dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx) 352 { 353 dsl_bookmark_destroy_arg_t *dbda = arg; 354 dsl_pool_t *dp = dmu_tx_pool(tx); 355 int rv = 0; 356 357 ASSERT(nvlist_empty(dbda->dbda_success)); 358 ASSERT(nvlist_empty(dbda->dbda_errors)); 359 360 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 361 return (0); 362 363 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL); 364 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) { 365 const char *fullname = nvpair_name(pair); 366 dsl_dataset_t *ds; 367 zfs_bookmark_phys_t bm; 368 int error; 369 char *shortname; 370 371 error = dsl_bookmark_hold_ds(dp, fullname, &ds, 372 FTAG, &shortname); 373 if (error == ENOENT) { 374 /* ignore it; the bookmark is "already destroyed" */ 375 continue; 376 } 377 if (error == 0) { 378 error = dsl_dataset_bmark_lookup(ds, shortname, &bm); 379 dsl_dataset_rele(ds, FTAG); 380 if (error == ESRCH) { 381 /* 382 * ignore it; the bookmark is 383 * "already destroyed" 384 */ 385 continue; 386 } 387 } 388 if (error == 0) { 389 if (dmu_tx_is_syncing(tx)) { 390 fnvlist_add_boolean(dbda->dbda_success, 391 fullname); 392 } 393 } else { 394 fnvlist_add_int32(dbda->dbda_errors, fullname, error); 395 rv = error; 396 } 397 } 398 return (rv); 399 } 400 401 static void 402 dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx) 403 { 404 dsl_bookmark_destroy_arg_t *dbda = arg; 405 dsl_pool_t *dp = dmu_tx_pool(tx); 406 objset_t *mos = dp->dp_meta_objset; 407 408 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL); 409 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) { 410 dsl_dataset_t *ds; 411 char *shortname; 412 uint64_t zap_cnt; 413 414 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 415 &ds, FTAG, &shortname)); 416 VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx)); 417 418 /* 419 * If all of this dataset's bookmarks have been destroyed, 420 * free the zap object and decrement the feature's use count. 421 */ 422 VERIFY0(zap_count(mos, ds->ds_bookmarks, 423 &zap_cnt)); 424 if (zap_cnt == 0) { 425 dmu_buf_will_dirty(ds->ds_dbuf, tx); 426 VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); 427 ds->ds_bookmarks = 0; 428 spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 429 VERIFY0(zap_remove(mos, ds->ds_object, 430 DS_FIELD_BOOKMARK_NAMES, tx)); 431 } 432 433 spa_history_log_internal_ds(ds, "remove bookmark", tx, 434 "name=%s", shortname); 435 436 dsl_dataset_rele(ds, FTAG); 437 } 438 } 439 440 /* 441 * The bookmarks must all be in the same pool. 442 */ 443 int 444 dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors) 445 { 446 int rv; 447 dsl_bookmark_destroy_arg_t dbda; 448 nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); 449 if (pair == NULL) 450 return (0); 451 452 dbda.dbda_bmarks = bmarks; 453 dbda.dbda_errors = errors; 454 dbda.dbda_success = fnvlist_alloc(); 455 456 rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check, 457 dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks), 458 ZFS_SPACE_CHECK_RESERVED); 459 fnvlist_free(dbda.dbda_success); 460 return (rv); 461 } 462