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 ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); 471 if (ret == 0 && pool_guid != NULL) 472 *pool_guid = spa->spa_guid; 473 return (ret); 474 } 475 476 static int 477 zfs_probe_partition(void *arg, const char *partname, 478 const struct ptable_entry *part) 479 { 480 struct zfs_probe_args *ppa, pa; 481 struct ptable *table; 482 char devname[32]; 483 int ret; 484 485 /* Probe only freebsd-zfs and freebsd partitions */ 486 if (part->type != PART_FREEBSD && 487 part->type != PART_FREEBSD_ZFS) 488 return (0); 489 490 ppa = (struct zfs_probe_args *)arg; 491 strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); 492 devname[strlen(ppa->devname) - 1] = '\0'; 493 sprintf(devname, "%s%s:", devname, partname); 494 pa.fd = open(devname, O_RDONLY); 495 if (pa.fd == -1) 496 return (0); 497 ret = zfs_probe(pa.fd, ppa->pool_guid); 498 if (ret == 0) 499 return (0); 500 /* Do we have BSD label here? */ 501 if (part->type == PART_FREEBSD) { 502 pa.devname = devname; 503 pa.pool_guid = ppa->pool_guid; 504 pa.secsz = ppa->secsz; 505 table = ptable_open(&pa, part->end - part->start + 1, 506 ppa->secsz, zfs_diskread); 507 if (table != NULL) { 508 ptable_iterate(table, &pa, zfs_probe_partition); 509 ptable_close(table); 510 } 511 } 512 close(pa.fd); 513 return (0); 514 } 515 516 int 517 zfs_probe_dev(const char *devname, uint64_t *pool_guid) 518 { 519 struct ptable *table; 520 struct zfs_probe_args pa; 521 uint64_t mediasz; 522 int ret; 523 524 if (pool_guid) 525 *pool_guid = 0; 526 pa.fd = open(devname, O_RDONLY); 527 if (pa.fd == -1) 528 return (ENXIO); 529 /* Probe the whole disk */ 530 ret = zfs_probe(pa.fd, pool_guid); 531 if (ret == 0) 532 return (0); 533 534 /* Probe each partition */ 535 ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz); 536 if (ret == 0) 537 ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz); 538 if (ret == 0) { 539 pa.devname = devname; 540 pa.pool_guid = pool_guid; 541 table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz, 542 zfs_diskread); 543 if (table != NULL) { 544 ptable_iterate(table, &pa, zfs_probe_partition); 545 ptable_close(table); 546 } 547 } 548 close(pa.fd); 549 if (pool_guid && *pool_guid == 0) 550 ret = ENXIO; 551 return (ret); 552 } 553 554 /* 555 * Print information about ZFS pools 556 */ 557 static int 558 zfs_dev_print(int verbose) 559 { 560 spa_t *spa; 561 char line[80]; 562 int ret = 0; 563 564 if (STAILQ_EMPTY(&zfs_pools)) 565 return (0); 566 567 printf("%s devices:", zfs_dev.dv_name); 568 if ((ret = pager_output("\n")) != 0) 569 return (ret); 570 571 if (verbose) { 572 return (spa_all_status()); 573 } 574 STAILQ_FOREACH(spa, &zfs_pools, spa_link) { 575 snprintf(line, sizeof(line), " zfs:%s\n", spa->spa_name); 576 ret = pager_output(line); 577 if (ret != 0) 578 break; 579 } 580 return (ret); 581 } 582 583 /* 584 * Attempt to open the pool described by (dev) for use by (f). 585 */ 586 static int 587 zfs_dev_open(struct open_file *f, ...) 588 { 589 va_list args; 590 struct zfs_devdesc *dev; 591 struct zfsmount *mount; 592 spa_t *spa; 593 int rv; 594 595 va_start(args, f); 596 dev = va_arg(args, struct zfs_devdesc *); 597 va_end(args); 598 599 if (dev->pool_guid == 0) 600 spa = STAILQ_FIRST(&zfs_pools); 601 else 602 spa = spa_find_by_guid(dev->pool_guid); 603 if (!spa) 604 return (ENXIO); 605 mount = malloc(sizeof(*mount)); 606 rv = zfs_mount(spa, dev->root_guid, mount); 607 if (rv != 0) { 608 free(mount); 609 return (rv); 610 } 611 if (mount->objset.os_type != DMU_OST_ZFS) { 612 printf("Unexpected object set type %ju\n", 613 (uintmax_t)mount->objset.os_type); 614 free(mount); 615 return (EIO); 616 } 617 f->f_devdata = mount; 618 free(dev); 619 return (0); 620 } 621 622 static int 623 zfs_dev_close(struct open_file *f) 624 { 625 626 free(f->f_devdata); 627 f->f_devdata = NULL; 628 return (0); 629 } 630 631 static int 632 zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 633 { 634 635 return (ENOSYS); 636 } 637 638 struct devsw zfs_dev = { 639 .dv_name = "zfs", 640 .dv_type = DEVT_ZFS, 641 .dv_init = zfs_dev_init, 642 .dv_strategy = zfs_dev_strategy, 643 .dv_open = zfs_dev_open, 644 .dv_close = zfs_dev_close, 645 .dv_ioctl = noioctl, 646 .dv_print = zfs_dev_print, 647 .dv_cleanup = NULL 648 }; 649 650 int 651 zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) 652 { 653 static char rootname[ZFS_MAXNAMELEN]; 654 static char poolname[ZFS_MAXNAMELEN]; 655 spa_t *spa; 656 const char *end; 657 const char *np; 658 const char *sep; 659 int rv; 660 661 np = devspec; 662 if (*np != ':') 663 return (EINVAL); 664 np++; 665 end = strrchr(np, ':'); 666 if (end == NULL) 667 return (EINVAL); 668 sep = strchr(np, '/'); 669 if (sep == NULL || sep >= end) 670 sep = end; 671 memcpy(poolname, np, sep - np); 672 poolname[sep - np] = '\0'; 673 if (sep < end) { 674 sep++; 675 memcpy(rootname, sep, end - sep); 676 rootname[end - sep] = '\0'; 677 } 678 else 679 rootname[0] = '\0'; 680 681 spa = spa_find_by_name(poolname); 682 if (!spa) 683 return (ENXIO); 684 dev->pool_guid = spa->spa_guid; 685 rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid); 686 if (rv != 0) 687 return (rv); 688 if (path != NULL) 689 *path = (*end == '\0') ? end : end + 1; 690 dev->dd.d_dev = &zfs_dev; 691 return (0); 692 } 693 694 char * 695 zfs_fmtdev(void *vdev) 696 { 697 static char rootname[ZFS_MAXNAMELEN]; 698 static char buf[2 * ZFS_MAXNAMELEN + 8]; 699 struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; 700 spa_t *spa; 701 702 buf[0] = '\0'; 703 if (dev->dd.d_dev->dv_type != DEVT_ZFS) 704 return (buf); 705 706 if (dev->pool_guid == 0) { 707 spa = STAILQ_FIRST(&zfs_pools); 708 dev->pool_guid = spa->spa_guid; 709 } else 710 spa = spa_find_by_guid(dev->pool_guid); 711 if (spa == NULL) { 712 printf("ZFS: can't find pool by guid\n"); 713 return (buf); 714 } 715 if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { 716 printf("ZFS: can't find root filesystem\n"); 717 return (buf); 718 } 719 if (zfs_rlookup(spa, dev->root_guid, rootname)) { 720 printf("ZFS: can't find filesystem by guid\n"); 721 return (buf); 722 } 723 724 if (rootname[0] == '\0') 725 sprintf(buf, "%s:%s:", dev->dd.d_dev->dv_name, spa->spa_name); 726 else 727 sprintf(buf, "%s:%s/%s:", dev->dd.d_dev->dv_name, spa->spa_name, 728 rootname); 729 return (buf); 730 } 731 732 int 733 zfs_list(const char *name) 734 { 735 static char poolname[ZFS_MAXNAMELEN]; 736 uint64_t objid; 737 spa_t *spa; 738 const char *dsname; 739 int len; 740 int rv; 741 742 len = strlen(name); 743 dsname = strchr(name, '/'); 744 if (dsname != NULL) { 745 len = dsname - name; 746 dsname++; 747 } else 748 dsname = ""; 749 memcpy(poolname, name, len); 750 poolname[len] = '\0'; 751 752 spa = spa_find_by_name(poolname); 753 if (!spa) 754 return (ENXIO); 755 rv = zfs_lookup_dataset(spa, dsname, &objid); 756 if (rv != 0) 757 return (rv); 758 759 return (zfs_list_dataset(spa, objid)); 760 } 761 762 void 763 init_zfs_bootenv(const char *currdev_in) 764 { 765 char *beroot, *currdev; 766 int currdev_len; 767 768 currdev = NULL; 769 currdev_len = strlen(currdev_in); 770 if (currdev_len == 0) 771 return; 772 if (strncmp(currdev_in, "zfs:", 4) != 0) 773 return; 774 currdev = strdup(currdev_in); 775 if (currdev == NULL) 776 return; 777 /* Remove the trailing : */ 778 currdev[currdev_len - 1] = '\0'; 779 setenv("zfs_be_active", currdev, 1); 780 setenv("zfs_be_currpage", "1", 1); 781 /* Remove the last element (current bootenv) */ 782 beroot = strrchr(currdev, '/'); 783 if (beroot != NULL) 784 beroot[0] = '\0'; 785 beroot = strchr(currdev, ':') + 1; 786 setenv("zfs_be_root", beroot, 1); 787 zfs_bootenv_initial(beroot); 788 free(currdev); 789 } 790 791 static void 792 zfs_bootenv_initial(const char *name) 793 { 794 char poolname[ZFS_MAXNAMELEN], *dsname; 795 char envname[32], envval[256]; 796 uint64_t objid; 797 spa_t *spa; 798 int bootenvs_idx, len, rv; 799 800 SLIST_INIT(&zfs_be_head); 801 zfs_env_count = 0; 802 len = strlen(name); 803 dsname = strchr(name, '/'); 804 if (dsname != NULL) { 805 len = dsname - name; 806 dsname++; 807 } else 808 dsname = ""; 809 strlcpy(poolname, name, len + 1); 810 spa = spa_find_by_name(poolname); 811 if (spa == NULL) 812 return; 813 rv = zfs_lookup_dataset(spa, dsname, &objid); 814 if (rv != 0) 815 return; 816 rv = zfs_callback_dataset(spa, objid, zfs_belist_add); 817 bootenvs_idx = 0; 818 /* Populate the initial environment variables */ 819 SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { 820 /* Enumerate all bootenvs for general usage */ 821 snprintf(envname, sizeof(envname), "bootenvs[%d]", bootenvs_idx); 822 snprintf(envval, sizeof(envval), "zfs:%s/%s", name, zfs_be->name); 823 rv = setenv(envname, envval, 1); 824 if (rv != 0) 825 break; 826 bootenvs_idx++; 827 } 828 snprintf(envval, sizeof(envval), "%d", bootenvs_idx); 829 setenv("bootenvs_count", envval, 1); 830 831 /* Clean up the SLIST of ZFS BEs */ 832 while (!SLIST_EMPTY(&zfs_be_head)) { 833 zfs_be = SLIST_FIRST(&zfs_be_head); 834 SLIST_REMOVE_HEAD(&zfs_be_head, entries); 835 free(zfs_be); 836 } 837 838 return; 839 840 } 841 842 int 843 zfs_bootenv(const char *name) 844 { 845 static char poolname[ZFS_MAXNAMELEN], *dsname, *root; 846 char becount[4]; 847 uint64_t objid; 848 spa_t *spa; 849 int len, rv, pages, perpage, currpage; 850 851 if (name == NULL) 852 return (EINVAL); 853 if ((root = getenv("zfs_be_root")) == NULL) 854 return (EINVAL); 855 856 if (strcmp(name, root) != 0) { 857 if (setenv("zfs_be_root", name, 1) != 0) 858 return (ENOMEM); 859 } 860 861 SLIST_INIT(&zfs_be_head); 862 zfs_env_count = 0; 863 len = strlen(name); 864 dsname = strchr(name, '/'); 865 if (dsname != NULL) { 866 len = dsname - name; 867 dsname++; 868 } else 869 dsname = ""; 870 memcpy(poolname, name, len); 871 poolname[len] = '\0'; 872 873 spa = spa_find_by_name(poolname); 874 if (!spa) 875 return (ENXIO); 876 rv = zfs_lookup_dataset(spa, dsname, &objid); 877 if (rv != 0) 878 return (rv); 879 rv = zfs_callback_dataset(spa, objid, zfs_belist_add); 880 881 /* Calculate and store the number of pages of BEs */ 882 perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1); 883 pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0); 884 snprintf(becount, 4, "%d", pages); 885 if (setenv("zfs_be_pages", becount, 1) != 0) 886 return (ENOMEM); 887 888 /* Roll over the page counter if it has exceeded the maximum */ 889 currpage = strtol(getenv("zfs_be_currpage"), NULL, 10); 890 if (currpage > pages) { 891 if (setenv("zfs_be_currpage", "1", 1) != 0) 892 return (ENOMEM); 893 } 894 895 /* Populate the menu environment variables */ 896 zfs_set_env(); 897 898 /* Clean up the SLIST of ZFS BEs */ 899 while (!SLIST_EMPTY(&zfs_be_head)) { 900 zfs_be = SLIST_FIRST(&zfs_be_head); 901 SLIST_REMOVE_HEAD(&zfs_be_head, entries); 902 free(zfs_be); 903 } 904 905 return (rv); 906 } 907 908 int 909 zfs_belist_add(const char *name, uint64_t value __unused) 910 { 911 912 /* Skip special datasets that start with a $ character */ 913 if (strncmp(name, "$", 1) == 0) { 914 return (0); 915 } 916 /* Add the boot environment to the head of the SLIST */ 917 zfs_be = malloc(sizeof(struct zfs_be_entry)); 918 if (zfs_be == NULL) { 919 return (ENOMEM); 920 } 921 zfs_be->name = name; 922 SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries); 923 zfs_env_count++; 924 925 return (0); 926 } 927 928 int 929 zfs_set_env(void) 930 { 931 char envname[32], envval[256]; 932 char *beroot, *pagenum; 933 int rv, page, ctr; 934 935 beroot = getenv("zfs_be_root"); 936 if (beroot == NULL) { 937 return (1); 938 } 939 940 pagenum = getenv("zfs_be_currpage"); 941 if (pagenum != NULL) { 942 page = strtol(pagenum, NULL, 10); 943 } else { 944 page = 1; 945 } 946 947 ctr = 1; 948 rv = 0; 949 zfs_env_index = ZFS_BE_FIRST; 950 SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { 951 /* Skip to the requested page number */ 952 if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) { 953 ctr++; 954 continue; 955 } 956 957 snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); 958 snprintf(envval, sizeof(envval), "%s", zfs_be->name); 959 rv = setenv(envname, envval, 1); 960 if (rv != 0) { 961 break; 962 } 963 964 snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); 965 rv = setenv(envname, envval, 1); 966 if (rv != 0){ 967 break; 968 } 969 970 snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); 971 rv = setenv(envname, "set_bootenv", 1); 972 if (rv != 0){ 973 break; 974 } 975 976 snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); 977 snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name); 978 rv = setenv(envname, envval, 1); 979 if (rv != 0){ 980 break; 981 } 982 983 zfs_env_index++; 984 if (zfs_env_index > ZFS_BE_LAST) { 985 break; 986 } 987 988 } 989 990 for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) { 991 snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); 992 (void)unsetenv(envname); 993 snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); 994 (void)unsetenv(envname); 995 snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); 996 (void)unsetenv(envname); 997 snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); 998 (void)unsetenv(envname); 999 } 1000 1001 return (rv); 1002 } 1003