]>
Commit | Line | Data |
---|---|---|
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. | |
23 | * Copyright (c) 2013 by Delphix. All rights reserved. | |
24 | * Copyright (c) 2013 Steven Hartland. All rights reserved. | |
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; | |
41 | nvlist_t *dduha_chkholds; | |
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 | ||
58 | ASSERT(dsl_pool_config_held(dp)); | |
59 | ||
60 | if (strlen(htag) > MAXNAMELEN) | |
61 | return (SET_ERROR(E2BIG)); | |
62 | /* Tempholds have a more restricted length */ | |
63 | if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN) | |
64 | return (SET_ERROR(E2BIG)); | |
65 | ||
66 | /* tags must be unique (if ds already exists) */ | |
67 | if (ds != NULL && dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { | |
68 | uint64_t value; | |
69 | ||
70 | error = zap_lookup(mos, dsl_dataset_phys(ds)->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; | |
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; | |
87 | ||
88 | if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) | |
89 | return (SET_ERROR(ENOTSUP)); | |
90 | ||
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)) { | |
96 | dsl_dataset_t *ds; | |
97 | int error = 0; | |
98 | char *htag, *name; | |
99 | ||
100 | /* must be a snapshot */ | |
101 | name = nvpair_name(pair); | |
102 | if (strchr(name, '@') == NULL) | |
103 | error = SET_ERROR(EINVAL); | |
104 | ||
105 | if (error == 0) | |
106 | error = nvpair_value_string(pair, &htag); | |
107 | ||
108 | if (error == 0) | |
109 | error = dsl_dataset_hold(dp, name, FTAG, &ds); | |
110 | ||
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 | ||
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); | |
127 | } | |
128 | } | |
129 | ||
130 | return (0); | |
131 | } | |
132 | ||
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) | |
137 | { | |
138 | dsl_pool_t *dp = ds->ds_dir->dd_pool; | |
139 | objset_t *mos = dp->dp_meta_objset; | |
140 | uint64_t zapobj; | |
141 | ||
142 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); | |
143 | ||
144 | if (dsl_dataset_phys(ds)->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 = dsl_dataset_phys(ds)->ds_userrefs_obj = | |
151 | zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); | |
152 | } else { | |
153 | zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; | |
154 | } | |
155 | ds->ds_userrefs++; | |
156 | ||
157 | VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx)); | |
158 | ||
159 | if (minor != 0) { | |
160 | char name[MAXNAMELEN]; | |
161 | nvlist_t *tags; | |
162 | ||
163 | VERIFY0(dsl_pool_user_hold(dp, ds->ds_object, | |
164 | htag, now, tx)); | |
165 | (void) snprintf(name, sizeof (name), "%llx", | |
166 | (u_longlong_t)ds->ds_object); | |
167 | ||
168 | if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) { | |
169 | tags = fnvlist_alloc(); | |
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 | } | |
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 | ||
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); | |
228 | ca = kmem_alloc(sizeof (*ca), KM_SLEEP); | |
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) | |
245 | tmpholds = fnvlist_alloc(); | |
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 | ||
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); | |
257 | nvlist_t *tmpholds; | |
258 | nvpair_t *pair; | |
259 | uint64_t now = gethrestime_sec(); | |
260 | ||
261 | if (dduha->dduha_minor != 0) | |
262 | tmpholds = fnvlist_alloc(); | |
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)) { | |
268 | dsl_dataset_t *ds; | |
269 | ||
270 | VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); | |
271 | dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, | |
272 | fnvpair_value_string(pair), dduha->dduha_minor, now, tx); | |
273 | dsl_dataset_rele(ds, FTAG); | |
274 | } | |
275 | dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor); | |
276 | } | |
277 | ||
278 | /* | |
279 | * The full semantics of this function are described in the comment above | |
280 | * lzc_hold(). | |
281 | * | |
282 | * To summarize: | |
283 | * holds is nvl of snapname -> holdname | |
284 | * errlist will be filled in with snapname -> error | |
285 | * | |
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. | |
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; | |
310 | int ret; | |
311 | ||
312 | pair = nvlist_next_nvpair(holds, NULL); | |
313 | if (pair == NULL) | |
314 | return (0); | |
315 | ||
316 | dduha.dduha_holds = holds; | |
317 | dduha.dduha_chkholds = fnvlist_alloc(); | |
318 | dduha.dduha_errlist = errlist; | |
319 | dduha.dduha_minor = cleanup_minor; | |
320 | ||
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); | |
326 | } | |
327 | ||
328 | typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag, | |
329 | dsl_dataset_t **dsp); | |
330 | ||
331 | typedef struct dsl_dataset_user_release_arg { | |
332 | dsl_holdfunc_t *ddura_holdfunc; | |
333 | nvlist_t *ddura_holds; | |
334 | nvlist_t *ddura_todelete; | |
335 | nvlist_t *ddura_errlist; | |
336 | nvlist_t *ddura_chkholds; | |
337 | } dsl_dataset_user_release_arg_t; | |
338 | ||
339 | /* Place a dataset hold on the snapshot identified by passed dsobj string */ | |
340 | static int | |
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) | |
350 | { | |
351 | uint64_t zapobj; | |
352 | nvlist_t *holds_found; | |
353 | nvpair_t *pair; | |
354 | objset_t *mos; | |
355 | int numholds; | |
356 | ||
357 | if (!ds->ds_is_snapshot) | |
358 | return (SET_ERROR(EINVAL)); | |
359 | ||
360 | if (nvlist_empty(holds)) | |
361 | return (0); | |
362 | ||
363 | numholds = 0; | |
364 | mos = ds->ds_dir->dd_pool->dp_meta_objset; | |
365 | zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; | |
366 | VERIFY0(nvlist_alloc(&holds_found, NV_UNIQUE_NAME, KM_SLEEP)); | |
367 | ||
368 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
369 | pair = nvlist_next_nvpair(holds, pair)) { | |
370 | uint64_t tmp; | |
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); | |
396 | return (error); | |
397 | } | |
398 | ||
399 | fnvlist_add_boolean(holds_found, holdname); | |
400 | numholds++; | |
401 | } | |
402 | ||
403 | if (DS_IS_DEFER_DESTROY(ds) && | |
404 | dsl_dataset_phys(ds)->ds_num_children == 1 && | |
405 | ds->ds_userrefs == numholds) { | |
406 | /* we need to destroy the snapshot as well */ | |
407 | if (dsl_dataset_long_held(ds)) { | |
408 | fnvlist_free(holds_found); | |
409 | return (SET_ERROR(EBUSY)); | |
410 | } | |
411 | fnvlist_add_boolean(ddura->ddura_todelete, snapname); | |
412 | } | |
413 | ||
414 | if (numholds != 0) { | |
415 | fnvlist_add_nvlist(ddura->ddura_chkholds, snapname, | |
416 | holds_found); | |
417 | } | |
418 | fnvlist_free(holds_found); | |
419 | ||
420 | return (0); | |
421 | } | |
422 | ||
423 | static int | |
424 | dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) | |
425 | { | |
426 | dsl_dataset_user_release_arg_t *ddura; | |
427 | dsl_holdfunc_t *holdfunc; | |
428 | dsl_pool_t *dp; | |
429 | nvpair_t *pair; | |
430 | ||
431 | if (!dmu_tx_is_syncing(tx)) | |
432 | return (0); | |
433 | ||
434 | dp = dmu_tx_pool(tx); | |
435 | ||
436 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); | |
437 | ||
438 | ddura = arg; | |
439 | holdfunc = ddura->ddura_holdfunc; | |
440 | ||
441 | for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); | |
442 | pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { | |
443 | int error; | |
444 | dsl_dataset_t *ds; | |
445 | nvlist_t *holds; | |
446 | const char *snapname = nvpair_name(pair); | |
447 | ||
448 | error = nvpair_value_nvlist(pair, &holds); | |
449 | if (error != 0) | |
450 | error = (SET_ERROR(EINVAL)); | |
451 | else | |
452 | error = holdfunc(dp, snapname, FTAG, &ds); | |
453 | if (error == 0) { | |
454 | error = dsl_dataset_user_release_check_one(ddura, ds, | |
455 | holds, snapname); | |
456 | dsl_dataset_rele(ds, FTAG); | |
457 | } | |
458 | if (error != 0) { | |
459 | if (ddura->ddura_errlist != NULL) { | |
460 | fnvlist_add_int32(ddura->ddura_errlist, | |
461 | snapname, error); | |
462 | } | |
463 | /* | |
464 | * Non-existent snapshots are put on the errlist, | |
465 | * but don't cause an overall failure. | |
466 | */ | |
467 | if (error != ENOENT) | |
468 | return (error); | |
469 | } | |
470 | } | |
471 | ||
472 | return (0); | |
473 | } | |
474 | ||
475 | static void | |
476 | dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds, | |
477 | dmu_tx_t *tx) | |
478 | { | |
479 | dsl_pool_t *dp = ds->ds_dir->dd_pool; | |
480 | objset_t *mos = dp->dp_meta_objset; | |
481 | nvpair_t *pair; | |
482 | ||
483 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
484 | pair = nvlist_next_nvpair(holds, pair)) { | |
485 | int error; | |
486 | const char *holdname = nvpair_name(pair); | |
487 | ||
488 | /* Remove temporary hold if one exists. */ | |
489 | error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx); | |
490 | VERIFY(error == 0 || error == ENOENT); | |
491 | ||
492 | VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, | |
493 | holdname, tx)); | |
494 | ds->ds_userrefs--; | |
495 | ||
496 | spa_history_log_internal_ds(ds, "release", tx, | |
497 | "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs); | |
498 | } | |
499 | } | |
500 | ||
501 | static void | |
502 | dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) | |
503 | { | |
504 | dsl_dataset_user_release_arg_t *ddura = arg; | |
505 | dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc; | |
506 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
507 | nvpair_t *pair; | |
508 | ||
509 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); | |
510 | ||
511 | for (pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL); | |
512 | pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds, | |
513 | pair)) { | |
514 | dsl_dataset_t *ds; | |
515 | const char *name = nvpair_name(pair); | |
516 | ||
517 | VERIFY0(holdfunc(dp, name, FTAG, &ds)); | |
518 | ||
519 | dsl_dataset_user_release_sync_one(ds, | |
520 | fnvpair_value_nvlist(pair), tx); | |
521 | if (nvlist_exists(ddura->ddura_todelete, name)) { | |
522 | ASSERT(ds->ds_userrefs == 0 && | |
523 | dsl_dataset_phys(ds)->ds_num_children == 1 && | |
524 | DS_IS_DEFER_DESTROY(ds)); | |
525 | dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); | |
526 | } | |
527 | dsl_dataset_rele(ds, FTAG); | |
528 | } | |
529 | } | |
530 | ||
531 | /* | |
532 | * The full semantics of this function are described in the comment above | |
533 | * lzc_release(). | |
534 | * | |
535 | * To summarize: | |
536 | * Releases holds specified in the nvl holds. | |
537 | * | |
538 | * holds is nvl of snapname -> { holdname, ... } | |
539 | * errlist will be filled in with snapname -> error | |
540 | * | |
541 | * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots, | |
542 | * otherwise they should be the names of shapshots. | |
543 | * | |
544 | * As a release may cause snapshots to be destroyed this trys to ensure they | |
545 | * aren't mounted. | |
546 | * | |
547 | * The release of non-existent holds are skipped. | |
548 | * | |
549 | * At least one hold must have been released for the this function to succeed | |
550 | * and return 0. | |
551 | */ | |
552 | static int | |
553 | dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist, | |
554 | dsl_pool_t *tmpdp) | |
555 | { | |
556 | dsl_dataset_user_release_arg_t ddura; | |
557 | nvpair_t *pair; | |
558 | char *pool; | |
559 | int error; | |
560 | ||
561 | pair = nvlist_next_nvpair(holds, NULL); | |
562 | if (pair == NULL) | |
563 | return (0); | |
564 | ||
565 | /* | |
566 | * The release may cause snapshots to be destroyed; make sure they | |
567 | * are not mounted. | |
568 | */ | |
569 | if (tmpdp != NULL) { | |
570 | /* Temporary holds are specified by dsobj string. */ | |
571 | ddura.ddura_holdfunc = dsl_dataset_hold_obj_string; | |
572 | pool = spa_name(tmpdp->dp_spa); | |
573 | #ifdef _KERNEL | |
574 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
575 | pair = nvlist_next_nvpair(holds, pair)) { | |
576 | dsl_dataset_t *ds; | |
577 | ||
578 | dsl_pool_config_enter(tmpdp, FTAG); | |
579 | error = dsl_dataset_hold_obj_string(tmpdp, | |
580 | nvpair_name(pair), FTAG, &ds); | |
581 | if (error == 0) { | |
582 | char name[MAXNAMELEN]; | |
583 | dsl_dataset_name(ds, name); | |
584 | dsl_pool_config_exit(tmpdp, FTAG); | |
585 | dsl_dataset_rele(ds, FTAG); | |
586 | (void) zfs_unmount_snap(name); | |
587 | } else { | |
588 | dsl_pool_config_exit(tmpdp, FTAG); | |
589 | } | |
590 | } | |
591 | #endif | |
592 | } else { | |
593 | /* Non-temporary holds are specified by name. */ | |
594 | ddura.ddura_holdfunc = dsl_dataset_hold; | |
595 | pool = nvpair_name(pair); | |
596 | #ifdef _KERNEL | |
597 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
598 | pair = nvlist_next_nvpair(holds, pair)) { | |
599 | (void) zfs_unmount_snap(nvpair_name(pair)); | |
600 | } | |
601 | #endif | |
602 | } | |
603 | ||
604 | ddura.ddura_holds = holds; | |
605 | ddura.ddura_errlist = errlist; | |
606 | VERIFY0(nvlist_alloc(&ddura.ddura_todelete, NV_UNIQUE_NAME, | |
607 | KM_SLEEP)); | |
608 | VERIFY0(nvlist_alloc(&ddura.ddura_chkholds, NV_UNIQUE_NAME, | |
609 | KM_SLEEP)); | |
610 | ||
611 | error = dsl_sync_task(pool, dsl_dataset_user_release_check, | |
612 | dsl_dataset_user_release_sync, &ddura, 0); | |
613 | fnvlist_free(ddura.ddura_todelete); | |
614 | fnvlist_free(ddura.ddura_chkholds); | |
615 | ||
616 | return (error); | |
617 | } | |
618 | ||
619 | /* | |
620 | * holds is nvl of snapname -> { holdname, ... } | |
621 | * errlist will be filled in with snapname -> error | |
622 | */ | |
623 | int | |
624 | dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist) | |
625 | { | |
626 | return (dsl_dataset_user_release_impl(holds, errlist, NULL)); | |
627 | } | |
628 | ||
629 | /* | |
630 | * holds is nvl of snapdsobj -> { holdname, ... } | |
631 | */ | |
632 | void | |
633 | dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds) | |
634 | { | |
635 | ASSERT(dp != NULL); | |
636 | (void) dsl_dataset_user_release_impl(holds, NULL, dp); | |
637 | } | |
638 | ||
639 | int | |
640 | dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) | |
641 | { | |
642 | dsl_pool_t *dp; | |
643 | dsl_dataset_t *ds; | |
644 | int err; | |
645 | ||
646 | err = dsl_pool_hold(dsname, FTAG, &dp); | |
647 | if (err != 0) | |
648 | return (err); | |
649 | err = dsl_dataset_hold(dp, dsname, FTAG, &ds); | |
650 | if (err != 0) { | |
651 | dsl_pool_rele(dp, FTAG); | |
652 | return (err); | |
653 | } | |
654 | ||
655 | if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { | |
656 | zap_attribute_t *za; | |
657 | zap_cursor_t zc; | |
658 | ||
659 | za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); | |
660 | for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, | |
661 | dsl_dataset_phys(ds)->ds_userrefs_obj); | |
662 | zap_cursor_retrieve(&zc, za) == 0; | |
663 | zap_cursor_advance(&zc)) { | |
664 | fnvlist_add_uint64(nvl, za->za_name, | |
665 | za->za_first_integer); | |
666 | } | |
667 | zap_cursor_fini(&zc); | |
668 | kmem_free(za, sizeof (zap_attribute_t)); | |
669 | } | |
670 | dsl_dataset_rele(ds, FTAG); | |
671 | dsl_pool_rele(dp, FTAG); | |
672 | return (0); | |
673 | } |