1185029Spjd/*-
2185029Spjd * Copyright (c) 2007 Doug Rabson
3185029Spjd * All rights reserved.
4185029Spjd *
5185029Spjd * Redistribution and use in source and binary forms, with or without
6185029Spjd * modification, are permitted provided that the following conditions
7185029Spjd * are met:
8185029Spjd * 1. Redistributions of source code must retain the above copyright
9185029Spjd *    notice, this list of conditions and the following disclaimer.
10185029Spjd * 2. Redistributions in binary form must reproduce the above copyright
11185029Spjd *    notice, this list of conditions and the following disclaimer in the
12185029Spjd *    documentation and/or other materials provided with the distribution.
13185029Spjd *
14185029Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15185029Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16185029Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17185029Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18185029Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19185029Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20185029Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21185029Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22185029Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23185029Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24185029Spjd * SUCH DAMAGE.
25185029Spjd *
26185029Spjd *	$FreeBSD$
27185029Spjd */
28185029Spjd
29185029Spjd#include <sys/cdefs.h>
30185029Spjd__FBSDID("$FreeBSD$");
31185029Spjd
32185029Spjd/*
33185029Spjd *	Stand-alone file reading package.
34185029Spjd */
35185029Spjd
36239068Sae#include <sys/disk.h>
37185029Spjd#include <sys/param.h>
38185029Spjd#include <sys/time.h>
39185029Spjd#include <sys/queue.h>
40239068Sae#include <part.h>
41185029Spjd#include <stddef.h>
42185029Spjd#include <stdarg.h>
43185029Spjd#include <string.h>
44185029Spjd#include <stand.h>
45185029Spjd#include <bootstrap.h>
46185029Spjd
47235329Savg#include "libzfs.h"
48235329Savg
49185029Spjd#include "zfsimpl.c"
50185029Spjd
51293802Sallanjude/* Define the range of indexes to be populated with ZFS Boot Environments */
52293802Sallanjude#define		ZFS_BE_FIRST	4
53293802Sallanjude#define		ZFS_BE_LAST	8
54293802Sallanjude
55185029Spjdstatic int	zfs_open(const char *path, struct open_file *f);
56185029Spjdstatic int	zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
57185029Spjdstatic int	zfs_close(struct open_file *f);
58185029Spjdstatic int	zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
59185029Spjdstatic off_t	zfs_seek(struct open_file *f, off_t offset, int where);
60185029Spjdstatic int	zfs_stat(struct open_file *f, struct stat *sb);
61185029Spjdstatic int	zfs_readdir(struct open_file *f, struct dirent *d);
62185029Spjd
63185029Spjdstruct devsw zfs_dev;
64185029Spjd
65185029Spjdstruct fs_ops zfs_fsops = {
66185029Spjd	"zfs",
67185029Spjd	zfs_open,
68185029Spjd	zfs_close,
69185029Spjd	zfs_read,
70185029Spjd	zfs_write,
71185029Spjd	zfs_seek,
72185029Spjd	zfs_stat,
73185029Spjd	zfs_readdir
74185029Spjd};
75185029Spjd
76185029Spjd/*
77185029Spjd * In-core open file.
78185029Spjd */
79185029Spjdstruct file {
80185029Spjd	off_t		f_seekp;	/* seek pointer */
81185029Spjd	dnode_phys_t	f_dnode;
82185029Spjd	uint64_t	f_zap_type;	/* zap type for readdir */
83185029Spjd	uint64_t	f_num_leafs;	/* number of fzap leaf blocks */
84185029Spjd	zap_leaf_phys_t	*f_zap_leaf;	/* zap leaf buffer */
85185029Spjd};
86185029Spjd
87293802Sallanjudestatic int	zfs_env_index;
88293802Sallanjudestatic int	zfs_env_count;
89293802Sallanjude
90293802SallanjudeSLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head);
91293802Sallanjudestruct zfs_be_list *zfs_be_headp;
92293802Sallanjudestruct zfs_be_entry {
93293802Sallanjude	const char *name;
94293802Sallanjude	SLIST_ENTRY(zfs_be_entry) entries;
95293802Sallanjude} *zfs_be, *zfs_be_tmp;
96293802Sallanjude
97185029Spjd/*
98185029Spjd * Open a file.
99185029Spjd */
100185029Spjdstatic int
101185029Spjdzfs_open(const char *upath, struct open_file *f)
102185029Spjd{
103235329Savg	struct zfsmount *mount = (struct zfsmount *)f->f_devdata;
104185029Spjd	struct file *fp;
105185029Spjd	int rc;
106185029Spjd
107235394Savg	if (f->f_dev != &zfs_dev)
108185029Spjd		return (EINVAL);
109185029Spjd
110185029Spjd	/* allocate file system specific data structure */
111185029Spjd	fp = malloc(sizeof(struct file));
112185029Spjd	bzero(fp, sizeof(struct file));
113185029Spjd	f->f_fsdata = (void *)fp;
114185029Spjd
115235329Savg	rc = zfs_lookup(mount, upath, &fp->f_dnode);
116185029Spjd	fp->f_seekp = 0;
117185029Spjd	if (rc) {
118185029Spjd		f->f_fsdata = NULL;
119185029Spjd		free(fp);
120185029Spjd	}
121185029Spjd	return (rc);
122185029Spjd}
123185029Spjd
124185029Spjdstatic int
125185029Spjdzfs_close(struct open_file *f)
126185029Spjd{
127185029Spjd	struct file *fp = (struct file *)f->f_fsdata;
128185029Spjd
129185029Spjd	dnode_cache_obj = 0;
130185029Spjd	f->f_fsdata = (void *)0;
131185029Spjd	if (fp == (struct file *)0)
132185029Spjd		return (0);
133185029Spjd
134185029Spjd	free(fp);
135185029Spjd	return (0);
136185029Spjd}
137185029Spjd
138185029Spjd/*
139185029Spjd * Copy a portion of a file into kernel memory.
140185029Spjd * Cross block boundaries when necessary.
141185029Spjd */
142185029Spjdstatic int
143185029Spjdzfs_read(struct open_file *f, void *start, size_t size, size_t *resid	/* out */)
144185029Spjd{
145235390Savg	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
146185029Spjd	struct file *fp = (struct file *)f->f_fsdata;
147219089Spjd	struct stat sb;
148185029Spjd	size_t n;
149185029Spjd	int rc;
150185029Spjd
151219089Spjd	rc = zfs_stat(f, &sb);
152219089Spjd	if (rc)
153219089Spjd		return (rc);
154185029Spjd	n = size;
155219089Spjd	if (fp->f_seekp + n > sb.st_size)
156219089Spjd		n = sb.st_size - fp->f_seekp;
157294716Ssmh
158185029Spjd	rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n);
159185029Spjd	if (rc)
160185029Spjd		return (rc);
161185029Spjd
162185029Spjd	if (0) {
163185029Spjd	    int i;
164185029Spjd	    for (i = 0; i < n; i++)
165185029Spjd		putchar(((char*) start)[i]);
166185029Spjd	}
167185029Spjd	fp->f_seekp += n;
168185029Spjd	if (resid)
169185029Spjd		*resid = size - n;
170185029Spjd
171185029Spjd	return (0);
172185029Spjd}
173185029Spjd
174185029Spjd/*
175185029Spjd * Don't be silly - the bootstrap has no business writing anything.
176185029Spjd */
177185029Spjdstatic int
178185029Spjdzfs_write(struct open_file *f, void *start, size_t size, size_t *resid	/* out */)
179185029Spjd{
180185029Spjd
181185029Spjd	return (EROFS);
182185029Spjd}
183185029Spjd
184185029Spjdstatic off_t
185185029Spjdzfs_seek(struct open_file *f, off_t offset, int where)
186185029Spjd{
187185029Spjd	struct file *fp = (struct file *)f->f_fsdata;
188185029Spjd
189185029Spjd	switch (where) {
190185029Spjd	case SEEK_SET:
191185029Spjd		fp->f_seekp = offset;
192185029Spjd		break;
193185029Spjd	case SEEK_CUR:
194185029Spjd		fp->f_seekp += offset;
195185029Spjd		break;
196185029Spjd	case SEEK_END:
197219089Spjd	    {
198219089Spjd		struct stat sb;
199219089Spjd		int error;
200219089Spjd
201219089Spjd		error = zfs_stat(f, &sb);
202219089Spjd		if (error != 0) {
203219089Spjd			errno = error;
204219089Spjd			return (-1);
205219089Spjd		}
206219089Spjd		fp->f_seekp = sb.st_size - offset;
207185029Spjd		break;
208219089Spjd	    }
209185029Spjd	default:
210185029Spjd		errno = EINVAL;
211185029Spjd		return (-1);
212185029Spjd	}
213185029Spjd	return (fp->f_seekp);
214185029Spjd}
215185029Spjd
216185029Spjdstatic int
217185029Spjdzfs_stat(struct open_file *f, struct stat *sb)
218185029Spjd{
219235390Savg	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
220185029Spjd	struct file *fp = (struct file *)f->f_fsdata;
221185029Spjd
222219089Spjd	return (zfs_dnode_stat(spa, &fp->f_dnode, sb));
223185029Spjd}
224185029Spjd
225185029Spjdstatic int
226185029Spjdzfs_readdir(struct open_file *f, struct dirent *d)
227185029Spjd{
228235390Savg	const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
229185029Spjd	struct file *fp = (struct file *)f->f_fsdata;
230185029Spjd	mzap_ent_phys_t mze;
231219089Spjd	struct stat sb;
232185029Spjd	size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT;
233185029Spjd	int rc;
234185029Spjd
235219089Spjd	rc = zfs_stat(f, &sb);
236219089Spjd	if (rc)
237219089Spjd		return (rc);
238219089Spjd	if (!S_ISDIR(sb.st_mode))
239185029Spjd		return (ENOTDIR);
240185029Spjd
241185029Spjd	/*
242185029Spjd	 * If this is the first read, get the zap type.
243185029Spjd	 */
244185029Spjd	if (fp->f_seekp == 0) {
245185029Spjd		rc = dnode_read(spa, &fp->f_dnode,
246185029Spjd				0, &fp->f_zap_type, sizeof(fp->f_zap_type));
247185029Spjd		if (rc)
248185029Spjd			return (rc);
249185029Spjd
250185029Spjd		if (fp->f_zap_type == ZBT_MICRO) {
251185029Spjd			fp->f_seekp = offsetof(mzap_phys_t, mz_chunk);
252185029Spjd		} else {
253185029Spjd			rc = dnode_read(spa, &fp->f_dnode,
254185029Spjd					offsetof(zap_phys_t, zap_num_leafs),
255185029Spjd					&fp->f_num_leafs,
256185029Spjd					sizeof(fp->f_num_leafs));
257185029Spjd			if (rc)
258185029Spjd				return (rc);
259185029Spjd
260185029Spjd			fp->f_seekp = bsize;
261185029Spjd			fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize);
262185029Spjd			rc = dnode_read(spa, &fp->f_dnode,
263185029Spjd					fp->f_seekp,
264185029Spjd					fp->f_zap_leaf,
265185029Spjd					bsize);
266185029Spjd			if (rc)
267185029Spjd				return (rc);
268185029Spjd		}
269185029Spjd	}
270185029Spjd
271185029Spjd	if (fp->f_zap_type == ZBT_MICRO) {
272185029Spjd	mzap_next:
273185029Spjd		if (fp->f_seekp >= bsize)
274185029Spjd			return (ENOENT);
275185029Spjd
276185029Spjd		rc = dnode_read(spa, &fp->f_dnode,
277185029Spjd				fp->f_seekp, &mze, sizeof(mze));
278208669Savg		if (rc)
279208669Savg			return (rc);
280185029Spjd		fp->f_seekp += sizeof(mze);
281185029Spjd
282185029Spjd		if (!mze.mze_name[0])
283185029Spjd			goto mzap_next;
284185029Spjd
285185029Spjd		d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value);
286185029Spjd		d->d_type = ZFS_DIRENT_TYPE(mze.mze_value);
287185029Spjd		strcpy(d->d_name, mze.mze_name);
288185029Spjd		d->d_namlen = strlen(d->d_name);
289185029Spjd		return (0);
290185029Spjd	} else {
291185029Spjd		zap_leaf_t zl;
292185029Spjd		zap_leaf_chunk_t *zc, *nc;
293185029Spjd		int chunk;
294185029Spjd		size_t namelen;
295185029Spjd		char *p;
296185029Spjd		uint64_t value;
297185029Spjd
298185029Spjd		/*
299185029Spjd		 * Initialise this so we can use the ZAP size
300185029Spjd		 * calculating macros.
301185029Spjd		 */
302185029Spjd		zl.l_bs = ilog2(bsize);
303185029Spjd		zl.l_phys = fp->f_zap_leaf;
304185029Spjd
305185029Spjd		/*
306185029Spjd		 * Figure out which chunk we are currently looking at
307185029Spjd		 * and consider seeking to the next leaf. We use the
308185029Spjd		 * low bits of f_seekp as a simple chunk index.
309185029Spjd		 */
310185029Spjd	fzap_next:
311185029Spjd		chunk = fp->f_seekp & (bsize - 1);
312185029Spjd		if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) {
313185029Spjd			fp->f_seekp = (fp->f_seekp & ~(bsize - 1)) + bsize;
314185029Spjd			chunk = 0;
315185029Spjd
316185029Spjd			/*
317185029Spjd			 * Check for EOF and read the new leaf.
318185029Spjd			 */
319185029Spjd			if (fp->f_seekp >= bsize * fp->f_num_leafs)
320185029Spjd				return (ENOENT);
321185029Spjd
322185029Spjd			rc = dnode_read(spa, &fp->f_dnode,
323185029Spjd					fp->f_seekp,
324185029Spjd					fp->f_zap_leaf,
325185029Spjd					bsize);
326185029Spjd			if (rc)
327185029Spjd				return (rc);
328185029Spjd		}
329185029Spjd
330185029Spjd		zc = &ZAP_LEAF_CHUNK(&zl, chunk);
331185029Spjd		fp->f_seekp++;
332185029Spjd		if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
333185029Spjd			goto fzap_next;
334185029Spjd
335240346Savg		namelen = zc->l_entry.le_name_numints;
336185029Spjd		if (namelen > sizeof(d->d_name))
337185029Spjd			namelen = sizeof(d->d_name);
338185029Spjd
339185029Spjd		/*
340185029Spjd		 * Paste the name back together.
341185029Spjd		 */
342185029Spjd		nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
343185029Spjd		p = d->d_name;
344185029Spjd		while (namelen > 0) {
345185029Spjd			int len;
346185029Spjd			len = namelen;
347185029Spjd			if (len > ZAP_LEAF_ARRAY_BYTES)
348185029Spjd				len = ZAP_LEAF_ARRAY_BYTES;
349185029Spjd			memcpy(p, nc->l_array.la_array, len);
350185029Spjd			p += len;
351185029Spjd			namelen -= len;
352185029Spjd			nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
353185029Spjd		}
354185029Spjd		d->d_name[sizeof(d->d_name) - 1] = 0;
355185029Spjd
356185029Spjd		/*
357185029Spjd		 * Assume the first eight bytes of the value are
358185029Spjd		 * a uint64_t.
359185029Spjd		 */
360185029Spjd		value = fzap_leaf_value(&zl, zc);
361185029Spjd
362185029Spjd		d->d_fileno = ZFS_DIRENT_OBJ(value);
363185029Spjd		d->d_type = ZFS_DIRENT_TYPE(value);
364185029Spjd		d->d_namlen = strlen(d->d_name);
365185029Spjd
366185029Spjd		return (0);
367185029Spjd	}
368185029Spjd}
369185029Spjd
370185029Spjdstatic int
371185029Spjdvdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size)
372185029Spjd{
373185029Spjd	int fd;
374185029Spjd
375185029Spjd	fd = (uintptr_t) priv;
376185029Spjd	lseek(fd, offset, SEEK_SET);
377185029Spjd	if (read(fd, buf, size) == size) {
378185029Spjd		return 0;
379185029Spjd	} else {
380185029Spjd		return (EIO);
381185029Spjd	}
382185029Spjd}
383185029Spjd
384235329Savgstatic int
385235329Savgzfs_dev_init(void)
386185029Spjd{
387241289Savg	spa_t *spa;
388241289Savg	spa_t *next;
389241289Savg	spa_t *prev;
390241289Savg
391235329Savg	zfs_init();
392235329Savg	if (archsw.arch_zfs_probe == NULL)
393235329Savg		return (ENXIO);
394235329Savg	archsw.arch_zfs_probe();
395241289Savg
396241289Savg	prev = NULL;
397241289Savg	spa = STAILQ_FIRST(&zfs_pools);
398241289Savg	while (spa != NULL) {
399241289Savg		next = STAILQ_NEXT(spa, spa_link);
400241289Savg		if (zfs_spa_init(spa)) {
401241289Savg			if (prev == NULL)
402241289Savg				STAILQ_REMOVE_HEAD(&zfs_pools, spa_link);
403241289Savg			else
404241289Savg				STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link);
405241289Savg		} else
406241289Savg			prev = spa;
407241289Savg		spa = next;
408241289Savg	}
409235329Savg	return (0);
410185029Spjd}
411185029Spjd
412239068Saestruct zfs_probe_args {
413239068Sae	int		fd;
414239068Sae	const char	*devname;
415239068Sae	uint64_t	*pool_guid;
416239068Sae	uint16_t	secsz;
417239068Sae};
418239068Sae
419239068Saestatic int
420239068Saezfs_diskread(void *arg, void *buf, size_t blocks, off_t offset)
421239068Sae{
422239068Sae	struct zfs_probe_args *ppa;
423239068Sae
424239068Sae	ppa = (struct zfs_probe_args *)arg;
425239068Sae	return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd,
426239068Sae	    offset * ppa->secsz, buf, blocks * ppa->secsz));
427239068Sae}
428239068Sae
429239068Saestatic int
430239068Saezfs_probe(int fd, uint64_t *pool_guid)
431239068Sae{
432239068Sae	spa_t *spa;
433239068Sae	int ret;
434239068Sae
435239068Sae	ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa);
436239068Sae	if (ret == 0 && pool_guid != NULL)
437239068Sae		*pool_guid = spa->spa_guid;
438239068Sae	return (ret);
439239068Sae}
440239068Sae
441239068Saestatic void
442239068Saezfs_probe_partition(void *arg, const char *partname,
443239068Sae    const struct ptable_entry *part)
444239068Sae{
445239068Sae	struct zfs_probe_args *ppa, pa;
446239068Sae	struct ptable *table;
447239068Sae	char devname[32];
448239068Sae	int ret;
449239068Sae
450239068Sae	/* Probe only freebsd-zfs and freebsd partitions */
451239068Sae	if (part->type != PART_FREEBSD &&
452239068Sae	    part->type != PART_FREEBSD_ZFS)
453239068Sae		return;
454239068Sae
455239068Sae	ppa = (struct zfs_probe_args *)arg;
456239068Sae	strncpy(devname, ppa->devname, strlen(ppa->devname) - 1);
457239292Sae	devname[strlen(ppa->devname) - 1] = '\0';
458239068Sae	sprintf(devname, "%s%s:", devname, partname);
459239068Sae	pa.fd = open(devname, O_RDONLY);
460239068Sae	if (pa.fd == -1)
461239068Sae		return;
462239068Sae	ret = zfs_probe(pa.fd, ppa->pool_guid);
463239068Sae	if (ret == 0)
464239068Sae		return;
465239068Sae	/* Do we have BSD label here? */
466239068Sae	if (part->type == PART_FREEBSD) {
467239068Sae		pa.devname = devname;
468239068Sae		pa.pool_guid = ppa->pool_guid;
469239068Sae		pa.secsz = ppa->secsz;
470239068Sae		table = ptable_open(&pa, part->end - part->start + 1,
471239068Sae		    ppa->secsz, zfs_diskread);
472239068Sae		if (table != NULL) {
473239068Sae			ptable_iterate(table, &pa, zfs_probe_partition);
474239068Sae			ptable_close(table);
475239068Sae		}
476239068Sae	}
477239068Sae	close(pa.fd);
478239068Sae}
479239068Sae
480235329Savgint
481235329Savgzfs_probe_dev(const char *devname, uint64_t *pool_guid)
482185029Spjd{
483239068Sae	struct ptable *table;
484239068Sae	struct zfs_probe_args pa;
485239068Sae	off_t mediasz;
486235329Savg	int ret;
487185029Spjd
488239068Sae	pa.fd = open(devname, O_RDONLY);
489239068Sae	if (pa.fd == -1)
490235329Savg		return (ENXIO);
491239068Sae	/* Probe the whole disk */
492239068Sae	ret = zfs_probe(pa.fd, pool_guid);
493239068Sae	if (ret == 0)
494239068Sae		return (0);
495239068Sae	/* Probe each partition */
496239068Sae	ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
497239068Sae	if (ret == 0)
498239068Sae		ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz);
499239068Sae	if (ret == 0) {
500239068Sae		pa.devname = devname;
501239068Sae		pa.pool_guid = pool_guid;
502239068Sae		table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz,
503239068Sae		    zfs_diskread);
504239068Sae		if (table != NULL) {
505239068Sae			ptable_iterate(table, &pa, zfs_probe_partition);
506239068Sae			ptable_close(table);
507239068Sae		}
508239068Sae	}
509239068Sae	close(pa.fd);
510294716Ssmh	return (ret);
511185029Spjd}
512185029Spjd
513185029Spjd/*
514185029Spjd * Print information about ZFS pools
515185029Spjd */
516185029Spjdstatic void
517185029Spjdzfs_dev_print(int verbose)
518185029Spjd{
519185029Spjd	spa_t *spa;
520185029Spjd	char line[80];
521185029Spjd
522185029Spjd	if (verbose) {
523185029Spjd		spa_all_status();
524185029Spjd		return;
525185029Spjd	}
526185029Spjd	STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
527235329Savg		sprintf(line, "    zfs:%s\n", spa->spa_name);
528185029Spjd		pager_output(line);
529185029Spjd	}
530185029Spjd}
531185029Spjd
532185029Spjd/*
533185029Spjd * Attempt to open the pool described by (dev) for use by (f).
534185029Spjd */
535235329Savgstatic int
536235364Savgzfs_dev_open(struct open_file *f, ...)
537235329Savg{
538235364Savg	va_list		args;
539235364Savg	struct zfs_devdesc	*dev;
540235329Savg	struct zfsmount	*mount;
541235364Savg	spa_t		*spa;
542235329Savg	int		rv;
543235329Savg
544235364Savg	va_start(args, f);
545235364Savg	dev = va_arg(args, struct zfs_devdesc *);
546235364Savg	va_end(args);
547235364Savg
548241282Savg	if (dev->pool_guid == 0)
549241282Savg		spa = STAILQ_FIRST(&zfs_pools);
550241282Savg	else
551241282Savg		spa = spa_find_by_guid(dev->pool_guid);
552235364Savg	if (!spa)
553235364Savg		return (ENXIO);
554235329Savg	mount = malloc(sizeof(*mount));
555235364Savg	rv = zfs_mount(spa, dev->root_guid, mount);
556235329Savg	if (rv != 0) {
557235329Savg		free(mount);
558235329Savg		return (rv);
559235329Savg	}
560235329Savg	if (mount->objset.os_type != DMU_OST_ZFS) {
561235361Savg		printf("Unexpected object set type %ju\n",
562235361Savg		    (uintmax_t)mount->objset.os_type);
563235329Savg		free(mount);
564235329Savg		return (EIO);
565235329Savg	}
566235329Savg	f->f_devdata = mount;
567235329Savg	free(dev);
568235329Savg	return (0);
569235329Savg}
570235329Savg
571241290Savgstatic int
572185029Spjdzfs_dev_close(struct open_file *f)
573185029Spjd{
574185029Spjd
575235329Savg	free(f->f_devdata);
576185029Spjd	f->f_devdata = NULL;
577185029Spjd	return (0);
578185029Spjd}
579185029Spjd
580241290Savgstatic int
581185029Spjdzfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
582185029Spjd{
583185029Spjd
584185029Spjd	return (ENOSYS);
585185029Spjd}
586185029Spjd
587185029Spjdstruct devsw zfs_dev = {
588235329Savg	.dv_name = "zfs",
589235329Savg	.dv_type = DEVT_ZFS,
590185029Spjd	.dv_init = zfs_dev_init,
591235329Savg	.dv_strategy = zfs_dev_strategy,
592235329Savg	.dv_open = zfs_dev_open,
593235329Savg	.dv_close = zfs_dev_close,
594185029Spjd	.dv_ioctl = noioctl,
595185029Spjd	.dv_print = zfs_dev_print,
596185029Spjd	.dv_cleanup = NULL
597185029Spjd};
598235329Savg
599235329Savgint
600235329Savgzfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path)
601235329Savg{
602235329Savg	static char	rootname[ZFS_MAXNAMELEN];
603235329Savg	static char	poolname[ZFS_MAXNAMELEN];
604235329Savg	spa_t		*spa;
605235329Savg	const char	*end;
606235329Savg	const char	*np;
607235329Savg	const char	*sep;
608235329Savg	int		rv;
609235329Savg
610235329Savg	np = devspec;
611235329Savg	if (*np != ':')
612235329Savg		return (EINVAL);
613235329Savg	np++;
614235329Savg	end = strchr(np, ':');
615235329Savg	if (end == NULL)
616235329Savg		return (EINVAL);
617235329Savg	sep = strchr(np, '/');
618235329Savg	if (sep == NULL || sep >= end)
619235329Savg		sep = end;
620235329Savg	memcpy(poolname, np, sep - np);
621235329Savg	poolname[sep - np] = '\0';
622235329Savg	if (sep < end) {
623235329Savg		sep++;
624235329Savg		memcpy(rootname, sep, end - sep);
625235329Savg		rootname[end - sep] = '\0';
626235329Savg	}
627235329Savg	else
628235329Savg		rootname[0] = '\0';
629235329Savg
630235329Savg	spa = spa_find_by_name(poolname);
631235329Savg	if (!spa)
632235329Savg		return (ENXIO);
633235329Savg	dev->pool_guid = spa->spa_guid;
634241292Savg	rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid);
635241292Savg	if (rv != 0)
636241292Savg		return (rv);
637235329Savg	if (path != NULL)
638235329Savg		*path = (*end == '\0') ? end : end + 1;
639235329Savg	dev->d_dev = &zfs_dev;
640235329Savg	dev->d_type = zfs_dev.dv_type;
641235329Savg	return (0);
642235329Savg}
643235329Savg
644235329Savgchar *
645235329Savgzfs_fmtdev(void *vdev)
646235329Savg{
647235329Savg	static char		rootname[ZFS_MAXNAMELEN];
648235329Savg	static char		buf[2 * ZFS_MAXNAMELEN + 8];
649235329Savg	struct zfs_devdesc	*dev = (struct zfs_devdesc *)vdev;
650235329Savg	spa_t			*spa;
651235329Savg
652235329Savg	buf[0] = '\0';
653235329Savg	if (dev->d_type != DEVT_ZFS)
654235329Savg		return (buf);
655235329Savg
656241292Savg	if (dev->pool_guid == 0) {
657241282Savg		spa = STAILQ_FIRST(&zfs_pools);
658241292Savg		dev->pool_guid = spa->spa_guid;
659241292Savg	} else
660241282Savg		spa = spa_find_by_guid(dev->pool_guid);
661235329Savg	if (spa == NULL) {
662235329Savg		printf("ZFS: can't find pool by guid\n");
663235329Savg		return (buf);
664235329Savg	}
665235329Savg	if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) {
666235329Savg		printf("ZFS: can't find root filesystem\n");
667235329Savg		return (buf);
668235329Savg	}
669235329Savg	if (zfs_rlookup(spa, dev->root_guid, rootname)) {
670235329Savg		printf("ZFS: can't find filesystem by guid\n");
671235329Savg		return (buf);
672235329Savg	}
673235329Savg
674235329Savg	if (rootname[0] == '\0')
675235329Savg		sprintf(buf, "%s:%s:", dev->d_dev->dv_name, spa->spa_name);
676235329Savg	else
677235329Savg		sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, spa->spa_name,
678235329Savg		    rootname);
679235329Savg	return (buf);
680235329Savg}
681241283Savg
682241283Savgint
683241283Savgzfs_list(const char *name)
684241283Savg{
685241283Savg	static char	poolname[ZFS_MAXNAMELEN];
686241283Savg	uint64_t	objid;
687241283Savg	spa_t		*spa;
688241283Savg	const char	*dsname;
689241283Savg	int		len;
690241283Savg	int		rv;
691241283Savg
692241283Savg	len = strlen(name);
693241283Savg	dsname = strchr(name, '/');
694241283Savg	if (dsname != NULL) {
695241283Savg		len = dsname - name;
696241283Savg		dsname++;
697241292Savg	} else
698241292Savg		dsname = "";
699241283Savg	memcpy(poolname, name, len);
700241283Savg	poolname[len] = '\0';
701241283Savg
702241283Savg	spa = spa_find_by_name(poolname);
703241283Savg	if (!spa)
704241283Savg		return (ENXIO);
705241292Savg	rv = zfs_lookup_dataset(spa, dsname, &objid);
706241283Savg	if (rv != 0)
707241283Savg		return (rv);
708293802Sallanjude
709293802Sallanjude	return (zfs_list_dataset(spa, objid));
710293802Sallanjude}
711293802Sallanjude
712295475Sallanjudevoid
713295475Sallanjudeinit_zfs_bootenv(char *currdev)
714295475Sallanjude{
715295475Sallanjude	char *beroot;
716295475Sallanjude
717295475Sallanjude	if (strlen(currdev) == 0)
718295475Sallanjude		return;
719295475Sallanjude	if(strncmp(currdev, "zfs:", 4) != 0)
720295475Sallanjude		return;
721295475Sallanjude	/* Remove the trailing : */
722295475Sallanjude	currdev[strlen(currdev) - 1] = '\0';
723295475Sallanjude	setenv("zfs_be_active", currdev, 1);
724295475Sallanjude	setenv("zfs_be_currpage", "1", 1);
725295475Sallanjude	/* Forward past zfs: */
726295475Sallanjude	currdev = strchr(currdev, ':');
727295475Sallanjude	currdev++;
728295475Sallanjude	/* Remove the last element (current bootenv) */
729295475Sallanjude	beroot = strrchr(currdev, '/');
730295475Sallanjude	if (beroot != NULL)
731295475Sallanjude		beroot[0] = '\0';
732295475Sallanjude	beroot = currdev;
733295475Sallanjude	setenv("zfs_be_root", beroot, 1);
734295475Sallanjude}
735295475Sallanjude
736293802Sallanjudeint
737293802Sallanjudezfs_bootenv(const char *name)
738293802Sallanjude{
739293802Sallanjude	static char	poolname[ZFS_MAXNAMELEN], *dsname, *root;
740293802Sallanjude	char		becount[4];
741293802Sallanjude	uint64_t	objid;
742293802Sallanjude	spa_t		*spa;
743293802Sallanjude	int		len, rv, pages, perpage, currpage;
744293802Sallanjude
745293802Sallanjude	if (name == NULL)
746293802Sallanjude		return (EINVAL);
747293802Sallanjude	if ((root = getenv("zfs_be_root")) == NULL)
748293802Sallanjude		return (EINVAL);
749293802Sallanjude
750293802Sallanjude	if (strcmp(name, root) != 0) {
751293802Sallanjude		if (setenv("zfs_be_root", name, 1) != 0)
752293802Sallanjude			return (ENOMEM);
753293802Sallanjude	}
754293802Sallanjude
755293802Sallanjude	SLIST_INIT(&zfs_be_head);
756293802Sallanjude	zfs_env_count = 0;
757293802Sallanjude	len = strlen(name);
758293802Sallanjude	dsname = strchr(name, '/');
759293802Sallanjude	if (dsname != NULL) {
760293802Sallanjude		len = dsname - name;
761293802Sallanjude		dsname++;
762293802Sallanjude	} else
763293802Sallanjude		dsname = "";
764293802Sallanjude	memcpy(poolname, name, len);
765293802Sallanjude	poolname[len] = '\0';
766293802Sallanjude
767293802Sallanjude	spa = spa_find_by_name(poolname);
768293802Sallanjude	if (!spa)
769293802Sallanjude		return (ENXIO);
770293802Sallanjude	rv = zfs_lookup_dataset(spa, dsname, &objid);
771293802Sallanjude	if (rv != 0)
772293802Sallanjude		return (rv);
773293802Sallanjude	rv = zfs_callback_dataset(spa, objid, zfs_belist_add);
774293802Sallanjude
775293802Sallanjude	/* Calculate and store the number of pages of BEs */
776293802Sallanjude	perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1);
777293802Sallanjude	pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0);
778293802Sallanjude	snprintf(becount, 4, "%d", pages);
779293802Sallanjude	if (setenv("zfs_be_pages", becount, 1) != 0)
780293802Sallanjude		return (ENOMEM);
781293802Sallanjude
782293802Sallanjude	/* Roll over the page counter if it has exceeded the maximum */
783293802Sallanjude	currpage = strtol(getenv("zfs_be_currpage"), NULL, 10);
784293802Sallanjude	if (currpage > pages) {
785293802Sallanjude		if (setenv("zfs_be_currpage", "1", 1) != 0)
786293802Sallanjude			return (ENOMEM);
787293802Sallanjude	}
788293802Sallanjude
789293802Sallanjude	/* Populate the menu environment variables */
790293802Sallanjude	zfs_set_env();
791293802Sallanjude
792293802Sallanjude	/* Clean up the SLIST of ZFS BEs */
793293802Sallanjude	while (!SLIST_EMPTY(&zfs_be_head)) {
794293802Sallanjude		zfs_be = SLIST_FIRST(&zfs_be_head);
795293802Sallanjude		SLIST_REMOVE_HEAD(&zfs_be_head, entries);
796293802Sallanjude		free(zfs_be);
797293802Sallanjude	}
798293802Sallanjude
799241292Savg	return (rv);
800241283Savg}
801293802Sallanjude
802293802Sallanjudeint
803293802Sallanjudezfs_belist_add(const char *name)
804293802Sallanjude{
805293802Sallanjude
806295475Sallanjude	/* Skip special datasets that start with a $ character */
807295475Sallanjude	if (strncmp(name, "$", 1) == 0) {
808295475Sallanjude		return (0);
809295475Sallanjude	}
810293802Sallanjude	/* Add the boot environment to the head of the SLIST */
811293802Sallanjude	zfs_be = malloc(sizeof(struct zfs_be_entry));
812295475Sallanjude	if (zfs_be == NULL) {
813295475Sallanjude		return (ENOMEM);
814295475Sallanjude	}
815293802Sallanjude	zfs_be->name = name;
816293802Sallanjude	SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries);
817293802Sallanjude	zfs_env_count++;
818293802Sallanjude
819293802Sallanjude	return (0);
820293802Sallanjude}
821293802Sallanjude
822293802Sallanjudeint
823293802Sallanjudezfs_set_env(void)
824293802Sallanjude{
825293802Sallanjude	char envname[32], envval[256];
826293802Sallanjude	char *beroot, *pagenum;
827293802Sallanjude	int rv, page, ctr;
828293802Sallanjude
829293802Sallanjude	beroot = getenv("zfs_be_root");
830293802Sallanjude	if (beroot == NULL) {
831293802Sallanjude		return (1);
832293802Sallanjude	}
833293802Sallanjude
834293802Sallanjude	pagenum = getenv("zfs_be_currpage");
835293802Sallanjude	if (pagenum != NULL) {
836293802Sallanjude		page = strtol(pagenum, NULL, 10);
837293802Sallanjude	} else {
838293802Sallanjude		page = 1;
839293802Sallanjude	}
840293802Sallanjude
841293802Sallanjude	ctr = 1;
842293802Sallanjude	rv = 0;
843293802Sallanjude	zfs_env_index = ZFS_BE_FIRST;
844293802Sallanjude	SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) {
845293802Sallanjude		/* Skip to the requested page number */
846293802Sallanjude		if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) {
847293802Sallanjude			ctr++;
848293802Sallanjude			continue;
849293802Sallanjude		}
850293802Sallanjude
851293802Sallanjude		snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
852293802Sallanjude		snprintf(envval, sizeof(envval), "%s", zfs_be->name);
853293802Sallanjude		rv = setenv(envname, envval, 1);
854293802Sallanjude		if (rv != 0) {
855293802Sallanjude			break;
856293802Sallanjude		}
857293802Sallanjude
858293802Sallanjude		snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index);
859293802Sallanjude		rv = setenv(envname, envval, 1);
860293802Sallanjude		if (rv != 0){
861293802Sallanjude			break;
862293802Sallanjude		}
863293802Sallanjude
864293802Sallanjude		snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index);
865293802Sallanjude		rv = setenv(envname, "set_bootenv", 1);
866293802Sallanjude		if (rv != 0){
867293802Sallanjude			break;
868293802Sallanjude		}
869293802Sallanjude
870293802Sallanjude		snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index);
871293802Sallanjude		snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name);
872293802Sallanjude		rv = setenv(envname, envval, 1);
873293802Sallanjude		if (rv != 0){
874293802Sallanjude			break;
875293802Sallanjude		}
876293802Sallanjude
877293802Sallanjude		zfs_env_index++;
878293802Sallanjude		if (zfs_env_index > ZFS_BE_LAST) {
879293802Sallanjude			break;
880293802Sallanjude		}
881293802Sallanjude
882293802Sallanjude	}
883293802Sallanjude
884293802Sallanjude	for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) {
885293802Sallanjude		snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
886293802Sallanjude		(void)unsetenv(envname);
887293802Sallanjude		snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index);
888293802Sallanjude		(void)unsetenv(envname);
889293802Sallanjude		snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index);
890293802Sallanjude		(void)unsetenv(envname);
891293802Sallanjude		snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index);
892293802Sallanjude		(void)unsetenv(envname);
893293802Sallanjude	}
894293802Sallanjude
895293802Sallanjude	return (rv);
896293802Sallanjude}