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 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 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 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 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 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 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 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