xref: /illumos-gate/usr/src/lib/libbe/common/be_snapshot.c (revision a154f012db8e83f5f1b49be92c91827d1868a12a)
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 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * System includes
28  */
29 #include <assert.h>
30 #include <libintl.h>
31 #include <libnvpair.h>
32 #include <libzfs.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 
40 #include <libbe.h>
41 #include <libbe_priv.h>
42 
43 /* Private function prototypes */
44 static int be_rollback_check_callback(zfs_handle_t *, void *);
45 static int be_rollback_callback(zfs_handle_t *, void *);
46 
47 
48 /* ******************************************************************** */
49 /*			Public Functions				*/
50 /* ******************************************************************** */
51 
52 /*
53  * Function:	be_create_snapshot
54  * Description:	Creates a recursive snapshot of all the datasets within a BE.
55  *		If the name of the BE to snapshot is not provided, it assumes
56  *		we're snapshotting the currently running BE.  If the snapshot
57  *		name is not provided it creates an auto named snapshot, which
58  *		will be returned to the caller upon success.
59  * Parameters:
60  *		be_attrs - pointer to nvlist_t of attributes being passed in.
61  *			The following attributes are used by this function:
62  *
63  *			BE_ATTR_ORIG_BE_NAME		*optional
64  *			BE_ATTR_SNAP_NAME		*optional
65  *			BE_ATTR_POLICY			*optional
66  *
67  *			If the BE_ATTR_SNAP_NAME was not passed in, upon
68  *			successful BE snapshot creation, the following
69  *			attribute value will be returned to the caller by
70  *			setting it in the be_attrs parameter passed in:
71  *
72  *			BE_ATTR_SNAP_NAME
73  *
74  * Return:
75  *		BE_SUCCESS - Success
76  *		be_errno_t - Failure
77  * Scope:
78  *		Public
79  */
80 int
81 be_create_snapshot(nvlist_t *be_attrs)
82 {
83 	char		*be_name = NULL;
84 	char		*snap_name = NULL;
85 	char		*policy = NULL;
86 	boolean_t	autoname = B_FALSE;
87 	int 		ret = BE_SUCCESS;
88 
89 	/* Initialize libzfs handle */
90 	if (!be_zfs_init())
91 		return (BE_ERR_INIT);
92 
93 	/* Get original BE name if one was provided */
94 	if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
95 	    BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &be_name, NULL) != 0) {
96 		be_print_err(gettext("be_create_snapshot: failed to "
97 		    "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
98 		be_zfs_fini();
99 		return (BE_ERR_INVAL);
100 	}
101 
102 	/* Validate original BE name if one was provided */
103 	if (be_name != NULL && !be_valid_be_name(be_name)) {
104 		be_print_err(gettext("be_create_snapshot: "
105 		    "invalid BE name %s\n"), be_name);
106 		be_zfs_fini();
107 		return (BE_ERR_INVAL);
108 	}
109 
110 	/* Get snapshot name to create if one was provided */
111 	if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
112 	    BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &snap_name, NULL) != 0) {
113 		be_print_err(gettext("be_create_snapshot: "
114 		    "failed to lookup BE_ATTR_SNAP_NAME attribute\n"));
115 		be_zfs_fini();
116 		return (BE_ERR_INVAL);
117 	}
118 
119 	/* Get BE policy to create this snapshot under */
120 	if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
121 	    BE_ATTR_POLICY, DATA_TYPE_STRING, &policy, NULL) != 0) {
122 		be_print_err(gettext("be_create_snapshot: "
123 		    "failed to lookup BE_ATTR_POLICY attribute\n"));
124 		be_zfs_fini();
125 		return (BE_ERR_INVAL);
126 	}
127 
128 	/*
129 	 * If no snap_name ws provided, we're going to create an
130 	 * auto named snapshot.  Set flag so that we know to pass
131 	 * the auto named snapshot to the caller later.
132 	 */
133 	if (snap_name == NULL)
134 		autoname = B_TRUE;
135 
136 	if ((ret = _be_create_snapshot(be_name, &snap_name, policy))
137 	    == BE_SUCCESS) {
138 		if (autoname == B_TRUE) {
139 			/*
140 			 * Set auto named snapshot name in the
141 			 * nvlist passed in by the caller.
142 			 */
143 			if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME,
144 			    snap_name) != 0) {
145 				be_print_err(gettext("be_create_snapshot: "
146 				    "failed to add auto snap name (%s) to "
147 				    "be_attrs\n"), snap_name);
148 				ret = BE_ERR_NOMEM;
149 			}
150 		}
151 	}
152 
153 	be_zfs_fini();
154 
155 	return (ret);
156 }
157 
158 /*
159  * Function:	be_destroy_snapshot
160  * Description:	Iterates through all the datasets of the BE and deletes
161  *		the snapshots of each one with the specified name.  If the
162  *		BE name is not provided, it assumes we're operating on the
163  *		currently running BE.  The name of the snapshot name to
164  *		destroy must be provided.
165  * Parameters:
166  *		be_attrs - pointer to nvlist_t of attributes being passed in.
167  *			   The following attribute values are used by this
168  *			   function:
169  *
170  *			   BE_ATTR_ORIG_BE_NAME		*optional
171  *			   BE_ATTR_SNAP_NAME		*required
172  * Return:
173  *		BE_SUCCESS - Success
174  *		be_errno_t - Failure
175  * Scope:
176  *		Public
177  */
178 int
179 be_destroy_snapshot(nvlist_t *be_attrs)
180 {
181 	char	*be_name = NULL;
182 	char	*snap_name = NULL;
183 	int 	ret = BE_SUCCESS;
184 
185 	/* Initialize libzfs handle */
186 	if (!be_zfs_init())
187 		return (BE_ERR_INIT);
188 
189 	/* Get original BE name if one was provided */
190 	if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
191 	    BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &be_name, NULL) != 0) {
192 		be_print_err(gettext("be_destroy_snapshot: "
193 		    "failed to lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
194 		return (BE_ERR_INVAL);
195 	}
196 
197 	/* Validate original BE name if one was provided */
198 	if (be_name != NULL && !be_valid_be_name(be_name)) {
199 		be_print_err(gettext("be_destroy_snapshot: "
200 		    "invalid BE name %s\n"), be_name);
201 		return (BE_ERR_INVAL);
202 	}
203 
204 	/* Get snapshot name to destroy */
205 	if (nvlist_lookup_string(be_attrs, BE_ATTR_SNAP_NAME, &snap_name)
206 	    != 0) {
207 		be_print_err(gettext("be_destroy_snapshot: "
208 		    "failed to lookup BE_ATTR_SNAP_NAME attribute.\n"));
209 		return (BE_ERR_INVAL);
210 	}
211 
212 	ret = _be_destroy_snapshot(be_name, snap_name);
213 
214 	be_zfs_fini();
215 
216 	return (ret);
217 }
218 
219 /*
220  * Function:	be_rollback
221  * Description:	Rolls back a BE and all of its children datasets to the
222  *		named snapshot.  All of the BE's datasets must have the
223  *		named snapshot for this function to succeed.  If the name
224  *		of the BE is not passed in, this function assumes we're
225  *		operating on the currently booted live BE.
226  *
227  *		Note - This function does not check if the BE has any
228  *		younger snapshots than the one we're trying to rollback to.
229  *		If it does, then those younger snapshots and their dependent
230  *		clone file systems will get destroyed in the process of
231  *		rolling back.
232  *
233  * Parameters:
234  *		be_attrs - pointer to nvlist_t of attributes being passed in.
235  *			   The following attributes are used by this function:
236  *
237  *			   BE_ATTR_ORIG_BE_NAME		*optional
238  *			   BE_ATTR_SNAP_NAME		*required
239  *
240  * Returns:
241  *		BE_SUCCESS - Success
242  *		be_errno_t - Failure
243  * Scope:
244  *		Public
245  */
246 int
247 be_rollback(nvlist_t *be_attrs)
248 {
249 	be_transaction_data_t	bt = { 0 };
250 	zfs_handle_t		*zhp = NULL;
251 	char			obe_root_ds[MAXPATHLEN];
252 	int			zret = 0, ret = BE_SUCCESS;
253 
254 	/* Initialize libzfs handle */
255 	if (!be_zfs_init())
256 		return (BE_ERR_INIT);
257 
258 	/* Get original BE name if one was provided */
259 	if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
260 	    BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &bt.obe_name, NULL) != 0) {
261 		be_print_err(gettext("be_rollback: "
262 		    "failed to lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
263 		return (BE_ERR_INVAL);
264 	}
265 
266 	/* If original BE name not provided, use current BE */
267 	if (bt.obe_name == NULL) {
268 		if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
269 			return (ret);
270 		}
271 	} else {
272 		/* Validate original BE name  */
273 		if (!be_valid_be_name(bt.obe_name)) {
274 			be_print_err(gettext("be_rollback: "
275 			    "invalid BE name %s\n"), bt.obe_name);
276 			return (BE_ERR_INVAL);
277 		}
278 	}
279 
280 	/* Get snapshot name to rollback to */
281 	if (nvlist_lookup_string(be_attrs, BE_ATTR_SNAP_NAME, &bt.obe_snap_name)
282 	    != 0) {
283 		be_print_err(gettext("be_rollback: "
284 		    "failed to lookup BE_ATTR_SNAP_NAME attribute.\n"));
285 		return (BE_ERR_INVAL);
286 	}
287 
288 	/* Find which zpool obe_name lives in */
289 	if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
290 		be_print_err(gettext("be_rollback: "
291 		    "failed to find zpool for BE (%s)\n"), bt.obe_name);
292 		return (BE_ERR_BE_NOENT);
293 	} else if (zret < 0) {
294 		be_print_err(gettext("be_rollback: "
295 		    "zpool_iter failed: %s\n"),
296 		    libzfs_error_description(g_zfs));
297 		return (zfs_err_to_be_err(g_zfs));
298 	}
299 
300 	/* Generate string for BE's root dataset */
301 	be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
302 	    sizeof (obe_root_ds));
303 	bt.obe_root_ds = obe_root_ds;
304 
305 	/* Get handle to BE's root dataset */
306 	if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET)) == NULL) {
307 		be_print_err(gettext("be_rollback: "
308 		    "failed to open BE root dataset (%s): %s\n"),
309 		    bt.obe_root_ds, libzfs_error_description(g_zfs));
310 		return (zfs_err_to_be_err(g_zfs));
311 	}
312 
313 	/*
314 	 * Check that snapshot name exists for this BE and all of its
315 	 * children file systems.  This call will end up closing the
316 	 * zfs handle passed in whether it succeeds or fails.
317 	 */
318 	if ((ret = be_rollback_check_callback(zhp, bt.obe_snap_name)) != 0) {
319 		zhp = NULL;
320 		return (ret);
321 	}
322 
323 	/* Get handle to BE's root dataset */
324 	if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET)) == NULL) {
325 		be_print_err(gettext("be_rollback: "
326 		    "failed to open BE root dataset (%s): %s\n"),
327 		    bt.obe_root_ds, libzfs_error_description(g_zfs));
328 		return (zfs_err_to_be_err(g_zfs));
329 	}
330 
331 	/*
332 	 * Iterate through a BE's datasets and roll them all back to
333 	 * the specified snapshot.  This call will end up closing the
334 	 * zfs handle passed in whether it succeeds or fails.
335 	 */
336 	if ((ret = be_rollback_callback(zhp, bt.obe_snap_name)) != 0) {
337 		zhp = NULL;
338 		be_print_err(gettext("be_rollback: "
339 		    "failed to rollback BE %s to %s\n"), bt.obe_name,
340 		    bt.obe_snap_name);
341 		return (ret);
342 	}
343 	zhp = NULL;
344 	be_zfs_fini();
345 	return (BE_SUCCESS);
346 }
347 
348 
349 /* ******************************************************************** */
350 /*			Semi-Private Functions				*/
351 /* ******************************************************************** */
352 
353 /*
354  * Function:	_be_create_snapshot
355  * Description:	see be_create_snapshot
356  * Parameters:
357  *		be_name - The name of the BE that we're taking a snapshot of.
358  *		snap_name - The name of the snapshot we're creating. If
359  *			snap_name is NULL an auto generated name will be used,
360  *			and upon success, will return that name via this
361  *			reference pointer.  The caller is responsible for
362  *			freeing the returned name.
363  *		policy - The clean-up policy type. (library wide use only)
364  * Return:
365  *		BE_SUCCESS - Success
366  *		be_errno_t - Failure
367  * Scope:
368  *		Semi-private (library wide use only)
369  */
370 int
371 _be_create_snapshot(char *be_name, char **snap_name, char *policy)
372 {
373 	be_transaction_data_t	bt = { 0 };
374 	zfs_handle_t		*zhp = NULL;
375 	nvlist_t		*ss_props = NULL;
376 	char			ss[MAXPATHLEN];
377 	char			root_ds[MAXPATHLEN];
378 	int			pool_version = 0;
379 	int			i = 0;
380 	int			zret = 0, ret = BE_SUCCESS;
381 	boolean_t		autoname = B_FALSE;
382 
383 	/* Set parameters in bt structure */
384 	bt.obe_name = be_name;
385 	bt.obe_snap_name = *snap_name;
386 	bt.policy = policy;
387 
388 	/* If original BE name not supplied, use current BE */
389 	if (bt.obe_name == NULL) {
390 		if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
391 			return (ret);
392 		}
393 	}
394 
395 	/* Find which zpool obe_name lives in */
396 	if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
397 		be_print_err(gettext("be_create_snapshot: failed to "
398 		    "find zpool for BE (%s)\n"), bt.obe_name);
399 		return (BE_ERR_BE_NOENT);
400 	} else if (zret < 0) {
401 		be_print_err(gettext("be_create_snapshot: "
402 		    "zpool_iter failed: %s\n"),
403 		    libzfs_error_description(g_zfs));
404 		return (zfs_err_to_be_err(g_zfs));
405 	}
406 
407 	be_make_root_ds(bt.obe_zpool, bt.obe_name, root_ds,
408 	    sizeof (root_ds));
409 	bt.obe_root_ds = root_ds;
410 
411 	/* If BE policy not specified, use the default policy */
412 	if (bt.policy == NULL) {
413 		bt.policy = be_default_policy();
414 	} else {
415 		/* Validate policy type */
416 		if (!valid_be_policy(bt.policy)) {
417 			be_print_err(gettext("be_create_snapshot: "
418 			    "invalid BE policy type (%s)\n"), bt.policy);
419 			return (BE_ERR_INVAL);
420 		}
421 	}
422 
423 	/*
424 	 * If snapshot name not specified, set auto name flag and
425 	 * generate auto snapshot name.
426 	 */
427 	if (bt.obe_snap_name == NULL) {
428 		autoname = B_TRUE;
429 		if ((bt.obe_snap_name = be_auto_snap_name())
430 		    == NULL) {
431 			be_print_err(gettext("be_create_snapshot: "
432 			    "failed to create auto snapshot name\n"));
433 			ret =  BE_ERR_AUTONAME;
434 			goto done;
435 		}
436 	}
437 
438 	/* Generate the name of the snapshot to take. */
439 	(void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_root_ds,
440 	    bt.obe_snap_name);
441 
442 	/* Get handle to BE's root dataset */
443 	if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET))
444 	    == NULL) {
445 		be_print_err(gettext("be_create_snapshot: "
446 		    "failed to open BE root dataset (%s): %s\n"),
447 		    bt.obe_root_ds, libzfs_error_description(g_zfs));
448 		ret = zfs_err_to_be_err(g_zfs);
449 		goto done;
450 	}
451 
452 	/* Get the ZFS pool version of the pool where this dataset resides */
453 	if (zfs_spa_version(zhp, &pool_version) != 0) {
454 		be_print_err(gettext("be_create_snapshot: failed to "
455 		    "get ZFS pool version for %s: %s\n"), zfs_get_name(zhp),
456 		    libzfs_error_description(g_zfs));
457 	}
458 
459 	/*
460 	 * If ZFS pool version supports snapshot user properties, store
461 	 * cleanup policy there.  Otherwise don't set one - this snapshot
462 	 * will always inherit the cleanup policy from its parent.
463 	 */
464 	if (pool_version >= SPA_VERSION_SNAP_PROPS) {
465 		if (nvlist_alloc(&ss_props, NV_UNIQUE_NAME, 0) != 0) {
466 			be_print_err(gettext("be_create_snapshot: internal "
467 			    "error: out of memory\n"));
468 			return (BE_ERR_NOMEM);
469 		}
470 		if (nvlist_add_string(ss_props, BE_POLICY_PROPERTY, bt.policy)
471 		    != 0) {
472 			be_print_err(gettext("be_create_snapshot: internal "
473 			    "error: out of memory\n"));
474 			nvlist_free(ss_props);
475 			return (BE_ERR_NOMEM);
476 		}
477 	} else if (policy != NULL) {
478 		/*
479 		 * If an explicit cleanup policy was requested
480 		 * by the caller and we don't support it, error out.
481 		 */
482 		be_print_err(gettext("be_create_snapshot: cannot set "
483 		    "cleanup policy: ZFS pool version is %d\n"), pool_version);
484 		return (BE_ERR_NOTSUP);
485 	}
486 
487 	/* Create the snapshots recursively */
488 	if (zfs_snapshot(g_zfs, ss, B_TRUE, ss_props) != 0) {
489 		if (!autoname || libzfs_errno(g_zfs) != EZFS_EXISTS) {
490 			be_print_err(gettext("be_create_snapshot: "
491 			    "recursive snapshot of %s failed: %s\n"),
492 			    ss, libzfs_error_description(g_zfs));
493 
494 			if (libzfs_errno(g_zfs) == EZFS_EXISTS)
495 				ret = BE_ERR_SS_EXISTS;
496 			else
497 				ret = zfs_err_to_be_err(g_zfs);
498 
499 			goto done;
500 		} else {
501 			for (i = 1; i < BE_AUTO_NAME_MAX_TRY; i++) {
502 
503 				/* Sleep 1 before retrying */
504 				(void) sleep(1);
505 
506 				/* Generate new auto snapshot name. */
507 				free(bt.obe_snap_name);
508 				if ((bt.obe_snap_name =
509 				    be_auto_snap_name()) == NULL) {
510 					be_print_err(gettext(
511 					    "be_create_snapshot: failed to "
512 					    "create auto snapshot name\n"));
513 					ret = BE_ERR_AUTONAME;
514 					goto done;
515 				}
516 
517 				/* Generate string of the snapshot to take. */
518 				(void) snprintf(ss, sizeof (ss), "%s@%s",
519 				    bt.obe_root_ds, bt.obe_snap_name);
520 
521 				/* Create the snapshots recursively */
522 				if (zfs_snapshot(g_zfs, ss, B_TRUE, ss_props)
523 				    != 0) {
524 					if (libzfs_errno(g_zfs) !=
525 					    EZFS_EXISTS) {
526 						be_print_err(gettext(
527 						    "be_create_snapshot: "
528 						    "recursive snapshot of %s "
529 						    "failed: %s\n"), ss,
530 						    libzfs_error_description(
531 						    g_zfs));
532 						ret = zfs_err_to_be_err(g_zfs);
533 						goto done;
534 					}
535 				} else {
536 					break;
537 				}
538 			}
539 
540 			/*
541 			 * If we exhausted the maximum number of tries,
542 			 * free the auto snap name and set error.
543 			 */
544 			if (i == BE_AUTO_NAME_MAX_TRY) {
545 				be_print_err(gettext("be_create_snapshot: "
546 				    "failed to create unique auto snapshot "
547 				    "name\n"));
548 				free(bt.obe_snap_name);
549 				bt.obe_snap_name = NULL;
550 				ret = BE_ERR_AUTONAME;
551 			}
552 		}
553 	}
554 
555 	/*
556 	 * If we succeeded in creating an auto named snapshot, store
557 	 * the name in the nvlist passed in by the caller.
558 	 */
559 	if (autoname && bt.obe_snap_name) {
560 		*snap_name = bt.obe_snap_name;
561 	}
562 
563 done:
564 	ZFS_CLOSE(zhp);
565 
566 	if (ss_props != NULL)
567 		nvlist_free(ss_props);
568 
569 	return (ret);
570 }
571 
572 /*
573  * Function:	_be_destroy_snapshot
574  * Description:	see be_destroy_snapshot
575  * Parameters:
576  *		be_name - The name of the BE that the snapshot belongs to.
577  *		snap_name - The name of the snapshot we're destroying.
578  * Return:
579  *		BE_SUCCESS - Success
580  *		be_errno_t - Failure
581  * Scope:
582  *		Semi-private (library wide use only)
583  */
584 int
585 _be_destroy_snapshot(char *be_name, char *snap_name)
586 {
587 	be_transaction_data_t	bt = { 0 };
588 	zfs_handle_t		*zhp;
589 	char			ss[MAXPATHLEN];
590 	char			root_ds[MAXPATHLEN];
591 	int			err = BE_SUCCESS, ret = BE_SUCCESS;
592 
593 	/* Make sure we actaully have a snapshot name */
594 	if (snap_name == NULL) {
595 		be_print_err(gettext("be_destroy_snapshot: "
596 		    "invalid snapshot name\n"));
597 		return (BE_ERR_INVAL);
598 	}
599 
600 	/* Set parameters in bt structure */
601 	bt.obe_name = be_name;
602 	bt.obe_snap_name = snap_name;
603 
604 	/* If original BE name not supplied, use current BE */
605 	if (bt.obe_name == NULL) {
606 		if ((err = be_find_current_be(&bt)) != BE_SUCCESS) {
607 			return (err);
608 		}
609 	}
610 
611 	/* Find which zpool be_name lives in */
612 	if ((ret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
613 		be_print_err(gettext("be_destroy_snapshot: "
614 		    "failed to find zpool for BE (%s)\n"), bt.obe_name);
615 		return (BE_ERR_BE_NOENT);
616 	} else if (ret < 0) {
617 		be_print_err(gettext("be_destroy_snapshot: "
618 		    "zpool_iter failed: %s\n"),
619 		    libzfs_error_description(g_zfs));
620 		return (zfs_err_to_be_err(g_zfs));
621 	}
622 
623 	be_make_root_ds(bt.obe_zpool, bt.obe_name, root_ds,
624 	    sizeof (root_ds));
625 	bt.obe_root_ds = root_ds;
626 
627 	zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_DATASET);
628 	if (zhp == NULL) {
629 		/*
630 		 * The zfs_open failed, return an error.
631 		 */
632 		be_print_err(gettext("be_destroy_snapshot: "
633 		    "failed to open BE root dataset (%s): %s\n"),
634 		    bt.obe_root_ds, libzfs_error_description(g_zfs));
635 		err = zfs_err_to_be_err(g_zfs);
636 	} else {
637 		/*
638 		 * Generate the name of the snapshot to take.
639 		 */
640 		(void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_name,
641 		    bt.obe_snap_name);
642 
643 		/*
644 		 * destroy the snapshot.
645 		 */
646 		/*
647 		 * The boolean set to B_FALSE and passed to zfs_destroy_snaps()
648 		 * tells zfs to process and destroy the snapshots now.
649 		 * Otherwise the call will potentially return where the
650 		 * snapshot isn't actually destroyed yet, and ZFS is waiting
651 		 * until all the references to the snapshot have been
652 		 * released before actually destroying the snapshot.
653 		 */
654 		if (zfs_destroy_snaps(zhp, bt.obe_snap_name, B_FALSE) != 0) {
655 			err = zfs_err_to_be_err(g_zfs);
656 			be_print_err(gettext("be_destroy_snapshot: "
657 			    "failed to destroy snapshot %s: %s\n"), ss,
658 			    libzfs_error_description(g_zfs));
659 		}
660 	}
661 
662 	ZFS_CLOSE(zhp);
663 
664 	return (err);
665 }
666 
667 /* ********************************************************************	*/
668 /*			Private Functions				*/
669 /* ********************************************************************	*/
670 
671 /*
672  * Function:	be_rollback_check_callback
673  * Description:	Callback function used to iterate through a BE's filesystems
674  *		to check if a given snapshot name exists.
675  * Parameters:
676  *		zhp - zfs_handle_t pointer to filesystem being processed.
677  *		data - name of the snapshot to check for.
678  * Returns:
679  *		0 - Success, snapshot name exists for all filesystems.
680  *		be_errno_t - Failure, snapshot name does not exist for all
681  *		filesystems.
682  * Scope:
683  *		Private
684  */
685 static int
686 be_rollback_check_callback(zfs_handle_t *zhp, void *data)
687 {
688 	char		*snap_name = data;
689 	char		ss[MAXPATHLEN];
690 	int		ret = BE_SUCCESS;
691 
692 	/* Generate string for this filesystem's snapshot name */
693 	(void) snprintf(ss, sizeof (ss), "%s@%s", zfs_get_name(zhp), snap_name);
694 
695 	/* Check if snapshot exists */
696 	if (!zfs_dataset_exists(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) {
697 		be_print_err(gettext("be_rollback_check_callback: "
698 		    "snapshot does not exist %s\n"), ss);
699 		ZFS_CLOSE(zhp);
700 		return (BE_ERR_SS_NOENT);
701 	}
702 
703 	/* Iterate this dataset's children and check them */
704 	if ((ret = zfs_iter_filesystems(zhp, be_rollback_check_callback,
705 	    snap_name)) != 0) {
706 		ZFS_CLOSE(zhp);
707 		return (ret);
708 	}
709 
710 	ZFS_CLOSE(zhp);
711 	return (0);
712 }
713 
714 /*
715  * Function:	be_rollback_callback
716  * Description:	Callback function used to iterate through a BE's filesystems
717  *		and roll them all back to the specified snapshot name.
718  * Parameters:
719  *		zhp - zfs_handle_t pointer to filesystem being processed.
720  *		data - name of snapshot to rollback to.
721  * Returns:
722  *		0 - Success
723  *		be_errno_t - Failure
724  * Scope:
725  *		Private
726  */
727 static int
728 be_rollback_callback(zfs_handle_t *zhp, void *data)
729 {
730 	zfs_handle_t	*zhp_snap = NULL;
731 	char		*snap_name = data;
732 	char		ss[MAXPATHLEN];
733 	int		ret = 0;
734 
735 	/* Generate string for this filesystem's snapshot name */
736 	(void) snprintf(ss, sizeof (ss), "%s@%s", zfs_get_name(zhp), snap_name);
737 
738 	/* Get handle to this filesystem's snapshot */
739 	if ((zhp_snap = zfs_open(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) == NULL) {
740 		be_print_err(gettext("be_rollback_callback: "
741 		    "failed to open snapshot %s: %s\n"), zfs_get_name(zhp),
742 		    libzfs_error_description(g_zfs));
743 		ret = zfs_err_to_be_err(g_zfs);
744 		ZFS_CLOSE(zhp);
745 		return (ret);
746 	}
747 
748 	/* Rollback dataset */
749 	if (zfs_rollback(zhp, zhp_snap, B_FALSE) != 0) {
750 		be_print_err(gettext("be_rollback_callback: "
751 		    "failed to rollback BE dataset %s to snapshot %s: %s\n"),
752 		    zfs_get_name(zhp), ss, libzfs_error_description(g_zfs));
753 		ret = zfs_err_to_be_err(g_zfs);
754 		ZFS_CLOSE(zhp_snap);
755 		ZFS_CLOSE(zhp);
756 		return (ret);
757 	}
758 
759 	ZFS_CLOSE(zhp_snap);
760 	/* Iterate this dataset's children and roll them back */
761 	if ((ret = zfs_iter_filesystems(zhp, be_rollback_callback,
762 	    snap_name)) != 0) {
763 		ZFS_CLOSE(zhp);
764 		return (ret);
765 	}
766 
767 	ZFS_CLOSE(zhp);
768 	return (0);
769 }
770