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