xref: /illumos-gate/usr/src/boot/libsa/zfs/zfs.c (revision 1b0fd692)
1cbe94e17SToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 2007 Doug Rabson
3199767f8SToomas Soome  * All rights reserved.
4199767f8SToomas Soome  *
5199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
6199767f8SToomas Soome  * modification, are permitted provided that the following conditions
7199767f8SToomas Soome  * are met:
8199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
9199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
10199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
12199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
13199767f8SToomas Soome  *
14199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15199767f8SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16199767f8SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199767f8SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18199767f8SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19199767f8SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20199767f8SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21199767f8SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22199767f8SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199767f8SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24199767f8SToomas Soome  * SUCH DAMAGE.
25199767f8SToomas Soome  */
26199767f8SToomas Soome 
27199767f8SToomas Soome #include <sys/cdefs.h>
28199767f8SToomas Soome 
29199767f8SToomas Soome /*
30199767f8SToomas Soome  *	Stand-alone file reading package.
31199767f8SToomas Soome  */
32199767f8SToomas Soome 
33781f142dSToomas Soome #include <stand.h>
34199767f8SToomas Soome #include <sys/disk.h>
35199767f8SToomas Soome #include <sys/param.h>
36199767f8SToomas Soome #include <sys/time.h>
37199767f8SToomas Soome #include <sys/queue.h>
38781f142dSToomas Soome #include <disk.h>
39199767f8SToomas Soome #include <part.h>
40199767f8SToomas Soome #include <stddef.h>
41199767f8SToomas Soome #include <stdarg.h>
42199767f8SToomas Soome #include <string.h>
43199767f8SToomas Soome #include <bootstrap.h>
44f6dea603SToomas Soome #include <inttypes.h>
45199767f8SToomas Soome 
46199767f8SToomas Soome #include "libzfs.h"
47199767f8SToomas Soome 
48199767f8SToomas Soome #include "zfsimpl.c"
49199767f8SToomas Soome 
50199767f8SToomas Soome /* Define the range of indexes to be populated with ZFS Boot Environments */
51199767f8SToomas Soome #define		ZFS_BE_FIRST	4
52199767f8SToomas Soome #define		ZFS_BE_LAST	8
53199767f8SToomas Soome 
54fec66293SToomas Soome static int	zfs_open(const char *, struct open_file *);
55fec66293SToomas Soome static int	zfs_close(struct open_file *);
56fec66293SToomas Soome static int	zfs_read(struct open_file *, void *, size_t, size_t *);
57fec66293SToomas Soome static off_t	zfs_seek(struct open_file *, off_t, int);
58fec66293SToomas Soome static int	zfs_stat(struct open_file *, struct stat *);
59fec66293SToomas Soome static int	zfs_readdir(struct open_file *, struct dirent *);
60199767f8SToomas Soome 
61199767f8SToomas Soome struct devsw zfs_dev;
62199767f8SToomas Soome 
63199767f8SToomas Soome struct fs_ops zfs_fsops = {
64199767f8SToomas Soome 	"zfs",
65199767f8SToomas Soome 	zfs_open,
66199767f8SToomas Soome 	zfs_close,
67199767f8SToomas Soome 	zfs_read,
68cf837ed8SToomas Soome 	null_write,
69199767f8SToomas Soome 	zfs_seek,
70199767f8SToomas Soome 	zfs_stat,
71199767f8SToomas Soome 	zfs_readdir
72199767f8SToomas Soome };
73199767f8SToomas Soome 
74199767f8SToomas Soome /*
75199767f8SToomas Soome  * In-core open file.
76199767f8SToomas Soome  */
77199767f8SToomas Soome struct file {
78199767f8SToomas Soome 	off_t		f_seekp;	/* seek pointer */
79199767f8SToomas Soome 	dnode_phys_t	f_dnode;
80199767f8SToomas Soome 	uint64_t	f_zap_type;	/* zap type for readdir */
81199767f8SToomas Soome 	uint64_t	f_num_leafs;	/* number of fzap leaf blocks */
82199767f8SToomas Soome 	zap_leaf_phys_t	*f_zap_leaf;	/* zap leaf buffer */
83199767f8SToomas Soome };
84199767f8SToomas Soome 
85fec66293SToomas Soome SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head =
86fec66293SToomas Soome     SLIST_HEAD_INITIALIZER(zfs_be_head);
87199767f8SToomas Soome struct zfs_be_list *zfs_be_headp;
88199767f8SToomas Soome struct zfs_be_entry {
89199767f8SToomas Soome 	const char *name;
90199767f8SToomas Soome 	SLIST_ENTRY(zfs_be_entry) entries;
91199767f8SToomas Soome } *zfs_be, *zfs_be_tmp;
92199767f8SToomas Soome 
93199767f8SToomas Soome /*
94199767f8SToomas Soome  * Open a file.
95199767f8SToomas Soome  */
96199767f8SToomas Soome static int
zfs_open(const char * upath,struct open_file * f)97199767f8SToomas Soome zfs_open(const char *upath, struct open_file *f)
98199767f8SToomas Soome {
99199767f8SToomas Soome 	struct zfsmount *mount = (struct zfsmount *)f->f_devdata;
100199767f8SToomas Soome 	struct file *fp;
101199767f8SToomas Soome 	int rc;
102199767f8SToomas Soome 
103199767f8SToomas Soome 	if (f->f_dev != &zfs_dev)
104199767f8SToomas Soome 		return (EINVAL);
105199767f8SToomas Soome 
106199767f8SToomas Soome 	/* allocate file system specific data structure */
107777db879SToomas Soome 	fp = calloc(1, sizeof (struct file));
108777db879SToomas Soome 	if (fp == NULL)
109777db879SToomas Soome 		return (ENOMEM);
110777db879SToomas Soome 	f->f_fsdata = fp;
111199767f8SToomas Soome 
112199767f8SToomas Soome 	rc = zfs_lookup(mount, upath, &fp->f_dnode);
113199767f8SToomas Soome 	fp->f_seekp = 0;
114199767f8SToomas Soome 	if (rc) {
115199767f8SToomas Soome 		f->f_fsdata = NULL;
116199767f8SToomas Soome 		free(fp);
117199767f8SToomas Soome 	}
118199767f8SToomas Soome 	return (rc);
119199767f8SToomas Soome }
120199767f8SToomas Soome 
121199767f8SToomas Soome static int
zfs_close(struct open_file * f)122199767f8SToomas Soome zfs_close(struct open_file *f)
123199767f8SToomas Soome {
124199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
125199767f8SToomas Soome 
126b713c91eSToomas Soome 	dnode_cache_obj = NULL;
127777db879SToomas Soome 	f->f_fsdata = NULL;
128199767f8SToomas Soome 
129199767f8SToomas Soome 	free(fp);
130199767f8SToomas Soome 	return (0);
131199767f8SToomas Soome }
132199767f8SToomas Soome 
133199767f8SToomas Soome /*
134199767f8SToomas Soome  * Copy a portion of a file into kernel memory.
135199767f8SToomas Soome  * Cross block boundaries when necessary.
136199767f8SToomas Soome  */
137199767f8SToomas Soome static int
zfs_read(struct open_file * f,void * start,size_t size,size_t * resid)138fec66293SToomas Soome zfs_read(struct open_file *f, void *start, size_t size, size_t *resid)
139199767f8SToomas Soome {
140199767f8SToomas Soome 	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
141199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
142199767f8SToomas Soome 	struct stat sb;
143199767f8SToomas Soome 	size_t n;
144199767f8SToomas Soome 	int rc;
145199767f8SToomas Soome 
146199767f8SToomas Soome 	rc = zfs_stat(f, &sb);
147199767f8SToomas Soome 	if (rc)
148199767f8SToomas Soome 		return (rc);
149199767f8SToomas Soome 	n = size;
150199767f8SToomas Soome 	if (fp->f_seekp + n > sb.st_size)
151199767f8SToomas Soome 		n = sb.st_size - fp->f_seekp;
152199767f8SToomas Soome 
153199767f8SToomas Soome 	rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n);
154199767f8SToomas Soome 	if (rc)
155199767f8SToomas Soome 		return (rc);
156199767f8SToomas Soome 
157199767f8SToomas Soome 	fp->f_seekp += n;
158199767f8SToomas Soome 	if (resid)
159199767f8SToomas Soome 		*resid = size - n;
160199767f8SToomas Soome 
161199767f8SToomas Soome 	return (0);
162199767f8SToomas Soome }
163199767f8SToomas Soome 
164199767f8SToomas Soome static off_t
zfs_seek(struct open_file * f,off_t offset,int where)165199767f8SToomas Soome zfs_seek(struct open_file *f, off_t offset, int where)
166199767f8SToomas Soome {
167199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
168fec66293SToomas Soome 	struct stat sb;
169fec66293SToomas Soome 	int error;
170fec66293SToomas Soome 
171199767f8SToomas Soome 	switch (where) {
172199767f8SToomas Soome 	case SEEK_SET:
173199767f8SToomas Soome 		fp->f_seekp = offset;
174199767f8SToomas Soome 		break;
175199767f8SToomas Soome 	case SEEK_CUR:
176199767f8SToomas Soome 		fp->f_seekp += offset;
177199767f8SToomas Soome 		break;
178199767f8SToomas Soome 	case SEEK_END:
179199767f8SToomas Soome 		error = zfs_stat(f, &sb);
180199767f8SToomas Soome 		if (error != 0) {
181199767f8SToomas Soome 			errno = error;
182199767f8SToomas Soome 			return (-1);
183199767f8SToomas Soome 		}
184199767f8SToomas Soome 		fp->f_seekp = sb.st_size - offset;
185199767f8SToomas Soome 		break;
186199767f8SToomas Soome 	default:
187199767f8SToomas Soome 		errno = EINVAL;
188199767f8SToomas Soome 		return (-1);
189199767f8SToomas Soome 	}
190199767f8SToomas Soome 	return (fp->f_seekp);
191199767f8SToomas Soome }
192199767f8SToomas Soome 
193199767f8SToomas Soome static int
zfs_stat(struct open_file * f,struct stat * sb)194199767f8SToomas Soome zfs_stat(struct open_file *f, struct stat *sb)
195199767f8SToomas Soome {
196199767f8SToomas Soome 	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
197199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
198199767f8SToomas Soome 
199199767f8SToomas Soome 	return (zfs_dnode_stat(spa, &fp->f_dnode, sb));
200199767f8SToomas Soome }
201199767f8SToomas Soome 
202199767f8SToomas Soome static int
zfs_readdir(struct open_file * f,struct dirent * d)203199767f8SToomas Soome zfs_readdir(struct open_file *f, struct dirent *d)
204199767f8SToomas Soome {
205199767f8SToomas Soome 	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
206199767f8SToomas Soome 	struct file *fp = (struct file *)f->f_fsdata;
207199767f8SToomas Soome 	mzap_ent_phys_t mze;
208199767f8SToomas Soome 	struct stat sb;
209199767f8SToomas Soome 	size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT;
210199767f8SToomas Soome 	int rc;
211199767f8SToomas Soome 
212199767f8SToomas Soome 	rc = zfs_stat(f, &sb);
213199767f8SToomas Soome 	if (rc)
214199767f8SToomas Soome 		return (rc);
215199767f8SToomas Soome 	if (!S_ISDIR(sb.st_mode))
216199767f8SToomas Soome 		return (ENOTDIR);
217199767f8SToomas Soome 
218199767f8SToomas Soome 	/*
219199767f8SToomas Soome 	 * If this is the first read, get the zap type.
220199767f8SToomas Soome 	 */
221199767f8SToomas Soome 	if (fp->f_seekp == 0) {
222fec66293SToomas Soome 		rc = dnode_read(spa, &fp->f_dnode, 0, &fp->f_zap_type,
223fec66293SToomas Soome 		    sizeof (fp->f_zap_type));
224199767f8SToomas Soome 		if (rc)
225199767f8SToomas Soome 			return (rc);
226199767f8SToomas Soome 
227199767f8SToomas Soome 		if (fp->f_zap_type == ZBT_MICRO) {
228199767f8SToomas Soome 			fp->f_seekp = offsetof(mzap_phys_t, mz_chunk);
229199767f8SToomas Soome 		} else {
230199767f8SToomas Soome 			rc = dnode_read(spa, &fp->f_dnode,
231fec66293SToomas Soome 			    offsetof(zap_phys_t, zap_num_leafs),
232fec66293SToomas Soome 			    &fp->f_num_leafs, sizeof (fp->f_num_leafs));
233199767f8SToomas Soome 			if (rc)
234199767f8SToomas Soome 				return (rc);
235199767f8SToomas Soome 
236199767f8SToomas Soome 			fp->f_seekp = bsize;
237777db879SToomas Soome 			fp->f_zap_leaf = malloc(bsize);
238777db879SToomas Soome 			if (fp->f_zap_leaf == NULL)
239777db879SToomas Soome 				return (ENOMEM);
240fec66293SToomas Soome 			rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp,
241fec66293SToomas Soome 			    fp->f_zap_leaf, bsize);
242199767f8SToomas Soome 			if (rc)
243199767f8SToomas Soome 				return (rc);
244199767f8SToomas Soome 		}
245199767f8SToomas Soome 	}
246199767f8SToomas Soome 
247199767f8SToomas Soome 	if (fp->f_zap_type == ZBT_MICRO) {
248199767f8SToomas Soome 	mzap_next:
249199767f8SToomas Soome 		if (fp->f_seekp >= bsize)
250199767f8SToomas Soome 			return (ENOENT);
251199767f8SToomas Soome 
252fec66293SToomas Soome 		rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, &mze,
253fec66293SToomas Soome 		    sizeof (mze));
254199767f8SToomas Soome 		if (rc)
255199767f8SToomas Soome 			return (rc);
256fec66293SToomas Soome 		fp->f_seekp += sizeof (mze);
257199767f8SToomas Soome 
258199767f8SToomas Soome 		if (!mze.mze_name[0])
259199767f8SToomas Soome 			goto mzap_next;
260199767f8SToomas Soome 
261199767f8SToomas Soome 		d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value);
262199767f8SToomas Soome 		d->d_type = ZFS_DIRENT_TYPE(mze.mze_value);
263199767f8SToomas Soome 		strcpy(d->d_name, mze.mze_name);
264199767f8SToomas Soome 		d->d_namlen = strlen(d->d_name);
265199767f8SToomas Soome 		return (0);
266199767f8SToomas Soome 	} else {
267199767f8SToomas Soome 		zap_leaf_t zl;
268199767f8SToomas Soome 		zap_leaf_chunk_t *zc, *nc;
269199767f8SToomas Soome 		int chunk;
270199767f8SToomas Soome 		size_t namelen;
271199767f8SToomas Soome 		char *p;
272199767f8SToomas Soome 		uint64_t value;
273199767f8SToomas Soome 
274199767f8SToomas Soome 		/*
275199767f8SToomas Soome 		 * Initialise this so we can use the ZAP size
276199767f8SToomas Soome 		 * calculating macros.
277199767f8SToomas Soome 		 */
278199767f8SToomas Soome 		zl.l_bs = ilog2(bsize);
279199767f8SToomas Soome 		zl.l_phys = fp->f_zap_leaf;
280199767f8SToomas Soome 
281199767f8SToomas Soome 		/*
282199767f8SToomas Soome 		 * Figure out which chunk we are currently looking at
283199767f8SToomas Soome 		 * and consider seeking to the next leaf. We use the
284199767f8SToomas Soome 		 * low bits of f_seekp as a simple chunk index.
285199767f8SToomas Soome 		 */
286199767f8SToomas Soome 	fzap_next:
287199767f8SToomas Soome 		chunk = fp->f_seekp & (bsize - 1);
288199767f8SToomas Soome 		if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) {
289199767f8SToomas Soome 			fp->f_seekp = (fp->f_seekp & ~(bsize - 1)) + bsize;
290199767f8SToomas Soome 			chunk = 0;
291199767f8SToomas Soome 
292199767f8SToomas Soome 			/*
293199767f8SToomas Soome 			 * Check for EOF and read the new leaf.
294199767f8SToomas Soome 			 */
295199767f8SToomas Soome 			if (fp->f_seekp >= bsize * fp->f_num_leafs)
296199767f8SToomas Soome 				return (ENOENT);
297199767f8SToomas Soome 
298fec66293SToomas Soome 			rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp,
299fec66293SToomas Soome 			    fp->f_zap_leaf, bsize);
300199767f8SToomas Soome 			if (rc)
301199767f8SToomas Soome 				return (rc);
302199767f8SToomas Soome 		}
303199767f8SToomas Soome 
304199767f8SToomas Soome 		zc = &ZAP_LEAF_CHUNK(&zl, chunk);
305199767f8SToomas Soome 		fp->f_seekp++;
306199767f8SToomas Soome 		if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
307199767f8SToomas Soome 			goto fzap_next;
308199767f8SToomas Soome 
309199767f8SToomas Soome 		namelen = zc->l_entry.le_name_numints;
310fec66293SToomas Soome 		if (namelen > sizeof (d->d_name))
311fec66293SToomas Soome 			namelen = sizeof (d->d_name);
312199767f8SToomas Soome 
313199767f8SToomas Soome 		/*
314199767f8SToomas Soome 		 * Paste the name back together.
315199767f8SToomas Soome 		 */
316199767f8SToomas Soome 		nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
317199767f8SToomas Soome 		p = d->d_name;
318199767f8SToomas Soome 		while (namelen > 0) {
319199767f8SToomas Soome 			int len;
320199767f8SToomas Soome 			len = namelen;
321199767f8SToomas Soome 			if (len > ZAP_LEAF_ARRAY_BYTES)
322199767f8SToomas Soome 				len = ZAP_LEAF_ARRAY_BYTES;
323199767f8SToomas Soome 			memcpy(p, nc->l_array.la_array, len);
324199767f8SToomas Soome 			p += len;
325199767f8SToomas Soome 			namelen -= len;
326199767f8SToomas Soome 			nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
327199767f8SToomas Soome 		}
328fec66293SToomas Soome 		d->d_name[sizeof (d->d_name) - 1] = 0;
329199767f8SToomas Soome 
330199767f8SToomas Soome 		/*
331199767f8SToomas Soome 		 * Assume the first eight bytes of the value are
332199767f8SToomas Soome 		 * a uint64_t.
333199767f8SToomas Soome 		 */
334199767f8SToomas Soome 		value = fzap_leaf_value(&zl, zc);
335199767f8SToomas Soome 
336199767f8SToomas Soome 		d->d_fileno = ZFS_DIRENT_OBJ(value);
337199767f8SToomas Soome 		d->d_type = ZFS_DIRENT_TYPE(value);
338199767f8SToomas Soome 		d->d_namlen = strlen(d->d_name);
339199767f8SToomas Soome 
340199767f8SToomas Soome 		return (0);
341199767f8SToomas Soome 	}
342199767f8SToomas Soome }
343199767f8SToomas Soome 
344199767f8SToomas Soome static int
vdev_read(vdev_t * vdev __unused,void * priv,off_t offset,void * buf,size_t bytes)3458eef2ab6SToomas Soome vdev_read(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
3468eef2ab6SToomas Soome     size_t bytes)
347199767f8SToomas Soome {
348cbe94e17SToomas Soome 	int fd, ret;
349cb09bd3cSToomas Soome 	size_t res, head, tail, total_size, full_sec_size;
350cb09bd3cSToomas Soome 	unsigned secsz, do_tail_read;
351cb09bd3cSToomas Soome 	off_t start_sec;
352cb09bd3cSToomas Soome 	char *outbuf, *bouncebuf;
353199767f8SToomas Soome 
354fec66293SToomas Soome 	fd = (uintptr_t)priv;
355cb09bd3cSToomas Soome 	outbuf = (char *)buf;
356cbe94e17SToomas Soome 	bouncebuf = NULL;
357cbe94e17SToomas Soome 
358cbe94e17SToomas Soome 	ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
359cbe94e17SToomas Soome 	if (ret != 0)
360cbe94e17SToomas Soome 		return (ret);
361cbe94e17SToomas Soome 
362b713c91eSToomas Soome 	/* BEGIN CSTYLED */
363cb09bd3cSToomas Soome 	/*
364cb09bd3cSToomas Soome 	 * Handling reads of arbitrary offset and size - multi-sector case
365cb09bd3cSToomas Soome 	 * and single-sector case.
366cb09bd3cSToomas Soome 	 *
367cb09bd3cSToomas Soome 	 *                        Multi-sector Case
368cb09bd3cSToomas Soome 	 *                (do_tail_read = true if tail > 0)
369cb09bd3cSToomas Soome 	 *
370cb09bd3cSToomas Soome 	 *   |<----------------------total_size--------------------->|
371cb09bd3cSToomas Soome 	 *   |                                                       |
372cb09bd3cSToomas Soome 	 *   |<--head-->|<--------------bytes------------>|<--tail-->|
373cb09bd3cSToomas Soome 	 *   |          |                                 |          |
374cb09bd3cSToomas Soome 	 *   |          |       |<~full_sec_size~>|       |          |
375cb09bd3cSToomas Soome 	 *   +------------------+                 +------------------+
376cb09bd3cSToomas Soome 	 *   |          |0101010|     .  .  .     |0101011|          |
377cb09bd3cSToomas Soome 	 *   +------------------+                 +------------------+
378cb09bd3cSToomas Soome 	 *         start_sec                         start_sec + n
379cb09bd3cSToomas Soome 	 *
380cb09bd3cSToomas Soome 	 *
381cb09bd3cSToomas Soome 	 *                      Single-sector Case
382cb09bd3cSToomas Soome 	 *                    (do_tail_read = false)
383cb09bd3cSToomas Soome 	 *
384cb09bd3cSToomas Soome 	 *              |<------total_size = secsz----->|
385cb09bd3cSToomas Soome 	 *              |                               |
386cb09bd3cSToomas Soome 	 *              |<-head->|<---bytes--->|<-tail->|
387cb09bd3cSToomas Soome 	 *              +-------------------------------+
388cb09bd3cSToomas Soome 	 *              |        |0101010101010|        |
389cb09bd3cSToomas Soome 	 *              +-------------------------------+
390cb09bd3cSToomas Soome 	 *                          start_sec
391cb09bd3cSToomas Soome 	 */
392b713c91eSToomas Soome 	/* END CSTYLED */
393cb09bd3cSToomas Soome 	start_sec = offset / secsz;
394cb09bd3cSToomas Soome 	head = offset % secsz;
395cb09bd3cSToomas Soome 	total_size = roundup2(head + bytes, secsz);
396cb09bd3cSToomas Soome 	tail = total_size - (head + bytes);
397cb09bd3cSToomas Soome 	do_tail_read = ((tail > 0) && (head + bytes > secsz));
398cb09bd3cSToomas Soome 	full_sec_size = total_size;
399cb09bd3cSToomas Soome 	if (head > 0)
400cb09bd3cSToomas Soome 		full_sec_size -= secsz;
401cb09bd3cSToomas Soome 	if (do_tail_read)
402cb09bd3cSToomas Soome 		full_sec_size -= secsz;
403cb09bd3cSToomas Soome 
404cb09bd3cSToomas Soome 	/* Return of partial sector data requires a bounce buffer. */
405cee39f3fSToomas Soome 	if ((head > 0) || do_tail_read || bytes < secsz) {
406cb09bd3cSToomas Soome 		bouncebuf = malloc(secsz);
407cbe94e17SToomas Soome 		if (bouncebuf == NULL) {
408cbe94e17SToomas Soome 			printf("vdev_read: out of memory\n");
409cbe94e17SToomas Soome 			return (ENOMEM);
410cbe94e17SToomas Soome 		}
411199767f8SToomas Soome 	}
412cbe94e17SToomas Soome 
413cb09bd3cSToomas Soome 	if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) {
414cb09bd3cSToomas Soome 		ret = errno;
415cb09bd3cSToomas Soome 		goto error;
416cb09bd3cSToomas Soome 	}
417cb09bd3cSToomas Soome 
418cb09bd3cSToomas Soome 	/* Partial data return from first sector */
419cb09bd3cSToomas Soome 	if (head > 0) {
420cb09bd3cSToomas Soome 		res = read(fd, bouncebuf, secsz);
421cb09bd3cSToomas Soome 		if (res != secsz) {
422cb09bd3cSToomas Soome 			ret = EIO;
423cb09bd3cSToomas Soome 			goto error;
424cb09bd3cSToomas Soome 		}
425cb09bd3cSToomas Soome 		memcpy(outbuf, bouncebuf + head, min(secsz - head, bytes));
426cb09bd3cSToomas Soome 		outbuf += min(secsz - head, bytes);
427cb09bd3cSToomas Soome 	}
428cb09bd3cSToomas Soome 
429cb09bd3cSToomas Soome 	/* Full data return from read sectors */
430cb09bd3cSToomas Soome 	if (full_sec_size > 0) {
431cee39f3fSToomas Soome 		if (bytes < full_sec_size) {
432cee39f3fSToomas Soome 			res = read(fd, bouncebuf, secsz);
433cee39f3fSToomas Soome 			if (res != secsz) {
434cee39f3fSToomas Soome 				ret = EIO;
435cee39f3fSToomas Soome 				goto error;
436cee39f3fSToomas Soome 			}
437cee39f3fSToomas Soome 			memcpy(outbuf, bouncebuf, bytes);
438cee39f3fSToomas Soome 		} else {
439cee39f3fSToomas Soome 			res = read(fd, outbuf, full_sec_size);
440cee39f3fSToomas Soome 			if (res != full_sec_size) {
441cee39f3fSToomas Soome 				ret = EIO;
442cee39f3fSToomas Soome 				goto error;
443cee39f3fSToomas Soome 			}
444cee39f3fSToomas Soome 			outbuf += full_sec_size;
445cb09bd3cSToomas Soome 		}
446cb09bd3cSToomas Soome 	}
447cb09bd3cSToomas Soome 
448cb09bd3cSToomas Soome 	/* Partial data return from last sector */
449cb09bd3cSToomas Soome 	if (do_tail_read) {
450cb09bd3cSToomas Soome 		res = read(fd, bouncebuf, secsz);
451cb09bd3cSToomas Soome 		if (res != secsz) {
452cbe94e17SToomas Soome 			ret = EIO;
453cbe94e17SToomas Soome 			goto error;
454cbe94e17SToomas Soome 		}
455cb09bd3cSToomas Soome 		memcpy(outbuf, bouncebuf, secsz - tail);
456cbe94e17SToomas Soome 	}
457cbe94e17SToomas Soome 
458cbe94e17SToomas Soome 	ret = 0;
459cbe94e17SToomas Soome error:
460cb09bd3cSToomas Soome 	free(bouncebuf);
461cbe94e17SToomas Soome 	return (ret);
462199767f8SToomas Soome }
463199767f8SToomas Soome 
464b713c91eSToomas Soome static int
vdev_write(vdev_t * vdev,off_t offset,void * buf,size_t bytes)465b713c91eSToomas Soome vdev_write(vdev_t *vdev, off_t offset, void *buf, size_t bytes)
466b713c91eSToomas Soome {
467b713c91eSToomas Soome 	int fd, ret;
468b713c91eSToomas Soome 	size_t head, tail, total_size, full_sec_size;
469b713c91eSToomas Soome 	unsigned secsz, do_tail_write;
470b713c91eSToomas Soome 	off_t start_sec;
471b713c91eSToomas Soome 	ssize_t res;
472b713c91eSToomas Soome 	char *outbuf, *bouncebuf;
473b713c91eSToomas Soome 
474b713c91eSToomas Soome 	fd = (uintptr_t)vdev->v_priv;
475b713c91eSToomas Soome 	outbuf = (char *)buf;
476b713c91eSToomas Soome 	bouncebuf = NULL;
477b713c91eSToomas Soome 
478b713c91eSToomas Soome 	ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
479b713c91eSToomas Soome 	if (ret != 0)
480b713c91eSToomas Soome 		return (ret);
481b713c91eSToomas Soome 
482b713c91eSToomas Soome 	start_sec = offset / secsz;
483b713c91eSToomas Soome 	head = offset % secsz;
484b713c91eSToomas Soome 	total_size = roundup2(head + bytes, secsz);
485b713c91eSToomas Soome 	tail = total_size - (head + bytes);
486b713c91eSToomas Soome 	do_tail_write = ((tail > 0) && (head + bytes > secsz));
487b713c91eSToomas Soome 	full_sec_size = total_size;
488b713c91eSToomas Soome 	if (head > 0)
489b713c91eSToomas Soome 		full_sec_size -= secsz;
490b713c91eSToomas Soome 	if (do_tail_write)
491b713c91eSToomas Soome 		full_sec_size -= secsz;
492b713c91eSToomas Soome 
493b713c91eSToomas Soome 	/* Partial sector write requires a bounce buffer. */
494b713c91eSToomas Soome 	if ((head > 0) || do_tail_write || bytes < secsz) {
495b713c91eSToomas Soome 		bouncebuf = malloc(secsz);
496b713c91eSToomas Soome 		if (bouncebuf == NULL) {
497b713c91eSToomas Soome 			printf("vdev_write: out of memory\n");
498b713c91eSToomas Soome 			return (ENOMEM);
499b713c91eSToomas Soome 		}
500b713c91eSToomas Soome 	}
501b713c91eSToomas Soome 
502b713c91eSToomas Soome 	if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) {
503b713c91eSToomas Soome 		ret = errno;
504b713c91eSToomas Soome 		goto error;
505b713c91eSToomas Soome 	}
506b713c91eSToomas Soome 
507b713c91eSToomas Soome 	/* Partial data for first sector */
508b713c91eSToomas Soome 	if (head > 0) {
509b713c91eSToomas Soome 		res = read(fd, bouncebuf, secsz);
510b713c91eSToomas Soome 		if ((unsigned)res != secsz) {
511b713c91eSToomas Soome 			ret = EIO;
512b713c91eSToomas Soome 			goto error;
513b713c91eSToomas Soome 		}
514b713c91eSToomas Soome 		memcpy(bouncebuf + head, outbuf, min(secsz - head, bytes));
515b713c91eSToomas Soome 		(void) lseek(fd, -secsz, SEEK_CUR);
516b713c91eSToomas Soome 		res = write(fd, bouncebuf, secsz);
517b713c91eSToomas Soome 		if ((unsigned)res != secsz) {
518b713c91eSToomas Soome 			ret = EIO;
519b713c91eSToomas Soome 			goto error;
520b713c91eSToomas Soome 		}
521b713c91eSToomas Soome 		outbuf += min(secsz - head, bytes);
522b713c91eSToomas Soome 	}
523b713c91eSToomas Soome 
524b713c91eSToomas Soome 	/*
525b713c91eSToomas Soome 	 * Full data write to sectors.
526b713c91eSToomas Soome 	 * Note, there is still corner case where we write
527b713c91eSToomas Soome 	 * to sector boundary, but less than sector size, e.g. write 512B
528b713c91eSToomas Soome 	 * to 4k sector.
529b713c91eSToomas Soome 	 */
530b713c91eSToomas Soome 	if (full_sec_size > 0) {
531b713c91eSToomas Soome 		if (bytes < full_sec_size) {
532b713c91eSToomas Soome 			res = read(fd, bouncebuf, secsz);
533b713c91eSToomas Soome 			if ((unsigned)res != secsz) {
534b713c91eSToomas Soome 				ret = EIO;
535b713c91eSToomas Soome 				goto error;
536b713c91eSToomas Soome 			}
537b713c91eSToomas Soome 			memcpy(bouncebuf, outbuf, bytes);
538b713c91eSToomas Soome 			(void) lseek(fd, -secsz, SEEK_CUR);
539b713c91eSToomas Soome 			res = write(fd, bouncebuf, secsz);
540b713c91eSToomas Soome 			if ((unsigned)res != secsz) {
541b713c91eSToomas Soome 				ret = EIO;
542b713c91eSToomas Soome 				goto error;
543b713c91eSToomas Soome 			}
544b713c91eSToomas Soome 		} else {
545b713c91eSToomas Soome 			res = write(fd, outbuf, full_sec_size);
546b713c91eSToomas Soome 			if ((unsigned)res != full_sec_size) {
547b713c91eSToomas Soome 				ret = EIO;
548b713c91eSToomas Soome 				goto error;
549b713c91eSToomas Soome 			}
550b713c91eSToomas Soome 			outbuf += full_sec_size;
551b713c91eSToomas Soome 		}
552b713c91eSToomas Soome 	}
553b713c91eSToomas Soome 
554b713c91eSToomas Soome 	/* Partial data write to last sector */
555b713c91eSToomas Soome 	if (do_tail_write) {
556b713c91eSToomas Soome 		res = read(fd, bouncebuf, secsz);
557b713c91eSToomas Soome 		if ((unsigned)res != secsz) {
558b713c91eSToomas Soome 			ret = EIO;
559b713c91eSToomas Soome 			goto error;
560b713c91eSToomas Soome 		}
561b713c91eSToomas Soome 		memcpy(bouncebuf, outbuf, secsz - tail);
562b713c91eSToomas Soome 		(void) lseek(fd, -secsz, SEEK_CUR);
563b713c91eSToomas Soome 		res = write(fd, bouncebuf, secsz);
564b713c91eSToomas Soome 		if ((unsigned)res != secsz) {
565b713c91eSToomas Soome 			ret = EIO;
566b713c91eSToomas Soome 			goto error;
567b713c91eSToomas Soome 		}
568b713c91eSToomas Soome 	}
569b713c91eSToomas Soome 
570b713c91eSToomas Soome 	ret = 0;
571b713c91eSToomas Soome error:
572b713c91eSToomas Soome 	free(bouncebuf);
573b713c91eSToomas Soome 	return (ret);
574b713c91eSToomas Soome }
575b713c91eSToomas Soome 
576199767f8SToomas Soome static int
zfs_dev_init(void)577199767f8SToomas Soome zfs_dev_init(void)
578199767f8SToomas Soome {
579199767f8SToomas Soome 	spa_t *spa;
580199767f8SToomas Soome 	spa_t *next;
581199767f8SToomas Soome 	spa_t *prev;
582199767f8SToomas Soome 
583199767f8SToomas Soome 	zfs_init();
584199767f8SToomas Soome 	if (archsw.arch_zfs_probe == NULL)
585199767f8SToomas Soome 		return (ENXIO);
586199767f8SToomas Soome 	archsw.arch_zfs_probe();
587199767f8SToomas Soome 
588199767f8SToomas Soome 	prev = NULL;
589199767f8SToomas Soome 	spa = STAILQ_FIRST(&zfs_pools);
590199767f8SToomas Soome 	while (spa != NULL) {
591199767f8SToomas Soome 		next = STAILQ_NEXT(spa, spa_link);
592199767f8SToomas Soome 		if (zfs_spa_init(spa)) {
593199767f8SToomas Soome 			if (prev == NULL)
594199767f8SToomas Soome 				STAILQ_REMOVE_HEAD(&zfs_pools, spa_link);
595199767f8SToomas Soome 			else
596199767f8SToomas Soome 				STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link);
597199767f8SToomas Soome 		} else
598199767f8SToomas Soome 			prev = spa;
599199767f8SToomas Soome 		spa = next;
600199767f8SToomas Soome 	}
601199767f8SToomas Soome 	return (0);
602199767f8SToomas Soome }
603199767f8SToomas Soome 
604199767f8SToomas Soome struct zfs_probe_args {
605199767f8SToomas Soome 	int		fd;
606199767f8SToomas Soome 	const char	*devname;
607199767f8SToomas Soome 	uint64_t	*pool_guid;
608fec66293SToomas Soome 	unsigned	secsz;
609199767f8SToomas Soome };
610199767f8SToomas Soome 
611199767f8SToomas Soome static int
zfs_diskread(void * arg,void * buf,size_t blocks,uint64_t offset)61279bea51bSToomas Soome zfs_diskread(void *arg, void *buf, size_t blocks, uint64_t offset)
613199767f8SToomas Soome {
614199767f8SToomas Soome 	struct zfs_probe_args *ppa;
615199767f8SToomas Soome 
616199767f8SToomas Soome 	ppa = (struct zfs_probe_args *)arg;
617199767f8SToomas Soome 	return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd,
618199767f8SToomas Soome 	    offset * ppa->secsz, buf, blocks * ppa->secsz));
619199767f8SToomas Soome }
620199767f8SToomas Soome 
621199767f8SToomas Soome static int
zfs_probe(int fd,uint64_t * pool_guid)622199767f8SToomas Soome zfs_probe(int fd, uint64_t *pool_guid)
623199767f8SToomas Soome {
624199767f8SToomas Soome 	spa_t *spa;
625199767f8SToomas Soome 	int ret;
626199767f8SToomas Soome 
627cd8e64e2SToomas Soome 	spa = NULL;
628b713c91eSToomas Soome 	ret = vdev_probe(vdev_read, vdev_write, (void *)(uintptr_t)fd, &spa);
629199767f8SToomas Soome 	if (ret == 0 && pool_guid != NULL)
630199767f8SToomas Soome 		*pool_guid = spa->spa_guid;
631199767f8SToomas Soome 	return (ret);
632199767f8SToomas Soome }
633199767f8SToomas Soome 
634199767f8SToomas Soome static int
zfs_probe_partition(void * arg,const char * partname,const struct ptable_entry * part)635199767f8SToomas Soome zfs_probe_partition(void *arg, const char *partname,
636199767f8SToomas Soome     const struct ptable_entry *part)
637199767f8SToomas Soome {
638199767f8SToomas Soome 	struct zfs_probe_args *ppa, pa;
639199767f8SToomas Soome 	struct ptable *table;
640199767f8SToomas Soome 	char devname[32];
641199767f8SToomas Soome 	int ret = 0;
642199767f8SToomas Soome 
643199767f8SToomas Soome 	/* filter out partitions *not* used by zfs */
644199767f8SToomas Soome 	switch (part->type) {
645553cfb3fSToomas Soome 	case PART_EFI:		/* efi system partition */
646199767f8SToomas Soome 	case PART_RESERVED:	/* efi reserverd */
647199767f8SToomas Soome 	case PART_VTOC_BOOT:	/* vtoc boot area */
648199767f8SToomas Soome 	case PART_VTOC_SWAP:
649199767f8SToomas Soome 		return (ret);
650199767f8SToomas Soome 	default:
65131898fe7SToomas Soome 		break;
652199767f8SToomas Soome 	}
653199767f8SToomas Soome 	ppa = (struct zfs_probe_args *)arg;
654199767f8SToomas Soome 	strncpy(devname, ppa->devname, strlen(ppa->devname) - 1);
655199767f8SToomas Soome 	devname[strlen(ppa->devname) - 1] = '\0';
656b713c91eSToomas Soome 	snprintf(devname, sizeof (devname), "%s%s:", devname, partname);
657b713c91eSToomas Soome 	pa.fd = open(devname, O_RDWR);
658199767f8SToomas Soome 	if (pa.fd == -1)
659199767f8SToomas Soome 		return (ret);
660199767f8SToomas Soome 	ret = zfs_probe(pa.fd, ppa->pool_guid);
661199767f8SToomas Soome 	if (ret == 0)
662199767f8SToomas Soome 		return (ret);
663199767f8SToomas Soome 	if (part->type == PART_SOLARIS2) {
664199767f8SToomas Soome 		pa.devname = devname;
665199767f8SToomas Soome 		pa.pool_guid = ppa->pool_guid;
666199767f8SToomas Soome 		pa.secsz = ppa->secsz;
667199767f8SToomas Soome 		table = ptable_open(&pa, part->end - part->start + 1,
668199767f8SToomas Soome 		    ppa->secsz, zfs_diskread);
669199767f8SToomas Soome 		if (table != NULL) {
6701cfad7ceSToomas Soome 			enum ptable_type pt = ptable_gettype(table);
6711cfad7ceSToomas Soome 
6721cfad7ceSToomas Soome 			if (pt == PTABLE_VTOC8 || pt == PTABLE_VTOC)
67331898fe7SToomas Soome 				ptable_iterate(table, &pa, zfs_probe_partition);
674199767f8SToomas Soome 			ptable_close(table);
675199767f8SToomas Soome 		}
676199767f8SToomas Soome 	}
677199767f8SToomas Soome 	close(pa.fd);
678199767f8SToomas Soome 	return (0);
679199767f8SToomas Soome }
680199767f8SToomas Soome 
681b713c91eSToomas Soome /*
682b713c91eSToomas Soome  * Return bootenv nvlist from pool label.
683b713c91eSToomas Soome  */
684b713c91eSToomas Soome int
zfs_get_bootenv(void * vdev,nvlist_t ** benvp)685b713c91eSToomas Soome zfs_get_bootenv(void *vdev, nvlist_t **benvp)
686b713c91eSToomas Soome {
687b713c91eSToomas Soome 	struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
688b713c91eSToomas Soome 	nvlist_t *benv = NULL;
689b713c91eSToomas Soome 	vdev_t *vd;
690b713c91eSToomas Soome 	spa_t *spa;
691b713c91eSToomas Soome 
692b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
693b713c91eSToomas Soome 		return (ENOTSUP);
694b713c91eSToomas Soome 
695b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
696b713c91eSToomas Soome 		return (ENXIO);
697b713c91eSToomas Soome 
698b713c91eSToomas Soome 	if (spa->spa_bootenv == NULL) {
699b713c91eSToomas Soome 		STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children,
700b713c91eSToomas Soome 		    v_childlink) {
701b713c91eSToomas Soome 			benv = vdev_read_bootenv(vd);
702b713c91eSToomas Soome 
703b713c91eSToomas Soome 			if (benv != NULL)
704b713c91eSToomas Soome 				break;
705b713c91eSToomas Soome 		}
706b713c91eSToomas Soome 		spa->spa_bootenv = benv;
707b713c91eSToomas Soome 	} else {
708b713c91eSToomas Soome 		benv = spa->spa_bootenv;
709b713c91eSToomas Soome 	}
710b713c91eSToomas Soome 
711b713c91eSToomas Soome 	if (benv == NULL)
712b713c91eSToomas Soome 		return (ENOENT);
713b713c91eSToomas Soome 
714b713c91eSToomas Soome 	*benvp = benv;
715b713c91eSToomas Soome 	return (0);
716b713c91eSToomas Soome }
717b713c91eSToomas Soome 
718b713c91eSToomas Soome /*
719b713c91eSToomas Soome  * Store nvlist to pool label bootenv area. Also updates cached pointer in spa.
720b713c91eSToomas Soome  */
721b713c91eSToomas Soome int
zfs_set_bootenv(void * vdev,nvlist_t * benv)722b713c91eSToomas Soome zfs_set_bootenv(void *vdev, nvlist_t *benv)
723b713c91eSToomas Soome {
724b713c91eSToomas Soome 	struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
725b713c91eSToomas Soome 	spa_t *spa;
726b713c91eSToomas Soome 	vdev_t *vd;
727b713c91eSToomas Soome 
728b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
729b713c91eSToomas Soome 		return (ENOTSUP);
730b713c91eSToomas Soome 
731b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
732b713c91eSToomas Soome 		return (ENXIO);
733b713c91eSToomas Soome 
734b713c91eSToomas Soome 	STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
735b713c91eSToomas Soome 		vdev_write_bootenv(vd, benv);
736b713c91eSToomas Soome 	}
737b713c91eSToomas Soome 
738b713c91eSToomas Soome 	spa->spa_bootenv = benv;
739b713c91eSToomas Soome 	return (0);
740b713c91eSToomas Soome }
741b713c91eSToomas Soome 
742b713c91eSToomas Soome /*
743b713c91eSToomas Soome  * Get bootonce value by key. The bootonce <key, value> pair is removed
744b713c91eSToomas Soome  * from the bootenv nvlist and the remaining nvlist is committed back to disk.
745b713c91eSToomas Soome  */
746b713c91eSToomas Soome int
zfs_get_bootonce(void * vdev,const char * key,char * buf,size_t size)747b713c91eSToomas Soome zfs_get_bootonce(void *vdev, const char *key, char *buf, size_t size)
748b713c91eSToomas Soome {
749b713c91eSToomas Soome 	nvlist_t *benv;
750b713c91eSToomas Soome 	char *result = NULL;
751b713c91eSToomas Soome 	int result_size, rv;
752b713c91eSToomas Soome 
753b713c91eSToomas Soome 	if ((rv = zfs_get_bootenv(vdev, &benv)) != 0)
754b713c91eSToomas Soome 		return (rv);
755b713c91eSToomas Soome 
756b713c91eSToomas Soome 	if ((rv = nvlist_find(benv, key, DATA_TYPE_STRING, NULL,
757b713c91eSToomas Soome 	    &result, &result_size)) == 0) {
758b713c91eSToomas Soome 		if (result_size == 0) {
759b713c91eSToomas Soome 			/* ignore empty string */
760b713c91eSToomas Soome 			rv = ENOENT;
761b713c91eSToomas Soome 		} else {
762b713c91eSToomas Soome 			size = MIN((size_t)result_size + 1, size);
763b713c91eSToomas Soome 			strlcpy(buf, result, size);
764b713c91eSToomas Soome 		}
765b713c91eSToomas Soome 		(void) nvlist_remove(benv, key, DATA_TYPE_STRING);
766b713c91eSToomas Soome 		(void) zfs_set_bootenv(vdev, benv);
767b713c91eSToomas Soome 	}
768b713c91eSToomas Soome 
769b713c91eSToomas Soome 	return (rv);
770b713c91eSToomas Soome }
771b713c91eSToomas Soome 
772b713c91eSToomas Soome /*
773b713c91eSToomas Soome  * nvstore backend.
774b713c91eSToomas Soome  */
775b713c91eSToomas Soome 
776b713c91eSToomas Soome static int zfs_nvstore_setter(void *, int, const char *,
777b713c91eSToomas Soome     const void *, size_t);
778b713c91eSToomas Soome static int zfs_nvstore_setter_str(void *, const char *, const char *,
779b713c91eSToomas Soome     const char *);
780b713c91eSToomas Soome static int zfs_nvstore_unset_impl(void *, const char *, bool);
781b713c91eSToomas Soome static int zfs_nvstore_setenv(void *, void *);
782b713c91eSToomas Soome 
783b713c91eSToomas Soome /*
784b713c91eSToomas Soome  * nvstore is only present for current rootfs pool.
785b713c91eSToomas Soome  */
786b713c91eSToomas Soome static int
zfs_nvstore_sethook(struct env_var * ev,int flags __unused,const void * value)787b713c91eSToomas Soome zfs_nvstore_sethook(struct env_var *ev, int flags __unused, const void *value)
788b713c91eSToomas Soome {
789b713c91eSToomas Soome 	struct zfs_devdesc *dev;
790b713c91eSToomas Soome 	int rv;
791b713c91eSToomas Soome 
792b713c91eSToomas Soome 	archsw.arch_getdev((void **)&dev, NULL, NULL);
793b713c91eSToomas Soome 	if (dev == NULL)
794b713c91eSToomas Soome 		return (ENXIO);
795b713c91eSToomas Soome 
796b713c91eSToomas Soome 	rv = zfs_nvstore_setter_str(dev, NULL, ev->ev_name, value);
797b713c91eSToomas Soome 
798b713c91eSToomas Soome 	free(dev);
799b713c91eSToomas Soome 	return (rv);
800b713c91eSToomas Soome }
801b713c91eSToomas Soome 
802b713c91eSToomas Soome /*
803b713c91eSToomas Soome  * nvstore is only present for current rootfs pool.
804b713c91eSToomas Soome  */
805b713c91eSToomas Soome static int
zfs_nvstore_unsethook(struct env_var * ev)806b713c91eSToomas Soome zfs_nvstore_unsethook(struct env_var *ev)
807b713c91eSToomas Soome {
808b713c91eSToomas Soome 	struct zfs_devdesc *dev;
809b713c91eSToomas Soome 	int rv;
810b713c91eSToomas Soome 
811b713c91eSToomas Soome 	archsw.arch_getdev((void **)&dev, NULL, NULL);
812b713c91eSToomas Soome 	if (dev == NULL)
813b713c91eSToomas Soome 		return (ENXIO);
814b713c91eSToomas Soome 
815b713c91eSToomas Soome 	rv = zfs_nvstore_unset_impl(dev, ev->ev_name, false);
816b713c91eSToomas Soome 
817b713c91eSToomas Soome 	free(dev);
818b713c91eSToomas Soome 	return (rv);
819b713c91eSToomas Soome }
820b713c91eSToomas Soome 
821b713c91eSToomas Soome static int
zfs_nvstore_getter(void * vdev,const char * name,void ** data)822b713c91eSToomas Soome zfs_nvstore_getter(void *vdev, const char *name, void **data)
823b713c91eSToomas Soome {
824b713c91eSToomas Soome 	struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
825b713c91eSToomas Soome 	spa_t *spa;
826b713c91eSToomas Soome 	nvlist_t *nv;
827b713c91eSToomas Soome 	char *str, **ptr;
828b713c91eSToomas Soome 	int size;
829b713c91eSToomas Soome 	int rv;
830b713c91eSToomas Soome 
831b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
832b713c91eSToomas Soome 		return (ENOTSUP);
833b713c91eSToomas Soome 
834b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
835b713c91eSToomas Soome 		return (ENXIO);
836b713c91eSToomas Soome 
837b713c91eSToomas Soome 	if (spa->spa_bootenv == NULL)
838b713c91eSToomas Soome 		return (ENXIO);
839b713c91eSToomas Soome 
840b713c91eSToomas Soome 	if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
841b713c91eSToomas Soome 	    NULL, &nv, NULL) != 0)
842b713c91eSToomas Soome 		return (ENOENT);
843b713c91eSToomas Soome 
844b713c91eSToomas Soome 	rv = nvlist_find(nv, name, DATA_TYPE_STRING, NULL, &str, &size);
845b713c91eSToomas Soome 	if (rv == 0) {
846b713c91eSToomas Soome 		ptr = (char **)data;
847b713c91eSToomas Soome 		asprintf(ptr, "%.*s", size, str);
848b713c91eSToomas Soome 		if (*data == NULL)
849b713c91eSToomas Soome 			rv = ENOMEM;
850b713c91eSToomas Soome 	}
851b713c91eSToomas Soome 	nvlist_destroy(nv);
852b713c91eSToomas Soome 	return (rv);
853b713c91eSToomas Soome }
854b713c91eSToomas Soome 
855b713c91eSToomas Soome static int
zfs_nvstore_setter(void * vdev,int type,const char * name,const void * data,size_t size)856b713c91eSToomas Soome zfs_nvstore_setter(void *vdev, int type, const char *name,
857b713c91eSToomas Soome     const void *data, size_t size)
858b713c91eSToomas Soome {
859b713c91eSToomas Soome 	struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
860b713c91eSToomas Soome 	spa_t *spa;
861b713c91eSToomas Soome 	nvlist_t *nv;
862b713c91eSToomas Soome 	int rv;
863b713c91eSToomas Soome 	bool env_set = true;
864b713c91eSToomas Soome 
865b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
866b713c91eSToomas Soome 		return (ENOTSUP);
867b713c91eSToomas Soome 
868b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
869b713c91eSToomas Soome 		return (ENXIO);
870b713c91eSToomas Soome 
871b713c91eSToomas Soome 	if (spa->spa_bootenv == NULL)
872b713c91eSToomas Soome 		return (ENXIO);
873b713c91eSToomas Soome 
874b713c91eSToomas Soome 	if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
875b713c91eSToomas Soome 	    NULL, &nv, NULL) != 0) {
876b713c91eSToomas Soome 		nv = nvlist_create(NV_UNIQUE_NAME);
877b713c91eSToomas Soome 		if (nv == NULL)
878b713c91eSToomas Soome 			return (ENOMEM);
879b713c91eSToomas Soome 	}
880b713c91eSToomas Soome 
881b713c91eSToomas Soome 	rv = 0;
882b713c91eSToomas Soome 	switch (type) {
883b713c91eSToomas Soome 	case DATA_TYPE_INT8:
884b713c91eSToomas Soome 		if (size != sizeof (int8_t)) {
885b713c91eSToomas Soome 			rv = EINVAL;
886b713c91eSToomas Soome 			break;
887b713c91eSToomas Soome 		}
888b713c91eSToomas Soome 		rv = nvlist_add_int8(nv, name, *(int8_t *)data);
889b713c91eSToomas Soome 		break;
890b713c91eSToomas Soome 
891b713c91eSToomas Soome 	case DATA_TYPE_INT16:
892b713c91eSToomas Soome 		if (size != sizeof (int16_t)) {
893b713c91eSToomas Soome 			rv = EINVAL;
894b713c91eSToomas Soome 			break;
895b713c91eSToomas Soome 		}
896b713c91eSToomas Soome 		rv = nvlist_add_int16(nv, name, *(int16_t *)data);
897b713c91eSToomas Soome 		break;
898b713c91eSToomas Soome 
899b713c91eSToomas Soome 	case DATA_TYPE_INT32:
900b713c91eSToomas Soome 		if (size != sizeof (int32_t)) {
901b713c91eSToomas Soome 			rv = EINVAL;
902b713c91eSToomas Soome 			break;
903b713c91eSToomas Soome 		}
904b713c91eSToomas Soome 		rv = nvlist_add_int32(nv, name, *(int32_t *)data);
905b713c91eSToomas Soome 		break;
906b713c91eSToomas Soome 
907b713c91eSToomas Soome 	case DATA_TYPE_INT64:
908b713c91eSToomas Soome 		if (size != sizeof (int64_t)) {
909b713c91eSToomas Soome 			rv = EINVAL;
910b713c91eSToomas Soome 			break;
911b713c91eSToomas Soome 		}
912b713c91eSToomas Soome 		rv = nvlist_add_int64(nv, name, *(int64_t *)data);
913b713c91eSToomas Soome 		break;
914b713c91eSToomas Soome 
915b713c91eSToomas Soome 	case DATA_TYPE_BYTE:
916b713c91eSToomas Soome 		if (size != sizeof (uint8_t)) {
917b713c91eSToomas Soome 			rv = EINVAL;
918b713c91eSToomas Soome 			break;
919b713c91eSToomas Soome 		}
920b713c91eSToomas Soome 		rv = nvlist_add_byte(nv, name, *(int8_t *)data);
921b713c91eSToomas Soome 		break;
922b713c91eSToomas Soome 
923b713c91eSToomas Soome 	case DATA_TYPE_UINT8:
924b713c91eSToomas Soome 		if (size != sizeof (uint8_t)) {
925b713c91eSToomas Soome 			rv = EINVAL;
926b713c91eSToomas Soome 			break;
927b713c91eSToomas Soome 		}
928b713c91eSToomas Soome 		rv = nvlist_add_uint8(nv, name, *(int8_t *)data);
929b713c91eSToomas Soome 		break;
930b713c91eSToomas Soome 	case DATA_TYPE_UINT16:
931b713c91eSToomas Soome 		if (size != sizeof (uint16_t)) {
932b713c91eSToomas Soome 			rv = EINVAL;
933b713c91eSToomas Soome 			break;
934b713c91eSToomas Soome 		}
935b713c91eSToomas Soome 		rv = nvlist_add_uint16(nv, name, *(uint16_t *)data);
936b713c91eSToomas Soome 		break;
937b713c91eSToomas Soome 
938b713c91eSToomas Soome 	case DATA_TYPE_UINT32:
939b713c91eSToomas Soome 		if (size != sizeof (uint32_t)) {
940b713c91eSToomas Soome 			rv = EINVAL;
941b713c91eSToomas Soome 			break;
942b713c91eSToomas Soome 		}
943b713c91eSToomas Soome 		rv = nvlist_add_uint32(nv, name, *(uint32_t *)data);
944b713c91eSToomas Soome 		break;
945b713c91eSToomas Soome 
946b713c91eSToomas Soome 	case DATA_TYPE_UINT64:
947b713c91eSToomas Soome 		if (size != sizeof (uint64_t)) {
948b713c91eSToomas Soome 			rv = EINVAL;
949b713c91eSToomas Soome 			break;
950b713c91eSToomas Soome 		}
951b713c91eSToomas Soome 		rv = nvlist_add_uint64(nv, name, *(uint64_t *)data);
952b713c91eSToomas Soome 		break;
953b713c91eSToomas Soome 
954b713c91eSToomas Soome 	case DATA_TYPE_STRING:
955b713c91eSToomas Soome 		rv = nvlist_add_string(nv, name, data);
956b713c91eSToomas Soome 		break;
957b713c91eSToomas Soome 
958b713c91eSToomas Soome 	case DATA_TYPE_BOOLEAN_VALUE:
959b713c91eSToomas Soome 		if (size != sizeof (boolean_t)) {
960b713c91eSToomas Soome 			rv = EINVAL;
961b713c91eSToomas Soome 			break;
962b713c91eSToomas Soome 		}
963b713c91eSToomas Soome 		rv = nvlist_add_boolean_value(nv, name, *(boolean_t *)data);
964b713c91eSToomas Soome 		break;
965b713c91eSToomas Soome 
966b713c91eSToomas Soome 	default:
967b713c91eSToomas Soome 		rv = EINVAL;
968b713c91eSToomas Soome 		break;
969b713c91eSToomas Soome 	}
970b713c91eSToomas Soome 
971b713c91eSToomas Soome 	if (rv == 0) {
972b713c91eSToomas Soome 		rv = nvlist_add_nvlist(spa->spa_bootenv, OS_NVSTORE, nv);
973b713c91eSToomas Soome 		if (rv == 0) {
974b713c91eSToomas Soome 			rv = zfs_set_bootenv(vdev, spa->spa_bootenv);
975b713c91eSToomas Soome 		}
976b713c91eSToomas Soome 		if (rv == 0) {
977b713c91eSToomas Soome 			if (env_set) {
978b713c91eSToomas Soome 				rv = zfs_nvstore_setenv(vdev,
979b713c91eSToomas Soome 				    nvpair_find(nv, name));
980b713c91eSToomas Soome 			} else {
981b713c91eSToomas Soome 				env_discard(env_getenv(name));
982b713c91eSToomas Soome 				rv = 0;
983b713c91eSToomas Soome 			}
984b713c91eSToomas Soome 		}
985b713c91eSToomas Soome 	}
986b713c91eSToomas Soome 
987b713c91eSToomas Soome 	nvlist_destroy(nv);
988b713c91eSToomas Soome 	return (rv);
989b713c91eSToomas Soome }
990b713c91eSToomas Soome 
991b713c91eSToomas Soome static int
get_int64(const char * data,int64_t * ip)992b713c91eSToomas Soome get_int64(const char *data, int64_t *ip)
993b713c91eSToomas Soome {
994b713c91eSToomas Soome 	char *end;
995b713c91eSToomas Soome 	int64_t val;
996b713c91eSToomas Soome 
997b713c91eSToomas Soome 	errno = 0;
998b713c91eSToomas Soome 	val = strtoll(data, &end, 0);
999b713c91eSToomas Soome 	if (errno != 0 || *data == '\0' || *end != '\0')
1000b713c91eSToomas Soome 		return (EINVAL);
1001b713c91eSToomas Soome 
1002b713c91eSToomas Soome 	*ip = val;
1003b713c91eSToomas Soome 	return (0);
1004b713c91eSToomas Soome }
1005b713c91eSToomas Soome 
1006b713c91eSToomas Soome static int
get_uint64(const char * data,uint64_t * ip)1007b713c91eSToomas Soome get_uint64(const char *data, uint64_t *ip)
1008b713c91eSToomas Soome {
1009b713c91eSToomas Soome 	char *end;
1010b713c91eSToomas Soome 	uint64_t val;
1011b713c91eSToomas Soome 
1012b713c91eSToomas Soome 	errno = 0;
1013b713c91eSToomas Soome 	val = strtoull(data, &end, 0);
1014b713c91eSToomas Soome 	if (errno != 0 || *data == '\0' || *end != '\0')
1015b713c91eSToomas Soome 		return (EINVAL);
1016b713c91eSToomas Soome 
1017b713c91eSToomas Soome 	*ip = val;
1018b713c91eSToomas Soome 	return (0);
1019b713c91eSToomas Soome }
1020b713c91eSToomas Soome 
1021b713c91eSToomas Soome /*
1022b713c91eSToomas Soome  * Translate textual data to data type. If type is not set, and we are
1023b713c91eSToomas Soome  * creating new pair, use DATA_TYPE_STRING.
1024b713c91eSToomas Soome  */
1025b713c91eSToomas Soome static int
zfs_nvstore_setter_str(void * vdev,const char * type,const char * name,const char * data)1026b713c91eSToomas Soome zfs_nvstore_setter_str(void *vdev, const char *type, const char *name,
1027b713c91eSToomas Soome     const char *data)
1028b713c91eSToomas Soome {
1029b713c91eSToomas Soome 	struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
1030b713c91eSToomas Soome 	spa_t *spa;
1031b713c91eSToomas Soome 	nvlist_t *nv;
1032b713c91eSToomas Soome 	int rv;
1033b713c91eSToomas Soome 	data_type_t dt;
1034b713c91eSToomas Soome 	int64_t val;
1035b713c91eSToomas Soome 	uint64_t uval;
1036b713c91eSToomas Soome 
1037b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1038b713c91eSToomas Soome 		return (ENOTSUP);
1039b713c91eSToomas Soome 
1040b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
1041b713c91eSToomas Soome 		return (ENXIO);
1042b713c91eSToomas Soome 
1043b713c91eSToomas Soome 	if (spa->spa_bootenv == NULL)
1044b713c91eSToomas Soome 		return (ENXIO);
1045b713c91eSToomas Soome 
1046b713c91eSToomas Soome 	if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
1047b713c91eSToomas Soome 	    NULL, &nv, NULL) != 0) {
1048b713c91eSToomas Soome 		nv = NULL;
1049b713c91eSToomas Soome 	}
1050b713c91eSToomas Soome 
1051b713c91eSToomas Soome 	if (type == NULL) {
1052b713c91eSToomas Soome 		nvp_header_t *nvh;
1053b713c91eSToomas Soome 
1054b713c91eSToomas Soome 		/*
1055b713c91eSToomas Soome 		 * if there is no existing pair, default to string.
1056b713c91eSToomas Soome 		 * Otherwise, use type from existing pair.
1057b713c91eSToomas Soome 		 */
1058b713c91eSToomas Soome 		nvh = nvpair_find(nv, name);
1059b713c91eSToomas Soome 		if (nvh == NULL) {
1060b713c91eSToomas Soome 			dt = DATA_TYPE_STRING;
1061b713c91eSToomas Soome 		} else {
1062b713c91eSToomas Soome 			nv_string_t *nvp_name;
1063b713c91eSToomas Soome 			nv_pair_data_t *nvp_data;
1064b713c91eSToomas Soome 
1065b713c91eSToomas Soome 			nvp_name = (nv_string_t *)(nvh + 1);
1066b713c91eSToomas Soome 			nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
1067b713c91eSToomas Soome 			    NV_ALIGN4(nvp_name->nv_size));
1068b713c91eSToomas Soome 			dt = nvp_data->nv_type;
1069b713c91eSToomas Soome 		}
1070b713c91eSToomas Soome 	} else {
1071b713c91eSToomas Soome 		dt = nvpair_type_from_name(type);
1072b713c91eSToomas Soome 	}
1073b713c91eSToomas Soome 	nvlist_destroy(nv);
1074b713c91eSToomas Soome 
1075b713c91eSToomas Soome 	rv = 0;
1076b713c91eSToomas Soome 	switch (dt) {
1077b713c91eSToomas Soome 	case DATA_TYPE_INT8:
1078b713c91eSToomas Soome 		rv = get_int64(data, &val);
1079b713c91eSToomas Soome 		if (rv == 0) {
1080b713c91eSToomas Soome 			int8_t v = val;
1081b713c91eSToomas Soome 
1082b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1083b713c91eSToomas Soome 		}
1084b713c91eSToomas Soome 		break;
1085b713c91eSToomas Soome 	case DATA_TYPE_INT16:
1086b713c91eSToomas Soome 		rv = get_int64(data, &val);
1087b713c91eSToomas Soome 		if (rv == 0) {
1088b713c91eSToomas Soome 			int16_t v = val;
1089b713c91eSToomas Soome 
1090b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1091b713c91eSToomas Soome 		}
1092b713c91eSToomas Soome 		break;
1093b713c91eSToomas Soome 	case DATA_TYPE_INT32:
1094b713c91eSToomas Soome 		rv = get_int64(data, &val);
1095b713c91eSToomas Soome 		if (rv == 0) {
1096b713c91eSToomas Soome 			int32_t v = val;
1097b713c91eSToomas Soome 
1098b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1099b713c91eSToomas Soome 		}
1100b713c91eSToomas Soome 		break;
1101b713c91eSToomas Soome 	case DATA_TYPE_INT64:
1102b713c91eSToomas Soome 		rv = get_int64(data, &val);
1103b713c91eSToomas Soome 		if (rv == 0) {
1104b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &val,
1105b713c91eSToomas Soome 			    sizeof (val));
1106b713c91eSToomas Soome 		}
1107b713c91eSToomas Soome 		break;
1108b713c91eSToomas Soome 
1109b713c91eSToomas Soome 	case DATA_TYPE_BYTE:
1110b713c91eSToomas Soome 		rv = get_uint64(data, &uval);
1111b713c91eSToomas Soome 		if (rv == 0) {
1112b713c91eSToomas Soome 			uint8_t v = uval;
1113b713c91eSToomas Soome 
1114b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1115b713c91eSToomas Soome 		}
1116b713c91eSToomas Soome 		break;
1117b713c91eSToomas Soome 
1118b713c91eSToomas Soome 	case DATA_TYPE_UINT8:
1119b713c91eSToomas Soome 		rv = get_uint64(data, &uval);
1120b713c91eSToomas Soome 		if (rv == 0) {
1121b713c91eSToomas Soome 			uint8_t v = uval;
1122b713c91eSToomas Soome 
1123b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1124b713c91eSToomas Soome 		}
1125b713c91eSToomas Soome 		break;
1126b713c91eSToomas Soome 
1127b713c91eSToomas Soome 	case DATA_TYPE_UINT16:
1128b713c91eSToomas Soome 		rv = get_uint64(data, &uval);
1129b713c91eSToomas Soome 		if (rv == 0) {
1130b713c91eSToomas Soome 			uint16_t v = uval;
1131b713c91eSToomas Soome 
1132b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1133b713c91eSToomas Soome 		}
1134b713c91eSToomas Soome 		break;
1135b713c91eSToomas Soome 
1136b713c91eSToomas Soome 	case DATA_TYPE_UINT32:
1137b713c91eSToomas Soome 		rv = get_uint64(data, &uval);
1138b713c91eSToomas Soome 		if (rv == 0) {
1139b713c91eSToomas Soome 			uint32_t v = uval;
1140b713c91eSToomas Soome 
1141b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1142b713c91eSToomas Soome 		}
1143b713c91eSToomas Soome 		break;
1144b713c91eSToomas Soome 
1145b713c91eSToomas Soome 	case DATA_TYPE_UINT64:
1146b713c91eSToomas Soome 		rv = get_uint64(data, &uval);
1147b713c91eSToomas Soome 		if (rv == 0) {
1148b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &uval,
1149b713c91eSToomas Soome 			    sizeof (uval));
1150b713c91eSToomas Soome 		}
1151b713c91eSToomas Soome 		break;
1152b713c91eSToomas Soome 
1153b713c91eSToomas Soome 	case DATA_TYPE_STRING:
1154b713c91eSToomas Soome 		rv = zfs_nvstore_setter(vdev, dt, name, data, strlen(data) + 1);
1155b713c91eSToomas Soome 		break;
1156b713c91eSToomas Soome 
1157b713c91eSToomas Soome 	case DATA_TYPE_BOOLEAN_VALUE:
1158b713c91eSToomas Soome 		rv = get_int64(data, &val);
1159b713c91eSToomas Soome 		if (rv == 0) {
1160b713c91eSToomas Soome 			boolean_t v = val;
1161b713c91eSToomas Soome 
1162b713c91eSToomas Soome 			rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
1163b713c91eSToomas Soome 		}
1164b713c91eSToomas Soome 		break;
1165b713c91eSToomas Soome 
1166b713c91eSToomas Soome 	default:
1167b713c91eSToomas Soome 		rv = EINVAL;
1168b713c91eSToomas Soome 	}
1169b713c91eSToomas Soome 	return (rv);
1170b713c91eSToomas Soome }
1171b713c91eSToomas Soome 
1172b713c91eSToomas Soome static int
zfs_nvstore_unset_impl(void * vdev,const char * name,bool unset_env)1173b713c91eSToomas Soome zfs_nvstore_unset_impl(void *vdev, const char *name, bool unset_env)
1174b713c91eSToomas Soome {
1175b713c91eSToomas Soome 	struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
1176b713c91eSToomas Soome 	spa_t *spa;
1177b713c91eSToomas Soome 	nvlist_t *nv;
1178b713c91eSToomas Soome 	int rv;
1179b713c91eSToomas Soome 
1180b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1181b713c91eSToomas Soome 		return (ENOTSUP);
1182b713c91eSToomas Soome 
1183b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
1184b713c91eSToomas Soome 		return (ENXIO);
1185b713c91eSToomas Soome 
1186b713c91eSToomas Soome 	if (spa->spa_bootenv == NULL)
1187b713c91eSToomas Soome 		return (ENXIO);
1188b713c91eSToomas Soome 
1189b713c91eSToomas Soome 	if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
1190b713c91eSToomas Soome 	    NULL, &nv, NULL) != 0)
1191b713c91eSToomas Soome 		return (ENOENT);
1192b713c91eSToomas Soome 
1193b713c91eSToomas Soome 	rv = nvlist_remove(nv, name, DATA_TYPE_UNKNOWN);
1194b713c91eSToomas Soome 	if (rv == 0) {
1195b713c91eSToomas Soome 		if (nvlist_next_nvpair(nv, NULL) == NULL) {
1196b713c91eSToomas Soome 			rv = nvlist_remove(spa->spa_bootenv, OS_NVSTORE,
1197b713c91eSToomas Soome 			    DATA_TYPE_NVLIST);
1198b713c91eSToomas Soome 		} else {
1199b713c91eSToomas Soome 			rv = nvlist_add_nvlist(spa->spa_bootenv,
1200b713c91eSToomas Soome 			    OS_NVSTORE, nv);
1201b713c91eSToomas Soome 		}
1202b713c91eSToomas Soome 		if (rv == 0)
1203b713c91eSToomas Soome 			rv = zfs_set_bootenv(vdev, spa->spa_bootenv);
1204b713c91eSToomas Soome 	}
1205b713c91eSToomas Soome 
1206*1b0fd692SToomas Soome 	if (unset_env) {
1207*1b0fd692SToomas Soome 		struct env_var *ev = env_getenv(name);
1208*1b0fd692SToomas Soome 
1209*1b0fd692SToomas Soome 		if (ev != NULL)
1210*1b0fd692SToomas Soome 			env_discard(ev);
1211*1b0fd692SToomas Soome 	}
1212b713c91eSToomas Soome 	return (rv);
1213b713c91eSToomas Soome }
1214b713c91eSToomas Soome 
1215b713c91eSToomas Soome static int
zfs_nvstore_unset(void * vdev,const char * name)1216b713c91eSToomas Soome zfs_nvstore_unset(void *vdev, const char *name)
1217b713c91eSToomas Soome {
1218b713c91eSToomas Soome 	return (zfs_nvstore_unset_impl(vdev, name, true));
1219b713c91eSToomas Soome }
1220b713c91eSToomas Soome 
1221b713c91eSToomas Soome static int
zfs_nvstore_print(void * vdev __unused,void * ptr)1222b713c91eSToomas Soome zfs_nvstore_print(void *vdev __unused, void *ptr)
1223b713c91eSToomas Soome {
1224b713c91eSToomas Soome 
1225b713c91eSToomas Soome 	nvpair_print(ptr, 0);
1226b713c91eSToomas Soome 	return (0);
1227b713c91eSToomas Soome }
1228b713c91eSToomas Soome 
1229b713c91eSToomas Soome /*
1230b713c91eSToomas Soome  * Create environment variable from nvpair.
1231b713c91eSToomas Soome  * set hook will update nvstore with new value, unset hook will remove
1232b713c91eSToomas Soome  * variable from nvstore.
1233b713c91eSToomas Soome  */
1234b713c91eSToomas Soome static int
zfs_nvstore_setenv(void * vdev __unused,void * ptr)1235b713c91eSToomas Soome zfs_nvstore_setenv(void *vdev __unused, void *ptr)
1236b713c91eSToomas Soome {
1237b713c91eSToomas Soome 	nvp_header_t *nvh = ptr;
1238b713c91eSToomas Soome 	nv_string_t *nvp_name, *nvp_value;
1239b713c91eSToomas Soome 	nv_pair_data_t *nvp_data;
1240b713c91eSToomas Soome 	char *name, *value;
1241b713c91eSToomas Soome 	int rv = 0;
1242b713c91eSToomas Soome 
1243b713c91eSToomas Soome 	if (nvh == NULL)
1244b713c91eSToomas Soome 		return (ENOENT);
1245b713c91eSToomas Soome 
1246b713c91eSToomas Soome 	nvp_name = (nv_string_t *)(nvh + 1);
1247b713c91eSToomas Soome 	nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
1248b713c91eSToomas Soome 	    NV_ALIGN4(nvp_name->nv_size));
1249b713c91eSToomas Soome 
1250b713c91eSToomas Soome 	if ((name = nvstring_get(nvp_name)) == NULL)
1251b713c91eSToomas Soome 		return (ENOMEM);
1252b713c91eSToomas Soome 
1253b713c91eSToomas Soome 	value = NULL;
1254b713c91eSToomas Soome 	switch (nvp_data->nv_type) {
1255b713c91eSToomas Soome 	case DATA_TYPE_BYTE:
1256b713c91eSToomas Soome 	case DATA_TYPE_UINT8:
1257b713c91eSToomas Soome 		(void) asprintf(&value, "%uc",
1258b713c91eSToomas Soome 		    *(unsigned *)&nvp_data->nv_data[0]);
1259b713c91eSToomas Soome 		if (value == NULL)
1260b713c91eSToomas Soome 			rv = ENOMEM;
1261b713c91eSToomas Soome 		break;
1262b713c91eSToomas Soome 
1263b713c91eSToomas Soome 	case DATA_TYPE_INT8:
1264b713c91eSToomas Soome 		(void) asprintf(&value, "%c", *(int *)&nvp_data->nv_data[0]);
1265b713c91eSToomas Soome 		if (value == NULL)
1266b713c91eSToomas Soome 			rv = ENOMEM;
1267b713c91eSToomas Soome 		break;
1268b713c91eSToomas Soome 
1269b713c91eSToomas Soome 	case DATA_TYPE_INT16:
1270b713c91eSToomas Soome 		(void) asprintf(&value, "%hd", *(short *)&nvp_data->nv_data[0]);
1271b713c91eSToomas Soome 		if (value == NULL)
1272b713c91eSToomas Soome 			rv = ENOMEM;
1273b713c91eSToomas Soome 		break;
1274b713c91eSToomas Soome 
1275b713c91eSToomas Soome 	case DATA_TYPE_UINT16:
1276b713c91eSToomas Soome 		(void) asprintf(&value, "%hu",
1277b713c91eSToomas Soome 		    *(unsigned short *)&nvp_data->nv_data[0]);
1278b713c91eSToomas Soome 		if (value == NULL)
1279b713c91eSToomas Soome 			rv = ENOMEM;
1280b713c91eSToomas Soome 		break;
1281b713c91eSToomas Soome 
1282b713c91eSToomas Soome 	case DATA_TYPE_BOOLEAN_VALUE:
1283b713c91eSToomas Soome 	case DATA_TYPE_INT32:
1284b713c91eSToomas Soome 		(void) asprintf(&value, "%d", *(int *)&nvp_data->nv_data[0]);
1285b713c91eSToomas Soome 		if (value == NULL)
1286b713c91eSToomas Soome 			rv = ENOMEM;
1287b713c91eSToomas Soome 		break;
1288b713c91eSToomas Soome 
1289b713c91eSToomas Soome 	case DATA_TYPE_UINT32:
1290b713c91eSToomas Soome 		(void) asprintf(&value, "%u",
1291b713c91eSToomas Soome 		    *(unsigned *)&nvp_data->nv_data[0]);
1292b713c91eSToomas Soome 		if (value == NULL)
1293b713c91eSToomas Soome 			rv = ENOMEM;
1294b713c91eSToomas Soome 		break;
1295b713c91eSToomas Soome 
1296b713c91eSToomas Soome 	case DATA_TYPE_INT64:
1297b713c91eSToomas Soome 		(void) asprintf(&value, "%jd",
1298b713c91eSToomas Soome 		    (intmax_t)*(int64_t *)&nvp_data->nv_data[0]);
1299b713c91eSToomas Soome 		if (value == NULL)
1300b713c91eSToomas Soome 			rv = ENOMEM;
1301b713c91eSToomas Soome 		break;
1302b713c91eSToomas Soome 
1303b713c91eSToomas Soome 	case DATA_TYPE_UINT64:
1304b713c91eSToomas Soome 		(void) asprintf(&value, "%ju",
1305b713c91eSToomas Soome 		    (uintmax_t)*(uint64_t *)&nvp_data->nv_data[0]);
1306b713c91eSToomas Soome 		if (value == NULL)
1307b713c91eSToomas Soome 			rv = ENOMEM;
1308b713c91eSToomas Soome 		break;
1309b713c91eSToomas Soome 
1310b713c91eSToomas Soome 	case DATA_TYPE_STRING:
1311b713c91eSToomas Soome 		nvp_value = (nv_string_t *)&nvp_data->nv_data[0];
1312b713c91eSToomas Soome 		if ((value = nvstring_get(nvp_value)) == NULL) {
1313b713c91eSToomas Soome 			rv = ENOMEM;
1314b713c91eSToomas Soome 			break;
1315b713c91eSToomas Soome 		}
1316b713c91eSToomas Soome 		break;
1317b713c91eSToomas Soome 
1318b713c91eSToomas Soome 	default:
1319b713c91eSToomas Soome 		rv = EINVAL;
1320b713c91eSToomas Soome 		break;
1321b713c91eSToomas Soome 	}
1322b713c91eSToomas Soome 
1323b713c91eSToomas Soome 	if (value != NULL) {
1324b713c91eSToomas Soome 		rv = env_setenv(name, EV_VOLATILE | EV_NOHOOK, value,
1325b713c91eSToomas Soome 		    zfs_nvstore_sethook, zfs_nvstore_unsethook);
1326b713c91eSToomas Soome 		free(value);
1327b713c91eSToomas Soome 	}
1328b713c91eSToomas Soome 	free(name);
1329b713c91eSToomas Soome 	return (rv);
1330b713c91eSToomas Soome }
1331b713c91eSToomas Soome 
1332b713c91eSToomas Soome static int
zfs_nvstore_iterate(void * vdev,int (* cb)(void *,void *))1333b713c91eSToomas Soome zfs_nvstore_iterate(void *vdev, int (*cb)(void *, void *))
1334b713c91eSToomas Soome {
1335b713c91eSToomas Soome 	struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
1336b713c91eSToomas Soome 	spa_t *spa;
1337b713c91eSToomas Soome 	nvlist_t *nv;
1338b713c91eSToomas Soome 	nvp_header_t *nvh;
1339b713c91eSToomas Soome 	int rv;
1340b713c91eSToomas Soome 
1341b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1342b713c91eSToomas Soome 		return (ENOTSUP);
1343b713c91eSToomas Soome 
1344b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
1345b713c91eSToomas Soome 		return (ENXIO);
1346b713c91eSToomas Soome 
1347b713c91eSToomas Soome 	if (spa->spa_bootenv == NULL)
1348b713c91eSToomas Soome 		return (ENXIO);
1349b713c91eSToomas Soome 
1350b713c91eSToomas Soome 	if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
1351b713c91eSToomas Soome 	    NULL, &nv, NULL) != 0)
1352b713c91eSToomas Soome 		return (ENOENT);
1353b713c91eSToomas Soome 
1354b713c91eSToomas Soome 	rv = 0;
1355b713c91eSToomas Soome 	nvh = NULL;
1356b713c91eSToomas Soome 	while ((nvh = nvlist_next_nvpair(nv, nvh)) != NULL) {
1357b713c91eSToomas Soome 		rv = cb(vdev, nvh);
1358b713c91eSToomas Soome 		if (rv != 0)
1359b713c91eSToomas Soome 			break;
1360b713c91eSToomas Soome 	}
1361b713c91eSToomas Soome 	return (rv);
1362b713c91eSToomas Soome }
1363b713c91eSToomas Soome 
1364b713c91eSToomas Soome nvs_callbacks_t nvstore_zfs_cb = {
1365b713c91eSToomas Soome 	.nvs_getter = zfs_nvstore_getter,
1366b713c91eSToomas Soome 	.nvs_setter = zfs_nvstore_setter,
1367b713c91eSToomas Soome 	.nvs_setter_str = zfs_nvstore_setter_str,
1368b713c91eSToomas Soome 	.nvs_unset = zfs_nvstore_unset,
1369b713c91eSToomas Soome 	.nvs_print = zfs_nvstore_print,
1370b713c91eSToomas Soome 	.nvs_iterate = zfs_nvstore_iterate
1371b713c91eSToomas Soome };
1372b713c91eSToomas Soome 
1373b713c91eSToomas Soome int
zfs_attach_nvstore(void * vdev)1374b713c91eSToomas Soome zfs_attach_nvstore(void *vdev)
1375b713c91eSToomas Soome {
1376b713c91eSToomas Soome 	struct zfs_devdesc *dev = vdev;
1377b713c91eSToomas Soome 	spa_t *spa;
1378b713c91eSToomas Soome 	uint64_t version;
1379b713c91eSToomas Soome 	int rv;
1380b713c91eSToomas Soome 
1381b713c91eSToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1382b713c91eSToomas Soome 		return (ENOTSUP);
1383b713c91eSToomas Soome 
1384b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
1385b713c91eSToomas Soome 		return (ENXIO);
1386b713c91eSToomas Soome 
1387b713c91eSToomas Soome 	rv = nvlist_find(spa->spa_bootenv, BOOTENV_VERSION, DATA_TYPE_UINT64,
1388b713c91eSToomas Soome 	    NULL, &version, NULL);
1389b713c91eSToomas Soome 
1390b713c91eSToomas Soome 	if (rv != 0 || version != VB_NVLIST) {
1391b713c91eSToomas Soome 		return (ENXIO);
1392b713c91eSToomas Soome 	}
1393b713c91eSToomas Soome 
1394b713c91eSToomas Soome 	dev = malloc(sizeof (*dev));
1395b713c91eSToomas Soome 	if (dev == NULL)
1396b713c91eSToomas Soome 		return (ENOMEM);
1397b713c91eSToomas Soome 	memcpy(dev, vdev, sizeof (*dev));
1398b713c91eSToomas Soome 
1399b713c91eSToomas Soome 	rv = nvstore_init(spa->spa_name, &nvstore_zfs_cb, dev);
1400b713c91eSToomas Soome 	if (rv != 0)
1401b713c91eSToomas Soome 		free(dev);
1402b713c91eSToomas Soome 	else
1403b713c91eSToomas Soome 		rv = zfs_nvstore_iterate(dev, zfs_nvstore_setenv);
1404b713c91eSToomas Soome 	return (rv);
1405b713c91eSToomas Soome }
1406b713c91eSToomas Soome 
1407199767f8SToomas Soome int
zfs_probe_dev(const char * devname,uint64_t * pool_guid)1408199767f8SToomas Soome zfs_probe_dev(const char *devname, uint64_t *pool_guid)
1409199767f8SToomas Soome {
1410781f142dSToomas Soome 	struct disk_devdesc *dev;
1411199767f8SToomas Soome 	struct ptable *table;
1412199767f8SToomas Soome 	struct zfs_probe_args pa;
1413edb35047SToomas Soome 	uint64_t mediasz;
1414199767f8SToomas Soome 	int ret;
1415199767f8SToomas Soome 
1416dbacaf56SToomas Soome 	if (pool_guid)
1417dbacaf56SToomas Soome 		*pool_guid = 0;
1418b713c91eSToomas Soome 	pa.fd = open(devname, O_RDWR);
1419199767f8SToomas Soome 	if (pa.fd == -1)
1420199767f8SToomas Soome 		return (ENXIO);
1421781f142dSToomas Soome 	/*
1422781f142dSToomas Soome 	 * We will not probe the whole disk, we can not boot from such
1423781f142dSToomas Soome 	 * disks and some systems will misreport the disk sizes and will
1424781f142dSToomas Soome 	 * hang while accessing the disk.
1425781f142dSToomas Soome 	 */
1426781f142dSToomas Soome 	if (archsw.arch_getdev((void **)&dev, devname, NULL) == 0) {
1427781f142dSToomas Soome 		int partition = dev->d_partition;
1428781f142dSToomas Soome 		int slice = dev->d_slice;
1429781f142dSToomas Soome 
1430781f142dSToomas Soome 		free(dev);
14319a34674dSToomas Soome 		if (partition != D_PARTNONE && slice != D_SLICENONE) {
1432781f142dSToomas Soome 			ret = zfs_probe(pa.fd, pool_guid);
1433781f142dSToomas Soome 			if (ret == 0)
1434781f142dSToomas Soome 				return (0);
1435781f142dSToomas Soome 		}
1436781f142dSToomas Soome 	}
1437dbacaf56SToomas Soome 
1438199767f8SToomas Soome 	/* Probe each partition */
1439199767f8SToomas Soome 	ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
1440199767f8SToomas Soome 	if (ret == 0)
1441199767f8SToomas Soome 		ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz);
1442199767f8SToomas Soome 	if (ret == 0) {
1443199767f8SToomas Soome 		pa.devname = devname;
1444199767f8SToomas Soome 		pa.pool_guid = pool_guid;
1445199767f8SToomas Soome 		table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz,
1446199767f8SToomas Soome 		    zfs_diskread);
1447199767f8SToomas Soome 		if (table != NULL) {
1448199767f8SToomas Soome 			ptable_iterate(table, &pa, zfs_probe_partition);
1449199767f8SToomas Soome 			ptable_close(table);
1450199767f8SToomas Soome 		}
1451199767f8SToomas Soome 	}
1452199767f8SToomas Soome 	close(pa.fd);
1453dbacaf56SToomas Soome 	if (pool_guid && *pool_guid == 0)
1454dbacaf56SToomas Soome 		ret = ENXIO;
1455199767f8SToomas Soome 	return (ret);
1456199767f8SToomas Soome }
1457199767f8SToomas Soome 
1458199767f8SToomas Soome /*
1459199767f8SToomas Soome  * Print information about ZFS pools
1460199767f8SToomas Soome  */
1461199767f8SToomas Soome static int
zfs_dev_print(int verbose)1462199767f8SToomas Soome zfs_dev_print(int verbose)
1463199767f8SToomas Soome {
1464199767f8SToomas Soome 	spa_t *spa;
1465199767f8SToomas Soome 	char line[80];
1466199767f8SToomas Soome 	int ret = 0;
1467199767f8SToomas Soome 
1468502b33a5SToomas Soome 	if (STAILQ_EMPTY(&zfs_pools))
1469502b33a5SToomas Soome 		return (0);
1470502b33a5SToomas Soome 
1471502b33a5SToomas Soome 	printf("%s devices:", zfs_dev.dv_name);
1472502b33a5SToomas Soome 	if ((ret = pager_output("\n")) != 0)
1473502b33a5SToomas Soome 		return (ret);
1474502b33a5SToomas Soome 
1475199767f8SToomas Soome 	if (verbose) {
1476199767f8SToomas Soome 		return (spa_all_status());
1477199767f8SToomas Soome 	}
1478199767f8SToomas Soome 	STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
1479b713c91eSToomas Soome 		snprintf(line, sizeof (line), "    zfs:%s\n", spa->spa_name);
1480199767f8SToomas Soome 		ret = pager_output(line);
1481199767f8SToomas Soome 		if (ret != 0)
1482199767f8SToomas Soome 			break;
1483199767f8SToomas Soome 	}
1484199767f8SToomas Soome 	return (ret);
1485199767f8SToomas Soome }
1486199767f8SToomas Soome 
1487199767f8SToomas Soome /*
1488199767f8SToomas Soome  * Attempt to open the pool described by (dev) for use by (f).
1489199767f8SToomas Soome  */
1490199767f8SToomas Soome static int
zfs_dev_open(struct open_file * f,...)1491199767f8SToomas Soome zfs_dev_open(struct open_file *f, ...)
1492199767f8SToomas Soome {
1493199767f8SToomas Soome 	va_list		args;
1494199767f8SToomas Soome 	struct zfs_devdesc	*dev;
1495199767f8SToomas Soome 	struct zfsmount	*mount;
1496199767f8SToomas Soome 	spa_t		*spa;
1497199767f8SToomas Soome 	int		rv;
1498199767f8SToomas Soome 
1499199767f8SToomas Soome 	va_start(args, f);
1500199767f8SToomas Soome 	dev = va_arg(args, struct zfs_devdesc *);
1501199767f8SToomas Soome 	va_end(args);
1502199767f8SToomas Soome 
1503b713c91eSToomas Soome 	if ((spa = spa_find_by_dev(dev)) == NULL)
1504199767f8SToomas Soome 		return (ENXIO);
1505b713c91eSToomas Soome 
1506777db879SToomas Soome 	mount = malloc(sizeof (*mount));
1507777db879SToomas Soome 	if (mount == NULL)
1508777db879SToomas Soome 		rv = ENOMEM;
1509777db879SToomas Soome 	else
1510777db879SToomas Soome 		rv = zfs_mount(spa, dev->root_guid, mount);
1511199767f8SToomas Soome 	if (rv != 0) {
1512199767f8SToomas Soome 		free(mount);
1513199767f8SToomas Soome 		return (rv);
1514199767f8SToomas Soome 	}
1515199767f8SToomas Soome 	if (mount->objset.os_type != DMU_OST_ZFS) {
1516199767f8SToomas Soome 		printf("Unexpected object set type %ju\n",
1517199767f8SToomas Soome 		    (uintmax_t)mount->objset.os_type);
1518199767f8SToomas Soome 		free(mount);
1519199767f8SToomas Soome 		return (EIO);
1520199767f8SToomas Soome 	}
1521199767f8SToomas Soome 	f->f_devdata = mount;
1522199767f8SToomas Soome 	free(dev);
1523199767f8SToomas Soome 	return (0);
1524199767f8SToomas Soome }
1525199767f8SToomas Soome 
1526199767f8SToomas Soome static int
zfs_dev_close(struct open_file * f)1527199767f8SToomas Soome zfs_dev_close(struct open_file *f)
1528199767f8SToomas Soome {
1529199767f8SToomas Soome 
1530199767f8SToomas Soome 	free(f->f_devdata);
1531199767f8SToomas Soome 	f->f_devdata = NULL;
1532199767f8SToomas Soome 	return (0);
1533199767f8SToomas Soome }
1534199767f8SToomas Soome 
1535199767f8SToomas Soome static int
zfs_dev_strategy(void * devdata __unused,int rw __unused,daddr_t dblk __unused,size_t size __unused,char * buf __unused,size_t * rsize __unused)15368eef2ab6SToomas Soome zfs_dev_strategy(void *devdata __unused, int rw __unused,
15378eef2ab6SToomas Soome     daddr_t dblk __unused, size_t size __unused,
15388eef2ab6SToomas Soome     char *buf __unused, size_t *rsize __unused)
1539199767f8SToomas Soome {
1540199767f8SToomas Soome 
1541199767f8SToomas Soome 	return (ENOSYS);
1542199767f8SToomas Soome }
1543199767f8SToomas Soome 
1544199767f8SToomas Soome struct devsw zfs_dev = {
1545199767f8SToomas Soome 	.dv_name = "zfs",
1546199767f8SToomas Soome 	.dv_type = DEVT_ZFS,
1547199767f8SToomas Soome 	.dv_init = zfs_dev_init,
1548199767f8SToomas Soome 	.dv_strategy = zfs_dev_strategy,
1549199767f8SToomas Soome 	.dv_open = zfs_dev_open,
1550199767f8SToomas Soome 	.dv_close = zfs_dev_close,
1551199767f8SToomas Soome 	.dv_ioctl = noioctl,
1552199767f8SToomas Soome 	.dv_print = zfs_dev_print,
1553199767f8SToomas Soome 	.dv_cleanup = NULL
1554199767f8SToomas Soome };
1555199767f8SToomas Soome 
1556199767f8SToomas Soome int
zfs_parsedev(struct zfs_devdesc * dev,const char * devspec,const char ** path)1557199767f8SToomas Soome zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path)
1558199767f8SToomas Soome {
1559199767f8SToomas Soome 	static char	rootname[ZFS_MAXNAMELEN];
1560199767f8SToomas Soome 	static char	poolname[ZFS_MAXNAMELEN];
1561199767f8SToomas Soome 	spa_t		*spa;
1562199767f8SToomas Soome 	const char	*end;
1563199767f8SToomas Soome 	const char	*np;
1564199767f8SToomas Soome 	const char	*sep;
1565199767f8SToomas Soome 	int		rv;
1566199767f8SToomas Soome 
1567199767f8SToomas Soome 	np = devspec;
1568199767f8SToomas Soome 	if (*np != ':')
1569199767f8SToomas Soome 		return (EINVAL);
1570199767f8SToomas Soome 	np++;
15716f01cc52SToomas Soome 	end = strrchr(np, ':');
1572199767f8SToomas Soome 	if (end == NULL)
1573199767f8SToomas Soome 		return (EINVAL);
1574199767f8SToomas Soome 	sep = strchr(np, '/');
1575199767f8SToomas Soome 	if (sep == NULL || sep >= end)
1576199767f8SToomas Soome 		sep = end;
1577199767f8SToomas Soome 	memcpy(poolname, np, sep - np);
1578199767f8SToomas Soome 	poolname[sep - np] = '\0';
1579199767f8SToomas Soome 	if (sep < end) {
1580199767f8SToomas Soome 		sep++;
1581199767f8SToomas Soome 		memcpy(rootname, sep, end - sep);
1582199767f8SToomas Soome 		rootname[end - sep] = '\0';
1583199767f8SToomas Soome 	}
1584199767f8SToomas Soome 	else
1585199767f8SToomas Soome 		rootname[0] = '\0';
1586199767f8SToomas Soome 
1587199767f8SToomas Soome 	spa = spa_find_by_name(poolname);
1588199767f8SToomas Soome 	if (!spa)
1589199767f8SToomas Soome 		return (ENXIO);
1590199767f8SToomas Soome 	dev->pool_guid = spa->spa_guid;
1591199767f8SToomas Soome 	rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid);
1592199767f8SToomas Soome 	if (rv != 0)
1593199767f8SToomas Soome 		return (rv);
1594199767f8SToomas Soome 	if (path != NULL)
1595199767f8SToomas Soome 		*path = (*end == '\0') ? end : end + 1;
159676b35943SToomas Soome 	dev->dd.d_dev = &zfs_dev;
1597199767f8SToomas Soome 	return (0);
1598199767f8SToomas Soome }
1599199767f8SToomas Soome 
1600199767f8SToomas Soome char *
zfs_bootfs(void * zdev)1601199767f8SToomas Soome zfs_bootfs(void *zdev)
1602199767f8SToomas Soome {
1603199767f8SToomas Soome 	static char		rootname[ZFS_MAXNAMELEN];
1604199767f8SToomas Soome 	static char		buf[2 * ZFS_MAXNAMELEN];
1605199767f8SToomas Soome 	struct zfs_devdesc	*dev = (struct zfs_devdesc *)zdev;
1606199767f8SToomas Soome 	uint64_t		objnum;
1607199767f8SToomas Soome 	spa_t			*spa;
1608199767f8SToomas Soome 	int			n;
1609199767f8SToomas Soome 
1610199767f8SToomas Soome 	buf[0] = '\0';
1611c142ce19SToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1612199767f8SToomas Soome 		return (buf);
1613199767f8SToomas Soome 
1614199767f8SToomas Soome 	spa = spa_find_by_guid(dev->pool_guid);
1615199767f8SToomas Soome 	if (spa == NULL) {
1616199767f8SToomas Soome 		printf("ZFS: can't find pool by guid\n");
1617199767f8SToomas Soome 		return (buf);
1618199767f8SToomas Soome 	}
1619199767f8SToomas Soome 	if (zfs_rlookup(spa, dev->root_guid, rootname)) {
1620199767f8SToomas Soome 		printf("ZFS: can't find filesystem by guid\n");
1621199767f8SToomas Soome 		return (buf);
1622199767f8SToomas Soome 	}
1623199767f8SToomas Soome 	if (zfs_lookup_dataset(spa, rootname, &objnum)) {
1624199767f8SToomas Soome 		printf("ZFS: can't find filesystem by name\n");
1625199767f8SToomas Soome 		return (buf);
1626199767f8SToomas Soome 	}
1627199767f8SToomas Soome 
16286369122cSToomas Soome 	/* Set the environment. */
1629f6dea603SToomas Soome 	snprintf(buf, sizeof (buf), "%" PRIu64, dev->pool_guid);
1630f6dea603SToomas Soome 	setenv("zfs-bootpool", buf, 1);
1631f6dea603SToomas Soome 	snprintf(buf, sizeof (buf), "%" PRIu64, spa->spa_boot_vdev->v_guid);
1632f6dea603SToomas Soome 	setenv("zfs-bootvdev", buf, 1);
1633f6dea603SToomas Soome 	snprintf(buf, sizeof (buf), "%s/%" PRIu64, spa->spa_name, objnum);
16346369122cSToomas Soome 	setenv("zfs-bootfs", buf, 1);
163545137058SToomas Soome 	if (spa->spa_boot_vdev->v_phys_path != NULL)
163645137058SToomas Soome 		setenv("bootpath", spa->spa_boot_vdev->v_phys_path, 1);
163745137058SToomas Soome 	if (spa->spa_boot_vdev->v_devid != NULL)
163845137058SToomas Soome 		setenv("diskdevid", spa->spa_boot_vdev->v_devid, 1);
16396369122cSToomas Soome 
16406369122cSToomas Soome 	/*
16416369122cSToomas Soome 	 * Build the command line string. Once our kernel will read
16426369122cSToomas Soome 	 * the environment and we can stop caring about old kernels,
16436369122cSToomas Soome 	 * we can remove this part.
16446369122cSToomas Soome 	 */
1645fec66293SToomas Soome 	snprintf(buf, sizeof (buf), "zfs-bootfs=%s/%" PRIu64, spa->spa_name,
1646f6dea603SToomas Soome 	    objnum);
1647199767f8SToomas Soome 	n = strlen(buf);
164845137058SToomas Soome 	if (spa->spa_boot_vdev->v_phys_path != NULL) {
16496369122cSToomas Soome 		snprintf(buf+n, sizeof (buf) - n, ",bootpath=\"%s\"",
165045137058SToomas Soome 		    spa->spa_boot_vdev->v_phys_path);
1651199767f8SToomas Soome 		n = strlen(buf);
1652199767f8SToomas Soome 	}
165345137058SToomas Soome 	if (spa->spa_boot_vdev->v_devid != NULL) {
16546369122cSToomas Soome 		snprintf(buf+n, sizeof (buf) - n, ",diskdevid=\"%s\"",
165545137058SToomas Soome 		    spa->spa_boot_vdev->v_devid);
1656199767f8SToomas Soome 	}
1657199767f8SToomas Soome 	return (buf);
1658199767f8SToomas Soome }
1659199767f8SToomas Soome 
1660199767f8SToomas Soome char *
zfs_fmtdev(void * vdev)1661199767f8SToomas Soome zfs_fmtdev(void *vdev)
1662199767f8SToomas Soome {
1663199767f8SToomas Soome 	static char		rootname[ZFS_MAXNAMELEN];
1664199767f8SToomas Soome 	static char		buf[2 * ZFS_MAXNAMELEN + 8];
1665199767f8SToomas Soome 	struct zfs_devdesc	*dev = (struct zfs_devdesc *)vdev;
1666199767f8SToomas Soome 	spa_t			*spa;
1667199767f8SToomas Soome 
1668199767f8SToomas Soome 	buf[0] = '\0';
1669c142ce19SToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
1670199767f8SToomas Soome 		return (buf);
1671199767f8SToomas Soome 
1672c2e9ac5cSToomas Soome 	/* Do we have any pools? */
1673c2e9ac5cSToomas Soome 	spa = STAILQ_FIRST(&zfs_pools);
1674c2e9ac5cSToomas Soome 	if (spa == NULL)
1675c2e9ac5cSToomas Soome 		return (buf);
1676c2e9ac5cSToomas Soome 
1677c2e9ac5cSToomas Soome 	if (dev->pool_guid == 0)
1678199767f8SToomas Soome 		dev->pool_guid = spa->spa_guid;
1679c2e9ac5cSToomas Soome 	else
1680199767f8SToomas Soome 		spa = spa_find_by_guid(dev->pool_guid);
1681c2e9ac5cSToomas Soome 
1682199767f8SToomas Soome 	if (spa == NULL) {
1683199767f8SToomas Soome 		printf("ZFS: can't find pool by guid\n");
1684199767f8SToomas Soome 		return (buf);
1685199767f8SToomas Soome 	}
1686199767f8SToomas Soome 	if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) {
1687199767f8SToomas Soome 		printf("ZFS: can't find root filesystem\n");
1688199767f8SToomas Soome 		return (buf);
1689199767f8SToomas Soome 	}
1690199767f8SToomas Soome 	if (zfs_rlookup(spa, dev->root_guid, rootname)) {
1691199767f8SToomas Soome 		printf("ZFS: can't find filesystem by guid\n");
1692199767f8SToomas Soome 		return (buf);
1693199767f8SToomas Soome 	}
1694199767f8SToomas Soome 
1695199767f8SToomas Soome 	if (rootname[0] == '\0')
1696b713c91eSToomas Soome 		snprintf(buf, sizeof (buf), "%s:%s:", dev->dd.d_dev->dv_name,
1697b713c91eSToomas Soome 		    spa->spa_name);
1698199767f8SToomas Soome 	else
1699b713c91eSToomas Soome 		snprintf(buf, sizeof (buf), "%s:%s/%s:", dev->dd.d_dev->dv_name,
1700b713c91eSToomas Soome 		    spa->spa_name, rootname);
1701199767f8SToomas Soome 	return (buf);
1702199767f8SToomas Soome }
1703199767f8SToomas Soome 
1704199767f8SToomas Soome int
zfs_list(const char * name)1705199767f8SToomas Soome zfs_list(const char *name)
1706199767f8SToomas Soome {
1707199767f8SToomas Soome 	static char	poolname[ZFS_MAXNAMELEN];
1708199767f8SToomas Soome 	uint64_t	objid;
1709199767f8SToomas Soome 	spa_t		*spa;
1710199767f8SToomas Soome 	const char	*dsname;
1711199767f8SToomas Soome 	int		len;
1712199767f8SToomas Soome 	int		rv;
1713199767f8SToomas Soome 
1714199767f8SToomas Soome 	len = strlen(name);
1715199767f8SToomas Soome 	dsname = strchr(name, '/');
1716199767f8SToomas Soome 	if (dsname != NULL) {
1717199767f8SToomas Soome 		len = dsname - name;
1718199767f8SToomas Soome 		dsname++;
1719199767f8SToomas Soome 	} else
1720199767f8SToomas Soome 		dsname = "";
1721199767f8SToomas Soome 	memcpy(poolname, name, len);
1722199767f8SToomas Soome 	poolname[len] = '\0';
1723199767f8SToomas Soome 
1724199767f8SToomas Soome 	spa = spa_find_by_name(poolname);
1725199767f8SToomas Soome 	if (!spa)
1726199767f8SToomas Soome 		return (ENXIO);
1727199767f8SToomas Soome 	rv = zfs_lookup_dataset(spa, dsname, &objid);
1728199767f8SToomas Soome 	if (rv != 0)
1729199767f8SToomas Soome 		return (rv);
1730199767f8SToomas Soome 
1731199767f8SToomas Soome 	return (zfs_list_dataset(spa, objid));
1732199767f8SToomas Soome }
1733