xref: /freebsd/stand/libsa/zfs/zfs.c (revision 1b3f4ccb7dd8600d010fc6a09b09ee7d74872809)
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