]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / cddl / contrib / opensolaris / uts / common / fs / 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  * 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 (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 (!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 = 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 }