1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/stat.h> 33 #include <sys/types.h> 34 35 #include <ctype.h> 36 #include <kenv.h> 37 #include <libgen.h> 38 #include <libzfs_core.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <time.h> 42 #include <unistd.h> 43 44 #include "be.h" 45 #include "be_impl.h" 46 47 /* 48 * Iterator function for locating the rootfs amongst the children of the 49 * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 50 */ 51 static int 52 be_locate_rootfs(zfs_handle_t *chkds, void *data) 53 { 54 libbe_handle_t *lbh; 55 char *mntpoint; 56 57 lbh = (libbe_handle_t *)data; 58 if (lbh == NULL) 59 return (1); 60 61 if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) { 62 strncpy(lbh->rootfs, zfs_get_name(chkds), BE_MAXPATHLEN); 63 return (1); 64 } 65 66 return (0); 67 } 68 69 /* 70 * Initializes the libbe context to operate in the root boot environment 71 * dataset, for example, zroot/ROOT. 72 */ 73 libbe_handle_t * 74 libbe_init(void) 75 { 76 struct stat sb; 77 dev_t root_dev, boot_dev; 78 libbe_handle_t *lbh; 79 zfs_handle_t *rootds; 80 char *poolname, *pos; 81 int pnamelen; 82 83 lbh = NULL; 84 poolname = pos = NULL; 85 pnamelen = 0; 86 rootds = NULL; 87 88 /* Verify that /boot and / are mounted on the same filesystem */ 89 /* TODO: use errno here?? */ 90 if (stat("/", &sb) != 0) 91 goto err; 92 93 root_dev = sb.st_dev; 94 95 if (stat("/boot", &sb) != 0) 96 goto err; 97 98 boot_dev = sb.st_dev; 99 100 if (root_dev != boot_dev) { 101 fprintf(stderr, "/ and /boot not on same device, quitting\n"); 102 goto err; 103 } 104 105 if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 106 goto err; 107 108 if ((lbh->lzh = libzfs_init()) == NULL) 109 goto err; 110 111 /* Obtain path to boot environment root */ 112 if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1) 113 goto err; 114 115 /* Remove leading 'zfs:' if present, otherwise use value as-is */ 116 if (strcmp(lbh->root, "zfs:") == 0) 117 strncpy(lbh->root, strchr(lbh->root, ':') + sizeof(char), 118 BE_MAXPATHLEN); 119 120 if ((pos = strchr(lbh->root, '/')) == NULL) 121 goto err; 122 123 pnamelen = pos - lbh->root; 124 poolname = malloc(pnamelen + 1); 125 if (poolname == NULL) 126 goto err; 127 128 strncpy(poolname, lbh->root, pnamelen); 129 poolname[pnamelen] = '\0'; 130 if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 131 goto err; 132 133 if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 134 BE_MAXPATHLEN, NULL, true) != 0) 135 goto err; 136 137 /* Obtain path to boot environment rootfs (currently booted) */ 138 /* XXX Get dataset mounted at / by kenv/GUID from mountroot? */ 139 if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL) 140 goto err; 141 142 zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); 143 zfs_close(rootds); 144 rootds = NULL; 145 if (*lbh->rootfs == '\0') 146 goto err; 147 148 return (lbh); 149 err: 150 if (lbh != NULL) { 151 if (lbh->active_phandle != NULL) 152 zpool_close(lbh->active_phandle); 153 if (lbh->lzh != NULL) 154 libzfs_fini(lbh->lzh); 155 free(lbh); 156 } 157 if (rootds != NULL) 158 zfs_close(rootds); 159 free(poolname); 160 return (NULL); 161 } 162 163 164 /* 165 * Free memory allocated by libbe_init() 166 */ 167 void 168 libbe_close(libbe_handle_t *lbh) 169 { 170 171 if (lbh->active_phandle != NULL) 172 zpool_close(lbh->active_phandle); 173 libzfs_fini(lbh->lzh); 174 free(lbh); 175 } 176 177 /* 178 * Proxy through to libzfs for the moment. 179 */ 180 void 181 be_nicenum(uint64_t num, char *buf, size_t buflen) 182 { 183 184 zfs_nicenum(num, buf, buflen); 185 } 186 187 static int 188 be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 189 { 190 int err; 191 192 if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0) 193 return (err); 194 if ((err = zfs_destroy(zfs_hdl, false)) != 0) 195 return (err); 196 return (0); 197 } 198 199 /* 200 * Destroy the boot environment or snapshot specified by the name 201 * parameter. Options are or'd together with the possible values: 202 * BE_DESTROY_FORCE : forces operation on mounted datasets 203 */ 204 int 205 be_destroy(libbe_handle_t *lbh, const char *name, int options) 206 { 207 zfs_handle_t *fs; 208 char path[BE_MAXPATHLEN]; 209 char *p; 210 int err, force, mounted; 211 212 p = path; 213 force = options & BE_DESTROY_FORCE; 214 err = BE_ERR_SUCCESS; 215 216 be_root_concat(lbh, name, path); 217 218 if (strchr(name, '@') == NULL) { 219 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 220 return (set_error(lbh, BE_ERR_NOENT)); 221 222 if (strcmp(path, lbh->rootfs) == 0) 223 return (set_error(lbh, BE_ERR_DESTROYACT)); 224 225 fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); 226 } else { 227 228 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 229 return (set_error(lbh, BE_ERR_NOENT)); 230 231 fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT); 232 } 233 234 if (fs == NULL) 235 return (set_error(lbh, BE_ERR_ZFSOPEN)); 236 237 /* Check if mounted, unmount if force is specified */ 238 if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 239 if (force) 240 zfs_unmount(fs, NULL, 0); 241 else 242 return (set_error(lbh, BE_ERR_DESTROYMNT)); 243 } 244 245 if ((err = be_destroy_cb(fs, NULL)) != 0) { 246 /* Children are still present or the mount is referenced */ 247 if (err == EBUSY) 248 return (set_error(lbh, BE_ERR_DESTROYMNT)); 249 return (set_error(lbh, BE_ERR_UNKNOWN)); 250 } 251 252 return (0); 253 } 254 255 256 int 257 be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 258 bool recursive, char *result) 259 { 260 char buf[BE_MAXPATHLEN]; 261 time_t rawtime; 262 int len, err; 263 264 be_root_concat(lbh, source, buf); 265 266 if (!be_exists(lbh, buf)) 267 return (BE_ERR_NOENT); 268 269 if (snap_name != NULL) { 270 strcat(buf, "@"); 271 strcat(buf, snap_name); 272 if (result != NULL) 273 snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 274 snap_name); 275 } else { 276 time(&rawtime); 277 len = strlen(buf); 278 strftime(buf + len, BE_MAXPATHLEN - len, 279 "@%F-%T", localtime(&rawtime)); 280 if (result != NULL) 281 strcpy(result, strrchr(buf, '/') + 1); 282 } 283 284 if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 285 switch (err) { 286 case EZFS_INVALIDNAME: 287 return (set_error(lbh, BE_ERR_INVALIDNAME)); 288 289 default: 290 /* 291 * The other errors that zfs_ioc_snapshot might return 292 * shouldn't happen if we've set things up properly, so 293 * we'll gloss over them and call it UNKNOWN as it will 294 * require further triage. 295 */ 296 if (errno == ENOTSUP) 297 return (set_error(lbh, BE_ERR_NOPOOL)); 298 return (set_error(lbh, BE_ERR_UNKNOWN)); 299 } 300 } 301 302 return (BE_ERR_SUCCESS); 303 } 304 305 306 /* 307 * Create the boot environment specified by the name parameter 308 */ 309 int 310 be_create(libbe_handle_t *lbh, const char *name) 311 { 312 int err; 313 314 err = be_create_from_existing(lbh, name, be_active_path(lbh)); 315 316 return (set_error(lbh, err)); 317 } 318 319 320 static int 321 be_deep_clone_prop(int prop, void *cb) 322 { 323 int err; 324 struct libbe_dccb *dccb; 325 zprop_source_t src; 326 char pval[BE_MAXPATHLEN]; 327 char source[BE_MAXPATHLEN]; 328 329 dccb = cb; 330 /* Skip some properties we don't want to touch */ 331 if (prop == ZFS_PROP_CANMOUNT) 332 return (ZPROP_CONT); 333 334 /* Don't copy readonly properties */ 335 if (zfs_prop_readonly(prop)) 336 return (ZPROP_CONT); 337 338 if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 339 sizeof(pval), &src, (char *)&source, sizeof(source), false))) 340 /* Just continue if we fail to read a property */ 341 return (ZPROP_CONT); 342 343 /* Only copy locally defined properties */ 344 if (src != ZPROP_SRC_LOCAL) 345 return (ZPROP_CONT); 346 347 nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); 348 349 return (ZPROP_CONT); 350 } 351 352 static int 353 be_deep_clone(zfs_handle_t *ds, void *data) 354 { 355 int err; 356 char be_path[BE_MAXPATHLEN]; 357 char snap_path[BE_MAXPATHLEN]; 358 const char *dspath; 359 char *dsname; 360 zfs_handle_t *snap_hdl; 361 nvlist_t *props; 362 struct libbe_deep_clone *isdc, sdc; 363 struct libbe_dccb dccb; 364 365 isdc = (struct libbe_deep_clone *)data; 366 dspath = zfs_get_name(ds); 367 if ((dsname = strrchr(dspath, '/')) == NULL) 368 return (BE_ERR_UNKNOWN); 369 dsname++; 370 371 if (isdc->bename == NULL) 372 snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); 373 else 374 snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); 375 376 snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); 377 378 if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 379 return (set_error(isdc->lbh, BE_ERR_EXISTS)); 380 381 if ((snap_hdl = 382 zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 383 return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); 384 385 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 386 nvlist_add_string(props, "canmount", "noauto"); 387 388 dccb.zhp = ds; 389 dccb.props = props; 390 if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 391 ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 392 return (-1); 393 394 if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) { 395 switch (err) { 396 case EZFS_SUCCESS: 397 err = BE_ERR_SUCCESS; 398 break; 399 default: 400 err = BE_ERR_ZFSCLONE; 401 break; 402 } 403 } 404 405 nvlist_free(props); 406 zfs_close(snap_hdl); 407 408 sdc.lbh = isdc->lbh; 409 sdc.bename = NULL; 410 sdc.snapname = isdc->snapname; 411 sdc.be_root = (char *)&be_path; 412 413 err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); 414 415 return (err); 416 } 417 418 /* 419 * Create the boot environment from pre-existing snapshot 420 */ 421 int 422 be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, 423 const char *snap) 424 { 425 int err; 426 char be_path[BE_MAXPATHLEN]; 427 char snap_path[BE_MAXPATHLEN]; 428 const char *bename; 429 char *parentname, *snapname; 430 zfs_handle_t *parent_hdl; 431 struct libbe_deep_clone sdc; 432 433 if ((err = be_validate_name(lbh, name)) != 0) 434 return (set_error(lbh, err)); 435 if ((err = be_root_concat(lbh, snap, snap_path)) != 0) 436 return (set_error(lbh, err)); 437 if ((err = be_validate_snap(lbh, snap_path)) != 0) 438 return (set_error(lbh, err)); 439 440 if ((err = be_root_concat(lbh, name, be_path)) != 0) 441 return (set_error(lbh, err)); 442 443 if ((bename = strrchr(name, '/')) == NULL) 444 bename = name; 445 else 446 bename++; 447 448 if ((parentname = strdup(snap_path)) == NULL) { 449 err = BE_ERR_UNKNOWN; 450 return (set_error(lbh, err)); 451 } 452 snapname = strchr(parentname, '@'); 453 if (snapname == NULL) { 454 err = BE_ERR_UNKNOWN; 455 return (set_error(lbh, err)); 456 } 457 *snapname = '\0'; 458 snapname++; 459 460 sdc.lbh = lbh; 461 sdc.bename = bename; 462 sdc.snapname = snapname; 463 sdc.be_root = lbh->root; 464 465 parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 466 err = be_deep_clone(parent_hdl, &sdc); 467 468 return (set_error(lbh, err)); 469 } 470 471 472 /* 473 * Create a boot environment from an existing boot environment 474 */ 475 int 476 be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) 477 { 478 int err; 479 char buf[BE_MAXPATHLEN]; 480 481 if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf))) 482 return (set_error(lbh, err)); 483 484 err = be_create_from_existing_snap(lbh, name, (char *)buf); 485 486 return (set_error(lbh, err)); 487 } 488 489 490 /* 491 * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 492 * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 493 * failure. Does not set the internal library error state. 494 */ 495 int 496 be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 497 { 498 zfs_handle_t *zfs_hdl; 499 char buf[BE_MAXPATHLEN]; 500 char *delim_pos; 501 int err = BE_ERR_SUCCESS; 502 503 if (strlen(snap_name) >= BE_MAXPATHLEN) 504 return (BE_ERR_PATHLEN); 505 506 if (!zfs_dataset_exists(lbh->lzh, snap_name, 507 ZFS_TYPE_SNAPSHOT)) 508 return (BE_ERR_NOENT); 509 510 strncpy(buf, snap_name, BE_MAXPATHLEN); 511 512 /* Find the base filesystem of the snapshot */ 513 if ((delim_pos = strchr(buf, '@')) == NULL) 514 return (BE_ERR_INVALIDNAME); 515 *delim_pos = '\0'; 516 517 if ((zfs_hdl = 518 zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 519 return (BE_ERR_NOORIGIN); 520 521 if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN, 522 NULL, NULL, 0, 1)) != 0) 523 err = BE_ERR_INVORIGIN; 524 525 if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) 526 err = BE_ERR_INVORIGIN; 527 528 zfs_close(zfs_hdl); 529 530 return (err); 531 } 532 533 534 /* 535 * Idempotently appends the name argument to the root boot environment path 536 * and copies the resulting string into the result buffer (which is assumed 537 * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 538 * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 539 * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 540 * zfs_be_root. Does not set internal library error state. 541 */ 542 int 543 be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 544 { 545 size_t name_len, root_len; 546 547 name_len = strlen(name); 548 root_len = strlen(lbh->root); 549 550 /* Act idempotently; return be name if it is already a full path */ 551 if (strrchr(name, '/') != NULL) { 552 if (strstr(name, lbh->root) != name) 553 return (BE_ERR_INVALIDNAME); 554 555 if (name_len >= BE_MAXPATHLEN) 556 return (BE_ERR_PATHLEN); 557 558 strncpy(result, name, BE_MAXPATHLEN); 559 return (BE_ERR_SUCCESS); 560 } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 561 snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 562 name); 563 return (BE_ERR_SUCCESS); 564 } 565 566 return (BE_ERR_PATHLEN); 567 } 568 569 570 /* 571 * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 572 * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME. 573 * Does not set internal library error state. 574 */ 575 int 576 be_validate_name(libbe_handle_t *lbh __unused, const char *name) 577 { 578 for (int i = 0; *name; i++) { 579 char c = *(name++); 580 if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) 581 continue; 582 return (BE_ERR_INVALIDNAME); 583 } 584 585 return (BE_ERR_SUCCESS); 586 } 587 588 589 /* 590 * usage 591 */ 592 int 593 be_rename(libbe_handle_t *lbh, const char *old, const char *new) 594 { 595 char full_old[BE_MAXPATHLEN]; 596 char full_new[BE_MAXPATHLEN]; 597 zfs_handle_t *zfs_hdl; 598 int err; 599 600 if ((err = be_root_concat(lbh, old, full_old)) != 0) 601 return (set_error(lbh, err)); 602 if ((err = be_root_concat(lbh, new, full_new)) != 0) 603 return (set_error(lbh, err)); 604 605 if ((err = be_validate_name(lbh, new)) != 0) 606 return (err); 607 608 /* Check if old is active BE */ 609 if (strcmp(full_old, be_active_path(lbh)) == 0) 610 return (set_error(lbh, BE_ERR_MOUNTED)); 611 612 if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 613 return (set_error(lbh, BE_ERR_NOENT)); 614 615 if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 616 return (set_error(lbh, BE_ERR_EXISTS)); 617 618 if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 619 ZFS_TYPE_FILESYSTEM)) == NULL) 620 return (set_error(lbh, BE_ERR_ZFSOPEN)); 621 622 /* XXX TODO: Allow a force flag */ 623 if (zfs_is_mounted(zfs_hdl, NULL)) { 624 zfs_close(zfs_hdl); 625 return (set_error(lbh, BE_ERR_MOUNTED)); 626 } 627 628 /* recurse, nounmount, forceunmount */ 629 struct renameflags flags = { 0, 0, 0 }; 630 631 err = zfs_rename(zfs_hdl, NULL, full_new, flags); 632 633 zfs_close(zfs_hdl); 634 635 return (set_error(lbh, err)); 636 } 637 638 639 int 640 be_export(libbe_handle_t *lbh, const char *bootenv, int fd) 641 { 642 char snap_name[BE_MAXPATHLEN]; 643 char buf[BE_MAXPATHLEN]; 644 zfs_handle_t *zfs; 645 int err; 646 647 if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 648 /* Use the error set by be_snapshot */ 649 return (err); 650 651 be_root_concat(lbh, snap_name, buf); 652 653 if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 654 return (set_error(lbh, BE_ERR_ZFSOPEN)); 655 656 err = zfs_send_one(zfs, NULL, fd, 0); 657 zfs_close(zfs); 658 659 return (err); 660 } 661 662 663 int 664 be_import(libbe_handle_t *lbh, const char *bootenv, int fd) 665 { 666 char buf[BE_MAXPATHLEN]; 667 time_t rawtime; 668 nvlist_t *props; 669 zfs_handle_t *zfs; 670 int err, len; 671 char nbuf[24]; 672 673 /* 674 * We don't need this to be incredibly random, just unique enough that 675 * it won't conflict with an existing dataset name. Chopping time 676 * down to 32 bits is probably good enough for this. 677 */ 678 snprintf(nbuf, 24, "tmp%u", 679 (uint32_t)(time(NULL) & 0xFFFFFFFF)); 680 if ((err = be_root_concat(lbh, nbuf, buf)) != 0) 681 /* 682 * Technically this is our problem, but we try to use short 683 * enough names that we won't run into problems except in 684 * worst-case BE root approaching MAXPATHLEN. 685 */ 686 return (set_error(lbh, BE_ERR_PATHLEN)); 687 688 time(&rawtime); 689 len = strlen(buf); 690 strftime(buf + len, BE_MAXPATHLEN - len, 691 "@%F-%T", localtime(&rawtime)); 692 693 /* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */ 694 if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { 695 switch (err) { 696 case EINVAL: 697 return (set_error(lbh, BE_ERR_NOORIGIN)); 698 case ENOENT: 699 return (set_error(lbh, BE_ERR_NOENT)); 700 case EIO: 701 return (set_error(lbh, BE_ERR_IO)); 702 default: 703 return (set_error(lbh, BE_ERR_UNKNOWN)); 704 } 705 } 706 707 if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 708 return (set_error(lbh, BE_ERR_ZFSOPEN)); 709 710 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 711 nvlist_add_string(props, "canmount", "noauto"); 712 nvlist_add_string(props, "mountpoint", "/"); 713 714 be_root_concat(lbh, bootenv, buf); 715 716 err = zfs_clone(zfs, buf, props); 717 zfs_close(zfs); 718 719 nvlist_free(props); 720 721 return (be_destroy(lbh, nbuf, 0)); 722 } 723 724 725 int 726 be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) 727 { 728 struct stat sb; 729 char active[BE_MAXPATHLEN]; 730 char buf[BE_MAXPATHLEN]; 731 nvlist_t *props; 732 const char *s; 733 zfs_handle_t *zfs; 734 int err; 735 736 /* Require absolute paths */ 737 if (*child_path != '/') 738 return (set_error(lbh, BE_ERR_BADPATH)); 739 740 strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); 741 strcpy(buf, active); 742 743 /* Create non-mountable parent dataset(s) */ 744 s = child_path; 745 for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 746 size_t len = p - s; 747 strncat(buf, s, len); 748 749 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 750 nvlist_add_string(props, "canmount", "off"); 751 nvlist_add_string(props, "mountpoint", "none"); 752 zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 753 nvlist_free(props); 754 } 755 756 /* Path does not exist as a descendent of / yet */ 757 if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) 758 return (set_error(lbh, BE_ERR_PATHLEN)); 759 760 if (stat(child_path, &sb) != 0) { 761 /* Verify that error is ENOENT */ 762 if (errno != ENOENT) 763 return (set_error(lbh, BE_ERR_NOENT)); 764 765 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 766 nvlist_add_string(props, "canmount", "noauto"); 767 nvlist_add_string(props, "mountpoint", child_path); 768 769 /* Create */ 770 if ((err = 771 zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0) 772 /* XXX TODO handle error */ 773 return (set_error(lbh, BE_ERR_UNKNOWN)); 774 nvlist_free(props); 775 776 if ((zfs = 777 zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 778 return (set_error(lbh, BE_ERR_ZFSOPEN)); 779 780 /* Set props */ 781 if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) 782 /* TODO handle error */ 783 return (set_error(lbh, BE_ERR_UNKNOWN)); 784 } else if (cp_if_exists) { 785 /* Path is already a descendent of / and should be copied */ 786 787 /* XXX TODO ? */ 788 789 /* 790 * Establish if the existing path is a zfs dataset or just 791 * the subdirectory of one 792 */ 793 strlcpy(buf, "/tmp/be_snap.XXXXX", sizeof(buf)); 794 if (mktemp(buf) == NULL) 795 return (set_error(lbh, BE_ERR_UNKNOWN)); 796 797 if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) 798 /* XXX TODO correct error */ 799 return (set_error(lbh, BE_ERR_UNKNOWN)); 800 801 /* Clone */ 802 if ((zfs = 803 zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 804 return (BE_ERR_ZFSOPEN); 805 806 if ((err = zfs_clone(zfs, active, NULL)) != 0) 807 /* XXX TODO correct error */ 808 return (set_error(lbh, BE_ERR_UNKNOWN)); 809 810 /* set props */ 811 zfs_close(zfs); 812 } else 813 /* TODO: error code for exists, but not cp? */ 814 return (set_error(lbh, BE_ERR_EXISTS)); 815 816 return (BE_ERR_SUCCESS); 817 } 818 819 static int 820 be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 821 const char *zfsdev) 822 { 823 nvlist_t **child; 824 uint64_t vdev_guid; 825 int c, children; 826 827 if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 828 &children) == 0) { 829 for (c = 0; c < children; ++c) 830 if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 831 return (1); 832 return (0); 833 } 834 835 if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 836 &vdev_guid) != 0) { 837 return (1); 838 } 839 840 if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 841 perror("ZFS_IOC_NEXTBOOT failed"); 842 return (1); 843 } 844 845 return (0); 846 } 847 848 849 int 850 be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) 851 { 852 char be_path[BE_MAXPATHLEN]; 853 char buf[BE_MAXPATHLEN]; 854 uint64_t pool_guid; 855 nvlist_t *config, *vdevs; 856 int err; 857 858 be_root_concat(lbh, bootenv, be_path); 859 860 /* Note: be_exists fails if mountpoint is not / */ 861 if (!be_exists(lbh, be_path)) 862 return (BE_ERR_NOENT); 863 864 if (temporary) { 865 config = zpool_get_config(lbh->active_phandle, NULL); 866 if (config == NULL) { 867 printf("no config\n"); 868 return (1); 869 } 870 871 if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 872 &pool_guid) != 0) 873 return (1); 874 875 /* Expected format according to zfsbootcfg(8) man */ 876 strcpy(buf, "zfs:"); 877 strcat(buf, be_path); 878 strcat(buf, ":"); 879 880 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdevs) != 0) 881 return (1); 882 883 return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 884 } else { 885 /* Obtain bootenv zpool */ 886 err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 887 888 switch (err) { 889 case 0: 890 return (BE_ERR_SUCCESS); 891 892 default: 893 /* XXX TODO correct errors */ 894 return (-1); 895 } 896 } 897 } 898