]>
Commit | Line | Data |
---|---|---|
13fe0198 MA |
1 | /* |
2 | * CDDL HEADER START | |
3 | * | |
4 | * The contents of this file are subject to the terms of the | |
5 | * Common Development and Distribution License (the "License"). | |
6 | * You may not use this file except in compliance with the License. | |
7 | * | |
8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
9 | * or http://www.opensolaris.org/os/licensing. | |
10 | * See the License for the specific language governing permissions | |
11 | * and limitations under the License. | |
12 | * | |
13 | * When distributing Covered Code, include this CDDL HEADER in each | |
14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
15 | * If applicable, add the following below this CDDL HEADER, with the | |
16 | * fields enclosed by brackets "[]" replaced with your own identifying | |
17 | * information: Portions Copyright [yyyy] [name of copyright owner] | |
18 | * | |
19 | * CDDL HEADER END | |
20 | */ | |
21 | /* | |
22 | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
2e528b49 | 23 | * Copyright (c) 2013 by Delphix. All rights reserved. |
95fd54a1 | 24 | * Copyright (c) 2013 Steven Hartland. All rights reserved. |
13fe0198 MA |
25 | */ |
26 | ||
27 | #include <sys/zfs_context.h> | |
28 | #include <sys/dsl_userhold.h> | |
29 | #include <sys/dsl_dataset.h> | |
30 | #include <sys/dsl_destroy.h> | |
31 | #include <sys/dsl_synctask.h> | |
32 | #include <sys/dmu_tx.h> | |
33 | #include <sys/zfs_onexit.h> | |
34 | #include <sys/dsl_pool.h> | |
35 | #include <sys/dsl_dir.h> | |
36 | #include <sys/zfs_ioctl.h> | |
37 | #include <sys/zap.h> | |
38 | ||
39 | typedef struct dsl_dataset_user_hold_arg { | |
40 | nvlist_t *dduha_holds; | |
95fd54a1 | 41 | nvlist_t *dduha_chkholds; |
13fe0198 MA |
42 | nvlist_t *dduha_errlist; |
43 | minor_t dduha_minor; | |
44 | } dsl_dataset_user_hold_arg_t; | |
45 | ||
46 | /* | |
47 | * If you add new checks here, you may need to add additional checks to the | |
48 | * "temporary" case in snapshot_check() in dmu_objset.c. | |
49 | */ | |
50 | int | |
51 | dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag, | |
52 | boolean_t temphold, dmu_tx_t *tx) | |
53 | { | |
54 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
55 | objset_t *mos = dp->dp_meta_objset; | |
56 | int error = 0; | |
57 | ||
95fd54a1 SH |
58 | ASSERT(dsl_pool_config_held(dp)); |
59 | ||
13fe0198 | 60 | if (strlen(htag) > MAXNAMELEN) |
95fd54a1 | 61 | return (SET_ERROR(E2BIG)); |
13fe0198 MA |
62 | /* Tempholds have a more restricted length */ |
63 | if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN) | |
95fd54a1 | 64 | return (SET_ERROR(E2BIG)); |
13fe0198 MA |
65 | |
66 | /* tags must be unique (if ds already exists) */ | |
95fd54a1 SH |
67 | if (ds != NULL && ds->ds_phys->ds_userrefs_obj != 0) { |
68 | uint64_t value; | |
69 | ||
70 | error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj, | |
71 | htag, 8, 1, &value); | |
72 | if (error == 0) | |
73 | error = SET_ERROR(EEXIST); | |
74 | else if (error == ENOENT) | |
75 | error = 0; | |
13fe0198 MA |
76 | } |
77 | ||
78 | return (error); | |
79 | } | |
80 | ||
81 | static int | |
82 | dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) | |
83 | { | |
84 | dsl_dataset_user_hold_arg_t *dduha = arg; | |
85 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
86 | nvpair_t *pair; | |
13fe0198 MA |
87 | |
88 | if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) | |
2e528b49 | 89 | return (SET_ERROR(ENOTSUP)); |
13fe0198 | 90 | |
95fd54a1 SH |
91 | if (!dmu_tx_is_syncing(tx)) |
92 | return (0); | |
93 | ||
94 | for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); | |
95 | pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { | |
13fe0198 | 96 | dsl_dataset_t *ds; |
95fd54a1 SH |
97 | int error = 0; |
98 | char *htag, *name; | |
13fe0198 MA |
99 | |
100 | /* must be a snapshot */ | |
95fd54a1 SH |
101 | name = nvpair_name(pair); |
102 | if (strchr(name, '@') == NULL) | |
2e528b49 | 103 | error = SET_ERROR(EINVAL); |
13fe0198 MA |
104 | |
105 | if (error == 0) | |
106 | error = nvpair_value_string(pair, &htag); | |
95fd54a1 SH |
107 | |
108 | if (error == 0) | |
109 | error = dsl_dataset_hold(dp, name, FTAG, &ds); | |
110 | ||
13fe0198 MA |
111 | if (error == 0) { |
112 | error = dsl_dataset_user_hold_check_one(ds, htag, | |
113 | dduha->dduha_minor != 0, tx); | |
114 | dsl_dataset_rele(ds, FTAG); | |
115 | } | |
116 | ||
95fd54a1 SH |
117 | if (error == 0) { |
118 | fnvlist_add_string(dduha->dduha_chkholds, name, htag); | |
119 | } else { | |
120 | /* | |
121 | * We register ENOENT errors so they can be correctly | |
122 | * reported if needed, such as when all holds fail. | |
123 | */ | |
124 | fnvlist_add_int32(dduha->dduha_errlist, name, error); | |
125 | if (error != ENOENT) | |
126 | return (error); | |
13fe0198 MA |
127 | } |
128 | } | |
95fd54a1 | 129 | |
95fd54a1 | 130 | return (0); |
13fe0198 MA |
131 | } |
132 | ||
95fd54a1 SH |
133 | |
134 | static void | |
135 | dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds, | |
136 | const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx) | |
13fe0198 MA |
137 | { |
138 | dsl_pool_t *dp = ds->ds_dir->dd_pool; | |
139 | objset_t *mos = dp->dp_meta_objset; | |
140 | uint64_t zapobj; | |
141 | ||
95fd54a1 SH |
142 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); |
143 | ||
13fe0198 MA |
144 | if (ds->ds_phys->ds_userrefs_obj == 0) { |
145 | /* | |
146 | * This is the first user hold for this dataset. Create | |
147 | * the userrefs zap object. | |
148 | */ | |
149 | dmu_buf_will_dirty(ds->ds_dbuf, tx); | |
150 | zapobj = ds->ds_phys->ds_userrefs_obj = | |
151 | zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); | |
152 | } else { | |
153 | zapobj = ds->ds_phys->ds_userrefs_obj; | |
154 | } | |
155 | ds->ds_userrefs++; | |
13fe0198 MA |
156 | |
157 | VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx)); | |
158 | ||
159 | if (minor != 0) { | |
95fd54a1 SH |
160 | char name[MAXNAMELEN]; |
161 | nvlist_t *tags; | |
162 | ||
13fe0198 MA |
163 | VERIFY0(dsl_pool_user_hold(dp, ds->ds_object, |
164 | htag, now, tx)); | |
95fd54a1 SH |
165 | (void) snprintf(name, sizeof (name), "%llx", |
166 | (u_longlong_t)ds->ds_object); | |
167 | ||
168 | if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) { | |
79c76d5b | 169 | tags = fnvlist_alloc(); |
95fd54a1 SH |
170 | fnvlist_add_boolean(tags, htag); |
171 | fnvlist_add_nvlist(tmpholds, name, tags); | |
172 | fnvlist_free(tags); | |
173 | } else { | |
174 | fnvlist_add_boolean(tags, htag); | |
175 | } | |
13fe0198 MA |
176 | } |
177 | ||
178 | spa_history_log_internal_ds(ds, "hold", tx, | |
179 | "tag=%s temp=%d refs=%llu", | |
180 | htag, minor != 0, ds->ds_userrefs); | |
181 | } | |
182 | ||
95fd54a1 SH |
183 | typedef struct zfs_hold_cleanup_arg { |
184 | char zhca_spaname[MAXNAMELEN]; | |
185 | uint64_t zhca_spa_load_guid; | |
186 | nvlist_t *zhca_holds; | |
187 | } zfs_hold_cleanup_arg_t; | |
188 | ||
189 | static void | |
190 | dsl_dataset_user_release_onexit(void *arg) | |
191 | { | |
192 | zfs_hold_cleanup_arg_t *ca = arg; | |
193 | spa_t *spa; | |
194 | int error; | |
195 | ||
196 | error = spa_open(ca->zhca_spaname, &spa, FTAG); | |
197 | if (error != 0) { | |
198 | zfs_dbgmsg("couldn't release holds on pool=%s " | |
199 | "because pool is no longer loaded", | |
200 | ca->zhca_spaname); | |
201 | return; | |
202 | } | |
203 | if (spa_load_guid(spa) != ca->zhca_spa_load_guid) { | |
204 | zfs_dbgmsg("couldn't release holds on pool=%s " | |
205 | "because pool is no longer loaded (guid doesn't match)", | |
206 | ca->zhca_spaname); | |
207 | spa_close(spa, FTAG); | |
208 | return; | |
209 | } | |
210 | ||
211 | (void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds); | |
212 | fnvlist_free(ca->zhca_holds); | |
213 | kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t)); | |
214 | spa_close(spa, FTAG); | |
215 | } | |
216 | ||
217 | static void | |
218 | dsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor) | |
219 | { | |
220 | zfs_hold_cleanup_arg_t *ca; | |
221 | ||
222 | if (minor == 0 || nvlist_empty(holds)) { | |
223 | fnvlist_free(holds); | |
224 | return; | |
225 | } | |
226 | ||
227 | ASSERT(spa != NULL); | |
79c76d5b | 228 | ca = kmem_alloc(sizeof (*ca), KM_SLEEP); |
95fd54a1 SH |
229 | |
230 | (void) strlcpy(ca->zhca_spaname, spa_name(spa), | |
231 | sizeof (ca->zhca_spaname)); | |
232 | ca->zhca_spa_load_guid = spa_load_guid(spa); | |
233 | ca->zhca_holds = holds; | |
234 | VERIFY0(zfs_onexit_add_cb(minor, | |
235 | dsl_dataset_user_release_onexit, ca, NULL)); | |
236 | } | |
237 | ||
238 | void | |
239 | dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag, | |
240 | minor_t minor, uint64_t now, dmu_tx_t *tx) | |
241 | { | |
242 | nvlist_t *tmpholds; | |
243 | ||
244 | if (minor != 0) | |
79c76d5b | 245 | tmpholds = fnvlist_alloc(); |
95fd54a1 SH |
246 | else |
247 | tmpholds = NULL; | |
248 | dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx); | |
249 | dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor); | |
250 | } | |
251 | ||
13fe0198 MA |
252 | static void |
253 | dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx) | |
254 | { | |
255 | dsl_dataset_user_hold_arg_t *dduha = arg; | |
256 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
95fd54a1 | 257 | nvlist_t *tmpholds; |
13fe0198 MA |
258 | nvpair_t *pair; |
259 | uint64_t now = gethrestime_sec(); | |
260 | ||
95fd54a1 | 261 | if (dduha->dduha_minor != 0) |
79c76d5b | 262 | tmpholds = fnvlist_alloc(); |
95fd54a1 SH |
263 | else |
264 | tmpholds = NULL; | |
265 | for (pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL); | |
266 | pair != NULL; | |
267 | pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) { | |
13fe0198 | 268 | dsl_dataset_t *ds; |
95fd54a1 | 269 | |
13fe0198 | 270 | VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); |
95fd54a1 SH |
271 | dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, |
272 | fnvpair_value_string(pair), dduha->dduha_minor, now, tx); | |
13fe0198 MA |
273 | dsl_dataset_rele(ds, FTAG); |
274 | } | |
95fd54a1 | 275 | dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor); |
13fe0198 MA |
276 | } |
277 | ||
278 | /* | |
95fd54a1 SH |
279 | * The full semantics of this function are described in the comment above |
280 | * lzc_hold(). | |
281 | * | |
282 | * To summarize: | |
13fe0198 MA |
283 | * holds is nvl of snapname -> holdname |
284 | * errlist will be filled in with snapname -> error | |
13fe0198 | 285 | * |
95fd54a1 SH |
286 | * The snaphosts must all be in the same pool. |
287 | * | |
288 | * Holds for snapshots that don't exist will be skipped. | |
289 | * | |
290 | * If none of the snapshots for requested holds exist then ENOENT will be | |
291 | * returned. | |
292 | * | |
293 | * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned | |
294 | * up when the process exits. | |
295 | * | |
296 | * On success all the holds, for snapshots that existed, will be created and 0 | |
297 | * will be returned. | |
298 | * | |
299 | * On failure no holds will be created, the errlist will be filled in, | |
300 | * and an errno will returned. | |
301 | * | |
302 | * In all cases the errlist will contain entries for holds where the snapshot | |
303 | * didn't exist. | |
13fe0198 MA |
304 | */ |
305 | int | |
306 | dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist) | |
307 | { | |
308 | dsl_dataset_user_hold_arg_t dduha; | |
309 | nvpair_t *pair; | |
95fd54a1 | 310 | int ret; |
13fe0198 MA |
311 | |
312 | pair = nvlist_next_nvpair(holds, NULL); | |
313 | if (pair == NULL) | |
314 | return (0); | |
315 | ||
316 | dduha.dduha_holds = holds; | |
79c76d5b | 317 | dduha.dduha_chkholds = fnvlist_alloc(); |
13fe0198 MA |
318 | dduha.dduha_errlist = errlist; |
319 | dduha.dduha_minor = cleanup_minor; | |
320 | ||
95fd54a1 SH |
321 | ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check, |
322 | dsl_dataset_user_hold_sync, &dduha, fnvlist_num_pairs(holds)); | |
323 | fnvlist_free(dduha.dduha_chkholds); | |
324 | ||
325 | return (ret); | |
13fe0198 MA |
326 | } |
327 | ||
95fd54a1 SH |
328 | typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag, |
329 | dsl_dataset_t **dsp); | |
330 | ||
13fe0198 | 331 | typedef struct dsl_dataset_user_release_arg { |
95fd54a1 | 332 | dsl_holdfunc_t *ddura_holdfunc; |
13fe0198 MA |
333 | nvlist_t *ddura_holds; |
334 | nvlist_t *ddura_todelete; | |
335 | nvlist_t *ddura_errlist; | |
95fd54a1 | 336 | nvlist_t *ddura_chkholds; |
13fe0198 MA |
337 | } dsl_dataset_user_release_arg_t; |
338 | ||
95fd54a1 | 339 | /* Place a dataset hold on the snapshot identified by passed dsobj string */ |
13fe0198 | 340 | static int |
95fd54a1 SH |
341 | dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag, |
342 | dsl_dataset_t **dsp) | |
343 | { | |
344 | return (dsl_dataset_hold_obj(dp, strtonum(dsobj, NULL), tag, dsp)); | |
345 | } | |
346 | ||
347 | static int | |
348 | dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura, | |
349 | dsl_dataset_t *ds, nvlist_t *holds, const char *snapname) | |
13fe0198 MA |
350 | { |
351 | uint64_t zapobj; | |
95fd54a1 | 352 | nvlist_t *holds_found; |
13fe0198 | 353 | nvpair_t *pair; |
95fd54a1 SH |
354 | objset_t *mos; |
355 | int numholds; | |
13fe0198 MA |
356 | |
357 | if (!dsl_dataset_is_snapshot(ds)) | |
2e528b49 | 358 | return (SET_ERROR(EINVAL)); |
13fe0198 | 359 | |
95fd54a1 SH |
360 | if (nvlist_empty(holds)) |
361 | return (0); | |
362 | ||
363 | numholds = 0; | |
364 | mos = ds->ds_dir->dd_pool->dp_meta_objset; | |
13fe0198 | 365 | zapobj = ds->ds_phys->ds_userrefs_obj; |
79c76d5b | 366 | VERIFY0(nvlist_alloc(&holds_found, NV_UNIQUE_NAME, KM_SLEEP)); |
13fe0198 MA |
367 | |
368 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
369 | pair = nvlist_next_nvpair(holds, pair)) { | |
13fe0198 | 370 | uint64_t tmp; |
95fd54a1 SH |
371 | int error; |
372 | const char *holdname = nvpair_name(pair); | |
373 | ||
374 | if (zapobj != 0) | |
375 | error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp); | |
376 | else | |
377 | error = SET_ERROR(ENOENT); | |
378 | ||
379 | /* | |
380 | * Non-existent holds are put on the errlist, but don't | |
381 | * cause an overall failure. | |
382 | */ | |
383 | if (error == ENOENT) { | |
384 | if (ddura->ddura_errlist != NULL) { | |
385 | char *errtag = kmem_asprintf("%s#%s", | |
386 | snapname, holdname); | |
387 | fnvlist_add_int32(ddura->ddura_errlist, errtag, | |
388 | ENOENT); | |
389 | strfree(errtag); | |
390 | } | |
391 | continue; | |
392 | } | |
393 | ||
394 | if (error != 0) { | |
395 | fnvlist_free(holds_found); | |
13fe0198 | 396 | return (error); |
95fd54a1 SH |
397 | } |
398 | ||
399 | fnvlist_add_boolean(holds_found, holdname); | |
13fe0198 MA |
400 | numholds++; |
401 | } | |
402 | ||
403 | if (DS_IS_DEFER_DESTROY(ds) && ds->ds_phys->ds_num_children == 1 && | |
404 | ds->ds_userrefs == numholds) { | |
405 | /* we need to destroy the snapshot as well */ | |
95fd54a1 SH |
406 | if (dsl_dataset_long_held(ds)) { |
407 | fnvlist_free(holds_found); | |
2e528b49 | 408 | return (SET_ERROR(EBUSY)); |
95fd54a1 SH |
409 | } |
410 | fnvlist_add_boolean(ddura->ddura_todelete, snapname); | |
411 | } | |
412 | ||
413 | if (numholds != 0) { | |
414 | fnvlist_add_nvlist(ddura->ddura_chkholds, snapname, | |
415 | holds_found); | |
13fe0198 | 416 | } |
95fd54a1 SH |
417 | fnvlist_free(holds_found); |
418 | ||
13fe0198 MA |
419 | return (0); |
420 | } | |
421 | ||
422 | static int | |
423 | dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) | |
424 | { | |
95fd54a1 SH |
425 | dsl_dataset_user_release_arg_t *ddura; |
426 | dsl_holdfunc_t *holdfunc; | |
427 | dsl_pool_t *dp; | |
13fe0198 | 428 | nvpair_t *pair; |
13fe0198 MA |
429 | |
430 | if (!dmu_tx_is_syncing(tx)) | |
431 | return (0); | |
432 | ||
95fd54a1 SH |
433 | dp = dmu_tx_pool(tx); |
434 | ||
435 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); | |
436 | ||
437 | ddura = arg; | |
438 | holdfunc = ddura->ddura_holdfunc; | |
439 | ||
440 | for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); | |
441 | pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { | |
13fe0198 MA |
442 | int error; |
443 | dsl_dataset_t *ds; | |
444 | nvlist_t *holds; | |
95fd54a1 | 445 | const char *snapname = nvpair_name(pair); |
13fe0198 MA |
446 | |
447 | error = nvpair_value_nvlist(pair, &holds); | |
448 | if (error != 0) | |
95fd54a1 SH |
449 | error = (SET_ERROR(EINVAL)); |
450 | else | |
451 | error = holdfunc(dp, snapname, FTAG, &ds); | |
13fe0198 | 452 | if (error == 0) { |
95fd54a1 SH |
453 | error = dsl_dataset_user_release_check_one(ddura, ds, |
454 | holds, snapname); | |
13fe0198 MA |
455 | dsl_dataset_rele(ds, FTAG); |
456 | } | |
457 | if (error != 0) { | |
458 | if (ddura->ddura_errlist != NULL) { | |
459 | fnvlist_add_int32(ddura->ddura_errlist, | |
95fd54a1 | 460 | snapname, error); |
13fe0198 | 461 | } |
95fd54a1 SH |
462 | /* |
463 | * Non-existent snapshots are put on the errlist, | |
464 | * but don't cause an overall failure. | |
465 | */ | |
466 | if (error != ENOENT) | |
467 | return (error); | |
13fe0198 MA |
468 | } |
469 | } | |
95fd54a1 | 470 | |
95fd54a1 | 471 | return (0); |
13fe0198 MA |
472 | } |
473 | ||
474 | static void | |
475 | dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds, | |
476 | dmu_tx_t *tx) | |
477 | { | |
478 | dsl_pool_t *dp = ds->ds_dir->dd_pool; | |
479 | objset_t *mos = dp->dp_meta_objset; | |
13fe0198 MA |
480 | nvpair_t *pair; |
481 | ||
482 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
483 | pair = nvlist_next_nvpair(holds, pair)) { | |
95fd54a1 SH |
484 | int error; |
485 | const char *holdname = nvpair_name(pair); | |
486 | ||
487 | /* Remove temporary hold if one exists. */ | |
488 | error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx); | |
13fe0198 | 489 | VERIFY(error == 0 || error == ENOENT); |
95fd54a1 SH |
490 | |
491 | VERIFY0(zap_remove(mos, ds->ds_phys->ds_userrefs_obj, holdname, | |
492 | tx)); | |
493 | ds->ds_userrefs--; | |
13fe0198 MA |
494 | |
495 | spa_history_log_internal_ds(ds, "release", tx, | |
95fd54a1 | 496 | "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs); |
13fe0198 MA |
497 | } |
498 | } | |
499 | ||
500 | static void | |
501 | dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) | |
502 | { | |
503 | dsl_dataset_user_release_arg_t *ddura = arg; | |
95fd54a1 | 504 | dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc; |
13fe0198 MA |
505 | dsl_pool_t *dp = dmu_tx_pool(tx); |
506 | nvpair_t *pair; | |
507 | ||
95fd54a1 SH |
508 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); |
509 | ||
510 | for (pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL); | |
511 | pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds, | |
512 | pair)) { | |
13fe0198 | 513 | dsl_dataset_t *ds; |
95fd54a1 SH |
514 | const char *name = nvpair_name(pair); |
515 | ||
516 | VERIFY0(holdfunc(dp, name, FTAG, &ds)); | |
13fe0198 | 517 | |
13fe0198 MA |
518 | dsl_dataset_user_release_sync_one(ds, |
519 | fnvpair_value_nvlist(pair), tx); | |
95fd54a1 | 520 | if (nvlist_exists(ddura->ddura_todelete, name)) { |
13fe0198 MA |
521 | ASSERT(ds->ds_userrefs == 0 && |
522 | ds->ds_phys->ds_num_children == 1 && | |
523 | DS_IS_DEFER_DESTROY(ds)); | |
524 | dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); | |
525 | } | |
526 | dsl_dataset_rele(ds, FTAG); | |
527 | } | |
528 | } | |
529 | ||
530 | /* | |
95fd54a1 SH |
531 | * The full semantics of this function are described in the comment above |
532 | * lzc_release(). | |
533 | * | |
534 | * To summarize: | |
535 | * Releases holds specified in the nvl holds. | |
536 | * | |
13fe0198 MA |
537 | * holds is nvl of snapname -> { holdname, ... } |
538 | * errlist will be filled in with snapname -> error | |
539 | * | |
95fd54a1 SH |
540 | * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots, |
541 | * otherwise they should be the names of shapshots. | |
542 | * | |
543 | * As a release may cause snapshots to be destroyed this trys to ensure they | |
544 | * aren't mounted. | |
545 | * | |
546 | * The release of non-existent holds are skipped. | |
547 | * | |
548 | * At least one hold must have been released for the this function to succeed | |
549 | * and return 0. | |
13fe0198 | 550 | */ |
95fd54a1 SH |
551 | static int |
552 | dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist, | |
553 | dsl_pool_t *tmpdp) | |
13fe0198 MA |
554 | { |
555 | dsl_dataset_user_release_arg_t ddura; | |
556 | nvpair_t *pair; | |
95fd54a1 | 557 | char *pool; |
13fe0198 MA |
558 | int error; |
559 | ||
560 | pair = nvlist_next_nvpair(holds, NULL); | |
561 | if (pair == NULL) | |
562 | return (0); | |
563 | ||
95fd54a1 SH |
564 | /* |
565 | * The release may cause snapshots to be destroyed; make sure they | |
566 | * are not mounted. | |
567 | */ | |
568 | if (tmpdp != NULL) { | |
569 | /* Temporary holds are specified by dsobj string. */ | |
570 | ddura.ddura_holdfunc = dsl_dataset_hold_obj_string; | |
571 | pool = spa_name(tmpdp->dp_spa); | |
572 | #ifdef _KERNEL | |
95fd54a1 SH |
573 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; |
574 | pair = nvlist_next_nvpair(holds, pair)) { | |
575 | dsl_dataset_t *ds; | |
576 | ||
e5bacf21 | 577 | dsl_pool_config_enter(tmpdp, FTAG); |
95fd54a1 SH |
578 | error = dsl_dataset_hold_obj_string(tmpdp, |
579 | nvpair_name(pair), FTAG, &ds); | |
580 | if (error == 0) { | |
581 | char name[MAXNAMELEN]; | |
582 | dsl_dataset_name(ds, name); | |
e5bacf21 | 583 | dsl_pool_config_exit(tmpdp, FTAG); |
95fd54a1 SH |
584 | dsl_dataset_rele(ds, FTAG); |
585 | (void) zfs_unmount_snap(name); | |
e5bacf21 SH |
586 | } else { |
587 | dsl_pool_config_exit(tmpdp, FTAG); | |
95fd54a1 SH |
588 | } |
589 | } | |
95fd54a1 SH |
590 | #endif |
591 | } else { | |
592 | /* Non-temporary holds are specified by name. */ | |
593 | ddura.ddura_holdfunc = dsl_dataset_hold; | |
594 | pool = nvpair_name(pair); | |
595 | #ifdef _KERNEL | |
596 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
597 | pair = nvlist_next_nvpair(holds, pair)) { | |
598 | (void) zfs_unmount_snap(nvpair_name(pair)); | |
599 | } | |
600 | #endif | |
601 | } | |
602 | ||
13fe0198 MA |
603 | ddura.ddura_holds = holds; |
604 | ddura.ddura_errlist = errlist; | |
65c67ea8 | 605 | VERIFY0(nvlist_alloc(&ddura.ddura_todelete, NV_UNIQUE_NAME, |
79c76d5b | 606 | KM_SLEEP)); |
65c67ea8 | 607 | VERIFY0(nvlist_alloc(&ddura.ddura_chkholds, NV_UNIQUE_NAME, |
79c76d5b | 608 | KM_SLEEP)); |
13fe0198 | 609 | |
95fd54a1 | 610 | error = dsl_sync_task(pool, dsl_dataset_user_release_check, |
9b67f605 | 611 | dsl_dataset_user_release_sync, &ddura, 0); |
13fe0198 | 612 | fnvlist_free(ddura.ddura_todelete); |
95fd54a1 | 613 | fnvlist_free(ddura.ddura_chkholds); |
13fe0198 | 614 | |
13fe0198 MA |
615 | return (error); |
616 | } | |
617 | ||
13fe0198 | 618 | /* |
95fd54a1 SH |
619 | * holds is nvl of snapname -> { holdname, ... } |
620 | * errlist will be filled in with snapname -> error | |
13fe0198 | 621 | */ |
95fd54a1 SH |
622 | int |
623 | dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist) | |
13fe0198 | 624 | { |
95fd54a1 | 625 | return (dsl_dataset_user_release_impl(holds, errlist, NULL)); |
13fe0198 MA |
626 | } |
627 | ||
95fd54a1 SH |
628 | /* |
629 | * holds is nvl of snapdsobj -> { holdname, ... } | |
630 | */ | |
13fe0198 | 631 | void |
95fd54a1 | 632 | dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds) |
13fe0198 | 633 | { |
95fd54a1 SH |
634 | ASSERT(dp != NULL); |
635 | (void) dsl_dataset_user_release_impl(holds, NULL, dp); | |
13fe0198 MA |
636 | } |
637 | ||
638 | int | |
639 | dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) | |
640 | { | |
641 | dsl_pool_t *dp; | |
642 | dsl_dataset_t *ds; | |
643 | int err; | |
644 | ||
645 | err = dsl_pool_hold(dsname, FTAG, &dp); | |
646 | if (err != 0) | |
647 | return (err); | |
648 | err = dsl_dataset_hold(dp, dsname, FTAG, &ds); | |
649 | if (err != 0) { | |
650 | dsl_pool_rele(dp, FTAG); | |
651 | return (err); | |
652 | } | |
653 | ||
654 | if (ds->ds_phys->ds_userrefs_obj != 0) { | |
655 | zap_attribute_t *za; | |
656 | zap_cursor_t zc; | |
657 | ||
79c76d5b | 658 | za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); |
13fe0198 MA |
659 | for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, |
660 | ds->ds_phys->ds_userrefs_obj); | |
661 | zap_cursor_retrieve(&zc, za) == 0; | |
662 | zap_cursor_advance(&zc)) { | |
663 | fnvlist_add_uint64(nvl, za->za_name, | |
664 | za->za_first_integer); | |
665 | } | |
666 | zap_cursor_fini(&zc); | |
667 | kmem_free(za, sizeof (zap_attribute_t)); | |
668 | } | |
669 | dsl_dataset_rele(ds, FTAG); | |
670 | dsl_pool_rele(dp, FTAG); | |
671 | return (0); | |
672 | } |