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