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 */
44static int be_rollback_check_callback(zfs_handle_t *, void *);
45static 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 */
80int
81be_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 */
178int
179be_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 */
246int
247be_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 */
370int
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
563done:
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 */
584int
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 */
685static int
686be_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 */
727static int
728be_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