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
29 #include <sys/cdefs.h>
32 * Stand-alone file reading package.
36 #include <sys/param.h>
38 #include <sys/queue.h>
44 #include <bootstrap.h>
50 /* Define the range of indexes to be populated with ZFS Boot Environments */
51 #define ZFS_BE_FIRST 4
54 static int zfs_open(const char *path
, struct open_file
*f
);
55 static int zfs_write(struct open_file
*f
, void *buf
, size_t size
, size_t *resid
);
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
);
64 struct fs_ops zfs_fsops
= {
79 off_t f_seekp
; /* seek pointer */
81 uint64_t f_zap_type
; /* zap type for readdir */
82 uint64_t f_num_leafs
; /* number of fzap leaf blocks */
83 zap_leaf_phys_t
*f_zap_leaf
; /* zap leaf buffer */
87 static int zfs_env_index
;
88 static int zfs_env_count
;
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
;
95 SLIST_ENTRY(zfs_be_entry
) entries
;
96 } *zfs_be
, *zfs_be_tmp
;
102 zfs_open(const char *upath
, struct open_file
*f
)
104 struct zfsmount
*mount
= (struct zfsmount
*)f
->f_devdata
;
108 if (f
->f_dev
!= &zfs_dev
)
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
;
116 rc
= zfs_lookup(mount
, upath
, &fp
->f_dnode
);
126 zfs_close(struct open_file
*f
)
128 struct file
*fp
= (struct file
*)f
->f_fsdata
;
131 f
->f_fsdata
= (void *)0;
132 if (fp
== (struct file
*)0)
140 * Copy a portion of a file into kernel memory.
141 * Cross block boundaries when necessary.
144 zfs_read(struct open_file
*f
, void *start
, size_t size
, size_t *resid
/* out */)
146 const spa_t
*spa
= ((struct zfsmount
*)f
->f_devdata
)->spa
;
147 struct file
*fp
= (struct file
*)f
->f_fsdata
;
152 rc
= zfs_stat(f
, &sb
);
156 if (fp
->f_seekp
+ n
> sb
.st_size
)
157 n
= sb
.st_size
- fp
->f_seekp
;
159 rc
= dnode_read(spa
, &fp
->f_dnode
, fp
->f_seekp
, start
, n
);
165 for (i
= 0; i
< n
; i
++)
166 putchar(((char*) start
)[i
]);
176 * Don't be silly - the bootstrap has no business writing anything.
179 zfs_write(struct open_file
*f
, void *start
, size_t size
, size_t *resid
/* out */)
186 zfs_seek(struct open_file
*f
, off_t offset
, int where
)
188 struct file
*fp
= (struct file
*)f
->f_fsdata
;
192 fp
->f_seekp
= offset
;
195 fp
->f_seekp
+= offset
;
202 error
= zfs_stat(f
, &sb
);
207 fp
->f_seekp
= sb
.st_size
- offset
;
214 return (fp
->f_seekp
);
218 zfs_stat(struct open_file
*f
, struct stat
*sb
)
220 const spa_t
*spa
= ((struct zfsmount
*)f
->f_devdata
)->spa
;
221 struct file
*fp
= (struct file
*)f
->f_fsdata
;
223 return (zfs_dnode_stat(spa
, &fp
->f_dnode
, sb
));
227 zfs_readdir(struct open_file
*f
, struct dirent
*d
)
229 const spa_t
*spa
= ((struct zfsmount
*)f
->f_devdata
)->spa
;
230 struct file
*fp
= (struct file
*)f
->f_fsdata
;
233 size_t bsize
= fp
->f_dnode
.dn_datablkszsec
<< SPA_MINBLOCKSHIFT
;
236 rc
= zfs_stat(f
, &sb
);
239 if (!S_ISDIR(sb
.st_mode
))
243 * If this is the first read, get the zap type.
245 if (fp
->f_seekp
== 0) {
246 rc
= dnode_read(spa
, &fp
->f_dnode
,
247 0, &fp
->f_zap_type
, sizeof(fp
->f_zap_type
));
251 if (fp
->f_zap_type
== ZBT_MICRO
) {
252 fp
->f_seekp
= offsetof(mzap_phys_t
, mz_chunk
);
254 rc
= dnode_read(spa
, &fp
->f_dnode
,
255 offsetof(zap_phys_t
, zap_num_leafs
),
257 sizeof(fp
->f_num_leafs
));
262 fp
->f_zap_leaf
= (zap_leaf_phys_t
*)malloc(bsize
);
263 rc
= dnode_read(spa
, &fp
->f_dnode
,
272 if (fp
->f_zap_type
== ZBT_MICRO
) {
274 if (fp
->f_seekp
>= bsize
)
277 rc
= dnode_read(spa
, &fp
->f_dnode
,
278 fp
->f_seekp
, &mze
, sizeof(mze
));
281 fp
->f_seekp
+= sizeof(mze
);
283 if (!mze
.mze_name
[0])
286 d
->d_fileno
= ZFS_DIRENT_OBJ(mze
.mze_value
);
287 d
->d_type
= ZFS_DIRENT_TYPE(mze
.mze_value
);
288 strcpy(d
->d_name
, mze
.mze_name
);
289 d
->d_namlen
= strlen(d
->d_name
);
293 zap_leaf_chunk_t
*zc
, *nc
;
300 * Initialise this so we can use the ZAP size
301 * calculating macros.
303 zl
.l_bs
= ilog2(bsize
);
304 zl
.l_phys
= fp
->f_zap_leaf
;
307 * Figure out which chunk we are currently looking at
308 * and consider seeking to the next leaf. We use the
309 * low bits of f_seekp as a simple chunk index.
312 chunk
= fp
->f_seekp
& (bsize
- 1);
313 if (chunk
== ZAP_LEAF_NUMCHUNKS(&zl
)) {
314 fp
->f_seekp
= (fp
->f_seekp
& ~(bsize
- 1)) + bsize
;
318 * Check for EOF and read the new leaf.
320 if (fp
->f_seekp
>= bsize
* fp
->f_num_leafs
)
323 rc
= dnode_read(spa
, &fp
->f_dnode
,
331 zc
= &ZAP_LEAF_CHUNK(&zl
, chunk
);
333 if (zc
->l_entry
.le_type
!= ZAP_CHUNK_ENTRY
)
336 namelen
= zc
->l_entry
.le_name_numints
;
337 if (namelen
> sizeof(d
->d_name
))
338 namelen
= sizeof(d
->d_name
);
341 * Paste the name back together.
343 nc
= &ZAP_LEAF_CHUNK(&zl
, zc
->l_entry
.le_name_chunk
);
345 while (namelen
> 0) {
348 if (len
> ZAP_LEAF_ARRAY_BYTES
)
349 len
= ZAP_LEAF_ARRAY_BYTES
;
350 memcpy(p
, nc
->l_array
.la_array
, len
);
353 nc
= &ZAP_LEAF_CHUNK(&zl
, nc
->l_array
.la_next
);
355 d
->d_name
[sizeof(d
->d_name
) - 1] = 0;
358 * Assume the first eight bytes of the value are
361 value
= fzap_leaf_value(&zl
, zc
);
363 d
->d_fileno
= ZFS_DIRENT_OBJ(value
);
364 d
->d_type
= ZFS_DIRENT_TYPE(value
);
365 d
->d_namlen
= strlen(d
->d_name
);
372 vdev_read(vdev_t
*vdev
, void *priv
, off_t offset
, void *buf
, size_t size
)
376 fd
= (uintptr_t) priv
;
377 lseek(fd
, offset
, SEEK_SET
);
378 if (read(fd
, buf
, size
) == size
) {
393 if (archsw
.arch_zfs_probe
== NULL
)
395 archsw
.arch_zfs_probe();
398 spa
= STAILQ_FIRST(&zfs_pools
);
399 while (spa
!= NULL
) {
400 next
= STAILQ_NEXT(spa
, spa_link
);
401 if (zfs_spa_init(spa
)) {
403 STAILQ_REMOVE_HEAD(&zfs_pools
, spa_link
);
405 STAILQ_REMOVE_AFTER(&zfs_pools
, prev
, spa_link
);
413 struct zfs_probe_args
{
421 zfs_diskread(void *arg
, void *buf
, size_t blocks
, uint64_t offset
)
423 struct zfs_probe_args
*ppa
;
425 ppa
= (struct zfs_probe_args
*)arg
;
426 return (vdev_read(NULL
, (void *)(uintptr_t)ppa
->fd
,
427 offset
* ppa
->secsz
, buf
, blocks
* ppa
->secsz
));
431 zfs_probe(int fd
, uint64_t *pool_guid
)
436 ret
= vdev_probe(vdev_read
, (void *)(uintptr_t)fd
, &spa
);
437 if (ret
== 0 && pool_guid
!= NULL
)
438 *pool_guid
= spa
->spa_guid
;
443 zfs_probe_partition(void *arg
, const char *partname
,
444 const struct ptable_entry
*part
)
446 struct zfs_probe_args
*ppa
, pa
;
447 struct ptable
*table
;
451 /* filter out partitions *not* used by zfs */
452 switch (part
->type
) {
453 case PART_RESERVED
: /* efi reserverd */
454 case PART_VTOC_BOOT
: /* vtoc boot area */
460 ppa
= (struct zfs_probe_args
*)arg
;
461 strncpy(devname
, ppa
->devname
, strlen(ppa
->devname
) - 1);
462 devname
[strlen(ppa
->devname
) - 1] = '\0';
463 sprintf(devname
, "%s%s:", devname
, partname
);
464 pa
.fd
= open(devname
, O_RDONLY
);
467 ret
= zfs_probe(pa
.fd
, ppa
->pool_guid
);
470 if (part
->type
== PART_SOLARIS2
) {
471 pa
.devname
= devname
;
472 pa
.pool_guid
= ppa
->pool_guid
;
473 pa
.secsz
= ppa
->secsz
;
474 table
= ptable_open(&pa
, part
->end
- part
->start
+ 1,
475 ppa
->secsz
, zfs_diskread
);
477 ptable_iterate(table
, &pa
, zfs_probe_partition
);
486 zfs_probe_dev(const char *devname
, uint64_t *pool_guid
)
488 struct ptable
*table
;
489 struct zfs_probe_args pa
;
495 pa
.fd
= open(devname
, O_RDONLY
);
498 /* Probe the whole disk */
499 ret
= zfs_probe(pa
.fd
, pool_guid
);
503 /* Probe each partition */
504 ret
= ioctl(pa
.fd
, DIOCGMEDIASIZE
, &mediasz
);
506 ret
= ioctl(pa
.fd
, DIOCGSECTORSIZE
, &pa
.secsz
);
508 pa
.devname
= devname
;
509 pa
.pool_guid
= pool_guid
;
510 table
= ptable_open(&pa
, mediasz
/ pa
.secsz
, pa
.secsz
,
513 ptable_iterate(table
, &pa
, zfs_probe_partition
);
518 if (pool_guid
&& *pool_guid
== 0)
524 * Print information about ZFS pools
527 zfs_dev_print(int verbose
)
533 if (STAILQ_EMPTY(&zfs_pools
))
536 printf("%s devices:", zfs_dev
.dv_name
);
537 if ((ret
= pager_output("\n")) != 0)
541 return (spa_all_status());
543 STAILQ_FOREACH(spa
, &zfs_pools
, spa_link
) {
544 sprintf(line
, " zfs:%s\n", spa
->spa_name
);
545 ret
= pager_output(line
);
553 * Attempt to open the pool described by (dev) for use by (f).
556 zfs_dev_open(struct open_file
*f
, ...)
559 struct zfs_devdesc
*dev
;
560 struct zfsmount
*mount
;
565 dev
= va_arg(args
, struct zfs_devdesc
*);
568 if (dev
->pool_guid
== 0)
569 spa
= STAILQ_FIRST(&zfs_pools
);
571 spa
= spa_find_by_guid(dev
->pool_guid
);
574 mount
= malloc(sizeof(*mount
));
575 rv
= zfs_mount(spa
, dev
->root_guid
, mount
);
580 if (mount
->objset
.os_type
!= DMU_OST_ZFS
) {
581 printf("Unexpected object set type %ju\n",
582 (uintmax_t)mount
->objset
.os_type
);
586 f
->f_devdata
= mount
;
592 zfs_dev_close(struct open_file
*f
)
601 zfs_dev_strategy(void *devdata
, int rw
, daddr_t dblk
, size_t size
,
602 char *buf
, size_t *rsize
)
608 struct devsw zfs_dev
= {
611 .dv_init
= zfs_dev_init
,
612 .dv_strategy
= zfs_dev_strategy
,
613 .dv_open
= zfs_dev_open
,
614 .dv_close
= zfs_dev_close
,
616 .dv_print
= zfs_dev_print
,
621 zfs_parsedev(struct zfs_devdesc
*dev
, const char *devspec
, const char **path
)
623 static char rootname
[ZFS_MAXNAMELEN
];
624 static char poolname
[ZFS_MAXNAMELEN
];
635 end
= strchr(np
, ':');
638 sep
= strchr(np
, '/');
639 if (sep
== NULL
|| sep
>= end
)
641 memcpy(poolname
, np
, sep
- np
);
642 poolname
[sep
- np
] = '\0';
645 memcpy(rootname
, sep
, end
- sep
);
646 rootname
[end
- sep
] = '\0';
651 spa
= spa_find_by_name(poolname
);
654 dev
->pool_guid
= spa
->spa_guid
;
655 rv
= zfs_lookup_dataset(spa
, rootname
, &dev
->root_guid
);
659 *path
= (*end
== '\0') ? end
: end
+ 1;
660 dev
->d_dev
= &zfs_dev
;
661 dev
->d_type
= zfs_dev
.dv_type
;
666 zfs_bootfs(void *zdev
)
668 static char rootname
[ZFS_MAXNAMELEN
];
669 static char buf
[2 * ZFS_MAXNAMELEN
];
670 struct zfs_devdesc
*dev
= (struct zfs_devdesc
*)zdev
;
678 if (dev
->d_type
!= DEVT_ZFS
)
681 spa
= spa_find_by_guid(dev
->pool_guid
);
683 printf("ZFS: can't find pool by guid\n");
686 if (zfs_rlookup(spa
, dev
->root_guid
, rootname
)) {
687 printf("ZFS: can't find filesystem by guid\n");
690 if (zfs_lookup_dataset(spa
, rootname
, &objnum
)) {
691 printf("ZFS: can't find filesystem by name\n");
695 STAILQ_FOREACH(vdev
, &spa
->spa_vdevs
, v_childlink
) {
696 STAILQ_FOREACH(kid
, &vdev
->v_children
, v_childlink
) {
698 if (kid
->v_state
== VDEV_STATE_HEALTHY
&&
699 kid
->v_phys_path
!= NULL
) {
707 if (vdev
->v_state
== VDEV_STATE_HEALTHY
&&
708 vdev
->v_phys_path
!= NULL
) {
714 * since this pool was used to read in the kernel and boot archive,
715 * there has to be at least one healthy vdev, therefore vdev
718 /* Set the environment. */
719 snprintf(buf
, sizeof (buf
), "%s/%llu", spa
->spa_name
,
720 (unsigned long long)objnum
);
721 setenv("zfs-bootfs", buf
, 1);
722 if (vdev
->v_phys_path
!= NULL
)
723 setenv("bootpath", vdev
->v_phys_path
, 1);
724 if (vdev
->v_devid
!= NULL
)
725 setenv("diskdevid", vdev
->v_devid
, 1);
728 * Build the command line string. Once our kernel will read
729 * the environment and we can stop caring about old kernels,
730 * we can remove this part.
732 snprintf(buf
, sizeof(buf
), "zfs-bootfs=%s/%llu", spa
->spa_name
,
733 (unsigned long long)objnum
);
735 if (vdev
->v_phys_path
!= NULL
) {
736 snprintf(buf
+n
, sizeof (buf
) - n
, ",bootpath=\"%s\"",
740 if (vdev
->v_devid
!= NULL
) {
741 snprintf(buf
+n
, sizeof (buf
) - n
, ",diskdevid=\"%s\"",
748 zfs_fmtdev(void *vdev
)
750 static char rootname
[ZFS_MAXNAMELEN
];
751 static char buf
[2 * ZFS_MAXNAMELEN
+ 8];
752 struct zfs_devdesc
*dev
= (struct zfs_devdesc
*)vdev
;
756 if (dev
->d_type
!= DEVT_ZFS
)
759 if (dev
->pool_guid
== 0) {
760 spa
= STAILQ_FIRST(&zfs_pools
);
761 dev
->pool_guid
= spa
->spa_guid
;
763 spa
= spa_find_by_guid(dev
->pool_guid
);
765 printf("ZFS: can't find pool by guid\n");
768 if (dev
->root_guid
== 0 && zfs_get_root(spa
, &dev
->root_guid
)) {
769 printf("ZFS: can't find root filesystem\n");
772 if (zfs_rlookup(spa
, dev
->root_guid
, rootname
)) {
773 printf("ZFS: can't find filesystem by guid\n");
777 if (rootname
[0] == '\0')
778 sprintf(buf
, "%s:%s:", dev
->d_dev
->dv_name
, spa
->spa_name
);
780 sprintf(buf
, "%s:%s/%s:", dev
->d_dev
->dv_name
, spa
->spa_name
,
786 zfs_list(const char *name
)
788 static char poolname
[ZFS_MAXNAMELEN
];
796 dsname
= strchr(name
, '/');
797 if (dsname
!= NULL
) {
802 memcpy(poolname
, name
, len
);
803 poolname
[len
] = '\0';
805 spa
= spa_find_by_name(poolname
);
808 rv
= zfs_lookup_dataset(spa
, dsname
, &objid
);
812 return (zfs_list_dataset(spa
, objid
));
817 init_zfs_bootenv(char *currdev
)
821 if (strlen(currdev
) == 0)
823 if(strncmp(currdev
, "zfs:", 4) != 0)
825 /* Remove the trailing : */
826 currdev
[strlen(currdev
) - 1] = '\0';
827 setenv("zfs_be_active", currdev
, 1);
828 setenv("zfs_be_currpage", "1", 1);
829 /* Forward past zfs: */
830 currdev
= strchr(currdev
, ':');
832 /* Remove the last element (current bootenv) */
833 beroot
= strrchr(currdev
, '/');
837 setenv("zfs_be_root", beroot
, 1);
841 zfs_bootenv(const char *name
)
843 static char poolname
[ZFS_MAXNAMELEN
], *dsname
, *root
;
847 int len
, rv
, pages
, perpage
, currpage
;
851 if ((root
= getenv("zfs_be_root")) == NULL
)
854 if (strcmp(name
, root
) != 0) {
855 if (setenv("zfs_be_root", name
, 1) != 0)
859 SLIST_INIT(&zfs_be_head
);
862 dsname
= strchr(name
, '/');
863 if (dsname
!= NULL
) {
868 memcpy(poolname
, name
, len
);
869 poolname
[len
] = '\0';
871 spa
= spa_find_by_name(poolname
);
874 rv
= zfs_lookup_dataset(spa
, dsname
, &objid
);
877 rv
= zfs_callback_dataset(spa
, objid
, zfs_belist_add
);
879 /* Calculate and store the number of pages of BEs */
880 perpage
= (ZFS_BE_LAST
- ZFS_BE_FIRST
+ 1);
881 pages
= (zfs_env_count
/ perpage
) + ((zfs_env_count
% perpage
) > 0 ? 1 : 0);
882 snprintf(becount
, 4, "%d", pages
);
883 if (setenv("zfs_be_pages", becount
, 1) != 0)
886 /* Roll over the page counter if it has exceeded the maximum */
887 currpage
= strtol(getenv("zfs_be_currpage"), NULL
, 10);
888 if (currpage
> pages
) {
889 if (setenv("zfs_be_currpage", "1", 1) != 0)
893 /* Populate the menu environment variables */
896 /* Clean up the SLIST of ZFS BEs */
897 while (!SLIST_EMPTY(&zfs_be_head
)) {
898 zfs_be
= SLIST_FIRST(&zfs_be_head
);
899 SLIST_REMOVE_HEAD(&zfs_be_head
, entries
);
907 zfs_belist_add(const char *name
, uint64_t value __unused
)
910 /* Skip special datasets that start with a $ character */
911 if (strncmp(name
, "$", 1) == 0) {
914 /* Add the boot environment to the head of the SLIST */
915 zfs_be
= malloc(sizeof(struct zfs_be_entry
));
916 if (zfs_be
== NULL
) {
920 SLIST_INSERT_HEAD(&zfs_be_head
, zfs_be
, entries
);
929 char envname
[32], envval
[256];
930 char *beroot
, *pagenum
;
933 beroot
= getenv("zfs_be_root");
934 if (beroot
== NULL
) {
938 pagenum
= getenv("zfs_be_currpage");
939 if (pagenum
!= NULL
) {
940 page
= strtol(pagenum
, NULL
, 10);
947 zfs_env_index
= ZFS_BE_FIRST
;
948 SLIST_FOREACH_SAFE(zfs_be
, &zfs_be_head
, entries
, zfs_be_tmp
) {
949 /* Skip to the requested page number */
950 if (ctr
<= ((ZFS_BE_LAST
- ZFS_BE_FIRST
+ 1) * (page
- 1))) {
955 snprintf(envname
, sizeof(envname
), "bootenvmenu_caption[%d]", zfs_env_index
);
956 snprintf(envval
, sizeof(envval
), "%s", zfs_be
->name
);
957 rv
= setenv(envname
, envval
, 1);
962 snprintf(envname
, sizeof(envname
), "bootenvansi_caption[%d]", zfs_env_index
);
963 rv
= setenv(envname
, envval
, 1);
968 snprintf(envname
, sizeof(envname
), "bootenvmenu_command[%d]", zfs_env_index
);
969 rv
= setenv(envname
, "set_bootenv", 1);
974 snprintf(envname
, sizeof(envname
), "bootenv_root[%d]", zfs_env_index
);
975 snprintf(envval
, sizeof(envval
), "zfs:%s/%s", beroot
, zfs_be
->name
);
976 rv
= setenv(envname
, envval
, 1);
982 if (zfs_env_index
> ZFS_BE_LAST
) {
988 for (; zfs_env_index
<= ZFS_BE_LAST
; zfs_env_index
++) {
989 snprintf(envname
, sizeof(envname
), "bootenvmenu_caption[%d]", zfs_env_index
);
990 (void)unsetenv(envname
);
991 snprintf(envname
, sizeof(envname
), "bootenvansi_caption[%d]", zfs_env_index
);
992 (void)unsetenv(envname
);
993 snprintf(envname
, sizeof(envname
), "bootenvmenu_command[%d]", zfs_env_index
);
994 (void)unsetenv(envname
);
995 snprintf(envname
, sizeof(envname
), "bootenv_root[%d]", zfs_env_index
);
996 (void)unsetenv(envname
);