2 * Copyright (c) 2007 Doug Rabson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
30 * Stand-alone file reading package.
34 #include <sys/param.h>
36 #include <sys/queue.h>
42 #include <bootstrap.h>
48 /* Define the range of indexes to be populated with ZFS Boot Environments */
49 #define ZFS_BE_FIRST 4
52 static int zfs_open(const char *path
, struct open_file
*f
);
53 static int zfs_close(struct open_file
*f
);
54 static int zfs_read(struct open_file
*f
, void *buf
, size_t size
, size_t *resid
);
55 static off_t
zfs_seek(struct open_file
*f
, off_t offset
, int where
);
56 static int zfs_stat(struct open_file
*f
, struct stat
*sb
);
57 static int zfs_readdir(struct open_file
*f
, struct dirent
*d
);
61 struct fs_ops zfs_fsops
= {
76 off_t f_seekp
; /* seek pointer */
78 uint64_t f_zap_type
; /* zap type for readdir */
79 uint64_t f_num_leafs
; /* number of fzap leaf blocks */
80 zap_leaf_phys_t
*f_zap_leaf
; /* zap leaf buffer */
84 static int zfs_env_index
;
85 static int zfs_env_count
;
88 SLIST_HEAD(zfs_be_list
, zfs_be_entry
) zfs_be_head
= SLIST_HEAD_INITIALIZER(zfs_be_head
);
89 struct zfs_be_list
*zfs_be_headp
;
92 SLIST_ENTRY(zfs_be_entry
) entries
;
93 } *zfs_be
, *zfs_be_tmp
;
99 zfs_open(const char *upath
, struct open_file
*f
)
101 struct zfsmount
*mount
= (struct zfsmount
*)f
->f_devdata
;
105 if (f
->f_dev
!= &zfs_dev
)
108 /* allocate file system specific data structure */
109 fp
= malloc(sizeof(struct file
));
110 bzero(fp
, sizeof(struct file
));
111 f
->f_fsdata
= (void *)fp
;
113 rc
= zfs_lookup(mount
, upath
, &fp
->f_dnode
);
123 zfs_close(struct open_file
*f
)
125 struct file
*fp
= (struct file
*)f
->f_fsdata
;
128 f
->f_fsdata
= (void *)0;
129 if (fp
== (struct file
*)0)
137 * Copy a portion of a file into kernel memory.
138 * Cross block boundaries when necessary.
141 zfs_read(struct open_file
*f
, void *start
, size_t size
, size_t *resid
/* out */)
143 const spa_t
*spa
= ((struct zfsmount
*)f
->f_devdata
)->spa
;
144 struct file
*fp
= (struct file
*)f
->f_fsdata
;
149 rc
= zfs_stat(f
, &sb
);
153 if (fp
->f_seekp
+ n
> sb
.st_size
)
154 n
= sb
.st_size
- fp
->f_seekp
;
156 rc
= dnode_read(spa
, &fp
->f_dnode
, fp
->f_seekp
, start
, n
);
162 for (i
= 0; i
< n
; i
++)
163 putchar(((char*) start
)[i
]);
173 zfs_seek(struct open_file
*f
, off_t offset
, int where
)
175 struct file
*fp
= (struct file
*)f
->f_fsdata
;
179 fp
->f_seekp
= offset
;
182 fp
->f_seekp
+= offset
;
189 error
= zfs_stat(f
, &sb
);
194 fp
->f_seekp
= sb
.st_size
- offset
;
201 return (fp
->f_seekp
);
205 zfs_stat(struct open_file
*f
, struct stat
*sb
)
207 const spa_t
*spa
= ((struct zfsmount
*)f
->f_devdata
)->spa
;
208 struct file
*fp
= (struct file
*)f
->f_fsdata
;
210 return (zfs_dnode_stat(spa
, &fp
->f_dnode
, sb
));
214 zfs_readdir(struct open_file
*f
, struct dirent
*d
)
216 const spa_t
*spa
= ((struct zfsmount
*)f
->f_devdata
)->spa
;
217 struct file
*fp
= (struct file
*)f
->f_fsdata
;
220 size_t bsize
= fp
->f_dnode
.dn_datablkszsec
<< SPA_MINBLOCKSHIFT
;
223 rc
= zfs_stat(f
, &sb
);
226 if (!S_ISDIR(sb
.st_mode
))
230 * If this is the first read, get the zap type.
232 if (fp
->f_seekp
== 0) {
233 rc
= dnode_read(spa
, &fp
->f_dnode
,
234 0, &fp
->f_zap_type
, sizeof(fp
->f_zap_type
));
238 if (fp
->f_zap_type
== ZBT_MICRO
) {
239 fp
->f_seekp
= offsetof(mzap_phys_t
, mz_chunk
);
241 rc
= dnode_read(spa
, &fp
->f_dnode
,
242 offsetof(zap_phys_t
, zap_num_leafs
),
244 sizeof(fp
->f_num_leafs
));
249 fp
->f_zap_leaf
= (zap_leaf_phys_t
*)malloc(bsize
);
250 rc
= dnode_read(spa
, &fp
->f_dnode
,
259 if (fp
->f_zap_type
== ZBT_MICRO
) {
261 if (fp
->f_seekp
>= bsize
)
264 rc
= dnode_read(spa
, &fp
->f_dnode
,
265 fp
->f_seekp
, &mze
, sizeof(mze
));
268 fp
->f_seekp
+= sizeof(mze
);
270 if (!mze
.mze_name
[0])
273 d
->d_fileno
= ZFS_DIRENT_OBJ(mze
.mze_value
);
274 d
->d_type
= ZFS_DIRENT_TYPE(mze
.mze_value
);
275 strcpy(d
->d_name
, mze
.mze_name
);
276 d
->d_namlen
= strlen(d
->d_name
);
280 zap_leaf_chunk_t
*zc
, *nc
;
287 * Initialise this so we can use the ZAP size
288 * calculating macros.
290 zl
.l_bs
= ilog2(bsize
);
291 zl
.l_phys
= fp
->f_zap_leaf
;
294 * Figure out which chunk we are currently looking at
295 * and consider seeking to the next leaf. We use the
296 * low bits of f_seekp as a simple chunk index.
299 chunk
= fp
->f_seekp
& (bsize
- 1);
300 if (chunk
== ZAP_LEAF_NUMCHUNKS(&zl
)) {
301 fp
->f_seekp
= (fp
->f_seekp
& ~(bsize
- 1)) + bsize
;
305 * Check for EOF and read the new leaf.
307 if (fp
->f_seekp
>= bsize
* fp
->f_num_leafs
)
310 rc
= dnode_read(spa
, &fp
->f_dnode
,
318 zc
= &ZAP_LEAF_CHUNK(&zl
, chunk
);
320 if (zc
->l_entry
.le_type
!= ZAP_CHUNK_ENTRY
)
323 namelen
= zc
->l_entry
.le_name_numints
;
324 if (namelen
> sizeof(d
->d_name
))
325 namelen
= sizeof(d
->d_name
);
328 * Paste the name back together.
330 nc
= &ZAP_LEAF_CHUNK(&zl
, zc
->l_entry
.le_name_chunk
);
332 while (namelen
> 0) {
335 if (len
> ZAP_LEAF_ARRAY_BYTES
)
336 len
= ZAP_LEAF_ARRAY_BYTES
;
337 memcpy(p
, nc
->l_array
.la_array
, len
);
340 nc
= &ZAP_LEAF_CHUNK(&zl
, nc
->l_array
.la_next
);
342 d
->d_name
[sizeof(d
->d_name
) - 1] = 0;
345 * Assume the first eight bytes of the value are
348 value
= fzap_leaf_value(&zl
, zc
);
350 d
->d_fileno
= ZFS_DIRENT_OBJ(value
);
351 d
->d_type
= ZFS_DIRENT_TYPE(value
);
352 d
->d_namlen
= strlen(d
->d_name
);
359 vdev_read(vdev_t
*vdev
, void *priv
, off_t offset
, void *buf
, size_t bytes
)
362 size_t res
, size
, remainder
, rb_size
, blksz
;
365 char *bouncebuf
, *rb_buf
;
367 fd
= (uintptr_t) priv
;
370 ret
= ioctl(fd
, DIOCGSECTORSIZE
, &secsz
);
374 off
= offset
/ secsz
;
375 remainder
= offset
% secsz
;
376 if (lseek(fd
, off
* secsz
, SEEK_SET
) == -1)
381 size
= roundup2(bytes
+ remainder
, secsz
);
383 if (remainder
!= 0 || size
!= bytes
) {
384 bouncebuf
= zfs_alloc(secsz
);
385 if (bouncebuf
== NULL
) {
386 printf("vdev_read: out of memory\n");
390 blksz
= rb_size
- remainder
;
394 res
= read(fd
, rb_buf
, rb_size
);
395 if (res
!= rb_size
) {
401 if (bouncebuf
!= NULL
)
402 memcpy(buf
, rb_buf
+ remainder
, blksz
);
403 buf
= (void *)((uintptr_t)buf
+ blksz
);
411 if (bouncebuf
!= NULL
)
412 zfs_free(bouncebuf
, secsz
);
424 if (archsw
.arch_zfs_probe
== NULL
)
426 archsw
.arch_zfs_probe();
429 spa
= STAILQ_FIRST(&zfs_pools
);
430 while (spa
!= NULL
) {
431 next
= STAILQ_NEXT(spa
, spa_link
);
432 if (zfs_spa_init(spa
)) {
434 STAILQ_REMOVE_HEAD(&zfs_pools
, spa_link
);
436 STAILQ_REMOVE_AFTER(&zfs_pools
, prev
, spa_link
);
444 struct zfs_probe_args
{
452 zfs_diskread(void *arg
, void *buf
, size_t blocks
, uint64_t offset
)
454 struct zfs_probe_args
*ppa
;
456 ppa
= (struct zfs_probe_args
*)arg
;
457 return (vdev_read(NULL
, (void *)(uintptr_t)ppa
->fd
,
458 offset
* ppa
->secsz
, buf
, blocks
* ppa
->secsz
));
462 zfs_probe(int fd
, uint64_t *pool_guid
)
467 ret
= vdev_probe(vdev_read
, (void *)(uintptr_t)fd
, &spa
);
468 if (ret
== 0 && pool_guid
!= NULL
)
469 *pool_guid
= spa
->spa_guid
;
474 zfs_probe_partition(void *arg
, const char *partname
,
475 const struct ptable_entry
*part
)
477 struct zfs_probe_args
*ppa
, pa
;
478 struct ptable
*table
;
482 /* filter out partitions *not* used by zfs */
483 switch (part
->type
) {
484 case PART_RESERVED
: /* efi reserverd */
485 case PART_VTOC_BOOT
: /* vtoc boot area */
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
);
498 ret
= zfs_probe(pa
.fd
, ppa
->pool_guid
);
501 if (part
->type
== PART_SOLARIS2
) {
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
);
508 enum ptable_type pt
= ptable_gettype(table
);
510 if (pt
== PTABLE_VTOC8
|| pt
== PTABLE_VTOC
)
511 ptable_iterate(table
, &pa
, zfs_probe_partition
);
520 zfs_probe_dev(const char *devname
, uint64_t *pool_guid
)
522 struct ptable
*table
;
523 struct zfs_probe_args pa
;
529 pa
.fd
= open(devname
, O_RDONLY
);
532 /* Probe the whole disk */
533 ret
= zfs_probe(pa
.fd
, pool_guid
);
537 /* Probe each partition */
538 ret
= ioctl(pa
.fd
, DIOCGMEDIASIZE
, &mediasz
);
540 ret
= ioctl(pa
.fd
, DIOCGSECTORSIZE
, &pa
.secsz
);
542 pa
.devname
= devname
;
543 pa
.pool_guid
= pool_guid
;
544 table
= ptable_open(&pa
, mediasz
/ pa
.secsz
, pa
.secsz
,
547 ptable_iterate(table
, &pa
, zfs_probe_partition
);
552 if (pool_guid
&& *pool_guid
== 0)
558 * Print information about ZFS pools
561 zfs_dev_print(int verbose
)
567 if (STAILQ_EMPTY(&zfs_pools
))
570 printf("%s devices:", zfs_dev
.dv_name
);
571 if ((ret
= pager_output("\n")) != 0)
575 return (spa_all_status());
577 STAILQ_FOREACH(spa
, &zfs_pools
, spa_link
) {
578 sprintf(line
, " zfs:%s\n", spa
->spa_name
);
579 ret
= pager_output(line
);
587 * Attempt to open the pool described by (dev) for use by (f).
590 zfs_dev_open(struct open_file
*f
, ...)
593 struct zfs_devdesc
*dev
;
594 struct zfsmount
*mount
;
599 dev
= va_arg(args
, struct zfs_devdesc
*);
602 if (dev
->pool_guid
== 0)
603 spa
= STAILQ_FIRST(&zfs_pools
);
605 spa
= spa_find_by_guid(dev
->pool_guid
);
608 mount
= malloc(sizeof(*mount
));
609 rv
= zfs_mount(spa
, dev
->root_guid
, mount
);
614 if (mount
->objset
.os_type
!= DMU_OST_ZFS
) {
615 printf("Unexpected object set type %ju\n",
616 (uintmax_t)mount
->objset
.os_type
);
620 f
->f_devdata
= mount
;
626 zfs_dev_close(struct open_file
*f
)
635 zfs_dev_strategy(void *devdata
, int rw
, daddr_t dblk
, size_t size
,
636 char *buf
, size_t *rsize
)
642 struct devsw zfs_dev
= {
645 .dv_init
= zfs_dev_init
,
646 .dv_strategy
= zfs_dev_strategy
,
647 .dv_open
= zfs_dev_open
,
648 .dv_close
= zfs_dev_close
,
650 .dv_print
= zfs_dev_print
,
655 zfs_parsedev(struct zfs_devdesc
*dev
, const char *devspec
, const char **path
)
657 static char rootname
[ZFS_MAXNAMELEN
];
658 static char poolname
[ZFS_MAXNAMELEN
];
669 end
= strchr(np
, ':');
672 sep
= strchr(np
, '/');
673 if (sep
== NULL
|| sep
>= end
)
675 memcpy(poolname
, np
, sep
- np
);
676 poolname
[sep
- np
] = '\0';
679 memcpy(rootname
, sep
, end
- sep
);
680 rootname
[end
- sep
] = '\0';
685 spa
= spa_find_by_name(poolname
);
688 dev
->pool_guid
= spa
->spa_guid
;
689 rv
= zfs_lookup_dataset(spa
, rootname
, &dev
->root_guid
);
693 *path
= (*end
== '\0') ? end
: end
+ 1;
694 dev
->dd
.d_dev
= &zfs_dev
;
699 zfs_bootfs(void *zdev
)
701 static char rootname
[ZFS_MAXNAMELEN
];
702 static char buf
[2 * ZFS_MAXNAMELEN
];
703 struct zfs_devdesc
*dev
= (struct zfs_devdesc
*)zdev
;
709 if (dev
->dd
.d_dev
->dv_type
!= DEVT_ZFS
)
712 spa
= spa_find_by_guid(dev
->pool_guid
);
714 printf("ZFS: can't find pool by guid\n");
717 if (zfs_rlookup(spa
, dev
->root_guid
, rootname
)) {
718 printf("ZFS: can't find filesystem by guid\n");
721 if (zfs_lookup_dataset(spa
, rootname
, &objnum
)) {
722 printf("ZFS: can't find filesystem by name\n");
726 /* Set the environment. */
727 snprintf(buf
, sizeof (buf
), "%s/%llu", spa
->spa_name
,
728 (unsigned long long)objnum
);
729 setenv("zfs-bootfs", buf
, 1);
730 if (spa
->spa_boot_vdev
->v_phys_path
!= NULL
)
731 setenv("bootpath", spa
->spa_boot_vdev
->v_phys_path
, 1);
732 if (spa
->spa_boot_vdev
->v_devid
!= NULL
)
733 setenv("diskdevid", spa
->spa_boot_vdev
->v_devid
, 1);
736 * Build the command line string. Once our kernel will read
737 * the environment and we can stop caring about old kernels,
738 * we can remove this part.
740 snprintf(buf
, sizeof(buf
), "zfs-bootfs=%s/%llu", spa
->spa_name
,
741 (unsigned long long)objnum
);
743 if (spa
->spa_boot_vdev
->v_phys_path
!= NULL
) {
744 snprintf(buf
+n
, sizeof (buf
) - n
, ",bootpath=\"%s\"",
745 spa
->spa_boot_vdev
->v_phys_path
);
748 if (spa
->spa_boot_vdev
->v_devid
!= NULL
) {
749 snprintf(buf
+n
, sizeof (buf
) - n
, ",diskdevid=\"%s\"",
750 spa
->spa_boot_vdev
->v_devid
);
756 zfs_fmtdev(void *vdev
)
758 static char rootname
[ZFS_MAXNAMELEN
];
759 static char buf
[2 * ZFS_MAXNAMELEN
+ 8];
760 struct zfs_devdesc
*dev
= (struct zfs_devdesc
*)vdev
;
764 if (dev
->dd
.d_dev
->dv_type
!= DEVT_ZFS
)
767 if (dev
->pool_guid
== 0) {
768 spa
= STAILQ_FIRST(&zfs_pools
);
769 dev
->pool_guid
= spa
->spa_guid
;
771 spa
= spa_find_by_guid(dev
->pool_guid
);
773 printf("ZFS: can't find pool by guid\n");
776 if (dev
->root_guid
== 0 && zfs_get_root(spa
, &dev
->root_guid
)) {
777 printf("ZFS: can't find root filesystem\n");
780 if (zfs_rlookup(spa
, dev
->root_guid
, rootname
)) {
781 printf("ZFS: can't find filesystem by guid\n");
785 if (rootname
[0] == '\0')
786 sprintf(buf
, "%s:%s:", dev
->dd
.d_dev
->dv_name
, spa
->spa_name
);
788 sprintf(buf
, "%s:%s/%s:", dev
->dd
.d_dev
->dv_name
, spa
->spa_name
,
794 zfs_list(const char *name
)
796 static char poolname
[ZFS_MAXNAMELEN
];
804 dsname
= strchr(name
, '/');
805 if (dsname
!= NULL
) {
810 memcpy(poolname
, name
, len
);
811 poolname
[len
] = '\0';
813 spa
= spa_find_by_name(poolname
);
816 rv
= zfs_lookup_dataset(spa
, dsname
, &objid
);
820 return (zfs_list_dataset(spa
, objid
));
825 init_zfs_bootenv(char *currdev
)
829 if (strlen(currdev
) == 0)
831 if(strncmp(currdev
, "zfs:", 4) != 0)
833 /* Remove the trailing : */
834 currdev
[strlen(currdev
) - 1] = '\0';
835 setenv("zfs_be_active", currdev
, 1);
836 setenv("zfs_be_currpage", "1", 1);
837 /* Forward past zfs: */
838 currdev
= strchr(currdev
, ':');
840 /* Remove the last element (current bootenv) */
841 beroot
= strrchr(currdev
, '/');
845 setenv("zfs_be_root", beroot
, 1);
849 zfs_bootenv(const char *name
)
851 static char poolname
[ZFS_MAXNAMELEN
], *dsname
, *root
;
855 int len
, rv
, pages
, perpage
, currpage
;
859 if ((root
= getenv("zfs_be_root")) == NULL
)
862 if (strcmp(name
, root
) != 0) {
863 if (setenv("zfs_be_root", name
, 1) != 0)
867 SLIST_INIT(&zfs_be_head
);
870 dsname
= strchr(name
, '/');
871 if (dsname
!= NULL
) {
876 memcpy(poolname
, name
, len
);
877 poolname
[len
] = '\0';
879 spa
= spa_find_by_name(poolname
);
882 rv
= zfs_lookup_dataset(spa
, dsname
, &objid
);
885 rv
= zfs_callback_dataset(spa
, objid
, zfs_belist_add
);
887 /* Calculate and store the number of pages of BEs */
888 perpage
= (ZFS_BE_LAST
- ZFS_BE_FIRST
+ 1);
889 pages
= (zfs_env_count
/ perpage
) + ((zfs_env_count
% perpage
) > 0 ? 1 : 0);
890 snprintf(becount
, 4, "%d", pages
);
891 if (setenv("zfs_be_pages", becount
, 1) != 0)
894 /* Roll over the page counter if it has exceeded the maximum */
895 currpage
= strtol(getenv("zfs_be_currpage"), NULL
, 10);
896 if (currpage
> pages
) {
897 if (setenv("zfs_be_currpage", "1", 1) != 0)
901 /* Populate the menu environment variables */
904 /* Clean up the SLIST of ZFS BEs */
905 while (!SLIST_EMPTY(&zfs_be_head
)) {
906 zfs_be
= SLIST_FIRST(&zfs_be_head
);
907 SLIST_REMOVE_HEAD(&zfs_be_head
, entries
);
915 zfs_belist_add(const char *name
, uint64_t value __unused
)
918 /* Skip special datasets that start with a $ character */
919 if (strncmp(name
, "$", 1) == 0) {
922 /* Add the boot environment to the head of the SLIST */
923 zfs_be
= malloc(sizeof(struct zfs_be_entry
));
924 if (zfs_be
== NULL
) {
928 SLIST_INSERT_HEAD(&zfs_be_head
, zfs_be
, entries
);
937 char envname
[32], envval
[256];
938 char *beroot
, *pagenum
;
941 beroot
= getenv("zfs_be_root");
942 if (beroot
== NULL
) {
946 pagenum
= getenv("zfs_be_currpage");
947 if (pagenum
!= NULL
) {
948 page
= strtol(pagenum
, NULL
, 10);
955 zfs_env_index
= ZFS_BE_FIRST
;
956 SLIST_FOREACH_SAFE(zfs_be
, &zfs_be_head
, entries
, zfs_be_tmp
) {
957 /* Skip to the requested page number */
958 if (ctr
<= ((ZFS_BE_LAST
- ZFS_BE_FIRST
+ 1) * (page
- 1))) {
963 snprintf(envname
, sizeof(envname
), "bootenvmenu_caption[%d]", zfs_env_index
);
964 snprintf(envval
, sizeof(envval
), "%s", zfs_be
->name
);
965 rv
= setenv(envname
, envval
, 1);
970 snprintf(envname
, sizeof(envname
), "bootenvansi_caption[%d]", zfs_env_index
);
971 rv
= setenv(envname
, envval
, 1);
976 snprintf(envname
, sizeof(envname
), "bootenvmenu_command[%d]", zfs_env_index
);
977 rv
= setenv(envname
, "set_bootenv", 1);
982 snprintf(envname
, sizeof(envname
), "bootenv_root[%d]", zfs_env_index
);
983 snprintf(envval
, sizeof(envval
), "zfs:%s/%s", beroot
, zfs_be
->name
);
984 rv
= setenv(envname
, envval
, 1);
990 if (zfs_env_index
> ZFS_BE_LAST
) {
996 for (; zfs_env_index
<= ZFS_BE_LAST
; zfs_env_index
++) {
997 snprintf(envname
, sizeof(envname
), "bootenvmenu_caption[%d]", zfs_env_index
);
998 (void)unsetenv(envname
);
999 snprintf(envname
, sizeof(envname
), "bootenvansi_caption[%d]", zfs_env_index
);
1000 (void)unsetenv(envname
);
1001 snprintf(envname
, sizeof(envname
), "bootenvmenu_command[%d]", zfs_env_index
);
1002 (void)unsetenv(envname
);
1003 snprintf(envname
, sizeof(envname
), "bootenv_root[%d]", zfs_env_index
);
1004 (void)unsetenv(envname
);