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[MAXNAMELEN]; 38 char *hashp; 39 40 if (strlen(fullname) >= MAXNAMELEN) 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 (ds->ds_phys->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 (!dsl_dataset_is_snapshot(snapds)) 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 = snapds->ds_phys->ds_guid; 212 bmark_phys.zbm_creation_txg = snapds->ds_phys->ds_creation_txg; 213 bmark_phys.zbm_creation_time = 214 snapds->ds_phys->ds_creation_time; 215 216 VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks, 217 shortname, sizeof (uint64_t), 218 sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t), 219 &bmark_phys, tx)); 220 221 spa_history_log_internal_ds(bmark_fs, "bookmark", tx, 222 "name=%s creation_txg=%llu target_snap=%llu", 223 shortname, 224 (longlong_t)bmark_phys.zbm_creation_txg, 225 (longlong_t)snapds->ds_object); 226 227 dsl_dataset_rele(bmark_fs, FTAG); 228 dsl_dataset_rele(snapds, FTAG); 229 } 230 } 231 232 /* 233 * The bookmarks must all be in the same pool. 234 */ 235 int 236 dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors) 237 { 238 nvpair_t *pair; 239 dsl_bookmark_create_arg_t dbca; 240 241 pair = nvlist_next_nvpair(bmarks, NULL); 242 if (pair == NULL) 243 return (0); 244 245 dbca.dbca_bmarks = bmarks; 246 dbca.dbca_errors = errors; 247 248 return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check, 249 dsl_bookmark_create_sync, &dbca, 250 fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL)); 251 } 252 253 int 254 dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl) 255 { 256 int err = 0; 257 zap_cursor_t zc; 258 zap_attribute_t attr; 259 dsl_pool_t *dp = ds->ds_dir->dd_pool; 260 261 uint64_t bmark_zapobj = ds->ds_bookmarks; 262 if (bmark_zapobj == 0) 263 return (0); 264 265 for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj); 266 zap_cursor_retrieve(&zc, &attr) == 0; 267 zap_cursor_advance(&zc)) { 268 char *bmark_name = attr.za_name; 269 zfs_bookmark_phys_t bmark_phys; 270 271 err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys); 272 ASSERT3U(err, !=, ENOENT); 273 if (err != 0) 274 break; 275 276 nvlist_t *out_props = fnvlist_alloc(); 277 if (nvlist_exists(props, 278 zfs_prop_to_name(ZFS_PROP_GUID))) { 279 dsl_prop_nvlist_add_uint64(out_props, 280 ZFS_PROP_GUID, bmark_phys.zbm_guid); 281 } 282 if (nvlist_exists(props, 283 zfs_prop_to_name(ZFS_PROP_CREATETXG))) { 284 dsl_prop_nvlist_add_uint64(out_props, 285 ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg); 286 } 287 if (nvlist_exists(props, 288 zfs_prop_to_name(ZFS_PROP_CREATION))) { 289 dsl_prop_nvlist_add_uint64(out_props, 290 ZFS_PROP_CREATION, bmark_phys.zbm_creation_time); 291 } 292 293 fnvlist_add_nvlist(outnvl, bmark_name, out_props); 294 fnvlist_free(out_props); 295 } 296 zap_cursor_fini(&zc); 297 return (err); 298 } 299 300 /* 301 * Retrieve the bookmarks that exist in the specified dataset, and the 302 * requested properties of each bookmark. 303 * 304 * The "props" nvlist specifies which properties are requested. 305 * See lzc_get_bookmarks() for the list of valid properties. 306 */ 307 int 308 dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl) 309 { 310 dsl_pool_t *dp; 311 dsl_dataset_t *ds; 312 int err; 313 314 err = dsl_pool_hold(dsname, FTAG, &dp); 315 if (err != 0) 316 return (err); 317 err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 318 if (err != 0) { 319 dsl_pool_rele(dp, FTAG); 320 return (err); 321 } 322 323 err = dsl_get_bookmarks_impl(ds, props, outnvl); 324 325 dsl_dataset_rele(ds, FTAG); 326 dsl_pool_rele(dp, FTAG); 327 return (err); 328 } 329 330 typedef struct dsl_bookmark_destroy_arg { 331 nvlist_t *dbda_bmarks; 332 nvlist_t *dbda_success; 333 nvlist_t *dbda_errors; 334 } dsl_bookmark_destroy_arg_t; 335 336 static int 337 dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx) 338 { 339 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 340 uint64_t bmark_zapobj = ds->ds_bookmarks; 341 matchtype_t mt; 342 343 if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET) 344 mt = MT_FIRST; 345 else 346 mt = MT_EXACT; 347 348 return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); 349 } 350 351 static int 352 dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx) 353 { 354 dsl_bookmark_destroy_arg_t *dbda = arg; 355 dsl_pool_t *dp = dmu_tx_pool(tx); 356 int rv = 0; 357 358 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 359 return (0); 360 361 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL); 362 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) { 363 const char *fullname = nvpair_name(pair); 364 dsl_dataset_t *ds; 365 zfs_bookmark_phys_t bm; 366 int error; 367 char *shortname; 368 369 error = dsl_bookmark_hold_ds(dp, fullname, &ds, 370 FTAG, &shortname); 371 if (error == ENOENT) { 372 /* ignore it; the bookmark is "already destroyed" */ 373 continue; 374 } 375 if (error == 0) { 376 error = dsl_dataset_bmark_lookup(ds, shortname, &bm); 377 dsl_dataset_rele(ds, FTAG); 378 if (error == ESRCH) { 379 /* 380 * ignore it; the bookmark is 381 * "already destroyed" 382 */ 383 continue; 384 } 385 } 386 if (error == 0) { 387 fnvlist_add_boolean(dbda->dbda_success, fullname); 388 } else { 389 fnvlist_add_int32(dbda->dbda_errors, fullname, error); 390 rv = error; 391 } 392 } 393 return (rv); 394 } 395 396 static void 397 dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx) 398 { 399 dsl_bookmark_destroy_arg_t *dbda = arg; 400 dsl_pool_t *dp = dmu_tx_pool(tx); 401 objset_t *mos = dp->dp_meta_objset; 402 403 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL); 404 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) { 405 dsl_dataset_t *ds; 406 char *shortname; 407 uint64_t zap_cnt; 408 409 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 410 &ds, FTAG, &shortname)); 411 VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx)); 412 413 /* 414 * If all of this dataset's bookmarks have been destroyed, 415 * free the zap object and decrement the feature's use count. 416 */ 417 VERIFY0(zap_count(mos, ds->ds_bookmarks, 418 &zap_cnt)); 419 if (zap_cnt == 0) { 420 dmu_buf_will_dirty(ds->ds_dbuf, tx); 421 VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); 422 ds->ds_bookmarks = 0; 423 spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 424 VERIFY0(zap_remove(mos, ds->ds_object, 425 DS_FIELD_BOOKMARK_NAMES, tx)); 426 } 427 428 spa_history_log_internal_ds(ds, "remove bookmark", tx, 429 "name=%s", shortname); 430 431 dsl_dataset_rele(ds, FTAG); 432 } 433 } 434 435 /* 436 * The bookmarks must all be in the same pool. 437 */ 438 int 439 dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors) 440 { 441 int rv; 442 dsl_bookmark_destroy_arg_t dbda; 443 nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); 444 if (pair == NULL) 445 return (0); 446 447 dbda.dbda_bmarks = bmarks; 448 dbda.dbda_errors = errors; 449 dbda.dbda_success = fnvlist_alloc(); 450 451 rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check, 452 dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks), 453 ZFS_SPACE_CHECK_RESERVED); 454 fnvlist_free(dbda.dbda_success); 455 return (rv); 456 } 457