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