1 /*- 2 * Copyright (c) 2007 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 /* 33 * Stand-alone file reading package. 34 */ 35 36 #include <sys/disk.h> 37 #include <sys/param.h> 38 #include <sys/time.h> 39 #include <sys/queue.h> 40 #include <part.h> 41 #include <stddef.h> 42 #include <stdarg.h> 43 #include <string.h> 44 #include <stand.h> 45 #include <bootstrap.h> 46 47 #include "libzfs.h" 48 49 #include "zfsimpl.c" 50 51 /* Define the range of indexes to be populated with ZFS Boot Environments */ 52 #define ZFS_BE_FIRST 4 53 #define ZFS_BE_LAST 8 54 55 static int zfs_open(const char *path, struct open_file *f); 56 static int zfs_close(struct open_file *f); 57 static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 58 static off_t zfs_seek(struct open_file *f, off_t offset, int where); 59 static int zfs_stat(struct open_file *f, struct stat *sb); 60 static int zfs_readdir(struct open_file *f, struct dirent *d); 61 62 static void zfs_bootenv_initial(const char *); 63 64 struct devsw zfs_dev; 65 66 struct fs_ops zfs_fsops = { 67 "zfs", 68 zfs_open, 69 zfs_close, 70 zfs_read, 71 null_write, 72 zfs_seek, 73 zfs_stat, 74 zfs_readdir 75 }; 76 77 /* 78 * In-core open file. 79 */ 80 struct file { 81 off_t f_seekp; /* seek pointer */ 82 dnode_phys_t f_dnode; 83 uint64_t f_zap_type; /* zap type for readdir */ 84 uint64_t f_num_leafs; /* number of fzap leaf blocks */ 85 zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */ 86 }; 87 88 static int zfs_env_index; 89 static int zfs_env_count; 90 91 SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head); 92 struct zfs_be_list *zfs_be_headp; 93 struct zfs_be_entry { 94 const char *name; 95 SLIST_ENTRY(zfs_be_entry) entries; 96 } *zfs_be, *zfs_be_tmp; 97 98 /* 99 * Open a file. 100 */ 101 static int 102 zfs_open(const char *upath, struct open_file *f) 103 { 104 struct zfsmount *mount = (struct zfsmount *)f->f_devdata; 105 struct file *fp; 106 int rc; 107 108 if (f->f_dev != &zfs_dev) 109 return (EINVAL); 110 111 /* allocate file system specific data structure */ 112 fp = malloc(sizeof(struct file)); 113 bzero(fp, sizeof(struct file)); 114 f->f_fsdata = (void *)fp; 115 116 rc = zfs_lookup(mount, upath, &fp->f_dnode); 117 fp->f_seekp = 0; 118 if (rc) { 119 f->f_fsdata = NULL; 120 free(fp); 121 } 122 return (rc); 123 } 124 125 static int 126 zfs_close(struct open_file *f) 127 { 128 struct file *fp = (struct file *)f->f_fsdata; 129 130 dnode_cache_obj = NULL; 131 f->f_fsdata = (void *)0; 132 if (fp == (struct file *)0) 133 return (0); 134 135 free(fp); 136 return (0); 137 } 138 139 /* 140 * Copy a portion of a file into kernel memory. 141 * Cross block boundaries when necessary. 142 */ 143 static int 144 zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */) 145 { 146 const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; 147 struct file *fp = (struct file *)f->f_fsdata; 148 struct stat sb; 149 size_t n; 150 int rc; 151 152 rc = zfs_stat(f, &sb); 153 if (rc) 154 return (rc); 155 n = size; 156 if (fp->f_seekp + n > sb.st_size) 157 n = sb.st_size - fp->f_seekp; 158 159 rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n); 160 if (rc) 161 return (rc); 162 163 if (0) { 164 int i; 165 for (i = 0; i < n; i++) 166 putchar(((char*) start)[i]); 167 } 168 fp->f_seekp += n; 169 if (resid) 170 *resid = size - n; 171 172 return (0); 173 } 174 175 static off_t 176 zfs_seek(struct open_file *f, off_t offset, int where) 177 { 178 struct file *fp = (struct file *)f->f_fsdata; 179 180 switch (where) { 181 case SEEK_SET: 182 fp->f_seekp = offset; 183 break; 184 case SEEK_CUR: 185 fp->f_seekp += offset; 186 break; 187 case SEEK_END: 188 { 189 struct stat sb; 190 int error; 191 192 error = zfs_stat(f, &sb); 193 if (error != 0) { 194 errno = error; 195 return (-1); 196 } 197 fp->f_seekp = sb.st_size - offset; 198 break; 199 } 200 default: 201 errno = EINVAL; 202 return (-1); 203 } 204 return (fp->f_seekp); 205 } 206 207 static int 208 zfs_stat(struct open_file *f, struct stat *sb) 209 { 210 const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; 211 struct file *fp = (struct file *)f->f_fsdata; 212 213 return (zfs_dnode_stat(spa, &fp->f_dnode, sb)); 214 } 215 216 static int 217 zfs_readdir(struct open_file *f, struct dirent *d) 218 { 219 const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; 220 struct file *fp = (struct file *)f->f_fsdata; 221 mzap_ent_phys_t mze; 222 struct stat sb; 223 size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT; 224 int rc; 225 226 rc = zfs_stat(f, &sb); 227 if (rc) 228 return (rc); 229 if (!S_ISDIR(sb.st_mode)) 230 return (ENOTDIR); 231 232 /* 233 * If this is the first read, get the zap type. 234 */ 235 if (fp->f_seekp == 0) { 236 rc = dnode_read(spa, &fp->f_dnode, 237 0, &fp->f_zap_type, sizeof(fp->f_zap_type)); 238 if (rc) 239 return (rc); 240 241 if (fp->f_zap_type == ZBT_MICRO) { 242 fp->f_seekp = offsetof(mzap_phys_t, mz_chunk); 243 } else { 244 rc = dnode_read(spa, &fp->f_dnode, 245 offsetof(zap_phys_t, zap_num_leafs), 246 &fp->f_num_leafs, 247 sizeof(fp->f_num_leafs)); 248 if (rc) 249 return (rc); 250 251 fp->f_seekp = bsize; 252 fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize); 253 rc = dnode_read(spa, &fp->f_dnode, 254 fp->f_seekp, 255 fp->f_zap_leaf, 256 bsize); 257 if (rc) 258 return (rc); 259 } 260 } 261 262 if (fp->f_zap_type == ZBT_MICRO) { 263 mzap_next: 264 if (fp->f_seekp >= bsize) 265 return (ENOENT); 266 267 rc = dnode_read(spa, &fp->f_dnode, 268 fp->f_seekp, &mze, sizeof(mze)); 269 if (rc) 270 return (rc); 271 fp->f_seekp += sizeof(mze); 272 273 if (!mze.mze_name[0]) 274 goto mzap_next; 275 276 d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value); 277 d->d_type = ZFS_DIRENT_TYPE(mze.mze_value); 278 strcpy(d->d_name, mze.mze_name); 279 d->d_namlen = strlen(d->d_name); 280 return (0); 281 } else { 282 zap_leaf_t zl; 283 zap_leaf_chunk_t *zc, *nc; 284 int chunk; 285 size_t namelen; 286 char *p; 287 uint64_t value; 288 289 /* 290 * Initialise this so we can use the ZAP size 291 * calculating macros. 292 */ 293 zl.l_bs = ilog2(bsize); 294 zl.l_phys = fp->f_zap_leaf; 295 296 /* 297 * Figure out which chunk we are currently looking at 298 * and consider seeking to the next leaf. We use the 299 * low bits of f_seekp as a simple chunk index. 300 */ 301 fzap_next: 302 chunk = fp->f_seekp & (bsize - 1); 303 if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) { 304 fp->f_seekp = rounddown2(fp->f_seekp, bsize) + bsize; 305 chunk = 0; 306 307 /* 308 * Check for EOF and read the new leaf. 309 */ 310 if (fp->f_seekp >= bsize * fp->f_num_leafs) 311 return (ENOENT); 312 313 rc = dnode_read(spa, &fp->f_dnode, 314 fp->f_seekp, 315 fp->f_zap_leaf, 316 bsize); 317 if (rc) 318 return (rc); 319 } 320 321 zc = &ZAP_LEAF_CHUNK(&zl, chunk); 322 fp->f_seekp++; 323 if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY) 324 goto fzap_next; 325 326 namelen = zc->l_entry.le_name_numints; 327 if (namelen > sizeof(d->d_name)) 328 namelen = sizeof(d->d_name); 329 330 /* 331 * Paste the name back together. 332 */ 333 nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk); 334 p = d->d_name; 335 while (namelen > 0) { 336 int len; 337 len = namelen; 338 if (len > ZAP_LEAF_ARRAY_BYTES) 339 len = ZAP_LEAF_ARRAY_BYTES; 340 memcpy(p, nc->l_array.la_array, len); 341 p += len; 342 namelen -= len; 343 nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next); 344 } 345 d->d_name[sizeof(d->d_name) - 1] = 0; 346 347 /* 348 * Assume the first eight bytes of the value are 349 * a uint64_t. 350 */ 351 value = fzap_leaf_value(&zl, zc); 352 353 d->d_fileno = ZFS_DIRENT_OBJ(value); 354 d->d_type = ZFS_DIRENT_TYPE(value); 355 d->d_namlen = strlen(d->d_name); 356 357 return (0); 358 } 359 } 360 361 static int 362 vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes) 363 { 364 int fd, ret; 365 size_t res, size, remainder, rb_size, blksz; 366 unsigned secsz; 367 off_t off; 368 char *bouncebuf, *rb_buf; 369 370 fd = (uintptr_t) priv; 371 bouncebuf = NULL; 372 373 ret = ioctl(fd, DIOCGSECTORSIZE, &secsz); 374 if (ret != 0) 375 return (ret); 376 377 off = offset / secsz; 378 remainder = offset % secsz; 379 if (lseek(fd, off * secsz, SEEK_SET) == -1) 380 return (errno); 381 382 rb_buf = buf; 383 rb_size = bytes; 384 size = roundup2(bytes + remainder, secsz); 385 blksz = size; 386 if (remainder != 0 || size != bytes) { 387 bouncebuf = zfs_alloc(secsz); 388 if (bouncebuf == NULL) { 389 printf("vdev_read: out of memory\n"); 390 return (ENOMEM); 391 } 392 rb_buf = bouncebuf; 393 blksz = rb_size - remainder; 394 } 395 396 while (bytes > 0) { 397 res = read(fd, rb_buf, rb_size); 398 if (res != rb_size) { 399 ret = EIO; 400 goto error; 401 } 402 if (bytes < blksz) 403 blksz = bytes; 404 if (bouncebuf != NULL) 405 memcpy(buf, rb_buf + remainder, blksz); 406 buf = (void *)((uintptr_t)buf + blksz); 407 bytes -= blksz; 408 remainder = 0; 409 blksz = rb_size; 410 } 411 412 ret = 0; 413 error: 414 if (bouncebuf != NULL) 415 zfs_free(bouncebuf, secsz); 416 return (ret); 417 } 418 419 static int 420 zfs_dev_init(void) 421 { 422 spa_t *spa; 423 spa_t *next; 424 spa_t *prev; 425 426 zfs_init(); 427 if (archsw.arch_zfs_probe == NULL) 428 return (ENXIO); 429 archsw.arch_zfs_probe(); 430 431 prev = NULL; 432 spa = STAILQ_FIRST(&zfs_pools); 433 while (spa != NULL) { 434 next = STAILQ_NEXT(spa, spa_link); 435 if (zfs_spa_init(spa)) { 436 if (prev == NULL) 437 STAILQ_REMOVE_HEAD(&zfs_pools, spa_link); 438 else 439 STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link); 440 } else 441 prev = spa; 442 spa = next; 443 } 444 return (0); 445 } 446 447 struct zfs_probe_args { 448 int fd; 449 const char *devname; 450 uint64_t *pool_guid; 451 u_int secsz; 452 }; 453 454 static int 455 zfs_diskread(void *arg, void *buf, size_t blocks, uint64_t offset) 456 { 457 struct zfs_probe_args *ppa; 458 459 ppa = (struct zfs_probe_args *)arg; 460 return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd, 461 offset * ppa->secsz, buf, blocks * ppa->secsz)); 462 } 463 464 static int 465 zfs_probe(int fd, uint64_t *pool_guid) 466 { 467 spa_t *spa; 468 int ret; 469 470 spa = NULL; 471 ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); 472 if (ret == 0 && pool_guid != NULL) 473 *pool_guid = spa->spa_guid; 474 return (ret); 475 } 476 477 static int 478 zfs_probe_partition(void *arg, const char *partname, 479 const struct ptable_entry *part) 480 { 481 struct zfs_probe_args *ppa, pa; 482 struct ptable *table; 483 char devname[32]; 484 int ret; 485 486 /* Probe only freebsd-zfs and freebsd partitions */ 487 if (part->type != PART_FREEBSD && 488 part->type != PART_FREEBSD_ZFS) 489 return (0); 490 491 ppa = (struct zfs_probe_args *)arg; 492 strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); 493 devname[strlen(ppa->devname) - 1] = '\0'; 494 sprintf(devname, "%s%s:", devname, partname); 495 pa.fd = open(devname, O_RDONLY); 496 if (pa.fd == -1) 497 return (0); 498 ret = zfs_probe(pa.fd, ppa->pool_guid); 499 if (ret == 0) 500 return (0); 501 /* Do we have BSD label here? */ 502 if (part->type == PART_FREEBSD) { 503 pa.devname = devname; 504 pa.pool_guid = ppa->pool_guid; 505 pa.secsz = ppa->secsz; 506 table = ptable_open(&pa, part->end - part->start + 1, 507 ppa->secsz, zfs_diskread); 508 if (table != NULL) { 509 ptable_iterate(table, &pa, zfs_probe_partition); 510 ptable_close(table); 511 } 512 } 513 close(pa.fd); 514 return (0); 515 } 516 517 int 518 zfs_probe_dev(const char *devname, uint64_t *pool_guid) 519 { 520 struct ptable *table; 521 struct zfs_probe_args pa; 522 uint64_t mediasz; 523 int ret; 524 525 if (pool_guid) 526 *pool_guid = 0; 527 pa.fd = open(devname, O_RDONLY); 528 if (pa.fd == -1) 529 return (ENXIO); 530 /* Probe the whole disk */ 531 ret = zfs_probe(pa.fd, pool_guid); 532 if (ret == 0) 533 return (0); 534 535 /* Probe each partition */ 536 ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz); 537 if (ret == 0) 538 ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz); 539 if (ret == 0) { 540 pa.devname = devname; 541 pa.pool_guid = pool_guid; 542 table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz, 543 zfs_diskread); 544 if (table != NULL) { 545 ptable_iterate(table, &pa, zfs_probe_partition); 546 ptable_close(table); 547 } 548 } 549 close(pa.fd); 550 if (pool_guid && *pool_guid == 0) 551 ret = ENXIO; 552 return (ret); 553 } 554 555 /* 556 * Print information about ZFS pools 557 */ 558 static int 559 zfs_dev_print(int verbose) 560 { 561 spa_t *spa; 562 char line[80]; 563 int ret = 0; 564 565 if (STAILQ_EMPTY(&zfs_pools)) 566 return (0); 567 568 printf("%s devices:", zfs_dev.dv_name); 569 if ((ret = pager_output("\n")) != 0) 570 return (ret); 571 572 if (verbose) { 573 return (spa_all_status()); 574 } 575 STAILQ_FOREACH(spa, &zfs_pools, spa_link) { 576 snprintf(line, sizeof(line), " zfs:%s\n", spa->spa_name); 577 ret = pager_output(line); 578 if (ret != 0) 579 break; 580 } 581 return (ret); 582 } 583 584 /* 585 * Attempt to open the pool described by (dev) for use by (f). 586 */ 587 static int 588 zfs_dev_open(struct open_file *f, ...) 589 { 590 va_list args; 591 struct zfs_devdesc *dev; 592 struct zfsmount *mount; 593 spa_t *spa; 594 int rv; 595 596 va_start(args, f); 597 dev = va_arg(args, struct zfs_devdesc *); 598 va_end(args); 599 600 if (dev->pool_guid == 0) 601 spa = STAILQ_FIRST(&zfs_pools); 602 else 603 spa = spa_find_by_guid(dev->pool_guid); 604 if (!spa) 605 return (ENXIO); 606 mount = malloc(sizeof(*mount)); 607 rv = zfs_mount(spa, dev->root_guid, mount); 608 if (rv != 0) { 609 free(mount); 610 return (rv); 611 } 612 if (mount->objset.os_type != DMU_OST_ZFS) { 613 printf("Unexpected object set type %ju\n", 614 (uintmax_t)mount->objset.os_type); 615 free(mount); 616 return (EIO); 617 } 618 f->f_devdata = mount; 619 free(dev); 620 return (0); 621 } 622 623 static int 624 zfs_dev_close(struct open_file *f) 625 { 626 627 free(f->f_devdata); 628 f->f_devdata = NULL; 629 return (0); 630 } 631 632 static int 633 zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 634 { 635 636 return (ENOSYS); 637 } 638 639 struct devsw zfs_dev = { 640 .dv_name = "zfs", 641 .dv_type = DEVT_ZFS, 642 .dv_init = zfs_dev_init, 643 .dv_strategy = zfs_dev_strategy, 644 .dv_open = zfs_dev_open, 645 .dv_close = zfs_dev_close, 646 .dv_ioctl = noioctl, 647 .dv_print = zfs_dev_print, 648 .dv_cleanup = NULL 649 }; 650 651 int 652 zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) 653 { 654 static char rootname[ZFS_MAXNAMELEN]; 655 static char poolname[ZFS_MAXNAMELEN]; 656 spa_t *spa; 657 const char *end; 658 const char *np; 659 const char *sep; 660 int rv; 661 662 np = devspec; 663 if (*np != ':') 664 return (EINVAL); 665 np++; 666 end = strrchr(np, ':'); 667 if (end == NULL) 668 return (EINVAL); 669 sep = strchr(np, '/'); 670 if (sep == NULL || sep >= end) 671 sep = end; 672 memcpy(poolname, np, sep - np); 673 poolname[sep - np] = '\0'; 674 if (sep < end) { 675 sep++; 676 memcpy(rootname, sep, end - sep); 677 rootname[end - sep] = '\0'; 678 } 679 else 680 rootname[0] = '\0'; 681 682 spa = spa_find_by_name(poolname); 683 if (!spa) 684 return (ENXIO); 685 dev->pool_guid = spa->spa_guid; 686 rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid); 687 if (rv != 0) 688 return (rv); 689 if (path != NULL) 690 *path = (*end == '\0') ? end : end + 1; 691 dev->dd.d_dev = &zfs_dev; 692 return (0); 693 } 694 695 char * 696 zfs_fmtdev(void *vdev) 697 { 698 static char rootname[ZFS_MAXNAMELEN]; 699 static char buf[2 * ZFS_MAXNAMELEN + 8]; 700 struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; 701 spa_t *spa; 702 703 buf[0] = '\0'; 704 if (dev->dd.d_dev->dv_type != DEVT_ZFS) 705 return (buf); 706 707 if (dev->pool_guid == 0) { 708 spa = STAILQ_FIRST(&zfs_pools); 709 dev->pool_guid = spa->spa_guid; 710 } else 711 spa = spa_find_by_guid(dev->pool_guid); 712 if (spa == NULL) { 713 printf("ZFS: can't find pool by guid\n"); 714 return (buf); 715 } 716 if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { 717 printf("ZFS: can't find root filesystem\n"); 718 return (buf); 719 } 720 if (zfs_rlookup(spa, dev->root_guid, rootname)) { 721 printf("ZFS: can't find filesystem by guid\n"); 722 return (buf); 723 } 724 725 if (rootname[0] == '\0') 726 sprintf(buf, "%s:%s:", dev->dd.d_dev->dv_name, spa->spa_name); 727 else 728 sprintf(buf, "%s:%s/%s:", dev->dd.d_dev->dv_name, spa->spa_name, 729 rootname); 730 return (buf); 731 } 732 733 int 734 zfs_list(const char *name) 735 { 736 static char poolname[ZFS_MAXNAMELEN]; 737 uint64_t objid; 738 spa_t *spa; 739 const char *dsname; 740 int len; 741 int rv; 742 743 len = strlen(name); 744 dsname = strchr(name, '/'); 745 if (dsname != NULL) { 746 len = dsname - name; 747 dsname++; 748 } else 749 dsname = ""; 750 memcpy(poolname, name, len); 751 poolname[len] = '\0'; 752 753 spa = spa_find_by_name(poolname); 754 if (!spa) 755 return (ENXIO); 756 rv = zfs_lookup_dataset(spa, dsname, &objid); 757 if (rv != 0) 758 return (rv); 759 760 return (zfs_list_dataset(spa, objid)); 761 } 762 763 void 764 init_zfs_bootenv(const char *currdev_in) 765 { 766 char *beroot, *currdev; 767 int currdev_len; 768 769 currdev = NULL; 770 currdev_len = strlen(currdev_in); 771 if (currdev_len == 0) 772 return; 773 if (strncmp(currdev_in, "zfs:", 4) != 0) 774 return; 775 currdev = strdup(currdev_in); 776 if (currdev == NULL) 777 return; 778 /* Remove the trailing : */ 779 currdev[currdev_len - 1] = '\0'; 780 setenv("zfs_be_active", currdev, 1); 781 setenv("zfs_be_currpage", "1", 1); 782 /* Remove the last element (current bootenv) */ 783 beroot = strrchr(currdev, '/'); 784 if (beroot != NULL) 785 beroot[0] = '\0'; 786 beroot = strchr(currdev, ':') + 1; 787 setenv("zfs_be_root", beroot, 1); 788 zfs_bootenv_initial(beroot); 789 free(currdev); 790 } 791 792 static void 793 zfs_bootenv_initial(const char *name) 794 { 795 char poolname[ZFS_MAXNAMELEN], *dsname; 796 char envname[32], envval[256]; 797 uint64_t objid; 798 spa_t *spa; 799 int bootenvs_idx, len, rv; 800 801 SLIST_INIT(&zfs_be_head); 802 zfs_env_count = 0; 803 len = strlen(name); 804 dsname = strchr(name, '/'); 805 if (dsname != NULL) { 806 len = dsname - name; 807 dsname++; 808 } else 809 dsname = ""; 810 strlcpy(poolname, name, len + 1); 811 spa = spa_find_by_name(poolname); 812 if (spa == NULL) 813 return; 814 rv = zfs_lookup_dataset(spa, dsname, &objid); 815 if (rv != 0) 816 return; 817 rv = zfs_callback_dataset(spa, objid, zfs_belist_add); 818 bootenvs_idx = 0; 819 /* Populate the initial environment variables */ 820 SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { 821 /* Enumerate all bootenvs for general usage */ 822 snprintf(envname, sizeof(envname), "bootenvs[%d]", bootenvs_idx); 823 snprintf(envval, sizeof(envval), "zfs:%s/%s", name, zfs_be->name); 824 rv = setenv(envname, envval, 1); 825 if (rv != 0) 826 break; 827 bootenvs_idx++; 828 } 829 snprintf(envval, sizeof(envval), "%d", bootenvs_idx); 830 setenv("bootenvs_count", envval, 1); 831 832 /* Clean up the SLIST of ZFS BEs */ 833 while (!SLIST_EMPTY(&zfs_be_head)) { 834 zfs_be = SLIST_FIRST(&zfs_be_head); 835 SLIST_REMOVE_HEAD(&zfs_be_head, entries); 836 free(zfs_be); 837 } 838 839 return; 840 841 } 842 843 int 844 zfs_bootenv(const char *name) 845 { 846 static char poolname[ZFS_MAXNAMELEN], *dsname, *root; 847 char becount[4]; 848 uint64_t objid; 849 spa_t *spa; 850 int len, rv, pages, perpage, currpage; 851 852 if (name == NULL) 853 return (EINVAL); 854 if ((root = getenv("zfs_be_root")) == NULL) 855 return (EINVAL); 856 857 if (strcmp(name, root) != 0) { 858 if (setenv("zfs_be_root", name, 1) != 0) 859 return (ENOMEM); 860 } 861 862 SLIST_INIT(&zfs_be_head); 863 zfs_env_count = 0; 864 len = strlen(name); 865 dsname = strchr(name, '/'); 866 if (dsname != NULL) { 867 len = dsname - name; 868 dsname++; 869 } else 870 dsname = ""; 871 memcpy(poolname, name, len); 872 poolname[len] = '\0'; 873 874 spa = spa_find_by_name(poolname); 875 if (!spa) 876 return (ENXIO); 877 rv = zfs_lookup_dataset(spa, dsname, &objid); 878 if (rv != 0) 879 return (rv); 880 rv = zfs_callback_dataset(spa, objid, zfs_belist_add); 881 882 /* Calculate and store the number of pages of BEs */ 883 perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1); 884 pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0); 885 snprintf(becount, 4, "%d", pages); 886 if (setenv("zfs_be_pages", becount, 1) != 0) 887 return (ENOMEM); 888 889 /* Roll over the page counter if it has exceeded the maximum */ 890 currpage = strtol(getenv("zfs_be_currpage"), NULL, 10); 891 if (currpage > pages) { 892 if (setenv("zfs_be_currpage", "1", 1) != 0) 893 return (ENOMEM); 894 } 895 896 /* Populate the menu environment variables */ 897 zfs_set_env(); 898 899 /* Clean up the SLIST of ZFS BEs */ 900 while (!SLIST_EMPTY(&zfs_be_head)) { 901 zfs_be = SLIST_FIRST(&zfs_be_head); 902 SLIST_REMOVE_HEAD(&zfs_be_head, entries); 903 free(zfs_be); 904 } 905 906 return (rv); 907 } 908 909 int 910 zfs_belist_add(const char *name, uint64_t value __unused) 911 { 912 913 /* Skip special datasets that start with a $ character */ 914 if (strncmp(name, "$", 1) == 0) { 915 return (0); 916 } 917 /* Add the boot environment to the head of the SLIST */ 918 zfs_be = malloc(sizeof(struct zfs_be_entry)); 919 if (zfs_be == NULL) { 920 return (ENOMEM); 921 } 922 zfs_be->name = name; 923 SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries); 924 zfs_env_count++; 925 926 return (0); 927 } 928 929 int 930 zfs_set_env(void) 931 { 932 char envname[32], envval[256]; 933 char *beroot, *pagenum; 934 int rv, page, ctr; 935 936 beroot = getenv("zfs_be_root"); 937 if (beroot == NULL) { 938 return (1); 939 } 940 941 pagenum = getenv("zfs_be_currpage"); 942 if (pagenum != NULL) { 943 page = strtol(pagenum, NULL, 10); 944 } else { 945 page = 1; 946 } 947 948 ctr = 1; 949 rv = 0; 950 zfs_env_index = ZFS_BE_FIRST; 951 SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { 952 /* Skip to the requested page number */ 953 if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) { 954 ctr++; 955 continue; 956 } 957 958 snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); 959 snprintf(envval, sizeof(envval), "%s", zfs_be->name); 960 rv = setenv(envname, envval, 1); 961 if (rv != 0) { 962 break; 963 } 964 965 snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); 966 rv = setenv(envname, envval, 1); 967 if (rv != 0){ 968 break; 969 } 970 971 snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); 972 rv = setenv(envname, "set_bootenv", 1); 973 if (rv != 0){ 974 break; 975 } 976 977 snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); 978 snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name); 979 rv = setenv(envname, envval, 1); 980 if (rv != 0){ 981 break; 982 } 983 984 zfs_env_index++; 985 if (zfs_env_index > ZFS_BE_LAST) { 986 break; 987 } 988 989 } 990 991 for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) { 992 snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); 993 (void)unsetenv(envname); 994 snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); 995 (void)unsetenv(envname); 996 snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); 997 (void)unsetenv(envname); 998 snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); 999 (void)unsetenv(envname); 1000 } 1001 1002 return (rv); 1003 } 1004