xref: /illumos-gate/usr/src/boot/lib/libstand/zfs/zfs.c (revision 553cfb3f)
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) {
645*553cfb3fSToomas 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);
988