]> git.proxmox.com Git - mirror_zfs-debian.git/blob - module/zfs/dsl_bookmark.c
Merge tag 'upstream/0.7.5'
[mirror_zfs-debian.git] / module / zfs / dsl_bookmark.c
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 nvpair_t *pair;
153
154 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
155 return (SET_ERROR(ENOTSUP));
156
157 for (pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
158 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
159 dsl_dataset_t *snapds;
160 int error;
161
162 /* note: validity of nvlist checked by ioctl layer */
163 error = dsl_dataset_hold(dp, fnvpair_value_string(pair),
164 FTAG, &snapds);
165 if (error == 0) {
166 error = dsl_bookmark_create_check_impl(snapds,
167 nvpair_name(pair), tx);
168 dsl_dataset_rele(snapds, FTAG);
169 }
170 if (error != 0) {
171 fnvlist_add_int32(dbca->dbca_errors,
172 nvpair_name(pair), error);
173 rv = error;
174 }
175 }
176
177 return (rv);
178 }
179
180 static void
181 dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
182 {
183 dsl_bookmark_create_arg_t *dbca = arg;
184 dsl_pool_t *dp = dmu_tx_pool(tx);
185 objset_t *mos = dp->dp_meta_objset;
186 nvpair_t *pair;
187
188 ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
189
190 for (pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
191 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
192 dsl_dataset_t *snapds, *bmark_fs;
193 zfs_bookmark_phys_t bmark_phys;
194 char *shortname;
195
196 VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair),
197 FTAG, &snapds));
198 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
199 &bmark_fs, FTAG, &shortname));
200 if (bmark_fs->ds_bookmarks == 0) {
201 bmark_fs->ds_bookmarks =
202 zap_create_norm(mos, U8_TEXTPREP_TOUPPER,
203 DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
204 spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
205
206 dsl_dataset_zapify(bmark_fs, tx);
207 VERIFY0(zap_add(mos, bmark_fs->ds_object,
208 DS_FIELD_BOOKMARK_NAMES,
209 sizeof (bmark_fs->ds_bookmarks), 1,
210 &bmark_fs->ds_bookmarks, tx));
211 }
212
213 bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid;
214 bmark_phys.zbm_creation_txg =
215 dsl_dataset_phys(snapds)->ds_creation_txg;
216 bmark_phys.zbm_creation_time =
217 dsl_dataset_phys(snapds)->ds_creation_time;
218
219 VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
220 shortname, sizeof (uint64_t),
221 sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t),
222 &bmark_phys, tx));
223
224 spa_history_log_internal_ds(bmark_fs, "bookmark", tx,
225 "name=%s creation_txg=%llu target_snap=%llu",
226 shortname,
227 (longlong_t)bmark_phys.zbm_creation_txg,
228 (longlong_t)snapds->ds_object);
229
230 dsl_dataset_rele(bmark_fs, FTAG);
231 dsl_dataset_rele(snapds, FTAG);
232 }
233 }
234
235 /*
236 * The bookmarks must all be in the same pool.
237 */
238 int
239 dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors)
240 {
241 nvpair_t *pair;
242 dsl_bookmark_create_arg_t dbca;
243
244 pair = nvlist_next_nvpair(bmarks, NULL);
245 if (pair == NULL)
246 return (0);
247
248 dbca.dbca_bmarks = bmarks;
249 dbca.dbca_errors = errors;
250
251 return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check,
252 dsl_bookmark_create_sync, &dbca,
253 fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL));
254 }
255
256 int
257 dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
258 {
259 int err = 0;
260 zap_cursor_t zc;
261 zap_attribute_t attr;
262 dsl_pool_t *dp = ds->ds_dir->dd_pool;
263
264 uint64_t bmark_zapobj = ds->ds_bookmarks;
265 if (bmark_zapobj == 0)
266 return (0);
267
268 for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj);
269 zap_cursor_retrieve(&zc, &attr) == 0;
270 zap_cursor_advance(&zc)) {
271 nvlist_t *out_props;
272 char *bmark_name = attr.za_name;
273 zfs_bookmark_phys_t bmark_phys;
274
275 err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
276 ASSERT3U(err, !=, ENOENT);
277 if (err != 0)
278 break;
279
280 out_props = fnvlist_alloc();
281 if (nvlist_exists(props,
282 zfs_prop_to_name(ZFS_PROP_GUID))) {
283 dsl_prop_nvlist_add_uint64(out_props,
284 ZFS_PROP_GUID, bmark_phys.zbm_guid);
285 }
286 if (nvlist_exists(props,
287 zfs_prop_to_name(ZFS_PROP_CREATETXG))) {
288 dsl_prop_nvlist_add_uint64(out_props,
289 ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg);
290 }
291 if (nvlist_exists(props,
292 zfs_prop_to_name(ZFS_PROP_CREATION))) {
293 dsl_prop_nvlist_add_uint64(out_props,
294 ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
295 }
296
297 fnvlist_add_nvlist(outnvl, bmark_name, out_props);
298 fnvlist_free(out_props);
299 }
300 zap_cursor_fini(&zc);
301 return (err);
302 }
303
304 /*
305 * Retrieve the bookmarks that exist in the specified dataset, and the
306 * requested properties of each bookmark.
307 *
308 * The "props" nvlist specifies which properties are requested.
309 * See lzc_get_bookmarks() for the list of valid properties.
310 */
311 int
312 dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl)
313 {
314 dsl_pool_t *dp;
315 dsl_dataset_t *ds;
316 int err;
317
318 err = dsl_pool_hold(dsname, FTAG, &dp);
319 if (err != 0)
320 return (err);
321 err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
322 if (err != 0) {
323 dsl_pool_rele(dp, FTAG);
324 return (err);
325 }
326
327 err = dsl_get_bookmarks_impl(ds, props, outnvl);
328
329 dsl_dataset_rele(ds, FTAG);
330 dsl_pool_rele(dp, FTAG);
331 return (err);
332 }
333
334 typedef struct dsl_bookmark_destroy_arg {
335 nvlist_t *dbda_bmarks;
336 nvlist_t *dbda_success;
337 nvlist_t *dbda_errors;
338 } dsl_bookmark_destroy_arg_t;
339
340 static int
341 dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
342 {
343 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
344 uint64_t bmark_zapobj = ds->ds_bookmarks;
345 matchtype_t mt = 0;
346
347 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
348 mt = MT_NORMALIZE;
349
350 return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
351 }
352
353 static int
354 dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
355 {
356 dsl_bookmark_destroy_arg_t *dbda = arg;
357 dsl_pool_t *dp = dmu_tx_pool(tx);
358 int rv = 0;
359 nvpair_t *pair;
360
361 ASSERT(nvlist_empty(dbda->dbda_success));
362 ASSERT(nvlist_empty(dbda->dbda_errors));
363
364 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
365 return (0);
366
367 for (pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
368 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
369 const char *fullname = nvpair_name(pair);
370 dsl_dataset_t *ds;
371 zfs_bookmark_phys_t bm;
372 int error;
373 char *shortname;
374
375 error = dsl_bookmark_hold_ds(dp, fullname, &ds,
376 FTAG, &shortname);
377 if (error == ENOENT) {
378 /* ignore it; the bookmark is "already destroyed" */
379 continue;
380 }
381 if (error == 0) {
382 error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
383 dsl_dataset_rele(ds, FTAG);
384 if (error == ESRCH) {
385 /*
386 * ignore it; the bookmark is
387 * "already destroyed"
388 */
389 continue;
390 }
391 }
392 if (error == 0) {
393 if (dmu_tx_is_syncing(tx)) {
394 fnvlist_add_boolean(dbda->dbda_success,
395 fullname);
396 }
397 } else {
398 fnvlist_add_int32(dbda->dbda_errors, fullname, error);
399 rv = error;
400 }
401 }
402 return (rv);
403 }
404
405 static void
406 dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
407 {
408 dsl_bookmark_destroy_arg_t *dbda = arg;
409 dsl_pool_t *dp = dmu_tx_pool(tx);
410 objset_t *mos = dp->dp_meta_objset;
411 nvpair_t *pair;
412
413 for (pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
414 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
415 dsl_dataset_t *ds;
416 char *shortname;
417 uint64_t zap_cnt;
418
419 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
420 &ds, FTAG, &shortname));
421 VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
422
423 /*
424 * If all of this dataset's bookmarks have been destroyed,
425 * free the zap object and decrement the feature's use count.
426 */
427 VERIFY0(zap_count(mos, ds->ds_bookmarks,
428 &zap_cnt));
429 if (zap_cnt == 0) {
430 dmu_buf_will_dirty(ds->ds_dbuf, tx);
431 VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
432 ds->ds_bookmarks = 0;
433 spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
434 VERIFY0(zap_remove(mos, ds->ds_object,
435 DS_FIELD_BOOKMARK_NAMES, tx));
436 }
437
438 spa_history_log_internal_ds(ds, "remove bookmark", tx,
439 "name=%s", shortname);
440
441 dsl_dataset_rele(ds, FTAG);
442 }
443 }
444
445 /*
446 * The bookmarks must all be in the same pool.
447 */
448 int
449 dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
450 {
451 int rv;
452 dsl_bookmark_destroy_arg_t dbda;
453 nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
454 if (pair == NULL)
455 return (0);
456
457 dbda.dbda_bmarks = bmarks;
458 dbda.dbda_errors = errors;
459 dbda.dbda_success = fnvlist_alloc();
460
461 rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
462 dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks),
463 ZFS_SPACE_CHECK_RESERVED);
464 fnvlist_free(dbda.dbda_success);
465 return (rv);
466 }