1cbe94e1Toomas Soome/*
2199767fToomas Soome * Copyright (c) 2007 Doug Rabson
3199767fToomas Soome * All rights reserved.
4199767fToomas Soome *
5199767fToomas Soome * Redistribution and use in source and binary forms, with or without
6199767fToomas Soome * modification, are permitted provided that the following conditions
7199767fToomas Soome * are met:
8199767fToomas Soome * 1. Redistributions of source code must retain the above copyright
9199767fToomas Soome *    notice, this list of conditions and the following disclaimer.
10199767fToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
11199767fToomas Soome *    notice, this list of conditions and the following disclaimer in the
12199767fToomas Soome *    documentation and/or other materials provided with the distribution.
13199767fToomas Soome *
24199767fToomas Soome * SUCH DAMAGE.
25199767fToomas Soome */
26199767fToomas Soome
27199767fToomas Soome#include <sys/cdefs.h>
28199767fToomas Soome
29199767fToomas Soome/*
30199767fToomas Soome *	Stand-alone file reading package.
31199767fToomas Soome */
32199767fToomas Soome
33781f142Toomas Soome#include <stand.h>
34199767fToomas Soome#include <sys/disk.h>
35199767fToomas Soome#include <sys/param.h>
36199767fToomas Soome#include <sys/time.h>
37199767fToomas Soome#include <sys/queue.h>
38781f142Toomas Soome#include <disk.h>
39199767fToomas Soome#include <part.h>
40199767fToomas Soome#include <stddef.h>
41199767fToomas Soome#include <stdarg.h>
42199767fToomas Soome#include <string.h>
43199767fToomas Soome#include <bootstrap.h>
44f6dea60Toomas Soome#include <inttypes.h>
45199767fToomas Soome
46199767fToomas Soome#include "libzfs.h"
47199767fToomas Soome
48199767fToomas Soome#include "zfsimpl.c"
49199767fToomas Soome
50199767fToomas Soome/* Define the range of indexes to be populated with ZFS Boot Environments */
51199767fToomas Soome#define		ZFS_BE_FIRST	4
52199767fToomas Soome#define		ZFS_BE_LAST	8
53199767fToomas Soome
54fec6629Toomas Soomestatic int	zfs_open(const char *, struct open_file *);
55fec6629Toomas Soomestatic int	zfs_close(struct open_file *);
56fec6629Toomas Soomestatic int	zfs_read(struct open_file *, void *, size_t, size_t *);
57fec6629Toomas Soomestatic off_t	zfs_seek(struct open_file *, off_t, int);
58fec6629Toomas Soomestatic int	zfs_stat(struct open_file *, struct stat *);
59fec6629Toomas Soomestatic int	zfs_readdir(struct open_file *, struct dirent *);
60199767fToomas Soome
61199767fToomas Soomestruct devsw zfs_dev;
62199767fToomas Soome
63199767fToomas Soomestruct fs_ops zfs_fsops = {
64199767fToomas Soome	"zfs",
65199767fToomas Soome	zfs_open,
66199767fToomas Soome	zfs_close,
67199767fToomas Soome	zfs_read,
68cf837edToomas Soome	null_write,
69199767fToomas Soome	zfs_seek,
70199767fToomas Soome	zfs_stat,
71199767fToomas Soome	zfs_readdir
72199767fToomas Soome};
73199767fToomas Soome
74199767fToomas Soome/*
75199767fToomas Soome * In-core open file.
76199767fToomas Soome */
77199767fToomas Soomestruct file {
78199767fToomas Soome	off_t		f_seekp;	/* seek pointer */
79199767fToomas Soome	dnode_phys_t	f_dnode;
80199767fToomas Soome	uint64_t	f_zap_type;	/* zap type for readdir */
81199767fToomas Soome	uint64_t	f_num_leafs;	/* number of fzap leaf blocks */
82199767fToomas Soome	zap_leaf_phys_t	*f_zap_leaf;	/* zap leaf buffer */
83199767fToomas Soome};
84199767fToomas Soome
85fec6629Toomas SoomeSLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head =
86fec6629Toomas Soome    SLIST_HEAD_INITIALIZER(zfs_be_head);
87199767fToomas Soomestruct zfs_be_list *zfs_be_headp;
88199767fToomas Soomestruct zfs_be_entry {
89199767fToomas Soome	const char *name;
90199767fToomas Soome	SLIST_ENTRY(zfs_be_entry) entries;
91199767fToomas Soome} *zfs_be, *zfs_be_tmp;
92199767fToomas Soome
93199767fToomas Soome/*
94199767fToomas Soome * Open a file.
95199767fToomas Soome */
96199767fToomas Soomestatic int
97199767fToomas Soomezfs_open(const char *upath, struct open_file *f)
98199767fToomas Soome{
99199767fToomas Soome	struct zfsmount *mount = (struct zfsmount *)f->f_devdata;
100199767fToomas Soome	struct file *fp;
101199767fToomas Soome	int rc;
102199767fToomas Soome
103199767fToomas Soome	if (f->f_dev != &zfs_dev)
104199767fToomas Soome		return (EINVAL);
105199767fToomas Soome
106199767fToomas Soome	/* allocate file system specific data structure */
107777db87Toomas Soome	fp = calloc(1, sizeof (struct file));
108777db87Toomas Soome	if (fp == NULL)
109777db87Toomas Soome		return (ENOMEM);
110777db87Toomas Soome	f->f_fsdata = fp;
111199767fToomas Soome
112199767fToomas Soome	rc = zfs_lookup(mount, upath, &fp->f_dnode);
113199767fToomas Soome	fp->f_seekp = 0;
114199767fToomas Soome	if (rc) {
115199767fToomas Soome		f->f_fsdata = NULL;
116199767fToomas Soome		free(fp);
117199767fToomas Soome	}
118199767fToomas Soome	return (rc);
119199767fToomas Soome}
120199767fToomas Soome
121199767fToomas Soomestatic int
122199767fToomas Soomezfs_close(struct open_file *f)
123199767fToomas Soome{
124199767fToomas Soome	struct file *fp = (struct file *)f->f_fsdata;
125199767fToomas Soome
126199767fToomas Soome	dnode_cache_obj = 0;
127777db87Toomas Soome	f->f_fsdata = NULL;
128199767fToomas Soome
129199767fToomas Soome	free(fp);
130199767fToomas Soome	return (0);
131199767fToomas Soome}
132199767fToomas Soome
133199767fToomas Soome/*
134199767fToomas Soome * Copy a portion of a file into kernel memory.
135199767fToomas Soome * Cross block boundaries when necessary.
136199767fToomas Soome */
137199767fToomas Soomestatic int
138fec6629Toomas Soomezfs_read(struct open_file *f, void *start, size_t size, size_t *resid)
139199767fToomas Soome{
140199767fToomas Soome	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
141199767fToomas Soome	struct file *fp = (struct file *)f->f_fsdata;
142199767fToomas Soome	struct stat sb;
143199767fToomas Soome	size_t n;
144199767fToomas Soome	int rc;
145199767fToomas Soome
146199767fToomas Soome	rc = zfs_stat(f, &sb);
147199767fToomas Soome	if (rc)
148199767fToomas Soome		return (rc);
149199767fToomas Soome	n = size;
150199767fToomas Soome	if (fp->f_seekp + n > sb.st_size)
151199767fToomas Soome		n = sb.st_size - fp->f_seekp;
152199767fToomas Soome
153199767fToomas Soome	rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n);
154199767fToomas Soome	if (rc)
155199767fToomas Soome		return (rc);
156199767fToomas Soome
157199767fToomas Soome	fp->f_seekp += n;
158199767fToomas Soome	if (resid)
159199767fToomas Soome		*resid = size - n;
160199767fToomas Soome
161199767fToomas Soome	return (0);
162199767fToomas Soome}
163199767fToomas Soome
164199767fToomas Soomestatic off_t
165199767fToomas Soomezfs_seek(struct open_file *f, off_t offset, int where)
166199767fToomas Soome{
167199767fToomas Soome	struct file *fp = (struct file *)f->f_fsdata;
168fec6629Toomas Soome	struct stat sb;
169fec6629Toomas Soome	int error;
170fec6629Toomas Soome
171199767fToomas Soome
172199767fToomas Soome	switch (where) {
173199767fToomas Soome	case SEEK_SET:
174199767fToomas Soome		fp->f_seekp = offset;
175199767fToomas Soome		break;
176199767fToomas Soome	case SEEK_CUR:
177199767fToomas Soome		fp->f_seekp += offset;
178199767fToomas Soome		break;
179199767fToomas Soome	case SEEK_END:
180199767fToomas Soome		error = zfs_stat(f, &sb);
181199767fToomas Soome		if (error != 0) {
182199767fToomas Soome			errno = error;
183199767fToomas Soome			return (-1);
184199767fToomas Soome		}
185199767fToomas Soome		fp->f_seekp = sb.st_size - offset;
186199767fToomas Soome		break;
187199767fToomas Soome	default:
188199767fToomas Soome		errno = EINVAL;
189199767fToomas Soome		return (-1);
190199767fToomas Soome	}
191199767fToomas Soome	return (fp->f_seekp);
192199767fToomas Soome}
193199767fToomas Soome
194199767fToomas Soomestatic int
195199767fToomas Soomezfs_stat(struct open_file *f, struct stat *sb)
196199767fToomas Soome{
197199767fToomas Soome	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
198199767fToomas Soome	struct file *fp = (struct file *)f->f_fsdata;
199199767fToomas Soome
200199767fToomas Soome	return (zfs_dnode_stat(spa, &fp->f_dnode, sb));
201199767fToomas Soome}
202199767fToomas Soome
203199767fToomas Soomestatic int
204199767fToomas Soomezfs_readdir(struct open_file *f, struct dirent *d)
205199767fToomas Soome{
206199767fToomas Soome	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
207199767fToomas Soome	struct file *fp = (struct file *)f->f_fsdata;
208199767fToomas Soome	mzap_ent_phys_t mze;
209199767fToomas Soome	struct stat sb;
210199767fToomas Soome	size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT;
211199767fToomas Soome	int rc;
212199767fToomas Soome
213199767fToomas Soome	rc = zfs_stat(f, &sb);
214199767fToomas Soome	if (rc)
215199767fToomas Soome		return (rc);
216199767fToomas Soome	if (!S_ISDIR(sb.st_mode))
217199767fToomas Soome		return (ENOTDIR);
218199767fToomas Soome
219199767fToomas Soome	/*
220199767fToomas Soome	 * If this is the first read, get the zap type.
221199767fToomas Soome	 */
222199767fToomas Soome	if (fp->f_seekp == 0) {
223fec6629Toomas Soome		rc = dnode_read(spa, &fp->f_dnode, 0, &fp->f_zap_type,
224fec6629Toomas Soome		    sizeof (fp->f_zap_type));
225199767fToomas Soome		if (rc)
226199767fToomas Soome			return (rc);
227199767fToomas Soome
228199767fToomas Soome		if (fp->f_zap_type == ZBT_MICRO) {
229199767fToomas Soome			fp->f_seekp = offsetof(mzap_phys_t, mz_chunk);
230199767fToomas Soome		} else {
231199767fToomas Soome			rc = dnode_read(spa, &fp->f_dnode,
232fec6629Toomas Soome			    offsetof(zap_phys_t, zap_num_leafs),
233fec6629Toomas Soome			    &fp->f_num_leafs, sizeof (fp->f_num_leafs));
234199767fToomas Soome			if (rc)
235199767fToomas Soome				return (rc);
236199767fToomas Soome
237199767fToomas Soome			fp->f_seekp = bsize;
238777db87Toomas Soome			fp->f_zap_leaf = malloc(bsize);
239777db87Toomas Soome			if (fp->f_zap_leaf == NULL)
240777db87Toomas Soome				return (ENOMEM);
241fec6629Toomas Soome			rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp,
242fec6629Toomas Soome			    fp->f_zap_leaf, bsize);
243199767fToomas Soome			if (rc)
244199767fToomas Soome				return (rc);
245199767fToomas Soome		}
246199767fToomas Soome	}
247199767fToomas Soome
248199767fToomas Soome	if (fp->f_zap_type == ZBT_MICRO) {
249199767fToomas Soome	mzap_next:
250199767fToomas Soome		if (fp->f_seekp >= bsize)
251199767fToomas Soome			return (ENOENT);
252199767fToomas Soome
253fec6629Toomas Soome		rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, &mze,
254fec6629Toomas Soome		    sizeof (mze));
255199767fToomas Soome		if (rc)
256199767fToomas Soome			return (rc);
257fec6629Toomas Soome		fp->f_seekp += sizeof (mze);
258199767fToomas Soome
259199767fToomas Soome		if (!mze.mze_name[0])
260199767fToomas Soome			goto mzap_next;
261199767fToomas Soome
262199767fToomas Soome		d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value);
263199767fToomas Soome		d->d_type = ZFS_DIRENT_TYPE(mze.mze_value);
264199767fToomas Soome		strcpy(d->d_name, mze.mze_name);
265199767fToomas Soome		d->d_namlen = strlen(d->d_name);
266199767fToomas Soome		return (0);
267199767fToomas Soome	} else {
268199767fToomas Soome		zap_leaf_t zl;
269199767fToomas Soome		zap_leaf_chunk_t *zc, *nc;
270199767fToomas Soome		int chunk;
271199767fToomas Soome		size_t namelen;
272199767fToomas Soome		char *p;
273199767fToomas Soome		uint64_t value;
274199767fToomas Soome
275199767fToomas Soome		/*
276199767fToomas Soome		 * Initialise this so we can use the ZAP size
277199767fToomas Soome		 * calculating macros.
278199767fToomas Soome		 */
279199767fToomas Soome		zl.l_bs = ilog2(bsize);
280199767fToomas Soome		zl.l_phys = fp->f_zap_leaf;
281199767fToomas Soome
282199767fToomas Soome		/*
283199767fToomas Soome		 * Figure out which chunk we are currently looking at
284199767fToomas Soome		 * and consider seeking to the next leaf. We use the
285199767fToomas Soome		 * low bits of f_seekp as a simple chunk index.
286199767fToomas Soome		 */
287199767fToomas Soome	fzap_next:
288199767fToomas Soome		chunk = fp->f_seekp & (bsize - 1);
289199767fToomas Soome		if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) {
290199767fToomas Soome			fp->f_seekp = (fp->f_seekp & ~(bsize - 1)) + bsize;
291199767fToomas Soome			chunk = 0;
292199767fToomas Soome
293199767fToomas Soome			/*
294199767fToomas Soome			 * Check for EOF and read the new leaf.
295199767fToomas Soome			 */
296199767fToomas Soome			if (fp->f_seekp >= bsize * fp->f_num_leafs)
297199767fToomas Soome				return (ENOENT);
298199767fToomas Soome
299fec6629Toomas Soome			rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp,
300fec6629Toomas Soome			    fp->f_zap_leaf, bsize);
301199767fToomas Soome			if (rc)
302199767fToomas Soome				return (rc);
303199767fToomas Soome		}
304199767fToomas Soome
305199767fToomas Soome		zc = &ZAP_LEAF_CHUNK(&zl, chunk);
306199767fToomas Soome		fp->f_seekp++;
307199767fToomas Soome		if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
308199767fToomas Soome			goto fzap_next;
309199767fToomas Soome
310199767fToomas Soome		namelen = zc->l_entry.le_name_numints;
311fec6629Toomas Soome		if (namelen > sizeof (d->d_name))
312fec6629Toomas Soome			namelen = sizeof (d->d_name);
313199767fToomas Soome
314199767fToomas Soome		/*
315199767fToomas Soome		 * Paste the name back together.
316199767fToomas Soome		 */
317199767fToomas Soome		nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
318199767fToomas Soome		p = d->d_name;
319199767fToomas Soome		while (namelen > 0) {
320199767fToomas Soome			int len;
321199767fToomas Soome			len = namelen;
322199767fToomas Soome			if (len > ZAP_LEAF_ARRAY_BYTES)
323199767fToomas Soome				len = ZAP_LEAF_ARRAY_BYTES;
324199767fToomas Soome			memcpy(p, nc->l_array.la_array, len);
325199767fToomas Soome			p += len;
326199767fToomas Soome			namelen -= len;
327199767fToomas Soome			nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
328199767fToomas Soome		}
329fec6629Toomas Soome		d->d_name[sizeof (d->d_name) - 1] = 0;
330199767fToomas Soome
331199767fToomas Soome		/*
332199767fToomas Soome		 * Assume the first eight bytes of the value are
333199767fToomas Soome		 * a uint64_t.
334199767fToomas Soome		 */
335199767fToomas Soome		value = fzap_leaf_value(&zl, zc);
336199767fToomas Soome
337199767fToomas Soome		d->d_fileno = ZFS_DIRENT_OBJ(value);
338199767fToomas Soome		d->d_type = ZFS_DIRENT_TYPE(value);
339199767fToomas Soome		d->d_namlen = strlen(d->d_name);
340199767fToomas Soome
341199767fToomas Soome		return (0);
342199767fToomas Soome	}
343199767fToomas Soome}
344199767fToomas Soome
345199767fToomas Soomestatic int
3468eef2abToomas Soomevdev_read(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
3478eef2abToomas Soome    size_t bytes)
348199767fToomas Soome{
349cbe94e1Toomas Soome	int fd, ret;
350cb09bd3Toomas Soome	size_t res, head, tail, total_size, full_sec_size;
351cb09bd3Toomas Soome	unsigned secsz, do_tail_read;
352cb09bd3Toomas Soome	off_t start_sec;
353cb09bd3Toomas Soome	char *outbuf, *bouncebuf;
354199767fToomas Soome
355fec6629Toomas Soome	fd = (uintptr_t)priv;
356cb09bd3Toomas Soome	outbuf = (char *)buf;
357cbe94e1Toomas Soome	bouncebuf = NULL;
358cbe94e1Toomas Soome
359cbe94e1Toomas Soome	ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
360cbe94e1Toomas Soome	if (ret != 0)
361cbe94e1Toomas Soome		return (ret);
362cbe94e1Toomas Soome
363cb09bd3Toomas Soome	/*
364cb09bd3Toomas Soome	 * Handling reads of arbitrary offset and size - multi-sector case
365cb09bd3Toomas Soome	 * and single-sector case.
366cb09bd3Toomas Soome	 *
367cb09bd3Toomas Soome	 *                        Multi-sector Case
368cb09bd3Toomas Soome	 *                (do_tail_read = true if tail > 0)
369cb09bd3Toomas Soome	 *
370cb09bd3Toomas Soome	 *   |<----------------------total_size--------------------->|
371cb09bd3Toomas Soome	 *   |                                                       |
372cb09bd3Toomas Soome	 *   |<--head-->|<--------------bytes------------>|<--tail-->|
373cb09bd3Toomas Soome	 *   |          |                                 |          |
374cb09bd3Toomas Soome	 *   |          |       |<~full_sec_size~>|       |          |
375cb09bd3Toomas Soome	 *   +------------------+                 +------------------+
376cb09bd3Toomas Soome	 *   |          |0101010|     .  .  .     |0101011|          |
377cb09bd3Toomas Soome	 *   +------------------+                 +------------------+
378cb09bd3Toomas Soome	 *         start_sec                         start_sec + n
379cb09bd3Toomas Soome	 *
380cb09bd3Toomas Soome	 *
381cb09bd3Toomas Soome	 *                      Single-sector Case
382cb09bd3Toomas Soome	 *                    (do_tail_read = false)
383cb09bd3Toomas Soome	 *
384cb09bd3Toomas Soome	 *              |<------total_size = secsz----->|
385cb09bd3Toomas Soome	 *              |                               |
386cb09bd3Toomas Soome	 *              |<-head->|<---bytes--->|<-tail->|
387cb09bd3Toomas Soome	 *              +-------------------------------+
388cb09bd3Toomas Soome	 *              |        |0101010101010|        |
389cb09bd3Toomas Soome	 *              +-------------------------------+
390cb09bd3Toomas Soome	 *                          start_sec
391cb09bd3Toomas Soome	 */
392cb09bd3Toomas Soome	start_sec = offset / secsz;
393cb09bd3Toomas Soome	head = offset % secsz;
394cb09bd3Toomas Soome	total_size = roundup2(head + bytes, secsz);
395cb09bd3Toomas Soome	tail = total_size - (head + bytes);
396cb09bd3Toomas Soome	do_tail_read = ((tail > 0) && (head + bytes > secsz));
397cb09bd3Toomas Soome	full_sec_size = total_size;
398cb09bd3Toomas Soome	if (head > 0)
399cb09bd3Toomas Soome		full_sec_size -= secsz;
400cb09bd3Toomas Soome	if (do_tail_read)
401cb09bd3Toomas Soome		full_sec_size -= secsz;
402cb09bd3Toomas Soome
403cb09bd3Toomas Soome	/* Return of partial sector data requires a bounce buffer. */
404cee39f3Toomas Soome	if ((head > 0) || do_tail_read || bytes < secsz) {
405cb09bd3Toomas Soome		bouncebuf = malloc(secsz);
406cbe94e1Toomas Soome		if (bouncebuf == NULL) {
407cbe94e1Toomas Soome			printf("vdev_read: out of memory\n");
408cbe94e1Toomas Soome			return (ENOMEM);
409cbe94e1Toomas Soome		}
410199767fToomas Soome	}
411cbe94e1Toomas Soome
412cb09bd3Toomas Soome	if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) {
413cb09bd3Toomas Soome		ret = errno;
414cb09bd3Toomas Soome		goto error;
415cb09bd3Toomas Soome	}
416cb09bd3Toomas Soome
417cb09bd3Toomas Soome	/* Partial data return from first sector */
418cb09bd3Toomas Soome	if (head > 0) {
419cb09bd3Toomas Soome		res = read(fd, bouncebuf, secsz);
420cb09bd3Toomas Soome		if (res != secsz) {
421cb09bd3Toomas Soome			ret = EIO;
422cb09bd3Toomas Soome			goto error;
423cb09bd3Toomas Soome		}
424cb09bd3Toomas Soome		memcpy(outbuf, bouncebuf + head, min(secsz - head, bytes));
425cb09bd3Toomas Soome		outbuf += min(secsz - head, bytes);
426cb09bd3Toomas Soome	}
427cb09bd3Toomas Soome
428cb09bd3Toomas Soome	/* Full data return from read sectors */
429cb09bd3Toomas Soome	if (full_sec_size > 0) {
430cee39f3Toomas Soome		if (bytes < full_sec_size) {
431cee39f3Toomas Soome			res = read(fd, bouncebuf, secsz);
432cee39f3Toomas Soome			if (res != secsz) {
433cee39f3Toomas Soome				ret = EIO;
434cee39f3Toomas Soome				goto error;
435cee39f3Toomas Soome			}
436cee39f3Toomas Soome			memcpy(outbuf, bouncebuf, bytes);
437cee39f3Toomas Soome		} else {
438cee39f3Toomas Soome			res = read(fd, outbuf, full_sec_size);
439cee39f3Toomas Soome			if (res != full_sec_size) {
440cee39f3Toomas Soome				ret = EIO;
441cee39f3Toomas Soome				goto error;
442cee39f3Toomas Soome			}
443cee39f3Toomas Soome			outbuf += full_sec_size;
444cb09bd3Toomas Soome		}
445cb09bd3Toomas Soome	}
446cb09bd3Toomas Soome
447cb09bd3Toomas Soome	/* Partial data return from last sector */
448cb09bd3Toomas Soome	if (do_tail_read) {
449cb09bd3Toomas Soome		res = read(fd, bouncebuf, secsz);
450cb09bd3Toomas Soome		if (res != secsz) {
451cbe94e1Toomas Soome			ret = EIO;
452cbe94e1Toomas Soome			goto error;
453cbe94e1Toomas Soome		}
454cb09bd3Toomas Soome		memcpy(outbuf, bouncebuf, secsz - tail);
455cbe94e1Toomas Soome	}
456cbe94e1Toomas Soome
457cbe94e1Toomas Soome	ret = 0;
458cbe94e1Toomas Soomeerror:
459cb09bd3Toomas Soome	free(bouncebuf);
460cbe94e1Toomas Soome	return (ret);
461199767fToomas Soome}
462199767fToomas Soome
463199767fToomas Soomestatic int
464199767fToomas Soomezfs_dev_init(void)
465199767fToomas Soome{
466199767fToomas Soome	spa_t *spa;
467199767fToomas Soome	spa_t *next;
468199767fToomas Soome	spa_t *prev;
469199767fToomas Soome
470199767fToomas Soome	zfs_init();
471199767fToomas Soome	if (archsw.arch_zfs_probe == NULL)
472199767fToomas Soome		return (ENXIO);
473199767fToomas Soome	archsw.arch_zfs_probe();
474199767fToomas Soome
475199767fToomas Soome	prev = NULL;
476199767fToomas Soome	spa = STAILQ_FIRST(&zfs_pools);
477199767fToomas Soome	while (spa != NULL) {
478199767fToomas Soome		next = STAILQ_NEXT(spa, spa_link);
479199767fToomas Soome		if (zfs_spa_init(spa)) {
480199767fToomas Soome			if (prev == NULL)
481199767fToomas Soome				STAILQ_REMOVE_HEAD(&zfs_pools, spa_link);
482199767fToomas Soome			else
483199767fToomas Soome				STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link);
484199767fToomas Soome		} else
485199767fToomas Soome			prev = spa;
486199767fToomas Soome		spa = next;
487199767fToomas Soome	}
488199767fToomas Soome	return (0);
489199767fToomas Soome}
490199767fToomas Soome
491199767fToomas Soomestruct zfs_probe_args {
492199767fToomas Soome	int		fd;
493199767fToomas Soome	const char	*devname;
494199767fToomas Soome	uint64_t	*pool_guid;
495fec6629Toomas Soome	unsigned	secsz;
496199767fToomas Soome};
497199767fToomas Soome
498199767fToomas Soomestatic int
49979bea51Toomas Soomezfs_diskread(void *arg, void *buf, size_t blocks, uint64_t offset)
500199767fToomas Soome{
501199767fToomas Soome	struct zfs_probe_args *ppa;
502199767fToomas Soome
503199767fToomas Soome	ppa = (struct zfs_probe_args *)arg;
504199767fToomas Soome	return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd,
505199767fToomas Soome	    offset * ppa->secsz, buf, blocks * ppa->secsz));
506199767fToomas Soome}
507199767fToomas Soome
508199767fToomas Soomestatic int
509199767fToomas Soomezfs_probe(int fd, uint64_t *pool_guid)
510199767fToomas Soome{
511199767fToomas Soome	spa_t *spa;
512199767fToomas Soome	int ret;
513199767fToomas Soome
514cd8e64eToomas Soome	spa = NULL;
515199767fToomas Soome	ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa);
516199767fToomas Soome	if (ret == 0 && pool_guid != NULL)
517199767fToomas Soome		*pool_guid = spa->spa_guid;
518199767fToomas Soome	return (ret);
519199767fToomas Soome}
520199767fToomas Soome
521199767fToomas Soomestatic int
522199767fToomas Soomezfs_probe_partition(void *arg, const char *partname,
523199767fToomas Soome    const struct ptable_entry *part)
524199767fToomas Soome{
525199767fToomas Soome	struct zfs_probe_args *ppa, pa;
526199767fToomas Soome	struct ptable *table;
527199767fToomas Soome	char devname[32];
528199767fToomas Soome	int ret = 0;
529199767fToomas Soome
530199767fToomas Soome	/* filter out partitions *not* used by zfs */
531199767fToomas Soome	switch (part->type) {
532199767fToomas Soome	case PART_RESERVED:	/* efi reserverd */
533199767fToomas Soome	case PART_VTOC_BOOT:	/* vtoc boot area */
534199767fToomas Soome	case PART_VTOC_SWAP:
535199767fToomas Soome		return (ret);
536199767fToomas Soome	default:
53731898feToomas Soome		break;
538199767fToomas Soome	}
539199767fToomas Soome	ppa = (struct zfs_probe_args *)arg;
540199767fToomas Soome	strncpy(devname, ppa->devname, strlen(ppa->devname) - 1);
541199767fToomas Soome	devname[strlen(ppa->devname) - 1] = '\0';
542199767fToomas Soome	sprintf(devname, "%s%s:", devname, partname);
543199767fToomas Soome	pa.fd = open(devname, O_RDONLY);
544199767fToomas Soome	if (pa.fd == -1)
545199767fToomas Soome		return (ret);
546199767fToomas Soome	ret = zfs_probe(pa.fd, ppa->pool_guid);
547199767fToomas Soome	if (ret == 0)
548199767fToomas Soome		return (ret);
549199767fToomas Soome	if (part->type == PART_SOLARIS2) {
550199767fToomas Soome		pa.devname = devname;
551199767fToomas Soome		pa.pool_guid = ppa->pool_guid;
552199767fToomas Soome		pa.secsz = ppa->secsz;
553199767fToomas Soome		table = ptable_open(&pa, part->end - part->start + 1,
554199767fToomas Soome		    ppa->secsz, zfs_diskread);
555199767fToomas Soome		if (table != NULL) {
5561cfad7cToomas Soome			enum ptable_type pt = ptable_gettype(table);
5571cfad7cToomas Soome
5581cfad7cToomas Soome			if (pt == PTABLE_VTOC8 || pt == PTABLE_VTOC)
55931898feToomas Soome				ptable_iterate(table, &pa, zfs_probe_partition);
560199767fToomas Soome			ptable_close(table);
561199767fToomas Soome		}
562199767fToomas Soome	}
563199767fToomas Soome	close(pa.fd);
564199767fToomas Soome	return (0);
565199767fToomas Soome}
566199767fToomas Soome
567199767fToomas Soomeint
568199767fToomas Soomezfs_probe_dev(const char *devname, uint64_t *pool_guid)
569199767fToomas Soome{
570781f142Toomas Soome	struct disk_devdesc *dev;
571199767fToomas Soome	struct ptable *table;
572199767fToomas Soome	struct zfs_probe_args pa;
573edb3504Toomas Soome	uint64_t mediasz;
574199767fToomas Soome	int ret;
575199767fToomas Soome
576dbacaf5Toomas Soome	if (pool_guid)
577dbacaf5Toomas Soome		*pool_guid = 0;
578199767fToomas Soome	pa.fd = open(devname, O_RDONLY);
579199767fToomas Soome	if (pa.fd == -1)
580199767fToomas Soome		return (ENXIO);
581781f142Toomas Soome	/*
582781f142Toomas Soome	 * We will not probe the whole disk, we can not boot from such
583781f142Toomas Soome	 * disks and some systems will misreport the disk sizes and will
584781f142Toomas Soome	 * hang while accessing the disk.
585781f142Toomas Soome	 */
586781f142Toomas Soome	if (archsw.arch_getdev((void **)&dev, devname, NULL) == 0) {
587781f142Toomas Soome		int partition = dev->d_partition;
588781f142Toomas Soome		int slice = dev->d_slice;
589781f142Toomas Soome
590781f142Toomas Soome		free(dev);
5919a34674Toomas Soome		if (partition != D_PARTNONE && slice != D_SLICENONE) {
592781f142Toomas Soome			ret = zfs_probe(pa.fd, pool_guid);
593781f142Toomas Soome			if (ret == 0)
594781f142Toomas Soome				return (0);
595781f142Toomas Soome		}
596781f142Toomas Soome	}
597dbacaf5Toomas Soome
598199767fToomas Soome	/* Probe each partition */
599199767fToomas Soome	ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
600199767fToomas Soome	if (ret == 0)
601199767fToomas Soome		ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz);
602199767fToomas Soome	if (ret == 0) {
603199767fToomas Soome		pa.devname = devname;
604199767fToomas Soome		pa.pool_guid = pool_guid;
605199767fToomas Soome		table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz,
606199767fToomas Soome		    zfs_diskread);
607199767fToomas Soome		if (table != NULL) {
608199767fToomas Soome			ptable_iterate(table, &pa, zfs_probe_partition);
609199767fToomas Soome			ptable_close(table);
610199767fToomas Soome		}
611199767fToomas Soome	}
612199767fToomas Soome	close(pa.fd);
613dbacaf5Toomas Soome	if (pool_guid && *pool_guid == 0)
614dbacaf5Toomas Soome		ret = ENXIO;
615199767fToomas Soome	return (ret);
616199767fToomas Soome}
617199767fToomas Soome
618199767fToomas Soome/*
619199767fToomas Soome * Print information about ZFS pools
620199767fToomas Soome */
621199767fToomas Soomestatic int
622199767fToomas Soomezfs_dev_print(int verbose)
623199767fToomas Soome{
624199767fToomas Soome	spa_t *spa;
625199767fToomas Soome	char line[80];
626199767fToomas Soome	int ret = 0;
627199767fToomas Soome
628502b33aToomas Soome	if (STAILQ_EMPTY(&zfs_pools))
629502b33aToomas Soome		return (0);
630502b33aToomas Soome
631502b33aToomas Soome	printf("%s devices:", zfs_dev.dv_name);
632502b33aToomas Soome	if ((ret = pager_output("\n")) != 0)
633502b33aToomas Soome		return (ret);
634502b33aToomas Soome
635199767fToomas Soome	if (verbose) {
636199767fToomas Soome		return (spa_all_status());
637199767fToomas Soome	}
638199767fToomas Soome	STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
639199767fToomas Soome		sprintf(line, "    zfs:%s\n", spa->spa_name);
640199767fToomas Soome		ret = pager_output(line);
641199767fToomas Soome		if (ret != 0)
642199767fToomas Soome			break;
643199767fToomas Soome	}
644199767fToomas Soome	return (ret);
645199767fToomas Soome}
646199767fToomas Soome
647199767fToomas Soome/*
648199767fToomas Soome * Attempt to open the pool described by (dev) for use by (f).
649199767fToomas Soome */
650199767fToomas Soomestatic int
651199767fToomas Soomezfs_dev_open(struct open_file *f, ...)
652199767fToomas Soome{
653199767fToomas Soome	va_list		args;
654199767fToomas Soome	struct zfs_devdesc	*dev;
655199767fToomas Soome	struct zfsmount	*mount;
656199767fToomas Soome	spa_t		*spa;
657199767fToomas Soome	int		rv;
658199767fToomas Soome
659199767fToomas Soome	va_start(args, f);
660199767fToomas Soome	dev = va_arg(args, struct zfs_devdesc *);
661199767fToomas Soome	va_end(args);
662199767fToomas Soome
663199767fToomas Soome	if (dev->pool_guid == 0)
664199767fToomas Soome		spa = STAILQ_FIRST(&zfs_pools);
665199767fToomas Soome	else
666199767fToomas Soome		spa = spa_find_by_guid(dev->pool_guid);
667199767fToomas Soome	if (!spa)
668199767fToomas Soome		return (ENXIO);
669777db87Toomas Soome	mount = malloc(sizeof (*mount));
670777db87Toomas Soome	if (mount == NULL)
671777db87Toomas Soome		rv = ENOMEM;
672777db87Toomas Soome	else
673777db87Toomas Soome		rv = zfs_mount(spa, dev->root_guid, mount);
674199767fToomas Soome	if (rv != 0) {
675199767fToomas Soome		free(mount);
676199767fToomas Soome		return (rv);
677199767fToomas Soome	}
678199767fToomas Soome	if (mount->objset.os_type != DMU_OST_ZFS) {
679199767fToomas Soome		printf("Unexpected object set type %ju\n",
680199767fToomas Soome		    (uintmax_t)mount->objset.os_type);
681199767fToomas Soome		free(mount);
682199767fToomas Soome		return (EIO);
683199767fToomas Soome	}
684199767fToomas Soome	f->f_devdata = mount;
685199767fToomas Soome	free(dev);
686199767fToomas Soome	return (0);
687199767fToomas Soome}
688199767fToomas Soome
689199767fToomas Soomestatic int
690199767fToomas Soomezfs_dev_close(struct open_file *f)
691199767fToomas Soome{
692199767fToomas Soome
693199767fToomas Soome	free(f->f_devdata);
694199767fToomas Soome	f->f_devdata = NULL;
695199767fToomas Soome	return (0);
696199767fToomas Soome}
697199767fToomas Soome
698199767fToomas Soomestatic int
6998eef2abToomas Soomezfs_dev_strategy(void *devdata __unused, int rw __unused,
7008eef2abToomas Soome    daddr_t dblk __unused, size_t size __unused,
7018eef2abToomas Soome    char *buf __unused, size_t *rsize __unused)
702199767fToomas Soome{
703199767fToomas Soome
704199767fToomas Soome	return (ENOSYS);
705199767fToomas Soome}
706199767fToomas Soome
707199767fToomas Soomestruct devsw zfs_dev = {
708199767fToomas Soome	.dv_name = "zfs",
709199767fToomas Soome	.dv_type = DEVT_ZFS,
710199767fToomas Soome	.dv_init = zfs_dev_init,
711199767fToomas Soome	.dv_strategy = zfs_dev_strategy,
712199767fToomas Soome	.dv_open = zfs_dev_open,
713199767fToomas Soome	.dv_close = zfs_dev_close,
714199767fToomas Soome	.dv_ioctl = noioctl,
715199767fToomas Soome	.dv_print = zfs_dev_print,
716199767fToomas Soome	.dv_cleanup = NULL
717199767fToomas Soome};
718199767fToomas Soome
719199767fToomas Soomeint
720199767fToomas Soomezfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path)
721199767fToomas Soome{
722199767fToomas Soome	static char	rootname[ZFS_MAXNAMELEN];
723199767fToomas Soome	static char	poolname[ZFS_MAXNAMELEN];
724199767fToomas Soome	spa_t		*spa;
725199767fToomas Soome	const char	*end;
726199767fToomas Soome	const char	*np;
727199767fToomas Soome	const char	*sep;
728199767fToomas Soome	int		rv;
729199767fToomas Soome
730199767fToomas Soome	np = devspec;
731199767fToomas Soome	if (*np != ':')
732199767fToomas Soome		return (EINVAL);
733199767fToomas Soome	np++;
7346f01cc5Toomas Soome	end = strrchr(np, ':');
735199767fToomas Soome	if (end == NULL)
736199767fToomas Soome		return (EINVAL);
737199767fToomas Soome	sep = strchr(np, '/');
738199767fToomas Soome	if (sep == NULL || sep >= end)
739199767fToomas Soome		sep = end;
740199767fToomas Soome	memcpy(poolname, np, sep - np);
741199767fToomas Soome	poolname[sep - np] = '\0';
742199767fToomas Soome	if (sep < end) {
743199767fToomas Soome		sep++;
744199767fToomas Soome		memcpy(rootname, sep, end - sep);
745199767fToomas Soome		rootname[end - sep] = '\0';
746199767fToomas Soome	}
747199767fToomas Soome	else
748199767fToomas Soome		rootname[0] = '\0';
749199767fToomas Soome
750199767fToomas Soome	spa = spa_find_by_name(poolname);
751199767fToomas Soome	if (!spa)
752199767fToomas Soome		return (ENXIO);
753199767fToomas Soome	dev->pool_guid = spa->spa_guid;
754199767fToomas Soome	rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid);
755199767fToomas Soome	if (rv != 0)
756199767fToomas Soome		return (rv);
757199767fToomas Soome	if (path != NULL)
758199767fToomas Soome		*path = (*end == '\0') ? end : end + 1;
75976b3594Toomas Soome	dev->dd.d_dev = &zfs_dev;
760199767fToomas Soome	return (0);
761199767fToomas Soome}
762199767fToomas Soome
763199767fToomas Soomechar *
764199767fToomas Soomezfs_bootfs(void *zdev)
765199767fToomas Soome{
766199767fToomas Soome	static char		rootname[ZFS_MAXNAMELEN];
767199767fToomas Soome	static char		buf[2 * ZFS_MAXNAMELEN];
768199767fToomas Soome	struct zfs_devdesc	*dev = (struct zfs_devdesc *)zdev;
769199767fToomas Soome	uint64_t		objnum;
770199767fToomas Soome	spa_t			*spa;
771199767fToomas Soome	int			n;
772199767fToomas Soome
773199767fToomas Soome	buf[0] = '\0';
774c142ce1Toomas Soome	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
775199767fToomas Soome		return (buf);
776199767fToomas Soome
777199767fToomas Soome	spa = spa_find_by_guid(dev->pool_guid);
778199767fToomas Soome	if (spa == NULL) {
779199767fToomas Soome		printf("ZFS: can't find pool by guid\n");
780199767fToomas Soome		return (buf);
781199767fToomas Soome	}
782199767fToomas Soome	if (zfs_rlookup(spa, dev->root_guid, rootname)) {
783199767fToomas Soome		printf("ZFS: can't find filesystem by guid\n");
784199767fToomas Soome		return (buf);
785199767fToomas Soome	}
786199767fToomas Soome	if (zfs_lookup_dataset(spa, rootname, &objnum)) {
787199767fToomas Soome		printf("ZFS: can't find filesystem by name\n");
788199767fToomas Soome		return (buf);
789199767fToomas Soome	}
790199767fToomas Soome
7916369122Toomas Soome	/* Set the environment. */
792f6dea60Toomas Soome	snprintf(buf, sizeof (buf), "%" PRIu64, dev->pool_guid);
793f6dea60Toomas Soome	setenv("zfs-bootpool", buf, 1);
794f6dea60Toomas Soome	snprintf(buf, sizeof (buf), "%" PRIu64, spa->spa_boot_vdev->v_guid);
795f6dea60Toomas Soome	setenv("zfs-bootvdev", buf, 1);
796f6dea60Toomas Soome	snprintf(buf, sizeof (buf), "%s/%" PRIu64, spa->spa_name, objnum);
7976369122Toomas Soome	setenv("zfs-bootfs", buf, 1);
7984513705Toomas Soome	if (spa->spa_boot_vdev->v_phys_path != NULL)
7994513705Toomas Soome		setenv("bootpath", spa->spa_boot_vdev->v_phys_path, 1);
8004513705Toomas Soome	if (spa->spa_boot_vdev->v_devid != NULL)
8014513705Toomas Soome		setenv("diskdevid", spa->spa_boot_vdev->v_devid, 1);
8026369122Toomas Soome
8036369122Toomas Soome	/*
8046369122Toomas Soome	 * Build the command line string. Once our kernel will read
8056369122Toomas Soome	 * the environment and we can stop caring about old kernels,
8066369122Toomas Soome	 * we can remove this part.
8076369122Toomas Soome	 */
808fec6629Toomas Soome	snprintf(buf, sizeof (buf), "zfs-bootfs=%s/%" PRIu64, spa->spa_name,
809f6dea60Toomas Soome	    objnum);
810199767fToomas Soome	n = strlen(buf);
8114513705Toomas Soome	if (spa->spa_boot_vdev->v_phys_path != NULL) {
8126369122Toomas Soome		snprintf(buf+n, sizeof (buf) - n, ",bootpath=\"%s\"",
8134513705Toomas Soome		    spa->spa_boot_vdev->v_phys_path);
814199767fToomas Soome		n = strlen(buf);
815199767fToomas Soome	}
8164513705Toomas Soome	if (spa->spa_boot_vdev->v_devid != NULL) {
8176369122Toomas Soome		snprintf(buf+n, sizeof (buf) - n, ",diskdevid=\"%s\"",
8184513705Toomas Soome		    spa->spa_boot_vdev->v_devid);
819199767fToomas Soome	}
820199767fToomas Soome	return (buf);
821199767fToomas Soome}
822199767fToomas Soome
823199767fToomas Soomechar *
824199767fToomas Soomezfs_fmtdev(void *vdev)
825199767fToomas Soome{
826199767fToomas Soome	static char		rootname[ZFS_MAXNAMELEN];
827199767fToomas Soome	static char		buf[2 * ZFS_MAXNAMELEN + 8];
828199767fToomas Soome	struct zfs_devdesc	*dev = (struct zfs_devdesc *)vdev;
829199767fToomas Soome	spa_t			*spa;
830199767fToomas Soome
831199767fToomas Soome	buf[0] = '\0';
832c142ce1Toomas Soome	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
833199767fToomas Soome		return (buf);
834199767fToomas Soome
835c2e9ac5Toomas Soome	/* Do we have any pools? */
836c2e9ac5Toomas Soome	spa = STAILQ_FIRST(&zfs_pools);
837c2e9ac5Toomas Soome	if (spa == NULL)
838c2e9ac5Toomas Soome		return (buf);
839c2e9ac5Toomas Soome
840c2e9ac5Toomas Soome	if (dev->pool_guid == 0)
841199767fToomas Soome		dev->pool_guid = spa->spa_guid;
842c2e9ac5Toomas Soome	else
843199767fToomas Soome		spa = spa_find_by_guid(dev->pool_guid);
844c2e9ac5Toomas Soome
845199767fToomas Soome	if (spa == NULL) {
846199767fToomas Soome		printf("ZFS: can't find pool by guid\n");
847199767fToomas Soome		return (buf);
848199767fToomas Soome	}
849199767fToomas Soome	if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) {
850199767fToomas Soome		printf("ZFS: can't find root filesystem\n");
851199767fToomas Soome		return (buf);
852199767fToomas Soome	}
853199767fToomas Soome	if (zfs_rlookup(spa, dev->root_guid, rootname)) {
854199767fToomas Soome		printf("ZFS: can't find filesystem by guid\n");
855199767fToomas Soome		return (buf);
856199767fToomas Soome	}
857199767fToomas Soome
858199767fToomas Soome	if (rootname[0] == '\0')
85976b3594Toomas Soome		sprintf(buf, "%s:%s:", dev->dd.d_dev->dv_name, spa->spa_name);
860199767fToomas Soome	else
86176b3594Toomas Soome		sprintf(buf, "%s:%s/%s:", dev->dd.d_dev->dv_name, spa->spa_name,
862199767fToomas Soome		    rootname);
863199767fToomas Soome	return (buf);
864199767fToomas Soome}
865199767fToomas Soome
866199767fToomas Soomeint
867199767fToomas Soomezfs_list(const char *name)
868199767fToomas Soome{
869199767fToomas Soome	static char	poolname[ZFS_MAXNAMELEN];
870199767fToomas Soome	uint64_t	objid;
871199767fToomas Soome	spa_t		*spa;
872199767fToomas Soome	const char	*dsname;
873199767fToomas Soome	int		len;
874199767fToomas Soome	int		rv;
875199767fToomas Soome
876199767fToomas Soome	len = strlen(name);
877199767fToomas Soome	dsname = strchr(name, '/');
878199767fToomas Soome	if (dsname != NULL) {
879199767fToomas Soome		len = dsname - name;
880199767fToomas Soome		dsname++;
881199767fToomas Soome	} else
882199767fToomas Soome		dsname = "";
883199767fToomas Soome	memcpy(poolname, name, len);
884199767fToomas Soome	poolname[len] = '\0';
885199767fToomas Soome
886199767fToomas Soome	spa = spa_find_by_name(poolname);
887199767fToomas Soome	if (!spa)
888199767fToomas Soome		return (ENXIO);
889199767fToomas Soome	rv = zfs_lookup_dataset(spa, dsname, &objid);
890199767fToomas Soome	if (rv != 0)
891199767fToomas Soome		return (rv);
892199767fToomas Soome
893199767fToomas Soome	return (zfs_list_dataset(spa, objid));
894199767fToomas Soome}