]>
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. | |
eca7b760 | 23 | * Copyright (c) 2012, 2015 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) */ | |
d683ddbb | 67 | if (ds != NULL && dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { |
95fd54a1 SH |
68 | uint64_t value; |
69 | ||
d683ddbb | 70 | error = zap_lookup(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, |
95fd54a1 SH |
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 | ||
d683ddbb | 144 | if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) { |
13fe0198 MA |
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); | |
d683ddbb | 150 | zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj = |
13fe0198 MA |
151 | zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); |
152 | } else { | |
d683ddbb | 153 | zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; |
13fe0198 MA |
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 | 183 | typedef struct zfs_hold_cleanup_arg { |
eca7b760 | 184 | char zhca_spaname[ZFS_MAX_DATASET_NAME_LEN]; |
95fd54a1 SH |
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 | 321 | ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check, |
3d45fdd6 MA |
322 | dsl_dataset_user_hold_sync, &dduha, |
323 | fnvlist_num_pairs(holds), ZFS_SPACE_CHECK_RESERVED); | |
95fd54a1 SH |
324 | fnvlist_free(dduha.dduha_chkholds); |
325 | ||
326 | return (ret); | |
13fe0198 MA |
327 | } |
328 | ||
95fd54a1 SH |
329 | typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag, |
330 | dsl_dataset_t **dsp); | |
331 | ||
13fe0198 | 332 | typedef struct dsl_dataset_user_release_arg { |
95fd54a1 | 333 | dsl_holdfunc_t *ddura_holdfunc; |
13fe0198 MA |
334 | nvlist_t *ddura_holds; |
335 | nvlist_t *ddura_todelete; | |
336 | nvlist_t *ddura_errlist; | |
95fd54a1 | 337 | nvlist_t *ddura_chkholds; |
13fe0198 MA |
338 | } dsl_dataset_user_release_arg_t; |
339 | ||
95fd54a1 | 340 | /* Place a dataset hold on the snapshot identified by passed dsobj string */ |
13fe0198 | 341 | static int |
95fd54a1 SH |
342 | dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag, |
343 | dsl_dataset_t **dsp) | |
344 | { | |
345 | return (dsl_dataset_hold_obj(dp, strtonum(dsobj, NULL), tag, dsp)); | |
346 | } | |
347 | ||
348 | static int | |
349 | dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura, | |
350 | dsl_dataset_t *ds, nvlist_t *holds, const char *snapname) | |
13fe0198 MA |
351 | { |
352 | uint64_t zapobj; | |
95fd54a1 | 353 | nvlist_t *holds_found; |
13fe0198 | 354 | nvpair_t *pair; |
95fd54a1 SH |
355 | objset_t *mos; |
356 | int numholds; | |
13fe0198 | 357 | |
0c66c32d | 358 | if (!ds->ds_is_snapshot) |
2e528b49 | 359 | return (SET_ERROR(EINVAL)); |
13fe0198 | 360 | |
95fd54a1 SH |
361 | if (nvlist_empty(holds)) |
362 | return (0); | |
363 | ||
364 | numholds = 0; | |
365 | mos = ds->ds_dir->dd_pool->dp_meta_objset; | |
d683ddbb | 366 | zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj; |
79c76d5b | 367 | VERIFY0(nvlist_alloc(&holds_found, NV_UNIQUE_NAME, KM_SLEEP)); |
13fe0198 MA |
368 | |
369 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
370 | pair = nvlist_next_nvpair(holds, pair)) { | |
13fe0198 | 371 | uint64_t tmp; |
95fd54a1 SH |
372 | int error; |
373 | const char *holdname = nvpair_name(pair); | |
374 | ||
375 | if (zapobj != 0) | |
376 | error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp); | |
377 | else | |
378 | error = SET_ERROR(ENOENT); | |
379 | ||
380 | /* | |
381 | * Non-existent holds are put on the errlist, but don't | |
382 | * cause an overall failure. | |
383 | */ | |
384 | if (error == ENOENT) { | |
385 | if (ddura->ddura_errlist != NULL) { | |
386 | char *errtag = kmem_asprintf("%s#%s", | |
387 | snapname, holdname); | |
388 | fnvlist_add_int32(ddura->ddura_errlist, errtag, | |
389 | ENOENT); | |
390 | strfree(errtag); | |
391 | } | |
392 | continue; | |
393 | } | |
394 | ||
395 | if (error != 0) { | |
396 | fnvlist_free(holds_found); | |
13fe0198 | 397 | return (error); |
95fd54a1 SH |
398 | } |
399 | ||
400 | fnvlist_add_boolean(holds_found, holdname); | |
13fe0198 MA |
401 | numholds++; |
402 | } | |
403 | ||
d683ddbb JG |
404 | if (DS_IS_DEFER_DESTROY(ds) && |
405 | dsl_dataset_phys(ds)->ds_num_children == 1 && | |
13fe0198 MA |
406 | ds->ds_userrefs == numholds) { |
407 | /* we need to destroy the snapshot as well */ | |
95fd54a1 SH |
408 | if (dsl_dataset_long_held(ds)) { |
409 | fnvlist_free(holds_found); | |
2e528b49 | 410 | return (SET_ERROR(EBUSY)); |
95fd54a1 SH |
411 | } |
412 | fnvlist_add_boolean(ddura->ddura_todelete, snapname); | |
413 | } | |
414 | ||
415 | if (numholds != 0) { | |
416 | fnvlist_add_nvlist(ddura->ddura_chkholds, snapname, | |
417 | holds_found); | |
13fe0198 | 418 | } |
95fd54a1 SH |
419 | fnvlist_free(holds_found); |
420 | ||
13fe0198 MA |
421 | return (0); |
422 | } | |
423 | ||
424 | static int | |
425 | dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) | |
426 | { | |
95fd54a1 SH |
427 | dsl_dataset_user_release_arg_t *ddura; |
428 | dsl_holdfunc_t *holdfunc; | |
429 | dsl_pool_t *dp; | |
13fe0198 | 430 | nvpair_t *pair; |
13fe0198 MA |
431 | |
432 | if (!dmu_tx_is_syncing(tx)) | |
433 | return (0); | |
434 | ||
95fd54a1 SH |
435 | dp = dmu_tx_pool(tx); |
436 | ||
437 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); | |
438 | ||
439 | ddura = arg; | |
440 | holdfunc = ddura->ddura_holdfunc; | |
441 | ||
442 | for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); | |
443 | pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { | |
13fe0198 MA |
444 | int error; |
445 | dsl_dataset_t *ds; | |
446 | nvlist_t *holds; | |
95fd54a1 | 447 | const char *snapname = nvpair_name(pair); |
13fe0198 MA |
448 | |
449 | error = nvpair_value_nvlist(pair, &holds); | |
450 | if (error != 0) | |
95fd54a1 SH |
451 | error = (SET_ERROR(EINVAL)); |
452 | else | |
453 | error = holdfunc(dp, snapname, FTAG, &ds); | |
13fe0198 | 454 | if (error == 0) { |
95fd54a1 SH |
455 | error = dsl_dataset_user_release_check_one(ddura, ds, |
456 | holds, snapname); | |
13fe0198 MA |
457 | dsl_dataset_rele(ds, FTAG); |
458 | } | |
459 | if (error != 0) { | |
460 | if (ddura->ddura_errlist != NULL) { | |
461 | fnvlist_add_int32(ddura->ddura_errlist, | |
95fd54a1 | 462 | snapname, error); |
13fe0198 | 463 | } |
95fd54a1 SH |
464 | /* |
465 | * Non-existent snapshots are put on the errlist, | |
466 | * but don't cause an overall failure. | |
467 | */ | |
468 | if (error != ENOENT) | |
469 | return (error); | |
13fe0198 MA |
470 | } |
471 | } | |
95fd54a1 | 472 | |
95fd54a1 | 473 | return (0); |
13fe0198 MA |
474 | } |
475 | ||
476 | static void | |
477 | dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds, | |
478 | dmu_tx_t *tx) | |
479 | { | |
480 | dsl_pool_t *dp = ds->ds_dir->dd_pool; | |
481 | objset_t *mos = dp->dp_meta_objset; | |
13fe0198 MA |
482 | nvpair_t *pair; |
483 | ||
484 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
485 | pair = nvlist_next_nvpair(holds, pair)) { | |
95fd54a1 SH |
486 | int error; |
487 | const char *holdname = nvpair_name(pair); | |
488 | ||
489 | /* Remove temporary hold if one exists. */ | |
490 | error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx); | |
13fe0198 | 491 | VERIFY(error == 0 || error == ENOENT); |
95fd54a1 | 492 | |
d683ddbb JG |
493 | VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, |
494 | holdname, tx)); | |
95fd54a1 | 495 | ds->ds_userrefs--; |
13fe0198 MA |
496 | |
497 | spa_history_log_internal_ds(ds, "release", tx, | |
95fd54a1 | 498 | "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs); |
13fe0198 MA |
499 | } |
500 | } | |
501 | ||
502 | static void | |
503 | dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) | |
504 | { | |
505 | dsl_dataset_user_release_arg_t *ddura = arg; | |
95fd54a1 | 506 | dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc; |
13fe0198 MA |
507 | dsl_pool_t *dp = dmu_tx_pool(tx); |
508 | nvpair_t *pair; | |
509 | ||
95fd54a1 SH |
510 | ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); |
511 | ||
512 | for (pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL); | |
513 | pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds, | |
514 | pair)) { | |
13fe0198 | 515 | dsl_dataset_t *ds; |
95fd54a1 SH |
516 | const char *name = nvpair_name(pair); |
517 | ||
518 | VERIFY0(holdfunc(dp, name, FTAG, &ds)); | |
13fe0198 | 519 | |
13fe0198 MA |
520 | dsl_dataset_user_release_sync_one(ds, |
521 | fnvpair_value_nvlist(pair), tx); | |
95fd54a1 | 522 | if (nvlist_exists(ddura->ddura_todelete, name)) { |
13fe0198 | 523 | ASSERT(ds->ds_userrefs == 0 && |
d683ddbb | 524 | dsl_dataset_phys(ds)->ds_num_children == 1 && |
13fe0198 MA |
525 | DS_IS_DEFER_DESTROY(ds)); |
526 | dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); | |
527 | } | |
528 | dsl_dataset_rele(ds, FTAG); | |
529 | } | |
530 | } | |
531 | ||
532 | /* | |
95fd54a1 SH |
533 | * The full semantics of this function are described in the comment above |
534 | * lzc_release(). | |
535 | * | |
536 | * To summarize: | |
537 | * Releases holds specified in the nvl holds. | |
538 | * | |
13fe0198 MA |
539 | * holds is nvl of snapname -> { holdname, ... } |
540 | * errlist will be filled in with snapname -> error | |
541 | * | |
95fd54a1 SH |
542 | * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots, |
543 | * otherwise they should be the names of shapshots. | |
544 | * | |
545 | * As a release may cause snapshots to be destroyed this trys to ensure they | |
546 | * aren't mounted. | |
547 | * | |
548 | * The release of non-existent holds are skipped. | |
549 | * | |
550 | * At least one hold must have been released for the this function to succeed | |
551 | * and return 0. | |
13fe0198 | 552 | */ |
95fd54a1 SH |
553 | static int |
554 | dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist, | |
555 | dsl_pool_t *tmpdp) | |
13fe0198 MA |
556 | { |
557 | dsl_dataset_user_release_arg_t ddura; | |
558 | nvpair_t *pair; | |
95fd54a1 | 559 | char *pool; |
13fe0198 MA |
560 | int error; |
561 | ||
562 | pair = nvlist_next_nvpair(holds, NULL); | |
563 | if (pair == NULL) | |
564 | return (0); | |
565 | ||
95fd54a1 SH |
566 | /* |
567 | * The release may cause snapshots to be destroyed; make sure they | |
568 | * are not mounted. | |
569 | */ | |
570 | if (tmpdp != NULL) { | |
571 | /* Temporary holds are specified by dsobj string. */ | |
572 | ddura.ddura_holdfunc = dsl_dataset_hold_obj_string; | |
573 | pool = spa_name(tmpdp->dp_spa); | |
574 | #ifdef _KERNEL | |
95fd54a1 SH |
575 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; |
576 | pair = nvlist_next_nvpair(holds, pair)) { | |
577 | dsl_dataset_t *ds; | |
578 | ||
e5bacf21 | 579 | dsl_pool_config_enter(tmpdp, FTAG); |
95fd54a1 SH |
580 | error = dsl_dataset_hold_obj_string(tmpdp, |
581 | nvpair_name(pair), FTAG, &ds); | |
582 | if (error == 0) { | |
eca7b760 | 583 | char name[ZFS_MAX_DATASET_NAME_LEN]; |
95fd54a1 | 584 | dsl_dataset_name(ds, name); |
e5bacf21 | 585 | dsl_pool_config_exit(tmpdp, FTAG); |
95fd54a1 SH |
586 | dsl_dataset_rele(ds, FTAG); |
587 | (void) zfs_unmount_snap(name); | |
e5bacf21 SH |
588 | } else { |
589 | dsl_pool_config_exit(tmpdp, FTAG); | |
95fd54a1 SH |
590 | } |
591 | } | |
95fd54a1 SH |
592 | #endif |
593 | } else { | |
594 | /* Non-temporary holds are specified by name. */ | |
595 | ddura.ddura_holdfunc = dsl_dataset_hold; | |
596 | pool = nvpair_name(pair); | |
597 | #ifdef _KERNEL | |
598 | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | |
599 | pair = nvlist_next_nvpair(holds, pair)) { | |
600 | (void) zfs_unmount_snap(nvpair_name(pair)); | |
601 | } | |
602 | #endif | |
603 | } | |
604 | ||
13fe0198 MA |
605 | ddura.ddura_holds = holds; |
606 | ddura.ddura_errlist = errlist; | |
65c67ea8 | 607 | VERIFY0(nvlist_alloc(&ddura.ddura_todelete, NV_UNIQUE_NAME, |
79c76d5b | 608 | KM_SLEEP)); |
65c67ea8 | 609 | VERIFY0(nvlist_alloc(&ddura.ddura_chkholds, NV_UNIQUE_NAME, |
79c76d5b | 610 | KM_SLEEP)); |
13fe0198 | 611 | |
95fd54a1 | 612 | error = dsl_sync_task(pool, dsl_dataset_user_release_check, |
3d45fdd6 | 613 | dsl_dataset_user_release_sync, &ddura, 0, ZFS_SPACE_CHECK_NONE); |
13fe0198 | 614 | fnvlist_free(ddura.ddura_todelete); |
95fd54a1 | 615 | fnvlist_free(ddura.ddura_chkholds); |
13fe0198 | 616 | |
13fe0198 MA |
617 | return (error); |
618 | } | |
619 | ||
13fe0198 | 620 | /* |
95fd54a1 SH |
621 | * holds is nvl of snapname -> { holdname, ... } |
622 | * errlist will be filled in with snapname -> error | |
13fe0198 | 623 | */ |
95fd54a1 SH |
624 | int |
625 | dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist) | |
13fe0198 | 626 | { |
95fd54a1 | 627 | return (dsl_dataset_user_release_impl(holds, errlist, NULL)); |
13fe0198 MA |
628 | } |
629 | ||
95fd54a1 SH |
630 | /* |
631 | * holds is nvl of snapdsobj -> { holdname, ... } | |
632 | */ | |
13fe0198 | 633 | void |
95fd54a1 | 634 | dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds) |
13fe0198 | 635 | { |
95fd54a1 SH |
636 | ASSERT(dp != NULL); |
637 | (void) dsl_dataset_user_release_impl(holds, NULL, dp); | |
13fe0198 MA |
638 | } |
639 | ||
640 | int | |
641 | dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) | |
642 | { | |
643 | dsl_pool_t *dp; | |
644 | dsl_dataset_t *ds; | |
645 | int err; | |
646 | ||
647 | err = dsl_pool_hold(dsname, FTAG, &dp); | |
648 | if (err != 0) | |
649 | return (err); | |
650 | err = dsl_dataset_hold(dp, dsname, FTAG, &ds); | |
651 | if (err != 0) { | |
652 | dsl_pool_rele(dp, FTAG); | |
653 | return (err); | |
654 | } | |
655 | ||
d683ddbb | 656 | if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { |
13fe0198 MA |
657 | zap_attribute_t *za; |
658 | zap_cursor_t zc; | |
659 | ||
79c76d5b | 660 | za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); |
13fe0198 | 661 | for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, |
d683ddbb | 662 | dsl_dataset_phys(ds)->ds_userrefs_obj); |
13fe0198 MA |
663 | zap_cursor_retrieve(&zc, za) == 0; |
664 | zap_cursor_advance(&zc)) { | |
665 | fnvlist_add_uint64(nvl, za->za_name, | |
666 | za->za_first_integer); | |
667 | } | |
668 | zap_cursor_fini(&zc); | |
669 | kmem_free(za, sizeof (zap_attribute_t)); | |
670 | } | |
671 | dsl_dataset_rele(ds, FTAG); | |
672 | dsl_pool_rele(dp, FTAG); | |
673 | return (0); | |
674 | } |