1 /* 2 * be.c 3 * 4 * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 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 * Proxy through to libzfs for the moment. 176 */ 177 void 178 be_nicenum(uint64_t num, char *buf, size_t buflen) 179 { 180 181 zfs_nicenum(num, buf, buflen); 182 } 183 184 /* 185 * Destroy the boot environment or snapshot specified by the name 186 * parameter. Options are or'd together with the possible values: 187 * BE_DESTROY_FORCE : forces operation on mounted datasets 188 * TODO: Test destroying a non active but mounted be 189 */ 190 int 191 be_destroy(libbe_handle_t *lbh, char *name, int options) 192 { 193 zfs_handle_t *fs; 194 char path[BE_MAXPATHLEN]; 195 char *p; 196 int err, force, mounted; 197 198 p = path; 199 force = options & BE_DESTROY_FORCE; 200 err = BE_ERR_SUCCESS; 201 202 be_root_concat(lbh, name, path); 203 204 if (strchr(name, '@') == NULL) { 205 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 206 return (set_error(lbh, BE_ERR_NOENT)); 207 208 if (strcmp(path, lbh->rootfs) == 0) 209 return (set_error(lbh, BE_ERR_DESTROYACT)); 210 211 fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); 212 } else { 213 214 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 215 return (set_error(lbh, BE_ERR_NOENT)); 216 217 fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT); 218 } 219 220 if (fs == NULL) 221 return (set_error(lbh, BE_ERR_ZFSOPEN)); 222 223 /* Check if mounted, unmount if force is specified */ 224 if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 225 if (force) 226 zfs_unmount(fs, NULL, 0); 227 else 228 return (set_error(lbh, BE_ERR_DESTROYMNT)); 229 } 230 231 232 /* XXX TODO: convert this to use zfs_iter_children first for deep BEs */ 233 /* XXX Note: errno 16 (device busy) occurs when chilren are present */ 234 if ((err = zfs_destroy(fs, false)) != 0) 235 fprintf(stderr, "delete failed errno: %d\n", errno); 236 237 return (err); 238 } 239 240 241 int 242 be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 243 bool recursive, char *result) 244 { 245 char buf[BE_MAXPATHLEN]; 246 time_t rawtime; 247 int len, err; 248 249 be_root_concat(lbh, source, buf); 250 251 if (!be_exists(lbh, buf)) 252 return (BE_ERR_NOENT); 253 254 if (snap_name != NULL) { 255 strcat(buf, "@"); 256 strcat(buf, snap_name); 257 if (result != NULL) 258 snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 259 snap_name); 260 } else { 261 time(&rawtime); 262 len = strlen(buf); 263 strftime(buf + len, BE_MAXPATHLEN - len, 264 "@%F-%T", localtime(&rawtime)); 265 if (result != NULL) 266 strcpy(result, strrchr(buf, '/') + 1); 267 } 268 269 if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 270 switch (err) { 271 case EZFS_INVALIDNAME: 272 return (set_error(lbh, BE_ERR_INVALIDNAME)); 273 274 default: 275 /* XXX TODO: elaborate return codes */ 276 return (set_error(lbh, BE_ERR_UNKNOWN)); 277 } 278 } 279 280 return (BE_ERR_SUCCESS); 281 } 282 283 284 /* 285 * Create the boot environment specified by the name parameter 286 */ 287 int 288 be_create(libbe_handle_t *lbh, char *name) 289 { 290 int err; 291 292 err = be_create_from_existing(lbh, name, be_active_path(lbh)); 293 294 return (set_error(lbh, err)); 295 } 296 297 298 static int 299 be_deep_clone_prop(int prop, void *cb) 300 { 301 int err; 302 struct libbe_dccb *dccb; 303 zprop_source_t src; 304 char pval[BE_MAXPATHLEN]; 305 char source[BE_MAXPATHLEN]; 306 307 dccb = cb; 308 /* Skip some properties we don't want to touch */ 309 switch (prop) { 310 case ZFS_PROP_CANMOUNT: 311 return (ZPROP_CONT); 312 break; 313 } 314 315 /* Don't copy readonly properties */ 316 if (zfs_prop_readonly(prop)) 317 return (ZPROP_CONT); 318 319 if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 320 sizeof(pval), &src, (char *)&source, sizeof(source), false))) 321 /* Just continue if we fail to read a property */ 322 return (ZPROP_CONT); 323 324 /* Only copy locally defined properties */ 325 if (src != ZPROP_SRC_LOCAL) 326 return (ZPROP_CONT); 327 328 nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); 329 330 return (ZPROP_CONT); 331 } 332 333 static int 334 be_deep_clone(zfs_handle_t *ds, void *data) 335 { 336 int err; 337 char be_path[BE_MAXPATHLEN]; 338 char snap_path[BE_MAXPATHLEN]; 339 const char *dspath; 340 char *dsname; 341 zfs_handle_t *snap_hdl; 342 nvlist_t *props; 343 struct libbe_deep_clone *isdc, sdc; 344 struct libbe_dccb dccb; 345 346 isdc = (struct libbe_deep_clone *)data; 347 dspath = zfs_get_name(ds); 348 if ((dsname = strrchr(dspath, '/')) == NULL) 349 return (BE_ERR_UNKNOWN); 350 dsname++; 351 352 if (isdc->bename == NULL) 353 snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); 354 else 355 snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); 356 357 snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); 358 359 if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 360 return (set_error(isdc->lbh, BE_ERR_EXISTS)); 361 362 if ((snap_hdl = 363 zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 364 return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); 365 366 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 367 nvlist_add_string(props, "canmount", "noauto"); 368 369 dccb.zhp = ds; 370 dccb.props = props; 371 if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 372 ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 373 return (-1); 374 375 if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) { 376 switch (err) { 377 case EZFS_SUCCESS: 378 err = BE_ERR_SUCCESS; 379 break; 380 default: 381 err = BE_ERR_ZFSCLONE; 382 break; 383 } 384 } 385 386 nvlist_free(props); 387 zfs_close(snap_hdl); 388 389 sdc.lbh = isdc->lbh; 390 sdc.bename = NULL; 391 sdc.snapname = isdc->snapname; 392 sdc.be_root = (char *)&be_path; 393 394 err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); 395 396 return (err); 397 } 398 399 /* 400 * Create the boot environment from pre-existing snapshot 401 */ 402 int 403 be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, 404 const char *snap) 405 { 406 int err; 407 char be_path[BE_MAXPATHLEN]; 408 char snap_path[BE_MAXPATHLEN]; 409 const char *bename; 410 char *parentname, *snapname; 411 zfs_handle_t *parent_hdl; 412 struct libbe_deep_clone sdc; 413 414 if ((err = be_validate_name(lbh, name)) != 0) 415 return (set_error(lbh, err)); 416 if ((err = be_root_concat(lbh, snap, snap_path)) != 0) 417 return (set_error(lbh, err)); 418 if ((err = be_validate_snap(lbh, snap_path)) != 0) 419 return (set_error(lbh, err)); 420 421 if ((err = be_root_concat(lbh, name, be_path)) != 0) 422 return (set_error(lbh, err)); 423 424 if ((bename = strrchr(name, '/')) == NULL) 425 bename = name; 426 else 427 bename++; 428 429 if ((parentname = strdup(snap_path)) == NULL) { 430 err = BE_ERR_UNKNOWN; 431 return (set_error(lbh, err)); 432 } 433 snapname = strchr(parentname, '@'); 434 if (snapname == NULL) { 435 err = BE_ERR_UNKNOWN; 436 return (set_error(lbh, err)); 437 } 438 *snapname = '\0'; 439 snapname++; 440 441 sdc.lbh = lbh; 442 sdc.bename = bename; 443 sdc.snapname = snapname; 444 sdc.be_root = lbh->root; 445 446 parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 447 err = be_deep_clone(parent_hdl, &sdc); 448 449 return (set_error(lbh, err)); 450 } 451 452 453 /* 454 * Create a boot environment from an existing boot environment 455 */ 456 int 457 be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) 458 { 459 int err; 460 char buf[BE_MAXPATHLEN]; 461 462 if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf))) 463 return (set_error(lbh, err)); 464 465 err = be_create_from_existing_snap(lbh, name, (char *)buf); 466 467 return (set_error(lbh, err)); 468 } 469 470 471 /* 472 * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 473 * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 474 * failure. Does not set the internal library error state. 475 */ 476 int 477 be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 478 { 479 zfs_handle_t *zfs_hdl; 480 char buf[BE_MAXPATHLEN]; 481 char *delim_pos; 482 int err = BE_ERR_SUCCESS; 483 484 if (strlen(snap_name) >= BE_MAXPATHLEN) 485 return (BE_ERR_PATHLEN); 486 487 if (!zfs_dataset_exists(lbh->lzh, snap_name, 488 ZFS_TYPE_SNAPSHOT)) 489 return (BE_ERR_NOENT); 490 491 strncpy(buf, snap_name, BE_MAXPATHLEN); 492 493 /* Find the base filesystem of the snapshot */ 494 if ((delim_pos = strchr(buf, '@')) == NULL) 495 return (BE_ERR_INVALIDNAME); 496 *delim_pos = '\0'; 497 498 if ((zfs_hdl = 499 zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 500 return (BE_ERR_NOORIGIN); 501 502 if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN, 503 NULL, NULL, 0, 1)) != 0) 504 err = BE_ERR_INVORIGIN; 505 506 if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) 507 err = BE_ERR_INVORIGIN; 508 509 zfs_close(zfs_hdl); 510 511 return (err); 512 } 513 514 515 /* 516 * Idempotently appends the name argument to the root boot environment path 517 * and copies the resulting string into the result buffer (which is assumed 518 * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 519 * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 520 * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 521 * zfs_be_root. Does not set internal library error state. 522 */ 523 int 524 be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 525 { 526 size_t name_len, root_len; 527 528 name_len = strlen(name); 529 root_len = strlen(lbh->root); 530 531 /* Act idempotently; return be name if it is already a full path */ 532 if (strrchr(name, '/') != NULL) { 533 if (strstr(name, lbh->root) != name) 534 return (BE_ERR_INVALIDNAME); 535 536 if (name_len >= BE_MAXPATHLEN) 537 return (BE_ERR_PATHLEN); 538 539 strncpy(result, name, BE_MAXPATHLEN); 540 return (BE_ERR_SUCCESS); 541 } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 542 snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 543 name); 544 return (BE_ERR_SUCCESS); 545 } 546 547 return (BE_ERR_PATHLEN); 548 } 549 550 551 /* 552 * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 553 * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME. 554 * Does not set internal library error state. 555 */ 556 int 557 be_validate_name(libbe_handle_t *lbh __unused, const char *name) 558 { 559 for (int i = 0; *name; i++) { 560 char c = *(name++); 561 if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) 562 continue; 563 return (BE_ERR_INVALIDNAME); 564 } 565 566 return (BE_ERR_SUCCESS); 567 } 568 569 570 /* 571 * usage 572 */ 573 int 574 be_rename(libbe_handle_t *lbh, char *old, char *new) 575 { 576 char full_old[BE_MAXPATHLEN]; 577 char full_new[BE_MAXPATHLEN]; 578 zfs_handle_t *zfs_hdl; 579 int err; 580 581 if ((err = be_root_concat(lbh, old, full_old)) != 0) 582 return (set_error(lbh, err)); 583 if ((err = be_root_concat(lbh, new, full_new)) != 0) 584 return (set_error(lbh, err)); 585 586 if (be_validate_name(lbh, new) != 0) 587 return (BE_ERR_UNKNOWN); 588 /* XXX TODO set and return correct error */ 589 590 /* Check if old is active BE */ 591 if (strcmp(full_new, be_active_path(lbh)) == 0) 592 return (BE_ERR_UNKNOWN); 593 /* XXX TODO set and return correct error */ 594 595 if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 596 return (BE_ERR_UNKNOWN); 597 /* XXX TODO set and return correct error */ 598 599 if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 600 return (BE_ERR_UNKNOWN); 601 /* XXX TODO set and return correct error */ 602 603 /* XXX TODO 604 * - What about mounted BEs? 605 * - if mounted error out unless a force flag is set? 606 */ 607 if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 608 ZFS_TYPE_FILESYSTEM)) == NULL) 609 return (BE_ERR_UNKNOWN); 610 /* XXX TODO set and return correct error */ 611 612 613 /* recurse, nounmount, forceunmount */ 614 struct renameflags flags = { 0, 0, 0 }; 615 616 /* XXX TODO: error log on this call */ 617 err = zfs_rename(zfs_hdl, NULL, full_new, flags); 618 619 zfs_close(zfs_hdl); 620 621 return (set_error(lbh, err)); 622 } 623 624 625 int 626 be_export(libbe_handle_t *lbh, char *bootenv, int fd) 627 { 628 char snap_name[BE_MAXPATHLEN]; 629 char buf[BE_MAXPATHLEN]; 630 zfs_handle_t *zfs; 631 int err; 632 633 if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 634 /* XXX TODO error handle */ 635 return (-1); 636 637 be_root_concat(lbh, snap_name, buf); 638 639 if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 640 return (BE_ERR_ZFSOPEN); 641 642 err = zfs_send_one(zfs, NULL, fd, 0); 643 return (err); 644 } 645 646 647 int 648 be_import(libbe_handle_t *lbh, char *bootenv, int fd) 649 { 650 char buf[BE_MAXPATHLEN]; 651 time_t rawtime; 652 nvlist_t *props; 653 zfs_handle_t *zfs; 654 int err, len; 655 656 /* 657 * XXX TODO: this is a very likely name for someone to already have 658 * used... we should avoid it. 659 */ 660 if ((err = be_root_concat(lbh, "libbe_import_temp", buf)) != 0) 661 /* XXX TODO error handle */ 662 return (-1); 663 664 time(&rawtime); 665 len = strlen(buf); 666 strftime(buf + len, BE_MAXPATHLEN - len, 667 "@%F-%T", localtime(&rawtime)); 668 669 /* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */ 670 if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { 671 /* TODO: go through libzfs_core's recv_impl and find returned 672 * errors and set appropriate BE_ERR 673 * edit: errors are not in libzfs_core, my assumption is 674 * that they use libzfs errors 675 * note: 17 is err for dataset already existing 676 */ 677 return (err); 678 } 679 680 if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 681 /* XXX TODO correct error */ 682 return (-1); 683 684 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 685 nvlist_add_string(props, "canmount", "noauto"); 686 nvlist_add_string(props, "mountpoint", "/"); 687 688 be_root_concat(lbh, bootenv, buf); 689 690 err = zfs_clone(zfs, buf, props); 691 zfs_close(zfs); 692 693 nvlist_free(props); 694 695 /* XXX TODO: recursively delete be_import_temp dataset */ 696 return (err); 697 } 698 699 700 int 701 be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) 702 { 703 char active[BE_MAXPATHLEN]; 704 char buf[BE_MAXPATHLEN]; 705 nvlist_t *props; 706 zfs_handle_t *zfs; 707 struct stat sb; 708 int err; 709 710 /* Require absolute paths */ 711 if (*child_path != '/') 712 /* XXX TODO: create appropriate error */ 713 return (-1); 714 715 strncpy(active, be_active_path(lbh), BE_MAXPATHLEN); 716 strcpy(buf, active); 717 718 /* Create non-mountable parent dataset(s) */ 719 char *s = child_path; 720 for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 721 size_t len = p - s; 722 strncat(buf, s, len); 723 724 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 725 nvlist_add_string(props, "canmount", "off"); 726 nvlist_add_string(props, "mountpoint", "none"); 727 zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 728 nvlist_free(props); 729 } 730 731 732 /* Path does not exist as a descendent of / yet */ 733 int pos = strlen(active); 734 735 /* XXX TODO: Verify that resulting str is less than BE_MAXPATHLEN */ 736 strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos); 737 738 if (stat(child_path, &sb) != 0) { 739 /* Verify that error is ENOENT */ 740 if (errno != 2) 741 /* XXX TODO: create appropriate error */ 742 return (-1); 743 744 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 745 nvlist_add_string(props, "canmount", "noauto"); 746 nvlist_add_string(props, "mountpoint", child_path); 747 748 /* Create */ 749 if ((err = 750 zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0) 751 /* XXX TODO handle error */ 752 return (-1); 753 nvlist_free(props); 754 755 if ((zfs = 756 zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 757 /* XXX TODO handle error */ 758 return (-1); 759 760 /* Set props */ 761 if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) 762 /* TODO handle error */ 763 return (-1); 764 } else if (cp_if_exists) { 765 /* Path is already a descendent of / and should be copied */ 766 767 /* XXX TODO ? */ 768 769 /* 770 * Establish if the existing path is a zfs dataset or just 771 * the subdirectory of one 772 */ 773 774 /* XXX TODO: use mktemp */ 775 long int snap_name = random(); 776 777 snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); 778 779 if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) 780 /* XXX TODO correct error */ 781 return (-1); 782 783 /* Clone */ 784 if ((zfs = 785 zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 786 /* XXX TODO correct error */ 787 return (-1); 788 789 if ((err = zfs_clone(zfs, active, NULL)) != 0) 790 /* XXX TODO correct error */ 791 return (-1); 792 793 /* set props */ 794 } else 795 /* TODO: error code for exists, but not cp? */ 796 return (-1); 797 798 return (BE_ERR_SUCCESS); 799 } 800 801 static int 802 be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 803 const char *zfsdev) 804 { 805 nvlist_t **child; 806 uint64_t vdev_guid; 807 int c, children; 808 809 if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 810 &children) == 0) { 811 for (c = 0; c < children; ++c) 812 if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 813 return (1); 814 return (0); 815 } 816 817 if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 818 &vdev_guid) != 0) { 819 return (1); 820 } 821 822 if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 823 perror("ZFS_IOC_NEXTBOOT failed"); 824 return (1); 825 } 826 827 return (0); 828 } 829 830 831 int 832 be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) 833 { 834 char be_path[BE_MAXPATHLEN]; 835 char buf[BE_MAXPATHLEN]; 836 uint64_t pool_guid; 837 nvlist_t *config, *vdevs; 838 int err; 839 840 be_root_concat(lbh, bootenv, be_path); 841 842 /* Note: be_exists fails if mountpoint is not / */ 843 if (!be_exists(lbh, be_path)) 844 return (BE_ERR_NOENT); 845 846 if (temporary) { 847 config = zpool_get_config(lbh->active_phandle, NULL); 848 if (config == NULL) { 849 printf("no config\n"); 850 return (1); 851 } 852 853 if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 854 &pool_guid) != 0) 855 return (1); 856 857 /* Expected format according to zfsbootcfg(8) man */ 858 strcpy(buf, "zfs:"); 859 strcat(buf, be_path); 860 strcat(buf, ":"); 861 862 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdevs) != 0) 863 return (1); 864 865 return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 866 } else { 867 /* Obtain bootenv zpool */ 868 err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 869 870 switch (err) { 871 case 0: 872 return (BE_ERR_SUCCESS); 873 874 default: 875 /* XXX TODO correct errors */ 876 return (-1); 877 } 878 } 879 } 880