1 /* 2 * be.c 3 * 4 * Copyright (c) 2017 Kyle J. Kneitinger <[email protected]> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/stat.h> 30 #include <sys/types.h> 31 32 #include <ctype.h> 33 #include <kenv.h> 34 #include <libgen.h> 35 #include <libzfs_core.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <time.h> 39 #include <unistd.h> 40 41 #include "be.h" 42 #include "be_impl.h" 43 44 /* 45 * Iterator function for locating the rootfs amongst the children of the 46 * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 47 */ 48 static int 49 be_locate_rootfs(zfs_handle_t *chkds, void *data) 50 { 51 libbe_handle_t *lbh; 52 char *mntpoint; 53 54 lbh = (libbe_handle_t *)data; 55 if (lbh == NULL) 56 return (1); 57 58 if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) { 59 strncpy(lbh->rootfs, zfs_get_name(chkds), BE_MAXPATHLEN); 60 return (1); 61 } 62 63 return (0); 64 } 65 66 /* 67 * Initializes the libbe context to operate in the root boot environment 68 * dataset, for example, zroot/ROOT. 69 */ 70 libbe_handle_t * 71 libbe_init(void) 72 { 73 struct stat sb; 74 dev_t root_dev, boot_dev; 75 libbe_handle_t *lbh; 76 zfs_handle_t *rootds; 77 char *poolname, *pos; 78 int pnamelen; 79 80 lbh = NULL; 81 poolname = pos = NULL; 82 pnamelen = 0; 83 rootds = NULL; 84 85 /* Verify that /boot and / are mounted on the same filesystem */ 86 /* TODO: use errno here?? */ 87 if (stat("/", &sb) != 0) 88 goto err; 89 90 root_dev = sb.st_dev; 91 92 if (stat("/boot", &sb) != 0) 93 goto err; 94 95 boot_dev = sb.st_dev; 96 97 if (root_dev != boot_dev) { 98 fprintf(stderr, "/ and /boot not on same device, quitting\n"); 99 goto err; 100 } 101 102 if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 103 goto err; 104 105 if ((lbh->lzh = libzfs_init()) == NULL) 106 goto err; 107 108 /* Obtain path to boot environment root */ 109 if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1) 110 goto err; 111 112 /* Remove leading 'zfs:' if present, otherwise use value as-is */ 113 if (strcmp(lbh->root, "zfs:") == 0) 114 strncpy(lbh->root, strchr(lbh->root, ':') + sizeof(char), 115 BE_MAXPATHLEN); 116 117 if ((pos = strchr(lbh->root, '/')) == NULL) 118 goto err; 119 120 pnamelen = pos - lbh->root; 121 poolname = malloc(pnamelen + 1); 122 if (poolname == NULL) 123 goto err; 124 125 strncpy(poolname, lbh->root, pnamelen); 126 poolname[pnamelen] = '\0'; 127 if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 128 goto err; 129 130 if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 131 BE_MAXPATHLEN, NULL, true) != 0) 132 goto err; 133 134 /* Obtain path to boot environment rootfs (currently booted) */ 135 /* XXX Get dataset mounted at / by kenv/GUID from mountroot? */ 136 if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL) 137 goto err; 138 139 zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); 140 zfs_close(rootds); 141 rootds = NULL; 142 if (*lbh->rootfs == '\0') 143 goto err; 144 145 return (lbh); 146 err: 147 if (lbh != NULL) { 148 if (lbh->active_phandle != NULL) 149 zpool_close(lbh->active_phandle); 150 if (lbh->lzh != NULL) 151 libzfs_fini(lbh->lzh); 152 free(lbh); 153 } 154 if (rootds != NULL) 155 zfs_close(rootds); 156 free(poolname); 157 return (NULL); 158 } 159 160 161 /* 162 * Free memory allocated by libbe_init() 163 */ 164 void 165 libbe_close(libbe_handle_t *lbh) 166 { 167 168 if (lbh->active_phandle != NULL) 169 zpool_close(lbh->active_phandle); 170 libzfs_fini(lbh->lzh); 171 free(lbh); 172 } 173 174 175 /* 176 * Destroy the boot environment or snapshot specified by the name 177 * parameter. Options are or'd together with the possible values: 178 * BE_DESTROY_FORCE : forces operation on mounted datasets 179 * TODO: Test destroying a non active but mounted be 180 */ 181 int 182 be_destroy(libbe_handle_t *lbh, char *name, int options) 183 { 184 zfs_handle_t *fs; 185 char path[BE_MAXPATHLEN]; 186 char *p; 187 int err, force, mounted; 188 189 p = path; 190 force = options & BE_DESTROY_FORCE; 191 err = BE_ERR_SUCCESS; 192 193 be_root_concat(lbh, name, path); 194 195 if (strchr(name, '@') == NULL) { 196 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 197 return (set_error(lbh, BE_ERR_NOENT)); 198 199 if (strcmp(path, lbh->rootfs) == 0) 200 return (set_error(lbh, BE_ERR_DESTROYACT)); 201 202 fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); 203 } else { 204 205 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 206 return (set_error(lbh, BE_ERR_NOENT)); 207 208 fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT); 209 } 210 211 if (fs == NULL) 212 return (set_error(lbh, BE_ERR_ZFSOPEN)); 213 214 /* Check if mounted, unmount if force is specified */ 215 if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 216 if (force) 217 zfs_unmount(fs, NULL, 0); 218 else 219 return (set_error(lbh, BE_ERR_DESTROYMNT)); 220 } 221 222 223 /* XXX TODO: convert this to use zfs_iter_children first for deep BEs */ 224 /* XXX Note: errno 16 (device busy) occurs when chilren are present */ 225 if ((err = zfs_destroy(fs, false)) != 0) 226 fprintf(stderr, "delete failed errno: %d\n", errno); 227 228 return (err); 229 } 230 231 232 int 233 be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 234 bool recursive, char *result) 235 { 236 char buf[BE_MAXPATHLEN]; 237 time_t rawtime; 238 int len, err; 239 240 be_root_concat(lbh, source, buf); 241 242 if (!be_exists(lbh, buf)) 243 return (BE_ERR_NOENT); 244 245 if (snap_name != NULL) { 246 strcat(buf, "@"); 247 strcat(buf, snap_name); 248 if (result != NULL) 249 snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 250 snap_name); 251 } else { 252 time(&rawtime); 253 len = strlen(buf); 254 strftime(buf + len, BE_MAXPATHLEN - len, 255 "@%F-%T", localtime(&rawtime)); 256 if (result != NULL) 257 strcpy(result, strrchr(buf, '/') + 1); 258 } 259 260 if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 261 switch (err) { 262 case EZFS_INVALIDNAME: 263 return (set_error(lbh, BE_ERR_INVALIDNAME)); 264 265 default: 266 /* XXX TODO: elaborate return codes */ 267 return (set_error(lbh, BE_ERR_UNKNOWN)); 268 } 269 } 270 271 return (BE_ERR_SUCCESS); 272 } 273 274 275 /* 276 * Create the boot environment specified by the name parameter 277 */ 278 int 279 be_create(libbe_handle_t *lbh, char *name) 280 { 281 int err; 282 283 err = be_create_from_existing(lbh, name, be_active_path(lbh)); 284 285 return (set_error(lbh, err)); 286 } 287 288 289 static int 290 be_deep_clone_prop(int prop, void *cb) 291 { 292 int err; 293 struct libbe_dccb *dccb; 294 zprop_source_t src; 295 char pval[BE_MAXPATHLEN]; 296 char source[BE_MAXPATHLEN]; 297 298 dccb = cb; 299 /* Skip some properties we don't want to touch */ 300 switch (prop) { 301 case ZFS_PROP_CANMOUNT: 302 return (ZPROP_CONT); 303 break; 304 } 305 306 /* Don't copy readonly properties */ 307 if (zfs_prop_readonly(prop)) 308 return (ZPROP_CONT); 309 310 if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 311 sizeof(pval), &src, (char *)&source, sizeof(source), false))) 312 /* Just continue if we fail to read a property */ 313 return (ZPROP_CONT); 314 315 /* Only copy locally defined properties */ 316 if (src != ZPROP_SRC_LOCAL) 317 return (ZPROP_CONT); 318 319 nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); 320 321 return (ZPROP_CONT); 322 } 323 324 static int 325 be_deep_clone(zfs_handle_t *ds, void *data) 326 { 327 int err; 328 char be_path[BE_MAXPATHLEN]; 329 char snap_path[BE_MAXPATHLEN]; 330 const char *dspath; 331 char *dsname; 332 zfs_handle_t *snap_hdl; 333 nvlist_t *props; 334 struct libbe_deep_clone *isdc, sdc; 335 struct libbe_dccb dccb; 336 337 isdc = (struct libbe_deep_clone *)data; 338 dspath = zfs_get_name(ds); 339 if ((dsname = strrchr(dspath, '/')) == NULL) 340 return (BE_ERR_UNKNOWN); 341 dsname++; 342 343 if (isdc->bename == NULL) 344 snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); 345 else 346 snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); 347 348 snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); 349 350 if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 351 return (set_error(isdc->lbh, BE_ERR_EXISTS)); 352 353 if ((snap_hdl = 354 zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 355 return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); 356 357 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 358 nvlist_add_string(props, "canmount", "noauto"); 359 360 dccb.zhp = ds; 361 dccb.props = props; 362 if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 363 ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 364 return (-1); 365 366 if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) { 367 switch (err) { 368 case EZFS_SUCCESS: 369 err = BE_ERR_SUCCESS; 370 break; 371 default: 372 err = BE_ERR_ZFSCLONE; 373 break; 374 } 375 } 376 377 nvlist_free(props); 378 zfs_close(snap_hdl); 379 380 sdc.lbh = isdc->lbh; 381 sdc.bename = NULL; 382 sdc.snapname = isdc->snapname; 383 sdc.be_root = (char *)&be_path; 384 385 err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); 386 387 return (err); 388 } 389 390 /* 391 * Create the boot environment from pre-existing snapshot 392 */ 393 int 394 be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, 395 const char *snap) 396 { 397 int err; 398 char be_path[BE_MAXPATHLEN]; 399 char snap_path[BE_MAXPATHLEN]; 400 const char *bename; 401 char *parentname, *snapname; 402 zfs_handle_t *parent_hdl; 403 struct libbe_deep_clone sdc; 404 405 if ((err = be_validate_name(lbh, name)) != 0) 406 return (set_error(lbh, err)); 407 if ((err = be_root_concat(lbh, snap, snap_path)) != 0) 408 return (set_error(lbh, err)); 409 if ((err = be_validate_snap(lbh, snap_path)) != 0) 410 return (set_error(lbh, err)); 411 412 if ((err = be_root_concat(lbh, name, be_path)) != 0) 413 return (set_error(lbh, err)); 414 415 if ((bename = strrchr(name, '/')) == NULL) 416 bename = name; 417 else 418 bename++; 419 420 if ((parentname = strdup(snap_path)) == NULL) { 421 err = BE_ERR_UNKNOWN; 422 return (set_error(lbh, err)); 423 } 424 snapname = strchr(parentname, '@'); 425 if (snapname == NULL) { 426 err = BE_ERR_UNKNOWN; 427 return (set_error(lbh, err)); 428 } 429 *snapname = '\0'; 430 snapname++; 431 432 sdc.lbh = lbh; 433 sdc.bename = bename; 434 sdc.snapname = snapname; 435 sdc.be_root = lbh->root; 436 437 parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 438 err = be_deep_clone(parent_hdl, &sdc); 439 440 return (set_error(lbh, err)); 441 } 442 443 444 /* 445 * Create a boot environment from an existing boot environment 446 */ 447 int 448 be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) 449 { 450 int err; 451 char buf[BE_MAXPATHLEN]; 452 453 if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf))) 454 return (set_error(lbh, err)); 455 456 err = be_create_from_existing_snap(lbh, name, (char *)buf); 457 458 return (set_error(lbh, err)); 459 } 460 461 462 /* 463 * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 464 * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 465 * failure. Does not set the internal library error state. 466 */ 467 int 468 be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 469 { 470 zfs_handle_t *zfs_hdl; 471 char buf[BE_MAXPATHLEN]; 472 char *delim_pos; 473 int err = BE_ERR_SUCCESS; 474 475 if (strlen(snap_name) >= BE_MAXPATHLEN) 476 return (BE_ERR_PATHLEN); 477 478 if (!zfs_dataset_exists(lbh->lzh, snap_name, 479 ZFS_TYPE_SNAPSHOT)) 480 return (BE_ERR_NOENT); 481 482 strncpy(buf, snap_name, BE_MAXPATHLEN); 483 484 /* Find the base filesystem of the snapshot */ 485 if ((delim_pos = strchr(buf, '@')) == NULL) 486 return (BE_ERR_INVALIDNAME); 487 *delim_pos = '\0'; 488 489 if ((zfs_hdl = 490 zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 491 return (BE_ERR_NOORIGIN); 492 493 if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN, 494 NULL, NULL, 0, 1)) != 0) 495 err = BE_ERR_INVORIGIN; 496 497 if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) 498 err = BE_ERR_INVORIGIN; 499 500 zfs_close(zfs_hdl); 501 502 return (err); 503 } 504 505 506 /* 507 * Idempotently appends the name argument to the root boot environment path 508 * and copies the resulting string into the result buffer (which is assumed 509 * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 510 * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 511 * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 512 * zfs_be_root. Does not set internal library error state. 513 */ 514 int 515 be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 516 { 517 size_t name_len, root_len; 518 519 name_len = strlen(name); 520 root_len = strlen(lbh->root); 521 522 /* Act idempotently; return be name if it is already a full path */ 523 if (strrchr(name, '/') != NULL) { 524 if (strstr(name, lbh->root) != name) 525 return (BE_ERR_INVALIDNAME); 526 527 if (name_len >= BE_MAXPATHLEN) 528 return (BE_ERR_PATHLEN); 529 530 strncpy(result, name, BE_MAXPATHLEN); 531 return (BE_ERR_SUCCESS); 532 } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 533 snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 534 name); 535 return (BE_ERR_SUCCESS); 536 } 537 538 return (BE_ERR_PATHLEN); 539 } 540 541 542 /* 543 * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 544 * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME. 545 * Does not set internal library error state. 546 */ 547 int 548 be_validate_name(libbe_handle_t *lbh __unused, const char *name) 549 { 550 for (int i = 0; *name; i++) { 551 char c = *(name++); 552 if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) 553 continue; 554 return (BE_ERR_INVALIDNAME); 555 } 556 557 return (BE_ERR_SUCCESS); 558 } 559 560 561 /* 562 * usage 563 */ 564 int 565 be_rename(libbe_handle_t *lbh, char *old, char *new) 566 { 567 char full_old[BE_MAXPATHLEN]; 568 char full_new[BE_MAXPATHLEN]; 569 zfs_handle_t *zfs_hdl; 570 int err; 571 572 if ((err = be_root_concat(lbh, old, full_old)) != 0) 573 return (set_error(lbh, err)); 574 if ((err = be_root_concat(lbh, new, full_new)) != 0) 575 return (set_error(lbh, err)); 576 577 if (be_validate_name(lbh, new) != 0) 578 return (BE_ERR_UNKNOWN); 579 /* XXX TODO set and return correct error */ 580 581 /* Check if old is active BE */ 582 if (strcmp(full_new, be_active_path(lbh)) == 0) 583 return (BE_ERR_UNKNOWN); 584 /* XXX TODO set and return correct error */ 585 586 if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 587 return (BE_ERR_UNKNOWN); 588 /* XXX TODO set and return correct error */ 589 590 if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 591 return (BE_ERR_UNKNOWN); 592 /* XXX TODO set and return correct error */ 593 594 /* XXX TODO 595 * - What about mounted BEs? 596 * - if mounted error out unless a force flag is set? 597 */ 598 if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 599 ZFS_TYPE_FILESYSTEM)) == NULL) 600 return (BE_ERR_UNKNOWN); 601 /* XXX TODO set and return correct error */ 602 603 604 /* recurse, nounmount, forceunmount */ 605 struct renameflags flags = { 0, 0, 0 }; 606 607 /* XXX TODO: error log on this call */ 608 err = zfs_rename(zfs_hdl, NULL, full_new, flags); 609 610 zfs_close(zfs_hdl); 611 612 return (set_error(lbh, err)); 613 } 614 615 616 int 617 be_export(libbe_handle_t *lbh, char *bootenv, int fd) 618 { 619 char snap_name[BE_MAXPATHLEN]; 620 char buf[BE_MAXPATHLEN]; 621 zfs_handle_t *zfs; 622 int err; 623 624 if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 625 /* XXX TODO error handle */ 626 return (-1); 627 628 be_root_concat(lbh, snap_name, buf); 629 630 if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 631 return (BE_ERR_ZFSOPEN); 632 633 err = zfs_send_one(zfs, NULL, fd, 0); 634 return (err); 635 } 636 637 638 int 639 be_import(libbe_handle_t *lbh, char *bootenv, int fd) 640 { 641 char buf[BE_MAXPATHLEN]; 642 time_t rawtime; 643 nvlist_t *props; 644 zfs_handle_t *zfs; 645 int err, len; 646 647 /* 648 * XXX TODO: this is a very likely name for someone to already have 649 * used... we should avoid it. 650 */ 651 if ((err = be_root_concat(lbh, "be_import_temp", buf)) != 0) 652 /* XXX TODO error handle */ 653 return (-1); 654 655 time(&rawtime); 656 len = strlen(buf); 657 strftime(buf + len, BE_MAXPATHLEN - len, 658 "@%F-%T", localtime(&rawtime)); 659 660 /* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */ 661 if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { 662 /* TODO: go through libzfs_core's recv_impl and find returned 663 * errors and set appropriate BE_ERR 664 * edit: errors are not in libzfs_core, my assumption is 665 * that they use libzfs errors 666 * note: 17 is err for dataset already existing 667 */ 668 return (err); 669 } 670 671 if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 672 /* XXX TODO correct error */ 673 return (-1); 674 675 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 676 nvlist_add_string(props, "canmount", "noauto"); 677 nvlist_add_string(props, "mountpoint", "/"); 678 679 be_root_concat(lbh, bootenv, buf); 680 681 err = zfs_clone(zfs, buf, props); 682 zfs_close(zfs); 683 684 nvlist_free(props); 685 686 /* XXX TODO: recursively delete be_import_temp dataset */ 687 return (err); 688 } 689 690 691 int 692 be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) 693 { 694 char active[BE_MAXPATHLEN]; 695 char buf[BE_MAXPATHLEN]; 696 nvlist_t *props; 697 zfs_handle_t *zfs; 698 struct stat sb; 699 int err; 700 701 /* Require absolute paths */ 702 if (*child_path != '/') 703 /* XXX TODO: create appropriate error */ 704 return (-1); 705 706 strncpy(active, be_active_path(lbh), BE_MAXPATHLEN); 707 strcpy(buf, active); 708 709 /* Create non-mountable parent dataset(s) */ 710 char *s = child_path; 711 for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 712 size_t len = p - s; 713 strncat(buf, s, len); 714 715 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 716 nvlist_add_string(props, "canmount", "off"); 717 nvlist_add_string(props, "mountpoint", "none"); 718 zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 719 nvlist_free(props); 720 } 721 722 723 /* Path does not exist as a descendent of / yet */ 724 int pos = strlen(active); 725 726 /* XXX TODO: Verify that resulting str is less than BE_MAXPATHLEN */ 727 strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos); 728 729 if (stat(child_path, &sb) != 0) { 730 /* Verify that error is ENOENT */ 731 if (errno != 2) 732 /* XXX TODO: create appropriate error */ 733 return (-1); 734 735 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 736 nvlist_add_string(props, "canmount", "noauto"); 737 nvlist_add_string(props, "mountpoint", child_path); 738 739 /* Create */ 740 if ((err = 741 zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0) 742 /* XXX TODO handle error */ 743 return (-1); 744 nvlist_free(props); 745 746 if ((zfs = 747 zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 748 /* XXX TODO handle error */ 749 return (-1); 750 751 /* Set props */ 752 if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) 753 /* TODO handle error */ 754 return (-1); 755 } else if (cp_if_exists) { 756 /* Path is already a descendent of / and should be copied */ 757 758 /* XXX TODO ? */ 759 760 /* 761 * Establish if the existing path is a zfs dataset or just 762 * the subdirectory of one 763 */ 764 765 /* XXX TODO: use mktemp */ 766 long int snap_name = random(); 767 768 snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); 769 770 if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) 771 /* XXX TODO correct error */ 772 return (-1); 773 774 /* Clone */ 775 if ((zfs = 776 zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 777 /* XXX TODO correct error */ 778 return (-1); 779 780 if ((err = zfs_clone(zfs, active, NULL)) != 0) 781 /* XXX TODO correct error */ 782 return (-1); 783 784 /* set props */ 785 } else 786 /* TODO: error code for exists, but not cp? */ 787 return (-1); 788 789 return (BE_ERR_SUCCESS); 790 } 791 792 793 int 794 be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) 795 { 796 char be_path[BE_MAXPATHLEN]; 797 char buf[BE_MAXPATHLEN]; 798 uint64_t pool_guid; 799 uint64_t vdev_guid; 800 int err; 801 802 be_root_concat(lbh, bootenv, be_path); 803 804 /* Note: be_exists fails if mountpoint is not / */ 805 if (!be_exists(lbh, be_path)) 806 return (BE_ERR_NOENT); 807 808 if (temporary) { 809 /* 810 * XXX TODO: give proper attribution to author(s) of zfsbootcfg 811 * for this snippet. 812 */ 813 814 if (kenv(KENV_GET, "vfs.zfs.boot.primary_pool", buf, 815 sizeof(buf)) <= 0) 816 return (1); 817 pool_guid = strtoumax(buf, NULL, 10); 818 if (pool_guid == 0) 819 return (1); 820 821 if (kenv(KENV_GET, "vfs.zfs.boot.primary_vdev", buf, 822 sizeof(buf)) <= 0) 823 return (1); 824 vdev_guid = strtoumax(buf, NULL, 10); 825 if (vdev_guid == 0) { 826 return (1); 827 } 828 829 /* Expected format according to zfsbootcfg(8) man */ 830 strcpy(buf, "zfs:"); 831 strcat(buf, be_path); 832 strcat(buf, ":"); 833 834 if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, buf) != 0) { 835 perror("ZFS_IOC_NEXTBOOT failed"); 836 return (1); 837 } 838 839 return (BE_ERR_SUCCESS); 840 } else { 841 /* Obtain bootenv zpool */ 842 err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 843 844 switch (err) { 845 case 0: 846 return (BE_ERR_SUCCESS); 847 848 default: 849 /* XXX TODO correct errors */ 850 return (-1); 851 } 852 } 853 } 854