zfs.c revision 294716
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: stable/10/sys/boot/zfs/zfs.c 294716 2016-01-25 10:43:44Z smh $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/10/sys/boot/zfs/zfs.c 294716 2016-01-25 10:43:44Z smh $"); 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 55static int zfs_open(const char *path, struct open_file *f); 56static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid); 57static int zfs_close(struct open_file *f); 58static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 59static off_t zfs_seek(struct open_file *f, off_t offset, int where); 60static int zfs_stat(struct open_file *f, struct stat *sb); 61static int zfs_readdir(struct open_file *f, struct dirent *d); 62 63struct devsw zfs_dev; 64 65struct fs_ops zfs_fsops = { 66 "zfs", 67 zfs_open, 68 zfs_close, 69 zfs_read, 70 zfs_write, 71 zfs_seek, 72 zfs_stat, 73 zfs_readdir 74}; 75 76/* 77 * In-core open file. 78 */ 79struct file { 80 off_t f_seekp; /* seek pointer */ 81 dnode_phys_t f_dnode; 82 uint64_t f_zap_type; /* zap type for readdir */ 83 uint64_t f_num_leafs; /* number of fzap leaf blocks */ 84 zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */ 85}; 86 87static int zfs_env_index; 88static int zfs_env_count; 89 90SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head); 91struct zfs_be_list *zfs_be_headp; 92struct zfs_be_entry { 93 const char *name; 94 SLIST_ENTRY(zfs_be_entry) entries; 95} *zfs_be, *zfs_be_tmp; 96 97/* 98 * Open a file. 99 */ 100static int 101zfs_open(const char *upath, struct open_file *f) 102{ 103 struct zfsmount *mount = (struct zfsmount *)f->f_devdata; 104 struct file *fp; 105 int rc; 106 107 if (f->f_dev != &zfs_dev) 108 return (EINVAL); 109 110 /* allocate file system specific data structure */ 111 fp = malloc(sizeof(struct file)); 112 bzero(fp, sizeof(struct file)); 113 f->f_fsdata = (void *)fp; 114 115 rc = zfs_lookup(mount, upath, &fp->f_dnode); 116 fp->f_seekp = 0; 117 if (rc) { 118 f->f_fsdata = NULL; 119 free(fp); 120 } 121 return (rc); 122} 123 124static int 125zfs_close(struct open_file *f) 126{ 127 struct file *fp = (struct file *)f->f_fsdata; 128 129 dnode_cache_obj = 0; 130 f->f_fsdata = (void *)0; 131 if (fp == (struct file *)0) 132 return (0); 133 134 free(fp); 135 return (0); 136} 137 138/* 139 * Copy a portion of a file into kernel memory. 140 * Cross block boundaries when necessary. 141 */ 142static int 143zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */) 144{ 145 const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; 146 struct file *fp = (struct file *)f->f_fsdata; 147 struct stat sb; 148 size_t n; 149 int rc; 150 151 rc = zfs_stat(f, &sb); 152 if (rc) 153 return (rc); 154 n = size; 155 if (fp->f_seekp + n > sb.st_size) 156 n = sb.st_size - fp->f_seekp; 157 158 rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n); 159 if (rc) 160 return (rc); 161 162 if (0) { 163 int i; 164 for (i = 0; i < n; i++) 165 putchar(((char*) start)[i]); 166 } 167 fp->f_seekp += n; 168 if (resid) 169 *resid = size - n; 170 171 return (0); 172} 173 174/* 175 * Don't be silly - the bootstrap has no business writing anything. 176 */ 177static int 178zfs_write(struct open_file *f, void *start, size_t size, size_t *resid /* out */) 179{ 180 181 return (EROFS); 182} 183 184static off_t 185zfs_seek(struct open_file *f, off_t offset, int where) 186{ 187 struct file *fp = (struct file *)f->f_fsdata; 188 189 switch (where) { 190 case SEEK_SET: 191 fp->f_seekp = offset; 192 break; 193 case SEEK_CUR: 194 fp->f_seekp += offset; 195 break; 196 case SEEK_END: 197 { 198 struct stat sb; 199 int error; 200 201 error = zfs_stat(f, &sb); 202 if (error != 0) { 203 errno = error; 204 return (-1); 205 } 206 fp->f_seekp = sb.st_size - offset; 207 break; 208 } 209 default: 210 errno = EINVAL; 211 return (-1); 212 } 213 return (fp->f_seekp); 214} 215 216static int 217zfs_stat(struct open_file *f, struct stat *sb) 218{ 219 const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; 220 struct file *fp = (struct file *)f->f_fsdata; 221 222 return (zfs_dnode_stat(spa, &fp->f_dnode, sb)); 223} 224 225static int 226zfs_readdir(struct open_file *f, struct dirent *d) 227{ 228 const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; 229 struct file *fp = (struct file *)f->f_fsdata; 230 mzap_ent_phys_t mze; 231 struct stat sb; 232 size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT; 233 int rc; 234 235 rc = zfs_stat(f, &sb); 236 if (rc) 237 return (rc); 238 if (!S_ISDIR(sb.st_mode)) 239 return (ENOTDIR); 240 241 /* 242 * If this is the first read, get the zap type. 243 */ 244 if (fp->f_seekp == 0) { 245 rc = dnode_read(spa, &fp->f_dnode, 246 0, &fp->f_zap_type, sizeof(fp->f_zap_type)); 247 if (rc) 248 return (rc); 249 250 if (fp->f_zap_type == ZBT_MICRO) { 251 fp->f_seekp = offsetof(mzap_phys_t, mz_chunk); 252 } else { 253 rc = dnode_read(spa, &fp->f_dnode, 254 offsetof(zap_phys_t, zap_num_leafs), 255 &fp->f_num_leafs, 256 sizeof(fp->f_num_leafs)); 257 if (rc) 258 return (rc); 259 260 fp->f_seekp = bsize; 261 fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize); 262 rc = dnode_read(spa, &fp->f_dnode, 263 fp->f_seekp, 264 fp->f_zap_leaf, 265 bsize); 266 if (rc) 267 return (rc); 268 } 269 } 270 271 if (fp->f_zap_type == ZBT_MICRO) { 272 mzap_next: 273 if (fp->f_seekp >= bsize) 274 return (ENOENT); 275 276 rc = dnode_read(spa, &fp->f_dnode, 277 fp->f_seekp, &mze, sizeof(mze)); 278 if (rc) 279 return (rc); 280 fp->f_seekp += sizeof(mze); 281 282 if (!mze.mze_name[0]) 283 goto mzap_next; 284 285 d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value); 286 d->d_type = ZFS_DIRENT_TYPE(mze.mze_value); 287 strcpy(d->d_name, mze.mze_name); 288 d->d_namlen = strlen(d->d_name); 289 return (0); 290 } else { 291 zap_leaf_t zl; 292 zap_leaf_chunk_t *zc, *nc; 293 int chunk; 294 size_t namelen; 295 char *p; 296 uint64_t value; 297 298 /* 299 * Initialise this so we can use the ZAP size 300 * calculating macros. 301 */ 302 zl.l_bs = ilog2(bsize); 303 zl.l_phys = fp->f_zap_leaf; 304 305 /* 306 * Figure out which chunk we are currently looking at 307 * and consider seeking to the next leaf. We use the 308 * low bits of f_seekp as a simple chunk index. 309 */ 310 fzap_next: 311 chunk = fp->f_seekp & (bsize - 1); 312 if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) { 313 fp->f_seekp = (fp->f_seekp & ~(bsize - 1)) + bsize; 314 chunk = 0; 315 316 /* 317 * Check for EOF and read the new leaf. 318 */ 319 if (fp->f_seekp >= bsize * fp->f_num_leafs) 320 return (ENOENT); 321 322 rc = dnode_read(spa, &fp->f_dnode, 323 fp->f_seekp, 324 fp->f_zap_leaf, 325 bsize); 326 if (rc) 327 return (rc); 328 } 329 330 zc = &ZAP_LEAF_CHUNK(&zl, chunk); 331 fp->f_seekp++; 332 if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY) 333 goto fzap_next; 334 335 namelen = zc->l_entry.le_name_numints; 336 if (namelen > sizeof(d->d_name)) 337 namelen = sizeof(d->d_name); 338 339 /* 340 * Paste the name back together. 341 */ 342 nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk); 343 p = d->d_name; 344 while (namelen > 0) { 345 int len; 346 len = namelen; 347 if (len > ZAP_LEAF_ARRAY_BYTES) 348 len = ZAP_LEAF_ARRAY_BYTES; 349 memcpy(p, nc->l_array.la_array, len); 350 p += len; 351 namelen -= len; 352 nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next); 353 } 354 d->d_name[sizeof(d->d_name) - 1] = 0; 355 356 /* 357 * Assume the first eight bytes of the value are 358 * a uint64_t. 359 */ 360 value = fzap_leaf_value(&zl, zc); 361 362 d->d_fileno = ZFS_DIRENT_OBJ(value); 363 d->d_type = ZFS_DIRENT_TYPE(value); 364 d->d_namlen = strlen(d->d_name); 365 366 return (0); 367 } 368} 369 370static int 371vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size) 372{ 373 int fd; 374 375 fd = (uintptr_t) priv; 376 lseek(fd, offset, SEEK_SET); 377 if (read(fd, buf, size) == size) { 378 return 0; 379 } else { 380 return (EIO); 381 } 382} 383 384static int 385zfs_dev_init(void) 386{ 387 spa_t *spa; 388 spa_t *next; 389 spa_t *prev; 390 391 zfs_init(); 392 if (archsw.arch_zfs_probe == NULL) 393 return (ENXIO); 394 archsw.arch_zfs_probe(); 395 396 prev = NULL; 397 spa = STAILQ_FIRST(&zfs_pools); 398 while (spa != NULL) { 399 next = STAILQ_NEXT(spa, spa_link); 400 if (zfs_spa_init(spa)) { 401 if (prev == NULL) 402 STAILQ_REMOVE_HEAD(&zfs_pools, spa_link); 403 else 404 STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link); 405 } else 406 prev = spa; 407 spa = next; 408 } 409 return (0); 410} 411 412struct zfs_probe_args { 413 int fd; 414 const char *devname; 415 uint64_t *pool_guid; 416 uint16_t secsz; 417}; 418 419static int 420zfs_diskread(void *arg, void *buf, size_t blocks, off_t offset) 421{ 422 struct zfs_probe_args *ppa; 423 424 ppa = (struct zfs_probe_args *)arg; 425 return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd, 426 offset * ppa->secsz, buf, blocks * ppa->secsz)); 427} 428 429static int 430zfs_probe(int fd, uint64_t *pool_guid) 431{ 432 spa_t *spa; 433 int ret; 434 435 ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); 436 if (ret == 0 && pool_guid != NULL) 437 *pool_guid = spa->spa_guid; 438 return (ret); 439} 440 441static void 442zfs_probe_partition(void *arg, const char *partname, 443 const struct ptable_entry *part) 444{ 445 struct zfs_probe_args *ppa, pa; 446 struct ptable *table; 447 char devname[32]; 448 int ret; 449 450 /* Probe only freebsd-zfs and freebsd partitions */ 451 if (part->type != PART_FREEBSD && 452 part->type != PART_FREEBSD_ZFS) 453 return; 454 455 ppa = (struct zfs_probe_args *)arg; 456 strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); 457 devname[strlen(ppa->devname) - 1] = '\0'; 458 sprintf(devname, "%s%s:", devname, partname); 459 pa.fd = open(devname, O_RDONLY); 460 if (pa.fd == -1) 461 return; 462 ret = zfs_probe(pa.fd, ppa->pool_guid); 463 if (ret == 0) 464 return; 465 /* Do we have BSD label here? */ 466 if (part->type == PART_FREEBSD) { 467 pa.devname = devname; 468 pa.pool_guid = ppa->pool_guid; 469 pa.secsz = ppa->secsz; 470 table = ptable_open(&pa, part->end - part->start + 1, 471 ppa->secsz, zfs_diskread); 472 if (table != NULL) { 473 ptable_iterate(table, &pa, zfs_probe_partition); 474 ptable_close(table); 475 } 476 } 477 close(pa.fd); 478} 479 480int 481zfs_probe_dev(const char *devname, uint64_t *pool_guid) 482{ 483 struct ptable *table; 484 struct zfs_probe_args pa; 485 off_t mediasz; 486 int ret; 487 488 pa.fd = open(devname, O_RDONLY); 489 if (pa.fd == -1) 490 return (ENXIO); 491 /* Probe the whole disk */ 492 ret = zfs_probe(pa.fd, pool_guid); 493 if (ret == 0) 494 return (0); 495 /* Probe each partition */ 496 ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz); 497 if (ret == 0) 498 ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz); 499 if (ret == 0) { 500 pa.devname = devname; 501 pa.pool_guid = pool_guid; 502 table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz, 503 zfs_diskread); 504 if (table != NULL) { 505 ptable_iterate(table, &pa, zfs_probe_partition); 506 ptable_close(table); 507 } 508 } 509 close(pa.fd); 510 return (ret); 511} 512 513/* 514 * Print information about ZFS pools 515 */ 516static void 517zfs_dev_print(int verbose) 518{ 519 spa_t *spa; 520 char line[80]; 521 522 if (verbose) { 523 spa_all_status(); 524 return; 525 } 526 STAILQ_FOREACH(spa, &zfs_pools, spa_link) { 527 sprintf(line, " zfs:%s\n", spa->spa_name); 528 pager_output(line); 529 } 530} 531 532/* 533 * Attempt to open the pool described by (dev) for use by (f). 534 */ 535static int 536zfs_dev_open(struct open_file *f, ...) 537{ 538 va_list args; 539 struct zfs_devdesc *dev; 540 struct zfsmount *mount; 541 spa_t *spa; 542 int rv; 543 544 va_start(args, f); 545 dev = va_arg(args, struct zfs_devdesc *); 546 va_end(args); 547 548 if (dev->pool_guid == 0) 549 spa = STAILQ_FIRST(&zfs_pools); 550 else 551 spa = spa_find_by_guid(dev->pool_guid); 552 if (!spa) 553 return (ENXIO); 554 mount = malloc(sizeof(*mount)); 555 rv = zfs_mount(spa, dev->root_guid, mount); 556 if (rv != 0) { 557 free(mount); 558 return (rv); 559 } 560 if (mount->objset.os_type != DMU_OST_ZFS) { 561 printf("Unexpected object set type %ju\n", 562 (uintmax_t)mount->objset.os_type); 563 free(mount); 564 return (EIO); 565 } 566 f->f_devdata = mount; 567 free(dev); 568 return (0); 569} 570 571static int 572zfs_dev_close(struct open_file *f) 573{ 574 575 free(f->f_devdata); 576 f->f_devdata = NULL; 577 return (0); 578} 579 580static int 581zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 582{ 583 584 return (ENOSYS); 585} 586 587struct devsw zfs_dev = { 588 .dv_name = "zfs", 589 .dv_type = DEVT_ZFS, 590 .dv_init = zfs_dev_init, 591 .dv_strategy = zfs_dev_strategy, 592 .dv_open = zfs_dev_open, 593 .dv_close = zfs_dev_close, 594 .dv_ioctl = noioctl, 595 .dv_print = zfs_dev_print, 596 .dv_cleanup = NULL 597}; 598 599int 600zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) 601{ 602 static char rootname[ZFS_MAXNAMELEN]; 603 static char poolname[ZFS_MAXNAMELEN]; 604 spa_t *spa; 605 const char *end; 606 const char *np; 607 const char *sep; 608 int rv; 609 610 np = devspec; 611 if (*np != ':') 612 return (EINVAL); 613 np++; 614 end = strchr(np, ':'); 615 if (end == NULL) 616 return (EINVAL); 617 sep = strchr(np, '/'); 618 if (sep == NULL || sep >= end) 619 sep = end; 620 memcpy(poolname, np, sep - np); 621 poolname[sep - np] = '\0'; 622 if (sep < end) { 623 sep++; 624 memcpy(rootname, sep, end - sep); 625 rootname[end - sep] = '\0'; 626 } 627 else 628 rootname[0] = '\0'; 629 630 spa = spa_find_by_name(poolname); 631 if (!spa) 632 return (ENXIO); 633 dev->pool_guid = spa->spa_guid; 634 rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid); 635 if (rv != 0) 636 return (rv); 637 if (path != NULL) 638 *path = (*end == '\0') ? end : end + 1; 639 dev->d_dev = &zfs_dev; 640 dev->d_type = zfs_dev.dv_type; 641 return (0); 642} 643 644char * 645zfs_fmtdev(void *vdev) 646{ 647 static char rootname[ZFS_MAXNAMELEN]; 648 static char buf[2 * ZFS_MAXNAMELEN + 8]; 649 struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; 650 spa_t *spa; 651 652 buf[0] = '\0'; 653 if (dev->d_type != DEVT_ZFS) 654 return (buf); 655 656 if (dev->pool_guid == 0) { 657 spa = STAILQ_FIRST(&zfs_pools); 658 dev->pool_guid = spa->spa_guid; 659 } else 660 spa = spa_find_by_guid(dev->pool_guid); 661 if (spa == NULL) { 662 printf("ZFS: can't find pool by guid\n"); 663 return (buf); 664 } 665 if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { 666 printf("ZFS: can't find root filesystem\n"); 667 return (buf); 668 } 669 if (zfs_rlookup(spa, dev->root_guid, rootname)) { 670 printf("ZFS: can't find filesystem by guid\n"); 671 return (buf); 672 } 673 674 if (rootname[0] == '\0') 675 sprintf(buf, "%s:%s:", dev->d_dev->dv_name, spa->spa_name); 676 else 677 sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, spa->spa_name, 678 rootname); 679 return (buf); 680} 681 682int 683zfs_list(const char *name) 684{ 685 static char poolname[ZFS_MAXNAMELEN]; 686 uint64_t objid; 687 spa_t *spa; 688 const char *dsname; 689 int len; 690 int rv; 691 692 len = strlen(name); 693 dsname = strchr(name, '/'); 694 if (dsname != NULL) { 695 len = dsname - name; 696 dsname++; 697 } else 698 dsname = ""; 699 memcpy(poolname, name, len); 700 poolname[len] = '\0'; 701 702 spa = spa_find_by_name(poolname); 703 if (!spa) 704 return (ENXIO); 705 rv = zfs_lookup_dataset(spa, dsname, &objid); 706 if (rv != 0) 707 return (rv); 708 709 return (zfs_list_dataset(spa, objid)); 710} 711 712int 713zfs_bootenv(const char *name) 714{ 715 static char poolname[ZFS_MAXNAMELEN], *dsname, *root; 716 char becount[4]; 717 uint64_t objid; 718 spa_t *spa; 719 int len, rv, pages, perpage, currpage; 720 721 if (name == NULL) 722 return (EINVAL); 723 if ((root = getenv("zfs_be_root")) == NULL) 724 return (EINVAL); 725 726 if (strcmp(name, root) != 0) { 727 if (setenv("zfs_be_root", name, 1) != 0) 728 return (ENOMEM); 729 } 730 731 SLIST_INIT(&zfs_be_head); 732 zfs_env_count = 0; 733 len = strlen(name); 734 dsname = strchr(name, '/'); 735 if (dsname != NULL) { 736 len = dsname - name; 737 dsname++; 738 } else 739 dsname = ""; 740 memcpy(poolname, name, len); 741 poolname[len] = '\0'; 742 743 spa = spa_find_by_name(poolname); 744 if (!spa) 745 return (ENXIO); 746 rv = zfs_lookup_dataset(spa, dsname, &objid); 747 if (rv != 0) 748 return (rv); 749 rv = zfs_callback_dataset(spa, objid, zfs_belist_add); 750 751 /* Calculate and store the number of pages of BEs */ 752 perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1); 753 pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0); 754 snprintf(becount, 4, "%d", pages); 755 if (setenv("zfs_be_pages", becount, 1) != 0) 756 return (ENOMEM); 757 758 /* Roll over the page counter if it has exceeded the maximum */ 759 currpage = strtol(getenv("zfs_be_currpage"), NULL, 10); 760 if (currpage > pages) { 761 if (setenv("zfs_be_currpage", "1", 1) != 0) 762 return (ENOMEM); 763 } 764 765 /* Populate the menu environment variables */ 766 zfs_set_env(); 767 768 /* Clean up the SLIST of ZFS BEs */ 769 while (!SLIST_EMPTY(&zfs_be_head)) { 770 zfs_be = SLIST_FIRST(&zfs_be_head); 771 SLIST_REMOVE_HEAD(&zfs_be_head, entries); 772 free(zfs_be); 773 } 774 775 return (rv); 776} 777 778int 779zfs_belist_add(const char *name) 780{ 781 782 /* Add the boot environment to the head of the SLIST */ 783 zfs_be = malloc(sizeof(struct zfs_be_entry)); 784 zfs_be->name = name; 785 SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries); 786 zfs_env_count++; 787 788 return (0); 789} 790 791int 792zfs_set_env(void) 793{ 794 char envname[32], envval[256]; 795 char *beroot, *pagenum; 796 int rv, page, ctr; 797 798 beroot = getenv("zfs_be_root"); 799 if (beroot == NULL) { 800 return (1); 801 } 802 803 pagenum = getenv("zfs_be_currpage"); 804 if (pagenum != NULL) { 805 page = strtol(pagenum, NULL, 10); 806 } else { 807 page = 1; 808 } 809 810 ctr = 1; 811 rv = 0; 812 zfs_env_index = ZFS_BE_FIRST; 813 SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { 814 /* Skip to the requested page number */ 815 if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) { 816 ctr++; 817 continue; 818 } 819 820 snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); 821 snprintf(envval, sizeof(envval), "%s", zfs_be->name); 822 rv = setenv(envname, envval, 1); 823 if (rv != 0) { 824 break; 825 } 826 827 snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); 828 rv = setenv(envname, envval, 1); 829 if (rv != 0){ 830 break; 831 } 832 833 snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); 834 rv = setenv(envname, "set_bootenv", 1); 835 if (rv != 0){ 836 break; 837 } 838 839 snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); 840 snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name); 841 rv = setenv(envname, envval, 1); 842 if (rv != 0){ 843 break; 844 } 845 846 zfs_env_index++; 847 if (zfs_env_index > ZFS_BE_LAST) { 848 break; 849 } 850 851 } 852 853 for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) { 854 snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); 855 (void)unsetenv(envname); 856 snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); 857 (void)unsetenv(envname); 858 snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); 859 (void)unsetenv(envname); 860 snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); 861 (void)unsetenv(envname); 862 } 863 864 return (rv); 865}