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.
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.
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]
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
35 #include <libnvpair.h>
40 #include <sys/types.h>
45 #include <libbe_priv.h>
47 /* Private function prototypes */
48 static int be_rollback_check_callback(zfs_handle_t
*, void *);
49 static int be_rollback_callback(zfs_handle_t
*, void *);
52 /* ******************************************************************** */
53 /* Public Functions */
54 /* ******************************************************************** */
57 * Function: be_create_snapshot
58 * Description: Creates a recursive snapshot of all the datasets within a BE.
59 * If the name of the BE to snapshot is not provided, it assumes
60 * we're snapshotting the currently running BE. If the snapshot
61 * name is not provided it creates an auto named snapshot, which
62 * will be returned to the caller upon success.
64 * be_attrs - pointer to nvlist_t of attributes being passed in.
65 * The following attributes are used by this function:
67 * BE_ATTR_ORIG_BE_NAME *optional
68 * BE_ATTR_SNAP_NAME *optional
69 * BE_ATTR_POLICY *optional
71 * If the BE_ATTR_SNAP_NAME was not passed in, upon
72 * successful BE snapshot creation, the following
73 * attribute value will be returned to the caller by
74 * setting it in the be_attrs parameter passed in:
79 * BE_SUCCESS - Success
80 * be_errno_t - Failure
85 be_create_snapshot(nvlist_t
*be_attrs
)
88 char *snap_name
= NULL
;
90 boolean_t autoname
= B_FALSE
;
93 /* Initialize libzfs handle */
97 /* Get original BE name if one was provided */
98 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
99 BE_ATTR_ORIG_BE_NAME
, DATA_TYPE_STRING
, &be_name
, NULL
) != 0) {
100 be_print_err(gettext("be_create_snapshot: failed to "
101 "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
103 return (BE_ERR_INVAL
);
106 /* Validate original BE name if one was provided */
107 if (be_name
!= NULL
&& !be_valid_be_name(be_name
)) {
108 be_print_err(gettext("be_create_snapshot: "
109 "invalid BE name %s\n"), be_name
);
111 return (BE_ERR_INVAL
);
114 /* Get snapshot name to create if one was provided */
115 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
116 BE_ATTR_SNAP_NAME
, DATA_TYPE_STRING
, &snap_name
, NULL
) != 0) {
117 be_print_err(gettext("be_create_snapshot: "
118 "failed to lookup BE_ATTR_SNAP_NAME attribute\n"));
120 return (BE_ERR_INVAL
);
123 /* Get BE policy to create this snapshot under */
124 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
125 BE_ATTR_POLICY
, DATA_TYPE_STRING
, &policy
, NULL
) != 0) {
126 be_print_err(gettext("be_create_snapshot: "
127 "failed to lookup BE_ATTR_POLICY attribute\n"));
129 return (BE_ERR_INVAL
);
133 * If no snap_name ws provided, we're going to create an
134 * auto named snapshot. Set flag so that we know to pass
135 * the auto named snapshot to the caller later.
137 if (snap_name
== NULL
)
140 if ((ret
= _be_create_snapshot(be_name
, &snap_name
, policy
))
142 if (autoname
== B_TRUE
) {
144 * Set auto named snapshot name in the
145 * nvlist passed in by the caller.
147 if (nvlist_add_string(be_attrs
, BE_ATTR_SNAP_NAME
,
149 be_print_err(gettext("be_create_snapshot: "
150 "failed to add auto snap name (%s) to "
151 "be_attrs\n"), snap_name
);
163 * Function: be_destroy_snapshot
164 * Description: Iterates through all the datasets of the BE and deletes
165 * the snapshots of each one with the specified name. If the
166 * BE name is not provided, it assumes we're operating on the
167 * currently running BE. The name of the snapshot name to
168 * destroy must be provided.
170 * be_attrs - pointer to nvlist_t of attributes being passed in.
171 * The following attribute values are used by this
174 * BE_ATTR_ORIG_BE_NAME *optional
175 * BE_ATTR_SNAP_NAME *required
177 * BE_SUCCESS - Success
178 * be_errno_t - Failure
183 be_destroy_snapshot(nvlist_t
*be_attrs
)
185 char *be_name
= NULL
;
186 char *snap_name
= NULL
;
187 int ret
= BE_SUCCESS
;
189 /* Initialize libzfs handle */
191 return (BE_ERR_INIT
);
193 /* Get original BE name if one was provided */
194 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
195 BE_ATTR_ORIG_BE_NAME
, DATA_TYPE_STRING
, &be_name
, NULL
) != 0) {
196 be_print_err(gettext("be_destroy_snapshot: "
197 "failed to lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
198 return (BE_ERR_INVAL
);
201 /* Validate original BE name if one was provided */
202 if (be_name
!= NULL
&& !be_valid_be_name(be_name
)) {
203 be_print_err(gettext("be_destroy_snapshot: "
204 "invalid BE name %s\n"), be_name
);
205 return (BE_ERR_INVAL
);
208 /* Get snapshot name to destroy */
209 if (nvlist_lookup_string(be_attrs
, BE_ATTR_SNAP_NAME
, &snap_name
)
211 be_print_err(gettext("be_destroy_snapshot: "
212 "failed to lookup BE_ATTR_SNAP_NAME attribute.\n"));
213 return (BE_ERR_INVAL
);
216 ret
= _be_destroy_snapshot(be_name
, snap_name
);
224 * Function: be_rollback
225 * Description: Rolls back a BE and all of its children datasets to the
226 * named snapshot. All of the BE's datasets must have the
227 * named snapshot for this function to succeed. If the name
228 * of the BE is not passed in, this function assumes we're
229 * operating on the currently booted live BE.
231 * Note - This function does not check if the BE has any
232 * younger snapshots than the one we're trying to rollback to.
233 * If it does, then those younger snapshots and their dependent
234 * clone file systems will get destroyed in the process of
238 * be_attrs - pointer to nvlist_t of attributes being passed in.
239 * The following attributes are used by this function:
241 * BE_ATTR_ORIG_BE_NAME *optional
242 * BE_ATTR_SNAP_NAME *required
245 * BE_SUCCESS - Success
246 * be_errno_t - Failure
251 be_rollback(nvlist_t
*be_attrs
)
253 be_transaction_data_t bt
= { 0 };
254 zfs_handle_t
*zhp
= NULL
;
255 zpool_handle_t
*zphp
;
256 char obe_root_ds
[MAXPATHLEN
];
257 char *obe_name
= NULL
;
258 int zret
= 0, ret
= BE_SUCCESS
;
259 struct be_defaults be_defaults
;
261 /* Initialize libzfs handle */
263 return (BE_ERR_INIT
);
265 if ((ret
= be_find_current_be(&bt
)) != BE_SUCCESS
) {
269 /* Get original BE name if one was provided */
270 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
271 BE_ATTR_ORIG_BE_NAME
, DATA_TYPE_STRING
, &obe_name
, NULL
) != 0) {
272 be_print_err(gettext("be_rollback: "
273 "failed to lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
274 return (BE_ERR_INVAL
);
277 be_get_defaults(&be_defaults
);
279 /* If original BE name not provided, use current BE */
280 if (obe_name
!= NULL
) {
281 bt
.obe_name
= obe_name
;
282 /* Validate original BE name */
283 if (!be_valid_be_name(bt
.obe_name
)) {
284 be_print_err(gettext("be_rollback: "
285 "invalid BE name %s\n"), bt
.obe_name
);
286 return (BE_ERR_INVAL
);
290 /* Get snapshot name to rollback to */
291 if (nvlist_lookup_string(be_attrs
, BE_ATTR_SNAP_NAME
, &bt
.obe_snap_name
)
293 be_print_err(gettext("be_rollback: "
294 "failed to lookup BE_ATTR_SNAP_NAME attribute.\n"));
295 return (BE_ERR_INVAL
);
298 if (be_defaults
.be_deflt_rpool_container
) {
299 if ((zphp
= zpool_open(g_zfs
, bt
.obe_zpool
)) == NULL
) {
300 be_print_err(gettext("be_rollback: failed to "
301 "open rpool (%s): %s\n"), bt
.obe_zpool
,
302 libzfs_error_description(g_zfs
));
303 return (zfs_err_to_be_err(g_zfs
));
305 zret
= be_find_zpool_callback(zphp
, &bt
);
307 /* Find which zpool obe_name lives in */
308 if ((zret
= zpool_iter(g_zfs
, be_find_zpool_callback
, &bt
)) ==
310 be_print_err(gettext("be_rollback: "
311 "failed to find zpool for BE (%s)\n"), bt
.obe_name
);
312 return (BE_ERR_BE_NOENT
);
313 } else if (zret
< 0) {
314 be_print_err(gettext("be_rollback: "
315 "zpool_iter failed: %s\n"),
316 libzfs_error_description(g_zfs
));
317 return (zfs_err_to_be_err(g_zfs
));
321 /* Generate string for BE's root dataset */
322 be_make_root_ds(bt
.obe_zpool
, bt
.obe_name
, obe_root_ds
,
323 sizeof (obe_root_ds
));
324 bt
.obe_root_ds
= obe_root_ds
;
326 if (getzoneid() != GLOBAL_ZONEID
) {
327 if (!be_zone_compare_uuids(bt
.obe_root_ds
)) {
328 be_print_err(gettext("be_rollback: rolling back zone "
329 "root dataset from non-active global BE is not "
331 return (BE_ERR_NOTSUP
);
335 /* Get handle to BE's root dataset */
336 if ((zhp
= zfs_open(g_zfs
, bt
.obe_root_ds
, ZFS_TYPE_DATASET
)) == NULL
) {
337 be_print_err(gettext("be_rollback: "
338 "failed to open BE root dataset (%s): %s\n"),
339 bt
.obe_root_ds
, libzfs_error_description(g_zfs
));
340 return (zfs_err_to_be_err(g_zfs
));
344 * Check that snapshot name exists for this BE and all of its
345 * children file systems. This call will end up closing the
346 * zfs handle passed in whether it succeeds or fails.
348 if ((ret
= be_rollback_check_callback(zhp
, bt
.obe_snap_name
)) != 0) {
353 /* Get handle to BE's root dataset */
354 if ((zhp
= zfs_open(g_zfs
, bt
.obe_root_ds
, ZFS_TYPE_DATASET
)) == NULL
) {
355 be_print_err(gettext("be_rollback: "
356 "failed to open BE root dataset (%s): %s\n"),
357 bt
.obe_root_ds
, libzfs_error_description(g_zfs
));
358 return (zfs_err_to_be_err(g_zfs
));
362 * Iterate through a BE's datasets and roll them all back to
363 * the specified snapshot. This call will end up closing the
364 * zfs handle passed in whether it succeeds or fails.
366 if ((ret
= be_rollback_callback(zhp
, bt
.obe_snap_name
)) != 0) {
368 be_print_err(gettext("be_rollback: "
369 "failed to rollback BE %s to %s\n"), bt
.obe_name
,
379 /* ******************************************************************** */
380 /* Semi-Private Functions */
381 /* ******************************************************************** */
384 * Function: _be_create_snapshot
385 * Description: see be_create_snapshot
387 * be_name - The name of the BE that we're taking a snapshot of.
388 * snap_name - The name of the snapshot we're creating. If
389 * snap_name is NULL an auto generated name will be used,
390 * and upon success, will return that name via this
391 * reference pointer. The caller is responsible for
392 * freeing the returned name.
393 * policy - The clean-up policy type. (library wide use only)
395 * BE_SUCCESS - Success
396 * be_errno_t - Failure
398 * Semi-private (library wide use only)
401 _be_create_snapshot(char *be_name
, char **snap_name
, char *policy
)
403 be_transaction_data_t bt
= { 0 };
404 zfs_handle_t
*zhp
= NULL
;
405 nvlist_t
*ss_props
= NULL
;
407 char root_ds
[MAXPATHLEN
];
408 int pool_version
= 0;
410 int zret
= 0, ret
= BE_SUCCESS
;
411 boolean_t autoname
= B_FALSE
;
413 /* Set parameters in bt structure */
414 bt
.obe_name
= be_name
;
415 bt
.obe_snap_name
= *snap_name
;
418 /* If original BE name not supplied, use current BE */
419 if (bt
.obe_name
== NULL
) {
420 if ((ret
= be_find_current_be(&bt
)) != BE_SUCCESS
) {
425 /* Find which zpool obe_name lives in */
426 if ((zret
= zpool_iter(g_zfs
, be_find_zpool_callback
, &bt
)) == 0) {
427 be_print_err(gettext("be_create_snapshot: failed to "
428 "find zpool for BE (%s)\n"), bt
.obe_name
);
429 return (BE_ERR_BE_NOENT
);
430 } else if (zret
< 0) {
431 be_print_err(gettext("be_create_snapshot: "
432 "zpool_iter failed: %s\n"),
433 libzfs_error_description(g_zfs
));
434 return (zfs_err_to_be_err(g_zfs
));
437 be_make_root_ds(bt
.obe_zpool
, bt
.obe_name
, root_ds
,
439 bt
.obe_root_ds
= root_ds
;
441 if (getzoneid() != GLOBAL_ZONEID
) {
442 if (!be_zone_compare_uuids(bt
.obe_root_ds
)) {
443 be_print_err(gettext("be_create_snapshot: creating "
444 "snapshot for the zone root dataset from "
445 "non-active global BE is not "
447 return (BE_ERR_NOTSUP
);
451 /* If BE policy not specified, use the default policy */
452 if (bt
.policy
== NULL
) {
453 bt
.policy
= be_default_policy();
455 /* Validate policy type */
456 if (!valid_be_policy(bt
.policy
)) {
457 be_print_err(gettext("be_create_snapshot: "
458 "invalid BE policy type (%s)\n"), bt
.policy
);
459 return (BE_ERR_INVAL
);
464 * If snapshot name not specified, set auto name flag and
465 * generate auto snapshot name.
467 if (bt
.obe_snap_name
== NULL
) {
469 if ((bt
.obe_snap_name
= be_auto_snap_name())
471 be_print_err(gettext("be_create_snapshot: "
472 "failed to create auto snapshot name\n"));
473 ret
= BE_ERR_AUTONAME
;
478 /* Generate the name of the snapshot to take. */
479 (void) snprintf(ss
, sizeof (ss
), "%s@%s", bt
.obe_root_ds
,
482 /* Get handle to BE's root dataset */
483 if ((zhp
= zfs_open(g_zfs
, bt
.obe_root_ds
, ZFS_TYPE_DATASET
))
485 be_print_err(gettext("be_create_snapshot: "
486 "failed to open BE root dataset (%s): %s\n"),
487 bt
.obe_root_ds
, libzfs_error_description(g_zfs
));
488 ret
= zfs_err_to_be_err(g_zfs
);
492 /* Get the ZFS pool version of the pool where this dataset resides */
493 if (zfs_spa_version(zhp
, &pool_version
) != 0) {
494 be_print_err(gettext("be_create_snapshot: failed to "
495 "get ZFS pool version for %s: %s\n"), zfs_get_name(zhp
),
496 libzfs_error_description(g_zfs
));
500 * If ZFS pool version supports snapshot user properties, store
501 * cleanup policy there. Otherwise don't set one - this snapshot
502 * will always inherit the cleanup policy from its parent.
504 if (getzoneid() == GLOBAL_ZONEID
) {
505 if (pool_version
>= SPA_VERSION_SNAP_PROPS
) {
506 if (nvlist_alloc(&ss_props
, NV_UNIQUE_NAME
, 0) != 0) {
507 be_print_err(gettext("be_create_snapshot: "
508 "internal error: out of memory\n"));
509 return (BE_ERR_NOMEM
);
511 if (nvlist_add_string(ss_props
, BE_POLICY_PROPERTY
,
513 be_print_err(gettext("be_create_snapshot: "
514 "internal error: out of memory\n"));
515 nvlist_free(ss_props
);
516 return (BE_ERR_NOMEM
);
518 } else if (policy
!= NULL
) {
520 * If an explicit cleanup policy was requested
521 * by the caller and we don't support it, error out.
523 be_print_err(gettext("be_create_snapshot: cannot set "
524 "cleanup policy: ZFS pool version is %d\n"),
526 return (BE_ERR_NOTSUP
);
530 /* Create the snapshots recursively */
531 if (zfs_snapshot(g_zfs
, ss
, B_TRUE
, ss_props
) != 0) {
532 if (!autoname
|| libzfs_errno(g_zfs
) != EZFS_EXISTS
) {
533 be_print_err(gettext("be_create_snapshot: "
534 "recursive snapshot of %s failed: %s\n"),
535 ss
, libzfs_error_description(g_zfs
));
537 if (libzfs_errno(g_zfs
) == EZFS_EXISTS
)
538 ret
= BE_ERR_SS_EXISTS
;
540 ret
= zfs_err_to_be_err(g_zfs
);
544 for (i
= 1; i
< BE_AUTO_NAME_MAX_TRY
; i
++) {
546 /* Sleep 1 before retrying */
549 /* Generate new auto snapshot name. */
550 free(bt
.obe_snap_name
);
551 if ((bt
.obe_snap_name
=
552 be_auto_snap_name()) == NULL
) {
553 be_print_err(gettext(
554 "be_create_snapshot: failed to "
555 "create auto snapshot name\n"));
556 ret
= BE_ERR_AUTONAME
;
560 /* Generate string of the snapshot to take. */
561 (void) snprintf(ss
, sizeof (ss
), "%s@%s",
562 bt
.obe_root_ds
, bt
.obe_snap_name
);
564 /* Create the snapshots recursively */
565 if (zfs_snapshot(g_zfs
, ss
, B_TRUE
, ss_props
)
567 if (libzfs_errno(g_zfs
) !=
569 be_print_err(gettext(
570 "be_create_snapshot: "
571 "recursive snapshot of %s "
573 libzfs_error_description(
575 ret
= zfs_err_to_be_err(g_zfs
);
584 * If we exhausted the maximum number of tries,
585 * free the auto snap name and set error.
587 if (i
== BE_AUTO_NAME_MAX_TRY
) {
588 be_print_err(gettext("be_create_snapshot: "
589 "failed to create unique auto snapshot "
591 free(bt
.obe_snap_name
);
592 bt
.obe_snap_name
= NULL
;
593 ret
= BE_ERR_AUTONAME
;
599 * If we succeeded in creating an auto named snapshot, store
600 * the name in the nvlist passed in by the caller.
602 if (autoname
&& bt
.obe_snap_name
) {
603 *snap_name
= bt
.obe_snap_name
;
609 nvlist_free(ss_props
);
615 * Function: _be_destroy_snapshot
616 * Description: see be_destroy_snapshot
618 * be_name - The name of the BE that the snapshot belongs to.
619 * snap_name - The name of the snapshot we're destroying.
621 * BE_SUCCESS - Success
622 * be_errno_t - Failure
624 * Semi-private (library wide use only)
627 _be_destroy_snapshot(char *be_name
, char *snap_name
)
629 be_transaction_data_t bt
= { 0 };
632 char root_ds
[MAXPATHLEN
];
633 int err
= BE_SUCCESS
, ret
= BE_SUCCESS
;
635 /* Make sure we actaully have a snapshot name */
636 if (snap_name
== NULL
) {
637 be_print_err(gettext("be_destroy_snapshot: "
638 "invalid snapshot name\n"));
639 return (BE_ERR_INVAL
);
642 /* Set parameters in bt structure */
643 bt
.obe_name
= be_name
;
644 bt
.obe_snap_name
= snap_name
;
646 /* If original BE name not supplied, use current BE */
647 if (bt
.obe_name
== NULL
) {
648 if ((err
= be_find_current_be(&bt
)) != BE_SUCCESS
) {
653 /* Find which zpool be_name lives in */
654 if ((ret
= zpool_iter(g_zfs
, be_find_zpool_callback
, &bt
)) == 0) {
655 be_print_err(gettext("be_destroy_snapshot: "
656 "failed to find zpool for BE (%s)\n"), bt
.obe_name
);
657 return (BE_ERR_BE_NOENT
);
658 } else if (ret
< 0) {
659 be_print_err(gettext("be_destroy_snapshot: "
660 "zpool_iter failed: %s\n"),
661 libzfs_error_description(g_zfs
));
662 return (zfs_err_to_be_err(g_zfs
));
665 be_make_root_ds(bt
.obe_zpool
, bt
.obe_name
, root_ds
,
667 bt
.obe_root_ds
= root_ds
;
669 zhp
= zfs_open(g_zfs
, bt
.obe_root_ds
, ZFS_TYPE_DATASET
);
672 * The zfs_open failed, return an error.
674 be_print_err(gettext("be_destroy_snapshot: "
675 "failed to open BE root dataset (%s): %s\n"),
676 bt
.obe_root_ds
, libzfs_error_description(g_zfs
));
677 err
= zfs_err_to_be_err(g_zfs
);
680 * Generate the name of the snapshot to take.
682 (void) snprintf(ss
, sizeof (ss
), "%s@%s", bt
.obe_name
,
686 * destroy the snapshot.
689 * The boolean set to B_FALSE and passed to zfs_destroy_snaps()
690 * tells zfs to process and destroy the snapshots now.
691 * Otherwise the call will potentially return where the
692 * snapshot isn't actually destroyed yet, and ZFS is waiting
693 * until all the references to the snapshot have been
694 * released before actually destroying the snapshot.
696 if (zfs_destroy_snaps(zhp
, bt
.obe_snap_name
, B_FALSE
) != 0) {
697 err
= zfs_err_to_be_err(g_zfs
);
698 be_print_err(gettext("be_destroy_snapshot: "
699 "failed to destroy snapshot %s: %s\n"), ss
,
700 libzfs_error_description(g_zfs
));
709 /* ******************************************************************** */
710 /* Private Functions */
711 /* ******************************************************************** */
714 * Function: be_rollback_check_callback
715 * Description: Callback function used to iterate through a BE's filesystems
716 * to check if a given snapshot name exists.
718 * zhp - zfs_handle_t pointer to filesystem being processed.
719 * data - name of the snapshot to check for.
721 * 0 - Success, snapshot name exists for all filesystems.
722 * be_errno_t - Failure, snapshot name does not exist for all
728 be_rollback_check_callback(zfs_handle_t
*zhp
, void *data
)
730 char *snap_name
= data
;
732 int ret
= BE_SUCCESS
;
734 /* Generate string for this filesystem's snapshot name */
735 (void) snprintf(ss
, sizeof (ss
), "%s@%s", zfs_get_name(zhp
), snap_name
);
737 /* Check if snapshot exists */
738 if (!zfs_dataset_exists(g_zfs
, ss
, ZFS_TYPE_SNAPSHOT
)) {
739 be_print_err(gettext("be_rollback_check_callback: "
740 "snapshot does not exist %s\n"), ss
);
742 return (BE_ERR_SS_NOENT
);
745 /* Iterate this dataset's children and check them */
746 if ((ret
= zfs_iter_filesystems(zhp
, be_rollback_check_callback
,
757 * Function: be_rollback_callback
758 * Description: Callback function used to iterate through a BE's filesystems
759 * and roll them all back to the specified snapshot name.
761 * zhp - zfs_handle_t pointer to filesystem being processed.
762 * data - name of snapshot to rollback to.
765 * be_errno_t - Failure
770 be_rollback_callback(zfs_handle_t
*zhp
, void *data
)
772 zfs_handle_t
*zhp_snap
= NULL
;
773 char *snap_name
= data
;
777 /* Generate string for this filesystem's snapshot name */
778 (void) snprintf(ss
, sizeof (ss
), "%s@%s", zfs_get_name(zhp
), snap_name
);
780 /* Get handle to this filesystem's snapshot */
781 if ((zhp_snap
= zfs_open(g_zfs
, ss
, ZFS_TYPE_SNAPSHOT
)) == NULL
) {
782 be_print_err(gettext("be_rollback_callback: "
783 "failed to open snapshot %s: %s\n"), zfs_get_name(zhp
),
784 libzfs_error_description(g_zfs
));
785 ret
= zfs_err_to_be_err(g_zfs
);
790 /* Rollback dataset */
791 if (zfs_rollback(zhp
, zhp_snap
, B_FALSE
) != 0) {
792 be_print_err(gettext("be_rollback_callback: "
793 "failed to rollback BE dataset %s to snapshot %s: %s\n"),
794 zfs_get_name(zhp
), ss
, libzfs_error_description(g_zfs
));
795 ret
= zfs_err_to_be_err(g_zfs
);
802 /* Iterate this dataset's children and roll them back */
803 if ((ret
= zfs_iter_filesystems(zhp
, be_rollback_callback
,