1b8902de1SWarner Losh /*-
2b8902de1SWarner Losh * Copyright (c) 2007 Doug Rabson
3b8902de1SWarner Losh * All rights reserved.
4b8902de1SWarner Losh *
5b8902de1SWarner Losh * Redistribution and use in source and binary forms, with or without
6b8902de1SWarner Losh * modification, are permitted provided that the following conditions
7b8902de1SWarner Losh * are met:
8b8902de1SWarner Losh * 1. Redistributions of source code must retain the above copyright
9b8902de1SWarner Losh * notice, this list of conditions and the following disclaimer.
10b8902de1SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright
11b8902de1SWarner Losh * notice, this list of conditions and the following disclaimer in the
12b8902de1SWarner Losh * documentation and/or other materials provided with the distribution.
13b8902de1SWarner Losh *
14b8902de1SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15b8902de1SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16b8902de1SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17b8902de1SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18b8902de1SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19b8902de1SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20b8902de1SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21b8902de1SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22b8902de1SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23b8902de1SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24b8902de1SWarner Losh * SUCH DAMAGE.
25b8902de1SWarner Losh */
26b8902de1SWarner Losh
27b8902de1SWarner Losh /*
28b8902de1SWarner Losh * Stand-alone file reading package.
29b8902de1SWarner Losh */
30b8902de1SWarner Losh
314f22b40aSToomas Soome #include <stand.h>
32b8902de1SWarner Losh #include <sys/disk.h>
33b8902de1SWarner Losh #include <sys/param.h>
34b8902de1SWarner Losh #include <sys/time.h>
35b8902de1SWarner Losh #include <sys/queue.h>
36b8902de1SWarner Losh #include <part.h>
37b8902de1SWarner Losh #include <stddef.h>
38b8902de1SWarner Losh #include <stdarg.h>
39b8902de1SWarner Losh #include <string.h>
40b8902de1SWarner Losh #include <bootstrap.h>
41b8902de1SWarner Losh
42b8902de1SWarner Losh #include "libzfs.h"
43b8902de1SWarner Losh
44b8902de1SWarner Losh #include "zfsimpl.c"
45b8902de1SWarner Losh
46b8902de1SWarner Losh /* Define the range of indexes to be populated with ZFS Boot Environments */
47b8902de1SWarner Losh #define ZFS_BE_FIRST 4
48b8902de1SWarner Losh #define ZFS_BE_LAST 8
49b8902de1SWarner Losh
50b8902de1SWarner Losh static int zfs_open(const char *path, struct open_file *f);
51b8902de1SWarner Losh static int zfs_close(struct open_file *f);
52b8902de1SWarner Losh static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
53b8902de1SWarner Losh static off_t zfs_seek(struct open_file *f, off_t offset, int where);
54b8902de1SWarner Losh static int zfs_stat(struct open_file *f, struct stat *sb);
55b8902de1SWarner Losh static int zfs_readdir(struct open_file *f, struct dirent *d);
56b4cb3fe0SToomas Soome static int zfs_mount(const char *dev, const char *path, void **data);
57b4cb3fe0SToomas Soome static int zfs_unmount(const char *dev, void *data);
58b8902de1SWarner Losh
59277f38abSMariusz Zaborski static void zfs_bootenv_initial(const char *envname, spa_t *spa,
60277f38abSMariusz Zaborski const char *name, const char *dsname, int checkpoint);
61277f38abSMariusz Zaborski static void zfs_checkpoints_initial(spa_t *spa, const char *name,
62277f38abSMariusz Zaborski const char *dsname);
63b8902de1SWarner Losh
6490412431SWarner Losh static int zfs_parsedev(struct devdesc **idev, const char *devspec,
6590412431SWarner Losh const char **path);
6690412431SWarner Losh
67b8902de1SWarner Losh struct devsw zfs_dev;
68b8902de1SWarner Losh
69b8902de1SWarner Losh struct fs_ops zfs_fsops = {
70b4cb3fe0SToomas Soome .fs_name = "zfs",
71b4cb3fe0SToomas Soome .fo_open = zfs_open,
72b4cb3fe0SToomas Soome .fo_close = zfs_close,
73b4cb3fe0SToomas Soome .fo_read = zfs_read,
74b4cb3fe0SToomas Soome .fo_write = null_write,
75b4cb3fe0SToomas Soome .fo_seek = zfs_seek,
76b4cb3fe0SToomas Soome .fo_stat = zfs_stat,
77b4cb3fe0SToomas Soome .fo_readdir = zfs_readdir,
78b4cb3fe0SToomas Soome .fo_mount = zfs_mount,
79b4cb3fe0SToomas Soome .fo_unmount = zfs_unmount
80b8902de1SWarner Losh };
81b8902de1SWarner Losh
82b8902de1SWarner Losh /*
83b8902de1SWarner Losh * In-core open file.
84b8902de1SWarner Losh */
85b8902de1SWarner Losh struct file {
86b8902de1SWarner Losh off_t f_seekp; /* seek pointer */
87b8902de1SWarner Losh dnode_phys_t f_dnode;
88b8902de1SWarner Losh uint64_t f_zap_type; /* zap type for readdir */
89b8902de1SWarner Losh uint64_t f_num_leafs; /* number of fzap leaf blocks */
90b8902de1SWarner Losh zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */
91b8902de1SWarner Losh };
92b8902de1SWarner Losh
93b8902de1SWarner Losh static int zfs_env_index;
94b8902de1SWarner Losh static int zfs_env_count;
95b8902de1SWarner Losh
96b8902de1SWarner Losh SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head);
97b8902de1SWarner Losh struct zfs_be_list *zfs_be_headp;
98b8902de1SWarner Losh struct zfs_be_entry {
996c01b710SToomas Soome char *name;
100b8902de1SWarner Losh SLIST_ENTRY(zfs_be_entry) entries;
101b8902de1SWarner Losh } *zfs_be, *zfs_be_tmp;
102b8902de1SWarner Losh
103b8902de1SWarner Losh /*
104b8902de1SWarner Losh * Open a file.
105b8902de1SWarner Losh */
106b8902de1SWarner Losh static int
zfs_open(const char * upath,struct open_file * f)107b8902de1SWarner Losh zfs_open(const char *upath, struct open_file *f)
108b8902de1SWarner Losh {
109d98de744SToomas Soome struct devdesc *dev = f->f_devdata;
110d98de744SToomas Soome struct zfsmount *mount = dev->d_opendata;
111b8902de1SWarner Losh struct file *fp;
112b8902de1SWarner Losh int rc;
113b8902de1SWarner Losh
114b8902de1SWarner Losh if (f->f_dev != &zfs_dev)
115b8902de1SWarner Losh return (EINVAL);
116b8902de1SWarner Losh
117b8902de1SWarner Losh /* allocate file system specific data structure */
11821da9f14SToomas Soome fp = calloc(1, sizeof(struct file));
11921da9f14SToomas Soome if (fp == NULL)
12021da9f14SToomas Soome return (ENOMEM);
12121da9f14SToomas Soome f->f_fsdata = fp;
122b8902de1SWarner Losh
123b8902de1SWarner Losh rc = zfs_lookup(mount, upath, &fp->f_dnode);
124b8902de1SWarner Losh fp->f_seekp = 0;
125b8902de1SWarner Losh if (rc) {
126b8902de1SWarner Losh f->f_fsdata = NULL;
127b8902de1SWarner Losh free(fp);
128b8902de1SWarner Losh }
129b8902de1SWarner Losh return (rc);
130b8902de1SWarner Losh }
131b8902de1SWarner Losh
132b8902de1SWarner Losh static int
zfs_close(struct open_file * f)133b8902de1SWarner Losh zfs_close(struct open_file *f)
134b8902de1SWarner Losh {
135b8902de1SWarner Losh struct file *fp = (struct file *)f->f_fsdata;
136b8902de1SWarner Losh
137b8902de1SWarner Losh dnode_cache_obj = NULL;
13821da9f14SToomas Soome f->f_fsdata = NULL;
139b8902de1SWarner Losh
140b8902de1SWarner Losh free(fp);
141b8902de1SWarner Losh return (0);
142b8902de1SWarner Losh }
143b8902de1SWarner Losh
144b8902de1SWarner Losh /*
145b8902de1SWarner Losh * Copy a portion of a file into kernel memory.
146b8902de1SWarner Losh * Cross block boundaries when necessary.
147b8902de1SWarner Losh */
148b8902de1SWarner Losh static int
zfs_read(struct open_file * f,void * start,size_t size,size_t * resid)149b8902de1SWarner Losh zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */)
150b8902de1SWarner Losh {
151d98de744SToomas Soome struct devdesc *dev = f->f_devdata;
152d98de744SToomas Soome const spa_t *spa = ((struct zfsmount *)dev->d_opendata)->spa;
153b8902de1SWarner Losh struct file *fp = (struct file *)f->f_fsdata;
154b8902de1SWarner Losh struct stat sb;
155b8902de1SWarner Losh size_t n;
156b8902de1SWarner Losh int rc;
157b8902de1SWarner Losh
158b8902de1SWarner Losh rc = zfs_stat(f, &sb);
159b8902de1SWarner Losh if (rc)
160b8902de1SWarner Losh return (rc);
161b8902de1SWarner Losh n = size;
162b8902de1SWarner Losh if (fp->f_seekp + n > sb.st_size)
163b8902de1SWarner Losh n = sb.st_size - fp->f_seekp;
164b8902de1SWarner Losh
165b8902de1SWarner Losh rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n);
166b8902de1SWarner Losh if (rc)
167b8902de1SWarner Losh return (rc);
168b8902de1SWarner Losh
169b8902de1SWarner Losh if (0) {
170b8902de1SWarner Losh int i;
171b8902de1SWarner Losh for (i = 0; i < n; i++)
172b8902de1SWarner Losh putchar(((char*) start)[i]);
173b8902de1SWarner Losh }
174b8902de1SWarner Losh fp->f_seekp += n;
175b8902de1SWarner Losh if (resid)
176b8902de1SWarner Losh *resid = size - n;
177b8902de1SWarner Losh
178b8902de1SWarner Losh return (0);
179b8902de1SWarner Losh }
180b8902de1SWarner Losh
181b8902de1SWarner Losh static off_t
zfs_seek(struct open_file * f,off_t offset,int where)182b8902de1SWarner Losh zfs_seek(struct open_file *f, off_t offset, int where)
183b8902de1SWarner Losh {
184b8902de1SWarner Losh struct file *fp = (struct file *)f->f_fsdata;
185b8902de1SWarner Losh
186b8902de1SWarner Losh switch (where) {
187b8902de1SWarner Losh case SEEK_SET:
188b8902de1SWarner Losh fp->f_seekp = offset;
189b8902de1SWarner Losh break;
190b8902de1SWarner Losh case SEEK_CUR:
191b8902de1SWarner Losh fp->f_seekp += offset;
192b8902de1SWarner Losh break;
193b8902de1SWarner Losh case SEEK_END:
194b8902de1SWarner Losh {
195b8902de1SWarner Losh struct stat sb;
196b8902de1SWarner Losh int error;
197b8902de1SWarner Losh
198b8902de1SWarner Losh error = zfs_stat(f, &sb);
199b8902de1SWarner Losh if (error != 0) {
200b8902de1SWarner Losh errno = error;
201b8902de1SWarner Losh return (-1);
202b8902de1SWarner Losh }
203b8902de1SWarner Losh fp->f_seekp = sb.st_size - offset;
204b8902de1SWarner Losh break;
205b8902de1SWarner Losh }
206b8902de1SWarner Losh default:
207b8902de1SWarner Losh errno = EINVAL;
208b8902de1SWarner Losh return (-1);
209b8902de1SWarner Losh }
210b8902de1SWarner Losh return (fp->f_seekp);
211b8902de1SWarner Losh }
212b8902de1SWarner Losh
213b8902de1SWarner Losh static int
zfs_stat(struct open_file * f,struct stat * sb)214b8902de1SWarner Losh zfs_stat(struct open_file *f, struct stat *sb)
215b8902de1SWarner Losh {
216d98de744SToomas Soome struct devdesc *dev = f->f_devdata;
217d98de744SToomas Soome const spa_t *spa = ((struct zfsmount *)dev->d_opendata)->spa;
218b8902de1SWarner Losh struct file *fp = (struct file *)f->f_fsdata;
219b8902de1SWarner Losh
220b8902de1SWarner Losh return (zfs_dnode_stat(spa, &fp->f_dnode, sb));
221b8902de1SWarner Losh }
222b8902de1SWarner Losh
223b8902de1SWarner Losh static int
zfs_readdir(struct open_file * f,struct dirent * d)224b8902de1SWarner Losh zfs_readdir(struct open_file *f, struct dirent *d)
225b8902de1SWarner Losh {
226d98de744SToomas Soome struct devdesc *dev = f->f_devdata;
227d98de744SToomas Soome const spa_t *spa = ((struct zfsmount *)dev->d_opendata)->spa;
228b8902de1SWarner Losh struct file *fp = (struct file *)f->f_fsdata;
229b8902de1SWarner Losh mzap_ent_phys_t mze;
230b8902de1SWarner Losh struct stat sb;
231b8902de1SWarner Losh size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT;
232b8902de1SWarner Losh int rc;
233b8902de1SWarner Losh
234b8902de1SWarner Losh rc = zfs_stat(f, &sb);
235b8902de1SWarner Losh if (rc)
236b8902de1SWarner Losh return (rc);
237b8902de1SWarner Losh if (!S_ISDIR(sb.st_mode))
238b8902de1SWarner Losh return (ENOTDIR);
239b8902de1SWarner Losh
240b8902de1SWarner Losh /*
241b8902de1SWarner Losh * If this is the first read, get the zap type.
242b8902de1SWarner Losh */
243b8902de1SWarner Losh if (fp->f_seekp == 0) {
244b8902de1SWarner Losh rc = dnode_read(spa, &fp->f_dnode,
245b8902de1SWarner Losh 0, &fp->f_zap_type, sizeof(fp->f_zap_type));
246b8902de1SWarner Losh if (rc)
247b8902de1SWarner Losh return (rc);
248b8902de1SWarner Losh
249b8902de1SWarner Losh if (fp->f_zap_type == ZBT_MICRO) {
250b8902de1SWarner Losh fp->f_seekp = offsetof(mzap_phys_t, mz_chunk);
251b8902de1SWarner Losh } else {
252b8902de1SWarner Losh rc = dnode_read(spa, &fp->f_dnode,
253b8902de1SWarner Losh offsetof(zap_phys_t, zap_num_leafs),
254b8902de1SWarner Losh &fp->f_num_leafs,
255b8902de1SWarner Losh sizeof(fp->f_num_leafs));
256b8902de1SWarner Losh if (rc)
257b8902de1SWarner Losh return (rc);
258b8902de1SWarner Losh
259b8902de1SWarner Losh fp->f_seekp = bsize;
26021da9f14SToomas Soome fp->f_zap_leaf = malloc(bsize);
26121da9f14SToomas Soome if (fp->f_zap_leaf == NULL)
26221da9f14SToomas Soome return (ENOMEM);
263b8902de1SWarner Losh rc = dnode_read(spa, &fp->f_dnode,
264b8902de1SWarner Losh fp->f_seekp,
265b8902de1SWarner Losh fp->f_zap_leaf,
266b8902de1SWarner Losh bsize);
267b8902de1SWarner Losh if (rc)
268b8902de1SWarner Losh return (rc);
269b8902de1SWarner Losh }
270b8902de1SWarner Losh }
271b8902de1SWarner Losh
272b8902de1SWarner Losh if (fp->f_zap_type == ZBT_MICRO) {
273b8902de1SWarner Losh mzap_next:
274b8902de1SWarner Losh if (fp->f_seekp >= bsize)
275b8902de1SWarner Losh return (ENOENT);
276b8902de1SWarner Losh
277b8902de1SWarner Losh rc = dnode_read(spa, &fp->f_dnode,
278b8902de1SWarner Losh fp->f_seekp, &mze, sizeof(mze));
279b8902de1SWarner Losh if (rc)
280b8902de1SWarner Losh return (rc);
281b8902de1SWarner Losh fp->f_seekp += sizeof(mze);
282b8902de1SWarner Losh
283b8902de1SWarner Losh if (!mze.mze_name[0])
284b8902de1SWarner Losh goto mzap_next;
285b8902de1SWarner Losh
286b8902de1SWarner Losh d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value);
287b8902de1SWarner Losh d->d_type = ZFS_DIRENT_TYPE(mze.mze_value);
288b8902de1SWarner Losh strcpy(d->d_name, mze.mze_name);
289b8902de1SWarner Losh d->d_namlen = strlen(d->d_name);
290b8902de1SWarner Losh return (0);
291b8902de1SWarner Losh } else {
292b8902de1SWarner Losh zap_leaf_t zl;
293b8902de1SWarner Losh zap_leaf_chunk_t *zc, *nc;
294b8902de1SWarner Losh int chunk;
295b8902de1SWarner Losh size_t namelen;
296b8902de1SWarner Losh char *p;
297b8902de1SWarner Losh uint64_t value;
298b8902de1SWarner Losh
299b8902de1SWarner Losh /*
300b8902de1SWarner Losh * Initialise this so we can use the ZAP size
301b8902de1SWarner Losh * calculating macros.
302b8902de1SWarner Losh */
303b8902de1SWarner Losh zl.l_bs = ilog2(bsize);
304b8902de1SWarner Losh zl.l_phys = fp->f_zap_leaf;
305b8902de1SWarner Losh
306b8902de1SWarner Losh /*
307b8902de1SWarner Losh * Figure out which chunk we are currently looking at
308b8902de1SWarner Losh * and consider seeking to the next leaf. We use the
309b8902de1SWarner Losh * low bits of f_seekp as a simple chunk index.
310b8902de1SWarner Losh */
311b8902de1SWarner Losh fzap_next:
312b8902de1SWarner Losh chunk = fp->f_seekp & (bsize - 1);
313b8902de1SWarner Losh if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) {
314b8902de1SWarner Losh fp->f_seekp = rounddown2(fp->f_seekp, bsize) + bsize;
315b8902de1SWarner Losh chunk = 0;
316b8902de1SWarner Losh
317b8902de1SWarner Losh /*
318b8902de1SWarner Losh * Check for EOF and read the new leaf.
319b8902de1SWarner Losh */
320b8902de1SWarner Losh if (fp->f_seekp >= bsize * fp->f_num_leafs)
321b8902de1SWarner Losh return (ENOENT);
322b8902de1SWarner Losh
323b8902de1SWarner Losh rc = dnode_read(spa, &fp->f_dnode,
324b8902de1SWarner Losh fp->f_seekp,
325b8902de1SWarner Losh fp->f_zap_leaf,
326b8902de1SWarner Losh bsize);
327b8902de1SWarner Losh if (rc)
328b8902de1SWarner Losh return (rc);
329b8902de1SWarner Losh }
330b8902de1SWarner Losh
331b8902de1SWarner Losh zc = &ZAP_LEAF_CHUNK(&zl, chunk);
332b8902de1SWarner Losh fp->f_seekp++;
333b8902de1SWarner Losh if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
334b8902de1SWarner Losh goto fzap_next;
335b8902de1SWarner Losh
336b8902de1SWarner Losh namelen = zc->l_entry.le_name_numints;
337b8902de1SWarner Losh if (namelen > sizeof(d->d_name))
338b8902de1SWarner Losh namelen = sizeof(d->d_name);
339b8902de1SWarner Losh
340b8902de1SWarner Losh /*
341b8902de1SWarner Losh * Paste the name back together.
342b8902de1SWarner Losh */
343b8902de1SWarner Losh nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
344b8902de1SWarner Losh p = d->d_name;
345b8902de1SWarner Losh while (namelen > 0) {
346b8902de1SWarner Losh int len;
347b8902de1SWarner Losh len = namelen;
348b8902de1SWarner Losh if (len > ZAP_LEAF_ARRAY_BYTES)
349b8902de1SWarner Losh len = ZAP_LEAF_ARRAY_BYTES;
350b8902de1SWarner Losh memcpy(p, nc->l_array.la_array, len);
351b8902de1SWarner Losh p += len;
352b8902de1SWarner Losh namelen -= len;
353b8902de1SWarner Losh nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
354b8902de1SWarner Losh }
355b8902de1SWarner Losh d->d_name[sizeof(d->d_name) - 1] = 0;
356b8902de1SWarner Losh
357b8902de1SWarner Losh /*
358b8902de1SWarner Losh * Assume the first eight bytes of the value are
359b8902de1SWarner Losh * a uint64_t.
360b8902de1SWarner Losh */
361b8902de1SWarner Losh value = fzap_leaf_value(&zl, zc);
362b8902de1SWarner Losh
363b8902de1SWarner Losh d->d_fileno = ZFS_DIRENT_OBJ(value);
364b8902de1SWarner Losh d->d_type = ZFS_DIRENT_TYPE(value);
365b8902de1SWarner Losh d->d_namlen = strlen(d->d_name);
366b8902de1SWarner Losh
367b8902de1SWarner Losh return (0);
368b8902de1SWarner Losh }
369b8902de1SWarner Losh }
370b8902de1SWarner Losh
371439a9766SWarner Losh static spa_t *
spa_find_by_dev(struct zfs_devdesc * dev)372439a9766SWarner Losh spa_find_by_dev(struct zfs_devdesc *dev)
373439a9766SWarner Losh {
374439a9766SWarner Losh
375439a9766SWarner Losh if (dev->dd.d_dev->dv_type != DEVT_ZFS)
376439a9766SWarner Losh return (NULL);
377439a9766SWarner Losh
378439a9766SWarner Losh if (dev->pool_guid == 0)
379439a9766SWarner Losh return (STAILQ_FIRST(&zfs_pools));
380439a9766SWarner Losh
381439a9766SWarner Losh return (spa_find_by_guid(dev->pool_guid));
382439a9766SWarner Losh }
383439a9766SWarner Losh
384b4cb3fe0SToomas Soome /*
385b4cb3fe0SToomas Soome * if path is NULL, create mount structure, but do not add it to list.
386b4cb3fe0SToomas Soome */
387b4cb3fe0SToomas Soome static int
zfs_mount(const char * dev,const char * path,void ** data)388b4cb3fe0SToomas Soome zfs_mount(const char *dev, const char *path, void **data)
389b4cb3fe0SToomas Soome {
3905385c7e1SWarner Losh struct zfs_devdesc *zfsdev = NULL;
391b4cb3fe0SToomas Soome spa_t *spa;
3925385c7e1SWarner Losh struct zfsmount *mnt = NULL;
393b4cb3fe0SToomas Soome int rv;
394b4cb3fe0SToomas Soome
395b4cb3fe0SToomas Soome errno = 0;
39633bbe5ddSWarner Losh rv = zfs_parsedev((struct devdesc **)&zfsdev, dev, NULL);
397b4cb3fe0SToomas Soome if (rv != 0) {
398b4cb3fe0SToomas Soome return (rv);
399b4cb3fe0SToomas Soome }
400b4cb3fe0SToomas Soome
401b4cb3fe0SToomas Soome spa = spa_find_by_dev(zfsdev);
4025385c7e1SWarner Losh if (spa == NULL) {
4035385c7e1SWarner Losh rv = ENXIO;
4045385c7e1SWarner Losh goto err;
4055385c7e1SWarner Losh }
406b4cb3fe0SToomas Soome
407b4cb3fe0SToomas Soome mnt = calloc(1, sizeof(*mnt));
4085385c7e1SWarner Losh if (mnt == NULL) {
4095385c7e1SWarner Losh rv = ENOMEM;
4105385c7e1SWarner Losh goto err;
4115385c7e1SWarner Losh }
4125385c7e1SWarner Losh
4135385c7e1SWarner Losh if (mnt->path != NULL) {
414b4cb3fe0SToomas Soome mnt->path = strdup(path);
4155385c7e1SWarner Losh if (mnt->path == NULL) {
4165385c7e1SWarner Losh rv = ENOMEM;
4175385c7e1SWarner Losh goto err;
4185385c7e1SWarner Losh }
4195385c7e1SWarner Losh }
420b4cb3fe0SToomas Soome
421b4cb3fe0SToomas Soome rv = zfs_mount_impl(spa, zfsdev->root_guid, mnt);
422b4cb3fe0SToomas Soome
4235385c7e1SWarner Losh if (rv == 0 && mnt->objset.os_type != DMU_OST_ZFS) {
424b4cb3fe0SToomas Soome printf("Unexpected object set type %ju\n",
425b4cb3fe0SToomas Soome (uintmax_t)mnt->objset.os_type);
426b4cb3fe0SToomas Soome rv = EIO;
427b4cb3fe0SToomas Soome }
4285385c7e1SWarner Losh err:
429b4cb3fe0SToomas Soome if (rv != 0) {
430b4cb3fe0SToomas Soome if (mnt != NULL)
431b4cb3fe0SToomas Soome free(mnt->path);
432b4cb3fe0SToomas Soome free(mnt);
4335385c7e1SWarner Losh free(zfsdev);
434b4cb3fe0SToomas Soome return (rv);
435b4cb3fe0SToomas Soome }
436b4cb3fe0SToomas Soome
437b4cb3fe0SToomas Soome *data = mnt;
438b4cb3fe0SToomas Soome if (path != NULL)
439b4cb3fe0SToomas Soome STAILQ_INSERT_TAIL(&zfsmount, mnt, next);
4405385c7e1SWarner Losh
4415385c7e1SWarner Losh free(zfsdev);
442b4cb3fe0SToomas Soome
443b4cb3fe0SToomas Soome return (rv);
444b4cb3fe0SToomas Soome }
445b4cb3fe0SToomas Soome
446b4cb3fe0SToomas Soome static int
zfs_unmount(const char * dev,void * data)447b4cb3fe0SToomas Soome zfs_unmount(const char *dev, void *data)
448b4cb3fe0SToomas Soome {
449b4cb3fe0SToomas Soome struct zfsmount *mnt = data;
450b4cb3fe0SToomas Soome
451b4cb3fe0SToomas Soome STAILQ_REMOVE(&zfsmount, mnt, zfsmount, next);
452b4cb3fe0SToomas Soome free(mnt->path);
453b4cb3fe0SToomas Soome free(mnt);
454b4cb3fe0SToomas Soome return (0);
455b4cb3fe0SToomas Soome }
456b4cb3fe0SToomas Soome
457b8902de1SWarner Losh static int
vdev_read(vdev_t * vdev,void * priv,off_t offset,void * buf,size_t bytes)458b8902de1SWarner Losh vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes)
459b8902de1SWarner Losh {
460b8902de1SWarner Losh int fd, ret;
46107362361SPatrick Kelsey size_t res, head, tail, total_size, full_sec_size;
46207362361SPatrick Kelsey unsigned secsz, do_tail_read;
46307362361SPatrick Kelsey off_t start_sec;
46407362361SPatrick Kelsey char *outbuf, *bouncebuf;
465b8902de1SWarner Losh
466b8902de1SWarner Losh fd = (uintptr_t) priv;
46707362361SPatrick Kelsey outbuf = (char *) buf;
468b8902de1SWarner Losh bouncebuf = NULL;
469b8902de1SWarner Losh
470b8902de1SWarner Losh ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
471b8902de1SWarner Losh if (ret != 0)
472b8902de1SWarner Losh return (ret);
473b8902de1SWarner Losh
47407362361SPatrick Kelsey /*
47507362361SPatrick Kelsey * Handling reads of arbitrary offset and size - multi-sector case
47607362361SPatrick Kelsey * and single-sector case.
47707362361SPatrick Kelsey *
47807362361SPatrick Kelsey * Multi-sector Case
47907362361SPatrick Kelsey * (do_tail_read = true if tail > 0)
48007362361SPatrick Kelsey *
48107362361SPatrick Kelsey * |<----------------------total_size--------------------->|
48207362361SPatrick Kelsey * | |
48307362361SPatrick Kelsey * |<--head-->|<--------------bytes------------>|<--tail-->|
48407362361SPatrick Kelsey * | | | |
48507362361SPatrick Kelsey * | | |<~full_sec_size~>| | |
48607362361SPatrick Kelsey * +------------------+ +------------------+
48707362361SPatrick Kelsey * | |0101010| . . . |0101011| |
48807362361SPatrick Kelsey * +------------------+ +------------------+
48907362361SPatrick Kelsey * start_sec start_sec + n
49007362361SPatrick Kelsey *
49107362361SPatrick Kelsey *
49207362361SPatrick Kelsey * Single-sector Case
49307362361SPatrick Kelsey * (do_tail_read = false)
49407362361SPatrick Kelsey *
49507362361SPatrick Kelsey * |<------total_size = secsz----->|
49607362361SPatrick Kelsey * | |
49707362361SPatrick Kelsey * |<-head->|<---bytes--->|<-tail->|
49807362361SPatrick Kelsey * +-------------------------------+
49907362361SPatrick Kelsey * | |0101010101010| |
50007362361SPatrick Kelsey * +-------------------------------+
50107362361SPatrick Kelsey * start_sec
50207362361SPatrick Kelsey */
50307362361SPatrick Kelsey start_sec = offset / secsz;
50407362361SPatrick Kelsey head = offset % secsz;
50507362361SPatrick Kelsey total_size = roundup2(head + bytes, secsz);
50607362361SPatrick Kelsey tail = total_size - (head + bytes);
50707362361SPatrick Kelsey do_tail_read = ((tail > 0) && (head + bytes > secsz));
50807362361SPatrick Kelsey full_sec_size = total_size;
50907362361SPatrick Kelsey if (head > 0)
51007362361SPatrick Kelsey full_sec_size -= secsz;
51107362361SPatrick Kelsey if (do_tail_read)
51207362361SPatrick Kelsey full_sec_size -= secsz;
513b8902de1SWarner Losh
51407362361SPatrick Kelsey /* Return of partial sector data requires a bounce buffer. */
5154a2d7ceeSToomas Soome if ((head > 0) || do_tail_read || bytes < secsz) {
516c1c4c81fSToomas Soome bouncebuf = malloc(secsz);
517b8902de1SWarner Losh if (bouncebuf == NULL) {
518b8902de1SWarner Losh printf("vdev_read: out of memory\n");
519b8902de1SWarner Losh return (ENOMEM);
520b8902de1SWarner Losh }
521b8902de1SWarner Losh }
522b8902de1SWarner Losh
5239892cc9aSKyle Evans if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) {
5249892cc9aSKyle Evans ret = errno;
5259892cc9aSKyle Evans goto error;
5269892cc9aSKyle Evans }
52707362361SPatrick Kelsey
52807362361SPatrick Kelsey /* Partial data return from first sector */
52907362361SPatrick Kelsey if (head > 0) {
53007362361SPatrick Kelsey res = read(fd, bouncebuf, secsz);
53107362361SPatrick Kelsey if (res != secsz) {
532b8902de1SWarner Losh ret = EIO;
533b8902de1SWarner Losh goto error;
534b8902de1SWarner Losh }
535a0705597SPatrick Kelsey memcpy(outbuf, bouncebuf + head, min(secsz - head, bytes));
536a0705597SPatrick Kelsey outbuf += min(secsz - head, bytes);
53707362361SPatrick Kelsey }
53807362361SPatrick Kelsey
5394a2d7ceeSToomas Soome /*
5404a2d7ceeSToomas Soome * Full data return from read sectors.
5414a2d7ceeSToomas Soome * Note, there is still corner case where we read
5424a2d7ceeSToomas Soome * from sector boundary, but less than sector size, e.g. reading 512B
5434a2d7ceeSToomas Soome * from 4k sector.
5444a2d7ceeSToomas Soome */
54507362361SPatrick Kelsey if (full_sec_size > 0) {
5464a2d7ceeSToomas Soome if (bytes < full_sec_size) {
5474a2d7ceeSToomas Soome res = read(fd, bouncebuf, secsz);
5484a2d7ceeSToomas Soome if (res != secsz) {
5494a2d7ceeSToomas Soome ret = EIO;
5504a2d7ceeSToomas Soome goto error;
5514a2d7ceeSToomas Soome }
5524a2d7ceeSToomas Soome memcpy(outbuf, bouncebuf, bytes);
5534a2d7ceeSToomas Soome } else {
55407362361SPatrick Kelsey res = read(fd, outbuf, full_sec_size);
55507362361SPatrick Kelsey if (res != full_sec_size) {
55607362361SPatrick Kelsey ret = EIO;
55707362361SPatrick Kelsey goto error;
55807362361SPatrick Kelsey }
55907362361SPatrick Kelsey outbuf += full_sec_size;
56007362361SPatrick Kelsey }
5614a2d7ceeSToomas Soome }
56207362361SPatrick Kelsey
56307362361SPatrick Kelsey /* Partial data return from last sector */
56407362361SPatrick Kelsey if (do_tail_read) {
56507362361SPatrick Kelsey res = read(fd, bouncebuf, secsz);
56607362361SPatrick Kelsey if (res != secsz) {
56707362361SPatrick Kelsey ret = EIO;
56807362361SPatrick Kelsey goto error;
56907362361SPatrick Kelsey }
57007362361SPatrick Kelsey memcpy(outbuf, bouncebuf, secsz - tail);
571b8902de1SWarner Losh }
572b8902de1SWarner Losh
573b8902de1SWarner Losh ret = 0;
574b8902de1SWarner Losh error:
575c1c4c81fSToomas Soome free(bouncebuf);
576b8902de1SWarner Losh return (ret);
577b8902de1SWarner Losh }
578b8902de1SWarner Losh
579b8902de1SWarner Losh static int
vdev_write(vdev_t * vdev,off_t offset,void * buf,size_t bytes)580e307eb94SToomas Soome vdev_write(vdev_t *vdev, off_t offset, void *buf, size_t bytes)
5813830659eSToomas Soome {
5823830659eSToomas Soome int fd, ret;
5833830659eSToomas Soome size_t head, tail, total_size, full_sec_size;
5843830659eSToomas Soome unsigned secsz, do_tail_write;
5853830659eSToomas Soome off_t start_sec;
5863830659eSToomas Soome ssize_t res;
5873830659eSToomas Soome char *outbuf, *bouncebuf;
5883830659eSToomas Soome
589e307eb94SToomas Soome fd = (uintptr_t)vdev->v_priv;
5903830659eSToomas Soome outbuf = (char *)buf;
5913830659eSToomas Soome bouncebuf = NULL;
5923830659eSToomas Soome
5933830659eSToomas Soome ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
5943830659eSToomas Soome if (ret != 0)
5953830659eSToomas Soome return (ret);
5963830659eSToomas Soome
5973830659eSToomas Soome start_sec = offset / secsz;
5983830659eSToomas Soome head = offset % secsz;
5993830659eSToomas Soome total_size = roundup2(head + bytes, secsz);
6003830659eSToomas Soome tail = total_size - (head + bytes);
6013830659eSToomas Soome do_tail_write = ((tail > 0) && (head + bytes > secsz));
6023830659eSToomas Soome full_sec_size = total_size;
6033830659eSToomas Soome if (head > 0)
6043830659eSToomas Soome full_sec_size -= secsz;
6053830659eSToomas Soome if (do_tail_write)
6063830659eSToomas Soome full_sec_size -= secsz;
6073830659eSToomas Soome
6083830659eSToomas Soome /* Partial sector write requires a bounce buffer. */
6093830659eSToomas Soome if ((head > 0) || do_tail_write || bytes < secsz) {
6103830659eSToomas Soome bouncebuf = malloc(secsz);
6113830659eSToomas Soome if (bouncebuf == NULL) {
6123830659eSToomas Soome printf("vdev_write: out of memory\n");
6133830659eSToomas Soome return (ENOMEM);
6143830659eSToomas Soome }
6153830659eSToomas Soome }
6163830659eSToomas Soome
6173830659eSToomas Soome if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) {
6183830659eSToomas Soome ret = errno;
6193830659eSToomas Soome goto error;
6203830659eSToomas Soome }
6213830659eSToomas Soome
6223830659eSToomas Soome /* Partial data for first sector */
6233830659eSToomas Soome if (head > 0) {
6243830659eSToomas Soome res = read(fd, bouncebuf, secsz);
625e307eb94SToomas Soome if ((unsigned)res != secsz) {
6263830659eSToomas Soome ret = EIO;
6273830659eSToomas Soome goto error;
6283830659eSToomas Soome }
6293830659eSToomas Soome memcpy(bouncebuf + head, outbuf, min(secsz - head, bytes));
6303830659eSToomas Soome (void) lseek(fd, -secsz, SEEK_CUR);
6313830659eSToomas Soome res = write(fd, bouncebuf, secsz);
632e307eb94SToomas Soome if ((unsigned)res != secsz) {
6333830659eSToomas Soome ret = EIO;
6343830659eSToomas Soome goto error;
6353830659eSToomas Soome }
6363830659eSToomas Soome outbuf += min(secsz - head, bytes);
6373830659eSToomas Soome }
6383830659eSToomas Soome
6393830659eSToomas Soome /*
6403830659eSToomas Soome * Full data write to sectors.
6413830659eSToomas Soome * Note, there is still corner case where we write
6423830659eSToomas Soome * to sector boundary, but less than sector size, e.g. write 512B
6433830659eSToomas Soome * to 4k sector.
6443830659eSToomas Soome */
6453830659eSToomas Soome if (full_sec_size > 0) {
6463830659eSToomas Soome if (bytes < full_sec_size) {
6473830659eSToomas Soome res = read(fd, bouncebuf, secsz);
648e307eb94SToomas Soome if ((unsigned)res != secsz) {
6493830659eSToomas Soome ret = EIO;
6503830659eSToomas Soome goto error;
6513830659eSToomas Soome }
6523830659eSToomas Soome memcpy(bouncebuf, outbuf, bytes);
6533830659eSToomas Soome (void) lseek(fd, -secsz, SEEK_CUR);
6543830659eSToomas Soome res = write(fd, bouncebuf, secsz);
655e307eb94SToomas Soome if ((unsigned)res != secsz) {
6563830659eSToomas Soome ret = EIO;
6573830659eSToomas Soome goto error;
6583830659eSToomas Soome }
6593830659eSToomas Soome } else {
6603830659eSToomas Soome res = write(fd, outbuf, full_sec_size);
661e307eb94SToomas Soome if ((unsigned)res != full_sec_size) {
6623830659eSToomas Soome ret = EIO;
6633830659eSToomas Soome goto error;
6643830659eSToomas Soome }
6653830659eSToomas Soome outbuf += full_sec_size;
6663830659eSToomas Soome }
6673830659eSToomas Soome }
6683830659eSToomas Soome
6693830659eSToomas Soome /* Partial data write to last sector */
6703830659eSToomas Soome if (do_tail_write) {
6713830659eSToomas Soome res = read(fd, bouncebuf, secsz);
672e307eb94SToomas Soome if ((unsigned)res != secsz) {
6733830659eSToomas Soome ret = EIO;
6743830659eSToomas Soome goto error;
6753830659eSToomas Soome }
6763830659eSToomas Soome memcpy(bouncebuf, outbuf, secsz - tail);
6773830659eSToomas Soome (void) lseek(fd, -secsz, SEEK_CUR);
6783830659eSToomas Soome res = write(fd, bouncebuf, secsz);
679e307eb94SToomas Soome if ((unsigned)res != secsz) {
6803830659eSToomas Soome ret = EIO;
6813830659eSToomas Soome goto error;
6823830659eSToomas Soome }
6833830659eSToomas Soome }
6843830659eSToomas Soome
6853830659eSToomas Soome ret = 0;
6863830659eSToomas Soome error:
6873830659eSToomas Soome free(bouncebuf);
6883830659eSToomas Soome return (ret);
6893830659eSToomas Soome }
6903830659eSToomas Soome
6913830659eSToomas Soome static int
zfs_dev_init(void)692b8902de1SWarner Losh zfs_dev_init(void)
693b8902de1SWarner Losh {
694b8902de1SWarner Losh spa_t *spa;
695b8902de1SWarner Losh spa_t *next;
696b8902de1SWarner Losh spa_t *prev;
697b8902de1SWarner Losh
698b8902de1SWarner Losh zfs_init();
699b8902de1SWarner Losh if (archsw.arch_zfs_probe == NULL)
700b8902de1SWarner Losh return (ENXIO);
701b8902de1SWarner Losh archsw.arch_zfs_probe();
702b8902de1SWarner Losh
703b8902de1SWarner Losh prev = NULL;
704b8902de1SWarner Losh spa = STAILQ_FIRST(&zfs_pools);
705b8902de1SWarner Losh while (spa != NULL) {
706b8902de1SWarner Losh next = STAILQ_NEXT(spa, spa_link);
707b8902de1SWarner Losh if (zfs_spa_init(spa)) {
708b8902de1SWarner Losh if (prev == NULL)
709b8902de1SWarner Losh STAILQ_REMOVE_HEAD(&zfs_pools, spa_link);
710b8902de1SWarner Losh else
711b8902de1SWarner Losh STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link);
712b8902de1SWarner Losh } else
713b8902de1SWarner Losh prev = spa;
714b8902de1SWarner Losh spa = next;
715b8902de1SWarner Losh }
716b8902de1SWarner Losh return (0);
717b8902de1SWarner Losh }
718b8902de1SWarner Losh
719b8902de1SWarner Losh struct zfs_probe_args {
720b8902de1SWarner Losh int fd;
721b8902de1SWarner Losh const char *devname;
722b8902de1SWarner Losh uint64_t *pool_guid;
723b8902de1SWarner Losh u_int secsz;
724b8902de1SWarner Losh };
725b8902de1SWarner Losh
726b8902de1SWarner Losh static int
zfs_diskread(void * arg,void * buf,size_t blocks,uint64_t offset)727b8902de1SWarner Losh zfs_diskread(void *arg, void *buf, size_t blocks, uint64_t offset)
728b8902de1SWarner Losh {
729b8902de1SWarner Losh struct zfs_probe_args *ppa;
730b8902de1SWarner Losh
731b8902de1SWarner Losh ppa = (struct zfs_probe_args *)arg;
732b8902de1SWarner Losh return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd,
733b8902de1SWarner Losh offset * ppa->secsz, buf, blocks * ppa->secsz));
734b8902de1SWarner Losh }
735b8902de1SWarner Losh
736b8902de1SWarner Losh static int
zfs_probe(int fd,uint64_t * pool_guid)737b8902de1SWarner Losh zfs_probe(int fd, uint64_t *pool_guid)
738b8902de1SWarner Losh {
739b8902de1SWarner Losh spa_t *spa;
740b8902de1SWarner Losh int ret;
741b8902de1SWarner Losh
7425257ddd3SToomas Soome spa = NULL;
743e307eb94SToomas Soome ret = vdev_probe(vdev_read, vdev_write, (void *)(uintptr_t)fd, &spa);
744b8902de1SWarner Losh if (ret == 0 && pool_guid != NULL)
745867ae3c3SToomas Soome if (*pool_guid == 0)
746b8902de1SWarner Losh *pool_guid = spa->spa_guid;
747b8902de1SWarner Losh return (ret);
748b8902de1SWarner Losh }
749b8902de1SWarner Losh
750b8902de1SWarner Losh static int
zfs_probe_partition(void * arg,const char * partname,const struct ptable_entry * part)751b8902de1SWarner Losh zfs_probe_partition(void *arg, const char *partname,
752b8902de1SWarner Losh const struct ptable_entry *part)
753b8902de1SWarner Losh {
754b8902de1SWarner Losh struct zfs_probe_args *ppa, pa;
755b8902de1SWarner Losh struct ptable *table;
756b8902de1SWarner Losh char devname[32];
757b8902de1SWarner Losh int ret;
758b8902de1SWarner Losh
759b8902de1SWarner Losh /* Probe only freebsd-zfs and freebsd partitions */
760b8902de1SWarner Losh if (part->type != PART_FREEBSD &&
761b8902de1SWarner Losh part->type != PART_FREEBSD_ZFS)
762b8902de1SWarner Losh return (0);
763b8902de1SWarner Losh
764b8902de1SWarner Losh ppa = (struct zfs_probe_args *)arg;
765b8902de1SWarner Losh strncpy(devname, ppa->devname, strlen(ppa->devname) - 1);
766b8902de1SWarner Losh devname[strlen(ppa->devname) - 1] = '\0';
767e307eb94SToomas Soome snprintf(devname, sizeof(devname), "%s%s:", devname, partname);
7683830659eSToomas Soome pa.fd = open(devname, O_RDWR);
769b8902de1SWarner Losh if (pa.fd == -1)
770b8902de1SWarner Losh return (0);
771b8902de1SWarner Losh ret = zfs_probe(pa.fd, ppa->pool_guid);
772b8902de1SWarner Losh if (ret == 0)
773b8902de1SWarner Losh return (0);
774b8902de1SWarner Losh /* Do we have BSD label here? */
775b8902de1SWarner Losh if (part->type == PART_FREEBSD) {
776b8902de1SWarner Losh pa.devname = devname;
777b8902de1SWarner Losh pa.pool_guid = ppa->pool_guid;
778b8902de1SWarner Losh pa.secsz = ppa->secsz;
779b8902de1SWarner Losh table = ptable_open(&pa, part->end - part->start + 1,
780b8902de1SWarner Losh ppa->secsz, zfs_diskread);
781b8902de1SWarner Losh if (table != NULL) {
782b8902de1SWarner Losh ptable_iterate(table, &pa, zfs_probe_partition);
783b8902de1SWarner Losh ptable_close(table);
784b8902de1SWarner Losh }
785b8902de1SWarner Losh }
786b8902de1SWarner Losh close(pa.fd);
787b8902de1SWarner Losh return (0);
788b8902de1SWarner Losh }
789b8902de1SWarner Losh
790e307eb94SToomas Soome /*
791e307eb94SToomas Soome * Return bootenv nvlist from pool label.
792e307eb94SToomas Soome */
793b8902de1SWarner Losh int
zfs_get_bootenv(void * vdev,nvlist_t ** benvp)794e307eb94SToomas Soome zfs_get_bootenv(void *vdev, nvlist_t **benvp)
795e307eb94SToomas Soome {
796e307eb94SToomas Soome spa_t *spa;
797e307eb94SToomas Soome
7986479bd1bSWarner Losh if ((spa = spa_find_by_dev((struct zfs_devdesc *)vdev)) == NULL)
799e307eb94SToomas Soome return (ENXIO);
800e307eb94SToomas Soome
8016479bd1bSWarner Losh return (zfs_get_bootenv_spa(spa, benvp));
802e307eb94SToomas Soome }
803e307eb94SToomas Soome
804e307eb94SToomas Soome /*
805e307eb94SToomas Soome * Store nvlist to pool label bootenv area. Also updates cached pointer in spa.
806e307eb94SToomas Soome */
807e307eb94SToomas Soome int
zfs_set_bootenv(void * vdev,nvlist_t * benv)808e307eb94SToomas Soome zfs_set_bootenv(void *vdev, nvlist_t *benv)
8093830659eSToomas Soome {
8103830659eSToomas Soome spa_t *spa;
8113830659eSToomas Soome
8124dcae288SWarner Losh if ((spa = spa_find_by_dev((struct zfs_devdesc *)vdev)) == NULL)
813e307eb94SToomas Soome return (ENXIO);
8143830659eSToomas Soome
8154dcae288SWarner Losh return (zfs_set_bootenv_spa(spa, benv));
8163830659eSToomas Soome }
8173830659eSToomas Soome
818e307eb94SToomas Soome /*
819e307eb94SToomas Soome * Get bootonce value by key. The bootonce <key, value> pair is removed
820e307eb94SToomas Soome * from the bootenv nvlist and the remaining nvlist is committed back to disk.
821e307eb94SToomas Soome */
822e307eb94SToomas Soome int
zfs_get_bootonce(void * vdev,const char * key,char * buf,size_t size)823e307eb94SToomas Soome zfs_get_bootonce(void *vdev, const char *key, char *buf, size_t size)
824e307eb94SToomas Soome {
825b765cfa3SWarner Losh spa_t *spa;
826e307eb94SToomas Soome
827b765cfa3SWarner Losh if ((spa = spa_find_by_dev((struct zfs_devdesc *)vdev)) == NULL)
828b765cfa3SWarner Losh return (ENXIO);
829e307eb94SToomas Soome
830b765cfa3SWarner Losh return (zfs_get_bootonce_spa(spa, key, buf, size));
831e307eb94SToomas Soome }
832e307eb94SToomas Soome
833e307eb94SToomas Soome /*
834e307eb94SToomas Soome * nvstore backend.
835e307eb94SToomas Soome */
836e307eb94SToomas Soome
837e307eb94SToomas Soome static int zfs_nvstore_setter(void *, int, const char *,
838e307eb94SToomas Soome const void *, size_t);
839e307eb94SToomas Soome static int zfs_nvstore_setter_str(void *, const char *, const char *,
840e307eb94SToomas Soome const char *);
841e307eb94SToomas Soome static int zfs_nvstore_unset_impl(void *, const char *, bool);
842e307eb94SToomas Soome static int zfs_nvstore_setenv(void *, void *);
843e307eb94SToomas Soome
844e307eb94SToomas Soome /*
845e307eb94SToomas Soome * nvstore is only present for current rootfs pool.
846e307eb94SToomas Soome */
847e307eb94SToomas Soome static int
zfs_nvstore_sethook(struct env_var * ev,int flags __unused,const void * value)848e307eb94SToomas Soome zfs_nvstore_sethook(struct env_var *ev, int flags __unused, const void *value)
849e307eb94SToomas Soome {
850e307eb94SToomas Soome struct zfs_devdesc *dev;
851e307eb94SToomas Soome int rv;
852e307eb94SToomas Soome
853e307eb94SToomas Soome archsw.arch_getdev((void **)&dev, NULL, NULL);
854e307eb94SToomas Soome if (dev == NULL)
855e307eb94SToomas Soome return (ENXIO);
856e307eb94SToomas Soome
857e307eb94SToomas Soome rv = zfs_nvstore_setter_str(dev, NULL, ev->ev_name, value);
858e307eb94SToomas Soome
859e307eb94SToomas Soome free(dev);
860e307eb94SToomas Soome return (rv);
861e307eb94SToomas Soome }
862e307eb94SToomas Soome
863e307eb94SToomas Soome /*
864e307eb94SToomas Soome * nvstore is only present for current rootfs pool.
865e307eb94SToomas Soome */
866e307eb94SToomas Soome static int
zfs_nvstore_unsethook(struct env_var * ev)867e307eb94SToomas Soome zfs_nvstore_unsethook(struct env_var *ev)
868e307eb94SToomas Soome {
869e307eb94SToomas Soome struct zfs_devdesc *dev;
870e307eb94SToomas Soome int rv;
871e307eb94SToomas Soome
872e307eb94SToomas Soome archsw.arch_getdev((void **)&dev, NULL, NULL);
873e307eb94SToomas Soome if (dev == NULL)
874e307eb94SToomas Soome return (ENXIO);
875e307eb94SToomas Soome
876e307eb94SToomas Soome rv = zfs_nvstore_unset_impl(dev, ev->ev_name, false);
877e307eb94SToomas Soome
878e307eb94SToomas Soome free(dev);
879e307eb94SToomas Soome return (rv);
880e307eb94SToomas Soome }
881e307eb94SToomas Soome
882e307eb94SToomas Soome static int
zfs_nvstore_getter(void * vdev,const char * name,void ** data)883e307eb94SToomas Soome zfs_nvstore_getter(void *vdev, const char *name, void **data)
884e307eb94SToomas Soome {
885e307eb94SToomas Soome struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
886e307eb94SToomas Soome spa_t *spa;
887e307eb94SToomas Soome nvlist_t *nv;
888e307eb94SToomas Soome char *str, **ptr;
889e307eb94SToomas Soome int size;
890e307eb94SToomas Soome int rv;
891e307eb94SToomas Soome
892e307eb94SToomas Soome if (dev->dd.d_dev->dv_type != DEVT_ZFS)
893e307eb94SToomas Soome return (ENOTSUP);
894e307eb94SToomas Soome
895e307eb94SToomas Soome if ((spa = spa_find_by_dev(dev)) == NULL)
896e307eb94SToomas Soome return (ENXIO);
897e307eb94SToomas Soome
898e307eb94SToomas Soome if (spa->spa_bootenv == NULL)
899e307eb94SToomas Soome return (ENXIO);
900e307eb94SToomas Soome
901e307eb94SToomas Soome if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
902e307eb94SToomas Soome NULL, &nv, NULL) != 0)
903e307eb94SToomas Soome return (ENOENT);
904e307eb94SToomas Soome
905e307eb94SToomas Soome rv = nvlist_find(nv, name, DATA_TYPE_STRING, NULL, &str, &size);
906e307eb94SToomas Soome if (rv == 0) {
907e307eb94SToomas Soome ptr = (char **)data;
908e307eb94SToomas Soome asprintf(ptr, "%.*s", size, str);
909e307eb94SToomas Soome if (*data == NULL)
910e307eb94SToomas Soome rv = ENOMEM;
911e307eb94SToomas Soome }
912e307eb94SToomas Soome nvlist_destroy(nv);
913e307eb94SToomas Soome return (rv);
914e307eb94SToomas Soome }
915e307eb94SToomas Soome
916e307eb94SToomas Soome static int
zfs_nvstore_setter(void * vdev,int type,const char * name,const void * data,size_t size)917e307eb94SToomas Soome zfs_nvstore_setter(void *vdev, int type, const char *name,
918e307eb94SToomas Soome const void *data, size_t size)
919e307eb94SToomas Soome {
920e307eb94SToomas Soome struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
921e307eb94SToomas Soome spa_t *spa;
922e307eb94SToomas Soome nvlist_t *nv;
923e307eb94SToomas Soome int rv;
924e307eb94SToomas Soome bool env_set = true;
925e307eb94SToomas Soome
926e307eb94SToomas Soome if (dev->dd.d_dev->dv_type != DEVT_ZFS)
927e307eb94SToomas Soome return (ENOTSUP);
928e307eb94SToomas Soome
929e307eb94SToomas Soome if ((spa = spa_find_by_dev(dev)) == NULL)
930e307eb94SToomas Soome return (ENXIO);
931e307eb94SToomas Soome
932e307eb94SToomas Soome if (spa->spa_bootenv == NULL)
933e307eb94SToomas Soome return (ENXIO);
934e307eb94SToomas Soome
935e307eb94SToomas Soome if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
936e307eb94SToomas Soome NULL, &nv, NULL) != 0) {
937e307eb94SToomas Soome nv = nvlist_create(NV_UNIQUE_NAME);
938e307eb94SToomas Soome if (nv == NULL)
939e307eb94SToomas Soome return (ENOMEM);
940e307eb94SToomas Soome }
941e307eb94SToomas Soome
942e307eb94SToomas Soome rv = 0;
943e307eb94SToomas Soome switch (type) {
944e307eb94SToomas Soome case DATA_TYPE_INT8:
945e307eb94SToomas Soome if (size != sizeof (int8_t)) {
946e307eb94SToomas Soome rv = EINVAL;
947e307eb94SToomas Soome break;
948e307eb94SToomas Soome }
949e307eb94SToomas Soome rv = nvlist_add_int8(nv, name, *(int8_t *)data);
950e307eb94SToomas Soome break;
951e307eb94SToomas Soome
952e307eb94SToomas Soome case DATA_TYPE_INT16:
953e307eb94SToomas Soome if (size != sizeof (int16_t)) {
954e307eb94SToomas Soome rv = EINVAL;
955e307eb94SToomas Soome break;
956e307eb94SToomas Soome }
957e307eb94SToomas Soome rv = nvlist_add_int16(nv, name, *(int16_t *)data);
958e307eb94SToomas Soome break;
959e307eb94SToomas Soome
960e307eb94SToomas Soome case DATA_TYPE_INT32:
961e307eb94SToomas Soome if (size != sizeof (int32_t)) {
962e307eb94SToomas Soome rv = EINVAL;
963e307eb94SToomas Soome break;
964e307eb94SToomas Soome }
965e307eb94SToomas Soome rv = nvlist_add_int32(nv, name, *(int32_t *)data);
966e307eb94SToomas Soome break;
967e307eb94SToomas Soome
968e307eb94SToomas Soome case DATA_TYPE_INT64:
969e307eb94SToomas Soome if (size != sizeof (int64_t)) {
970e307eb94SToomas Soome rv = EINVAL;
971e307eb94SToomas Soome break;
972e307eb94SToomas Soome }
973e307eb94SToomas Soome rv = nvlist_add_int64(nv, name, *(int64_t *)data);
974e307eb94SToomas Soome break;
975e307eb94SToomas Soome
976e307eb94SToomas Soome case DATA_TYPE_BYTE:
977e307eb94SToomas Soome if (size != sizeof (uint8_t)) {
978e307eb94SToomas Soome rv = EINVAL;
979e307eb94SToomas Soome break;
980e307eb94SToomas Soome }
981e307eb94SToomas Soome rv = nvlist_add_byte(nv, name, *(int8_t *)data);
982e307eb94SToomas Soome break;
983e307eb94SToomas Soome
984e307eb94SToomas Soome case DATA_TYPE_UINT8:
985e307eb94SToomas Soome if (size != sizeof (uint8_t)) {
986e307eb94SToomas Soome rv = EINVAL;
987e307eb94SToomas Soome break;
988e307eb94SToomas Soome }
989e307eb94SToomas Soome rv = nvlist_add_uint8(nv, name, *(int8_t *)data);
990e307eb94SToomas Soome break;
991e307eb94SToomas Soome
992e307eb94SToomas Soome case DATA_TYPE_UINT16:
993e307eb94SToomas Soome if (size != sizeof (uint16_t)) {
994e307eb94SToomas Soome rv = EINVAL;
995e307eb94SToomas Soome break;
996e307eb94SToomas Soome }
997e307eb94SToomas Soome rv = nvlist_add_uint16(nv, name, *(uint16_t *)data);
998e307eb94SToomas Soome break;
999e307eb94SToomas Soome
1000e307eb94SToomas Soome case DATA_TYPE_UINT32:
1001e307eb94SToomas Soome if (size != sizeof (uint32_t)) {
1002e307eb94SToomas Soome rv = EINVAL;
1003e307eb94SToomas Soome break;
1004e307eb94SToomas Soome }
1005e307eb94SToomas Soome rv = nvlist_add_uint32(nv, name, *(uint32_t *)data);
1006e307eb94SToomas Soome break;
1007e307eb94SToomas Soome
1008e307eb94SToomas Soome case DATA_TYPE_UINT64:
1009e307eb94SToomas Soome if (size != sizeof (uint64_t)) {
1010e307eb94SToomas Soome rv = EINVAL;
1011e307eb94SToomas Soome break;
1012e307eb94SToomas Soome }
1013e307eb94SToomas Soome rv = nvlist_add_uint64(nv, name, *(uint64_t *)data);
1014e307eb94SToomas Soome break;
1015e307eb94SToomas Soome
1016e307eb94SToomas Soome case DATA_TYPE_STRING:
1017e307eb94SToomas Soome rv = nvlist_add_string(nv, name, data);
1018e307eb94SToomas Soome break;
1019e307eb94SToomas Soome
1020e307eb94SToomas Soome case DATA_TYPE_BOOLEAN_VALUE:
1021e307eb94SToomas Soome if (size != sizeof (boolean_t)) {
1022e307eb94SToomas Soome rv = EINVAL;
1023e307eb94SToomas Soome break;
1024e307eb94SToomas Soome }
1025e307eb94SToomas Soome rv = nvlist_add_boolean_value(nv, name, *(boolean_t *)data);
1026e307eb94SToomas Soome break;
1027e307eb94SToomas Soome
1028e307eb94SToomas Soome default:
1029e307eb94SToomas Soome rv = EINVAL;
1030e307eb94SToomas Soome break;
1031e307eb94SToomas Soome }
1032e307eb94SToomas Soome
1033e307eb94SToomas Soome if (rv == 0) {
1034e307eb94SToomas Soome rv = nvlist_add_nvlist(spa->spa_bootenv, OS_NVSTORE, nv);
1035e307eb94SToomas Soome if (rv == 0) {
1036e307eb94SToomas Soome rv = zfs_set_bootenv(vdev, spa->spa_bootenv);
1037e307eb94SToomas Soome }
1038e307eb94SToomas Soome if (rv == 0) {
1039e307eb94SToomas Soome if (env_set) {
1040e307eb94SToomas Soome rv = zfs_nvstore_setenv(vdev,
1041e307eb94SToomas Soome nvpair_find(nv, name));
1042e307eb94SToomas Soome } else {
1043e307eb94SToomas Soome env_discard(env_getenv(name));
1044e307eb94SToomas Soome rv = 0;
1045e307eb94SToomas Soome }
1046e307eb94SToomas Soome }
1047e307eb94SToomas Soome }
1048e307eb94SToomas Soome
1049e307eb94SToomas Soome nvlist_destroy(nv);
1050e307eb94SToomas Soome return (rv);
1051e307eb94SToomas Soome }
1052e307eb94SToomas Soome
1053e307eb94SToomas Soome static int
get_int64(const char * data,int64_t * ip)1054e307eb94SToomas Soome get_int64(const char *data, int64_t *ip)
1055e307eb94SToomas Soome {
1056e307eb94SToomas Soome char *end;
1057e307eb94SToomas Soome int64_t val;
1058e307eb94SToomas Soome
1059e307eb94SToomas Soome errno = 0;
1060e307eb94SToomas Soome val = strtoll(data, &end, 0);
1061e307eb94SToomas Soome if (errno != 0 || *data == '\0' || *end != '\0')
1062e307eb94SToomas Soome return (EINVAL);
1063e307eb94SToomas Soome
1064e307eb94SToomas Soome *ip = val;
1065e307eb94SToomas Soome return (0);
1066e307eb94SToomas Soome }
1067e307eb94SToomas Soome
1068e307eb94SToomas Soome static int
get_uint64(const char * data,uint64_t * ip)1069e307eb94SToomas Soome get_uint64(const char *data, uint64_t *ip)
1070e307eb94SToomas Soome {
1071e307eb94SToomas Soome char *end;
1072e307eb94SToomas Soome uint64_t val;
1073e307eb94SToomas Soome
1074e307eb94SToomas Soome errno = 0;
1075e307eb94SToomas Soome val = strtoull(data, &end, 0);
1076e307eb94SToomas Soome if (errno != 0 || *data == '\0' || *end != '\0')
1077e307eb94SToomas Soome return (EINVAL);
1078e307eb94SToomas Soome
1079e307eb94SToomas Soome *ip = val;
1080e307eb94SToomas Soome return (0);
1081e307eb94SToomas Soome }
1082e307eb94SToomas Soome
1083e307eb94SToomas Soome /*
1084e307eb94SToomas Soome * Translate textual data to data type. If type is not set, and we are
1085e307eb94SToomas Soome * creating new pair, use DATA_TYPE_STRING.
1086e307eb94SToomas Soome */
1087e307eb94SToomas Soome static int
zfs_nvstore_setter_str(void * vdev,const char * type,const char * name,const char * data)1088e307eb94SToomas Soome zfs_nvstore_setter_str(void *vdev, const char *type, const char *name,
1089e307eb94SToomas Soome const char *data)
1090e307eb94SToomas Soome {
1091e307eb94SToomas Soome struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
1092e307eb94SToomas Soome spa_t *spa;
1093e307eb94SToomas Soome nvlist_t *nv;
1094e307eb94SToomas Soome int rv;
1095e307eb94SToomas Soome data_type_t dt;
1096e307eb94SToomas Soome int64_t val;
1097e307eb94SToomas Soome uint64_t uval;
1098e307eb94SToomas Soome
1099e307eb94SToomas Soome if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1100e307eb94SToomas Soome return (ENOTSUP);
1101e307eb94SToomas Soome
1102e307eb94SToomas Soome if ((spa = spa_find_by_dev(dev)) == NULL)
1103e307eb94SToomas Soome return (ENXIO);
1104e307eb94SToomas Soome
1105e307eb94SToomas Soome if (spa->spa_bootenv == NULL)
1106e307eb94SToomas Soome return (ENXIO);
1107e307eb94SToomas Soome
1108e307eb94SToomas Soome if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
1109e307eb94SToomas Soome NULL, &nv, NULL) != 0) {
1110e307eb94SToomas Soome nv = NULL;
1111e307eb94SToomas Soome }
1112e307eb94SToomas Soome
1113e307eb94SToomas Soome if (type == NULL) {
1114e307eb94SToomas Soome nvp_header_t *nvh;
1115e307eb94SToomas Soome
1116e307eb94SToomas Soome /*
1117e307eb94SToomas Soome * if there is no existing pair, default to string.
1118e307eb94SToomas Soome * Otherwise, use type from existing pair.
1119e307eb94SToomas Soome */
1120e307eb94SToomas Soome nvh = nvpair_find(nv, name);
1121e307eb94SToomas Soome if (nvh == NULL) {
1122e307eb94SToomas Soome dt = DATA_TYPE_STRING;
1123e307eb94SToomas Soome } else {
1124e307eb94SToomas Soome nv_string_t *nvp_name;
1125e307eb94SToomas Soome nv_pair_data_t *nvp_data;
1126e307eb94SToomas Soome
1127e307eb94SToomas Soome nvp_name = (nv_string_t *)(nvh + 1);
1128e307eb94SToomas Soome nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
1129e307eb94SToomas Soome NV_ALIGN4(nvp_name->nv_size));
1130e307eb94SToomas Soome dt = nvp_data->nv_type;
1131e307eb94SToomas Soome }
1132e307eb94SToomas Soome } else {
1133e307eb94SToomas Soome dt = nvpair_type_from_name(type);
1134e307eb94SToomas Soome }
1135e307eb94SToomas Soome nvlist_destroy(nv);
1136e307eb94SToomas Soome
1137e307eb94SToomas Soome rv = 0;
1138e307eb94SToomas Soome switch (dt) {
1139e307eb94SToomas Soome case DATA_TYPE_INT8:
1140e307eb94SToomas Soome rv = get_int64(data, &val);
1141e307eb94SToomas Soome if (rv == 0) {
1142e307eb94SToomas Soome int8_t v = val;
1143e307eb94SToomas Soome
1144e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1145e307eb94SToomas Soome }
1146e307eb94SToomas Soome break;
1147e307eb94SToomas Soome case DATA_TYPE_INT16:
1148e307eb94SToomas Soome rv = get_int64(data, &val);
1149e307eb94SToomas Soome if (rv == 0) {
1150e307eb94SToomas Soome int16_t v = val;
1151e307eb94SToomas Soome
1152e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1153e307eb94SToomas Soome }
1154e307eb94SToomas Soome break;
1155e307eb94SToomas Soome case DATA_TYPE_INT32:
1156e307eb94SToomas Soome rv = get_int64(data, &val);
1157e307eb94SToomas Soome if (rv == 0) {
1158e307eb94SToomas Soome int32_t v = val;
1159e307eb94SToomas Soome
1160e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1161e307eb94SToomas Soome }
1162e307eb94SToomas Soome break;
1163e307eb94SToomas Soome case DATA_TYPE_INT64:
1164e307eb94SToomas Soome rv = get_int64(data, &val);
1165e307eb94SToomas Soome if (rv == 0) {
1166e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &val,
1167e307eb94SToomas Soome sizeof (val));
1168e307eb94SToomas Soome }
1169e307eb94SToomas Soome break;
1170e307eb94SToomas Soome
1171e307eb94SToomas Soome case DATA_TYPE_BYTE:
1172e307eb94SToomas Soome rv = get_uint64(data, &uval);
1173e307eb94SToomas Soome if (rv == 0) {
1174e307eb94SToomas Soome uint8_t v = uval;
1175e307eb94SToomas Soome
1176e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1177e307eb94SToomas Soome }
1178e307eb94SToomas Soome break;
1179e307eb94SToomas Soome
1180e307eb94SToomas Soome case DATA_TYPE_UINT8:
1181e307eb94SToomas Soome rv = get_uint64(data, &uval);
1182e307eb94SToomas Soome if (rv == 0) {
1183e307eb94SToomas Soome uint8_t v = uval;
1184e307eb94SToomas Soome
1185e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1186e307eb94SToomas Soome }
1187e307eb94SToomas Soome break;
1188e307eb94SToomas Soome
1189e307eb94SToomas Soome case DATA_TYPE_UINT16:
1190e307eb94SToomas Soome rv = get_uint64(data, &uval);
1191e307eb94SToomas Soome if (rv == 0) {
1192e307eb94SToomas Soome uint16_t v = uval;
1193e307eb94SToomas Soome
1194e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1195e307eb94SToomas Soome }
1196e307eb94SToomas Soome break;
1197e307eb94SToomas Soome
1198e307eb94SToomas Soome case DATA_TYPE_UINT32:
1199e307eb94SToomas Soome rv = get_uint64(data, &uval);
1200e307eb94SToomas Soome if (rv == 0) {
1201e307eb94SToomas Soome uint32_t v = uval;
1202e307eb94SToomas Soome
1203e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1204e307eb94SToomas Soome }
1205e307eb94SToomas Soome break;
1206e307eb94SToomas Soome
1207e307eb94SToomas Soome case DATA_TYPE_UINT64:
1208e307eb94SToomas Soome rv = get_uint64(data, &uval);
1209e307eb94SToomas Soome if (rv == 0) {
1210e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &uval,
1211e307eb94SToomas Soome sizeof (uval));
1212e307eb94SToomas Soome }
1213e307eb94SToomas Soome break;
1214e307eb94SToomas Soome
1215e307eb94SToomas Soome case DATA_TYPE_STRING:
1216e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, data, strlen(data) + 1);
1217e307eb94SToomas Soome break;
1218e307eb94SToomas Soome
1219e307eb94SToomas Soome case DATA_TYPE_BOOLEAN_VALUE:
1220e307eb94SToomas Soome rv = get_int64(data, &val);
1221e307eb94SToomas Soome if (rv == 0) {
1222e307eb94SToomas Soome boolean_t v = val;
1223e307eb94SToomas Soome
1224e307eb94SToomas Soome rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1225e307eb94SToomas Soome }
1226e307eb94SToomas Soome
1227e307eb94SToomas Soome default:
1228e307eb94SToomas Soome rv = EINVAL;
1229e307eb94SToomas Soome }
1230e307eb94SToomas Soome return (rv);
1231e307eb94SToomas Soome }
1232e307eb94SToomas Soome
1233e307eb94SToomas Soome static int
zfs_nvstore_unset_impl(void * vdev,const char * name,bool unset_env)1234e307eb94SToomas Soome zfs_nvstore_unset_impl(void *vdev, const char *name, bool unset_env)
1235e307eb94SToomas Soome {
1236e307eb94SToomas Soome struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
1237e307eb94SToomas Soome spa_t *spa;
1238e307eb94SToomas Soome nvlist_t *nv;
1239e307eb94SToomas Soome int rv;
1240e307eb94SToomas Soome
1241e307eb94SToomas Soome if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1242e307eb94SToomas Soome return (ENOTSUP);
1243e307eb94SToomas Soome
1244e307eb94SToomas Soome if ((spa = spa_find_by_dev(dev)) == NULL)
1245e307eb94SToomas Soome return (ENXIO);
1246e307eb94SToomas Soome
1247e307eb94SToomas Soome if (spa->spa_bootenv == NULL)
1248e307eb94SToomas Soome return (ENXIO);
1249e307eb94SToomas Soome
1250e307eb94SToomas Soome if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
1251e307eb94SToomas Soome NULL, &nv, NULL) != 0)
1252e307eb94SToomas Soome return (ENOENT);
1253e307eb94SToomas Soome
1254e307eb94SToomas Soome rv = nvlist_remove(nv, name, DATA_TYPE_UNKNOWN);
1255e307eb94SToomas Soome if (rv == 0) {
1256e307eb94SToomas Soome if (nvlist_next_nvpair(nv, NULL) == NULL) {
1257e307eb94SToomas Soome rv = nvlist_remove(spa->spa_bootenv, OS_NVSTORE,
1258e307eb94SToomas Soome DATA_TYPE_NVLIST);
1259e307eb94SToomas Soome } else {
1260e307eb94SToomas Soome rv = nvlist_add_nvlist(spa->spa_bootenv,
1261e307eb94SToomas Soome OS_NVSTORE, nv);
1262e307eb94SToomas Soome }
1263e307eb94SToomas Soome if (rv == 0)
1264e307eb94SToomas Soome rv = zfs_set_bootenv(vdev, spa->spa_bootenv);
1265e307eb94SToomas Soome }
1266e307eb94SToomas Soome
1267*1b3f4ccbSToomas Soome if (unset_env) {
1268*1b3f4ccbSToomas Soome struct env_var *ev = env_getenv(name);
1269*1b3f4ccbSToomas Soome
1270*1b3f4ccbSToomas Soome if (ev != NULL)
1271*1b3f4ccbSToomas Soome env_discard(ev);
1272*1b3f4ccbSToomas Soome }
1273e307eb94SToomas Soome return (rv);
1274e307eb94SToomas Soome }
1275e307eb94SToomas Soome
1276e307eb94SToomas Soome static int
zfs_nvstore_unset(void * vdev,const char * name)1277e307eb94SToomas Soome zfs_nvstore_unset(void *vdev, const char *name)
1278e307eb94SToomas Soome {
1279e307eb94SToomas Soome return (zfs_nvstore_unset_impl(vdev, name, true));
1280e307eb94SToomas Soome }
1281e307eb94SToomas Soome
1282e307eb94SToomas Soome static int
zfs_nvstore_print(void * vdev __unused,void * ptr)1283e307eb94SToomas Soome zfs_nvstore_print(void *vdev __unused, void *ptr)
1284e307eb94SToomas Soome {
1285e307eb94SToomas Soome
1286e307eb94SToomas Soome nvpair_print(ptr, 0);
1287e307eb94SToomas Soome return (0);
1288e307eb94SToomas Soome }
1289e307eb94SToomas Soome
1290e307eb94SToomas Soome /*
1291e307eb94SToomas Soome * Create environment variable from nvpair.
1292e307eb94SToomas Soome * set hook will update nvstore with new value, unset hook will remove
1293e307eb94SToomas Soome * variable from nvstore.
1294e307eb94SToomas Soome */
1295e307eb94SToomas Soome static int
zfs_nvstore_setenv(void * vdev __unused,void * ptr)1296e307eb94SToomas Soome zfs_nvstore_setenv(void *vdev __unused, void *ptr)
1297e307eb94SToomas Soome {
1298e307eb94SToomas Soome nvp_header_t *nvh = ptr;
1299e307eb94SToomas Soome nv_string_t *nvp_name, *nvp_value;
1300e307eb94SToomas Soome nv_pair_data_t *nvp_data;
1301e307eb94SToomas Soome char *name, *value;
1302e307eb94SToomas Soome int rv = 0;
1303e307eb94SToomas Soome
1304e307eb94SToomas Soome if (nvh == NULL)
1305e307eb94SToomas Soome return (ENOENT);
1306e307eb94SToomas Soome
1307e307eb94SToomas Soome nvp_name = (nv_string_t *)(nvh + 1);
1308e307eb94SToomas Soome nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
1309e307eb94SToomas Soome NV_ALIGN4(nvp_name->nv_size));
1310e307eb94SToomas Soome
1311e307eb94SToomas Soome if ((name = nvstring_get(nvp_name)) == NULL)
1312e307eb94SToomas Soome return (ENOMEM);
1313e307eb94SToomas Soome
1314e307eb94SToomas Soome value = NULL;
1315e307eb94SToomas Soome switch (nvp_data->nv_type) {
1316e307eb94SToomas Soome case DATA_TYPE_BYTE:
1317e307eb94SToomas Soome case DATA_TYPE_UINT8:
1318e307eb94SToomas Soome (void) asprintf(&value, "%uc",
1319e307eb94SToomas Soome *(unsigned *)&nvp_data->nv_data[0]);
1320e307eb94SToomas Soome if (value == NULL)
1321e307eb94SToomas Soome rv = ENOMEM;
1322e307eb94SToomas Soome break;
1323e307eb94SToomas Soome
1324e307eb94SToomas Soome case DATA_TYPE_INT8:
1325e307eb94SToomas Soome (void) asprintf(&value, "%c", *(int *)&nvp_data->nv_data[0]);
1326e307eb94SToomas Soome if (value == NULL)
1327e307eb94SToomas Soome rv = ENOMEM;
1328e307eb94SToomas Soome break;
1329e307eb94SToomas Soome
1330e307eb94SToomas Soome case DATA_TYPE_INT16:
1331e307eb94SToomas Soome (void) asprintf(&value, "%hd", *(short *)&nvp_data->nv_data[0]);
1332e307eb94SToomas Soome if (value == NULL)
1333e307eb94SToomas Soome rv = ENOMEM;
1334e307eb94SToomas Soome break;
1335e307eb94SToomas Soome
1336e307eb94SToomas Soome case DATA_TYPE_UINT16:
1337e307eb94SToomas Soome (void) asprintf(&value, "%hu",
1338e307eb94SToomas Soome *(unsigned short *)&nvp_data->nv_data[0]);
1339e307eb94SToomas Soome if (value == NULL)
1340e307eb94SToomas Soome rv = ENOMEM;
1341e307eb94SToomas Soome break;
1342e307eb94SToomas Soome
1343e307eb94SToomas Soome case DATA_TYPE_BOOLEAN_VALUE:
1344e307eb94SToomas Soome case DATA_TYPE_INT32:
1345e307eb94SToomas Soome (void) asprintf(&value, "%d", *(int *)&nvp_data->nv_data[0]);
1346e307eb94SToomas Soome if (value == NULL)
1347e307eb94SToomas Soome rv = ENOMEM;
1348e307eb94SToomas Soome break;
1349e307eb94SToomas Soome
1350e307eb94SToomas Soome case DATA_TYPE_UINT32:
1351e307eb94SToomas Soome (void) asprintf(&value, "%u",
1352e307eb94SToomas Soome *(unsigned *)&nvp_data->nv_data[0]);
1353e307eb94SToomas Soome if (value == NULL)
1354e307eb94SToomas Soome rv = ENOMEM;
1355e307eb94SToomas Soome break;
1356e307eb94SToomas Soome
1357e307eb94SToomas Soome case DATA_TYPE_INT64:
1358e307eb94SToomas Soome (void) asprintf(&value, "%jd",
1359e307eb94SToomas Soome (intmax_t)*(int64_t *)&nvp_data->nv_data[0]);
1360e307eb94SToomas Soome if (value == NULL)
1361e307eb94SToomas Soome rv = ENOMEM;
1362e307eb94SToomas Soome break;
1363e307eb94SToomas Soome
1364e307eb94SToomas Soome case DATA_TYPE_UINT64:
1365e307eb94SToomas Soome (void) asprintf(&value, "%ju",
1366e307eb94SToomas Soome (uintmax_t)*(uint64_t *)&nvp_data->nv_data[0]);
1367e307eb94SToomas Soome if (value == NULL)
1368e307eb94SToomas Soome rv = ENOMEM;
1369e307eb94SToomas Soome break;
1370e307eb94SToomas Soome
1371e307eb94SToomas Soome case DATA_TYPE_STRING:
1372e307eb94SToomas Soome nvp_value = (nv_string_t *)&nvp_data->nv_data[0];
1373e307eb94SToomas Soome if ((value = nvstring_get(nvp_value)) == NULL) {
1374e307eb94SToomas Soome rv = ENOMEM;
1375e307eb94SToomas Soome break;
1376e307eb94SToomas Soome }
1377e307eb94SToomas Soome break;
1378e307eb94SToomas Soome
1379e307eb94SToomas Soome default:
1380e307eb94SToomas Soome rv = EINVAL;
1381e307eb94SToomas Soome break;
1382e307eb94SToomas Soome }
1383e307eb94SToomas Soome
1384e307eb94SToomas Soome if (value != NULL) {
1385e307eb94SToomas Soome rv = env_setenv(name, EV_VOLATILE | EV_NOHOOK, value,
1386e307eb94SToomas Soome zfs_nvstore_sethook, zfs_nvstore_unsethook);
1387e307eb94SToomas Soome free(value);
1388e307eb94SToomas Soome }
1389e307eb94SToomas Soome free(name);
1390e307eb94SToomas Soome return (rv);
1391e307eb94SToomas Soome }
1392e307eb94SToomas Soome
1393e307eb94SToomas Soome static int
zfs_nvstore_iterate(void * vdev,int (* cb)(void *,void *))1394e307eb94SToomas Soome zfs_nvstore_iterate(void *vdev, int (*cb)(void *, void *))
1395e307eb94SToomas Soome {
1396e307eb94SToomas Soome struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
1397e307eb94SToomas Soome spa_t *spa;
1398e307eb94SToomas Soome nvlist_t *nv;
1399e307eb94SToomas Soome nvp_header_t *nvh;
1400e307eb94SToomas Soome int rv;
1401e307eb94SToomas Soome
1402e307eb94SToomas Soome if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1403e307eb94SToomas Soome return (ENOTSUP);
1404e307eb94SToomas Soome
1405e307eb94SToomas Soome if ((spa = spa_find_by_dev(dev)) == NULL)
1406e307eb94SToomas Soome return (ENXIO);
1407e307eb94SToomas Soome
1408e307eb94SToomas Soome if (spa->spa_bootenv == NULL)
1409e307eb94SToomas Soome return (ENXIO);
1410e307eb94SToomas Soome
1411e307eb94SToomas Soome if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
1412e307eb94SToomas Soome NULL, &nv, NULL) != 0)
1413e307eb94SToomas Soome return (ENOENT);
1414e307eb94SToomas Soome
1415e307eb94SToomas Soome rv = 0;
1416e307eb94SToomas Soome nvh = NULL;
1417e307eb94SToomas Soome while ((nvh = nvlist_next_nvpair(nv, nvh)) != NULL) {
1418e307eb94SToomas Soome rv = cb(vdev, nvh);
1419e307eb94SToomas Soome if (rv != 0)
1420e307eb94SToomas Soome break;
1421e307eb94SToomas Soome }
1422e307eb94SToomas Soome return (rv);
1423e307eb94SToomas Soome }
1424e307eb94SToomas Soome
1425e307eb94SToomas Soome nvs_callbacks_t nvstore_zfs_cb = {
1426e307eb94SToomas Soome .nvs_getter = zfs_nvstore_getter,
1427e307eb94SToomas Soome .nvs_setter = zfs_nvstore_setter,
1428e307eb94SToomas Soome .nvs_setter_str = zfs_nvstore_setter_str,
1429e307eb94SToomas Soome .nvs_unset = zfs_nvstore_unset,
1430e307eb94SToomas Soome .nvs_print = zfs_nvstore_print,
1431e307eb94SToomas Soome .nvs_iterate = zfs_nvstore_iterate
1432e307eb94SToomas Soome };
1433e307eb94SToomas Soome
1434e307eb94SToomas Soome int
zfs_attach_nvstore(void * vdev)1435e307eb94SToomas Soome zfs_attach_nvstore(void *vdev)
1436e307eb94SToomas Soome {
1437e307eb94SToomas Soome struct zfs_devdesc *dev = vdev;
1438e307eb94SToomas Soome spa_t *spa;
1439e307eb94SToomas Soome uint64_t version;
1440e307eb94SToomas Soome int rv;
1441e307eb94SToomas Soome
1442e307eb94SToomas Soome if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1443e307eb94SToomas Soome return (ENOTSUP);
1444e307eb94SToomas Soome
1445e307eb94SToomas Soome if ((spa = spa_find_by_dev(dev)) == NULL)
1446e307eb94SToomas Soome return (ENXIO);
1447e307eb94SToomas Soome
1448e307eb94SToomas Soome rv = nvlist_find(spa->spa_bootenv, BOOTENV_VERSION, DATA_TYPE_UINT64,
1449e307eb94SToomas Soome NULL, &version, NULL);
1450e307eb94SToomas Soome
1451e307eb94SToomas Soome if (rv != 0 || version != VB_NVLIST) {
1452e307eb94SToomas Soome return (ENXIO);
1453e307eb94SToomas Soome }
1454e307eb94SToomas Soome
1455e307eb94SToomas Soome dev = malloc(sizeof (*dev));
1456e307eb94SToomas Soome if (dev == NULL)
1457e307eb94SToomas Soome return (ENOMEM);
1458e307eb94SToomas Soome memcpy(dev, vdev, sizeof (*dev));
1459e307eb94SToomas Soome
1460e307eb94SToomas Soome rv = nvstore_init(spa->spa_name, &nvstore_zfs_cb, dev);
1461e307eb94SToomas Soome if (rv != 0)
1462e307eb94SToomas Soome free(dev);
1463e307eb94SToomas Soome else
1464e307eb94SToomas Soome rv = zfs_nvstore_iterate(dev, zfs_nvstore_setenv);
1465e307eb94SToomas Soome return (rv);
1466e307eb94SToomas Soome }
1467e307eb94SToomas Soome
14683830659eSToomas Soome int
zfs_probe_dev(const char * devname,uint64_t * pool_guid,bool parts_too)146971bbe6fbSWarner Losh zfs_probe_dev(const char *devname, uint64_t *pool_guid, bool parts_too)
1470b8902de1SWarner Losh {
1471b8902de1SWarner Losh struct ptable *table;
1472b8902de1SWarner Losh struct zfs_probe_args pa;
1473b8902de1SWarner Losh uint64_t mediasz;
1474b8902de1SWarner Losh int ret;
1475b8902de1SWarner Losh
1476b8902de1SWarner Losh if (pool_guid)
1477b8902de1SWarner Losh *pool_guid = 0;
14783830659eSToomas Soome pa.fd = open(devname, O_RDWR);
1479b8902de1SWarner Losh if (pa.fd == -1)
1480b8902de1SWarner Losh return (ENXIO);
1481e416eecbSToomas Soome /* Probe the whole disk */
14824f22b40aSToomas Soome ret = zfs_probe(pa.fd, pool_guid);
14834f22b40aSToomas Soome if (ret == 0)
14844f22b40aSToomas Soome return (0);
148571bbe6fbSWarner Losh if (!parts_too)
148671bbe6fbSWarner Losh return (ENXIO);
1487b8902de1SWarner Losh
1488b8902de1SWarner Losh /* Probe each partition */
1489b8902de1SWarner Losh ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
1490b8902de1SWarner Losh if (ret == 0)
1491b8902de1SWarner Losh ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz);
1492b8902de1SWarner Losh if (ret == 0) {
1493b8902de1SWarner Losh pa.devname = devname;
1494b8902de1SWarner Losh pa.pool_guid = pool_guid;
1495b8902de1SWarner Losh table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz,
1496b8902de1SWarner Losh zfs_diskread);
1497b8902de1SWarner Losh if (table != NULL) {
1498b8902de1SWarner Losh ptable_iterate(table, &pa, zfs_probe_partition);
1499b8902de1SWarner Losh ptable_close(table);
1500b8902de1SWarner Losh }
1501b8902de1SWarner Losh }
1502b8902de1SWarner Losh close(pa.fd);
1503b8902de1SWarner Losh if (pool_guid && *pool_guid == 0)
1504b8902de1SWarner Losh ret = ENXIO;
1505b8902de1SWarner Losh return (ret);
1506b8902de1SWarner Losh }
1507b8902de1SWarner Losh
1508b8902de1SWarner Losh /*
1509b8902de1SWarner Losh * Print information about ZFS pools
1510b8902de1SWarner Losh */
1511b8902de1SWarner Losh static int
zfs_dev_print(int verbose)1512b8902de1SWarner Losh zfs_dev_print(int verbose)
1513b8902de1SWarner Losh {
1514b8902de1SWarner Losh spa_t *spa;
1515b8902de1SWarner Losh char line[80];
1516b8902de1SWarner Losh int ret = 0;
1517b8902de1SWarner Losh
1518b8902de1SWarner Losh if (STAILQ_EMPTY(&zfs_pools))
1519b8902de1SWarner Losh return (0);
1520b8902de1SWarner Losh
1521b8902de1SWarner Losh printf("%s devices:", zfs_dev.dv_name);
1522b8902de1SWarner Losh if ((ret = pager_output("\n")) != 0)
1523b8902de1SWarner Losh return (ret);
1524b8902de1SWarner Losh
1525b8902de1SWarner Losh if (verbose) {
1526b8902de1SWarner Losh return (spa_all_status());
1527b8902de1SWarner Losh }
1528b8902de1SWarner Losh STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
1529b8902de1SWarner Losh snprintf(line, sizeof(line), " zfs:%s\n", spa->spa_name);
1530b8902de1SWarner Losh ret = pager_output(line);
1531b8902de1SWarner Losh if (ret != 0)
1532b8902de1SWarner Losh break;
1533b8902de1SWarner Losh }
1534b8902de1SWarner Losh return (ret);
1535b8902de1SWarner Losh }
1536b8902de1SWarner Losh
1537b8902de1SWarner Losh /*
1538b8902de1SWarner Losh * Attempt to open the pool described by (dev) for use by (f).
1539b8902de1SWarner Losh */
1540b8902de1SWarner Losh static int
zfs_dev_open(struct open_file * f,...)1541b8902de1SWarner Losh zfs_dev_open(struct open_file *f, ...)
1542b8902de1SWarner Losh {
1543b8902de1SWarner Losh va_list args;
1544b8902de1SWarner Losh struct zfs_devdesc *dev;
1545b8902de1SWarner Losh struct zfsmount *mount;
1546b8902de1SWarner Losh spa_t *spa;
1547b8902de1SWarner Losh int rv;
1548b8902de1SWarner Losh
1549b8902de1SWarner Losh va_start(args, f);
1550b8902de1SWarner Losh dev = va_arg(args, struct zfs_devdesc *);
1551b8902de1SWarner Losh va_end(args);
1552b8902de1SWarner Losh
1553e307eb94SToomas Soome if ((spa = spa_find_by_dev(dev)) == NULL)
1554b8902de1SWarner Losh return (ENXIO);
1555e307eb94SToomas Soome
1556b4cb3fe0SToomas Soome STAILQ_FOREACH(mount, &zfsmount, next) {
1557b4cb3fe0SToomas Soome if (spa->spa_guid == mount->spa->spa_guid)
1558b4cb3fe0SToomas Soome break;
1559b4cb3fe0SToomas Soome }
1560b4cb3fe0SToomas Soome
1561b4cb3fe0SToomas Soome rv = 0;
1562b4cb3fe0SToomas Soome /* This device is not set as currdev, mount us private copy. */
1563f4ed0045SToomas Soome if (mount == NULL)
1564edb26097SWarner Losh rv = zfs_mount(devformat(&dev->dd), NULL, (void **)&mount);
1565b4cb3fe0SToomas Soome
1566b4cb3fe0SToomas Soome if (rv == 0) {
1567d98de744SToomas Soome dev->dd.d_opendata = mount;
1568b4cb3fe0SToomas Soome }
1569b4cb3fe0SToomas Soome return (rv);
1570b8902de1SWarner Losh }
1571b8902de1SWarner Losh
1572b8902de1SWarner Losh static int
zfs_dev_close(struct open_file * f)1573b8902de1SWarner Losh zfs_dev_close(struct open_file *f)
1574b8902de1SWarner Losh {
1575d98de744SToomas Soome struct devdesc *dev;
1576b4cb3fe0SToomas Soome struct zfsmount *mnt, *mount;
1577b8902de1SWarner Losh
1578d98de744SToomas Soome dev = f->f_devdata;
1579d98de744SToomas Soome mnt = dev->d_opendata;
1580b4cb3fe0SToomas Soome
1581b4cb3fe0SToomas Soome STAILQ_FOREACH(mount, &zfsmount, next) {
1582b4cb3fe0SToomas Soome if (mnt->spa->spa_guid == mount->spa->spa_guid)
1583b4cb3fe0SToomas Soome break;
1584b4cb3fe0SToomas Soome }
1585b4cb3fe0SToomas Soome
1586d98de744SToomas Soome /* XXX */
1587b8902de1SWarner Losh return (0);
1588b8902de1SWarner Losh }
1589b8902de1SWarner Losh
1590b8902de1SWarner Losh static int
zfs_dev_strategy(void * devdata,int rw,daddr_t dblk,size_t size,char * buf,size_t * rsize)1591b8902de1SWarner Losh zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
1592b8902de1SWarner Losh {
1593b8902de1SWarner Losh
1594b8902de1SWarner Losh return (ENOSYS);
1595b8902de1SWarner Losh }
1596b8902de1SWarner Losh
1597b8902de1SWarner Losh struct devsw zfs_dev = {
1598b8902de1SWarner Losh .dv_name = "zfs",
1599b8902de1SWarner Losh .dv_type = DEVT_ZFS,
1600b8902de1SWarner Losh .dv_init = zfs_dev_init,
1601b8902de1SWarner Losh .dv_strategy = zfs_dev_strategy,
1602b8902de1SWarner Losh .dv_open = zfs_dev_open,
1603b8902de1SWarner Losh .dv_close = zfs_dev_close,
1604b8902de1SWarner Losh .dv_ioctl = noioctl,
1605b8902de1SWarner Losh .dv_print = zfs_dev_print,
1606e98f952cSWarner Losh .dv_cleanup = nullsys,
1607654b7837SWarner Losh .dv_fmtdev = zfs_fmtdev,
1608ca0654baSWarner Losh .dv_parsedev = zfs_parsedev,
1609b8902de1SWarner Losh };
1610b8902de1SWarner Losh
161190412431SWarner Losh static int
zfs_parsedev(struct devdesc ** idev,const char * devspec,const char ** path)1612ba11bc36SWarner Losh zfs_parsedev(struct devdesc **idev, const char *devspec, const char **path)
1613b8902de1SWarner Losh {
1614b8902de1SWarner Losh static char rootname[ZFS_MAXNAMELEN];
1615b8902de1SWarner Losh static char poolname[ZFS_MAXNAMELEN];
1616b8902de1SWarner Losh spa_t *spa;
1617b8902de1SWarner Losh const char *end;
1618b8902de1SWarner Losh const char *np;
1619b8902de1SWarner Losh const char *sep;
1620b8902de1SWarner Losh int rv;
1621ba11bc36SWarner Losh struct zfs_devdesc *dev;
1622b8902de1SWarner Losh
162333bbe5ddSWarner Losh np = devspec + 3; /* Skip the leading 'zfs' */
1624b8902de1SWarner Losh if (*np != ':')
1625b8902de1SWarner Losh return (EINVAL);
1626b8902de1SWarner Losh np++;
1627b8902de1SWarner Losh end = strrchr(np, ':');
1628b8902de1SWarner Losh if (end == NULL)
1629b8902de1SWarner Losh return (EINVAL);
1630b8902de1SWarner Losh sep = strchr(np, '/');
1631b8902de1SWarner Losh if (sep == NULL || sep >= end)
1632b8902de1SWarner Losh sep = end;
1633b8902de1SWarner Losh memcpy(poolname, np, sep - np);
1634b8902de1SWarner Losh poolname[sep - np] = '\0';
1635b8902de1SWarner Losh if (sep < end) {
1636b8902de1SWarner Losh sep++;
1637b8902de1SWarner Losh memcpy(rootname, sep, end - sep);
1638b8902de1SWarner Losh rootname[end - sep] = '\0';
1639b8902de1SWarner Losh }
1640b8902de1SWarner Losh else
1641b8902de1SWarner Losh rootname[0] = '\0';
1642b8902de1SWarner Losh
1643b8902de1SWarner Losh spa = spa_find_by_name(poolname);
1644b8902de1SWarner Losh if (!spa)
1645b8902de1SWarner Losh return (ENXIO);
1646ba11bc36SWarner Losh dev = malloc(sizeof(*dev));
1647ba11bc36SWarner Losh if (dev == NULL)
1648ba11bc36SWarner Losh return (ENOMEM);
1649b8902de1SWarner Losh dev->pool_guid = spa->spa_guid;
1650b8902de1SWarner Losh rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid);
1651ba11bc36SWarner Losh if (rv != 0) {
1652ba11bc36SWarner Losh free(dev);
1653b8902de1SWarner Losh return (rv);
1654ba11bc36SWarner Losh }
1655b8902de1SWarner Losh if (path != NULL)
1656b8902de1SWarner Losh *path = (*end == '\0') ? end : end + 1;
1657b8902de1SWarner Losh dev->dd.d_dev = &zfs_dev;
1658ba11bc36SWarner Losh *idev = &dev->dd;
1659b8902de1SWarner Losh return (0);
1660b8902de1SWarner Losh }
1661b8902de1SWarner Losh
1662b8902de1SWarner Losh char *
zfs_fmtdev(struct devdesc * vdev)1663d2d4e127SWarner Losh zfs_fmtdev(struct devdesc *vdev)
1664b8902de1SWarner Losh {
1665b8902de1SWarner Losh static char rootname[ZFS_MAXNAMELEN];
1666b8902de1SWarner Losh static char buf[2 * ZFS_MAXNAMELEN + 8];
1667b8902de1SWarner Losh struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
1668b8902de1SWarner Losh spa_t *spa;
1669b8902de1SWarner Losh
1670b8902de1SWarner Losh buf[0] = '\0';
1671d2d4e127SWarner Losh if (vdev->d_dev->dv_type != DEVT_ZFS)
1672b8902de1SWarner Losh return (buf);
1673b8902de1SWarner Losh
16746b74887fSToomas Soome /* Do we have any pools? */
1675b8902de1SWarner Losh spa = STAILQ_FIRST(&zfs_pools);
16766b74887fSToomas Soome if (spa == NULL)
16776b74887fSToomas Soome return (buf);
16786b74887fSToomas Soome
16796b74887fSToomas Soome if (dev->pool_guid == 0)
1680b8902de1SWarner Losh dev->pool_guid = spa->spa_guid;
16816b74887fSToomas Soome else
1682b8902de1SWarner Losh spa = spa_find_by_guid(dev->pool_guid);
16836b74887fSToomas Soome
1684b8902de1SWarner Losh if (spa == NULL) {
1685b8902de1SWarner Losh printf("ZFS: can't find pool by guid\n");
1686b8902de1SWarner Losh return (buf);
1687b8902de1SWarner Losh }
1688b8902de1SWarner Losh if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) {
1689b8902de1SWarner Losh printf("ZFS: can't find root filesystem\n");
1690b8902de1SWarner Losh return (buf);
1691b8902de1SWarner Losh }
1692b8902de1SWarner Losh if (zfs_rlookup(spa, dev->root_guid, rootname)) {
1693b8902de1SWarner Losh printf("ZFS: can't find filesystem by guid\n");
1694b8902de1SWarner Losh return (buf);
1695b8902de1SWarner Losh }
1696b8902de1SWarner Losh
1697b8902de1SWarner Losh if (rootname[0] == '\0')
1698e307eb94SToomas Soome snprintf(buf, sizeof(buf), "%s:%s:", dev->dd.d_dev->dv_name,
1699e307eb94SToomas Soome spa->spa_name);
1700b8902de1SWarner Losh else
1701e307eb94SToomas Soome snprintf(buf, sizeof(buf), "%s:%s/%s:", dev->dd.d_dev->dv_name,
1702e307eb94SToomas Soome spa->spa_name, rootname);
1703b8902de1SWarner Losh return (buf);
1704b8902de1SWarner Losh }
1705b8902de1SWarner Losh
1706277f38abSMariusz Zaborski static int
split_devname(const char * name,char * poolname,size_t size,const char ** dsnamep)1707277f38abSMariusz Zaborski split_devname(const char *name, char *poolname, size_t size,
1708277f38abSMariusz Zaborski const char **dsnamep)
1709b8902de1SWarner Losh {
1710b8902de1SWarner Losh const char *dsname;
1711277f38abSMariusz Zaborski size_t len;
1712277f38abSMariusz Zaborski
1713277f38abSMariusz Zaborski ASSERT(name != NULL);
1714277f38abSMariusz Zaborski ASSERT(poolname != NULL);
1715b8902de1SWarner Losh
1716b8902de1SWarner Losh len = strlen(name);
1717b8902de1SWarner Losh dsname = strchr(name, '/');
1718b8902de1SWarner Losh if (dsname != NULL) {
1719b8902de1SWarner Losh len = dsname - name;
1720b8902de1SWarner Losh dsname++;
1721b8902de1SWarner Losh } else
1722b8902de1SWarner Losh dsname = "";
1723277f38abSMariusz Zaborski
1724277f38abSMariusz Zaborski if (len + 1 > size)
1725277f38abSMariusz Zaborski return (EINVAL);
1726277f38abSMariusz Zaborski
1727277f38abSMariusz Zaborski strlcpy(poolname, name, len + 1);
1728277f38abSMariusz Zaborski
1729277f38abSMariusz Zaborski if (dsnamep != NULL)
1730277f38abSMariusz Zaborski *dsnamep = dsname;
1731277f38abSMariusz Zaborski
1732277f38abSMariusz Zaborski return (0);
1733277f38abSMariusz Zaborski }
1734277f38abSMariusz Zaborski
1735277f38abSMariusz Zaborski int
zfs_list(const char * name)1736277f38abSMariusz Zaborski zfs_list(const char *name)
1737277f38abSMariusz Zaborski {
1738277f38abSMariusz Zaborski static char poolname[ZFS_MAXNAMELEN];
1739277f38abSMariusz Zaborski uint64_t objid;
1740277f38abSMariusz Zaborski spa_t *spa;
1741277f38abSMariusz Zaborski const char *dsname;
1742277f38abSMariusz Zaborski int rv;
1743277f38abSMariusz Zaborski
1744277f38abSMariusz Zaborski if (split_devname(name, poolname, sizeof(poolname), &dsname) != 0)
1745277f38abSMariusz Zaborski return (EINVAL);
1746b8902de1SWarner Losh
1747b8902de1SWarner Losh spa = spa_find_by_name(poolname);
1748b8902de1SWarner Losh if (!spa)
1749b8902de1SWarner Losh return (ENXIO);
1750b8902de1SWarner Losh rv = zfs_lookup_dataset(spa, dsname, &objid);
1751b8902de1SWarner Losh if (rv != 0)
1752b8902de1SWarner Losh return (rv);
1753b8902de1SWarner Losh
1754b8902de1SWarner Losh return (zfs_list_dataset(spa, objid));
1755b8902de1SWarner Losh }
1756b8902de1SWarner Losh
1757b8902de1SWarner Losh void
init_zfs_boot_options(const char * currdev_in)1758277f38abSMariusz Zaborski init_zfs_boot_options(const char *currdev_in)
1759b8902de1SWarner Losh {
1760277f38abSMariusz Zaborski char poolname[ZFS_MAXNAMELEN];
1761b8902de1SWarner Losh char *beroot, *currdev;
1762277f38abSMariusz Zaborski spa_t *spa;
1763b8902de1SWarner Losh int currdev_len;
1764277f38abSMariusz Zaborski const char *dsname;
1765b8902de1SWarner Losh
1766b8902de1SWarner Losh currdev = NULL;
1767b8902de1SWarner Losh currdev_len = strlen(currdev_in);
1768b8902de1SWarner Losh if (currdev_len == 0)
1769b8902de1SWarner Losh return;
1770b8902de1SWarner Losh if (strncmp(currdev_in, "zfs:", 4) != 0)
1771b8902de1SWarner Losh return;
1772b8902de1SWarner Losh currdev = strdup(currdev_in);
1773b8902de1SWarner Losh if (currdev == NULL)
1774b8902de1SWarner Losh return;
1775b8902de1SWarner Losh /* Remove the trailing : */
1776b8902de1SWarner Losh currdev[currdev_len - 1] = '\0';
1777277f38abSMariusz Zaborski
1778b8902de1SWarner Losh setenv("zfs_be_active", currdev, 1);
1779b8902de1SWarner Losh setenv("zfs_be_currpage", "1", 1);
1780b8902de1SWarner Losh /* Remove the last element (current bootenv) */
1781b8902de1SWarner Losh beroot = strrchr(currdev, '/');
1782b8902de1SWarner Losh if (beroot != NULL)
1783b8902de1SWarner Losh beroot[0] = '\0';
1784b8902de1SWarner Losh beroot = strchr(currdev, ':') + 1;
1785b8902de1SWarner Losh setenv("zfs_be_root", beroot, 1);
1786277f38abSMariusz Zaborski
1787277f38abSMariusz Zaborski if (split_devname(beroot, poolname, sizeof(poolname), &dsname) != 0)
1788277f38abSMariusz Zaborski return;
1789277f38abSMariusz Zaborski
1790277f38abSMariusz Zaborski spa = spa_find_by_name(poolname);
1791277f38abSMariusz Zaborski if (spa == NULL)
1792277f38abSMariusz Zaborski return;
1793277f38abSMariusz Zaborski
1794277f38abSMariusz Zaborski zfs_bootenv_initial("bootenvs", spa, beroot, dsname, 0);
1795277f38abSMariusz Zaborski zfs_checkpoints_initial(spa, beroot, dsname);
1796277f38abSMariusz Zaborski
1797b8902de1SWarner Losh free(currdev);
1798b8902de1SWarner Losh }
1799b8902de1SWarner Losh
1800b8902de1SWarner Losh static void
zfs_checkpoints_initial(spa_t * spa,const char * name,const char * dsname)1801277f38abSMariusz Zaborski zfs_checkpoints_initial(spa_t *spa, const char *name, const char *dsname)
1802b8902de1SWarner Losh {
1803277f38abSMariusz Zaborski char envname[32];
1804277f38abSMariusz Zaborski
1805277f38abSMariusz Zaborski if (spa->spa_uberblock_checkpoint.ub_checkpoint_txg != 0) {
1806277f38abSMariusz Zaborski snprintf(envname, sizeof(envname), "zpool_checkpoint");
1807277f38abSMariusz Zaborski setenv(envname, name, 1);
1808277f38abSMariusz Zaborski
1809277f38abSMariusz Zaborski spa->spa_uberblock = &spa->spa_uberblock_checkpoint;
1810277f38abSMariusz Zaborski spa->spa_mos = &spa->spa_mos_checkpoint;
1811277f38abSMariusz Zaborski
1812277f38abSMariusz Zaborski zfs_bootenv_initial("bootenvs_check", spa, name, dsname, 1);
1813277f38abSMariusz Zaborski
1814277f38abSMariusz Zaborski spa->spa_uberblock = &spa->spa_uberblock_master;
1815277f38abSMariusz Zaborski spa->spa_mos = &spa->spa_mos_master;
1816277f38abSMariusz Zaborski }
1817277f38abSMariusz Zaborski }
1818277f38abSMariusz Zaborski
1819277f38abSMariusz Zaborski static void
zfs_bootenv_initial(const char * envprefix,spa_t * spa,const char * rootname,const char * dsname,int checkpoint)1820277f38abSMariusz Zaborski zfs_bootenv_initial(const char *envprefix, spa_t *spa, const char *rootname,
1821277f38abSMariusz Zaborski const char *dsname, int checkpoint)
1822277f38abSMariusz Zaborski {
1823b8902de1SWarner Losh char envname[32], envval[256];
1824b8902de1SWarner Losh uint64_t objid;
1825277f38abSMariusz Zaborski int bootenvs_idx, rv;
1826b8902de1SWarner Losh
1827b8902de1SWarner Losh SLIST_INIT(&zfs_be_head);
1828b8902de1SWarner Losh zfs_env_count = 0;
1829277f38abSMariusz Zaborski
1830b8902de1SWarner Losh rv = zfs_lookup_dataset(spa, dsname, &objid);
1831b8902de1SWarner Losh if (rv != 0)
1832b8902de1SWarner Losh return;
1833277f38abSMariusz Zaborski
1834b8902de1SWarner Losh rv = zfs_callback_dataset(spa, objid, zfs_belist_add);
1835b8902de1SWarner Losh bootenvs_idx = 0;
1836b8902de1SWarner Losh /* Populate the initial environment variables */
1837b8902de1SWarner Losh SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) {
1838b8902de1SWarner Losh /* Enumerate all bootenvs for general usage */
1839277f38abSMariusz Zaborski snprintf(envname, sizeof(envname), "%s[%d]",
1840277f38abSMariusz Zaborski envprefix, bootenvs_idx);
1841277f38abSMariusz Zaborski snprintf(envval, sizeof(envval), "zfs:%s%s/%s",
1842277f38abSMariusz Zaborski checkpoint ? "!" : "", rootname, zfs_be->name);
1843b8902de1SWarner Losh rv = setenv(envname, envval, 1);
1844b8902de1SWarner Losh if (rv != 0)
1845b8902de1SWarner Losh break;
1846b8902de1SWarner Losh bootenvs_idx++;
1847b8902de1SWarner Losh }
1848277f38abSMariusz Zaborski snprintf(envname, sizeof(envname), "%s_count", envprefix);
1849b8902de1SWarner Losh snprintf(envval, sizeof(envval), "%d", bootenvs_idx);
1850277f38abSMariusz Zaborski setenv(envname, envval, 1);
1851b8902de1SWarner Losh
1852b8902de1SWarner Losh /* Clean up the SLIST of ZFS BEs */
1853b8902de1SWarner Losh while (!SLIST_EMPTY(&zfs_be_head)) {
1854b8902de1SWarner Losh zfs_be = SLIST_FIRST(&zfs_be_head);
1855b8902de1SWarner Losh SLIST_REMOVE_HEAD(&zfs_be_head, entries);
1856215597f0SToomas Soome free(zfs_be->name);
1857b8902de1SWarner Losh free(zfs_be);
1858b8902de1SWarner Losh }
1859b8902de1SWarner Losh }
1860b8902de1SWarner Losh
1861b8902de1SWarner Losh int
zfs_bootenv(const char * name)1862b8902de1SWarner Losh zfs_bootenv(const char *name)
1863b8902de1SWarner Losh {
1864277f38abSMariusz Zaborski char poolname[ZFS_MAXNAMELEN], *root;
1865277f38abSMariusz Zaborski const char *dsname;
1866b8902de1SWarner Losh char becount[4];
1867b8902de1SWarner Losh uint64_t objid;
1868b8902de1SWarner Losh spa_t *spa;
1869277f38abSMariusz Zaborski int rv, pages, perpage, currpage;
1870b8902de1SWarner Losh
1871b8902de1SWarner Losh if (name == NULL)
1872b8902de1SWarner Losh return (EINVAL);
1873b8902de1SWarner Losh if ((root = getenv("zfs_be_root")) == NULL)
1874b8902de1SWarner Losh return (EINVAL);
1875b8902de1SWarner Losh
1876b8902de1SWarner Losh if (strcmp(name, root) != 0) {
1877b8902de1SWarner Losh if (setenv("zfs_be_root", name, 1) != 0)
1878b8902de1SWarner Losh return (ENOMEM);
1879b8902de1SWarner Losh }
1880b8902de1SWarner Losh
1881b8902de1SWarner Losh SLIST_INIT(&zfs_be_head);
1882b8902de1SWarner Losh zfs_env_count = 0;
1883277f38abSMariusz Zaborski
1884277f38abSMariusz Zaborski if (split_devname(name, poolname, sizeof(poolname), &dsname) != 0)
1885277f38abSMariusz Zaborski return (EINVAL);
1886b8902de1SWarner Losh
1887b8902de1SWarner Losh spa = spa_find_by_name(poolname);
1888b8902de1SWarner Losh if (!spa)
1889b8902de1SWarner Losh return (ENXIO);
1890b8902de1SWarner Losh rv = zfs_lookup_dataset(spa, dsname, &objid);
1891b8902de1SWarner Losh if (rv != 0)
1892b8902de1SWarner Losh return (rv);
1893b8902de1SWarner Losh rv = zfs_callback_dataset(spa, objid, zfs_belist_add);
1894b8902de1SWarner Losh
1895b8902de1SWarner Losh /* Calculate and store the number of pages of BEs */
1896b8902de1SWarner Losh perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1);
1897b8902de1SWarner Losh pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0);
1898b8902de1SWarner Losh snprintf(becount, 4, "%d", pages);
1899b8902de1SWarner Losh if (setenv("zfs_be_pages", becount, 1) != 0)
1900b8902de1SWarner Losh return (ENOMEM);
1901b8902de1SWarner Losh
1902b8902de1SWarner Losh /* Roll over the page counter if it has exceeded the maximum */
1903b8902de1SWarner Losh currpage = strtol(getenv("zfs_be_currpage"), NULL, 10);
1904b8902de1SWarner Losh if (currpage > pages) {
1905b8902de1SWarner Losh if (setenv("zfs_be_currpage", "1", 1) != 0)
1906b8902de1SWarner Losh return (ENOMEM);
1907b8902de1SWarner Losh }
1908b8902de1SWarner Losh
1909b8902de1SWarner Losh /* Populate the menu environment variables */
1910b8902de1SWarner Losh zfs_set_env();
1911b8902de1SWarner Losh
1912b8902de1SWarner Losh /* Clean up the SLIST of ZFS BEs */
1913b8902de1SWarner Losh while (!SLIST_EMPTY(&zfs_be_head)) {
1914b8902de1SWarner Losh zfs_be = SLIST_FIRST(&zfs_be_head);
1915b8902de1SWarner Losh SLIST_REMOVE_HEAD(&zfs_be_head, entries);
1916215597f0SToomas Soome free(zfs_be->name);
1917b8902de1SWarner Losh free(zfs_be);
1918b8902de1SWarner Losh }
1919b8902de1SWarner Losh
1920b8902de1SWarner Losh return (rv);
1921b8902de1SWarner Losh }
1922b8902de1SWarner Losh
1923b8902de1SWarner Losh int
zfs_belist_add(const char * name,uint64_t value __unused)1924b8902de1SWarner Losh zfs_belist_add(const char *name, uint64_t value __unused)
1925b8902de1SWarner Losh {
1926b8902de1SWarner Losh
1927b8902de1SWarner Losh /* Skip special datasets that start with a $ character */
1928b8902de1SWarner Losh if (strncmp(name, "$", 1) == 0) {
1929b8902de1SWarner Losh return (0);
1930b8902de1SWarner Losh }
1931b8902de1SWarner Losh /* Add the boot environment to the head of the SLIST */
1932b8902de1SWarner Losh zfs_be = malloc(sizeof(struct zfs_be_entry));
1933b8902de1SWarner Losh if (zfs_be == NULL) {
1934b8902de1SWarner Losh return (ENOMEM);
1935b8902de1SWarner Losh }
1936215597f0SToomas Soome zfs_be->name = strdup(name);
1937215597f0SToomas Soome if (zfs_be->name == NULL) {
1938215597f0SToomas Soome free(zfs_be);
1939215597f0SToomas Soome return (ENOMEM);
1940215597f0SToomas Soome }
1941b8902de1SWarner Losh SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries);
1942b8902de1SWarner Losh zfs_env_count++;
1943b8902de1SWarner Losh
1944b8902de1SWarner Losh return (0);
1945b8902de1SWarner Losh }
1946b8902de1SWarner Losh
1947b8902de1SWarner Losh int
zfs_set_env(void)1948b8902de1SWarner Losh zfs_set_env(void)
1949b8902de1SWarner Losh {
1950b8902de1SWarner Losh char envname[32], envval[256];
1951b8902de1SWarner Losh char *beroot, *pagenum;
1952b8902de1SWarner Losh int rv, page, ctr;
1953b8902de1SWarner Losh
1954b8902de1SWarner Losh beroot = getenv("zfs_be_root");
1955b8902de1SWarner Losh if (beroot == NULL) {
1956b8902de1SWarner Losh return (1);
1957b8902de1SWarner Losh }
1958b8902de1SWarner Losh
1959b8902de1SWarner Losh pagenum = getenv("zfs_be_currpage");
1960b8902de1SWarner Losh if (pagenum != NULL) {
1961b8902de1SWarner Losh page = strtol(pagenum, NULL, 10);
1962b8902de1SWarner Losh } else {
1963b8902de1SWarner Losh page = 1;
1964b8902de1SWarner Losh }
1965b8902de1SWarner Losh
1966b8902de1SWarner Losh ctr = 1;
1967b8902de1SWarner Losh rv = 0;
1968b8902de1SWarner Losh zfs_env_index = ZFS_BE_FIRST;
1969b8902de1SWarner Losh SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) {
1970b8902de1SWarner Losh /* Skip to the requested page number */
1971b8902de1SWarner Losh if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) {
1972b8902de1SWarner Losh ctr++;
1973b8902de1SWarner Losh continue;
1974b8902de1SWarner Losh }
1975b8902de1SWarner Losh
1976b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
1977b8902de1SWarner Losh snprintf(envval, sizeof(envval), "%s", zfs_be->name);
1978b8902de1SWarner Losh rv = setenv(envname, envval, 1);
1979b8902de1SWarner Losh if (rv != 0) {
1980b8902de1SWarner Losh break;
1981b8902de1SWarner Losh }
1982b8902de1SWarner Losh
1983b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index);
1984b8902de1SWarner Losh rv = setenv(envname, envval, 1);
1985b8902de1SWarner Losh if (rv != 0){
1986b8902de1SWarner Losh break;
1987b8902de1SWarner Losh }
1988b8902de1SWarner Losh
1989b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index);
1990b8902de1SWarner Losh rv = setenv(envname, "set_bootenv", 1);
1991b8902de1SWarner Losh if (rv != 0){
1992b8902de1SWarner Losh break;
1993b8902de1SWarner Losh }
1994b8902de1SWarner Losh
1995b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index);
1996b8902de1SWarner Losh snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name);
1997b8902de1SWarner Losh rv = setenv(envname, envval, 1);
1998b8902de1SWarner Losh if (rv != 0){
1999b8902de1SWarner Losh break;
2000b8902de1SWarner Losh }
2001b8902de1SWarner Losh
2002b8902de1SWarner Losh zfs_env_index++;
2003b8902de1SWarner Losh if (zfs_env_index > ZFS_BE_LAST) {
2004b8902de1SWarner Losh break;
2005b8902de1SWarner Losh }
2006b8902de1SWarner Losh
2007b8902de1SWarner Losh }
2008b8902de1SWarner Losh
2009b8902de1SWarner Losh for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) {
2010b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
2011b8902de1SWarner Losh (void)unsetenv(envname);
2012b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index);
2013b8902de1SWarner Losh (void)unsetenv(envname);
2014b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index);
2015b8902de1SWarner Losh (void)unsetenv(envname);
2016b8902de1SWarner Losh snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index);
2017b8902de1SWarner Losh (void)unsetenv(envname);
2018b8902de1SWarner Losh }
2019b8902de1SWarner Losh
2020b8902de1SWarner Losh return (rv);
2021b8902de1SWarner Losh }
2022