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